Skip to content

Commit bc3fa86

Browse files
committed
ImpUtils/ImgUtils: Fix wrong 16bit flag and CT swap issues
1 parent 089748b commit bc3fa86

3 files changed

Lines changed: 54 additions & 48 deletions

File tree

‎src/main/java/sc/fiji/snt/io/WekaModelLoader.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ private <T extends RealType<T>> boolean loadSecondaryLayerUsingLabkit(final bool
221221

222222
private String getClassChoice(final List<String> classes) {
223223
if (classes.size() == 1)
224-
return classes.get(0);
224+
return classes.getFirst();
225225
return new GuiUtils().getChoice(
226226
"Model has " + classes.size() + " classifier classes. Which one should be loaded?", "Choose Class...",
227227
classes.toArray(new String[0]), "");

‎src/main/java/sc/fiji/snt/util/ImgUtils.java‎

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -469,8 +469,10 @@ public static <T> RandomAccessibleInterval<T> crop(
469469
final int ndims = rai.numDimensions();
470470
final long[] imgMin = Intervals.minAsLongArray(rai);
471471
final long[] imgMax = Intervals.maxAsLongArray(rai);
472-
final long[] min = new long[ndims];
473-
final long[] max = new long[ndims];
472+
473+
// Start with full extent for all dimensions: only spatial dims are cropped
474+
final long[] min = imgMin.clone();
475+
final long[] max = imgMax.clone();
474476

475477
if (ndims >= 3) {
476478
// Assume ZYX order: dim0=Z, dim1=Y, dim2=X
@@ -684,12 +686,23 @@ public static <T extends RealType<T>> RandomAccessibleInterval<T> getCtSlice(fin
684686
final int frameIndex) {
685687
@SuppressWarnings("unchecked")
686688
RandomAccessibleInterval<T> slice = (RandomAccessibleInterval<T>) dataset;
687-
if (dataset.getFrames() > 1)
688-
slice = Views.hyperSlice(slice, dataset.dimensionIndex(Axes.TIME), frameIndex);
689689

690-
// Assuming time always comes after channel, we can use the same index as the Dataset
691-
if (dataset.getChannels() > 1)
692-
slice = Views.hyperSlice(slice, dataset.dimensionIndex(Axes.CHANNEL), channelIndex);
690+
final int timeDim = dataset.dimensionIndex(Axes.TIME);
691+
final int channelDim = dataset.dimensionIndex(Axes.CHANNEL);
692+
693+
// Slice TIME first, then CHANNEL. After removing the TIME dimension,
694+
// any axis that was positioned after it shifts down by one
695+
if (dataset.getFrames() > 1 && timeDim >= 0) {
696+
slice = Views.hyperSlice(slice, timeDim, frameIndex);
697+
}
698+
699+
if (dataset.getChannels() > 1 && channelDim >= 0) {
700+
// Adjust channel index if TIME was removed before it
701+
final int adjustedChannelDim = (dataset.getFrames() > 1 && timeDim >= 0 && timeDim < channelDim)
702+
? channelDim - 1
703+
: channelDim;
704+
slice = Views.hyperSlice(slice, adjustedChannelDim, channelIndex);
705+
}
693706

694707
return slice;
695708
}
@@ -1009,7 +1022,7 @@ public static <T extends NumericType<T>> ImgPlus<T> wrapWithAxes(
10091022
result.setName(name != null ? name : "result");
10101023

10111024
for (int d = 0; d < ndims; d++) {
1012-
result.setAxis(source.axis(d), d);
1025+
result.setAxis(source.axis(d).copy(), d);
10131026
}
10141027

10151028
return result;
@@ -1065,8 +1078,10 @@ public static ImgPlus<?> wrapWithSpacing(
10651078
final Img img = (rai instanceof Img) ? (Img) rai : ImgView.wrap((RandomAccessibleInterval) rai);
10661079
final ImgPlus result = new ImgPlus(img);
10671080

1068-
// Create calibrated axes (X, Y, Z order for spatial dimensions)
1069-
final AxisType[] defaultAxes = {Axes.X, Axes.Y, Axes.Z, Axes.TIME, Axes.CHANNEL};
1081+
// Create calibrated axes — XYZCT matches the canonical order used by
1082+
// permuteToXYZCT(), impToRealRai5d(), and BVV. Previously this was XYZTC,
1083+
// which would mis-label Channel as Time (and vice versa) for 5D images
1084+
final AxisType[] defaultAxes = {Axes.X, Axes.Y, Axes.Z, Axes.CHANNEL, Axes.TIME};
10701085
for (int d = 0; d < nDims; d++) {
10711086
final AxisType axisType = (d < defaultAxes.length) ? defaultAxes[d] : Axes.unknown();
10721087
final DefaultLinearAxis axis = (unit != null && !unit.isEmpty())
@@ -1157,7 +1172,7 @@ public static double computePercentile(final RandomAccessibleInterval<? extends
11571172
final List<Double> samples = new ArrayList<>();
11581173
final Random rand = new Random(42); // Fixed seed for reproducibility
11591174

1160-
final Cursor<? extends RealType<?>> cursor = source.cursor();
1175+
final Cursor<? extends RealType<?>> cursor = Views.flatIterable(source).cursor();
11611176
while (cursor.hasNext()) {
11621177
cursor.fwd();
11631178
if (sampleRate >= 1.0 || rand.nextDouble() < sampleRate) {
@@ -1180,13 +1195,15 @@ public static boolean isBinary(final RandomAccessibleInterval<?> rai) {
11801195
if (rai.firstElement() instanceof BitType) return true;
11811196
// Early exit on 3rd unique value
11821197
final Set<Double> unique = new HashSet<>(4);
1198+
boolean hasRealPixels = false;
11831199
for (final Object pixel : Views.flatIterable(rai)) {
11841200
if (pixel instanceof RealType) {
1201+
hasRealPixels = true;
11851202
unique.add(((RealType<?>) pixel).getRealDouble());
11861203
if (unique.size() > 2) return false; // Early exit
11871204
}
11881205
}
1189-
return true;
1206+
return hasRealPixels && unique.size() <= 2;
11901207
}
11911208

11921209
/**

‎src/main/java/sc/fiji/snt/util/ImpUtils.java‎

Lines changed: 24 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
import net.imglib2.loops.LoopBuilder;
4848
import net.imglib2.type.NativeType;
4949
import net.imglib2.type.numeric.RealType;
50-
import net.imglib2.util.Intervals;
5150
import org.scijava.convert.ConvertService;
5251
import sc.fiji.snt.Path;
5352
import sc.fiji.snt.SNTUtils;
@@ -105,8 +104,8 @@ public static ImagePlus getMIP(final ImagePlus imp) {
105104

106105
public static ImagePlus getMIP(final ImagePlus imp, final int startSlice, final int stopSlice) {
107106
final ImagePlus mip = ZProjector.run(imp, "max", startSlice, stopSlice);
108-
if (mip.getNChannels() == 1)
109-
mip.setLut(imp.getLuts()[0]); // assume single-channel image
107+
if (mip.getNChannels() == 1 && imp.getLuts().length > 0)
108+
mip.setLut(imp.getLuts()[0]);
110109
mip.copyScale(imp);
111110
new ContrastEnhancer().stretchHistogram(mip, 0.35);
112111
return mip;
@@ -139,7 +138,7 @@ public static void convertTo8bit(final ImagePlus imp) {
139138
}
140139

141140
public static void convertTo16bit(final ImagePlus imp) {
142-
if (imp.getType() != ImagePlus.GRAY8) {
141+
if (imp.getType() != ImagePlus.GRAY16) {
143142
final boolean doScaling = ImageConverter.getDoScaling();
144143
ImageConverter.setDoScaling(true);
145144
new ImageConverter(imp).convertToGray16();
@@ -149,8 +148,8 @@ public static void convertTo16bit(final ImagePlus imp) {
149148

150149
public static ImagePlus convertRGBtoComposite(final ImagePlus imp) {
151150
if (imp.getType() == ImagePlus.COLOR_RGB) {
152-
imp.hide();
153151
final boolean isShowing = imp.isVisible();
152+
imp.hide();
154153
final ImagePlus res = CompositeConverter.makeComposite(imp);
155154
imp.flush();
156155
if (isShowing) res.show();
@@ -175,7 +174,7 @@ public static ImagePlus open(final String filePathOrUrl, final String title) {
175174
final boolean redirecting = IJ.redirectingErrorMessages();
176175
IJ.redirectErrorMessages(true);
177176
final ImagePlus imp = IJ.openImage(filePathOrUrl);
178-
if (title != null)
177+
if (imp != null && title != null)
179178
imp.setTitle(title);
180179
IJ.redirectErrorMessages(redirecting);
181180
return imp;
@@ -249,13 +248,9 @@ public static ImagePlus getCT(final ImagePlus imp, final int channel, final int
249248
}
250249

251250
public static void removeSlices(final ImageStack stack, Collection<String> labels) {
252-
int count = 0;
253-
for (int i = 1; i <= stack.size(); i++) {
254-
if ((i - count) > stack.getSize())
255-
break;
251+
for (int i = stack.size(); i >= 1; i--) {
256252
if (labels.contains(stack.getSliceLabel(i))) {
257-
stack.deleteSlice(i - count);
258-
count++;
253+
stack.deleteSlice(i);
259254
}
260255
}
261256
}
@@ -463,7 +458,7 @@ private static boolean isGrayscale(final BufferedImage image, final int width, f
463458
* plugin); 'OP1'/'OP_1' for the DIADEM OP_1 dataset; 'cil701' and
464459
* 'cil810' for the respective Cell Image Library entries, and
465460
* 'binary timelapse' for a small 4-frame sequence of neurite growth
466-
* @return the demo image, or null if data could no be retrieved
461+
* @return the demo image, or null if data could not be retrieved
467462
*/
468463
public static ImagePlus demo(final String img) {
469464
if (img == null)
@@ -774,7 +769,7 @@ public static String ascii(final ImagePlus imp, final boolean dilate, final int
774769
/**
775770
* Converts the specified image into an easy displayable form, i.e., a non-composite 2D image
776771
* If the image is a timelapse, only the first frame is considered; if 3D, a MIP is retrieved;
777-
* if multichannel a RGB version is obtained. The image is flattened if its Overlay has ROIs.
772+
* if multichannel, an RGB version is obtained. The image is flattened if its Overlay has ROIs.
778773
*
779774
* @param imp The image to be converted
780775
* @param frame The frame to be considered (ignored if image is not a timelapse)
@@ -814,7 +809,7 @@ else if (imp.getNSlices() > 1) {
814809
* non-background values.
815810
*
816811
* @param imp The image to be cropped
817-
* @param backgroundValue the background value typically 'black': 0 for 8-/16bit, 0x000000 for RGB),
812+
* @param backgroundValue the background value typically 'black': 0 for 8-/16bit, 0x000000 for RGB,
818813
* or white (255 for 8-bit, 65535 for 16-bir, 0xFFFFFF for RGB)
819814
*/
820815
public static void crop(final ImagePlus imp, final Number backgroundValue) {
@@ -840,34 +835,29 @@ public static void rotate90(final ImagePlus imp, final String direction) {
840835
}
841836

842837
/**
843-
* Returns the cropping rectangle around non-background values.
838+
* Returns the cropping rectangle around non-background values, considering
839+
* all slices of the stack.
844840
*
845841
* @param imp The image to be parsed
846842
* @param backgroundValue the background value typically 'black': 0, or white (255 for 8-bit/RGB,
847843
* or 65535 for 16-bit)
848-
* @return the rectangular ROI defining non-background bounds
844+
* @return the rectangular ROI defining non-background bounds, or null if all pixels are background
849845
*/
850846
public static Roi getForegroundRect(final ImagePlus imp, final Number backgroundValue) {
851-
final ImageProcessor ip = imp.getProcessor();
852-
int w = ip.getWidth(), h = ip.getHeight();
847+
final ImageStack stack = imp.getStack();
848+
final int w = imp.getWidth(), h = imp.getHeight();
853849
int minX = w, maxX = 0, minY = h, maxY = 0;
854-
if (imp.getType() == ImagePlus.GRAY32) {
855-
float bgVal = backgroundValue.floatValue();
856-
for (int y = 0; y < h; y++) {
857-
for (int x = 0; x < w; x++) {
858-
if (Math.abs(ip.getPixelValue(x, y) - bgVal) > 0.0001f) {
859-
if (x < minX) minX = x;
860-
if (x > maxX) maxX = x;
861-
if (y < minY) minY = y;
862-
if (y > maxY) maxY = y;
863-
}
864-
}
865-
}
866-
} else {
867-
int bgVal = backgroundValue.intValue();
850+
final boolean isFloat = imp.getType() == ImagePlus.GRAY32;
851+
final float bgFloat = backgroundValue.floatValue();
852+
final int bgInt = backgroundValue.intValue();
853+
for (int z = 1; z <= stack.size(); z++) {
854+
final ImageProcessor ip = stack.getProcessor(z);
868855
for (int y = 0; y < h; y++) {
869856
for (int x = 0; x < w; x++) {
870-
if (ip.getPixelValue(x, y) != bgVal) {
857+
final boolean fg = isFloat
858+
? Math.abs(ip.getPixelValue(x, y) - bgFloat) > 0.0001f
859+
: ip.getPixelValue(x, y) != bgInt;
860+
if (fg) {
871861
if (x < minX) minX = x;
872862
if (x > maxX) maxX = x;
873863
if (y < minY) minY = y;
@@ -952,7 +942,6 @@ public static <T extends RealType<T> & NativeType<T>> ImgPlus<T> toImgPlus(final
952942
final RandomAccessibleInterval<T> rai = ImageJFunctions.wrapReal(imp);
953943

954944
// Create contiguous copy to avoid virtual stack issues
955-
final long[] dims = Intervals.dimensionsAsLongArray(rai);
956945
final Img<T> img = copyToArrayImg(rai);
957946

958947
// Build axes from calibration

0 commit comments

Comments
 (0)