package cz.cuni.lf1.lge.ThunderSTORM.UI;
import cz.cuni.lf1.lge.ThunderSTORM.ModuleLoader;
import cz.cuni.lf1.lge.ThunderSTORM.calibration.DefocusFunction;
import cz.cuni.lf1.lge.ThunderSTORM.detectors.ui.IDetectorUI;
import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.IEstimatorUI;
import cz.cuni.lf1.lge.ThunderSTORM.filters.ui.IFilterUI;
import cz.cuni.lf1.lge.ThunderSTORM.thresholding.Thresholder;
import cz.cuni.lf1.lge.ThunderSTORM.util.GridBagHelper;
import cz.cuni.lf1.lge.ThunderSTORM.util.MacroUI.DialogStub;
import cz.cuni.lf1.lge.ThunderSTORM.util.MacroUI.ParameterKey;
import cz.cuni.lf1.lge.ThunderSTORM.util.MacroUI.ParameterTracker;
import cz.cuni.lf1.lge.ThunderSTORM.util.MacroUI.validators.DoubleValidatorFactory;
import cz.cuni.lf1.lge.ThunderSTORM.util.PluginCommands;
import cz.cuni.lf1.lge.ThunderSTORM.util.Point;
import ij.IJ;
import ij.ImagePlus;
import ij.Macro;
import ij.gui.Roi;
import ij.plugin.frame.Recorder;
import ij.process.FloatProcessor;
import ij.process.ImageProcessor;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class AstigmatismCalibrationDialog extends DialogStub implements ActionListener {
ParameterKey.Double stageStep;
ParameterKey.Double zRangeLimit;
ParameterKey.String calibrationFilePath;
ParameterKey.String filterName;
ParameterKey.String detectorName;
ParameterKey.String estimatorName;
ParameterKey.String defocusName;
private List<IFilterUI> filters;
private List<IDetectorUI> detectors;
private List<IEstimatorUI> estimators;
private List<DefocusFunction> defocusing;
private ImagePlus imp;
ExecutorService previewThreadRunner = Executors.newSingleThreadExecutor();
Future<?> previewFuture = null;
public AstigmatismCalibrationDialog(ImagePlus imp, List<IFilterUI> filters, List<IDetectorUI> detectors, List<IEstimatorUI> estimators, List<DefocusFunction> defocusing) {
super(new ParameterTracker("thunderstorm.calibration"), IJ.getInstance(), "Calibration options");
params.getComponentHandlers().addForStringParameters(CardsPanel.class, new CardsPanelMacroUIHandler());
stageStep = params.createDoubleField("stage", DoubleValidatorFactory.positiveNonZero(), 10);
zRangeLimit = params.createDoubleField("zRange", DoubleValidatorFactory.positiveNonZero(), 400);
calibrationFilePath = params.createStringField("saveto", null, "");
filterName = params.createStringField("filter", null, filters.get(0).getName());
detectorName = params.createStringField("detector", null, detectors.get(0).getName());
estimatorName = params.createStringField("estimator", null, estimators.get(0).getName());
defocusName = params.createStringField("defocusing", null, defocusing.get(0).getName());
this.filters = filters;
this.detectors = detectors;
this.estimators = estimators;
this.defocusing = defocusing;
this.imp = imp;
}
@Override
protected void layoutComponents() {
Container pane = getContentPane();
//
pane.setLayout(new GridBagLayout());
GridBagConstraints componentConstraints = new GridBagConstraints();
componentConstraints.gridx = 0;
componentConstraints.fill = GridBagConstraints.BOTH;
componentConstraints.weightx = 1;
JButton cameraSetup = new JButton("Camera setup");
cameraSetup.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
MacroParser.runNestedWithRecording(PluginCommands.CAMERA_SETUP.getValue(), null);
}
});
JPanel cameraPanel = new JPanel(new BorderLayout());
cameraPanel.add(cameraSetup);
cameraPanel.setBorder(BorderFactory.createTitledBorder("Camera"));
pane.add(cameraPanel, componentConstraints);
CardsPanel<IFilterUI> filterCards = new CardsPanel<IFilterUI>(filters, 0);
filterName.registerComponent(filterCards);
JPanel p = filterCards.getPanel("Filter:");
p.setBorder(BorderFactory.createTitledBorder("Image filtering"));
pane.add(p, componentConstraints);
CardsPanel<IDetectorUI> detectorCards = new CardsPanel<IDetectorUI>(detectors, 0);
detectorName.registerComponent(detectorCards);
p = detectorCards.getPanel("Method:");
p.setBorder(BorderFactory.createTitledBorder("Approximate localization of molecules"));
pane.add(p, componentConstraints);
CardsPanel<IEstimatorUI> estimatorCards = new CardsPanel<IEstimatorUI>(estimators, 0);
estimatorName.registerComponent(estimatorCards);
p = estimatorCards.getPanel("Method:");
p.setBorder(BorderFactory.createTitledBorder("Sub-pixel localization of molecules"));
pane.add(p, componentConstraints);
CardsPanel<DefocusFunction> defocusCards = new CardsPanel<DefocusFunction>(defocusing, 0);
defocusName.registerComponent(defocusCards);
p = defocusCards.getPanel("Defocus model:");
p.setBorder(BorderFactory.createTitledBorder("3D defocusing curve"));
pane.add(p, componentConstraints);
JPanel aditionalOptions = new JPanel(new GridBagLayout());
aditionalOptions.setBorder(BorderFactory.createTitledBorder("Additional options"));
aditionalOptions.add(new JLabel("Z stage step [nm]:"), GridBagHelper.leftCol());
JTextField stageStepTextField = new JTextField("", 20);
stageStep.registerComponent(stageStepTextField);
aditionalOptions.add(stageStepTextField, GridBagHelper.rightCol());
aditionalOptions.add(new JLabel("Z range limit [nm]:"), GridBagHelper.leftCol());
JTextField zRangeTextField = new JTextField("", 20);
zRangeLimit.registerComponent(zRangeTextField);
aditionalOptions.add(zRangeTextField, GridBagHelper.rightCol());
aditionalOptions.add(new JLabel("Save to file: "), GridBagHelper.leftCol());
JPanel calibrationPanel = new JPanel(new BorderLayout());
JTextField calibrationFileTextField = new JTextField(15);
calibrationFilePath.registerComponent(calibrationFileTextField);
calibrationPanel.add(calibrationFileTextField, BorderLayout.CENTER);
calibrationPanel.add(createBrowseButton(calibrationFileTextField, true, new FileNameExtensionFilter("Yaml file", "yaml")), BorderLayout.EAST);
GridBagConstraints gbc = GridBagHelper.rightCol();
gbc.fill = GridBagConstraints.HORIZONTAL;
aditionalOptions.add(calibrationPanel, gbc);
pane.add(aditionalOptions, componentConstraints);
JButton defaults = new JButton("Defaults");
JButton preview = new JButton("Preview");
JButton ok = new JButton("OK");
JButton cancel = new JButton("Cancel");
defaults.addActionListener(this);
preview.addActionListener(this);
ok.addActionListener(this);
cancel.addActionListener(this);
//
JPanel buttons = new JPanel();
buttons.add(defaults);
buttons.add(Box.createHorizontalStrut(30));
buttons.add(preview);
buttons.add(ok);
buttons.add(cancel);
pane.add(buttons, componentConstraints);
getRootPane().setDefaultButton(ok);
getRootPane().setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
setResizable(false);
params.loadPrefs();
pack();
}
@Override
public void actionPerformed(ActionEvent e) {
try {
if("Preview".equals(e.getActionCommand())) {
Thresholder.setActiveFilter(getActiveFilterUIIndex());
// parse parameters
params.readDialogOptions();
getActiveFilterUI().readParameters();
getActiveDetectorUI().readParameters();
getActiveEstimatorUI().readParameters();
getActiveDefocusFunction().readParameters();
getActiveFilterUI().resetThreadLocal();
getActiveDetectorUI().resetThreadLocal();
getActiveEstimatorUI().resetThreadLocal();
getActiveDefocusFunction().resetThreadLocal();
params.savePrefs();
//if another preview task is still running, cancel it
if(previewFuture != null) {
previewFuture.cancel(true);
}
//do the preview task
previewFuture = previewThreadRunner.submit(new Runnable() {
@Override
public void run() {
try {
IJ.showStatus("Creating preview image.");
Roi roi = imp.getRoi();
ImageProcessor processor = imp.getProcessor();
if(roi != null) {
processor.setRoi(roi.getBounds());
processor = processor.crop();
} else {
processor = processor.duplicate();
}
FloatProcessor fp = (FloatProcessor) processor.convertToFloat();
if(roi != null) {
fp.setMask(roi.getMask());
}
Thresholder.setCurrentImage(fp);
FloatProcessor filtered = getActiveFilterUI().getThreadLocalImplementation().filterImage(fp);
new ImagePlus("Filtered frame " + Integer.toString(imp.getSlice()), filtered).show();
GUI.checkIJEscapePressed();
List<Point> detections = Point.applyRoiMask(imp.getRoi(), getActiveDetectorUI().getThreadLocalImplementation().detectMoleculeCandidates(filtered));
GUI.checkIJEscapePressed();
//
double[] xCoord = new double[detections.size()];
double[] yCoord = new double[detections.size()];
for(int i = 0; i < detections.size(); i++) {
xCoord[i] = detections.get(i).getX().doubleValue();
yCoord[i] = detections.get(i).getY().doubleValue();
}
//
ImagePlus impPreview = new ImagePlus("Preview for frame " + Integer.toString(imp.getSlice()), processor);
RenderingOverlay.showPointsInImage(impPreview, xCoord, yCoord, Color.red, RenderingOverlay.MARKER_CROSS);
impPreview.show();
} catch(StoppedByUserException ex) {
IJ.resetEscape();
IJ.showStatus("Preview interrupted.");
} catch(Exception ex) {
IJ.handleException(ex);
}
}
});
} else if("OK".equals(e.getActionCommand())) {
params.readDialogOptions();
Thresholder.setActiveFilter(getActiveFilterUIIndex());
getActiveFilterUI().readParameters();
getActiveDetectorUI().readParameters();
getActiveEstimatorUI().readParameters();
getActiveDefocusFunction().readParameters();
params.savePrefs();
if(Recorder.record) {
getActiveFilterUI().recordOptions();
getActiveDetectorUI().recordOptions();
getActiveEstimatorUI().recordOptions();
getActiveDefocusFunction().recordOptions();
params.recordMacroOptions();
}
result = JOptionPane.OK_OPTION;
dispose();
} else if("Cancel".equals(e.getActionCommand())) {
dispose();
} else if("Defaults".equals(e.getActionCommand())) {
params.resetToDefaults(true);
AnalysisOptionsDialog.resetModuleUIs(filters, detectors, estimators);
}
} catch(Exception ex) {
IJ.handleException(ex);
}
}
@Override
public int showAndGetResult() {
if(MacroParser.isRanFromMacro()) {
params.readMacroOptions();
String options = Macro.getOptions();
getActiveFilterUI().readMacroOptions(options);
getActiveDetectorUI().readMacroOptions(options);
getActiveEstimatorUI().readMacroOptions(options);
getActiveDefocusFunction().readMacroOptions(options);
return JOptionPane.OK_OPTION;
} else {
return super.showAndGetResult();
}
}
public IFilterUI getActiveFilterUI() {
return ModuleLoader.moduleByName(filters, filterName.getValue());
}
public int getActiveFilterUIIndex() {
return ModuleLoader.moduleIndexByName(filters, filterName.getValue());
}
public IDetectorUI getActiveDetectorUI() {
return ModuleLoader.moduleByName(detectors, detectorName.getValue());
}
public IEstimatorUI getActiveEstimatorUI() { return ModuleLoader.moduleByName(estimators, estimatorName.getValue()); }
public DefocusFunction getActiveDefocusFunction() { return ModuleLoader.moduleByName(defocusing, defocusName.getValue()); }
public String getSavePath() {
return calibrationFilePath.getValue();
}
public double getStageStep() {
return stageStep.getValue();
}
public double getZRangeLimit() {
return zRangeLimit.getValue();
}
@Override
public void dispose() {
super.dispose();
if(previewThreadRunner != null) {
previewThreadRunner.shutdownNow();
}
}
}