package cz.cuni.lf1.lge.ThunderSTORM.UI;
import cz.cuni.lf1.lge.ThunderSTORM.ModuleLoader;
import cz.cuni.lf1.lge.ThunderSTORM.calibration.CalibrationConfig;
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.MacroUI.validators.IntegerValidatorFactory;
import cz.cuni.lf1.lge.ThunderSTORM.util.PluginCommands;
import ij.IJ;
import ij.ImagePlus;
import ij.Macro;
import ij.WindowManager;
import ij.plugin.frame.Recorder;
import org.apache.commons.lang3.ObjectUtils;
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;
public class BiplaneCalibrationDialog 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;
ParameterKey.String rawImage1Stack;
ParameterKey.String rawImage2Stack;
private List<IFilterUI> filters;
private List<IDetectorUI> detectors;
private List<IEstimatorUI> estimators;
private List<DefocusFunction> defocusing;
ParameterKey.Double dist2thrZStackMatching;
ParameterKey.Integer minimumFitsCount;
ParameterKey.Integer polyFitMaxIters;
ParameterKey.Integer finalPolyFitMaxIters;
ParameterKey.Integer minFitsInZRange;
ParameterKey.Integer movingAverageLag;
ParameterKey.Boolean checkIfDefocusIsInRange;
ParameterKey.Integer inlierFittingMaxIters;
ParameterKey.Double inlierFittingInlierFraction;
ParameterKey.Boolean showResultsTable;
ParameterKey.Integer rtfIterNum;
ParameterKey.Double rtfThDist;
ParameterKey.Double rtfThInlr;
ParameterKey.Integer hIterNum;
ParameterKey.Double hThDist;
ParameterKey.Double hThInlr;
ParameterKey.Double hThPairDist;
ParameterKey.Double hThAllowedTransformChange;
public BiplaneCalibrationDialog(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());
rawImage1Stack = params.createStringField("raw_image1_stack", null, "");
rawImage2Stack = params.createStringField("raw_image2_stack", null, "");
CalibrationConfig defaultConfig = new CalibrationConfig();
dist2thrZStackMatching = params.createDoubleField("dist2thrZStackMatching", DoubleValidatorFactory.positive(), defaultConfig.dist2thrZStackMatching);
minimumFitsCount = params.createIntField("minimumFitsCount", IntegerValidatorFactory.positive(), defaultConfig.minimumFitsCount);
polyFitMaxIters = params.createIntField("polyFitMaxIters", IntegerValidatorFactory.positive(), defaultConfig.polyFitMaxIters);
finalPolyFitMaxIters = params.createIntField("finalPolyFitMaxIters", IntegerValidatorFactory.positive(), defaultConfig.finalPolyFitMaxIters);
minFitsInZRange = params.createIntField("minFitsInZRange", IntegerValidatorFactory.positive(), defaultConfig.minFitsInZRange);
movingAverageLag = params.createIntField("movingAverageLag", IntegerValidatorFactory.positive(), defaultConfig.movingAverageLag);
checkIfDefocusIsInRange = params.createBooleanField("checkIfDefocusIsInRange", null, defaultConfig.checkIfDefocusIsInRange);
inlierFittingMaxIters = params.createIntField("inlierFittingMaxIters", IntegerValidatorFactory.positive(), defaultConfig.inlierFittingMaxIters);
inlierFittingInlierFraction = params.createDoubleField("inlierFittingInlierFraction", DoubleValidatorFactory.rangeInclusive(0.0, 1.0), defaultConfig.inlierFittingInlierFraction);
showResultsTable = params.createBooleanField("showResultsTable", null, defaultConfig.showResultsTable);
rtfIterNum = params.createIntField("rtfIterNum", IntegerValidatorFactory.positive(), defaultConfig.ransacTranslationAndFlip.iterNum);
rtfThDist = params.createDoubleField("rtfThDist", DoubleValidatorFactory.positive(), defaultConfig.ransacTranslationAndFlip.thDist);
rtfThInlr = params.createDoubleField("rtfThInlr", DoubleValidatorFactory.rangeInclusive(0.0, 1.0), defaultConfig.ransacTranslationAndFlip.thInlr);
hIterNum = params.createIntField("hIterNum", IntegerValidatorFactory.positive(), defaultConfig.ransacHomography.iterNum);
hThDist = params.createDoubleField("hThDist", DoubleValidatorFactory.positive(), defaultConfig.ransacHomography.thDist);
hThInlr = params.createDoubleField("hThInlr", DoubleValidatorFactory.rangeInclusive(0.0, 1.0), defaultConfig.ransacHomography.thInlr);
hThPairDist = params.createDoubleField("hThPairDist", DoubleValidatorFactory.positive(), defaultConfig.ransacHomography.thPairDist);
hThAllowedTransformChange = params.createDoubleField("hThAllowedTransformChange", DoubleValidatorFactory.positive(), defaultConfig.ransacHomography.thAllowedTransformChange);
this.filters = filters;
this.detectors = detectors;
this.estimators = estimators;
this.defocusing = defocusing;
}
@Override
protected void layoutComponents() {
JPanel pane = new JPanel();
//
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);
JPanel dataPanel = new JPanel(new GridBagLayout());
JComboBox<String> rawImage1ComboBox = createOpenImagesComboBox(false);
JComboBox<String> rawImage2ComboBox = createOpenImagesComboBox(false);
rawImage1ComboBox.setPreferredSize(new Dimension(150, (int) rawImage1ComboBox.getPreferredSize().getHeight()));
rawImage2ComboBox.setPreferredSize(new Dimension(150, (int) rawImage2ComboBox.getPreferredSize().getHeight()));
rawImage1ComboBox.setSelectedIndex(0);
rawImage2ComboBox.setSelectedIndex(1);
if (rawImage1ComboBox.getItemCount() < 2) throw new NullPointerException();
rawImage1Stack.registerComponent(rawImage1ComboBox);
rawImage2Stack.registerComponent(rawImage2ComboBox);
dataPanel.add(new JLabel("First plane:"), GridBagHelper.leftCol());
dataPanel.add(rawImage1ComboBox, GridBagHelper.rightCol());
dataPanel.add(new JLabel("Second plane:"), GridBagHelper.leftCol());
dataPanel.add(rawImage2ComboBox, GridBagHelper.rightCol());
dataPanel.setBorder(BorderFactory.createTitledBorder("Source data"));
pane.add(dataPanel, 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 additionalOptions = new JPanel(new GridBagLayout());
additionalOptions.setBorder(BorderFactory.createTitledBorder("Additional options"));
additionalOptions.add(new JLabel("Z stage step [nm]:"), GridBagHelper.leftCol());
JTextField stageStepTextField = new JTextField("", 20);
stageStep.registerComponent(stageStepTextField);
additionalOptions.add(stageStepTextField, GridBagHelper.rightCol());
additionalOptions.add(new JLabel("Z range limit [nm]:"), GridBagHelper.leftCol());
JTextField zRangeTextField = new JTextField("", 20);
zRangeLimit.registerComponent(zRangeTextField);
additionalOptions.add(zRangeTextField, GridBagHelper.rightCol());
additionalOptions.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;
additionalOptions.add(calibrationPanel, gbc);
pane.add(additionalOptions, componentConstraints);
JPanel advancedOptions = new JPanel(new GridBagLayout());
advancedOptions.setBorder(BorderFactory.createTitledBorder("Advanced calibration settings"));
advancedOptions.add(new JLabel("Squared dist thr for z-stack matching:"), GridBagHelper.leftCol());
JTextField dist2thrZStackMatchingTextField = new JTextField("", 20);
dist2thrZStackMatching.registerComponent(dist2thrZStackMatchingTextField);
advancedOptions.add(dist2thrZStackMatchingTextField, GridBagHelper.rightCol());
advancedOptions.add(new JLabel("Minimum fits count:"), GridBagHelper.leftCol());
JTextField minimumFitsCountTextField = new JTextField("", 20);
minimumFitsCount.registerComponent(minimumFitsCountTextField);
advancedOptions.add(minimumFitsCountTextField, GridBagHelper.rightCol());
advancedOptions.add(new JLabel("Poly fit max iters:"), GridBagHelper.leftCol());
JTextField polyFitMaxItersTextField = new JTextField("", 20);
polyFitMaxIters.registerComponent(polyFitMaxItersTextField);
advancedOptions.add(polyFitMaxItersTextField, GridBagHelper.rightCol());
advancedOptions.add(new JLabel("Final poly fit max iters:"), GridBagHelper.leftCol());
JTextField finalPolyFitMaxItersTextField = new JTextField("", 20);
finalPolyFitMaxIters.registerComponent(finalPolyFitMaxItersTextField);
advancedOptions.add(finalPolyFitMaxItersTextField, GridBagHelper.rightCol());
advancedOptions.add(new JLabel("Min fits in z-range:"), GridBagHelper.leftCol());
JTextField minFitsInZRangeTextField = new JTextField("", 20);
minFitsInZRange.registerComponent(minFitsInZRangeTextField);
advancedOptions.add(minFitsInZRangeTextField, GridBagHelper.rightCol());
advancedOptions.add(new JLabel("Moving average lag:"), GridBagHelper.leftCol());
JTextField movingAverageLagTextField = new JTextField("", 20);
movingAverageLag.registerComponent(movingAverageLagTextField);
advancedOptions.add(movingAverageLagTextField, GridBagHelper.rightCol());
advancedOptions.add(new JLabel("Check if defocus is in range:"), GridBagHelper.leftCol());
JCheckBox checkIfDefocusIsInRangeCheckBox = new JCheckBox();
checkIfDefocusIsInRange.registerComponent(checkIfDefocusIsInRangeCheckBox);
advancedOptions.add(checkIfDefocusIsInRangeCheckBox, GridBagHelper.rightCol());
advancedOptions.add(new JLabel("Inlier fitting max iters:"), GridBagHelper.leftCol());
JTextField inlierFittingMaxItersTextField = new JTextField("", 20);
inlierFittingMaxIters.registerComponent(inlierFittingMaxItersTextField);
advancedOptions.add(inlierFittingMaxItersTextField, GridBagHelper.rightCol());
advancedOptions.add(new JLabel("Inliers fraction:"), GridBagHelper.leftCol());
JTextField inlierFittingInlierFractionTextField = new JTextField("", 20);
inlierFittingInlierFraction.registerComponent(inlierFittingInlierFractionTextField);
advancedOptions.add(inlierFittingInlierFractionTextField, GridBagHelper.rightCol());
advancedOptions.add(new JLabel("Show results table:"), GridBagHelper.leftCol());
JCheckBox showResultsTableCheckBox = new JCheckBox();
showResultsTable.registerComponent(showResultsTableCheckBox);
advancedOptions.add(showResultsTableCheckBox, GridBagHelper.rightCol());
JPanel rtfRansacOptions = new JPanel(new GridBagLayout());
rtfRansacOptions.setBorder(BorderFactory.createTitledBorder("RANSAC: rough translation and flip estimate"));
rtfRansacOptions.add(new JLabel("Iterations:"), GridBagHelper.leftCol());
JTextField rtfIterNumTextField = new JTextField("", 20);
rtfIterNum.registerComponent(rtfIterNumTextField);
rtfRansacOptions.add(rtfIterNumTextField, GridBagHelper.rightCol());
rtfRansacOptions.add(new JLabel("Inlier distance threshold:"), GridBagHelper.leftCol());
JTextField rtfThDistTextField = new JTextField("", 20);
rtfThDist.registerComponent(rtfThDistTextField);
rtfRansacOptions.add(rtfThDistTextField, GridBagHelper.rightCol());
rtfRansacOptions.add(new JLabel("Inliers portion threshold:"), GridBagHelper.leftCol());
JTextField rtfThInlrTextField = new JTextField("", 20);
rtfThInlr.registerComponent(rtfThInlrTextField);
rtfRansacOptions.add(rtfThInlrTextField, GridBagHelper.rightCol());
advancedOptions.add(rtfRansacOptions, GridBagHelper.twoCols());
JPanel hRansacOptions = new JPanel(new GridBagLayout());
hRansacOptions.setBorder(BorderFactory.createTitledBorder("RANSAC: fine homography estimate"));
hRansacOptions.add(new JLabel("Iterations:"), GridBagHelper.leftCol());
JTextField hIterNumTextField = new JTextField("", 20);
hIterNum.registerComponent(hIterNumTextField);
hRansacOptions.add(hIterNumTextField, GridBagHelper.rightCol());
hRansacOptions.add(new JLabel("Inlier distance threshold:"), GridBagHelper.leftCol());
JTextField hThDistTextField = new JTextField("", 20);
hThDist.registerComponent(hThDistTextField);
hRansacOptions.add(hThDistTextField, GridBagHelper.rightCol());
hRansacOptions.add(new JLabel("Inliers portion threshold:"), GridBagHelper.leftCol());
JTextField hThInlrTextField = new JTextField("", 20);
hThInlr.registerComponent(hThInlrTextField);
hRansacOptions.add(hThInlrTextField, GridBagHelper.rightCol());
hRansacOptions.add(new JLabel("Pair distance threshold:"), GridBagHelper.leftCol());
JTextField hThPairDistTextField = new JTextField("", 20);
hThPairDist.registerComponent(hThPairDistTextField);
hRansacOptions.add(hThPairDistTextField, GridBagHelper.rightCol());
hRansacOptions.add(new JLabel("Allowed transform change:"), GridBagHelper.leftCol());
JTextField hThAllowedTransformChangeTextField = new JTextField("", 20);
hThAllowedTransformChange.registerComponent(hThAllowedTransformChangeTextField);
hRansacOptions.add(hThAllowedTransformChangeTextField, GridBagHelper.rightCol());
advancedOptions.add(hRansacOptions, GridBagHelper.twoCols());
pane.add(advancedOptions, componentConstraints);
JButton defaults = new JButton("Defaults");
JButton ok = new JButton("OK");
JButton cancel = new JButton("Cancel");
defaults.addActionListener(this);
ok.addActionListener(this);
cancel.addActionListener(this);
JPanel buttons = new JPanel();
buttons.add(defaults);
buttons.add(Box.createHorizontalStrut(30));
buttons.add(ok);
buttons.add(cancel);
pane.add(buttons, componentConstraints);
pane.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20));
setResizable(true);
setLayout(new BorderLayout());
JScrollPane scrollPane = new JScrollPane(pane, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setBorder(BorderFactory.createEmptyBorder());
add(scrollPane);
getRootPane().setDefaultButton(ok);
params.loadPrefs();
}
@Override
public void actionPerformed(ActionEvent e) {
try {
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 {
int result = super.showAndGetResult();
int maxScreenHeight = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds().height;
if(getHeight() > maxScreenHeight) {
setSize(getWidth(), maxScreenHeight);
}
return result;
}
}
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();
}
public ImagePlus getFirstPlaneStack() {
return WindowManager.getImage(rawImage1Stack.getValue());
}
public ImagePlus getSecondPlaneStack() {
return WindowManager.getImage(rawImage2Stack.getValue());
}
public CalibrationConfig getCalibrationConfig() {
return new CalibrationConfig(dist2thrZStackMatching.getValue(), minimumFitsCount.getValue(),
polyFitMaxIters.getValue(), finalPolyFitMaxIters.getValue(), minFitsInZRange.getValue(),
movingAverageLag.getValue(), checkIfDefocusIsInRange.getValue(), inlierFittingMaxIters.getValue(),
inlierFittingInlierFraction.getValue(), showResultsTable.getValue(),
CalibrationConfig.RansacConfig.createTranslationAndFlipConfig(
rtfIterNum.getValue(), rtfThDist.getValue(), rtfThInlr.getValue()),
CalibrationConfig.RansacConfig.createHomographyConfig(
hIterNum.getValue(), hThDist.getValue(), hThInlr.getValue(),
hThPairDist.getValue(), hThAllowedTransformChange.getValue()));
}
}