|
24 | 24 |
|
25 | 25 | import ij.ImagePlus; |
26 | 26 | import ij.measure.Calibration; |
| 27 | +import io.scif.services.DatasetIOService; |
27 | 28 | import net.imagej.Dataset; |
| 29 | +import net.imagej.DatasetService; |
28 | 30 | import net.imagej.ImgPlus; |
29 | 31 | import net.imagej.axis.Axes; |
30 | 32 | import net.imagej.axis.AxisType; |
@@ -1233,6 +1235,69 @@ public static ImgPlus<?> open(final String filePathOrUrl) { |
1233 | 1235 | } |
1234 | 1236 | } |
1235 | 1237 |
|
| 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 | + |
1236 | 1301 | /** |
1237 | 1302 | * Opens an image file as an ImgPlus. |
1238 | 1303 | * |
|
0 commit comments