Skip to content

Commit df6622c

Browse files
committed
Bvv: Allow multiple sources in the same scene
- Improve support for multi-channel volumes - Organize channels by groups - Allow manual transformations to be saved/exported
1 parent bde65a6 commit df6622c

3 files changed

Lines changed: 971 additions & 66 deletions

File tree

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

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424

2525
import ij.ImagePlus;
2626
import ij.measure.Calibration;
27+
import io.scif.services.DatasetIOService;
2728
import net.imagej.Dataset;
29+
import net.imagej.DatasetService;
2830
import net.imagej.ImgPlus;
2931
import net.imagej.axis.Axes;
3032
import net.imagej.axis.AxisType;
@@ -1233,6 +1235,69 @@ public static ImgPlus<?> open(final String filePathOrUrl) {
12331235
}
12341236
}
12351237

1238+
/**
1239+
* Saves an image to disk as a TIFF file. This is the save counterpart of
1240+
* {@link #open(String)}.
1241+
* <p>
1242+
* The image is copied into a 32-bit float representation prior to saving,
1243+
* ensuring compatibility regardless of the source pixel type.
1244+
* For lower bit-depth output use
1245+
* {@link #save(RandomAccessibleInterval, String, RealType)}.
1246+
* </p>
1247+
*
1248+
* @param img the image to save; may be virtual or transformed
1249+
* @param filePath the destination file path; a {@code .tif} extension is
1250+
* appended automatically if absent
1251+
* @throws Exception if the image cannot be serialized or written to disk
1252+
*/
1253+
public static void save(final RandomAccessibleInterval<? extends RealType<?>> img,
1254+
final String filePath) throws Exception {
1255+
save(img, filePath, new FloatType());
1256+
}
1257+
1258+
/**
1259+
* Saves an image to disk as a TIFF file, converting pixel values to the
1260+
* specified output type before writing. This is the save counterpart of
1261+
* {@link #open(String)}.
1262+
* <p>
1263+
* The image is materialized into a concrete in-memory {@link Img} of the
1264+
* requested type, so the caller must ensure sufficient RAM is available.
1265+
* Conversion is done via {@link RealType#setReal(double)}, which clamps
1266+
* values to the target type's range automatically (e.g. 16-bit unsigned
1267+
* values are clamped to [0, 65535]).
1268+
* </p>
1269+
* <p>
1270+
* Typical usage:
1271+
* <pre>
1272+
* ImgUtils.save(rai, "/path/to/out.tif", new UnsignedShortType());
1273+
* ImgUtils.save(rai, "/path/to/out.tif", new UnsignedByteType());
1274+
* ImgUtils.save(rai, "/path/to/out.tif", new FloatType());
1275+
* </pre>
1276+
* </p>
1277+
*
1278+
* @param <T> the output pixel type; must be a {@link NativeType} so
1279+
* that an {@link ArrayImgFactory} can allocate it
1280+
* @param img the image to save; may be virtual or transformed
1281+
* @param filePath the destination file path; a {@code .tif} extension is
1282+
* appended automatically if absent
1283+
* @param outType an instance of the desired output pixel type, e.g.
1284+
* {@code new UnsignedShortType()} for 16-bit output
1285+
* @throws Exception if the image cannot be serialized or written to disk
1286+
*/
1287+
public static <T extends RealType<T> & NativeType<T>> void save(
1288+
final RandomAccessibleInterval<? extends RealType<?>> img,
1289+
final String filePath,
1290+
final T outType) throws Exception {
1291+
final String path = (filePath.endsWith(".tif") || filePath.endsWith(".tiff"))
1292+
? filePath : filePath + ".tif";
1293+
final Img<T> output = new ArrayImgFactory<>(outType).create(img);
1294+
LoopBuilder.setImages(img, output).multiThreaded()
1295+
.forEachPixel((in, out) -> out.setReal(in.getRealDouble()));
1296+
final Context ctx = SNTUtils.getContext();
1297+
final Dataset dataset = ctx.service(DatasetService.class).create(output);
1298+
ctx.service(DatasetIOService.class).save(dataset, path);
1299+
}
1300+
12361301
/**
12371302
* Opens an image file as an ImgPlus.
12381303
*

0 commit comments

Comments
 (0)