Commit c9acd835 authored by Andrey Filippov's avatar Andrey Filippov

CLAUDE: TestDataView: manifest-shaped ImageJ hyperstack viewer for test-case/results buffers

Kills the manual raw-import papercut: dims come from manifest.txt, TD
buffers (trailing 256) render as tile grids with presentCltData quadrant
placement, per-tile planes as tilesX x tilesY images, generic fallback
otherwise. viewCase/viewResults/showAll; verified shapes on the avg_td
round-trip case (80x64 x16 TD grid, 5x4 counts).
Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
parent 1acb4341
/**
**
** TestDataView.java - open any test-case or C++-results buffer as a
** properly-shaped ImageJ hyperstack, straight from the manifest — no more
** typing dimensions into the raw importer by hand. TD buffers (trailing
** dim 256 = 4 x 8x8 CLT tile) are rendered as tile grids with the same
** 2x2 quadrant placement as GpuQuad.presentCltData(); count/tile planes
** (product = tilesy*tilesx*colors) become tilesX x tilesY images; anything
** else falls back to [slices][height][width] from its last two dims.
**
** Copyright (C) 2026 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
** TestDataView.java is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
** -----------------------------------------------------------------------------**
**
** By Claude on 07/10/2026
*/
package com.elphel.imagej.gpu.testdata;
import java.io.IOException;
import com.elphel.imagej.gpu.GPUTileProcessor;
import ij.ImagePlus;
import ij.ImageStack;
import ij.process.FloatProcessor;
public class TestDataView {
/** View a buffer from the test-case inputs (<dir>/manifest.txt). */
public static ImagePlus viewCase(String case_dir, String name) throws IOException {
TestDataReader rdr = new TestDataReader(case_dir);
return view(rdr, rdr, name, name);
}
/**
* View a buffer from the C++ test results (<dir>/results/). Scalar
* parameters (tilesx/tilesy/colors) come from the case manifest — the
* results manifest only lists buffers.
*/
public static ImagePlus viewResults(String case_dir, String name) throws IOException {
TestDataReader prms = new TestDataReader(case_dir);
TestDataReader rdr = TestDataReader.results(case_dir);
return view(rdr, prms, name, name + " (results)");
}
/** Open every buffer of the case and of results/ (if present) as windows. */
public static void showAll(String case_dir) throws IOException {
TestDataReader rdr = new TestDataReader(case_dir);
for (String name : rdr.getNames()) {
view(rdr, rdr, name, name).show();
}
try {
TestDataReader res = TestDataReader.results(case_dir);
for (String name : res.getNames()) {
view(res, rdr, name, name + " (results)").show();
}
} catch (IOException e) { // no results yet - fine
}
}
/**
* Shape a buffer into an ImagePlus using its manifest dims (+ tile
* parameters from prms when the shape is TD- or tile-plane-like).
*/
public static ImagePlus view(
TestDataReader rdr,
TestDataReader prms,
String name,
String title) throws IOException {
float [] data;
if (rdr.getEntry(name).dtype.equals("i32")) {
int [] idata = rdr.getInts(name);
data = new float [idata.length];
for (int i = 0; i < idata.length; i++) data[i] = idata[i];
} else {
data = rdr.getFloats(name);
}
int [] dims = rdr.dims(name);
int tilesX = prms.paramInt("tilesx", 0);
int tilesY = prms.paramInt("tilesy", 0);
int colors = prms.paramInt("colors", 1);
int dtt = GPUTileProcessor.DTT_SIZE;
int chunk = 4 * dtt * dtt; // one TD tile per color, 256
int num_tiles = tilesX * tilesY * colors;
if ((dims[dims.length - 1] == chunk) && (num_tiles > 0) &&
((data.length % (num_tiles * chunk)) == 0)) {
return tdGrid(data, title, tilesX, tilesY, colors, data.length / (num_tiles * chunk));
}
if ((num_tiles > 0) && (data.length % num_tiles == 0) &&
((data.length / num_tiles) <= 16)) { // counts/weights/flags per tile
return planes(data, title, tilesX, tilesY, (data.length / num_tiles) * colors);
}
// generic fallback: [slices][height][width] from the last two dims
int width = dims[dims.length - 1];
int height = (dims.length > 1) ? dims[dims.length - 2] : 1;
return planes(data, title, width, height, data.length / (width * height));
}
/** TD tile grid, same quadrant placement as GpuQuad.presentCltData(). */
private static ImagePlus tdGrid(
float [] data,
String title,
int tilesX,
int tilesY,
int colors,
int num_slices) { // e.g. 16 sensors, or 1 for the average
int dtt = GPUTileProcessor.DTT_SIZE;
int quad_size = dtt * dtt;
int width = 2 * dtt * tilesX;
int height = 2 * dtt * tilesY;
int slice_len = tilesX * tilesY * colors * 4 * quad_size;
ImageStack stack = new ImageStack(width, height);
for (int slice = 0; slice < num_slices; slice++) {
for (int color = 0; color < colors; color++) {
float [] pixels = new float [width * height];
for (int ntile = 0; ntile < (tilesX * tilesY); ntile++) {
for (int nquad = 0; nquad < 4; nquad++) {
for (int row = 0; row < dtt; row++) {
int src = slice * slice_len +
(((ntile * colors + color) * 4) + nquad) * quad_size + row * dtt;
int dst = (((ntile / tilesX) * 2 + (nquad / 2)) * dtt + row) * width +
((ntile % tilesX) * 2 + (nquad % 2)) * dtt;
System.arraycopy(data, src, pixels, dst, dtt);
}
}
}
stack.addSlice(title + "-" + slice + ((colors > 1) ? ("-c" + color) : ""), pixels);
}
}
return new ImagePlus(title, stack);
}
private static ImagePlus planes(float [] data, String title, int width, int height, int num_slices) {
ImageStack stack = new ImageStack(width, height);
for (int slice = 0; slice < num_slices; slice++) {
float [] pixels = new float [width * height];
System.arraycopy(data, slice * width * height, pixels, 0, width * height);
stack.addSlice(title + "-" + slice, new FloatProcessor(width, height, pixels, null).getPixels());
}
return new ImagePlus(title, stack);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment