diff --git a/Compiled plugin/Deprecated versions/Thunder_STORM-ImageJ_mar18.jar b/Compiled plugin/Deprecated versions/Thunder_STORM-ImageJ_mar18.jar new file mode 100644 index 00000000..c5e408eb Binary files /dev/null and b/Compiled plugin/Deprecated versions/Thunder_STORM-ImageJ_mar18.jar differ diff --git a/Compiled plugin/Deprecated versions/Thunder_STORM_FIJI_jan2018.jar b/Compiled plugin/Deprecated versions/Thunder_STORM_FIJI_jan2018.jar new file mode 100644 index 00000000..a58d203e Binary files /dev/null and b/Compiled plugin/Deprecated versions/Thunder_STORM_FIJI_jan2018.jar differ diff --git a/Compiled plugin/Deprecated versions/Thunder_STORM_FIJI_mar18.jar b/Compiled plugin/Deprecated versions/Thunder_STORM_FIJI_mar18.jar new file mode 100644 index 00000000..a8731c03 Binary files /dev/null and b/Compiled plugin/Deprecated versions/Thunder_STORM_FIJI_mar18.jar differ diff --git a/Compiled plugin/Deprecated versions/Thunder_STORM_FIJI_okt2017.jar b/Compiled plugin/Deprecated versions/Thunder_STORM_FIJI_okt2017.jar new file mode 100644 index 00000000..985abedf Binary files /dev/null and b/Compiled plugin/Deprecated versions/Thunder_STORM_FIJI_okt2017.jar differ diff --git a/Compiled plugin/Deprecated versions/Thunder_STORM_ImageJ_jan2018.jar b/Compiled plugin/Deprecated versions/Thunder_STORM_ImageJ_jan2018.jar new file mode 100644 index 00000000..52b0577c Binary files /dev/null and b/Compiled plugin/Deprecated versions/Thunder_STORM_ImageJ_jan2018.jar differ diff --git a/Compiled plugin/Deprecated versions/Thunder_STORM_ImageJ_okt2017.jar b/Compiled plugin/Deprecated versions/Thunder_STORM_ImageJ_okt2017.jar new file mode 100644 index 00000000..2351072b Binary files /dev/null and b/Compiled plugin/Deprecated versions/Thunder_STORM_ImageJ_okt2017.jar differ diff --git a/Compiled plugin/Thunder_STORM_FIJI.jar b/Compiled plugin/Thunder_STORM_FIJI.jar new file mode 100644 index 00000000..c764bc39 Binary files /dev/null and b/Compiled plugin/Thunder_STORM_FIJI.jar differ diff --git a/Compiled plugin/Thunder_STORM_ImageJ.jar b/Compiled plugin/Thunder_STORM_ImageJ.jar new file mode 100644 index 00000000..869b7806 Binary files /dev/null and b/Compiled plugin/Thunder_STORM_ImageJ.jar differ diff --git a/Documentation of changes in ThunderSTORM with Phasor added.docx b/Documentation of changes in ThunderSTORM with Phasor added.docx new file mode 100644 index 00000000..4bf8d6ed Binary files /dev/null and b/Documentation of changes in ThunderSTORM with Phasor added.docx differ diff --git a/README.md b/README.md index 8bd0d9c8..c6e09641 100644 --- a/README.md +++ b/README.md @@ -19,3 +19,6 @@ Install [ImageJ](http://imagej.nih.gov/ij/index.html) and download the latest ve ## How to cite ThunderSTORM If you use ThunderSTORM to process your data, please, cite our [paper](http://dx.doi.org/10.1093/bioinformatics/btu202): * M. Ovesný, P. Křížek, J. Borkovec, Z. Švindrych, G. M. Hagen. _ThunderSTORM: a comprehensive ImageJ plugin for PALM and STORM data analysis and super-resolution imaging._ Bioinformatics 30(16):2389-2390, 2014. + +If you use the phasor sub-pixel estimator, please cite this paper in addition (https://doi.org/10.1101/191957): + * K.J.A. Martens, A.N. Bader, S. Baas, B. Rieger, J. Hohlbein. _Phasor based single-molecule localization microscopy in 3D (pSMLM-3D): an algorithm for MHz localization rates using standard CPUs_ bioRxiv, 2017. \ No newline at end of file diff --git a/pom.xml b/pom.xml index 79dacb81..f23554ac 100755 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ dev-2016-09-10-b1 jar - ThunderSTORM + ThunderSTORM-Phasor_KM ThunderSTORM: a comprehensive ImageJ plugin for PALM and STORM data analysis and super-resolution imaging https://github.com/zitmen/thunderstorm @@ -15,7 +15,7 @@ true UTF-8 1.0.3 - C:\Program Files\ImageJ + C:\Users\Koen Martens\Desktop\Fiji.app 1.49s 1.8 1.8 @@ -270,6 +270,14 @@ swingbox 1.1 + + + org.slf4j + slf4j-api + 1.7.25 + provided + + net.java.balloontip balloontip @@ -311,16 +319,16 @@ 4.11 test - - org.bitbucket.pepa_borkovec - MacroAwareUI - 889b2b553e96e8493bf6a103acfe2197eb0733ca - commons-io commons-io 2.4 jar + + org.bitbucket.pepa_borkovec + MacroAwareUI + 889b2b553e96e8493bf6a103acfe2197eb0733ca + diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/BiPlaneCalibrationPlugin.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/BiPlaneCalibrationPlugin.java index 5ee40983..759c8061 100644 --- a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/BiPlaneCalibrationPlugin.java +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/BiPlaneCalibrationPlugin.java @@ -32,6 +32,7 @@ public class BiPlaneCalibrationPlugin implements PlugIn { String savePath; double stageStep; double zRangeLimit;//in nm + double zzeropos;//in nm ImagePlus imp1, imp2; Roi roi1, roi2; @@ -81,6 +82,7 @@ public void run(String arg) { savePath = dialog.getSavePath(); stageStep = dialog.getStageStep(); zRangeLimit = dialog.getZRangeLimit(); + zzeropos = 0; defocusModel = dialog.getActiveDefocusFunction(); if (!isStack(imp1 = dialog.getFirstPlaneStack())) return; @@ -98,7 +100,7 @@ public void run(String arg) { final ICalibrationProcess process = CalibrationProcessFactory.create( calibrationConfig, selectedFilterUI, selectedDetectorUI, calibrationEstimatorUI, - defocusModel, stageStep, zRangeLimit, imp1, imp2, roi1, roi2); + defocusModel, stageStep, zRangeLimit, imp1, imp2, roi1, roi2, zzeropos); try { process.runCalibration(); diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/CylindricalLensCalibrationPlugin.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/CylindricalLensCalibrationPlugin.java index f94812fc..e89efa5f 100755 --- a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/CylindricalLensCalibrationPlugin.java +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/CylindricalLensCalibrationPlugin.java @@ -1,5 +1,6 @@ package cz.cuni.lf1.lge.ThunderSTORM; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.PhasorAstigmatismCalibrationEstimatorUI; import cz.cuni.lf1.lge.ThunderSTORM.UI.AstigmatismCalibrationDialog; import cz.cuni.lf1.lge.ThunderSTORM.UI.GUI; import cz.cuni.lf1.lge.ThunderSTORM.calibration.*; @@ -24,10 +25,13 @@ public class CylindricalLensCalibrationPlugin implements PlugIn { DefocusFunction defocusModel; IFilterUI selectedFilterUI; IDetectorUI selectedDetectorUI; + IEstimatorUI selectedEstimatorUI; + PhasorAstigmatismCalibrationEstimatorUI phasorCalibrationEstimatorUI; AstigmatismCalibrationEstimatorUI calibrationEstimatorUI; String savePath; double stageStep; double zRangeLimit;//in nm + double zzeropos; ImagePlus imp; Roi roi; @@ -47,9 +51,10 @@ public void run(String arg) { try { //load modules calibrationEstimatorUI = new AstigmatismCalibrationEstimatorUI(); + phasorCalibrationEstimatorUI = new PhasorAstigmatismCalibrationEstimatorUI(); List filters = ModuleLoader.getUIModules(IFilterUI.class); List detectors = ModuleLoader.getUIModules(IDetectorUI.class); - List estimators = Arrays.asList(new IEstimatorUI[]{calibrationEstimatorUI}); // only one estimator can be used + List estimators = Arrays.asList(new IEstimatorUI[]{calibrationEstimatorUI, phasorCalibrationEstimatorUI}); List defocusFunctions = ModuleLoader.getUIModules(DefocusFunction.class); Thresholder.loadFilters(filters); @@ -60,24 +65,59 @@ public void run(String arg) { IJ.handleException(e); } AstigmatismCalibrationDialog dialog; + dialog = new AstigmatismCalibrationDialog(imp, filters, detectors, estimators, defocusFunctions); if(dialog.showAndGetResult() != JOptionPane.OK_OPTION) { return; } selectedFilterUI = dialog.getActiveFilterUI(); selectedDetectorUI = dialog.getActiveDetectorUI(); + selectedEstimatorUI = dialog.getActiveEstimatorUI(); savePath = dialog.getSavePath(); stageStep = dialog.getStageStep(); zRangeLimit = dialog.getZRangeLimit(); defocusModel = dialog.getActiveDefocusFunction(); - + //zzeropos is boolean indicator for setting z=0 at intersection of defocus curves, or at middle of calibration stack - KM + zzeropos = dialog.getZZeroPos(); + + String activeEstimator = ""+selectedEstimatorUI; + String subStringGauss = "ui.AstigmatismCalibrationEstimatorUI"; + String subStringPhasor = "ui.PhasorAstigmatismCalibrationEstimatorUI"; + //IJ.log(activeEstimator); roi = imp.getRoi() != null ? imp.getRoi() : new Roi(0, 0, imp.getWidth(), imp.getHeight()); // perform the calibration + //if Phasor is selected - KM + if (activeEstimator.toLowerCase().contains(subStringPhasor.toLowerCase())){ + final PhasorAstigmaticCalibrationProcess process = (PhasorAstigmaticCalibrationProcess) PhasorCalibrationProcessFactory.create( + new CalibrationConfig(), + selectedFilterUI, selectedDetectorUI, phasorCalibrationEstimatorUI, + defocusModel, stageStep, zRangeLimit, imp, roi, zzeropos); + + try { + process.runCalibration(); + } catch(NoMoleculesFittedException ex) { + // if no beads were succesfully fitted, draw localizations anyway + process.drawOverlay(); + IJ.handleException(ex); + return; + } + process.drawOverlay(); + process.drawSigmaPlots(); + + try { + process.getCalibration(defocusModel).saveToFile(savePath); + } catch(IOException ex) { + UI.showAnotherLocationDialog(ex, process.getCalibration(defocusModel)); + } + } else + //If Gauss is selected, do the standard calibration -KM + { + //The boolean zzeropos is added to differentiate between zero at intersection or middle of calibration stack -KM final AstigmaticCalibrationProcess process = (AstigmaticCalibrationProcess) CalibrationProcessFactory.create( new CalibrationConfig(), selectedFilterUI, selectedDetectorUI, calibrationEstimatorUI, - defocusModel, stageStep, zRangeLimit, imp, roi); + defocusModel, stageStep, zRangeLimit, imp, roi, zzeropos); try { process.runCalibration(); @@ -95,6 +135,7 @@ public void run(String arg) { } catch(IOException ex) { UI.showAnotherLocationDialog(ex, process.getCalibration(defocusModel)); } + } } catch(Exception ex) { IJ.handleException(ex); } diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/ImportExportPlugIn.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/ImportExportPlugIn.java index 57a2420d..1df7678b 100755 --- a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/ImportExportPlugIn.java +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/ImportExportPlugIn.java @@ -353,11 +353,11 @@ public ImportDialog(Window owner, boolean groundTruth) { assert moduleNames != null && moduleNames.length > 0; fileFormat = fileParams.createStringField("fileFormat", StringValidatorFactory.isMember(moduleNames), moduleNames[0]); filePath = fileParams.createStringField("filePath", StringValidatorFactory.fileExists(), ""); - detectMeasurementProtocol = fileParams.createBooleanField("detectMeasurementProtocol", null, true); startingFrame = params.createIntField("startingFrame", IntegerValidatorFactory.positiveNonZero(), 1); append = params.createBooleanField("append", null, false); this.groundTruth = groundTruth; if(!groundTruth) { + detectMeasurementProtocol = fileParams.createBooleanField("detectMeasurementProtocol", null, true); showPreview = params.createBooleanField("livePreview", null, true); rawImageStack = params.createStringField("rawImageStack", StringValidatorFactory.openImages(true), ""); } diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/UI/AstigmatismCalibrationDialog.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/UI/AstigmatismCalibrationDialog.java index 13ec79bd..6d06c49c 100644 --- a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/UI/AstigmatismCalibrationDialog.java +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/UI/AstigmatismCalibrationDialog.java @@ -40,7 +40,10 @@ public class AstigmatismCalibrationDialog extends DialogStub implements ActionLi ParameterKey.String detectorName; ParameterKey.String estimatorName; ParameterKey.String defocusName; + ParameterKey.String zzeropos; + protected transient ParameterKey.String ZZEROPOS; + private List filters; private List detectors; private List estimators; @@ -60,6 +63,8 @@ public AstigmatismCalibrationDialog(ImagePlus imp, List filters, List 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()); + + zzeropos = params.createStringField("ZzeroPosition", null, "Z=0 at intersection of polynomials"); this.filters = filters; this.detectors = detectors; @@ -77,7 +82,6 @@ protected void layoutComponents() { componentConstraints.gridx = 0; componentConstraints.fill = GridBagConstraints.BOTH; componentConstraints.weightx = 1; - JButton cameraSetup = new JButton("Camera setup"); cameraSetup.addActionListener(new ActionListener() { @Override @@ -112,6 +116,15 @@ public void actionPerformed(ActionEvent e) { JPanel aditionalOptions = new JPanel(new GridBagLayout()); aditionalOptions.setBorder(BorderFactory.createTitledBorder("Additional options")); + + + ButtonGroup btnGroup = new ButtonGroup(); + JRadioButton intersectionRadioButton = new JRadioButton("Z=0 at intersection of polynomials"); + JRadioButton middleStackRadioButton = new JRadioButton("Z=0 at middle of image stack"); + btnGroup.add(intersectionRadioButton); + btnGroup.add(middleStackRadioButton); + zzeropos.registerComponent(btnGroup); + aditionalOptions.add(new JLabel("Z stage step [nm]:"), GridBagHelper.leftCol()); JTextField stageStepTextField = new JTextField("", 20); stageStep.registerComponent(stageStepTextField); @@ -130,7 +143,10 @@ public void actionPerformed(ActionEvent e) { gbc.fill = GridBagConstraints.HORIZONTAL; aditionalOptions.add(calibrationPanel, gbc); pane.add(aditionalOptions, componentConstraints); - + aditionalOptions.add(new JLabel("Z calibration: "), GridBagHelper.leftCol()); + aditionalOptions.add(intersectionRadioButton, GridBagHelper.rightCol()); + aditionalOptions.add(middleStackRadioButton, GridBagHelper.rightCol()); + JButton defaults = new JButton("Defaults"); JButton preview = new JButton("Preview"); JButton ok = new JButton("OK"); @@ -295,6 +311,11 @@ public double getZRangeLimit() { return zRangeLimit.getValue(); } + public double getZZeroPos() { + if (zzeropos.getValue()=="Z=0 at middle of image stack"){return 1;} + else {return 0;} + } + @Override public void dispose() { super.dispose(); diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/UI/HelpButton.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/UI/HelpButton.java index 392dfa4f..bb010e1a 100755 --- a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/UI/HelpButton.java +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/UI/HelpButton.java @@ -123,7 +123,7 @@ private void showInTextWindow() throws IOException { //set page shown in browser if(url != null && !url.equals(htmlBrowser.getPage())) { try { - htmlBrowser.setPage(url); + htmlBrowser.setPage(url.toString()); } catch(Exception e) { htmlBrowser.setText("Could not load help file"); } diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/AbstractCalibrationProcess.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/AbstractCalibrationProcess.java index 1e3de221..94349929 100644 --- a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/AbstractCalibrationProcess.java +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/AbstractCalibrationProcess.java @@ -40,6 +40,7 @@ abstract class AbstractCalibrationProcess implements ICalibrationProcess { protected DefocusFunction defocusModel; protected double stageStep; protected double zRange; + protected double zzeropos; // results protected double angle = 0.0; @@ -53,7 +54,7 @@ abstract class AbstractCalibrationProcess implements ICalibrationProcess { protected double[] allSigma1s; protected double[] allSigma2s; - public AbstractCalibrationProcess(CalibrationConfig config, IFilterUI selectedFilterUI, IDetectorUI selectedDetectorUI, ICalibrationEstimatorUI calibrationEstimatorUI, DefocusFunction defocusModel, double stageStep, double zRangeLimit) { + public AbstractCalibrationProcess(CalibrationConfig config, IFilterUI selectedFilterUI, IDetectorUI selectedDetectorUI, ICalibrationEstimatorUI calibrationEstimatorUI, DefocusFunction defocusModel, double stageStep, double zRangeLimit, double zzeropos) { this.config = config; this.selectedFilterUI = selectedFilterUI; this.selectedDetectorUI = selectedDetectorUI; @@ -61,6 +62,7 @@ public AbstractCalibrationProcess(CalibrationConfig config, IFilterUI selectedFi this.defocusModel = defocusModel; this.stageStep = stageStep; this.zRange = zRangeLimit; + this.zzeropos = zzeropos; } /** @@ -123,7 +125,7 @@ public void run(int i) { return MathProxy.atan2(sin, cos) / 4; } - protected void fitQuadraticPolynomials(Collection positions) { + protected void fitQuadraticPolynomials(ImagePlus imp, Collection positions) { // fit a quadratic polynomial to sigma1 = f(zpos) and sigma1 = f(zpos) for each bead IterativeFitting polynomialFitter = new IterativeFitting(config.inlierFittingMaxIters, config.inlierFittingInlierFraction); allPolynomsS1 = new ArrayList(); @@ -144,7 +146,7 @@ protected void fitQuadraticPolynomials(Collection positio if(p.getSize() < config.minimumFitsCount) { continue; } - double z0guess = guessZ0(p); + double z0guess = guessZ0(imp, p); p.discardFitsByFrameRange(z0guess - zRange/stageStep, z0guess + zRange/stageStep); // retrieve values again after filtering out fits not in range @@ -321,7 +323,8 @@ protected static void drawOverlay(ImagePlus imp, Roi roi, List allFits /** * guess z0 of molecule */ - protected double guessZ0(PSFSeparator.Position p) { + protected double guessZ0(ImagePlus imp, PSFSeparator.Position p) { + final ImageStack stack = imp.getStack(); double[] sigma1AsArray = p.getAsArray(LABEL_SIGMA1); double[] sigma2AsArray = p.getAsArray(LABEL_SIGMA2); double[] intensityAsArray = p.getAsArray(LABEL_INTENSITY); @@ -341,7 +344,11 @@ protected double guessZ0(PSFSeparator.Position p) { } } - return p.fits.get(minIdx).getParam(LABEL_FRAME); + double stackSize = stack.getSize(); + if (zzeropos==1){//If zero is at middle of image stack -KM + return (stackSize/2); + }else{//If zero is a polynomial crossover -KM + return p.fits.get(minIdx).getParam(LABEL_FRAME);} } private static double[] flattenListOfArrays(List list) { diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/AstigmaticBiplaneCalibrationProcess.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/AstigmaticBiplaneCalibrationProcess.java index 9a0dc707..6bf476a8 100644 --- a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/AstigmaticBiplaneCalibrationProcess.java +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/AstigmaticBiplaneCalibrationProcess.java @@ -45,8 +45,8 @@ public class AstigmaticBiplaneCalibrationProcess extends AbstractCalibrationProc public AstigmaticBiplaneCalibrationProcess(CalibrationConfig config, IFilterUI selectedFilterUI, IDetectorUI selectedDetectorUI, AstigmaticBiplaneCalibrationEstimatorUI calibrationEstimatorUI, DefocusFunction defocusModel, - double stageStep, double zRangeLimit, ImagePlus imp1, ImagePlus imp2, Roi roi1, Roi roi2) { - super(config, selectedFilterUI, selectedDetectorUI, calibrationEstimatorUI, defocusModel, stageStep, zRangeLimit); + double stageStep, double zRangeLimit, ImagePlus imp1, ImagePlus imp2, Roi roi1, Roi roi2, double zzeropos) { + super(config, selectedFilterUI, selectedDetectorUI, calibrationEstimatorUI, defocusModel, stageStep, zRangeLimit, zzeropos); this.imp1 = imp1; this.imp2 = imp2; this.roi1 = roi1; @@ -60,7 +60,7 @@ public void runCalibration() { IJ.log("angle2 = " + angle2); IJ.log("Homography transformation matrix: " + transformationMatrix.toString()); - fitQuadraticPolynomials(pos12.keySet()); + fitQuadraticPolynomials(imp1, pos12.keySet()); polyS11 = polynomS1Final; polyS12 = polynomS2Final; allPolyS11 = allPolynomsS1; @@ -71,7 +71,7 @@ public void runCalibration() { IJ.log("s11 = " + polynomS1Final.toString()); IJ.log("s12 = " + polynomS2Final.toString()); - fitQuadraticPolynomials(pos12.values()); + fitQuadraticPolynomials(imp1, pos12.values()); polyS21 = polynomS1Final; polyS22 = polynomS2Final; allPolyS21 = allPolynomsS1; @@ -155,7 +155,7 @@ public void drawSigmaPlots() { } @Override - protected double guessZ0(PSFSeparator.Position p) { + protected double guessZ0(ImagePlus imp, PSFSeparator.Position p) { double[] sigma1AsArray = p.getAsArray(LABEL_SIGMA1); double[] sigma2AsArray = p.getAsArray(LABEL_SIGMA2); double[] sigma3AsArray = p.getAsArray(LABEL_SIGMA3); diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/AstigmaticCalibrationProcess.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/AstigmaticCalibrationProcess.java index ee3a6a9a..3e9bb47f 100644 --- a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/AstigmaticCalibrationProcess.java +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/AstigmaticCalibrationProcess.java @@ -18,8 +18,8 @@ public class AstigmaticCalibrationProcess extends AbstractCalibrationProcess { public AstigmaticCalibrationProcess(CalibrationConfig config, IFilterUI selectedFilterUI, IDetectorUI selectedDetectorUI, AstigmatismCalibrationEstimatorUI calibrationEstimatorUI, DefocusFunction defocusModel, - double stageStep, double zRangeLimit, ImagePlus imp, Roi roi) { - super(config, selectedFilterUI, selectedDetectorUI, calibrationEstimatorUI, defocusModel, stageStep, zRangeLimit); + double stageStep, double zRangeLimit, ImagePlus imp, Roi roi, double zzeropos) { + super(config, selectedFilterUI, selectedDetectorUI, calibrationEstimatorUI, defocusModel, stageStep, zRangeLimit, zzeropos); this.imp = imp; this.roi = roi; } @@ -30,7 +30,7 @@ public void runCalibration() { IJ.log("angle = " + angle); beadFits = fitFixedAngle(angle, imp, roi, selectedFilterUI, selectedDetectorUI, calibrationEstimatorUI, defocusModel, config.showResultsTable); - fitQuadraticPolynomials(beadFits.getPositions()); + fitQuadraticPolynomials(imp, beadFits.getPositions()); IJ.log("s1 = " + polynomS1Final.toString()); IJ.log("s2 = " + polynomS2Final.toString()); } diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/BiplaneCalibrationProcess.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/BiplaneCalibrationProcess.java index 9b6c950c..80f14b39 100644 --- a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/BiplaneCalibrationProcess.java +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/BiplaneCalibrationProcess.java @@ -25,8 +25,8 @@ public class BiplaneCalibrationProcess extends AbstractCalibrationProcess { public BiplaneCalibrationProcess(CalibrationConfig config, IFilterUI selectedFilterUI, IDetectorUI selectedDetectorUI, BiplaneCalibrationEstimatorUI calibrationEstimatorUI, DefocusFunction defocusModel, - double stageStep, double zRangeLimit, ImagePlus imp1, ImagePlus imp2, Roi roi1, Roi roi2) { - super(config, selectedFilterUI, selectedDetectorUI, calibrationEstimatorUI, defocusModel, stageStep, zRangeLimit); + double stageStep, double zRangeLimit, ImagePlus imp1, ImagePlus imp2, Roi roi1, Roi roi2, double zzeropos) { + super(config, selectedFilterUI, selectedDetectorUI, calibrationEstimatorUI, defocusModel, stageStep, zRangeLimit, zzeropos); this.imp1 = imp1; this.imp2 = imp2; this.roi1 = roi1; @@ -38,7 +38,7 @@ public void runCalibration() { Collection positions = fitPositions(); IJ.log("Homography transformation matrix: " + transformationMatrix.toString()); - fitQuadraticPolynomials(positions); + fitQuadraticPolynomials(imp1, positions); IJ.log("s1 = " + polynomS1Final.toString()); IJ.log("s2 = " + polynomS2Final.toString()); } diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/CalibrationProcessFactory.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/CalibrationProcessFactory.java index 332ce236..bf128327 100644 --- a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/CalibrationProcessFactory.java +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/CalibrationProcessFactory.java @@ -12,20 +12,20 @@ public final class CalibrationProcessFactory { public static AbstractCalibrationProcess create(CalibrationConfig config, IFilterUI selectedFilterUI, IDetectorUI selectedDetectorUI, ICalibrationEstimatorUI calibrationEstimatorUI, - DefocusFunction defocusModel, double stageStep, double zRangeLimit, ImagePlus imp1, ImagePlus imp2, Roi roi1, Roi roi2) { + DefocusFunction defocusModel, double stageStep, double zRangeLimit, ImagePlus imp1, ImagePlus imp2, Roi roi1, Roi roi2, double zzeropos) { if (calibrationEstimatorUI instanceof BiplaneCalibrationEstimatorUI) { - return new BiplaneCalibrationProcess(config, selectedFilterUI, selectedDetectorUI, (BiplaneCalibrationEstimatorUI) calibrationEstimatorUI, defocusModel, stageStep, zRangeLimit, imp1, imp2, roi1, roi2); + return new BiplaneCalibrationProcess(config, selectedFilterUI, selectedDetectorUI, (BiplaneCalibrationEstimatorUI) calibrationEstimatorUI, defocusModel, stageStep, zRangeLimit, imp1, imp2, roi1, roi2, zzeropos); } else if (calibrationEstimatorUI instanceof AstigmaticBiplaneCalibrationEstimatorUI) { - return new AstigmaticBiplaneCalibrationProcess(config, selectedFilterUI, selectedDetectorUI, (AstigmaticBiplaneCalibrationEstimatorUI) calibrationEstimatorUI, defocusModel, stageStep, zRangeLimit, imp1, imp2, roi1, roi2); + return new AstigmaticBiplaneCalibrationProcess(config, selectedFilterUI, selectedDetectorUI, (AstigmaticBiplaneCalibrationEstimatorUI) calibrationEstimatorUI, defocusModel, stageStep, zRangeLimit, imp1, imp2, roi1, roi2, zzeropos); } else { throw new IllegalArgumentException("Unknown instance of astigmatic calibration estimator!"); } } public static AbstractCalibrationProcess create(CalibrationConfig config, IFilterUI selectedFilterUI, IDetectorUI selectedDetectorUI, ICalibrationEstimatorUI calibrationEstimatorUI, - DefocusFunction defocusModel, double stageStep, double zRangeLimit, ImagePlus imp, Roi roi) { + DefocusFunction defocusModel, double stageStep, double zRangeLimit, ImagePlus imp, Roi roi, double zzeropos) { if (calibrationEstimatorUI instanceof AstigmatismCalibrationEstimatorUI) { - return new AstigmaticCalibrationProcess(config, selectedFilterUI, selectedDetectorUI, (AstigmatismCalibrationEstimatorUI) calibrationEstimatorUI, defocusModel, stageStep, zRangeLimit, imp, roi); + return new AstigmaticCalibrationProcess(config, selectedFilterUI, selectedDetectorUI, (AstigmatismCalibrationEstimatorUI) calibrationEstimatorUI, defocusModel, stageStep, zRangeLimit, imp, roi, zzeropos); } else { throw new IllegalArgumentException("Unknown instance of astigmatic calibration estimator!"); } diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/DefocusFunctionPoly.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/DefocusFunctionPoly.java index 2c9c0c67..91bcc70a 100755 --- a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/DefocusFunctionPoly.java +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/DefocusFunctionPoly.java @@ -18,7 +18,7 @@ * I am not sure how to do it in a simple way using the Apache Commons Math library */ public class DefocusFunctionPoly extends DefocusFunction { - + public static final String name = "ThunderSTORM"; public DefocusFunctionPoly() { diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/IterativeFitting.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/IterativeFitting.java index bbb853c2..1420d25d 100755 --- a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/IterativeFitting.java +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/IterativeFitting.java @@ -49,6 +49,9 @@ private double[] fit(double[] x, double[] y, ParametricUnivariateFunction functi } else { throw ex; } + } //KM - Adding debugging for catching negative values + catch(org.apache.commons.math3.exception.NotStrictlyPositiveException ex) { + return parameters; } } return parameters; diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/PhasorAbstractCalibrationProcess.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/PhasorAbstractCalibrationProcess.java new file mode 100644 index 00000000..5f8dc088 --- /dev/null +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/PhasorAbstractCalibrationProcess.java @@ -0,0 +1,599 @@ +package cz.cuni.lf1.lge.ThunderSTORM.calibration; + +import cz.cuni.lf1.lge.ThunderSTORM.UI.RenderingOverlay; +import cz.cuni.lf1.lge.ThunderSTORM.detectors.ui.IDetectorUI; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.Molecule; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.MoleculeDescriptor; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.ICalibrationEstimatorUI; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.PhasorAstigmatismCalibrationEstimatorUI; +import cz.cuni.lf1.lge.ThunderSTORM.filters.ui.IFilterUI; +import cz.cuni.lf1.lge.ThunderSTORM.results.IJResultsTable; +import cz.cuni.lf1.lge.ThunderSTORM.thresholding.Thresholder; +import cz.cuni.lf1.lge.ThunderSTORM.util.Loop; +import cz.cuni.lf1.lge.ThunderSTORM.util.MathProxy; +import cz.cuni.lf1.lge.ThunderSTORM.util.Point; +import cz.cuni.lf1.lge.ThunderSTORM.util.VectorMath; +import ij.IJ; +import ij.ImagePlus; +import ij.ImageStack; +import ij.gui.Plot; +import ij.gui.Roi; +import ij.process.FloatProcessor; +import ij.process.ImageProcessor; +import org.apache.commons.math3.exception.TooManyEvaluationsException; + +import java.awt.*; +import java.io.FileWriter; +import java.util.*; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import static cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.MoleculeDescriptor.LABEL_FRAME; +import static cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.PSFModel.Params.*; + +abstract class PhasorAbstractCalibrationProcess implements ICalibrationProcess { + + + // processing + protected CalibrationConfig config; + protected IFilterUI selectedFilterUI; + protected IDetectorUI selectedDetectorUI; + protected ICalibrationEstimatorUI calibrationEstimatorUI; + protected DefocusFunction defocusModel; + protected double stageStep; + protected double zRange; + protected double zzeropos; + + // results + protected double angle = 0; + protected Homography.TransformationMatrix transformationMatrix = null; + protected List usedPositions; + protected DefocusFunction polynomS2Final; + protected DefocusFunction polynomS1Final; + protected ArrayList allPolynomsS1; + protected ArrayList allPolynomsS2; + protected double[] allFrames; + protected double[] allSigma1s; + protected double[] allSigma2s; + + public PhasorAbstractCalibrationProcess(CalibrationConfig config, IFilterUI selectedFilterUI, IDetectorUI selectedDetectorUI, ICalibrationEstimatorUI calibrationEstimatorUI, DefocusFunction defocusModel, double stageStep, double zRangeLimit, double zzeropos) { + this.config = config; + this.selectedFilterUI = selectedFilterUI; + this.selectedDetectorUI = selectedDetectorUI; + this.calibrationEstimatorUI = calibrationEstimatorUI; + this.defocusModel = defocusModel; + this.stageStep = stageStep; + this.zRange = zRangeLimit; + this.zzeropos = zzeropos; + } + + /** + * Estimates the rotation angle of the cylindrical lens. If the lens is + * aligned with camera or the angle is known, you can use setAngle(double) + * instead. + */ + protected double estimateAngle(ImagePlus imp, final Roi roi) { + final List angles = Collections.synchronizedList(new ArrayList()); + final ImageStack stack = imp.getStack(); + final AtomicInteger framesProcessed = new AtomicInteger(0); + final ICalibrationEstimatorUI threadLocalEstimatorUI = calibrationEstimatorUI; + Loop.withIndex(1, stack.getSize() + 1, new Loop.BodyWithIndex() { + @Override + public void run(int i) { + ImageProcessor ip = stack.getProcessor(i); + ip.setRoi(roi.getBounds()); + FloatProcessor fp = (FloatProcessor) ip.crop().convertToFloat(); + fp.setMask(roi.getMask()); + Thresholder.setCurrentImage(fp); + List fits = threadLocalEstimatorUI.getThreadLocalImplementation().estimateParameters(fp, + Point.applyRoiMask(roi, selectedDetectorUI.getThreadLocalImplementation().detectMoleculeCandidates(selectedFilterUI.getThreadLocalImplementation().filterImage(fp)))); + framesProcessed.incrementAndGet(); + + for (Molecule psf : fits) { + double s1 = psf.getParam(LABEL_SIGMA1); + double s2 = psf.getParam(LABEL_SIGMA2); + double ratio = s1 / s2; + if (ratio > 2 || ratio < 0.5) { + continue; + } + if (ratio < 1.2 && ratio > 0.83) { + continue; + } + angles.add(psf.getParam(LABEL_ANGLE)); + } + IJ.showProgress(0.45 * (double) framesProcessed.intValue() / (double) stack.getSize()); + IJ.showStatus("Determining angle: frame " + framesProcessed + " of " + stack.getSize() + "..."); + } + }); + // calculation of circular mean + List sins = new ArrayList(angles); + List coss = new ArrayList(angles); + for(int i = 0; i < angles.size(); i++) { + double fittedAngle = angles.get(i); + //modulo 2PI/4 + fittedAngle = fittedAngle % (MathProxy.PI / 2); + //modulo of a negative number is defined to be negative in java, so translate it to range [0,pi/2] + if(fittedAngle < 0) { + fittedAngle += MathProxy.PI / 2; + } + //*4 + fittedAngle *= 4; + + sins.set(i, MathProxy.sin(fittedAngle)); + coss.set(i, MathProxy.cos(fittedAngle)); + } + double sin = bootstrapMeanEstimation(sins, 100, angles.size()); + double cos = bootstrapMeanEstimation(coss, 100, angles.size()); + return MathProxy.atan2(sin, cos) / 4; + } + + protected void fitQuadraticPolynomials(ImagePlus imp, Collection positions) { + // fit a quadratic polynomial to sigma1 = f(zpos) and sigma1 = f(zpos) for each bead + IterativeFitting polynomialFitter = new IterativeFitting(config.inlierFittingMaxIters, config.inlierFittingInlierFraction); + allPolynomsS1 = new ArrayList(); + allPolynomsS2 = new ArrayList(); + + List framesArrays = new ArrayList(); + List sigma1Arrays = new ArrayList(); + List sigma2Arrays = new ArrayList(); + + + AtomicInteger moleculesProcessed = new AtomicInteger(0); + usedPositions = new ArrayList(); + for(PSFSeparator.Position p : positions) { + moleculesProcessed.incrementAndGet(); + IJ.showProgress(0.9 + 0.1 * (double) moleculesProcessed.intValue() / (double) positions.size()); + IJ.showStatus("Fitting polynoms: molecule " + moleculesProcessed + " of " + positions.size() + "..."); + + try { + //if(p.getSize() < config.minimumFitsCount) { + // continue; + //}else{ + //IJ.log("Not enough fits to continue: "+p.getSize());} + + double z0guess = guessZ0(imp,p); + p.discardFitsByFrameRange(z0guess - zRange/stageStep, z0guess + zRange/stageStep); + + // retrieve values again after filtering out fits not in range + double[] framesArrayOfZ = p.getFramesAsArrayOfZ(z0guess, stageStep); + + double[] sigma12Array = new double[p.getAsArray(LABEL_SIGMA1).length]; + double[] sigma21Array = new double[p.getAsArray(LABEL_SIGMA1).length]; + for (int i=0;i fits = estimator.getThreadLocalImplementation().estimateParameters(fp, + Point.applyRoiMask(roi, detector.getThreadLocalImplementation().detectMoleculeCandidates(filter.getThreadLocalImplementation().filterImage(fp)))); + framesProcessed.incrementAndGet(); + + for(Molecule fit : fits) { + fit.insertParamAt(0, MoleculeDescriptor.LABEL_FRAME, MoleculeDescriptor.Units.UNITLESS, i); + separator.add(fit); + if (showResultsTable) { + IJResultsTable.getResultsTable().addRow(fit); + } + } + IJ.showProgress(0.45 + 0.45 * (double) framesProcessed.intValue() / (double) stack.getSize()); + IJ.showStatus("Fitting " + LABEL_SIGMA1 + " and " + LABEL_SIGMA2 + ": frame " + framesProcessed + " of " + stack.getSize() + "..."); + } + }); + for (PSFSeparator.Position p : separator.getPositions()) { + p.sortFitsByFrame(); + p.validate(); + } + if (showResultsTable) { + IJResultsTable.getResultsTable().sortTableByFrame(); + IJResultsTable.getResultsTable().deleteColumn(LABEL_Z); // not applicable here + IJResultsTable.getResultsTable().deleteColumn(LABEL_Z_REL); // not applicable here + IJResultsTable.getResultsTable().show(); + } + return separator; + } + + private static double bootstrapMeanEstimation(List values, int resamples, int sampleSize) { + Random rnd = new Random(System.nanoTime()); + + double finalMean = 0; + for(int i = 0; i < resamples; i++) { + double intermediateMean = 0; + for(int j = 0; j < sampleSize; j++) { + intermediateMean += values.get(rnd.nextInt(values.size())); + } + intermediateMean = intermediateMean / sampleSize; + finalMean += intermediateMean; + } + finalMean /= resamples; + return finalMean; + } + + /** + * draws overlay with each detection and also the positions of beads that + * were used for fitting polynomials + */ + protected static void drawOverlay(ImagePlus imp, Roi roi, List allFits, List usedPositions) { + imp.setOverlay(null); + Rectangle roiBounds = roi.getBounds(); + + //allFits + Map> fitsByFrame = new HashMap>(allFits.size()); + for(Molecule mol : allFits) { + int frame = (int) mol.getParam(LABEL_FRAME); + List list; + if(!fitsByFrame.containsKey(frame)) { + list = new ArrayList(); + fitsByFrame.put(frame, list); + } else { + list = fitsByFrame.get(frame); + } + list.add(mol); + } + for(Map.Entry> frameFitsEntry : fitsByFrame.entrySet()) { + int frame = frameFitsEntry.getKey(); + List fits = frameFitsEntry.getValue(); + double[] xAll = new double[fits.size()]; + double[] yAll = new double[fits.size()]; + for(int i = 0; i < fits.size(); i++) { + Molecule mol = fits.get(i); + xAll[i] = mol.getX(MoleculeDescriptor.Units.PIXEL) + roiBounds.x; + yAll[i] = mol.getY(MoleculeDescriptor.Units.PIXEL) + roiBounds.y; + } + RenderingOverlay.showPointsInImage(imp, xAll, yAll, frame, Color.BLUE, RenderingOverlay.MARKER_CROSS); + } + + //centroids of used molecules + double[] xCentroids = new double[usedPositions.size()]; + double[] yCentroids = new double[usedPositions.size()]; + for(int i = 0; i < xCentroids.length; i++) { + PSFSeparator.Position p = usedPositions.get(i); + xCentroids[i] = p.centroidX + roiBounds.x; + yCentroids[i] = p.centroidY + roiBounds.y; + } + RenderingOverlay.showPointsInImage(imp, xCentroids, yCentroids, Color.red, RenderingOverlay.MARKER_CIRCLE); + //usedFits + for(PSFSeparator.Position p : usedPositions) { + double[] frame = p.getAsArray(LABEL_FRAME); + double[] x = VectorMath.add(p.getAsArray(LABEL_X), roiBounds.x); + double[] y = VectorMath.add(p.getAsArray(LABEL_Y), roiBounds.y); + for(int i = 0; i < frame.length; i++) { + RenderingOverlay.showPointsInImage(imp, new double[]{x[i]}, new double[]{y[i]}, (int) frame[i], Color.RED, RenderingOverlay.MARKER_CROSS); + } + } + } + + /** + * guess z0 of molecule + */ + protected double guessZ0(ImagePlus imp, PSFSeparator.Position p) { + final ImageStack stack = imp.getStack(); + //Returning frame where sigmaX/sigmaY == 1 to Z=ZERO + double[] sigma1AsArray = p.getAsArray(LABEL_SIGMA1); + double[] sigma2AsArray = p.getAsArray(LABEL_SIGMA2); + //double[] intensityAsArray = p.getAsArray(LABEL_INTENSITY); + + double[] ratios = new double[sigma1AsArray.length]; + for(int i = 0; i < sigma1AsArray.length; i++) { + ratios[i] = Math.max(sigma1AsArray[i], sigma2AsArray[i]) / Math.min(sigma1AsArray[i], sigma2AsArray[i]); + //ratios[i] /= intensityAsArray[i]; + } + + ratios = VectorMath.movingAverage(ratios, config.movingAverageLag); + + int minIdx = 0; + for(int i = 1; i < ratios.length; i++) { + if(ratios[i] < ratios[minIdx]) { + minIdx = i; + } + } + double stackSize = stack.getSize(); + //IJ.log("Some params: "+sigma1AsArray.length+" | "+p.getSize() +" | "+(double) stack.getSize()); + //IJ.log("Get zero: giving "+p.fits.get(minIdx).getParam(LABEL_FRAME)); + if (zzeropos==1){//If zero is at middle of image stack + return (stackSize/2);//p.fits.get(minIdx).getParam(LABEL_FRAME)- + }else{//If zero is a polynomial crossover + return p.fits.get(minIdx).getParam(LABEL_FRAME);} + + } + + private static double[] flattenListOfArrays(List list) { + int allFitsCount = 0; + for(double[] ds : list) { + allFitsCount += ds.length; + } + double[] retVal = new double[allFitsCount]; + int idx = 0; + for (double[] aList : list) { + for (double anAList : aList) { + retVal[idx++] = anAList; + } + } + return retVal; + } + + private boolean isInZRange(double z) { + return z >= -zRange && z <= +zRange; + } + + private boolean hasEnoughData(double[] framesArray, double intersection) { + int smallerThanCenter = 0; + for (double aFramesArray : framesArray) { + if (aFramesArray < intersection) { + smallerThanCenter++; + } + } + int greaterThanCenter = framesArray.length - smallerThanCenter; + + //If all three statements here are false, it returns 1 + return !(smallerThanCenter < config.minFitsInZRange + || greaterThanCenter < config.minFitsInZRange + || framesArray.length < config.minimumFitsCount); + } + + protected static List filterPositions(PSFSeparator fits, int minimumFitsCount) { + List ret = new ArrayList(); + for (PSFSeparator.Position fit : fits.getPositions()) { + if (fit.getSize() >= minimumFitsCount) { + ret.add(fit); + } + } + return ret; + } + + protected static void drawSigmaPlots(Plot plot, double[] xVals, SigmaPlotConfig cfg) { + // add points + plot.setColor(cfg.allSigma1sColor); + plot.addPoints(cfg.allFrames, cfg.allSigma1s, Plot.CROSS); + plot.setColor(cfg.allSigma2sColor); + plot.addPoints(cfg.allFrames, cfg.allSigma2s, Plot.CROSS); + + // add polynomials + for(int i = 0; i < cfg.allPolynomsS1.size(); i++) { + double[] sigma1Vals = new double[xVals.length]; + double[] sigma2Vals = new double[xVals.length]; + for(int j = 0; j < sigma1Vals.length; j++) { + sigma1Vals[j] = cfg.allPolynomsS1.get(i).value(xVals[j]); + sigma2Vals[j] = cfg.allPolynomsS2.get(i).value(xVals[j]); + } + plot.setColor(cfg.allPolynomsS1Color); + plot.addPoints(xVals, sigma1Vals, Plot.LINE); + plot.setColor(cfg.allPolynomsS2Color); + plot.addPoints(xVals, sigma2Vals, Plot.LINE); + } + + // add final fitted curves + double[] sigma1ValsAll = new double[xVals.length]; + double[] sigma2ValsAll = new double[xVals.length]; + for(int j = 0; j < sigma1ValsAll.length; j++) { + sigma1ValsAll[j] = cfg.polynomS1Final.value(xVals[j]); + sigma2ValsAll[j] = cfg.polynomS2Final.value(xVals[j]); + } + plot.setColor(cfg.polynomS1FinalColor); + plot.addPoints(xVals, sigma1ValsAll, Plot.LINE); + plot.setColor(cfg.polynomS2FinalColor); + plot.addPoints(xVals, sigma2ValsAll, Plot.LINE); + + //legend + plot.setColor(cfg.polynomS1FinalColor); + plot.addLabel(cfg.legend1X, cfg.legend1Y, cfg.legend1Label); + plot.setColor(cfg.polynomS2FinalColor); + plot.addLabel(cfg.legend2X, cfg.legend2Y, cfg.legend2Label); + } + + private void showHistoImages() { + FloatProcessor a1 = new FloatProcessor(1, allPolynomsS1.size()); + FloatProcessor a2 = new FloatProcessor(1, allPolynomsS1.size()); + FloatProcessor b1 = new FloatProcessor(1, allPolynomsS1.size()); + FloatProcessor b2 = new FloatProcessor(1, allPolynomsS1.size()); + FloatProcessor cdif = new FloatProcessor(1, allPolynomsS1.size()); + + for(int i = 0; i < allPolynomsS1.size(); i++) { + a1.setf(i, (float) allPolynomsS1.get(i).getA()); + b1.setf(i, (float) allPolynomsS1.get(i).getB()); + a2.setf(i, (float) allPolynomsS2.get(i).getA()); + b2.setf(i, (float) allPolynomsS2.get(i).getB()); + cdif.setf(i, (float) (allPolynomsS2.get(i).getC() - allPolynomsS1.get(i).getC())); + } + new ImagePlus("a1", a1).show(); + new ImagePlus("a2", a2).show(); + new ImagePlus("b1", b1).show(); + new ImagePlus("b2", b2).show(); + new ImagePlus("cdif", cdif).show(); + } + + private void dumpShiftedPoints() { + try { + FileWriter fw = new FileWriter("d:\\dump.txt"); + fw.append("allFrames:\n"); + fw.append(Arrays.toString(allFrames)); + fw.append("\nallSigma1:\n"); + fw.append(Arrays.toString(allSigma1s)); + fw.append("\nallSigma2:\n"); + fw.append(Arrays.toString(allSigma2s)); + fw.close(); + } catch(Exception ex) { + IJ.handleException(ex); + } + } + + @Override + public void drawSigmaPlots() { + // config + SigmaPlotConfig cfg = new SigmaPlotConfig(); + cfg.allFrames = allFrames; + cfg.allSigma1s = allSigma1s; + cfg.allSigma2s = allSigma2s; + cfg.allPolynomsS1 = allPolynomsS1; + cfg.allPolynomsS2 = allPolynomsS2; + cfg.polynomS1Final = polynomS1Final; + cfg.polynomS2Final = polynomS2Final; + cfg.allSigma1sColor = new Color(255, 200, 200); + cfg.allSigma2sColor = new Color(200, 200, 255); + cfg.allPolynomsS1Color = new Color(255, 230, 230); + cfg.allPolynomsS2Color = new Color(230, 230, 255); + cfg.polynomS1FinalColor = new Color(255, 0, 0); + cfg.polynomS2FinalColor = new Color(0, 0, 255); + cfg.legend1X = 0.1; + cfg.legend1Y = 0.8; + cfg.legend1Label = "sigma1"; + cfg.legend2X = 0.1; + cfg.legend2Y = 0.9; + cfg.legend2Label = "sigma2"; + + // create and setup plot + Plot plot = new Plot("Sigma", "z [nm]", "sigma [px]", null, (float[]) null); + plot.setSize(1024, 768); + plot.setLimits(-2*zRange, +2*zRange, 0, 5); + double[] xVals = new double[(int)(2*zRange/stageStep) * 2 + 1]; + for(int val = -2*(int)zRange, i = 0; val <= +2*(int)zRange; val += stageStep, i++) { + xVals[i] = val; + } + plot.draw(); + + // plot + drawSigmaPlots(plot, xVals, cfg); + + // display + plot.show(); + } + + protected static class SigmaPlotConfig { + public double[] allFrames; + public double[] allSigma1s; + public double[] allSigma2s; + public List allPolynomsS1; + public List allPolynomsS2; + public DefocusFunction polynomS1Final; + public DefocusFunction polynomS2Final; + + public Color allSigma1sColor; + public Color allSigma2sColor; + public Color allPolynomsS1Color; + public Color allPolynomsS2Color; + public Color polynomS1FinalColor; + public Color polynomS2FinalColor; + + public double legend1X; + public double legend1Y; + public String legend1Label; + public double legend2X; + public double legend2Y; + public String legend2Label; + } +} diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/PhasorAstigmaticCalibrationProcess.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/PhasorAstigmaticCalibrationProcess.java new file mode 100644 index 00000000..7fb8a3f5 --- /dev/null +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/PhasorAstigmaticCalibrationProcess.java @@ -0,0 +1,48 @@ +package cz.cuni.lf1.lge.ThunderSTORM.calibration; + +import static cz.cuni.lf1.lge.ThunderSTORM.calibration.PhasorAbstractCalibrationProcess.fitFixedAngle; +import cz.cuni.lf1.lge.ThunderSTORM.detectors.ui.IDetectorUI; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.PhasorAstigmatismCalibrationEstimatorUI; +import cz.cuni.lf1.lge.ThunderSTORM.filters.ui.IFilterUI; +import ij.IJ; +import ij.ImagePlus; +import ij.gui.Roi; + +public class PhasorAstigmaticCalibrationProcess extends PhasorAbstractCalibrationProcess { + + // processing + ImagePlus imp; + Roi roi; + + // results + private PSFSeparator beadFits; + + public PhasorAstigmaticCalibrationProcess(CalibrationConfig config, IFilterUI selectedFilterUI, IDetectorUI selectedDetectorUI, + PhasorAstigmatismCalibrationEstimatorUI calibrationEstimatorUI, DefocusFunction defocusModel, + double stageStep, double zRangeLimit, ImagePlus imp, Roi roi, double zzeropos) { + super(config, selectedFilterUI, selectedDetectorUI, calibrationEstimatorUI, defocusModel, stageStep, zRangeLimit, zzeropos); + this.imp = imp; + this.roi = roi; + } + + @Override + public void runCalibration() { + //Adding 0.001 to escape angle=0 errors + angle = estimateAngle(imp, roi);//+0.0927; + + beadFits = fitFixedAngle(angle, imp, roi, selectedFilterUI, selectedDetectorUI, calibrationEstimatorUI, defocusModel, config.showResultsTable); + fitQuadraticPolynomials(imp, beadFits.getPositions()); + //To get fitradius: calibrationEstimatorUI.getFitradius() + IJ.log("s1 = " + polynomS1Final.toString()); + IJ.log("s2 = " + polynomS2Final.toString()); + } + + public DefocusCalibration getCalibration(DefocusFunction defocusModel) { + return defocusModel.getCalibration(angle, null, polynomS1Final, polynomS2Final); + } + + @Override + public void drawOverlay() { + drawOverlay(imp, roi, beadFits.getAllFits(), usedPositions); + } +} diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/PhasorCalibrationProcessFactory.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/PhasorCalibrationProcessFactory.java new file mode 100644 index 00000000..4abc204f --- /dev/null +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/calibration/PhasorCalibrationProcessFactory.java @@ -0,0 +1,33 @@ +package cz.cuni.lf1.lge.ThunderSTORM.calibration; + +import cz.cuni.lf1.lge.ThunderSTORM.detectors.ui.IDetectorUI; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.AstigmaticBiplaneCalibrationEstimatorUI; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.PhasorAstigmatismCalibrationEstimatorUI; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.BiplaneCalibrationEstimatorUI; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.ICalibrationEstimatorUI; +import cz.cuni.lf1.lge.ThunderSTORM.filters.ui.IFilterUI; +import ij.ImagePlus; +import ij.gui.Roi; + +public final class PhasorCalibrationProcessFactory { + + public static AbstractCalibrationProcess create(CalibrationConfig config, IFilterUI selectedFilterUI, IDetectorUI selectedDetectorUI, ICalibrationEstimatorUI calibrationEstimatorUI, + DefocusFunction defocusModel, double stageStep, double zRangeLimit, ImagePlus imp1, ImagePlus imp2, Roi roi1, Roi roi2, double zzeropos) { + if (calibrationEstimatorUI instanceof BiplaneCalibrationEstimatorUI) { + return new BiplaneCalibrationProcess(config, selectedFilterUI, selectedDetectorUI, (BiplaneCalibrationEstimatorUI) calibrationEstimatorUI, defocusModel, stageStep, zRangeLimit, imp1, imp2, roi1, roi2, zzeropos); + } else if (calibrationEstimatorUI instanceof AstigmaticBiplaneCalibrationEstimatorUI) { + return new AstigmaticBiplaneCalibrationProcess(config, selectedFilterUI, selectedDetectorUI, (AstigmaticBiplaneCalibrationEstimatorUI) calibrationEstimatorUI, defocusModel, stageStep, zRangeLimit, imp1, imp2, roi1, roi2,zzeropos); + } else { + throw new IllegalArgumentException("Unknown instance of astigmatic calibration estimator!"); + } + } + + public static PhasorAbstractCalibrationProcess create(CalibrationConfig config, IFilterUI selectedFilterUI, IDetectorUI selectedDetectorUI, ICalibrationEstimatorUI calibrationEstimatorUI, + DefocusFunction defocusModel, double stageStep, double zRangeLimit, ImagePlus imp, Roi roi, double zzeropos) { + if (calibrationEstimatorUI instanceof PhasorAstigmatismCalibrationEstimatorUI) { + return new PhasorAstigmaticCalibrationProcess(config, selectedFilterUI, selectedDetectorUI, (PhasorAstigmatismCalibrationEstimatorUI) calibrationEstimatorUI, defocusModel, stageStep, zRangeLimit, imp, roi, zzeropos); + } else { + throw new IllegalArgumentException("Unknown instance of astigmatic calibration estimator!"); + } + } +} diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/MultipleLocationsImageFitting.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/MultipleLocationsImageFitting.java index 83834ac3..9c8bb8e1 100755 --- a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/MultipleLocationsImageFitting.java +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/MultipleLocationsImageFitting.java @@ -101,8 +101,13 @@ public void run() throws StoppedByUserException { psf.setX(psf.getX() + xInt + 0.5); psf.setY(psf.getY() + yInt + 0.5); psf.setDetections(null); - appendGoodnessOfFit(psf, fitter, subImage); - appendCalculatedUncertainty(psf); + if (fitter instanceof PhasorFitter){ + //appendGoodnessOfFit(psf, fitter, subImage); + //appendCalculatedUncertainty(psf); + }else{ + appendGoodnessOfFit(psf, fitter, subImage); + appendCalculatedUncertainty(psf); + } results.add(psf); } } else { diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/PSF/MoleculeDescriptor.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/PSF/MoleculeDescriptor.java index f676b056..8a91ebd1 100755 --- a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/PSF/MoleculeDescriptor.java +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/PSF/MoleculeDescriptor.java @@ -9,10 +9,13 @@ import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.EllipticGaussianEstimatorUI; import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.SymmetricGaussianEstimatorUI; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.PhasorEstimatorUI; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.PhasorFitterUI; import cz.cuni.lf1.lge.ThunderSTORM.results.IJResultsTable; import cz.cuni.lf1.lge.ThunderSTORM.results.MeasurementProtocol; import cz.cuni.lf1.lge.ThunderSTORM.util.Pair; import cz.cuni.lf1.lge.ThunderSTORM.util.VectorMath; +import ij.IJ; import java.util.Arrays; import java.util.EnumMap; import java.util.HashMap; @@ -336,11 +339,23 @@ public static double uncertaintyXY(Molecule molecule) throws UncertaintyNotAppli } else if (protocol.analysisEstimator instanceof SymmetricGaussianEstimatorUI) { // 2D? Gauss or IntGauss fittingMethod = ((SymmetricGaussianEstimatorUI) protocol.analysisEstimator).getMethod(); tau = 2.0 * PI * bkgStd*bkgStd * (psfSigma2 + pixelSize*pixelSize/12.0) / (psfPhotons * pixelSize*pixelSize); + } else if (protocol.analysisEstimator instanceof PhasorFitterUI){//Phasor fitting - 2D or 3D + //For now, phasor gives uncertainly just like MLE/WLSQ - This is most definitely wrong, but still gives some relative value hopefully + fittingMethod = ((PhasorFitterUI) protocol.analysisEstimator).getMethod(); //Give either Phasor or PhasorAstig + if (fittingMethod == "PhasorAstig"){ + DaostormCalibration cal = ((PhasorFitterUI) protocol.analysisEstimator).getDaoCalibration(); + double l2 = abs(cal.getC1() * cal.getC2()); + double d2 = abs(cal.getD1() * cal.getD2()); + tau = 2.0 * PI * bkgStd*bkgStd * (psfSigma2*(1.0 + l2/d2) + pixelSize*pixelSize/12.0) / (psfPhotons * pixelSize*pixelSize); + }else{ + tau = 2.0 * PI * bkgStd*bkgStd * (psfSigma2 + pixelSize*pixelSize/12.0) / (psfPhotons * pixelSize*pixelSize); + } } if (fittingMethod != null) { if (fittingMethod.equals(SymmetricGaussianEstimatorUI.MLE) - || fittingMethod.equals(SymmetricGaussianEstimatorUI.WLSQ)) { + || fittingMethod.equals(SymmetricGaussianEstimatorUI.WLSQ) + || fittingMethod == "Phasor" || fittingMethod == "PhasorAstig") { // Note: here we don't distinguish between MLE and WLSQ, however, there is a difference! // For details, see supplementary note for Mortensen 2010, Eq. (46), which shows an extra offset-dependent term! return sqrt((gain * psfSigma2 + pixelSize*pixelSize/12.0) / psfPhotons * (1.0 + 4.0*tau + sqrt(2.0*tau/(1 + 4.0*tau)))); @@ -362,7 +377,14 @@ public static double uncertaintyZ(Molecule molecule) throws UncertaintyNotApplic MeasurementProtocol protocol = IJResultsTable.getResultsTable().getMeasurementProtocol(); if (!(protocol.analysisEstimator instanceof EllipticGaussianEstimatorUI) || !(molecule.hasParam(LABEL_SIGMA1) && molecule.hasParam(LABEL_SIGMA2))) { + //Add mention of PhasorAstig - if it's astig, go on, else, throw error + if (protocol.analysisEstimator instanceof PhasorFitterUI){ + if (((PhasorFitterUI) protocol.analysisEstimator).getMethod() != "PhasorAstig"){ + throw new UncertaintyNotApplicableException("Axial uncertainty cannot be calculated for 2D estimate (missing sigma1, sigma2)!"); + } + }else{ throw new UncertaintyNotApplicableException("Axial uncertainty cannot be calculated for 2D estimate (missing sigma1, sigma2)!"); + } } double gain, readout; @@ -381,10 +403,19 @@ public static double uncertaintyZ(Molecule molecule) throws UncertaintyNotApplic double zCoord = molecule.hasParam(LABEL_Z_REL) ? molecule.getParam(LABEL_Z_REL, Units.NANOMETER) : molecule.getParam(LABEL_Z, Units.NANOMETER); - - DaostormCalibration cal = ((EllipticGaussianEstimatorUI) protocol.analysisEstimator).getDaoCalibration(); - double l2 = abs(cal.getC1() * cal.getC2()); - double d2 = abs(cal.getD1() * cal.getD2()); + + double l2 = 0.0; + double d2 = 0.0; + if (protocol.analysisEstimator instanceof EllipticGaussianEstimatorUI){ + DaostormCalibration cal = ((EllipticGaussianEstimatorUI) protocol.analysisEstimator).getDaoCalibration(); + l2 = abs(cal.getC1() * cal.getC2()); + d2 = abs(cal.getD1() * cal.getD2()); + } + else if (protocol.analysisEstimator instanceof PhasorFitterUI){ + DaostormCalibration cal = ((PhasorFitterUI) protocol.analysisEstimator).getDaoCalibration(); + l2 = abs(cal.getC1() * cal.getC2()); + d2 = abs(cal.getD1() * cal.getD2()); + } double tau = 2.0 * PI * bkgStd*bkgStd * (psfSigma1*psfSigma2*(1.0 + l2/d2) + pixelSize*pixelSize/12.0) / (psfPhotons * pixelSize*pixelSize); double zLimit = sqrt(l2 + d2); // singularity in CRLB - do not evaluate at positions beyond if (abs(zCoord) >= zLimit) return Double.POSITIVE_INFINITY; @@ -394,10 +425,16 @@ public static double uncertaintyZ(Molecule molecule) throws UncertaintyNotApplic / 2.0; // finite pixel size and em gain compensation // double stdSigma; // method-dependent parameter - String fittingMethod = ((EllipticGaussianEstimatorUI) protocol.analysisEstimator).getMethod(); + String fittingMethod = ""; + if (protocol.analysisEstimator instanceof EllipticGaussianEstimatorUI){ + fittingMethod = ((EllipticGaussianEstimatorUI) protocol.analysisEstimator).getMethod(); + } else if (protocol.analysisEstimator instanceof PhasorFitterUI){ + fittingMethod = ((PhasorFitterUI) protocol.analysisEstimator).getMethod(); + } if (fittingMethod != null) { if (fittingMethod.equals(SymmetricGaussianEstimatorUI.MLE) - || fittingMethod.equals(SymmetricGaussianEstimatorUI.WLSQ)) { + || fittingMethod.equals(SymmetricGaussianEstimatorUI.WLSQ) + || fittingMethod == "PhasorAstig") { // Note: here we don't distinguish between MLE and WLSQ, however, there is a difference! // For details, see supplementary note for Mortensen 2010, Eq. (46), which shows an extra offset-dependent term! stdSigma = sqrt(1 + 8.0 * tau + sqrt(9.0 * tau / (1.0 + 4.0 * tau))) * compensation / sqrt(psfPhotons); diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/PhasorFitter.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/PhasorFitter.java new file mode 100644 index 00000000..95d2dac5 --- /dev/null +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/PhasorFitter.java @@ -0,0 +1,347 @@ +package cz.cuni.lf1.lge.ThunderSTORM.estimators; + +//import cz.cuni.lf1.lge.ThunderSTORM.CameraSetupPlugIn; +import cz.cuni.lf1.lge.ThunderSTORM.calibration.DefocusCalibration; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.Molecule; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.PSFModel; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.PSFModel.Params; +import ij.IJ; +import java.util.Arrays; + +/** + * Rewritten to java from the matlab implementation + * By Koen Martens + * March 2017 + * + */ +public class PhasorFitter implements IOneLocationFitter { + DefocusCalibration calibration = null; + double fi, a1, b1, c1, d1, a2, b2, c2, imsizedouble, dist, centersize, ringsize, totsignal, backgroundstd, fitcos[], fitsin[], fitomega[], noisearray[][], signalarray[][]; + int fitradius, imsizeint; + String calname; + boolean Astigmatism; + + public PhasorFitter(int fitradius, DefocusCalibration calibration) { + this.calibration = calibration; + this.calname = calibration.getName(); + this.a1 = calibration.getA1(); + this.b1 = calibration.getB1(); + this.c1 = calibration.getC1(); + this.a2 = calibration.getA2(); + this.b2 = calibration.getB2(); + this.c2 = calibration.getC2(); + this.d1 = calibration.getD1(); + Astigmatism = true; + this.fitradius = fitradius; + //Calculate parts of the fitting that are only dependant on the fitradius + this.imsizeint = 2*fitradius+1; + this.imsizedouble = 2*fitradius+1; + this.fitomega = new double[imsizeint]; + this.fitcos = new double[imsizeint]; + this.fitsin = new double[imsizeint]; + for (int indi = 0; indi < imsizeint; indi++){ + fitomega[indi] = (indi/imsizedouble)*2*Math.PI; + fitcos[indi] = Math.cos(fitomega[indi]); + fitsin[indi] = Math.sin(fitomega[indi]); + } + //Calculate regions for background and signal + centersize = imsizeint-2; + ringsize = 2; + this.noisearray = new double[imsizeint][imsizeint]; + this.signalarray = new double[imsizeint][imsizeint]; + for (int xx = 0; xx < imsizeint; xx++){ + for (int yy = 0; yy < imsizeint; yy++){ + //get distance to center + dist = Math.sqrt(Math.abs(xx-(imsizedouble-1)/2)*Math.abs(xx-(imsizedouble-1)/2)+Math.abs(yy-(imsizedouble-1)/2)*Math.abs(yy-(imsizedouble-1)/2)); + if (dist > (centersize+1)/2){ + if (dist <= (ringsize+(centersize+1)/2)){ + noisearray[xx][yy] = 1; + }else{ + noisearray[xx][yy] = 0; + } + }else{ + noisearray[xx][yy] = 0; + } + if (dist <= (centersize+1)/2){ + signalarray[xx][yy] = 1; + }else{ + signalarray[xx][yy] = 0; + } + } + } + } + public PhasorFitter(int fitradius){ + Astigmatism = false; + this.fitradius = fitradius; + //Calculate parts of the fitting that are only dependant on the fitradius + this.imsizeint = 2*fitradius+1; + this.imsizedouble = 2*fitradius+1; + this.fitomega = new double[imsizeint]; + this.fitcos = new double[imsizeint]; + this.fitsin = new double[imsizeint]; + for (int indi = 0; indi < imsizeint; indi++){ + fitomega[indi] = (indi/imsizedouble)*2*Math.PI; + fitcos[indi] = Math.cos(fitomega[indi]); + fitsin[indi] = Math.sin(fitomega[indi]); + } + //Calculate regions for background and signal + centersize = imsizeint-2; + ringsize = 2; + this.noisearray = new double[imsizeint][imsizeint]; + this.signalarray = new double[imsizeint][imsizeint]; + for (int xx = 0; xx < imsizeint; xx++){ + for (int yy = 0; yy < imsizeint; yy++){ + //get distance to center + dist = Math.sqrt(Math.abs(xx-(imsizedouble-1)/2)*Math.abs(xx-(imsizedouble-1)/2)+Math.abs(yy-(imsizedouble-1)/2)*Math.abs(yy-(imsizedouble-1)/2)); + if (dist > (centersize+1)/2){ + if (dist <= (ringsize+(centersize+1)/2)){ + noisearray[xx][yy] = 1; + }else{ + noisearray[xx][yy] = 0; + } + }else{ + noisearray[xx][yy] = 0; + } + if (dist <= (centersize+1)/2){ + signalarray[xx][yy] = 1; + }else{ + signalarray[xx][yy] = 0; + } + } + } + } + //Fitting below + @Override + public Molecule fit(SubImage img) { + //long startTime = System.nanoTime(); + + //Variable initiation + double totResult[] = new double[6]; + double omega = 2.0 * Math.PI / img.size_x; + double axy[][] = new double[img.size_x][img.size_y]; + double totalint = 0; + //Loop through all positions in array but in an X/Y manner: + //First calculate total intensity of matrix, then set each point, normalized by total intensity + for (int y = 0; y < img.size_x; y++) { + for (int x = 0; x < img.size_x; x++) { + totalint += img.values[x+y*img.size_x]; + } + } + for (int y = 0; y < img.size_x; y++) { + for (int x = 0; x < img.size_x; x++) { + axy[x][y] = img.values[x+y*img.size_x]/totalint; + } + } + //Do the partial Fourier transformation (only acquiring first order harmonics) + totResult = twoDfft(axy,fitcos,fitsin); + //Re-assign variables to something readable + double FirstHarmonicXRe = totResult[0]; + double FirstHarmonicXIm = totResult[1]; + double AmplitudeX = totResult[2]; + double FirstHarmonicYRe = totResult[3]; + double FirstHarmonicYIm = totResult[4]; + double AmplitudeY = totResult[5]; + //Backup next line from change 19/3/2018 + //double angY=(Math.PI-Math.atan(FirstHarmonicYIm/FirstHarmonicYRe))*-1; + //New line from change 19/3/2018 - atan2 resembles matlab more + double angY=Math.atan2(FirstHarmonicYIm,FirstHarmonicYRe); + //Removed strange part when 3x3 matrix is used at 19/3/2018 + //This used to be adding 1 PI when angle was below -PI + if (angY>0){ + angY=angY-2*Math.PI; + } + //Backup next line from change 19/3/2018 + //double angX=(Math.PI-Math.atan(FirstHarmonicXIm/FirstHarmonicXRe))*-1; + //New line from change 19/3/2018 - atan2 resembles matlab more + double angX=Math.atan2(FirstHarmonicXIm,FirstHarmonicXRe); + //Removed strange part when 3x3 matrix is used at 19/3/2018 + //This used to be adding 1 PI when angle was below -PI + if (angX>0){ + angX=angX-2*Math.PI; + } + + //Calculate X and Y positions + double xpos = (Math.abs(angY)/omega) - (img.size_x-1)/2;// -0.125;//+0.25; + double ypos = (Math.abs(angX)/omega) - (img.size_y-1)/2;// +0.125*1.5; + //Calculate corrected amplitudes for astigmatism + //double abslengthx = -1*AmplitudeX+1;//*-1+1; + //double abslengthy = -1*AmplitudeY+1;//*-1+1; + double abslengthx = AmplitudeX;//*-1+1; + double abslengthy = AmplitudeY;//*-1+1; + + //Set z-position to 0 if no astigmatism is used + double zpos = 0; + //If a 3D calibration astigmatism file is loaded + if (Astigmatism) { + // Old astigmatism calibration based on 2 curves + //Calculate subparts of the 3rd-factor function (done with Wolfram Alpha) + //double div = abslengthx/abslengthy; + //double part1 = a1*c1*c1*a2*div-2*a1*c1*a2*c2*div-a1*b1+a1*a2*c2*c2*div+a1*b2*div+b1*a2*div-a2*b2*div*div; + //double zpos1 = (-1*Math.sqrt(part1)+a1*c1-a2*c2*div)/(a1-a2*div); + //double zpos2 = (Math.sqrt(part1)+a1*c1-a2*c2*div)/(a1-a2*div); + //double zpos3 = (c1*c1*a2*div+b1-a2*c2*c2*div+b2*div)/(2*a2*div*(c1-c2)); + //Choose the correct zpos - the one that is closest to 0. + //zpos3 is chosen if div lies below the curve - error preventing + //if(div == a1/a2){zpos = zpos3;} + //else if (Math.abs(zpos2) < Math.abs(zpos1)) + //{ + // zpos = zpos2; + //} + //else{ + // zpos = zpos1; + //} + //Prevent NaN-errors + //if(Double.isNaN(zpos)){zpos=0;} + //End old astigmatism calibration + + //New astigmatism calibration: If magn1>magn2, then use sigma1Cali, otherwise sigma2Cali (i.e. 'linear' piece of calibration curves) + //ATM assuming ThunderSTORM calibration is used! + //Check if ThunderSTORM calibration is used - if not, don't give any Zpos and warn the user + if (calname.equals("ThunderSTORM")){ + //If clearly R1, use R1 + //double b = b1; + //double a = a1; + if (AmplitudeX/AmplitudeY > 1){//THIS Is the positive part in Christophe's data! + if (c1>c2){ + zpos = -1*Math.sqrt(((AmplitudeX/AmplitudeY)-b1)/a1)+c1; + }else{ + zpos = Math.sqrt(((AmplitudeX/AmplitudeY)-b2)/a2)-c2; + } + }else{ + if (c1>c2){ + zpos = Math.sqrt(((AmplitudeY/AmplitudeX)-b2)/a2)+c2; + }else{ + zpos = -1*Math.sqrt(((AmplitudeY/AmplitudeX)-b1)/a1)-c1; + } + } + //Prevent NaN-errors + if(Double.isNaN(zpos)){zpos=0;} + + }else{ + zpos = 0; + IJ.log("Please use the ThunderSTORM defocus curve option for calibration curves if using pSMLM-3D!"); + } + + } + + //Get background and signal levels + int totsignalarraysize = 0; + totsignal = 0; + int totbgnonzeros = 0; + int totbgzeros = 0; + double totbgarray[] = new double[axy.length*axy.length]; + for (int i = 0; i < axy.length; i++) { + for (int j = 0; j < axy.length; j++) { + //Noise + if (noisearray[i][j] > 0){ + totbgarray[i*axy.length+j] = axy[i][j]*totalint; + totbgnonzeros += 1; + }else{ + totbgzeros +=1; + } + //Signal + if (signalarray[i][j] > 0){ + totsignal += axy[i][j]*totalint; + totsignalarraysize += 1; + } + } + } + //Get Xth percentile + Arrays.sort(totbgarray); + int arraypercentile = (int) Math.ceil(totbgnonzeros*.56); + double xthpercentile = totbgarray[arraypercentile + totbgzeros]; + + //Get value for intensity, all > 0 + double intensity = Math.max(0,totsignal - xthpercentile * totsignalarraysize); + double background = (xthpercentile); + int id = 0; + double backgroundstdarr[] = new double[totbgarray.length-(totbgzeros)]; + for (int i = totbgzeros; i < totbgarray.length; i++){ + backgroundstdarr[id] = totbgarray[i]; + id+=1; + } + backgroundstd = getStdDev(backgroundstdarr); + double[] parameters = new double[]{xpos, ypos, zpos, intensity, background, backgroundstd, abslengthx, abslengthy}; + + return new Molecule(new Params(new int[]{PSFModel.Params.X, PSFModel.Params.Y, PSFModel.Params.Z, PSFModel.Params.INTENSITY, PSFModel.Params.OFFSET, PSFModel.Params.BACKGROUND, PSFModel.Params.SIGMA1, PSFModel.Params.SIGMA2}, parameters, false)); + } + public static double[] twoDfft(double[][] inputData, double[] cos, double[] sin) + { + //Initialize variables + double height = inputData.length; + double width = inputData[0].length; + double[] totxloopRe = new double[inputData.length]; + double[] totxloopIm = new double[inputData.length]; + double FirstHarmonicXRe = 0; + double FirstHarmonicXIm = 0; + double[] totyloopRe = new double[inputData.length]; + double[] totyloopIm = new double[inputData.length]; + double FirstHarmonicYRe = 0; + double FirstHarmonicYIm = 0; + double[] totalArray = new double[6]; + //loop over the rows of the input data, multiply by real and imag parts of omega1 column + for (int rowloop = 0; rowloop < height; rowloop++) + { + for (int colloop = 0; colloop < width; colloop++) + { + //Calculate real (cos) and imag (-sin) parts of the current row. + totxloopRe[colloop] += cos[colloop]*inputData[rowloop][colloop]; + totxloopIm[colloop] -= sin[colloop]*inputData[rowloop][colloop]; + } + } + //Sum all the rows to get the first order harmonic in X + for (int colloop = 0; colloop < width; colloop++) { + FirstHarmonicXRe += totxloopRe[colloop]; + FirstHarmonicXIm += totxloopIm[colloop]; + } + //Calculate amplitude + double AmplitudeX = Math.sqrt(FirstHarmonicXRe/height*FirstHarmonicXRe/height+FirstHarmonicXIm/height*FirstHarmonicXIm/height); + + //loop over the columnss of the input data, multiply by real and imag parts of omega1 row + for (int colloop = 0; colloop < width; colloop++) + { + for (int rowloop = 0; rowloop < height; rowloop++) + { + //Calculate real (cos) and imag (-sin) parts of the current column. + totyloopRe[rowloop] += cos[rowloop]*inputData[rowloop][colloop]; + totyloopIm[rowloop] -= sin[rowloop]*inputData[rowloop][colloop]; + } + } + //Sum all the rows to get the first order harmonic in Y + for (int rowloop = 0; rowloop < width; rowloop++) { + FirstHarmonicYRe += totyloopRe[rowloop]; + FirstHarmonicYIm += totyloopIm[rowloop]; + } + //Calculate amplitude + double AmplitudeY = Math.sqrt(FirstHarmonicYRe/height*FirstHarmonicYRe/height+FirstHarmonicYIm/height*FirstHarmonicYIm/height); + + //Placing the 6 variables in a array to be provided to the user + totalArray[0]=FirstHarmonicXRe; + totalArray[1]=FirstHarmonicXIm; + totalArray[2]=AmplitudeX; + totalArray[3]=FirstHarmonicYRe; + totalArray[4]=FirstHarmonicYIm; + totalArray[5]=AmplitudeY; + + //Return the values + return totalArray; + } + double getMean(double[] inputData){ + double sum = 0; + for(int a = 0; a < inputData.length; a++) + sum += inputData[a]; + return sum/inputData.length; + } + + double getVariance(double[] inputData){ + double mean = getMean(inputData); + double temp = 0; + for(int a = 0; a < inputData.length; a++) + temp += (inputData[a]-mean)*(inputData[a]-mean); + return temp/(inputData.length); + } + + double getStdDev(double[] inputData){ + return Math.sqrt(getVariance(inputData)); + } +} \ No newline at end of file diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorAstigmatismCalibrationEstimatorUI.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorAstigmatismCalibrationEstimatorUI.java new file mode 100644 index 00000000..75866d98 --- /dev/null +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorAstigmatismCalibrationEstimatorUI.java @@ -0,0 +1,94 @@ +package cz.cuni.lf1.lge.ThunderSTORM.estimators.ui; + +import cz.cuni.lf1.lge.ThunderSTORM.calibration.DefocusFunction; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.*; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.EllipticGaussianPSF; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.EllipticGaussianWAnglePSF; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.PSFModel; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.PSFModel.Params; +import static cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.SymmetricGaussianEstimatorUI.LSQ; +import static cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.SymmetricGaussianEstimatorUI.MLE; +import static cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.SymmetricGaussianEstimatorUI.WLSQ; + +import javax.swing.*; + +public class PhasorAstigmatismCalibrationEstimatorUI extends PhasorEstimatorUI implements ICalibrationEstimatorUI { + + private final String name = "Phasor Astigmatism"; + private double angle; + private boolean angleWasSet = false; + private DefocusFunction defocus = null; + + public PhasorAstigmatismCalibrationEstimatorUI() { + super(); + crowdedField = new CrowdedFieldEstimatorUI() { + @Override + MFA_LSQFitter getLSQImplementation(PSFModel psf, double sigma) { + return null; + } + + @Override + MFA_MLEFitter getMLEImplementation(PSFModel psf, double sigma) { + return null; + } + + @Override + public void resetToDefaults() { + } + + @Override + public void readMacroOptions(String options) { + } + + @Override + public void recordOptions() { + } + + @Override + public void readParameters() { + } + + @Override + public JPanel getOptionsPanel(JPanel panel) { + return panel; + } + + @Override + public boolean isEnabled() { + return false; + } + }; + } + + @Override + public String getName() { + return name; + } + + public void setAngle(double fixedAngle) { + this.angle = fixedAngle; + angleWasSet = true; + } + + public void setDefocusModel(DefocusFunction defocus) { + this.defocus = defocus; + } + + public void unsetAngle() { + angleWasSet = false; + } + + public int getFitradius() { + return parameters.getInt(FITRAD); + } + + @Override + public IEstimator getImplementation() { + //String method = METHOD.getValue(); + //double sigma = SIGMA.getValue(); + int fitradius = FITRAD.getValue(); + //PSFModel psf = angleWasSet ? new EllipticGaussianPSF(sigma, angle) : new EllipticGaussianWAnglePSF(sigma, 0); + return new MultipleLocationsImageFitting(fitradius, new PhasorFitter(fitradius)); + //throw new IllegalArgumentException("Unknown fitting method: " + method); + } +} diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorEstimatorUI.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorEstimatorUI.java new file mode 100644 index 00000000..95d83f5d --- /dev/null +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorEstimatorUI.java @@ -0,0 +1,91 @@ +package cz.cuni.lf1.lge.ThunderSTORM.estimators.ui; + +import cz.cuni.lf1.lge.ThunderSTORM.estimators.FullImageFitting; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.LSQFitter; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.IEstimator; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.MLEFitter; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.MultipleLocationsImageFitting; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.IOneLocationFitter; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.PSFModel.Params; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.SymmetricGaussianPSF; +import cz.cuni.lf1.lge.ThunderSTORM.util.GridBagHelper; +import cz.cuni.lf1.lge.ThunderSTORM.util.MacroUI.ParameterKey; +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.MacroUI.validators.StringValidatorFactory; +import ij.IJ; +import java.awt.GridBagLayout; +import javax.swing.ButtonGroup; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; + +public class PhasorEstimatorUI extends IEstimatorUI { + + public transient static final String name = "Phasor"; + // + //protected String name = "Phasor"; + protected int fittingRadius; + protected boolean fullImageFitting; + protected CrowdedFieldEstimatorUI crowdedField; + //params + protected transient ParameterKey.Integer FITRAD; + + public PhasorEstimatorUI() { + crowdedField = new CrowdedFieldEstimatorUI(); + FITRAD = parameters.createIntField("fitradius", IntegerValidatorFactory.positiveNonZero(), 3); + } + + @Override + public String getName() { + return name; + } + + public String getMethod() { + return null; + } + + @Override + public JPanel getOptionsPanel() { + JTextField fitregsizeTextField = new JTextField("", 20); + parameters.registerComponent(FITRAD, fitregsizeTextField); + JPanel panel = new JPanel(new GridBagLayout()); + panel.add(new JLabel("Fitting radius [px]:"), GridBagHelper.leftCol()); + panel.add(fitregsizeTextField, GridBagHelper.rightCol()); + + parameters.loadPrefs(); + return panel; + } + + + @Override + public void readParameters() { + super.readParameters(); + crowdedField.readParameters(); + } + + @Override + public IEstimator getImplementation() { + return null; + } + + @Override + public void recordOptions() { + super.recordOptions(); + crowdedField.recordOptions(); + } + + @Override + public void readMacroOptions(String options) { + super.readMacroOptions(options); + crowdedField.readMacroOptions(options); + } + + @Override + public void resetToDefaults() { + super.resetToDefaults(); + crowdedField.resetToDefaults(); + } +} diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI.java new file mode 100644 index 00000000..e5ebf8d0 --- /dev/null +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI.java @@ -0,0 +1,235 @@ +package cz.cuni.lf1.lge.ThunderSTORM.estimators.ui; + +import cz.cuni.lf1.lge.ThunderSTORM.calibration.DaostormCalibration; +import cz.cuni.lf1.lge.ThunderSTORM.calibration.DefocusCalibration; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.IEstimator; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.LSQFitter; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.MultipleLocationsImageFitting; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.PhasorFitter; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.IEstimatorUI; +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.IntegerValidatorFactory; +import cz.cuni.lf1.lge.ThunderSTORM.util.MacroUI.validators.StringValidatorFactory; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import ij.Prefs; +import ij.IJ; +import ij.ImagePlus; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.io.FileNotFoundException; +import java.io.FileReader; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.filechooser.FileNameExtensionFilter; +import org.yaml.snakeyaml.Yaml; + +public class PhasorFitterUI extends IEstimatorUI { + public transient static final String Phasor = "pSMLM2D"; + public transient static final String PhasorAstig = "pSMLMastigmatism"; + + private final String name = "Phasor Fitting"; + private boolean astigmatism; + private int fittingRadius; + private int fitregsize; + private String calibrationFile; + private String method; + //private transient ParameterKey.Integer FITRADPhasor; + //private transient ParameterKey.String CALIBRATION_PATH; + //private transient ParameterKey.Boolean USE_ASTIGMATISM; + JCheckBox astigmatismCheckBox = new JCheckBox("enable"); + private DaostormCalibration daoCalibration; // internal variable for calculation of uncertainty + + public DefocusCalibration calibration; + private String calibrationFilePath; + + protected transient ParameterTracker params; + protected transient ParameterKey.Boolean USE_ASTIGMATISM; + protected transient ParameterKey.Integer FITRADPhasor; + protected transient ParameterKey.String CALIBRATION_PATH; + protected transient ParameterKey.String METHOD; + + public PhasorFitterUI() { + params = new ParameterTracker("thunderstorm.estimators.phasor.isthreedim"); + ParameterTracker.Condition enabledPhasorAstigmatism = new ParameterTracker.Condition() { + @Override + public boolean isSatisfied() { + return USE_ASTIGMATISM.getValue(); + } + + @Override + public ParameterKey[] dependsOn() { + return new ParameterKey[]{USE_ASTIGMATISM}; + } + }; + + FITRADPhasor= params.createIntField("fitradius", IntegerValidatorFactory.positiveNonZero(), 2); + USE_ASTIGMATISM = params.createBooleanField("astigmatism", null, false); + CALIBRATION_PATH = params.createStringField("calibrationpath", null, "", enabledPhasorAstigmatism); + //METHOD = params.createStringField("method", StringValidatorFactory.isMember(new String[]{PhasorAstig, Phasor}), Phasor); + } + + @Override + public String getName() { + return name; + } + public String getMethod() { + loadValues(); + if (USE_ASTIGMATISM.getValue()){//(astigmatismCheckBox.isSelected()){ + return "PhasorAstig"; + }else{ + return "Phasor"; + } + //return method; + } + + public DaostormCalibration getDaoCalibration() { + if (daoCalibration == null) { + daoCalibration = calibration.getDaoCalibration(); + } + return daoCalibration; + } + + public boolean isEnabled() { + return USE_ASTIGMATISM.getValue(); + } + + @Override + public JPanel getOptionsPanel() { + //final JCheckBox astigmatismCheckBox = new JCheckBox("enable",true); + + JTextField fitregsizeTextField = new JTextField("", 20); + JPanel panel = new JPanel(new GridBagLayout()); + panel.add(new JLabel("Fit radius [px]:"), GridBagHelper.leftCol()); + panel.add(fitregsizeTextField, GridBagHelper.rightCol()); + params.registerComponent(FITRADPhasor, fitregsizeTextField); + + panel.add(new JLabel("Use 3D astigmatism:"), GridBagHelper.leftCol()); + panel.add(astigmatismCheckBox, GridBagHelper.rightCol()); + params.registerComponent(USE_ASTIGMATISM, astigmatismCheckBox); + + panel.add(new JLabel("Astigmatism calibration file:"), GridBagHelper.leftCol()); + final JTextField calibrationFileTextField = new JTextField(Prefs.get("thunderstorm.estimators.calibrationpath", "")); + JButton findCalibrationButton = DialogStub.createBrowseButton(calibrationFileTextField, true, new FileNameExtensionFilter("Yaml file", "yaml")); + JPanel calibrationPanel = new JPanel(new BorderLayout()){ + @Override + public Dimension getPreferredSize() { + return ((JTextField) params.getRegisteredComponent(FITRADPhasor)).getPreferredSize(); + } + }; + calibrationPanel.add(calibrationFileTextField, BorderLayout.CENTER); + calibrationPanel.add(findCalibrationButton, BorderLayout.EAST); + GridBagConstraints gbc = GridBagHelper.rightCol(); + panel.add(calibrationPanel, gbc); + //Set enabled status of calibration file finder to status of checkbox + calibrationFileTextField.setEnabled(astigmatismCheckBox.isSelected()); + findCalibrationButton.setEnabled(astigmatismCheckBox.isSelected()); + params.registerComponent(CALIBRATION_PATH, calibrationFileTextField); + + //If checkbox is changed, change status of calibration file finder + astigmatismCheckBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + calibrationFileTextField.setEnabled(astigmatismCheckBox.isSelected()); + findCalibrationButton.setEnabled(astigmatismCheckBox.isSelected()); + } + }); + params.loadPrefs(); + + return panel; + } + + @Override + public void readParameters() { + super.readParameters(); + params.readDialogOptions(); + params.savePrefs(); + } + + @Override + public void recordOptions() { + super.recordOptions(); + params.recordMacroOptions(); + } + + @Override + public void readMacroOptions(String options) { + super.readMacroOptions(options); + params.readMacroOptions(); + } + + @Override + public void resetToDefaults() { + super.resetToDefaults(); + params.resetToDefaults(true); + } + + @Override + protected ParameterTracker getParameterTracker() { + return params; + } + + @Override + public IEstimator getImplementation() { + //3D Phasor + /*if (astigmatismCheckBox.isSelected()){ + loadValues(); + //calibration = loadCalibration(params.getString(CALIBRATION_PATH)); + //return new MultipleLocationsImageFitting(fittingRadius = FITRADPhasor.getValue(), new PhasorFitter(FITRADPhasor.getValue(),calibration)); + calibration = loadCalibration(calibrationFile); + return new MultipleLocationsImageFitting(fitregsize,new PhasorFitter(fitregsize,calibration)); + }//2D Phasor + else { + loadValues(); + //return new MultipleLocationsImageFitting(fittingRadius = FITRADPhasor.getValue(), new PhasorFitter(FITRADPhasor.getValue())); + return new MultipleLocationsImageFitting(fitregsize,new PhasorFitter(fitregsize)); + }*/ + //method = METHOD.getValue(); + method = getMethod(); + loadValues(); + if (astigmatism){ + return new MultipleLocationsImageFitting(fittingRadius = getROIsize(),new PhasorFitter(getROIsize(),calibration = loadCalibration(getCalibrationPath()))); + }else{ + return new MultipleLocationsImageFitting(fittingRadius = getROIsize(),new PhasorFitter(getROIsize())); + } + } + + private void loadValues() { + fitregsize = getROIsize(); + astigmatism = isEnabled(); + if (astigmatism){ + calibrationFile = getCalibrationPath(); + } + //method = getMethod(); + } + + public int getROIsize() { + return FITRADPhasor.getValue(); + } + + public String getCalibrationPath() { + return CALIBRATION_PATH.getValue(); + } + + private DefocusCalibration loadCalibration(String calibrationFilePath) { + this.calibrationFilePath = getCalibrationPath();//calibrationFilePath; + try { + Yaml yaml = new Yaml(); + Object loaded = yaml.load(new FileReader(calibrationFilePath)); + return (DefocusCalibration) loaded; + } catch(FileNotFoundException ex) { + throw new RuntimeException("Could not read calibration file.", ex); + } catch(ClassCastException ex) { + throw new RuntimeException("Could not parse calibration file.", ex); + } + } +} diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI2D.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI2D.java new file mode 100644 index 00000000..4aa5da23 --- /dev/null +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI2D.java @@ -0,0 +1,71 @@ +package cz.cuni.lf1.lge.ThunderSTORM.estimators.ui; + +import cz.cuni.lf1.lge.ThunderSTORM.calibration.DaostormCalibration; +import cz.cuni.lf1.lge.ThunderSTORM.calibration.DefocusCalibration; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.IEstimator; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.LSQFitter; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.MultipleLocationsImageFitting; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.PhasorFitter; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.IEstimatorUI; +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.validators.IntegerValidatorFactory; +import cz.cuni.lf1.lge.ThunderSTORM.util.MacroUI.validators.StringValidatorFactory; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import ij.Prefs; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.io.FileNotFoundException; +import java.io.FileReader; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.filechooser.FileNameExtensionFilter; +import org.yaml.snakeyaml.Yaml; + +public class PhasorFitterUI2D extends IEstimatorUI { + + private final String name = "Phasor-based localisation 2D"; + private int fittingRadius; + private transient ParameterKey.Integer FITRADPhasor; + + public PhasorFitterUI2D() { + FITRADPhasor= parameters.createIntField("fitradius", IntegerValidatorFactory.positiveNonZero(), 2); + } + + @Override + public String getName() { + return name; + } + public String getMethod() { + + return "Phasor"; + } + + @Override + public JPanel getOptionsPanel() { + + JTextField fitregsizeTextField = new JTextField("", 20); + JPanel panel = new JPanel(new GridBagLayout()); + panel.add(new JLabel("Fit radius [px]:"), GridBagHelper.leftCol()); + panel.add(fitregsizeTextField, GridBagHelper.rightCol()); + parameters.registerComponent(FITRADPhasor, fitregsizeTextField); + parameters.loadPrefs(); + + return panel; + } + + @Override + public IEstimator getImplementation() { + return new MultipleLocationsImageFitting(fittingRadius = FITRADPhasor.getValue(), new PhasorFitter(FITRADPhasor.getValue())); + + } + +} diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI3D.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI3D.java new file mode 100644 index 00000000..6d542f81 --- /dev/null +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI3D.java @@ -0,0 +1,119 @@ +package cz.cuni.lf1.lge.ThunderSTORM.estimators.ui; + +import cz.cuni.lf1.lge.ThunderSTORM.calibration.DaostormCalibration; +import cz.cuni.lf1.lge.ThunderSTORM.calibration.DefocusCalibration; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.IEstimator; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.LSQFitter; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.MultipleLocationsImageFitting; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.PhasorFitter; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.IEstimatorUI; +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.validators.IntegerValidatorFactory; +import cz.cuni.lf1.lge.ThunderSTORM.util.MacroUI.validators.StringValidatorFactory; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import ij.Prefs; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.io.FileNotFoundException; +import java.io.FileReader; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.filechooser.FileNameExtensionFilter; +import org.yaml.snakeyaml.Yaml; + +public class PhasorFitterUI3D extends IEstimatorUI { + + private final String name = "Phasor-based localisation 3D astigmatism"; + private int fittingRadius; + private transient ParameterKey.Integer FITRADPhasor; + private transient ParameterKey.String CALIBRATION_PATH; + private transient DaostormCalibration daoCalibration; // internal variable for calculation of uncertainty + + public DefocusCalibration calibration; + private String calibrationFilePath; + + public PhasorFitterUI3D() { + FITRADPhasor= parameters.createIntField("fitradius", IntegerValidatorFactory.positiveNonZero(), 2); + CALIBRATION_PATH = parameters.createStringField("calibrationpath", null, ""); + } + + @Override + public String getName() { + return name; + } + public String getMethod() { + + return "PhasorAstig"; + } + + public DaostormCalibration getDaoCalibration() { + if (daoCalibration == null) { + daoCalibration = calibration.getDaoCalibration(); + } + return daoCalibration; + } + + @Override + public JPanel getOptionsPanel() { + //final JCheckBox astigmatismCheckBox = new JCheckBox("enable"); + + JTextField fitregsizeTextField = new JTextField("", 20); + JPanel panel = new JPanel(new GridBagLayout()); + panel.add(new JLabel("Fit radius [px]:"), GridBagHelper.leftCol()); + panel.add(fitregsizeTextField, GridBagHelper.rightCol()); + parameters.registerComponent(FITRADPhasor, fitregsizeTextField); + + + panel.add(new JLabel("Astigmatism calibration file:"), GridBagHelper.leftCol()); + final JTextField calibrationFileTextField = new JTextField(Prefs.get("thunderstorm.estimators.calibrationpath", "")); + JButton findCalibrationButton = DialogStub.createBrowseButton(calibrationFileTextField, true, new FileNameExtensionFilter("Yaml file", "yaml")); + JPanel calibrationPanel = new JPanel(new BorderLayout()){ + @Override + public Dimension getPreferredSize() { + return ((JTextField) parameters.getRegisteredComponent(FITRADPhasor)).getPreferredSize(); + } + }; + calibrationPanel.add(calibrationFileTextField, BorderLayout.CENTER); + calibrationPanel.add(findCalibrationButton, BorderLayout.EAST); + GridBagConstraints gbc = GridBagHelper.rightCol(); + panel.add(calibrationPanel, gbc); + //Set enabled status of calibration file finder to status of checkbox + + parameters.registerComponent(CALIBRATION_PATH, calibrationFileTextField); + + parameters.loadPrefs(); + + return panel; + } + + @Override + public IEstimator getImplementation() { + //3D Phasor + + calibration = loadCalibration(parameters.getString(CALIBRATION_PATH)); + return new MultipleLocationsImageFitting(fittingRadius = FITRADPhasor.getValue(), new PhasorFitter(FITRADPhasor.getValue(),calibration)); + + } + + private DefocusCalibration loadCalibration(String calibrationFilePath) { + this.calibrationFilePath = calibrationFilePath; + try { + Yaml yaml = new Yaml(); + Object loaded = yaml.load(new FileReader(calibrationFilePath)); + return (DefocusCalibration) loaded; + } catch(FileNotFoundException ex) { + throw new RuntimeException("Could not read calibration file.", ex); + } catch(ClassCastException ex) { + throw new RuntimeException("Could not parse calibration file.", ex); + } + } +} diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/SymmetricGaussianEstimatorUI.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/SymmetricGaussianEstimatorUI.java index 5cae4c40..352c7697 100755 --- a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/SymmetricGaussianEstimatorUI.java +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/SymmetricGaussianEstimatorUI.java @@ -13,6 +13,7 @@ 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.MacroUI.validators.StringValidatorFactory; +import ij.IJ; import java.awt.GridBagLayout; import javax.swing.JComboBox; import javax.swing.JLabel; @@ -62,7 +63,6 @@ public JPanel getOptionsPanel() { parameters.registerComponent(FITRAD, fitregsizeTextField); parameters.registerComponent(METHOD, methodComboBox); parameters.registerComponent(SIGMA, sigmaTextField); - JPanel panel = new JPanel(new GridBagLayout()); panel.add(new JLabel("Fitting radius [px]:"), GridBagHelper.leftCol()); panel.add(fitregsizeTextField, GridBagHelper.rightCol()); @@ -76,6 +76,7 @@ public JPanel getOptionsPanel() { return panel; } + @Override public void readParameters() { super.readParameters(); diff --git a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/results/MeasurementProtocol.java b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/results/MeasurementProtocol.java index db2294a1..fdd51fce 100755 --- a/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/results/MeasurementProtocol.java +++ b/src/main/java/cz/cuni/lf1/lge/ThunderSTORM/results/MeasurementProtocol.java @@ -9,9 +9,11 @@ import cz.cuni.lf1.lge.ThunderSTORM.detectors.EmptyDetector; import cz.cuni.lf1.lge.ThunderSTORM.detectors.ui.IDetectorUI; import cz.cuni.lf1.lge.ThunderSTORM.estimators.EmptyEstimator; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.PhasorFitter; import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.EllipticGaussianEstimatorUI; import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.IBiplaneEstimatorUI; import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.IEstimatorUI; +import cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.PhasorFitterUI; import cz.cuni.lf1.lge.ThunderSTORM.filters.EmptyFilter; import cz.cuni.lf1.lge.ThunderSTORM.filters.ui.IFilterUI; import cz.cuni.lf1.lge.ThunderSTORM.results.OperationsHistoryPanel.Operation; @@ -78,6 +80,11 @@ public boolean is3D() { // tru only for astigmatism if (!this.isSet3d) { this.is3d = ((analysisEstimator != null) && (analysisEstimator instanceof EllipticGaussianEstimatorUI)); + if (analysisEstimator instanceof PhasorFitterUI){ + if (((PhasorFitterUI) analysisEstimator).getMethod() == "PhasorAstig"){ + this.is3d = true; + } + } this.isSet3d = true; } return this.is3d; diff --git a/src/main/resources/META-INF/services/cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.IEstimatorUI b/src/main/resources/META-INF/services/cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.IEstimatorUI index 2384bad6..83e0d6ef 100755 --- a/src/main/resources/META-INF/services/cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.IEstimatorUI +++ b/src/main/resources/META-INF/services/cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.IEstimatorUI @@ -3,4 +3,6 @@ cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.SymmetricGaussianEstimatorUI cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.EllipticGaussianEstimatorUI cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.RadialSymmetryEstimatorUI cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.CenterOfMassEstimatorUI +cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.PhasorFitterUI2D +cz.cuni.lf1.lge.ThunderSTORM.estimators.ui.PhasorFitterUI3D cz.cuni.lf1.lge.ThunderSTORM.estimators.EmptyEstimator \ No newline at end of file diff --git a/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/CalibrationEstimatorUI.html b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/CalibrationEstimatorUI.html index 5f9450ec..b5477904 100644 --- a/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/CalibrationEstimatorUI.html +++ b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/CalibrationEstimatorUI.html @@ -150,6 +150,12 @@

See also

  • +
  • +
  • + +
  • diff --git a/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/CalibrationEstimatorUI.lyx b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/CalibrationEstimatorUI.lyx index e495602d..3f6c046a 100755 --- a/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/CalibrationEstimatorUI.lyx +++ b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/CalibrationEstimatorUI.lyx @@ -330,6 +330,7 @@ href{EllipticGaussianEstimatorUI.html}{Rotated elliptical Gaussian function \end_layout + \begin_layout Itemize \begin_inset ERT status open @@ -337,6 +338,18 @@ status open \begin_layout Plain Layout +\backslash +href{PhasorFitterUI.html}{Phasor localization} +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout + + \backslash href{../../calibration/DefocusModels.html}{Defocusing models} \end_layout diff --git a/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/Estimators.html b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/Estimators.html index 539d0c02..832fa8fa 100644 --- a/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/Estimators.html +++ b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/Estimators.html @@ -69,6 +69,12 @@

    See also

    Radial symmetry

    +
  • + + +
  • diff --git a/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/Estimators.lyx b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/Estimators.lyx index d9aa5b18..ca050669 100644 --- a/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/Estimators.lyx +++ b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/Estimators.lyx @@ -186,7 +186,21 @@ href{RadialSymmetryEstimatorUI.html}{Radial symmetry} \end_layout +\begin_layout Itemize +\begin_inset ERT +status open +\begin_layout Plain Layout + + +\backslash +href{PhasorFitterUI.html}{Phasor localization} +\end_layout + +\end_inset + + +\end_layout \begin_layout Itemize \begin_inset ERT status open diff --git a/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI.html b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI.html new file mode 100644 index 00000000..12fe67c3 --- /dev/null +++ b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI.html @@ -0,0 +1,122 @@ + + + + + + + + + + + +
    + +

    Phasor

    + +
    +The phasor algorithm is a simple, model-free and non-iterative localization +algorithm, described in [1]. It is based on the principle that +the first Fourier coefficient of a Fourier transform of a 1D or 2D +image will provide information about the major constituent in the +image. In case of single molecule localization, the major constituent +is the PSF of an emitter. The first Fourier coefficient will directly provide +information about the position and the FWHM of the PSF in the +horizontal and vertical directions. As phasor localization is a model-free +localization algorithm, it performs much faster than traditional Gaussian-based +methods, with equal localization accuracies in all dimensions. +
    +
     
    +
    +Phasor localization perfoms best at relatively small +ROI sizes compared to gaussian-based models. It is recommended to use a radius of 300nm (a +total ROI size of 700x700nm). Astigmatic localization could +benefit from slightly larger sub-image size if high signal-to-noise +levels are achieved. The small ROI sizes are caused +by the fact that the presence of high amounts of background +in the ROI introduces errors in the phasor localization. +
    +
     
    +
    +As phasor localization provides information about the width of the +PSF in X and Y, it can be used for astigmatic 3D positioning. This +requires a calibration curve (see Calibration of the imaging system). +Be aware that the calibration curve is dependent on the size +of the sub-image chosen for phasor! Use the same sub-image size for +calibration and localization. As the ratio of the phasor magnitude is +consistent amongst background levels, the calibration series can have +a different background-to-noise ratio than the actual measurement. +
    +
     
    +
    +The basic principle behind the technique consists of the following +steps: + +
      +
    1. From the ROI around an approximate localization, perform a partial Fourier transformation and isolate +the first Fourier coefficient in both X and Y direction. +
      +
    2. + +
    3. Plot the real and imaginary parts of the 2 coefficients in a phasor diagram +- a 2D plotting tool with real and imaginary axis. +
      +
    4. + +
    5. Calculate the phase angles corresponding to the 2 coefficients. This +is a direct value for the normalized position of the emitter in the +ROI. +
      +
    6. + +
    7. Calculate the phasor magnitudes. This is a value for the +PSF width in X and Y. +
      +
    8. + +
    9. Calculate the Z-position based on two defocus curves +as described in [1,2], if applicable. +
      +
    10. +
    + +
    +

    See also

    + + + +
    +

    References

    + +
    +
    [1] Martens, K.J.A., Bader, A.N., Baas, S., Rieger, B.& Hohlbein, J. (2017). + Phasor based single-molecule localization microscopy in 3D (pSMLM-3D): an algorithm for MHz localization rates using standard CPUs. https://doi.org/10.1101/191957
    +
    [2] Huang, B., Wang, W., Bates, M., & Zhuang, X. (2008). +Three-dimensional super-resolution imaging by stochastic optical reconstruction +microscopy. Science, 319(5864), 810-813.
    +
    + + diff --git a/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI.lyx b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI.lyx new file mode 100644 index 00000000..faa46add --- /dev/null +++ b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI.lyx @@ -0,0 +1,266 @@ +#LyX 2.2 created this file. For more info see http://www.lyx.org/ +\lyxformat 508 +\begin_document +\begin_header +\save_transient_properties true +\origin unavailable +\textclass article +\begin_preamble +\usepackage[unicode=true] {hyperref} +\end_preamble +\use_default_options true +\maintain_unincluded_children false +\language english +\language_package none +\inputencoding auto +\fontencoding global +\font_roman "default" "default" +\font_sans "default" "default" +\font_typewriter "default" "default" +\font_math "auto" "auto" +\font_default_family default +\use_non_tex_fonts false +\font_sc false +\font_osf false +\font_sf_scale 100 100 +\font_tt_scale 100 100 +\graphics default +\default_output_format default +\output_sync 0 +\bibtex_command default +\index_command default +\paperfontsize default +\spacing single +\use_hyperref false +\papersize default +\use_geometry false +\use_package amsmath 1 +\use_package amssymb 1 +\use_package cancel 1 +\use_package esint 1 +\use_package mathdots 1 +\use_package mathtools 1 +\use_package mhchem 1 +\use_package stackrel 1 +\use_package stmaryrd 1 +\use_package undertilde 1 +\cite_engine basic +\cite_engine_type default +\biblio_style plain +\use_bibtopic false +\use_indices false +\paperorientation portrait +\suppress_date false +\justification true +\use_refstyle 1 +\index Index +\shortcut idx +\color #008000 +\end_index +\secnumdepth 3 +\tocdepth 3 +\paragraph_separation indent +\paragraph_indentation default +\quotes_language english +\papercolumns 1 +\papersides 1 +\paperpagestyle default +\tracking_changes false +\output_changes false +\html_math_output 0 +\html_css_as_file 0 +\html_be_strict false +\end_header + +\begin_body + +\begin_layout Section* +Phasor +\end_layout + +\begin_layout Standard +The phasor algorithm is a simple, model-free and non-iterative localization +algorithm, described in [1]. It is based on the principle that +the first Fourier coefficient of a Fourier transform of a 1D or 2D +image will provide information about the major constituent in the +image. In case of single molecule localization, the major constituent +is the PSF of an emitter. The first Fourier coefficient will directly provide +information about the position and the FWHM of the PSF in the +horizontal and vertical directions. As phasor localization is a model-free +localization algorithm, it performs much faster than traditional Gaussian-based +methods, with equal localization accuracies in all dimensions. +\end_layout + +\begin_layout Standard +Phasor localization perfoms best at relatively small +ROI sizes compared to gaussian-based models. It is recommended to use a radius of 300nm (a +total ROI size of 700x700nm). Astigmatic localization could +benefit from slightly larger sub-image size if high signal-to-noise +levels are achieved. The small ROI sizes are caused +by the fact that the presence of high amounts of background +in the ROI introduces errors in the phasor localization. +\end_layout + +\begin_layout Standard +As phasor localization provides information about the width of the PSF in + X and Y, it can be used for astigmatic 3D positioning. + This requires a calibration curve (see +\begin_inset ERT +status open + +\begin_layout Plain Layout + + +\backslash +href{../estimators/ui/CalibrationEstimatorUI.html}{Calibration of the imaging + system} +\end_layout + +\end_inset + +). + +\series bold +Be aware that the calibration curve is dependent on the size of the sub-image + chosen for phasor! +\series default +Use the same sub-image size for +calibration and localization. As the ratio of the phasor magnitude is +consistent amongst background levels, the calibration series can have +a different background-to-noise ratio than the actual measurement. +\end_layout + +\begin_layout Standard +The basic principle behind the technique consists of the following steps: +\end_layout + +\begin_layout Enumerate +From the ROI around an approximate localization, perform a partial Fourier transformation and isolate +the first Fourier coefficient in both X and Y direction. +\end_layout + +\begin_layout Enumerate +Plot the real and imaginary parts of the 2 coefficients in a phasor diagram +- a 2D plotting tool with real and imaginary axis. + +\end_layout + +\begin_layout Enumerate +Calculate the phase angles corresponding to the 2 coefficients. This +is a direct value for the normalized position of the emitter in the +ROI. +\end_layout + +\begin_layout Enumerate +Calculate the phasor magnitudes. This is a value for the +PSF width in X and Y. +\end_layout + +\begin_layout Enumerate +If applicable, calculate the Z-position based on two defocus curves +as described in [2]. +\end_layout + +\begin_layout Subsection* +See also +\end_layout + +\begin_layout Itemize +\begin_inset ERT +status open + +\begin_layout Plain Layout + + +\backslash +href{Estimators.html}{Sub-pixel 2D localization of molecules} +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Itemize +\begin_inset ERT +status open +\begin_layout Plain Layout +\backslash +href{FittingRegion.html}{Definition of the fitting region} +\end_layout +\end_inset +\end_layout + + +\begin_layout Itemize +\begin_inset ERT +status open +\begin_layout Plain Layout +\backslash +href{../../calibration/DefocusModels.html}{Defocusing models} +\end_layout +\end_inset +\end_layout + + +\begin_layout Itemize +\begin_inset ERT +status open +\begin_layout Plain Layout +\backslash +href{CalibrationEstimatorUI.html}{Calibration of the imaging system for the astigmatism method} +\end_layout +\end_inset +\end_layout + + +\begin_layout Itemize +\begin_inset ERT +status open +\begin_layout Plain Layout +\backslash +href{PSF.html}{Point-spread function (PSF)} +\end_layout +\end_inset +\end_layout + + +\begin_layout Itemize +\begin_inset ERT +status open +\begin_layout Plain Layout +\backslash +href{LocalizationUncertainty.html}{Localization uncertainty} +\end_layout +\end_inset +\end_layout + + +\begin_layout Bibliography +\begin_inset CommandInset bibitem +LatexCommand bibitem +key "key-3" + +\end_inset + + +\end_layout + +\begin_layout Bibliography +\begin_inset CommandInset bibitem +LatexCommand bibitem +key "key-6" + +\end_inset + Martens, K.J.A., Bader, A.N., Baas, S., Rieger, B.& Hohlbein, J. (2017). + Phasor based single-molecule localization microscopy in 3D (pSMLM-3D): an algorithm for MHz localization rates using standard CPUs. + https://doi.org/10.1101/191957 +Huang, B., Wang, W., Bates, M., & Zhuang, X. + (2008). + Three-dimensional super-resolution imaging by stochastic optical reconstruction + microscopy. + Science, 319(5864), 810-813. +\end_layout + +\end_body +\end_document diff --git a/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI2D.html b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI2D.html new file mode 100644 index 00000000..12fe67c3 --- /dev/null +++ b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI2D.html @@ -0,0 +1,122 @@ + + + + + + + + + + + +
    + +

    Phasor

    + +
    +The phasor algorithm is a simple, model-free and non-iterative localization +algorithm, described in [1]. It is based on the principle that +the first Fourier coefficient of a Fourier transform of a 1D or 2D +image will provide information about the major constituent in the +image. In case of single molecule localization, the major constituent +is the PSF of an emitter. The first Fourier coefficient will directly provide +information about the position and the FWHM of the PSF in the +horizontal and vertical directions. As phasor localization is a model-free +localization algorithm, it performs much faster than traditional Gaussian-based +methods, with equal localization accuracies in all dimensions. +
    +
     
    +
    +Phasor localization perfoms best at relatively small +ROI sizes compared to gaussian-based models. It is recommended to use a radius of 300nm (a +total ROI size of 700x700nm). Astigmatic localization could +benefit from slightly larger sub-image size if high signal-to-noise +levels are achieved. The small ROI sizes are caused +by the fact that the presence of high amounts of background +in the ROI introduces errors in the phasor localization. +
    +
     
    +
    +As phasor localization provides information about the width of the +PSF in X and Y, it can be used for astigmatic 3D positioning. This +requires a calibration curve (see Calibration of the imaging system). +Be aware that the calibration curve is dependent on the size +of the sub-image chosen for phasor! Use the same sub-image size for +calibration and localization. As the ratio of the phasor magnitude is +consistent amongst background levels, the calibration series can have +a different background-to-noise ratio than the actual measurement. +
    +
     
    +
    +The basic principle behind the technique consists of the following +steps: + +
      +
    1. From the ROI around an approximate localization, perform a partial Fourier transformation and isolate +the first Fourier coefficient in both X and Y direction. +
      +
    2. + +
    3. Plot the real and imaginary parts of the 2 coefficients in a phasor diagram +- a 2D plotting tool with real and imaginary axis. +
      +
    4. + +
    5. Calculate the phase angles corresponding to the 2 coefficients. This +is a direct value for the normalized position of the emitter in the +ROI. +
      +
    6. + +
    7. Calculate the phasor magnitudes. This is a value for the +PSF width in X and Y. +
      +
    8. + +
    9. Calculate the Z-position based on two defocus curves +as described in [1,2], if applicable. +
      +
    10. +
    + +
    +

    See also

    + + + +
    +

    References

    + +
    +
    [1] Martens, K.J.A., Bader, A.N., Baas, S., Rieger, B.& Hohlbein, J. (2017). + Phasor based single-molecule localization microscopy in 3D (pSMLM-3D): an algorithm for MHz localization rates using standard CPUs. https://doi.org/10.1101/191957
    +
    [2] Huang, B., Wang, W., Bates, M., & Zhuang, X. (2008). +Three-dimensional super-resolution imaging by stochastic optical reconstruction +microscopy. Science, 319(5864), 810-813.
    +
    + + diff --git a/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI2D.lyx b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI2D.lyx new file mode 100644 index 00000000..faa46add --- /dev/null +++ b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI2D.lyx @@ -0,0 +1,266 @@ +#LyX 2.2 created this file. For more info see http://www.lyx.org/ +\lyxformat 508 +\begin_document +\begin_header +\save_transient_properties true +\origin unavailable +\textclass article +\begin_preamble +\usepackage[unicode=true] {hyperref} +\end_preamble +\use_default_options true +\maintain_unincluded_children false +\language english +\language_package none +\inputencoding auto +\fontencoding global +\font_roman "default" "default" +\font_sans "default" "default" +\font_typewriter "default" "default" +\font_math "auto" "auto" +\font_default_family default +\use_non_tex_fonts false +\font_sc false +\font_osf false +\font_sf_scale 100 100 +\font_tt_scale 100 100 +\graphics default +\default_output_format default +\output_sync 0 +\bibtex_command default +\index_command default +\paperfontsize default +\spacing single +\use_hyperref false +\papersize default +\use_geometry false +\use_package amsmath 1 +\use_package amssymb 1 +\use_package cancel 1 +\use_package esint 1 +\use_package mathdots 1 +\use_package mathtools 1 +\use_package mhchem 1 +\use_package stackrel 1 +\use_package stmaryrd 1 +\use_package undertilde 1 +\cite_engine basic +\cite_engine_type default +\biblio_style plain +\use_bibtopic false +\use_indices false +\paperorientation portrait +\suppress_date false +\justification true +\use_refstyle 1 +\index Index +\shortcut idx +\color #008000 +\end_index +\secnumdepth 3 +\tocdepth 3 +\paragraph_separation indent +\paragraph_indentation default +\quotes_language english +\papercolumns 1 +\papersides 1 +\paperpagestyle default +\tracking_changes false +\output_changes false +\html_math_output 0 +\html_css_as_file 0 +\html_be_strict false +\end_header + +\begin_body + +\begin_layout Section* +Phasor +\end_layout + +\begin_layout Standard +The phasor algorithm is a simple, model-free and non-iterative localization +algorithm, described in [1]. It is based on the principle that +the first Fourier coefficient of a Fourier transform of a 1D or 2D +image will provide information about the major constituent in the +image. In case of single molecule localization, the major constituent +is the PSF of an emitter. The first Fourier coefficient will directly provide +information about the position and the FWHM of the PSF in the +horizontal and vertical directions. As phasor localization is a model-free +localization algorithm, it performs much faster than traditional Gaussian-based +methods, with equal localization accuracies in all dimensions. +\end_layout + +\begin_layout Standard +Phasor localization perfoms best at relatively small +ROI sizes compared to gaussian-based models. It is recommended to use a radius of 300nm (a +total ROI size of 700x700nm). Astigmatic localization could +benefit from slightly larger sub-image size if high signal-to-noise +levels are achieved. The small ROI sizes are caused +by the fact that the presence of high amounts of background +in the ROI introduces errors in the phasor localization. +\end_layout + +\begin_layout Standard +As phasor localization provides information about the width of the PSF in + X and Y, it can be used for astigmatic 3D positioning. + This requires a calibration curve (see +\begin_inset ERT +status open + +\begin_layout Plain Layout + + +\backslash +href{../estimators/ui/CalibrationEstimatorUI.html}{Calibration of the imaging + system} +\end_layout + +\end_inset + +). + +\series bold +Be aware that the calibration curve is dependent on the size of the sub-image + chosen for phasor! +\series default +Use the same sub-image size for +calibration and localization. As the ratio of the phasor magnitude is +consistent amongst background levels, the calibration series can have +a different background-to-noise ratio than the actual measurement. +\end_layout + +\begin_layout Standard +The basic principle behind the technique consists of the following steps: +\end_layout + +\begin_layout Enumerate +From the ROI around an approximate localization, perform a partial Fourier transformation and isolate +the first Fourier coefficient in both X and Y direction. +\end_layout + +\begin_layout Enumerate +Plot the real and imaginary parts of the 2 coefficients in a phasor diagram +- a 2D plotting tool with real and imaginary axis. + +\end_layout + +\begin_layout Enumerate +Calculate the phase angles corresponding to the 2 coefficients. This +is a direct value for the normalized position of the emitter in the +ROI. +\end_layout + +\begin_layout Enumerate +Calculate the phasor magnitudes. This is a value for the +PSF width in X and Y. +\end_layout + +\begin_layout Enumerate +If applicable, calculate the Z-position based on two defocus curves +as described in [2]. +\end_layout + +\begin_layout Subsection* +See also +\end_layout + +\begin_layout Itemize +\begin_inset ERT +status open + +\begin_layout Plain Layout + + +\backslash +href{Estimators.html}{Sub-pixel 2D localization of molecules} +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Itemize +\begin_inset ERT +status open +\begin_layout Plain Layout +\backslash +href{FittingRegion.html}{Definition of the fitting region} +\end_layout +\end_inset +\end_layout + + +\begin_layout Itemize +\begin_inset ERT +status open +\begin_layout Plain Layout +\backslash +href{../../calibration/DefocusModels.html}{Defocusing models} +\end_layout +\end_inset +\end_layout + + +\begin_layout Itemize +\begin_inset ERT +status open +\begin_layout Plain Layout +\backslash +href{CalibrationEstimatorUI.html}{Calibration of the imaging system for the astigmatism method} +\end_layout +\end_inset +\end_layout + + +\begin_layout Itemize +\begin_inset ERT +status open +\begin_layout Plain Layout +\backslash +href{PSF.html}{Point-spread function (PSF)} +\end_layout +\end_inset +\end_layout + + +\begin_layout Itemize +\begin_inset ERT +status open +\begin_layout Plain Layout +\backslash +href{LocalizationUncertainty.html}{Localization uncertainty} +\end_layout +\end_inset +\end_layout + + +\begin_layout Bibliography +\begin_inset CommandInset bibitem +LatexCommand bibitem +key "key-3" + +\end_inset + + +\end_layout + +\begin_layout Bibliography +\begin_inset CommandInset bibitem +LatexCommand bibitem +key "key-6" + +\end_inset + Martens, K.J.A., Bader, A.N., Baas, S., Rieger, B.& Hohlbein, J. (2017). + Phasor based single-molecule localization microscopy in 3D (pSMLM-3D): an algorithm for MHz localization rates using standard CPUs. + https://doi.org/10.1101/191957 +Huang, B., Wang, W., Bates, M., & Zhuang, X. + (2008). + Three-dimensional super-resolution imaging by stochastic optical reconstruction + microscopy. + Science, 319(5864), 810-813. +\end_layout + +\end_body +\end_document diff --git a/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI3D.html b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI3D.html new file mode 100644 index 00000000..12fe67c3 --- /dev/null +++ b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI3D.html @@ -0,0 +1,122 @@ + + + + + + + + + + + +
    + +

    Phasor

    + +
    +The phasor algorithm is a simple, model-free and non-iterative localization +algorithm, described in [1]. It is based on the principle that +the first Fourier coefficient of a Fourier transform of a 1D or 2D +image will provide information about the major constituent in the +image. In case of single molecule localization, the major constituent +is the PSF of an emitter. The first Fourier coefficient will directly provide +information about the position and the FWHM of the PSF in the +horizontal and vertical directions. As phasor localization is a model-free +localization algorithm, it performs much faster than traditional Gaussian-based +methods, with equal localization accuracies in all dimensions. +
    +
     
    +
    +Phasor localization perfoms best at relatively small +ROI sizes compared to gaussian-based models. It is recommended to use a radius of 300nm (a +total ROI size of 700x700nm). Astigmatic localization could +benefit from slightly larger sub-image size if high signal-to-noise +levels are achieved. The small ROI sizes are caused +by the fact that the presence of high amounts of background +in the ROI introduces errors in the phasor localization. +
    +
     
    +
    +As phasor localization provides information about the width of the +PSF in X and Y, it can be used for astigmatic 3D positioning. This +requires a calibration curve (see Calibration of the imaging system). +Be aware that the calibration curve is dependent on the size +of the sub-image chosen for phasor! Use the same sub-image size for +calibration and localization. As the ratio of the phasor magnitude is +consistent amongst background levels, the calibration series can have +a different background-to-noise ratio than the actual measurement. +
    +
     
    +
    +The basic principle behind the technique consists of the following +steps: + +
      +
    1. From the ROI around an approximate localization, perform a partial Fourier transformation and isolate +the first Fourier coefficient in both X and Y direction. +
      +
    2. + +
    3. Plot the real and imaginary parts of the 2 coefficients in a phasor diagram +- a 2D plotting tool with real and imaginary axis. +
      +
    4. + +
    5. Calculate the phase angles corresponding to the 2 coefficients. This +is a direct value for the normalized position of the emitter in the +ROI. +
      +
    6. + +
    7. Calculate the phasor magnitudes. This is a value for the +PSF width in X and Y. +
      +
    8. + +
    9. Calculate the Z-position based on two defocus curves +as described in [1,2], if applicable. +
      +
    10. +
    + +
    +

    See also

    + + + +
    +

    References

    + +
    +
    [1] Martens, K.J.A., Bader, A.N., Baas, S., Rieger, B.& Hohlbein, J. (2017). + Phasor based single-molecule localization microscopy in 3D (pSMLM-3D): an algorithm for MHz localization rates using standard CPUs. https://doi.org/10.1101/191957
    +
    [2] Huang, B., Wang, W., Bates, M., & Zhuang, X. (2008). +Three-dimensional super-resolution imaging by stochastic optical reconstruction +microscopy. Science, 319(5864), 810-813.
    +
    + + diff --git a/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI3D.lyx b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI3D.lyx new file mode 100644 index 00000000..faa46add --- /dev/null +++ b/src/main/resources/resources/help/cz/cuni/lf1/lge/ThunderSTORM/estimators/ui/PhasorFitterUI3D.lyx @@ -0,0 +1,266 @@ +#LyX 2.2 created this file. For more info see http://www.lyx.org/ +\lyxformat 508 +\begin_document +\begin_header +\save_transient_properties true +\origin unavailable +\textclass article +\begin_preamble +\usepackage[unicode=true] {hyperref} +\end_preamble +\use_default_options true +\maintain_unincluded_children false +\language english +\language_package none +\inputencoding auto +\fontencoding global +\font_roman "default" "default" +\font_sans "default" "default" +\font_typewriter "default" "default" +\font_math "auto" "auto" +\font_default_family default +\use_non_tex_fonts false +\font_sc false +\font_osf false +\font_sf_scale 100 100 +\font_tt_scale 100 100 +\graphics default +\default_output_format default +\output_sync 0 +\bibtex_command default +\index_command default +\paperfontsize default +\spacing single +\use_hyperref false +\papersize default +\use_geometry false +\use_package amsmath 1 +\use_package amssymb 1 +\use_package cancel 1 +\use_package esint 1 +\use_package mathdots 1 +\use_package mathtools 1 +\use_package mhchem 1 +\use_package stackrel 1 +\use_package stmaryrd 1 +\use_package undertilde 1 +\cite_engine basic +\cite_engine_type default +\biblio_style plain +\use_bibtopic false +\use_indices false +\paperorientation portrait +\suppress_date false +\justification true +\use_refstyle 1 +\index Index +\shortcut idx +\color #008000 +\end_index +\secnumdepth 3 +\tocdepth 3 +\paragraph_separation indent +\paragraph_indentation default +\quotes_language english +\papercolumns 1 +\papersides 1 +\paperpagestyle default +\tracking_changes false +\output_changes false +\html_math_output 0 +\html_css_as_file 0 +\html_be_strict false +\end_header + +\begin_body + +\begin_layout Section* +Phasor +\end_layout + +\begin_layout Standard +The phasor algorithm is a simple, model-free and non-iterative localization +algorithm, described in [1]. It is based on the principle that +the first Fourier coefficient of a Fourier transform of a 1D or 2D +image will provide information about the major constituent in the +image. In case of single molecule localization, the major constituent +is the PSF of an emitter. The first Fourier coefficient will directly provide +information about the position and the FWHM of the PSF in the +horizontal and vertical directions. As phasor localization is a model-free +localization algorithm, it performs much faster than traditional Gaussian-based +methods, with equal localization accuracies in all dimensions. +\end_layout + +\begin_layout Standard +Phasor localization perfoms best at relatively small +ROI sizes compared to gaussian-based models. It is recommended to use a radius of 300nm (a +total ROI size of 700x700nm). Astigmatic localization could +benefit from slightly larger sub-image size if high signal-to-noise +levels are achieved. The small ROI sizes are caused +by the fact that the presence of high amounts of background +in the ROI introduces errors in the phasor localization. +\end_layout + +\begin_layout Standard +As phasor localization provides information about the width of the PSF in + X and Y, it can be used for astigmatic 3D positioning. + This requires a calibration curve (see +\begin_inset ERT +status open + +\begin_layout Plain Layout + + +\backslash +href{../estimators/ui/CalibrationEstimatorUI.html}{Calibration of the imaging + system} +\end_layout + +\end_inset + +). + +\series bold +Be aware that the calibration curve is dependent on the size of the sub-image + chosen for phasor! +\series default +Use the same sub-image size for +calibration and localization. As the ratio of the phasor magnitude is +consistent amongst background levels, the calibration series can have +a different background-to-noise ratio than the actual measurement. +\end_layout + +\begin_layout Standard +The basic principle behind the technique consists of the following steps: +\end_layout + +\begin_layout Enumerate +From the ROI around an approximate localization, perform a partial Fourier transformation and isolate +the first Fourier coefficient in both X and Y direction. +\end_layout + +\begin_layout Enumerate +Plot the real and imaginary parts of the 2 coefficients in a phasor diagram +- a 2D plotting tool with real and imaginary axis. + +\end_layout + +\begin_layout Enumerate +Calculate the phase angles corresponding to the 2 coefficients. This +is a direct value for the normalized position of the emitter in the +ROI. +\end_layout + +\begin_layout Enumerate +Calculate the phasor magnitudes. This is a value for the +PSF width in X and Y. +\end_layout + +\begin_layout Enumerate +If applicable, calculate the Z-position based on two defocus curves +as described in [2]. +\end_layout + +\begin_layout Subsection* +See also +\end_layout + +\begin_layout Itemize +\begin_inset ERT +status open + +\begin_layout Plain Layout + + +\backslash +href{Estimators.html}{Sub-pixel 2D localization of molecules} +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Itemize +\begin_inset ERT +status open +\begin_layout Plain Layout +\backslash +href{FittingRegion.html}{Definition of the fitting region} +\end_layout +\end_inset +\end_layout + + +\begin_layout Itemize +\begin_inset ERT +status open +\begin_layout Plain Layout +\backslash +href{../../calibration/DefocusModels.html}{Defocusing models} +\end_layout +\end_inset +\end_layout + + +\begin_layout Itemize +\begin_inset ERT +status open +\begin_layout Plain Layout +\backslash +href{CalibrationEstimatorUI.html}{Calibration of the imaging system for the astigmatism method} +\end_layout +\end_inset +\end_layout + + +\begin_layout Itemize +\begin_inset ERT +status open +\begin_layout Plain Layout +\backslash +href{PSF.html}{Point-spread function (PSF)} +\end_layout +\end_inset +\end_layout + + +\begin_layout Itemize +\begin_inset ERT +status open +\begin_layout Plain Layout +\backslash +href{LocalizationUncertainty.html}{Localization uncertainty} +\end_layout +\end_inset +\end_layout + + +\begin_layout Bibliography +\begin_inset CommandInset bibitem +LatexCommand bibitem +key "key-3" + +\end_inset + + +\end_layout + +\begin_layout Bibliography +\begin_inset CommandInset bibitem +LatexCommand bibitem +key "key-6" + +\end_inset + Martens, K.J.A., Bader, A.N., Baas, S., Rieger, B.& Hohlbein, J. (2017). + Phasor based single-molecule localization microscopy in 3D (pSMLM-3D): an algorithm for MHz localization rates using standard CPUs. + https://doi.org/10.1101/191957 +Huang, B., Wang, W., Bates, M., & Zhuang, X. + (2008). + Three-dimensional super-resolution imaging by stochastic optical reconstruction + microscopy. + Science, 319(5864), 810-813. +\end_layout + +\end_body +\end_document