From 5713a8614d74d7fa2ad011221dbfc94fb31a04a1 Mon Sep 17 00:00:00 2001 From: Tomaz Vieira Date: Wed, 29 Jul 2020 14:10:31 +0200 Subject: [PATCH 1/5] Implements Prediction Mask for Pixel Classification This still doesn;t work as a backwards compatible macro, since using pixel classification as a macro will require the value for 'useMask'. I could not find a way to have this extra parameter with a default value that would be picked up also by the macro usage. Either the input is marked as resolved and doesn't show up in the UI, or it is not marked as resolved, but requires the macro usage to specify the value interactively. --- .../executors/PixelClassification.java | 7 +++++- .../ui/IlastikPixelClassificationCommand.java | 24 +++++++++++++++++-- .../ilastik4ij/PixelClassificationDemo.java | 2 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/ilastik/ilastik4ij/executors/PixelClassification.java b/src/main/java/org/ilastik/ilastik4ij/executors/PixelClassification.java index 252f6b46..a592fbed 100644 --- a/src/main/java/org/ilastik/ilastik4ij/executors/PixelClassification.java +++ b/src/main/java/org/ilastik/ilastik4ij/executors/PixelClassification.java @@ -20,8 +20,9 @@ public PixelClassification(File executableFilePath, File projectFileName, LogSer } public > ImgPlus classifyPixels(ImgPlus> rawInputImg, + ImgPlus> predictionMask, PixelPredictionType pixelPredictionType) throws IOException { - return executeIlastik(rawInputImg, null, pixelPredictionType); + return executeIlastik(rawInputImg, predictionMask, pixelPredictionType); } @Override @@ -36,6 +37,10 @@ else if (pixelPredictionType == PixelPredictionType.Probabilities) { commandLine.add("--raw_data=" + tempFiles.get(rawInputTempFile)); commandLine.add("--output_filename_format=" + tempFiles.get(outputTempFile)); + if(tempFiles.containsKey(secondInputTempFile)){ + commandLine.add("--prediction_mask=" + tempFiles.get(secondInputTempFile)); + } + return commandLine; } } diff --git a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java index 02943d57..556ce0c0 100644 --- a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java +++ b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java @@ -7,6 +7,7 @@ import org.scijava.ItemIO; import org.scijava.app.StatusService; import org.scijava.command.Command; +import org.scijava.command.DynamicCommand; import org.scijava.log.LogService; import org.scijava.options.OptionsService; import org.scijava.plugin.Parameter; @@ -19,7 +20,7 @@ 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 { +public class IlastikPixelClassificationCommand extends DynamicCommand { @Parameter public LogService logService; @@ -42,6 +43,21 @@ public class IlastikPixelClassificationCommand implements Command { @Parameter(label = "Output type", choices = {UiConstants.PIXEL_PREDICTION_TYPE_PROBABILITIES, UiConstants.PIXEL_PREDICTION_TYPE_SEGMENTATION}, style = "radioButtonHorizontal") public String pixelClassificationType; + @Parameter(label = "Use Mask?", persist=false, initializer = "initUseMask") + public boolean useMask=false; + + protected void initUseMask(){ + useMask = false; + //resolveInput("useMask"); //this makes the input not be rendered -.- + } + + @Parameter( + label = "Prediction Mask", + required = false, + description = "An image with same dimensions as Raw Data, where the black pixels will be masked out of the predictions" + ) + public Dataset predictionMask; + @Parameter(type = ItemIO.OUTPUT) private ImgPlus> predictions; @@ -69,7 +85,11 @@ private void runClassification() throws IOException { projectFileName, logService, statusService, ilastikOptions.getNumThreads(), ilastikOptions.getMaxRamMb()); PixelPredictionType pixelPredictionType = PixelPredictionType.valueOf(pixelClassificationType); - this.predictions = pixelClassification.classifyPixels(inputImage.getImgPlus(), pixelPredictionType); + this.predictions = pixelClassification.classifyPixels( + inputImage.getImgPlus(), + useMask ? predictionMask.getImgPlus() : null, + pixelPredictionType + ); // DisplayUtils.showOutput(uiService, predictions); } diff --git a/src/test/java/org/ilastik/ilastik4ij/PixelClassificationDemo.java b/src/test/java/org/ilastik/ilastik4ij/PixelClassificationDemo.java index f742591f..e8e99462 100644 --- a/src/test/java/org/ilastik/ilastik4ij/PixelClassificationDemo.java +++ b/src/test/java/org/ilastik/ilastik4ij/PixelClassificationDemo.java @@ -62,7 +62,7 @@ public static & NativeType> void main(String[] args) t 1024 ); - final ImgPlus classifiedPixels = prediction.classifyPixels(inputDataset.getImgPlus(), PixelPredictionType.Probabilities); + final ImgPlus classifiedPixels = prediction.classifyPixels(inputDataset.getImgPlus(), null, PixelPredictionType.Probabilities); ImageJFunctions.show(classifiedPixels, "Probability maps"); } From 3a093f8a43f9e569e1d77de2545c875f3444e662 Mon Sep 17 00:00:00 2001 From: Maksim Novikov Date: Tue, 1 Sep 2020 18:27:41 +0200 Subject: [PATCH 2/5] Custom form for Ilastik Pixel Classification Command --- .../ui/IlastikPixelClassificationCommand.java | 71 ++++-- .../ui/IlastikPixelClassificationDialog.java | 238 ++++++++++++++++++ .../ui/IlastikPixelClassificationModel.java | 92 +++++++ .../PixelClassificationCommandDemo.java | 2 + 4 files changed, 389 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationDialog.java create mode 100644 src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationModel.java diff --git a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java index 556ce0c0..70419558 100644 --- a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java +++ b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java @@ -1,6 +1,9 @@ package org.ilastik.ilastik4ij.ui; +import ij.Macro; +import net.imagej.Data; import net.imagej.Dataset; +import net.imagej.DatasetService; import net.imagej.ImgPlus; import net.imglib2.type.NativeType; import org.ilastik.ilastik4ij.executors.PixelClassification; @@ -31,14 +34,19 @@ public class IlastikPixelClassificationCommand extends DynamicCommand { @Parameter public OptionsService optionsService; + @Parameter + public DatasetService datasetService; + @Parameter public UIService uiService; + @Parameter(label = "Raw input image") + public Dataset inputImage; + + /* @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; @@ -50,7 +58,10 @@ protected void initUseMask(){ useMask = false; //resolveInput("useMask"); //this makes the input not be rendered -.- } + */ + + /* @Parameter( label = "Prediction Mask", required = false, @@ -58,39 +69,71 @@ protected void initUseMask(){ ) public Dataset predictionMask; + */ @Parameter(type = ItemIO.OUTPUT) private ImgPlus> predictions; public IlastikOptions ilastikOptions; - /** - * Run method that calls ilastik - */ + private IlastikPixelClassificationModel createModel() { + IlastikPixelClassificationModel model = new IlastikPixelClassificationModel(logService); + model.setRawInput(inputImage); + + String macroOptions = Macro.getOptions(); + + if (macroOptions != null) { + String projectFilePath = Macro.getValue(macroOptions, "projectfilename", ""); + model.setIlastikProjectFile(new File(projectFilePath)); + model.setOutputType(Macro.getValue(macroOptions, "pixelclassificationtype", "")); + + String predictionMaskName = Macro.getValue(macroOptions, "predictionmask", ""); + for (Dataset ds : datasetService.getDatasets()) { + if (ds.getName().equals(predictionMaskName)) { + model.setPredictionMask(ds); + break; + } + } + } + + return model; + } + @Override public void run() { - if (ilastikOptions == null) ilastikOptions = optionsService.getOptions(IlastikOptions.class); + IlastikPixelClassificationModel model = createModel(); + IlastikPixelClassificationDialog dialog = new IlastikPixelClassificationDialog(logService, uiService, datasetService, model); + model.fireInitialProperties(); + dialog.setVisible(true); + if (dialog.wasCancelled()) { + return; + } + try { - runClassification(); + runClassification(model); } catch (IOException e) { logService.error("Pixel classification command failed", e); throw new RuntimeException(e); - } + } } - private void runClassification() throws IOException { + private void runClassification(IlastikPixelClassificationModel model) throws IOException { final PixelClassification pixelClassification = new PixelClassification(ilastikOptions.getExecutableFile(), - projectFileName, logService, statusService, ilastikOptions.getNumThreads(), ilastikOptions.getMaxRamMb()); + model.getIlastikProjectFile(), logService, statusService, ilastikOptions.getNumThreads(), ilastikOptions.getMaxRamMb()); + + PixelPredictionType pixelPredictionType = PixelPredictionType.valueOf(model.getOutputType()); + + ImgPlus predMaskImg = null; + if (model.getPredictionMask() != null) { + predMaskImg = model.getPredictionMask().getImgPlus(); + } - PixelPredictionType pixelPredictionType = PixelPredictionType.valueOf(pixelClassificationType); this.predictions = pixelClassification.classifyPixels( inputImage.getImgPlus(), - useMask ? predictionMask.getImgPlus() : null, + predMaskImg, pixelPredictionType ); - - // DisplayUtils.showOutput(uiService, predictions); } } diff --git a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationDialog.java b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationDialog.java new file mode 100644 index 00000000..411a3ea3 --- /dev/null +++ b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationDialog.java @@ -0,0 +1,238 @@ +package org.ilastik.ilastik4ij.ui; + +import net.imagej.Dataset; +import net.imagej.DatasetService; +import org.scijava.log.LogService; +import org.scijava.ui.UIService; +import org.scijava.widget.FileWidget; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; + +public class IlastikPixelClassificationDialog extends JDialog implements PropertyChangeListener { + private class DatasetComboboxEntry { + public final Dataset dataset; + public final String title; + + public DatasetComboboxEntry(String title, Dataset dataset) { + this.title = title; + this.dataset = dataset; + } + + @Override + public String toString() { + return this.title; + } + } + private class OutputTypeComboboxEntry { + public final String verboseName; + public final String type; + + public OutputTypeComboboxEntry(String verboseName, String type) { + this.verboseName = verboseName; + this.type = type; + } + + @Override + public String toString() { + return this.verboseName; + } + } + + private boolean cancelled = true; + private final LogService logService; + private final UIService uiService; + private final DatasetService datasetService; + private final IlastikPixelClassificationModel model; + + private final JPanel contentPanel = new JPanel(); + private final JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + + private final JLabel ilpPathLabel = new JLabel("Path:"); + private final JTextField ilpPath = new JTextField(); + private final JButton ilpPathBrowse = new JButton("Browse"); + + private final JLabel outputTypeLabel = new JLabel("Output type:"); + private final JComboBox outputType = new JComboBox<>(); + + private final JLabel predictionMaskLabel = new JLabel("Prediction Mask:"); + private final JComboBox predictionMask = new JComboBox<>(); + + private final JButton predictBtn = new JButton("Predict"); + private final JButton cancelBtn = new JButton("Cancel"); + + private void initializeComponentLayout() { + getContentPane().setLayout(new BorderLayout()); + GroupLayout layout = new GroupLayout(contentPanel); + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + contentPanel.setLayout(layout); + getContentPane().add(contentPanel, BorderLayout.PAGE_START); + getContentPane().add(controlPanel, BorderLayout.PAGE_END); + controlPanel.add(cancelBtn); + controlPanel.add(predictBtn); + ilpPath.setMinimumSize(new Dimension(400, 20)); + + layout.setHorizontalGroup(layout + .createSequentialGroup() + .addGroup(layout + .createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(ilpPathLabel) + .addComponent(outputTypeLabel) + .addComponent(predictionMaskLabel) + ) + .addGroup(layout + .createParallelGroup(GroupLayout.Alignment.LEADING) + .addGroup(layout + .createSequentialGroup() + .addComponent(ilpPath) + .addComponent(ilpPathBrowse) + ) + .addComponent(outputType) + .addComponent(predictionMask) + ) + ); + layout.setVerticalGroup(layout + .createSequentialGroup() + .addGroup( + layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(ilpPathLabel) + .addComponent(ilpPath) + .addComponent(ilpPathBrowse) + ) + .addGroup( + layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(outputTypeLabel) + .addComponent(outputType) + ) + .addGroup( + layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(predictionMaskLabel) + .addComponent(predictionMask) + ) + ); + layout.linkSize(SwingConstants.VERTICAL, ilpPathBrowse, ilpPath); + setResizable(true); + pack(); + } + + private void initIlpControl() { + ilpPathBrowse.addActionListener(actionEvent -> { + File parent = model.getIlastikProjectFile(); + File result = uiService.chooseFile(parent, FileWidget.OPEN_STYLE); + if (result != null) { + model.setIlastikProjectFile(result); + } + }); + + ilpPath.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent documentEvent) { + model.setIlastikProjectFile(new File(ilpPath.getText())); + } + + @Override + public void removeUpdate(DocumentEvent documentEvent) { + model.setIlastikProjectFile(new File(ilpPath.getText())); + } + + @Override + public void changedUpdate(DocumentEvent documentEvent) { + model.setIlastikProjectFile(new File(ilpPath.getText())); + } + }); + } + + private void initOutputTypeControl() { + outputType.addItem(new OutputTypeComboboxEntry("Probabilities", UiConstants.PIXEL_PREDICTION_TYPE_PROBABILITIES)); + outputType.addItem(new OutputTypeComboboxEntry("Segmentation", UiConstants.PIXEL_PREDICTION_TYPE_SEGMENTATION)); + outputType.addActionListener(evt -> { + OutputTypeComboboxEntry entry = (OutputTypeComboboxEntry)outputType.getSelectedItem(); + if (entry != null) { + model.setOutputType(entry.type); + } + }); + } + + private void initPredictionTypeControl() { + this.predictionMask.addItem(new DatasetComboboxEntry("", null)); + for (Dataset ds : datasetService.getDatasets()) { + this.predictionMask.addItem(new DatasetComboboxEntry(ds.getName(), ds)); + } + predictionMask.addActionListener(evt -> { + DatasetComboboxEntry entry = (DatasetComboboxEntry) predictionMask.getSelectedItem(); + if (entry != null) { + model.setPredictionMask(entry.dataset); + } + }); + } + public boolean wasCancelled() { + return this.cancelled; + } + + public IlastikPixelClassificationDialog(LogService logService, UIService uiService, DatasetService datasetService, IlastikPixelClassificationModel model) { + this.setModalityType(ModalityType.APPLICATION_MODAL); // Block until dialog is closed + this.uiService = uiService; + this.logService = logService; + this.datasetService = datasetService; + + this.model = model; + this.model.addPropertyChangeListener(this); + + this.initIlpControl(); + this.initOutputTypeControl(); + this.initPredictionTypeControl(); + + this.cancelBtn.addActionListener(evt -> { + this.dispose(); + }); + this.predictBtn.addActionListener(evt -> { + cancelled = false; + this.dispose(); + }); + + this.initializeComponentLayout(); + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals(IlastikPixelClassificationModel.PROPERTY_ILASTIK_PROJECT_FILE)) { + File newProjectFile = (File) evt.getNewValue(); + if (newProjectFile != null && !this.ilpPath.equals(newProjectFile.getAbsolutePath())) { + this.ilpPath.setText(newProjectFile.getAbsolutePath()); + } + + } else if (evt.getPropertyName().equals(IlastikPixelClassificationModel.PROPERTY_OUTPUT_TYPE)) { + String newType = (String) evt.getNewValue(); + int selectedIdx = outputType.getSelectedIndex(); + for (int i = 0; i < outputType.getItemCount(); i++) { + OutputTypeComboboxEntry entry = outputType.getItemAt(i); + if (entry.type.equals(newType) && selectedIdx != i) { + outputType.setSelectedIndex(i); + } + } + + } else if (evt.getPropertyName().equals(IlastikPixelClassificationModel.PROPERTY_PREDICTION_MASK)) { + Dataset newPredMask = (Dataset) evt.getNewValue(); + int selectedIdx = predictionMask.getSelectedIndex(); + if (newPredMask != null) { + for (int i = 0; i < predictionMask.getItemCount(); i++) { + DatasetComboboxEntry entry = predictionMask.getItemAt(i); + if (newPredMask.getName().equals(entry.title) && selectedIdx != i) { + predictionMask.setSelectedIndex(i); + } + } + } else { + if (selectedIdx != 0) { + outputType.setSelectedIndex(0); + } + } + } + } + +} diff --git a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationModel.java b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationModel.java new file mode 100644 index 00000000..33940947 --- /dev/null +++ b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationModel.java @@ -0,0 +1,92 @@ +package org.ilastik.ilastik4ij.ui; + +import net.imagej.Data; +import net.imagej.Dataset; +import net.imagej.DatasetService; +import org.scijava.log.LogService; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.File; +import java.util.List; +import java.util.Vector; + +public class IlastikPixelClassificationModel { + public static final String PROPERTY_ILASTIK_PROJECT_FILE = "ilastikProjectFile"; + public static final String PROPERTY_OUTPUT_TYPE = "outputType"; + public static final String PROPERTY_RAW_INPUT = "rawInput"; + public static final String PROPERTY_PREDICTION_MASK = "predictionMask"; + + private final LogService logService; + private final PropertyChangeSupport propertyChangeSupport; + + private File ilastikProjectFile = null; + private Dataset rawInput = null; + private Dataset predictionMask = null; + + private String outputType = UiConstants.PIXEL_PREDICTION_TYPE_PROBABILITIES; + + public IlastikPixelClassificationModel(LogService logService) { + this.logService = logService; + this.propertyChangeSupport = new PropertyChangeSupport(this); + } + + public File getIlastikProjectFile() { + return this.ilastikProjectFile; + } + + public void setIlastikProjectFile(File path) { + File oldValue = this.ilastikProjectFile; + this.ilastikProjectFile = path; + firePropertyChange(PROPERTY_ILASTIK_PROJECT_FILE, oldValue, path); + } + + public void setRawInput(Dataset dataset) { + Dataset oldValue = this.rawInput; + this.rawInput = dataset; + firePropertyChange(PROPERTY_RAW_INPUT, oldValue, dataset); + } + + public Dataset getRawInput() { + return this.rawInput; + } + + public void setPredictionMask(Dataset dataset) { + Dataset oldValue = this.predictionMask; + this.predictionMask = dataset; + firePropertyChange(PROPERTY_PREDICTION_MASK, oldValue, dataset); + } + + public Dataset getPredictionMask() { + return this.predictionMask; + } + + public void setOutputType(String type) { + String oldValue = this.outputType; + this.outputType = type; + firePropertyChange(PROPERTY_OUTPUT_TYPE, oldValue, type); + } + + public String getOutputType() { + return this.outputType; + } + + public void fireInitialProperties() { + firePropertyChange(PROPERTY_ILASTIK_PROJECT_FILE, null, this.ilastikProjectFile); + firePropertyChange(PROPERTY_OUTPUT_TYPE, null, this.outputType); + firePropertyChange(PROPERTY_PREDICTION_MASK, null, this.predictionMask); + firePropertyChange(PROPERTY_RAW_INPUT, null, this.rawInput); + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + propertyChangeSupport.addPropertyChangeListener(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + propertyChangeSupport.removePropertyChangeListener(listener); + } + + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue); + } +} diff --git a/src/test/java/org/ilastik/ilastik4ij/PixelClassificationCommandDemo.java b/src/test/java/org/ilastik/ilastik4ij/PixelClassificationCommandDemo.java index bbdc6d3e..9547a0e2 100644 --- a/src/test/java/org/ilastik/ilastik4ij/PixelClassificationCommandDemo.java +++ b/src/test/java/org/ilastik/ilastik4ij/PixelClassificationCommandDemo.java @@ -40,9 +40,11 @@ public static void main(String[] args) throws IOException { 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(); } } From 5cb5491cf2e516bbffbdbfa5ad53e56b832f6afa Mon Sep 17 00:00:00 2001 From: Maksim Novikov Date: Tue, 1 Sep 2020 21:35:25 +0200 Subject: [PATCH 3/5] Record macro --- .../ui/IlastikPixelClassificationCommand.java | 35 +++++-------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java index 70419558..2d3f02a4 100644 --- a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java +++ b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java @@ -1,6 +1,7 @@ package org.ilastik.ilastik4ij.ui; import ij.Macro; +import ij.plugin.frame.Recorder; import net.imagej.Data; import net.imagej.Dataset; import net.imagej.DatasetService; @@ -43,33 +44,6 @@ public class IlastikPixelClassificationCommand extends DynamicCommand { @Parameter(label = "Raw input image") public Dataset inputImage; - /* - @Parameter(label = "Trained ilastik project file") - public File projectFileName; - - - @Parameter(label = "Output type", choices = {UiConstants.PIXEL_PREDICTION_TYPE_PROBABILITIES, UiConstants.PIXEL_PREDICTION_TYPE_SEGMENTATION}, style = "radioButtonHorizontal") - public String pixelClassificationType; - - @Parameter(label = "Use Mask?", persist=false, initializer = "initUseMask") - public boolean useMask=false; - - protected void initUseMask(){ - useMask = false; - //resolveInput("useMask"); //this makes the input not be rendered -.- - } - */ - - - /* - @Parameter( - label = "Prediction Mask", - required = false, - description = "An image with same dimensions as Raw Data, where the black pixels will be masked out of the predictions" - ) - public Dataset predictionMask; - - */ @Parameter(type = ItemIO.OUTPUT) private ImgPlus> predictions; @@ -113,6 +87,13 @@ public void run() { try { runClassification(model); + if (Recorder.record) { + Recorder.recordOption("projectfilename", model.getIlastikProjectFile().getAbsolutePath()); + Recorder.recordOption("pixelclassificationtype", model.getOutputType()); + if (model.getPredictionMask() != null) { + Recorder.recordOption("predictionmask", model.getPredictionMask().getName()); + } + } } catch (IOException e) { logService.error("Pixel classification command failed", e); throw new RuntimeException(e); From 3f87fc46d41cca3fd8ebe240961c1a46e46ef1f0 Mon Sep 17 00:00:00 2001 From: Maksim Novikov Date: Tue, 1 Sep 2020 21:38:24 +0200 Subject: [PATCH 4/5] Cleanup unused code --- .../ilastik4ij/ui/IlastikPixelClassificationCommand.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java index 2d3f02a4..12811217 100644 --- a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java +++ b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java @@ -2,7 +2,6 @@ import ij.Macro; import ij.plugin.frame.Recorder; -import net.imagej.Data; import net.imagej.Dataset; import net.imagej.DatasetService; import net.imagej.ImgPlus; @@ -11,7 +10,6 @@ import org.scijava.ItemIO; import org.scijava.app.StatusService; import org.scijava.command.Command; -import org.scijava.command.DynamicCommand; import org.scijava.log.LogService; import org.scijava.options.OptionsService; import org.scijava.plugin.Parameter; @@ -24,7 +22,7 @@ import static org.ilastik.ilastik4ij.executors.AbstractIlastikExecutor.PixelPredictionType; @Plugin(type = Command.class, headless = true, menuPath = "Plugins>ilastik>Run Pixel Classification Prediction") -public class IlastikPixelClassificationCommand extends DynamicCommand { +public class IlastikPixelClassificationCommand implements Command { @Parameter public LogService logService; From 6943c8e5172ab70f39f629b7be84526125a52c50 Mon Sep 17 00:00:00 2001 From: Maksim Novikov Date: Wed, 2 Sep 2020 09:42:26 +0200 Subject: [PATCH 5/5] Cleanup and add validation --- .../ui/IlastikPixelClassificationCommand.java | 12 ++++--- .../ui/IlastikPixelClassificationDialog.java | 35 ++++++++++++++----- .../ui/IlastikPixelClassificationModel.java | 20 +++++++++++ 3 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java index 12811217..b3da4f1f 100644 --- a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java +++ b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationCommand.java @@ -76,11 +76,13 @@ public void run() { ilastikOptions = optionsService.getOptions(IlastikOptions.class); IlastikPixelClassificationModel model = createModel(); - IlastikPixelClassificationDialog dialog = new IlastikPixelClassificationDialog(logService, uiService, datasetService, model); - model.fireInitialProperties(); - dialog.setVisible(true); - if (dialog.wasCancelled()) { - return; + if (!model.isValid()) { + IlastikPixelClassificationDialog dialog = new IlastikPixelClassificationDialog(logService, uiService, datasetService, model); + model.fireInitialProperties(); + dialog.setVisible(true); + if (dialog.wasCancelled()) { + return; + } } try { diff --git a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationDialog.java b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationDialog.java index 411a3ea3..f0451543 100644 --- a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationDialog.java +++ b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationDialog.java @@ -6,7 +6,10 @@ import org.scijava.ui.UIService; import org.scijava.widget.FileWidget; +import javax.print.DocFlavor; import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.LineBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import java.awt.*; @@ -15,6 +18,9 @@ import java.io.File; public class IlastikPixelClassificationDialog extends JDialog implements PropertyChangeListener { + private static final Border VALID_BORDER = new JTextField().getBorder(); + private static final Border INVALID_BORDER = new LineBorder(Color.RED, 1); + private class DatasetComboboxEntry { public final Dataset dataset; public final String title; @@ -58,7 +64,7 @@ public String toString() { private final JButton ilpPathBrowse = new JButton("Browse"); private final JLabel outputTypeLabel = new JLabel("Output type:"); - private final JComboBox outputType = new JComboBox<>(); + private final JComboBox outputType = new JComboBox<>(); private final JLabel predictionMaskLabel = new JLabel("Prediction Mask:"); private final JComboBox predictionMask = new JComboBox<>(); @@ -149,12 +155,12 @@ public void changedUpdate(DocumentEvent documentEvent) { } private void initOutputTypeControl() { - outputType.addItem(new OutputTypeComboboxEntry("Probabilities", UiConstants.PIXEL_PREDICTION_TYPE_PROBABILITIES)); - outputType.addItem(new OutputTypeComboboxEntry("Segmentation", UiConstants.PIXEL_PREDICTION_TYPE_SEGMENTATION)); + outputType.addItem(UiConstants.PIXEL_PREDICTION_TYPE_PROBABILITIES); + outputType.addItem(UiConstants.PIXEL_PREDICTION_TYPE_SEGMENTATION); outputType.addActionListener(evt -> { - OutputTypeComboboxEntry entry = (OutputTypeComboboxEntry)outputType.getSelectedItem(); + String entry = (String)outputType.getSelectedItem(); if (entry != null) { - model.setOutputType(entry.type); + model.setOutputType(entry); } }); } @@ -192,13 +198,23 @@ public IlastikPixelClassificationDialog(LogService logService, UIService uiServi this.dispose(); }); this.predictBtn.addActionListener(evt -> { - cancelled = false; - this.dispose(); + if (model.isValid()) { + cancelled = false; + this.dispose(); + } }); this.initializeComponentLayout(); } + private static Border getLineBorder(boolean valid) { + if (valid) { + return VALID_BORDER; + } else { + return INVALID_BORDER; + } + } + @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(IlastikPixelClassificationModel.PROPERTY_ILASTIK_PROJECT_FILE)) { @@ -207,12 +223,13 @@ public void propertyChange(PropertyChangeEvent evt) { this.ilpPath.setText(newProjectFile.getAbsolutePath()); } + ilpPath.setBorder(getLineBorder(model.isValidIlastikProjectFile())); } else if (evt.getPropertyName().equals(IlastikPixelClassificationModel.PROPERTY_OUTPUT_TYPE)) { String newType = (String) evt.getNewValue(); int selectedIdx = outputType.getSelectedIndex(); for (int i = 0; i < outputType.getItemCount(); i++) { - OutputTypeComboboxEntry entry = outputType.getItemAt(i); - if (entry.type.equals(newType) && selectedIdx != i) { + String entry = outputType.getItemAt(i); + if (entry.equals(newType) && selectedIdx != i) { outputType.setSelectedIndex(i); } } diff --git a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationModel.java b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationModel.java index 33940947..245e2385 100644 --- a/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationModel.java +++ b/src/main/java/org/ilastik/ilastik4ij/ui/IlastikPixelClassificationModel.java @@ -40,6 +40,10 @@ public void setIlastikProjectFile(File path) { this.ilastikProjectFile = path; firePropertyChange(PROPERTY_ILASTIK_PROJECT_FILE, oldValue, path); } + + public boolean isValidIlastikProjectFile() { + return this.ilastikProjectFile != null && this.ilastikProjectFile.exists(); + } public void setRawInput(Dataset dataset) { Dataset oldValue = this.rawInput; @@ -51,6 +55,10 @@ public Dataset getRawInput() { return this.rawInput; } + public boolean isValidRawInput() { + return this.ilastikProjectFile != null; + } + public void setPredictionMask(Dataset dataset) { Dataset oldValue = this.predictionMask; this.predictionMask = dataset; @@ -61,6 +69,10 @@ public Dataset getPredictionMask() { return this.predictionMask; } + public boolean isValidPredictionMask() { + return true; + } + public void setOutputType(String type) { String oldValue = this.outputType; this.outputType = type; @@ -71,6 +83,14 @@ public String getOutputType() { return this.outputType; } + public boolean isValidOutputType() { + return true; + } + + public boolean isValid() { + return isValidIlastikProjectFile() && isValidOutputType() && isValidPredictionMask() && isValidRawInput(); + } + public void fireInitialProperties() { firePropertyChange(PROPERTY_ILASTIK_PROJECT_FILE, null, this.ilastikProjectFile); firePropertyChange(PROPERTY_OUTPUT_TYPE, null, this.outputType);