From 4aa8e3d1a19cd421c5aa72c42675f4643f7180ff Mon Sep 17 00:00:00 2001 From: Emil Melnikov Date: Thu, 27 Jul 2023 14:29:50 +0200 Subject: [PATCH] Simplify command classes and examples (#106) * Merge commands and executors for less indirection * Move subprocess running, temporary file management and common parameters to the CommandBase class * Improve user interaction during command execution * Simplify and merge together demo classes * Add a simple pixel classification ImageJ macro Co-authored-by: Dominik Kutra --- README.md | 5 +- examples/pixel_classification.ijm | 5 + .../executors/AbstractIlastikExecutor.java | 194 ------------ .../ilastik4ij/executors/Autocontext.java | 41 --- .../ilastik4ij/executors/Multicut.java | 40 --- .../executors/ObjectClassification.java | 44 --- .../executors/PixelClassification.java | 41 --- .../ilastik4ij/executors/Tracking.java | 42 --- .../ilastik4ij/hdf5/Hdf5DataSetReader.java | 12 +- .../ilastik4ij/hdf5/Hdf5DataSetWriter.java | 16 +- .../ui/IlastikAutoContextCommand.java | 76 ----- .../ilastik4ij/ui/IlastikMulticutCommand.java | 70 ----- .../IlastikObjectClassificationCommand.java | 79 ----- .../ilastik/ilastik4ij/ui/IlastikOptions.java | 74 +---- .../ui/IlastikPixelClassificationCommand.java | 76 ----- .../ilastik4ij/ui/IlastikTrackingCommand.java | 79 ----- .../ilastik/ilastik4ij/ui/UiConstants.java | 6 - .../org/ilastik/ilastik4ij/util/IOUtils.java | 36 --- .../workflow/AutocontextCommand.java | 26 ++ .../ilastik4ij/workflow/MulticutCommand.java | 27 ++ .../workflow/ObjectClassificationCommand.java | 30 ++ .../workflow/PixelClassificationCommand.java | 26 ++ .../ilastik4ij/workflow/TrackingCommand.java | 36 +++ .../ilastik4ij/workflow/WorkflowCommand.java | 282 ++++++++++++++++++ .../java/org/ilastik/ilastik4ij/Demo.java | 81 +++++ .../ilastik4ij/ObjectClassificationDemo.java | 81 ----- .../PixelClassificationCommandDemo.java | 48 --- .../ilastik4ij/PixelClassificationDemo.java | 68 ----- .../org/ilastik/ilastik4ij/TrackingDemo.java | 55 ---- 29 files changed, 545 insertions(+), 1151 deletions(-) create mode 100644 examples/pixel_classification.ijm delete mode 100644 src/main/java/org/ilastik/ilastik4ij/executors/AbstractIlastikExecutor.java delete mode 100644 src/main/java/org/ilastik/ilastik4ij/executors/Autocontext.java delete mode 100644 src/main/java/org/ilastik/ilastik4ij/executors/Multicut.java delete mode 100644 src/main/java/org/ilastik/ilastik4ij/executors/ObjectClassification.java delete mode 100644 src/main/java/org/ilastik/ilastik4ij/executors/PixelClassification.java delete mode 100644 src/main/java/org/ilastik/ilastik4ij/executors/Tracking.java delete mode 100644 src/main/java/org/ilastik/ilastik4ij/ui/IlastikAutoContextCommand.java delete mode 100644 src/main/java/org/ilastik/ilastik4ij/ui/IlastikMulticutCommand.java delete mode 100644 src/main/java/org/ilastik/ilastik4ij/ui/IlastikObjectClassificationCommand.java delete mode 100644 src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java delete mode 100644 src/main/java/org/ilastik/ilastik4ij/ui/IlastikTrackingCommand.java delete mode 100644 src/main/java/org/ilastik/ilastik4ij/ui/UiConstants.java delete mode 100644 src/main/java/org/ilastik/ilastik4ij/util/IOUtils.java create mode 100644 src/main/java/org/ilastik/ilastik4ij/workflow/AutocontextCommand.java create mode 100644 src/main/java/org/ilastik/ilastik4ij/workflow/MulticutCommand.java create mode 100644 src/main/java/org/ilastik/ilastik4ij/workflow/ObjectClassificationCommand.java create mode 100644 src/main/java/org/ilastik/ilastik4ij/workflow/PixelClassificationCommand.java create mode 100644 src/main/java/org/ilastik/ilastik4ij/workflow/TrackingCommand.java create mode 100644 src/main/java/org/ilastik/ilastik4ij/workflow/WorkflowCommand.java create mode 100644 src/test/java/org/ilastik/ilastik4ij/Demo.java delete mode 100644 src/test/java/org/ilastik/ilastik4ij/ObjectClassificationDemo.java delete mode 100644 src/test/java/org/ilastik/ilastik4ij/PixelClassificationCommandDemo.java delete mode 100644 src/test/java/org/ilastik/ilastik4ij/PixelClassificationDemo.java delete mode 100644 src/test/java/org/ilastik/ilastik4ij/TrackingDemo.java diff --git a/README.md b/README.md index 914e48f5..040d6133 100644 --- a/README.md +++ b/README.md @@ -251,8 +251,9 @@ as `ImagePlus`, containing `ImageProcessors` to access the underlying data. We t containers everywhere which are roughly wrapped as `Dataset > ImgPlus > Img > RandomAccessibleInterval`. -**Testing:** no real tests are included right now, but you can run the `main` method in -`WorkflowTests.java` which fires up an ImageJ instance for each of the three plugins. +### Demo + +`src/test/java/org/ilastik/ilastik4ij/Demo.java` contains usage examples of various workflows. ### Manually test in a local Fiji installation diff --git a/examples/pixel_classification.ijm b/examples/pixel_classification.ijm new file mode 100644 index 00000000..77ec11f1 --- /dev/null +++ b/examples/pixel_classification.ijm @@ -0,0 +1,5 @@ +project = "src/test/resources/pixel_class_2d_cells_apoptotic.ilp"; +input = "2d_cells_apoptotic.tif"; + +open(input); +run("Run Pixel Classification Prediction", "projectfilename=" + project + " input=" + input + " pixelclassificationtype=Probabilities"); diff --git a/src/main/java/org/ilastik/ilastik4ij/executors/AbstractIlastikExecutor.java b/src/main/java/org/ilastik/ilastik4ij/executors/AbstractIlastikExecutor.java deleted file mode 100644 index a45396b5..00000000 --- a/src/main/java/org/ilastik/ilastik4ij/executors/AbstractIlastikExecutor.java +++ /dev/null @@ -1,194 +0,0 @@ -package org.ilastik.ilastik4ij.executors; - -import ij.IJ; -import net.imagej.ImgPlus; -import net.imglib2.type.NativeType; -import net.imglib2.type.numeric.RealType; -import org.ilastik.ilastik4ij.hdf5.Hdf5DataSetReader; -import org.ilastik.ilastik4ij.hdf5.Hdf5DataSetWriter; -import org.ilastik.ilastik4ij.util.IOUtils; -import org.scijava.app.StatusService; -import org.scijava.log.LogService; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.*; - -public abstract class AbstractIlastikExecutor { - - protected static final String rawInputTempFile = "tempFileRawInput"; - protected static final String outputTempFile = "tempFileOutput"; - protected static final String secondInputTempFile = "tempFileSegmentationOrProbabilitiesInput"; - - private final int numThreads; - private final int maxRamMb; - - protected final File projectFileName; - protected final LogService logService; - protected final StatusService statusService; - private final File executableFile; - - public enum PixelPredictionType { - Segmentation, - Probabilities - } - - protected final List baseCommand; - - - public AbstractIlastikExecutor(File executableFile, File projectFileName, LogService logService, - StatusService statusService, int numThreads, int maxRamMb) { - this.numThreads = numThreads; - this.maxRamMb = maxRamMb; - this.executableFile = executableFile; - this.projectFileName = projectFileName; - this.logService = logService; - this.statusService = statusService; - this.baseCommand = Arrays.asList( - "--headless", - "--project=" + projectFileName.getAbsolutePath(), - "--output_format=hdf5", - "--output_axis_order=tzyxc", - "--input_axes=tzyxc", - "--readonly=1", - "--output_internal_path=exported_data", - "--input_axes=tzyxc" - ); - } - - /* - * implementations of the buildCommandLine method need to call the following lines: - * List commandLine = getBaseCommand(); - * then add the appropriate workflow arguments with - * commandLine.add("..."); - */ - protected abstract List buildCommandLine(Map tempFiles, PixelPredictionType pixelPredictionType); - - /* - * OS-aware getter for executable file path - * - * currently adds the internal path to the OSX executable from the .app path. - */ - private String getExecutableFilePath() { - Path p = executableFile.toPath().toAbsolutePath().normalize(); - // Convert macOS app bundle path to executable path. - if (IJ.isMacOSX() && p.toString().endsWith(".app")) { - p = p.resolve(Paths.get("Contents", "MacOS", "ilastik")); - } - return p.toString(); - } - - /* - * return a List of command line arguments that are common for all workflows - * - * intended to be called in respective implementations of buildCommandLine - */ - protected List getBaseCommand(){ - List baseCMD = new ArrayList<>(); - baseCMD.add(getExecutableFilePath()); - baseCMD.addAll(baseCommand); - return baseCMD; - } - - protected > ImgPlus executeIlastik(ImgPlus> rawInputImg, - ImgPlus> secondInputImg, - PixelPredictionType pixelPredictionType) throws IOException { - final Map tempFiles = prepareTempFiles(secondInputImg != null); - try { - - stageInputFiles(rawInputImg, secondInputImg, tempFiles); - - List commandLine = buildCommandLine(tempFiles, pixelPredictionType); - - executeCommandLine(commandLine); - - ImgPlus outputImg = new Hdf5DataSetReader(tempFiles.get(outputTempFile), "exported_data", - "tzyxc", logService, statusService).read(); - - return outputImg; - } finally { - deleteTempFiles(tempFiles); - } - } - - private void stageInputFiles(ImgPlus> rawInputImg, ImgPlus> secondInputImg, - Map tempFiles) { - int compressionLevel = 1; - - new Hdf5DataSetWriter(rawInputImg, tempFiles.get(rawInputTempFile), "data", - compressionLevel, logService, statusService).write(); - - if (secondInputImg != null) { - new Hdf5DataSetWriter(secondInputImg, tempFiles.get(secondInputTempFile), "data", - compressionLevel, logService, statusService).write(); - } - } - - private Map prepareTempFiles(boolean hasSecondInputImg) throws IOException { - LinkedHashMap tempFiles = new LinkedHashMap<>(); - - tempFiles.put(rawInputTempFile, IOUtils.getTemporaryFileName("_in_raw.h5")); - tempFiles.put(outputTempFile, IOUtils.getTemporaryFileName("_out.h5")); - - if (hasSecondInputImg) { - tempFiles.put(secondInputTempFile, IOUtils.getTemporaryFileName("_in_2nd.h5")); - } - - logService.info("Temporary files: " + tempFiles); - - return Collections.unmodifiableMap(tempFiles); - } - - private void deleteTempFiles(Map tempFiles) { - for (String tempFilePath : tempFiles.values()) { - final File tempFile = new File(tempFilePath); - if (tempFile.exists()) { - tempFile.delete(); - logService.info("Deleted tmp file: " + tempFile); - } - } - } - - private void executeCommandLine(List commandLine) throws IOException { - logService.info("Running ilastik headless command:"); - logService.info(commandLine.toString()); - - ProcessBuilder pB = new ProcessBuilder(commandLine); - configureProcessBuilderEnvironment(pB); - - // run ilastik - final Process p = pB.start(); - - // write ilastik output to log - IOUtils.redirectOutputToLogService(p.getInputStream(), logService, false); - IOUtils.redirectOutputToLogService(p.getErrorStream(), logService, true); - - try { - p.waitFor(); - } catch (InterruptedException e) { - logService.warn("Execution got interrupted", e); - p.destroy(); - } - - // 0 indicates successful execution - if (p.exitValue() != 0) { - logService.error("ilastik execution crashed"); - throw new RuntimeException("Execution of ilastik was not successful."); - } - - logService.info("ilastik execution finished successfully!"); - } - - private void configureProcessBuilderEnvironment(ProcessBuilder pb) { - final Map env = pb.environment(); - if (this.numThreads >= 0) { - env.put("LAZYFLOW_THREADS", String.valueOf(this.numThreads)); - } - env.put("LAZYFLOW_TOTAL_RAM_MB", String.valueOf(this.maxRamMb)); - env.put("LANG", "en_US.UTF-8"); - env.put("LC_ALL", "en_US.UTF-8"); - env.put("LC_CTYPE", "en_US.UTF-8"); - } -} diff --git a/src/main/java/org/ilastik/ilastik4ij/executors/Autocontext.java b/src/main/java/org/ilastik/ilastik4ij/executors/Autocontext.java deleted file mode 100644 index d51d94b0..00000000 --- a/src/main/java/org/ilastik/ilastik4ij/executors/Autocontext.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.ilastik.ilastik4ij.executors; - -import net.imagej.ImgPlus; -import net.imglib2.type.NativeType; -import net.imglib2.type.numeric.RealType; -import org.scijava.app.StatusService; -import org.scijava.log.LogService; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class Autocontext extends AbstractIlastikExecutor { - - public Autocontext(File executableFilePath, File projectFileName, LogService logService, - StatusService statusService, int numThreads, int maxRamMb) { - super(executableFilePath, projectFileName, logService, statusService, numThreads, maxRamMb); - } - - public > ImgPlus classifyPixels(ImgPlus> rawInputImg, - PixelPredictionType pixelPredictionType) throws IOException { - return executeIlastik(rawInputImg, null, pixelPredictionType); - } - - @Override - protected List buildCommandLine(Map tempFiles, PixelPredictionType pixelPredictionType) { - List commandLine = getBaseCommand(); - if (pixelPredictionType == PixelPredictionType.Segmentation) { - commandLine.add("--export_source=Simple Segmentation Stage 2"); - } - else if (pixelPredictionType == PixelPredictionType.Probabilities) { - commandLine.add("--export_source=Probabilities Stage 2"); - } - commandLine.add("--raw_data=" + tempFiles.get(rawInputTempFile)); - commandLine.add("--output_filename_format=" + tempFiles.get(outputTempFile)); - - return commandLine; - } -} diff --git a/src/main/java/org/ilastik/ilastik4ij/executors/Multicut.java b/src/main/java/org/ilastik/ilastik4ij/executors/Multicut.java deleted file mode 100644 index 1e45ae79..00000000 --- a/src/main/java/org/ilastik/ilastik4ij/executors/Multicut.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.ilastik.ilastik4ij.executors; - -import net.imagej.ImgPlus; -import net.imglib2.type.NativeType; -import net.imglib2.type.numeric.RealType; -import org.scijava.app.StatusService; -import org.scijava.log.LogService; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class Multicut extends AbstractIlastikExecutor { - - public Multicut(File executableFilePath, File projectFileName, LogService logService, - StatusService statusService, int numThreads, int maxRamMb) { - super(executableFilePath, projectFileName, logService, statusService, numThreads, maxRamMb); - } - - public > ImgPlus runMulticut(ImgPlus> rawInputImg, - ImgPlus> probOrSegInputImg - ) throws IOException { - // null specified here to satisfy execteIlastik interface... - return executeIlastik(rawInputImg, probOrSegInputImg, null); - } - - @Override - protected List buildCommandLine(Map tempFiles, PixelPredictionType pixelPredictionType) { - - List commandLine = getBaseCommand(); - commandLine.add("--raw_data=" + tempFiles.get(rawInputTempFile)); - commandLine.add("--probabilities=" + tempFiles.get(secondInputTempFile)); - commandLine.add("--output_filename_format=" + tempFiles.get(outputTempFile)); - commandLine.add("--export_source=Multicut Segmentation"); - - return commandLine; - } -} diff --git a/src/main/java/org/ilastik/ilastik4ij/executors/ObjectClassification.java b/src/main/java/org/ilastik/ilastik4ij/executors/ObjectClassification.java deleted file mode 100644 index 7d872853..00000000 --- a/src/main/java/org/ilastik/ilastik4ij/executors/ObjectClassification.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.ilastik.ilastik4ij.executors; - -import net.imagej.ImgPlus; -import net.imglib2.type.NativeType; -import net.imglib2.type.numeric.RealType; -import org.scijava.app.StatusService; -import org.scijava.log.LogService; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class ObjectClassification extends AbstractIlastikExecutor { - - public ObjectClassification(File executableFilePath, File projectFileName, LogService logService, - StatusService statusService, int numThreads, int maxRamMb) { - super(executableFilePath, projectFileName, logService, statusService, numThreads, maxRamMb); - } - - public > ImgPlus classifyObjects(ImgPlus> rawInputImg, - ImgPlus> probOrSegInputImg, - PixelPredictionType secondInputType) throws IOException { - return executeIlastik(rawInputImg, probOrSegInputImg, secondInputType); - } - - @Override - protected List buildCommandLine(Map tempFiles, PixelPredictionType secondInputType) { - - List commandLine = getBaseCommand(); - commandLine.add("--raw_data=" + tempFiles.get(rawInputTempFile)); - - if (secondInputType == PixelPredictionType.Segmentation) { - commandLine.add("--segmentation_image=" + tempFiles.get(secondInputTempFile)); - } else { - commandLine.add("--prediction_maps=" + tempFiles.get(secondInputTempFile)); - } - - commandLine.add("--output_filename_format=" + tempFiles.get(outputTempFile)); - - return commandLine; - } -} diff --git a/src/main/java/org/ilastik/ilastik4ij/executors/PixelClassification.java b/src/main/java/org/ilastik/ilastik4ij/executors/PixelClassification.java deleted file mode 100644 index 00d8ef36..00000000 --- a/src/main/java/org/ilastik/ilastik4ij/executors/PixelClassification.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.ilastik.ilastik4ij.executors; - -import net.imagej.ImgPlus; -import net.imglib2.type.NativeType; -import net.imglib2.type.numeric.RealType; -import org.scijava.app.StatusService; -import org.scijava.log.LogService; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class PixelClassification extends AbstractIlastikExecutor { - - public PixelClassification(File executableFilePath, File projectFileName, LogService logService, - StatusService statusService, int numThreads, int maxRamMb) { - super(executableFilePath, projectFileName, logService, statusService, numThreads, maxRamMb); - } - - public > ImgPlus classifyPixels(ImgPlus> rawInputImg, - PixelPredictionType pixelPredictionType) throws IOException { - return executeIlastik(rawInputImg, null, pixelPredictionType); - } - - @Override - protected List buildCommandLine(Map tempFiles, PixelPredictionType pixelPredictionType) { - List commandLine = getBaseCommand(); - if (pixelPredictionType == PixelPredictionType.Segmentation) { - commandLine.add("--export_source=Simple Segmentation"); - } - else if (pixelPredictionType == PixelPredictionType.Probabilities) { - commandLine.add("--export_source=Probabilities"); - } - commandLine.add("--raw_data=" + tempFiles.get(rawInputTempFile)); - commandLine.add("--output_filename_format=" + tempFiles.get(outputTempFile)); - - return commandLine; - } -} diff --git a/src/main/java/org/ilastik/ilastik4ij/executors/Tracking.java b/src/main/java/org/ilastik/ilastik4ij/executors/Tracking.java deleted file mode 100644 index 0689abb7..00000000 --- a/src/main/java/org/ilastik/ilastik4ij/executors/Tracking.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.ilastik.ilastik4ij.executors; - -import net.imagej.ImgPlus; -import net.imglib2.type.NativeType; -import net.imglib2.type.numeric.RealType; -import org.scijava.app.StatusService; -import org.scijava.log.LogService; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class Tracking extends AbstractIlastikExecutor { - - public Tracking(File executableFilePath, File projectFileName, LogService logService, StatusService statusService, int numThreads, int maxRamMb) { - super(executableFilePath, projectFileName, logService, statusService, numThreads, maxRamMb); - } - - public > ImgPlus trackObjects(ImgPlus> rawInputImg, ImgPlus> secondInputImg, - PixelPredictionType pixelPredictionType) throws IOException { - return executeIlastik(rawInputImg, secondInputImg, pixelPredictionType); - } - - @Override - protected List buildCommandLine(Map tempFiles, PixelPredictionType pixelPredictionType) { - List commandLine = getBaseCommand(); - commandLine.add("--export_source=Tracking-Result"); - commandLine.add("--raw_data=" + tempFiles.get(rawInputTempFile)); - - if (pixelPredictionType == PixelPredictionType.Segmentation) { - commandLine.add("--binary_image=" + tempFiles.get(secondInputTempFile)); - } else { - commandLine.add("--prediction_maps=" + tempFiles.get(secondInputTempFile)); - } - - commandLine.add("--output_filename_format=" + tempFiles.get(outputTempFile)); - - return commandLine; - } -} diff --git a/src/main/java/org/ilastik/ilastik4ij/hdf5/Hdf5DataSetReader.java b/src/main/java/org/ilastik/ilastik4ij/hdf5/Hdf5DataSetReader.java index 6dbeeb48..f4234748 100644 --- a/src/main/java/org/ilastik/ilastik4ij/hdf5/Hdf5DataSetReader.java +++ b/src/main/java/org/ilastik/ilastik4ij/hdf5/Hdf5DataSetReader.java @@ -18,7 +18,7 @@ import net.imglib2.type.numeric.real.FloatType; import org.ilastik.ilastik4ij.util.Hdf5Utils; import org.scijava.app.StatusService; -import org.scijava.log.LogService; +import org.scijava.log.Logger; import javax.swing.*; import java.nio.file.Paths; @@ -36,14 +36,14 @@ public class Hdf5DataSetReader> { private final String filename; private final String dataset; private final String axesorder; - private final LogService logService; + private final Logger logger; private final Optional statusService; - public Hdf5DataSetReader(String filename, String dataset, String axesorder, LogService logService, StatusService statusService) { + public Hdf5DataSetReader(String filename, String dataset, String axesorder, Logger logger, StatusService statusService) { this.filename = filename; this.dataset = dataset; this.axesorder = axesorder; - this.logService = logService; + this.logger = logger; this.statusService = Optional.ofNullable(statusService); } @@ -51,7 +51,7 @@ public ImgPlus read() { try (IHDF5Reader reader = HDF5Factory.openForReading(filename)) { HDF5DataSetInformation dsInfo = reader.object().getDataSetInformation(dataset); Hdf5DataSetConfig dsConfig = new Hdf5DataSetConfig(dsInfo, axesorder); - logService.info(String.format("Found dataset '%s' of type '%s'", dataset, dsConfig.typeInfo)); + logger.info(String.format("Found dataset '%s' of type '%s'", dataset, dsConfig.typeInfo)); // construct output image final long[] dims = {dsConfig.dimX, dsConfig.dimY, dsConfig.numChannels, dsConfig.dimZ, dsConfig.numFrames}; @@ -60,7 +60,7 @@ public ImgPlus read() { .mapToObj(String::valueOf) .collect(Collectors.joining(", ")); - logService.info(String.format("Constructing output image of shape (%s). Axis order: 'XYCZT'", strDims)); + logger.info(String.format("Constructing output image of shape (%s). Axis order: 'XYCZT'", strDims)); final T type = Hdf5Utils.getNativeType(dsConfig.typeInfo); diff --git a/src/main/java/org/ilastik/ilastik4ij/hdf5/Hdf5DataSetWriter.java b/src/main/java/org/ilastik/ilastik4ij/hdf5/Hdf5DataSetWriter.java index 6beb694b..a743ab0d 100644 --- a/src/main/java/org/ilastik/ilastik4ij/hdf5/Hdf5DataSetWriter.java +++ b/src/main/java/org/ilastik/ilastik4ij/hdf5/Hdf5DataSetWriter.java @@ -18,7 +18,7 @@ import net.imglib2.type.numeric.real.FloatType; import org.ilastik.ilastik4ij.util.Hdf5Utils; import org.scijava.app.StatusService; -import org.scijava.log.LogService; +import org.scijava.log.Logger; import java.util.Arrays; import java.util.Optional; @@ -33,13 +33,13 @@ public class Hdf5DataSetWriter> { private final int dimZ; private final int dimY; private final int dimX; - private final LogService logService; + private final Logger logger; private final Optional statusService; private final String filename; private final String dataset; private final int compressionLevel; - public Hdf5DataSetWriter(ImgPlus image, String filename, String dataset, int compressionLevel, LogService logService, StatusService statusService) { + public Hdf5DataSetWriter(ImgPlus image, String filename, String dataset, int compressionLevel, Logger logger, StatusService statusService) { this.image = image; this.numFrames = getDimension(image, Axes.TIME); this.numChannels = getDimension(image, Axes.CHANNEL); @@ -49,7 +49,7 @@ public Hdf5DataSetWriter(ImgPlus image, String filename, String dataset, int this.filename = filename; this.dataset = dataset; this.compressionLevel = compressionLevel; - this.logService = logService; + this.logger = logger; this.statusService = Optional.ofNullable(statusService); } @@ -59,7 +59,7 @@ public void write() { String shape = Arrays.stream(dims) .mapToObj(String::valueOf) .collect(Collectors.joining(", ")); - logService.info(String.format("Exporting image of shape (%s). Axis order: 'TZYXC'", shape)); + logger.info(String.format("Exporting image of shape (%s). Axis order: 'TZYXC'", shape)); try (IHDF5Writer writer = HDF5Factory.open(filename)) { T val = image.firstElement(); @@ -82,7 +82,7 @@ public void write() { } private void write(IHDF5Writer writer, long[] datasetDims, Class pixelClass) { - logService.info(String.format("Saving as '%s'. Compression level: %d", Hdf5Utils.getDtype(pixelClass), compressionLevel)); + logger.info(String.format("Saving as '%s'. Compression level: %d", Hdf5Utils.getDtype(pixelClass), compressionLevel)); final int totalCheckpoints = numFrames * numChannels * dimZ; final AtomicInteger checkpoint = new AtomicInteger(0); statusService.ifPresent(status -> status.showStatus(checkpoint.get(), totalCheckpoints, "Exporting HDF5...")); @@ -205,11 +205,11 @@ private void createMDArray(IHDF5Writer writer, long[] datasetDims, int[] blockSi } private void writeARGB(IHDF5Writer writer, long[] datasetDims) { - logService.info("Saving ARGB as 'uint8' (4 channels). Compression level: " + compressionLevel); + logger.info("Saving ARGB as 'uint8' (4 channels). Compression level: " + compressionLevel); boolean isAlphaChannelPresent = true; if (numChannels == ARGB_CHANNEL_NUM - 1) { - logService.warn("Only 3 channel RGB found. Setting ALPHA channel to -1 (transparent)."); + logger.warn("Only 3 channel RGB found. Setting ALPHA channel to -1 (transparent)."); isAlphaChannelPresent = false; datasetDims[4] = ARGB_CHANNEL_NUM; // set channel dimension to 4 explicitly } diff --git a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikAutoContextCommand.java b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikAutoContextCommand.java deleted file mode 100644 index 646bdbd3..00000000 --- a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikAutoContextCommand.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.ilastik.ilastik4ij.ui; - -import net.imagej.Dataset; -import net.imagej.ImgPlus; -import net.imglib2.type.NativeType; -import org.ilastik.ilastik4ij.executors.Autocontext; -import org.scijava.ItemIO; -import org.scijava.app.StatusService; -import org.scijava.command.Command; -import org.scijava.log.LogService; -import org.scijava.options.OptionsService; -import org.scijava.plugin.Parameter; -import org.scijava.plugin.Plugin; -import org.scijava.ui.UIService; - -import java.io.File; -import java.io.IOException; - -import static org.ilastik.ilastik4ij.executors.AbstractIlastikExecutor.PixelPredictionType; - -@Plugin(type = Command.class, headless = true, menuPath = "Plugins>ilastik>Run Autocontext Prediction") -public class IlastikAutoContextCommand implements Command { - - @Parameter - public LogService logService; - - @Parameter - public StatusService statusService; - - @Parameter - public OptionsService optionsService; - - @Parameter - public UIService uiService; - - @Parameter(label = "Trained ilastik project file") - public File projectFileName; - - @Parameter(label = "Raw input image") - public Dataset inputImage; - - @Parameter(label = "Output type", choices = {UiConstants.PIXEL_PREDICTION_TYPE_PROBABILITIES, UiConstants.PIXEL_PREDICTION_TYPE_SEGMENTATION}, style = "radioButtonHorizontal") - public String AutocontextPredictionType; - - @Parameter(type = ItemIO.OUTPUT) - private ImgPlus> predictions; - - public IlastikOptions ilastikOptions; - - /** - * Run method that calls ilastik - */ - @Override - public void run() { - - if (ilastikOptions == null) - ilastikOptions = optionsService.getOptions(IlastikOptions.class); - - try { - runClassification(); - } catch (IOException e) { - logService.error("Autocontext command failed", e); - throw new RuntimeException(e); - } - } - - private void runClassification() throws IOException { - final Autocontext AutocontextPrediction = new Autocontext(ilastikOptions.getExecutableFile(), - projectFileName, logService, statusService, ilastikOptions.getNumThreads(), ilastikOptions.getMaxRamMb()); - - PixelPredictionType pixelPredictionType = PixelPredictionType.valueOf(AutocontextPredictionType); - this.predictions = AutocontextPrediction.classifyPixels(inputImage.getImgPlus(), pixelPredictionType); - - // DisplayUtils.showOutput(uiService, predictions); - } -} diff --git a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikMulticutCommand.java b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikMulticutCommand.java deleted file mode 100644 index c4c818e6..00000000 --- a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikMulticutCommand.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.ilastik.ilastik4ij.ui; - -import net.imagej.Dataset; -import net.imagej.ImgPlus; -import net.imglib2.type.NativeType; -import org.ilastik.ilastik4ij.executors.Multicut; -import org.scijava.ItemIO; -import org.scijava.app.StatusService; -import org.scijava.command.Command; -import org.scijava.log.LogService; -import org.scijava.options.OptionsService; -import org.scijava.plugin.Parameter; -import org.scijava.plugin.Plugin; -import org.scijava.ui.UIService; - -import java.io.File; -import java.io.IOException; - -@Plugin(type = Command.class, headless = true, menuPath = "Plugins>ilastik>Run Multicut") -public class IlastikMulticutCommand implements Command { - - @Parameter - public LogService logService; - - @Parameter - public StatusService statusService; - - @Parameter - public OptionsService optionsService; - - @Parameter - public UIService uiService; - - @Parameter(label = "Trained ilastik project file") - public File projectFileName; - - @Parameter(label = "Raw input image") - public Dataset inputImage; - - @Parameter(label = "Boundary Prediction Image") - public Dataset boundaryPredictionImage; - - @Parameter(type = ItemIO.OUTPUT) - private ImgPlus> predictions; - - public IlastikOptions ilastikOptions; - - /** - * Run method that calls ilastik - */ - @Override - public void run() { - - if (ilastikOptions == null) - ilastikOptions = optionsService.getOptions(IlastikOptions.class); - - try { - runMulticut(); - } catch (IOException e) { - logService.error("Multicut command failed", e); - throw new RuntimeException(e); - } - } - - private void runMulticut() throws IOException { - final Multicut multicut = new Multicut(ilastikOptions.getExecutableFile(), projectFileName, logService, statusService, ilastikOptions.getNumThreads(), ilastikOptions.getMaxRamMb()); - - this.predictions = multicut.runMulticut(inputImage.getImgPlus(), boundaryPredictionImage.getImgPlus()); - } -} diff --git a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikObjectClassificationCommand.java b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikObjectClassificationCommand.java deleted file mode 100644 index 9ae3866e..00000000 --- a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikObjectClassificationCommand.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.ilastik.ilastik4ij.ui; - -import net.imagej.Dataset; -import net.imagej.ImgPlus; -import net.imglib2.type.NativeType; -import org.ilastik.ilastik4ij.executors.ObjectClassification; -import org.scijava.ItemIO; -import org.scijava.app.StatusService; -import org.scijava.command.Command; -import org.scijava.log.LogService; -import org.scijava.options.OptionsService; -import org.scijava.plugin.Parameter; -import org.scijava.plugin.Plugin; -import org.scijava.ui.UIService; - -import java.io.File; -import java.io.IOException; - -import static org.ilastik.ilastik4ij.executors.AbstractIlastikExecutor.PixelPredictionType; - -@Plugin(type = Command.class, headless = true, menuPath = "Plugins>ilastik>Run Object Classification Prediction") -public class IlastikObjectClassificationCommand implements Command { - - @Parameter - public LogService logService; - - @Parameter - public StatusService statusService; - - @Parameter - public OptionsService optionsService; - - @Parameter - public UIService uiService; - - @Parameter(label = "Trained ilastik project file") - public File projectFileName; - - @Parameter(label = "Raw input image") - public Dataset inputImage; - - @Parameter(label = "Pixel Probability or Segmentation image") - public Dataset inputProbOrSegImage; - - @Parameter(label = "Second Input Type", choices = {UiConstants.PIXEL_PREDICTION_TYPE_PROBABILITIES, UiConstants.PIXEL_PREDICTION_TYPE_SEGMENTATION}, style = "radioButtonHorizontal") - public String secondInputType = UiConstants.PIXEL_PREDICTION_TYPE_PROBABILITIES; - - @Parameter(type = ItemIO.OUTPUT) - private ImgPlus> predictions; - - public IlastikOptions ilastikOptions; - - /** - * Run method that calls ilastik - */ - @Override - public void run() { - - if (ilastikOptions == null) - ilastikOptions = optionsService.getOptions(IlastikOptions.class); - - try { - runClassification(); - } catch (IOException e) { - logService.error("Object classification command failed", e); - throw new RuntimeException(e); - } - } - - private void runClassification() throws IOException { - final ObjectClassification objectClassification = new ObjectClassification(ilastikOptions.getExecutableFile(), projectFileName, logService, statusService, ilastikOptions.getNumThreads(), ilastikOptions.getMaxRamMb()); - - final PixelPredictionType secondInputImageType = PixelPredictionType.valueOf(secondInputType); - - this.predictions = objectClassification.classifyObjects(inputImage.getImgPlus(), inputProbOrSegImage.getImgPlus(), secondInputImageType); - - // DisplayUtils.showOutput(uiService, predictions); - } -} diff --git a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikOptions.java b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikOptions.java index 86f2518e..da63082e 100644 --- a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikOptions.java +++ b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikOptions.java @@ -1,85 +1,41 @@ package org.ilastik.ilastik4ij.ui; +import org.scijava.module.MutableModuleItem; import org.scijava.options.OptionsPlugin; import org.scijava.plugin.Parameter; -import org.scijava.module.MutableModuleItem; import org.scijava.plugin.Plugin; import org.scijava.util.PlatformUtils; import org.scijava.widget.FileWidget; import java.io.File; - -/** - * The ilastik options let you configure where your ilastik installation is, - * and how many processors and RAM it is allowed to use. - *

- * Because of the way option plugins work in ImageJ, there is always just one instance - * of this class, that can be requested by every plugin to get these values of - * shared configuration. - */ @Plugin(type = OptionsPlugin.class, menuPath = "Plugins>ilastik>Configure ilastik executable location") -public class IlastikOptions extends OptionsPlugin { - private static final String ILASTIK_PATH_WIN = "C:\\\\Program Files\\ilastik-1.3.3\\ilastik.exe"; - private static final String ILASTIK_PATH_LINUX = "/opt/ilastik-1.3.3-Linux/run_ilastik.sh"; - private static final String ILASTIK_PATH_MACOS = "/Applications/ilastik-1.3.3-OSX.app/Contents/MacOS/ilastik"; - +public final class IlastikOptions extends OptionsPlugin { @Parameter(label = "Path to ilastik executable", style = FileWidget.OPEN_STYLE) - private File executableFile = new File("/opt/ilastik/run_ilastik.sh"); + public File executableFile = new File("/opt/ilastik/run_ilastik.sh"); @Parameter(label = "Number of Threads ilastik is allowed to use.\nNegative numbers means no restriction") - private int numThreads = -1; + public int numThreads = -1; @Parameter(min = "256", label = "Maximum amount of RAM (in MB) that ilastik is allowed to use.") - private int maxRamMb = 4096; + public int maxRamMb = 4096; - @Override - public void initialize() { - String executableLocation = null; + @Override + public void initialize() { + MutableModuleItem item = getInfo().getMutableInput("executableFile", File.class); + String path = ""; if (PlatformUtils.isWindows()) { - executableLocation = ILASTIK_PATH_WIN; + path = "C:\\\\Program Files\\ilastik-1.3.3\\ilastik.exe"; } else if (PlatformUtils.isLinux()) { - executableLocation = ILASTIK_PATH_LINUX; + path = "/opt/ilastik-1.3.3-Linux/run_ilastik.sh"; } else if (PlatformUtils.isMac()) { - executableLocation = ILASTIK_PATH_MACOS; - getExecutableFileItem().setWidgetStyle(FileWidget.DIRECTORY_STYLE); + path = "/Applications/ilastik-1.3.3-OSX.app/Contents/MacOS/ilastik"; + item.setWidgetStyle(FileWidget.DIRECTORY_STYLE); } - if (executableLocation != null) { - getExecutableFileItem().setLabel("Path to ilastik executable, e.g. " + executableLocation); + if (!path.isEmpty()) { + item.setLabel(item.getLabel() + ", e.g. " + path); } - } - - private final MutableModuleItem getExecutableFileItem(){ - final MutableModuleItem executableFileItem = getInfo().getMutableInput("executableFile", File.class); - return executableFileItem; - } - - public File getExecutableFile() { - load(); - return executableFile; - } - - public int getMaxRamMb() { - load(); - return maxRamMb; - } - - public int getNumThreads() { - load(); - return numThreads; - } - - public void setExecutableFile(File executableFile) { - this.executableFile = executableFile; - } - - public void setNumThreads(int numThreads) { - this.numThreads = numThreads; - } - - public void setMaxRamMb(int maxRamMb) { - this.maxRamMb = maxRamMb; } } diff --git a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java deleted file mode 100644 index 02943d57..00000000 --- a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.ilastik.ilastik4ij.ui; - -import net.imagej.Dataset; -import net.imagej.ImgPlus; -import net.imglib2.type.NativeType; -import org.ilastik.ilastik4ij.executors.PixelClassification; -import org.scijava.ItemIO; -import org.scijava.app.StatusService; -import org.scijava.command.Command; -import org.scijava.log.LogService; -import org.scijava.options.OptionsService; -import org.scijava.plugin.Parameter; -import org.scijava.plugin.Plugin; -import org.scijava.ui.UIService; - -import java.io.File; -import java.io.IOException; - -import static org.ilastik.ilastik4ij.executors.AbstractIlastikExecutor.PixelPredictionType; - -@Plugin(type = Command.class, headless = true, menuPath = "Plugins>ilastik>Run Pixel Classification Prediction") -public class IlastikPixelClassificationCommand implements Command { - - @Parameter - public LogService logService; - - @Parameter - public StatusService statusService; - - @Parameter - public OptionsService optionsService; - - @Parameter - public UIService uiService; - - @Parameter(label = "Trained ilastik project file") - public File projectFileName; - - @Parameter(label = "Raw input image") - public Dataset inputImage; - - @Parameter(label = "Output type", choices = {UiConstants.PIXEL_PREDICTION_TYPE_PROBABILITIES, UiConstants.PIXEL_PREDICTION_TYPE_SEGMENTATION}, style = "radioButtonHorizontal") - public String pixelClassificationType; - - @Parameter(type = ItemIO.OUTPUT) - private ImgPlus> predictions; - - public IlastikOptions ilastikOptions; - - /** - * Run method that calls ilastik - */ - @Override - public void run() { - - if (ilastikOptions == null) - ilastikOptions = optionsService.getOptions(IlastikOptions.class); - - try { - runClassification(); - } catch (IOException e) { - logService.error("Pixel classification command failed", e); - throw new RuntimeException(e); - } - } - - private void runClassification() throws IOException { - final PixelClassification pixelClassification = new PixelClassification(ilastikOptions.getExecutableFile(), - projectFileName, logService, statusService, ilastikOptions.getNumThreads(), ilastikOptions.getMaxRamMb()); - - PixelPredictionType pixelPredictionType = PixelPredictionType.valueOf(pixelClassificationType); - this.predictions = pixelClassification.classifyPixels(inputImage.getImgPlus(), pixelPredictionType); - - // DisplayUtils.showOutput(uiService, predictions); - } -} diff --git a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikTrackingCommand.java b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikTrackingCommand.java deleted file mode 100644 index 0a2561b1..00000000 --- a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikTrackingCommand.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.ilastik.ilastik4ij.ui; - -import net.imagej.Dataset; -import net.imagej.ImgPlus; -import net.imglib2.type.NativeType; -import org.ilastik.ilastik4ij.executors.Tracking; -import org.scijava.ItemIO; -import org.scijava.app.StatusService; -import org.scijava.command.Command; -import org.scijava.log.LogService; -import org.scijava.options.OptionsService; -import org.scijava.plugin.Parameter; -import org.scijava.plugin.Plugin; -import org.scijava.ui.UIService; - -import java.io.File; -import java.io.IOException; - -import static org.ilastik.ilastik4ij.executors.AbstractIlastikExecutor.PixelPredictionType; - -@Plugin(type = Command.class, headless = true, menuPath = "Plugins>ilastik>Run Tracking") -public class IlastikTrackingCommand implements Command { - - @Parameter - public LogService logService; - - @Parameter - public StatusService statusService; - - @Parameter - public OptionsService optionsService; - - @Parameter - public UIService uiService; - - @Parameter(label = "Trained ilastik project file") - public File projectFileName; - - @Parameter(label = "Raw input image") - public Dataset inputImage; - - @Parameter(label = "Pixel Probability or Segmentation image") - public Dataset inputProbOrSegImage; - - @Parameter(label = "Second Input Type", choices = {UiConstants.PIXEL_PREDICTION_TYPE_PROBABILITIES, UiConstants.PIXEL_PREDICTION_TYPE_SEGMENTATION}, style = "radioButtonHorizontal") - public String secondInputType = UiConstants.PIXEL_PREDICTION_TYPE_PROBABILITIES; - - @Parameter(type = ItemIO.OUTPUT) - private ImgPlus> predictions; - - public IlastikOptions ilastikOptions; - - /** - * Run method that calls ilastik - */ - @Override - public void run() { - - if (ilastikOptions == null) - ilastikOptions = optionsService.getOptions(IlastikOptions.class); - - try { - runTracking(); - } catch (IOException e) { - logService.error("Tracking command failed", e); - throw new RuntimeException(e); - } - } - - private void runTracking() throws IOException { - final Tracking tracking = new Tracking(ilastikOptions.getExecutableFile(), projectFileName, logService, - statusService, ilastikOptions.getNumThreads(), ilastikOptions.getMaxRamMb()); - - this.predictions = tracking.trackObjects(inputImage.getImgPlus(), inputProbOrSegImage.getImgPlus(), - PixelPredictionType.valueOf(secondInputType)); - - // DisplayUtils.showOutput(uiService, predictions); - } -} diff --git a/src/main/java/org/ilastik/ilastik4ij/ui/UiConstants.java b/src/main/java/org/ilastik/ilastik4ij/ui/UiConstants.java deleted file mode 100644 index d02e430d..00000000 --- a/src/main/java/org/ilastik/ilastik4ij/ui/UiConstants.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.ilastik.ilastik4ij.ui; - -public class UiConstants { - public static final String PIXEL_PREDICTION_TYPE_SEGMENTATION = "Segmentation"; - public static final String PIXEL_PREDICTION_TYPE_PROBABILITIES = "Probabilities"; -} diff --git a/src/main/java/org/ilastik/ilastik4ij/util/IOUtils.java b/src/main/java/org/ilastik/ilastik4ij/util/IOUtils.java deleted file mode 100644 index da82cb76..00000000 --- a/src/main/java/org/ilastik/ilastik4ij/util/IOUtils.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.ilastik.ilastik4ij.util; - -import org.scijava.log.LogService; - -import java.io.*; -import java.nio.charset.Charset; - -public class IOUtils { - public static String getTemporaryFileName(String extension) throws IOException { - File tmpFile = File.createTempFile("ilastik4j", extension, null); - try { - return tmpFile.getAbsolutePath(); - } finally { - tmpFile.delete(); - } - } - - public static void redirectOutputToLogService(final InputStream in, final LogService logService, final Boolean isErrorStream) { - new Thread(() -> { - - String line; - - try (BufferedReader bis = new BufferedReader(new InputStreamReader(in, Charset.defaultCharset()))) { - while ((line = bis.readLine()) != null) { - if (isErrorStream) { - logService.error(line); - } else { - logService.info(line); - } - } - } catch (IOException e) { - throw new RuntimeException("Could not read ilastik output", e); - } - }).start(); - } -} diff --git a/src/main/java/org/ilastik/ilastik4ij/workflow/AutocontextCommand.java b/src/main/java/org/ilastik/ilastik4ij/workflow/AutocontextCommand.java new file mode 100644 index 00000000..4c84e62a --- /dev/null +++ b/src/main/java/org/ilastik/ilastik4ij/workflow/AutocontextCommand.java @@ -0,0 +1,26 @@ +package org.ilastik.ilastik4ij.workflow; + +import net.imglib2.type.NativeType; +import org.scijava.command.Command; +import org.scijava.plugin.Parameter; +import org.scijava.plugin.Plugin; + +import java.util.Collections; +import java.util.List; + +@Plugin(type = Command.class, headless = true, menuPath = "Plugins>ilastik>Run Autocontext Prediction") +public final class AutocontextCommand> extends WorkflowCommand { + @Parameter(label = "Output type", choices = {ROLE_PROBABILITIES, ROLE_SEGMENTATION}, style = "radioButtonHorizontal") + public String AutocontextPredictionType; + + @Override + protected List workflowArgs() { + if (ROLE_PROBABILITIES.equals(AutocontextPredictionType)) { + return Collections.singletonList("--export_source=Probabilities Stage 2"); + } + if (ROLE_SEGMENTATION.equals(AutocontextPredictionType)) { + return Collections.singletonList("--export_source=Simple Segmentation Stage 2"); + } + throw new IllegalStateException("Unexpected value: " + AutocontextPredictionType); + } +} diff --git a/src/main/java/org/ilastik/ilastik4ij/workflow/MulticutCommand.java b/src/main/java/org/ilastik/ilastik4ij/workflow/MulticutCommand.java new file mode 100644 index 00000000..86f65f3f --- /dev/null +++ b/src/main/java/org/ilastik/ilastik4ij/workflow/MulticutCommand.java @@ -0,0 +1,27 @@ +package org.ilastik.ilastik4ij.workflow; + +import net.imagej.Dataset; +import net.imglib2.type.NativeType; +import org.scijava.command.Command; +import org.scijava.plugin.Parameter; +import org.scijava.plugin.Plugin; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +@Plugin(type = Command.class, headless = true, menuPath = "Plugins>ilastik>Run Multicut") +public final class MulticutCommand> extends WorkflowCommand { + @Parameter(label = "Boundary Prediction Image") + public Dataset boundaryPredictionImage; + + @Override + protected List workflowArgs() { + return Collections.singletonList("--export_source=Multicut Segmentation"); + } + + @Override + protected Map workflowInputs() { + return Collections.singletonMap("probabilities", boundaryPredictionImage); + } +} diff --git a/src/main/java/org/ilastik/ilastik4ij/workflow/ObjectClassificationCommand.java b/src/main/java/org/ilastik/ilastik4ij/workflow/ObjectClassificationCommand.java new file mode 100644 index 00000000..22371100 --- /dev/null +++ b/src/main/java/org/ilastik/ilastik4ij/workflow/ObjectClassificationCommand.java @@ -0,0 +1,30 @@ +package org.ilastik.ilastik4ij.workflow; + +import net.imagej.Dataset; +import net.imglib2.type.NativeType; +import org.scijava.command.Command; +import org.scijava.plugin.Parameter; +import org.scijava.plugin.Plugin; + +import java.util.Collections; +import java.util.Map; + +@Plugin(type = Command.class, headless = true, menuPath = "Plugins>ilastik>Run Object Classification Prediction") +public final class ObjectClassificationCommand> extends WorkflowCommand { + @Parameter(label = "Pixel Probability or Segmentation image") + public Dataset inputProbOrSegImage; + + @Parameter(label = "Second Input Type", choices = {ROLE_PROBABILITIES, ROLE_SEGMENTATION}, style = "radioButtonHorizontal") + public String secondInputType = ROLE_PROBABILITIES; + + @Override + protected Map workflowInputs() { + if (ROLE_PROBABILITIES.equals(secondInputType)) { + return Collections.singletonMap("prediction_maps", inputProbOrSegImage); + } + if (ROLE_SEGMENTATION.equals(secondInputType)) { + return Collections.singletonMap("segmentation_image", inputProbOrSegImage); + } + throw new IllegalStateException("Unexpected value: " + secondInputType); + } +} diff --git a/src/main/java/org/ilastik/ilastik4ij/workflow/PixelClassificationCommand.java b/src/main/java/org/ilastik/ilastik4ij/workflow/PixelClassificationCommand.java new file mode 100644 index 00000000..9d9e989c --- /dev/null +++ b/src/main/java/org/ilastik/ilastik4ij/workflow/PixelClassificationCommand.java @@ -0,0 +1,26 @@ +package org.ilastik.ilastik4ij.workflow; + +import net.imglib2.type.NativeType; +import org.scijava.command.Command; +import org.scijava.plugin.Parameter; +import org.scijava.plugin.Plugin; + +import java.util.Collections; +import java.util.List; + +@Plugin(type = Command.class, headless = true, menuPath = "Plugins>ilastik>Run Pixel Classification Prediction") +public final class PixelClassificationCommand> extends WorkflowCommand { + @Parameter(label = "Output type", choices = {ROLE_PROBABILITIES, ROLE_SEGMENTATION}, style = "radioButtonHorizontal") + public String pixelClassificationType; + + @Override + protected List workflowArgs() { + if (ROLE_PROBABILITIES.equals(pixelClassificationType)) { + return Collections.singletonList("--export_source=Probabilities"); + } + if (ROLE_SEGMENTATION.equals(pixelClassificationType)) { + return Collections.singletonList("--export_source=Simple Segmentation"); + } + throw new IllegalStateException("Unexpected value: " + pixelClassificationType); + } +} diff --git a/src/main/java/org/ilastik/ilastik4ij/workflow/TrackingCommand.java b/src/main/java/org/ilastik/ilastik4ij/workflow/TrackingCommand.java new file mode 100644 index 00000000..1742e70a --- /dev/null +++ b/src/main/java/org/ilastik/ilastik4ij/workflow/TrackingCommand.java @@ -0,0 +1,36 @@ +package org.ilastik.ilastik4ij.workflow; + +import net.imagej.Dataset; +import net.imglib2.type.NativeType; +import org.scijava.command.Command; +import org.scijava.plugin.Parameter; +import org.scijava.plugin.Plugin; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +@Plugin(type = Command.class, headless = true, menuPath = "Plugins>ilastik>Run Tracking") +public final class TrackingCommand> extends WorkflowCommand { + @Parameter(label = "Pixel Probability or Segmentation image") + public Dataset inputProbOrSegImage; + + @Parameter(label = "Second Input Type", choices = {ROLE_PROBABILITIES, ROLE_SEGMENTATION}, style = "radioButtonHorizontal") + public String secondInputType = ROLE_PROBABILITIES; + + @Override + protected List workflowArgs() { + return Collections.singletonList("--export_source=Tracking-Result"); + } + + @Override + protected Map workflowInputs() { + if (ROLE_PROBABILITIES.equals(secondInputType)) { + return Collections.singletonMap("prediction_maps", inputProbOrSegImage); + } + if (ROLE_SEGMENTATION.equals(secondInputType)) { + return Collections.singletonMap("binary_image", inputProbOrSegImage); + } + throw new IllegalStateException("Unexpected value: " + secondInputType); + } +} diff --git a/src/main/java/org/ilastik/ilastik4ij/workflow/WorkflowCommand.java b/src/main/java/org/ilastik/ilastik4ij/workflow/WorkflowCommand.java new file mode 100644 index 00000000..691e121f --- /dev/null +++ b/src/main/java/org/ilastik/ilastik4ij/workflow/WorkflowCommand.java @@ -0,0 +1,282 @@ +package org.ilastik.ilastik4ij.workflow; + +import net.imagej.Dataset; +import net.imagej.ImgPlus; +import net.imglib2.type.NativeType; +import org.ilastik.ilastik4ij.hdf5.Hdf5DataSetReader; +import org.ilastik.ilastik4ij.hdf5.Hdf5DataSetWriter; +import org.ilastik.ilastik4ij.ui.IlastikOptions; +import org.scijava.ItemIO; +import org.scijava.app.StatusService; +import org.scijava.command.ContextCommand; +import org.scijava.log.LogLevel; +import org.scijava.log.LogService; +import org.scijava.log.Logger; +import org.scijava.options.OptionsService; +import org.scijava.plugin.Parameter; +import org.scijava.util.PlatformUtils; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.*; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +/** + * Base class for all commands that run ilastik in a subprocess. + *

+ * Instead of {@link #run()}, derived classes can implement {@link #workflowArgs()} + * and {@link #workflowInputs()}, which are used to build workflow-specific + * command-line arguments. + *

+ * Subclasses should be named as {@code [MyWorkflowName]Command} + * because workflow names are derived from their corresponding class names. + */ +public abstract class WorkflowCommand> extends ContextCommand { + public static final String ROLE_PROBABILITIES = "Probabilities"; + public static final String ROLE_SEGMENTATION = "Segmentation"; + + private static final String SPINNER_CHARS = "|/-\\"; + + @Parameter + private LogService logService; + + @Parameter + private StatusService statusService; + + @Parameter + private OptionsService optionsService; + + @Parameter(label = "Trained ilastik project file") + public File projectFileName; + + @Parameter(label = "Raw input image") + public Dataset inputImage; + + @Parameter(type = ItemIO.OUTPUT) + public ImgPlus predictions; + + private Logger logger; + private ScheduledExecutorService spinnerPool; + + /** + * Workflow-specific command-line arguments. + */ + protected List workflowArgs() { + return Collections.emptyList(); + } + + /** + * Workflow-specific input datasets. + *

+ * Keys are corresponding command-line arguments without leading dashes. + * Values are input datasets. + */ + protected Map workflowInputs() { + return Collections.emptyMap(); + } + + @Override + public final void run() { + logger = logService.subLogger(getClass().getCanonicalName(), LogLevel.INFO); + spinnerPool = Executors.newScheduledThreadPool(1); + + try { + try (TempDir temp = new TempDir("ilastik4ij_")) { + runWithTempDirs( + Files.createDirectory(temp.dir.resolve("inputs")), + Files.createDirectory(temp.dir.resolve("outputs"))); + } + + } catch (Exception e) { + statusService.clearStatus(); + logger.error("ilastik run failed", e); + throw new RuntimeException(e); + } + } + + /** + * Write inputs, run subprocess, read outputs. + */ + private void runWithTempDirs(Path inputDir, Path outputDir) throws Exception { + ProcessBuilder pb = initialProcessBuilder(); + + Map inputs = new HashMap<>(workflowInputs()); + inputs.put("raw_data", inputImage); + pb.command().addAll(writeInputs(inputs, inputDir)); + + Path outputPath = outputDir.resolve("predictions.h5"); + pb.command().add("--output_filename_format=" + outputPath); + + runSubprocess(pb); + + logger.info(String.format("read from %s starting", outputPath)); + try (Spinner ignored = new Spinner("Reading predictions")) { + predictions = new Hdf5DataSetReader( + outputPath.toString(), "exported_data", "tzyxc", logger, statusService).read(); + } + logger.info("read finished"); + } + + /** + * Create {@link ProcessBuilder} with configured command-line arguments and environment variables, + * but without input and output parameters. + */ + private ProcessBuilder initialProcessBuilder() { + IlastikOptions opts = optionsService.getOptions(IlastikOptions.class); + opts.load(); + + Path exe = opts.executableFile.toPath().toAbsolutePath(); + if (PlatformUtils.isMac() && exe.toString().endsWith(".app")) { + // On macOS, we need an actual executable path, not an app bundle path. + exe = exe.resolve("Contents").resolve("MacOS").resolve("ilastik"); + } + + ProcessBuilder pb = new ProcessBuilder(); + pb.command(exe.toString(), + "--headless", + "--project=" + projectFileName.getAbsolutePath(), + "--output_format=hdf5", + "--output_axis_order=tzyxc", + "--input_axes=tzyxc", + "--readonly=1", + "--output_internal_path=exported_data"); + pb.command().addAll(workflowArgs()); + + Map env = pb.environment(); + if (opts.numThreads >= 0) { + env.put("LAZYFLOW_THREADS", String.valueOf(opts.numThreads)); + } + env.put("LAZYFLOW_TOTAL_RAM_MB", String.valueOf(opts.maxRamMb)); + env.put("LANG", "en_US.UTF-8"); + env.put("LC_ALL", "en_US.UTF-8"); + env.put("LC_CTYPE", "en_US.UTF-8"); + + return pb; + } + + /** + * Write inputs into temporary files, return command-line arguments for these inputs. + */ + private List writeInputs(Map inputs, Path dir) { + // We need a counter, but the lambda below requires an immutable reference. + final int[] progress = {0}; + + return inputs.keySet().stream().map(name -> { + @SuppressWarnings("unchecked") + ImgPlus img = (ImgPlus) inputs.get(name).getImgPlus(); + Path path = dir.resolve(name + ".h5"); + + logger.info(String.format("write %s to %s starting", name, path)); + statusService.showStatus(progress[0]++, inputs.size(), "Writing inputs"); + + new Hdf5DataSetWriter<>(img, path.toString(), "data", 1, logger, statusService).write(); + + logger.info("write finished"); + return String.format("--%s=%s", name, path); + + }).collect(Collectors.toList()); + } + + /** + * Run ilastik subprocess and wait for it to finish. + */ + private void runSubprocess(ProcessBuilder pb) throws Exception { + logger.info(String.format( + "ilastik run starting with arguments %s and environment %s", + pb.command(), + pb.environment())); + + Process process = pb.start(); + redirectInputStream(process.getInputStream(), logger::info); + // ilastik prints warnings to stderr, but we don't want them to be displayed as errors. + // Fatal errors should result in a non-zero exit code. + redirectInputStream(process.getErrorStream(), logger::info); + + // Get "My Workflow" string from a MyWorkflowCommand class. + String[] words = getClass().getSimpleName().split("(?=\\p{Lu})"); + String workflowName = String.join(" ", Arrays.copyOfRange(words, 0, words.length - 1)); + + try (Spinner ignored = new Spinner("Running " + workflowName)) { + if (process.waitFor() != 0) { + throw new RuntimeException("exit code " + process.exitValue()); + } + } + + logger.info("ilastik run finished"); + } + + /** + * During try-with-resources block, show message with a ticking spinner in the status bar. + */ + private final class Spinner implements AutoCloseable { + private final String message; + private final ScheduledFuture future; + private int i = 0; + + public Spinner(String message) { + this.message = message; + future = spinnerPool.scheduleWithFixedDelay(this::update, 0, 300, TimeUnit.MILLISECONDS); + } + + @Override + public void close() { + future.cancel(true); + } + + private void update() { + statusService.showStatus(message + " " + SPINNER_CHARS.charAt(i)); + i = (i + 1) % SPINNER_CHARS.length(); + } + } + + /** + * Start {@link Thread} that reads UTF-8 lines from source and sends them to destination. + */ + private static void redirectInputStream(InputStream src, Consumer dst) { + new Thread(() -> { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(src, StandardCharsets.UTF_8))) { + reader.lines().forEachOrdered(dst); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }).start(); + } + + /** + * Create temporary directory that exists within a try-with-resources block. + */ + private static final class TempDir implements AutoCloseable { + public final Path dir; + + public TempDir(String prefix) throws IOException { + dir = Files.createTempDirectory(prefix); + } + + @Override + public void close() throws IOException { + Files.walkFileTree(dir, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + }); + } + } +} diff --git a/src/test/java/org/ilastik/ilastik4ij/Demo.java b/src/test/java/org/ilastik/ilastik4ij/Demo.java new file mode 100644 index 00000000..3dd246f0 --- /dev/null +++ b/src/test/java/org/ilastik/ilastik4ij/Demo.java @@ -0,0 +1,81 @@ +package org.ilastik.ilastik4ij; + +import net.imagej.Dataset; +import net.imagej.ImageJ; +import org.ilastik.ilastik4ij.workflow.ObjectClassificationCommand; +import org.ilastik.ilastik4ij.workflow.PixelClassificationCommand; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.Objects; + +public final class Demo { + public static void main(String[] args) throws Exception { + ImageJ ij = new ImageJ(); + ij.ui().showUI(); + + // If you want to change ilastik options, e.g. executable path: + // IlastikOptions opts = ij.options().getOptions(IlastikOptions.class); + // opts.load(); + // opts.executableFile = new File("/path/to/your/local/ilastik/executable"); + // opts.save(); + + pixelClassification(ij); + objectClassification(ij); + } + + @SuppressWarnings("unused") + private static void pixelClassification(ImageJ ij) throws Exception { + Dataset inputImage = ij.scifio().datasetIO().open(fromResource("/2d_cells_apoptotic.tif")); + ij.ui().show(inputImage); + + PixelClassificationCommand cmd = new PixelClassificationCommand<>(); + cmd.setContext(ij.context()); + cmd.projectFileName = new File(fromResource("/pixel_class_2d_cells_apoptotic.ilp")); + cmd.inputImage = inputImage; + cmd.pixelClassificationType = PixelClassificationCommand.ROLE_SEGMENTATION; + cmd.run(); + + ij.ui().show(cmd.predictions); + } + + @SuppressWarnings("unused") + private static void objectClassification(ImageJ ij) throws Exception { + Dataset inputImage = ij.scifio().datasetIO().open(fromResource("/2d_cells_apoptotic.tif")); + ij.ui().show(inputImage); + + Dataset inputProbImage = ij.scifio().datasetIO().open(fromResource("/2d_cells_apoptotic_1channel-data_Probabilities.tif")); + ij.ui().show(inputProbImage); + + ObjectClassificationCommand cmd = new ObjectClassificationCommand<>(); + cmd.setContext(ij.context()); + cmd.projectFileName = new File(fromResource("/obj_class_2d_cells_apoptotic.ilp")); + cmd.inputImage = inputImage; + cmd.inputProbOrSegImage = inputProbImage; + cmd.secondInputType = ObjectClassificationCommand.ROLE_PROBABILITIES; + cmd.run(); + + ij.ui().show(cmd.predictions); + } + + /** + * Copy resource to a temporary file and return the file path. + *

+ * This function is used here just for the demonstration purposes. + * If you read files from a local disk, just use the path directly. + */ + private static String fromResource(String resourcePath) throws IOException { + Path target = Files.createTempFile("", resourcePath.replace('/', '-')); + try (InputStream in = Demo.class.getResourceAsStream(resourcePath)) { + Files.copy(Objects.requireNonNull(in), target, StandardCopyOption.REPLACE_EXISTING); + } + return target.toString(); + } + + private Demo() { + } +} diff --git a/src/test/java/org/ilastik/ilastik4ij/ObjectClassificationDemo.java b/src/test/java/org/ilastik/ilastik4ij/ObjectClassificationDemo.java deleted file mode 100644 index b2389928..00000000 --- a/src/test/java/org/ilastik/ilastik4ij/ObjectClassificationDemo.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.ilastik.ilastik4ij; - -import net.imagej.Dataset; -import net.imagej.ImageJ; -import net.imagej.ImgPlus; -import net.imglib2.img.display.imagej.ImageJFunctions; -import net.imglib2.type.NativeType; -import net.imglib2.type.numeric.RealType; -import org.ilastik.ilastik4ij.executors.ObjectClassification; -import org.ilastik.ilastik4ij.util.IOUtils; -import org.scijava.io.location.BytesLocation; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - -import static org.ilastik.ilastik4ij.executors.AbstractIlastikExecutor.PixelPredictionType; - -public class ObjectClassificationDemo { - public static & NativeType> void main(String[] args) throws IOException { - final String ilastikPath = "/opt/ilastik-1.3.3post1-Linux/run_ilastik.sh"; - final String inputImagePath = "/2d_cells_apoptotic.tif"; - final String inputProbabMaps = "/2d_cells_apoptotic_1channel-data_Probabilities.tif"; - final String ilastikProjectPath = "/obj_class_2d_cells_apoptotic.ilp"; - - // Open ImageJ - // - final ImageJ ij = new ImageJ(); - ij.ui().showUI(); - - - // Open input image - // - final InputStream inputFileStream = PixelClassificationDemo.class.getResourceAsStream(inputImagePath); - final ByteBuffer bb1 = ByteBuffer.allocate(inputFileStream.available()); - while (inputFileStream.available() > 0) { - bb1.put((byte) inputFileStream.read()); - } - final Dataset inputDataset = ij.scifio().datasetIO().open( - new BytesLocation(bb1.array(), "rawInputFile")); - - // Open pmaps image - // - final InputStream pmapsFileStream = PixelClassificationDemo.class.getResourceAsStream(inputProbabMaps); - final ByteBuffer bb2 = ByteBuffer.allocate(pmapsFileStream.available()); - while (pmapsFileStream.available() > 0) { - bb2.put((byte) pmapsFileStream.read()); - } - final Dataset pmapsDataset = ij.scifio().datasetIO().open( - new BytesLocation(bb2.array(), "pmapsFile")); - - ij.ui().show(inputDataset); - - // Copy project file to tmp - // - InputStream projectFileStream = PixelClassificationDemo.class.getResourceAsStream(ilastikProjectPath); - Path tmpIlastikProjectFile = Paths.get(IOUtils.getTemporaryFileName("obj_class.ilp")); - Files.copy(projectFileStream, tmpIlastikProjectFile); - - // Classify objects - // - final ObjectClassification prediction = new ObjectClassification( - new File(ilastikPath), - tmpIlastikProjectFile.toFile(), - ij.log(), - ij.status(), - 4, - 1024 - ); - - final ImgPlus classifiedObjects = prediction.classifyObjects(inputDataset.getImgPlus(), pmapsDataset.getImgPlus(), - PixelPredictionType.Probabilities); - - ImageJFunctions.show(classifiedObjects, "Classified objects"); - } - -} diff --git a/src/test/java/org/ilastik/ilastik4ij/PixelClassificationCommandDemo.java b/src/test/java/org/ilastik/ilastik4ij/PixelClassificationCommandDemo.java deleted file mode 100644 index bbdc6d3e..00000000 --- a/src/test/java/org/ilastik/ilastik4ij/PixelClassificationCommandDemo.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.ilastik.ilastik4ij; - -import net.imagej.Dataset; -import net.imagej.ImageJ; -import org.ilastik.ilastik4ij.ui.IlastikOptions; -import org.ilastik.ilastik4ij.ui.IlastikPixelClassificationCommand; -import org.ilastik.ilastik4ij.ui.UiConstants; - -import java.io.File; -import java.io.IOException; - -public class PixelClassificationCommandDemo { - public static void main(String[] args) throws IOException { - // Configure paths - // - final String ilastikPath = "/opt/ilastik-1.3.3post1-Linux/run_ilastik.sh"; - final String inputImagePath = "/home/adrian/workspace/ilastik4ij/src/test/resources/2d_cells_apoptotic.tif"; - final String ilastikProjectPath = "/home/adrian/workspace/ilastik4ij/src/test/resources/pixel_class_2d_cells_apoptotic.ilp"; - - // Run test - // - final ImageJ ij = new ImageJ(); - ij.ui().showUI(); - - // Open input image - // - final Dataset inputDataset = ij.scifio().datasetIO().open(inputImagePath); - ij.ui().show(inputDataset); - - // Configure options - // - final IlastikOptions options = new IlastikOptions(); - options.setExecutableFile(new File(ilastikPath)); - - // Classify pixels - // - final IlastikPixelClassificationCommand command = new IlastikPixelClassificationCommand(); - command.logService = ij.log(); - command.statusService = ij.status(); - command.uiService = ij.ui(); - command.optionsService = ij.options(); - command.ilastikOptions = options; - command.pixelClassificationType = UiConstants.PIXEL_PREDICTION_TYPE_PROBABILITIES; - command.inputImage = inputDataset; - command.projectFileName = new File(ilastikProjectPath); - command.run(); - } -} diff --git a/src/test/java/org/ilastik/ilastik4ij/PixelClassificationDemo.java b/src/test/java/org/ilastik/ilastik4ij/PixelClassificationDemo.java deleted file mode 100644 index bbbc85fa..00000000 --- a/src/test/java/org/ilastik/ilastik4ij/PixelClassificationDemo.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.ilastik.ilastik4ij; - -import net.imagej.Dataset; -import net.imagej.ImageJ; -import net.imagej.ImgPlus; -import net.imglib2.img.display.imagej.ImageJFunctions; -import net.imglib2.type.NativeType; -import net.imglib2.type.numeric.RealType; -import org.ilastik.ilastik4ij.executors.AbstractIlastikExecutor.PixelPredictionType; -import org.ilastik.ilastik4ij.executors.PixelClassification; -import org.ilastik.ilastik4ij.util.IOUtils; -import org.scijava.io.location.BytesLocation; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - -public class PixelClassificationDemo { - public static & NativeType> void main(String[] args) throws IOException { - final String ilastikPath = "/opt/ilastik-1.3.3post1-Linux/run_ilastik.sh"; - final String inputImagePath = "/2d_cells_apoptotic.tif"; - final String ilastikProjectPath = "/pixel_class_2d_cells_apoptotic.ilp"; - - // Open ImageJ - // - final ImageJ ij = new ImageJ(); - ij.ui().showUI(); - - - // Open input image - // - InputStream inputFileStream = PixelClassificationDemo.class.getResourceAsStream(inputImagePath); - ByteBuffer byteBuffer = ByteBuffer.allocate(inputFileStream.available()); - while (inputFileStream.available() > 0) { - byteBuffer.put((byte) inputFileStream.read()); - } - final Dataset inputDataset = ij.scifio().datasetIO().open( - new BytesLocation(byteBuffer.array(), "rawInputFile")); - - ij.ui().show(inputDataset); - - // Copy project file to tmp - // - InputStream projectFileStream = PixelClassificationDemo.class.getResourceAsStream(ilastikProjectPath); - Path tmpIlastikProjectFile = Paths.get(IOUtils.getTemporaryFileName("pixel_class.ilp")); - Files.copy(projectFileStream, tmpIlastikProjectFile); - - // Classify pixels - // - final PixelClassification prediction = new PixelClassification( - new File(ilastikPath), - tmpIlastikProjectFile.toFile(), - ij.log(), - ij.status(), - 4, - 1024 - ); - - final ImgPlus classifiedPixels = prediction.classifyPixels(inputDataset.getImgPlus(), PixelPredictionType.Probabilities); - - ImageJFunctions.show(classifiedPixels, "Probability maps"); - } - -} diff --git a/src/test/java/org/ilastik/ilastik4ij/TrackingDemo.java b/src/test/java/org/ilastik/ilastik4ij/TrackingDemo.java deleted file mode 100644 index 4366f7bb..00000000 --- a/src/test/java/org/ilastik/ilastik4ij/TrackingDemo.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.ilastik.ilastik4ij; - -import net.imagej.ImageJ; -import net.imagej.ImgPlus; -import net.imglib2.img.display.imagej.ImageJFunctions; -import net.imglib2.type.NativeType; -import net.imglib2.type.numeric.RealType; -import org.ilastik.ilastik4ij.executors.AbstractIlastikExecutor; -import org.ilastik.ilastik4ij.executors.Tracking; -import org.ilastik.ilastik4ij.hdf5.Hdf5DataSetReader; - -import java.io.File; -import java.io.IOException; - -public class TrackingDemo { - public static & NativeType> void main(String[] args) throws IOException { - final String ilastikPath = "/opt/ilastik-1.3.3post1-Linux/run_ilastik.sh"; - final String ilastikProjectPath = "/home/adrian/MyProject_tracking.ilp"; - final String inputImagePath = "/home/adrian/workspace/ilastik-datasets/mitocheck_2d+t/mitocheck_94570_2D+t_01-53_5D.h5"; - final String inputProbabMaps = "/home/adrian/workspace/ilastik-datasets/mitocheck_2d+t/mitocheck_94570_2D+t_01-53_export_5D.h5"; - - // Open ImageJ - // - final ImageJ ij = new ImageJ(); - ij.ui().showUI(); - - - // Open input image - final ImgPlus rawImage = new Hdf5DataSetReader(inputImagePath, "data", - "tzyxc", ij.log(), ij.status()).read(); - - // Open pmaps image - // - final ImgPlus pmapsImage = new Hdf5DataSetReader(inputProbabMaps, "data", - "tzyxc", ij.log(), ij.status()).read(); - - ij.ui().show(rawImage); - - // Track objects - // - final Tracking prediction = new Tracking( - new File(ilastikPath), - new File(ilastikProjectPath), - ij.log(), - ij.status(), - 4, - 1024 - ); - - final ImgPlus classifiedObjects = prediction.trackObjects(rawImage, pmapsImage, - AbstractIlastikExecutor.PixelPredictionType.Probabilities); - - ImageJFunctions.show(classifiedObjects, "Tracked objects"); - } -}