Search in sources :

Example 31 with PlotWindow

use of ij.gui.PlotWindow in project GDSC-SMLM by aherbert.

the class MeanVarianceTest method run.

@Override
public void run(String arg) {
    SmlmUsageTracker.recordPlugin(this.getClass(), arg);
    settings = Settings.load();
    settings.save();
    String helpKey = "mean-variance-test";
    if (ImageJUtils.isExtraOptions()) {
        final ImagePlus imp = WindowManager.getCurrentImage();
        if (imp.getStackSize() > 1) {
            final GenericDialog gd = new GenericDialog(TITLE);
            gd.addMessage("Perform single image analysis on the current image?");
            gd.addNumericField("Bias", settings.bias, 0);
            gd.addHelp(HelpUrls.getUrl(helpKey));
            gd.showDialog();
            if (gd.wasCanceled()) {
                return;
            }
            singleImage = true;
            settings.bias = Math.abs(gd.getNextNumber());
        } else {
            IJ.error(TITLE, "Single-image mode requires a stack");
            return;
        }
    }
    List<ImageSample> images;
    String inputDirectory = "";
    if (singleImage) {
        IJ.showStatus("Loading images...");
        images = getImages();
        if (images.size() == 0) {
            IJ.error(TITLE, "Not enough images for analysis");
            return;
        }
    } else {
        inputDirectory = IJ.getDirectory("Select image series ...");
        if (inputDirectory == null) {
            return;
        }
        final SeriesOpener series = new SeriesOpener(inputDirectory);
        series.setVariableSize(true);
        if (series.getNumberOfImages() < 3) {
            IJ.error(TITLE, "Not enough images in the selected directory");
            return;
        }
        if (!IJ.showMessageWithCancel(TITLE, String.format("Analyse %d images, first image:\n%s", series.getNumberOfImages(), series.getImageList()[0]))) {
            return;
        }
        IJ.showStatus("Loading images");
        images = getImages(series);
        if (images.size() < 3) {
            IJ.error(TITLE, "Not enough images for analysis");
            return;
        }
        if (images.get(0).exposure != 0) {
            IJ.error(TITLE, "First image in series must have exposure 0 (Bias image)");
            return;
        }
    }
    final boolean emMode = (arg != null && arg.contains("em"));
    GenericDialog gd = new GenericDialog(TITLE);
    gd.addMessage("Set the output options:");
    gd.addCheckbox("Show_table", settings.showTable);
    gd.addCheckbox("Show_charts", settings.showCharts);
    if (emMode) {
        // Ask the user for the camera gain ...
        gd.addMessage("Estimating the EM-gain requires the camera gain without EM readout enabled");
        gd.addNumericField("Camera_gain (Count/e-)", settings.cameraGain, 4);
    }
    if (emMode) {
        helpKey += "-em-ccd";
    }
    gd.addHelp(HelpUrls.getUrl(helpKey));
    gd.showDialog();
    if (gd.wasCanceled()) {
        return;
    }
    settings.showTable = gd.getNextBoolean();
    settings.showCharts = gd.getNextBoolean();
    if (emMode) {
        settings.cameraGain = gd.getNextNumber();
    }
    IJ.showStatus("Computing mean & variance");
    final double nImages = images.size();
    for (int i = 0; i < images.size(); i++) {
        IJ.showStatus(String.format("Computing mean & variance %d/%d", i + 1, images.size()));
        images.get(i).compute(singleImage, i / nImages, (i + 1) / nImages);
    }
    IJ.showProgress(1);
    IJ.showStatus("Computing results");
    // Allow user to input multiple bias images
    int start = 0;
    final Statistics biasStats = new Statistics();
    final Statistics noiseStats = new Statistics();
    final double bias;
    if (singleImage) {
        bias = settings.bias;
    } else {
        while (start < images.size()) {
            final ImageSample sample = images.get(start);
            if (sample.exposure == 0) {
                biasStats.add(sample.means);
                for (final PairSample pair : sample.samples) {
                    noiseStats.add(pair.variance);
                }
                start++;
            } else {
                break;
            }
        }
        bias = biasStats.getMean();
    }
    // Get the mean-variance data
    int total = 0;
    for (int i = start; i < images.size(); i++) {
        total += images.get(i).samples.size();
    }
    if (settings.showTable && total > 2000) {
        gd = new GenericDialog(TITLE);
        gd.addMessage("Table output requires " + total + " entries.\n \nYou may want to disable the table.");
        gd.addCheckbox("Show_table", settings.showTable);
        gd.showDialog();
        if (gd.wasCanceled()) {
            return;
        }
        settings.showTable = gd.getNextBoolean();
    }
    final TextWindow results = (settings.showTable) ? createResultsWindow() : null;
    double[] mean = new double[total];
    double[] variance = new double[mean.length];
    final Statistics gainStats = (singleImage) ? new StoredDataStatistics(total) : new Statistics();
    final WeightedObservedPoints obs = new WeightedObservedPoints();
    for (int i = (singleImage) ? 0 : start, j = 0; i < images.size(); i++) {
        final StringBuilder sb = (settings.showTable) ? new StringBuilder() : null;
        final ImageSample sample = images.get(i);
        for (final PairSample pair : sample.samples) {
            if (j % 16 == 0) {
                IJ.showProgress(j, total);
            }
            mean[j] = pair.getMean();
            variance[j] = pair.variance;
            // Gain is in Count / e
            double gain = variance[j] / (mean[j] - bias);
            gainStats.add(gain);
            obs.add(mean[j], variance[j]);
            if (emMode) {
                gain /= (2 * settings.cameraGain);
            }
            if (sb != null) {
                sb.append(sample.title).append('\t');
                sb.append(sample.exposure).append('\t');
                sb.append(pair.slice1).append('\t');
                sb.append(pair.slice2).append('\t');
                sb.append(IJ.d2s(pair.mean1, 2)).append('\t');
                sb.append(IJ.d2s(pair.mean2, 2)).append('\t');
                sb.append(IJ.d2s(mean[j], 2)).append('\t');
                sb.append(IJ.d2s(variance[j], 2)).append('\t');
                sb.append(MathUtils.rounded(gain, 4)).append("\n");
            }
            j++;
        }
        if (results != null && sb != null) {
            results.append(sb.toString());
        }
    }
    IJ.showProgress(1);
    if (singleImage) {
        StoredDataStatistics stats = (StoredDataStatistics) gainStats;
        ImageJUtils.log(TITLE);
        if (emMode) {
            final double[] values = stats.getValues();
            MathArrays.scaleInPlace(0.5, values);
            stats = StoredDataStatistics.create(values);
        }
        if (settings.showCharts) {
            // Plot the gain over time
            final String title = TITLE + " Gain vs Frame";
            final Plot plot = new Plot(title, "Slice", "Gain");
            plot.addPoints(SimpleArrayUtils.newArray(gainStats.getN(), 1, 1.0), stats.getValues(), Plot.LINE);
            final PlotWindow pw = ImageJUtils.display(title, plot);
            // Show a histogram
            final String label = String.format("Mean = %s, Median = %s", MathUtils.rounded(stats.getMean()), MathUtils.rounded(stats.getMedian()));
            final WindowOrganiser wo = new WindowOrganiser();
            final PlotWindow pw2 = new HistogramPlotBuilder(TITLE, stats, "Gain").setRemoveOutliersOption(1).setPlotLabel(label).show(wo);
            if (wo.isNotEmpty()) {
                final Point point = pw.getLocation();
                point.y += pw.getHeight();
                pw2.setLocation(point);
            }
        }
        ImageJUtils.log("Single-image mode: %s camera", (emMode) ? "EM-CCD" : "Standard");
        final double gain = stats.getMedian();
        if (emMode) {
            final double totalGain = gain;
            final double emGain = totalGain / settings.cameraGain;
            ImageJUtils.log("  Gain = 1 / %s (Count/e-)", MathUtils.rounded(settings.cameraGain, 4));
            ImageJUtils.log("  EM-Gain = %s", MathUtils.rounded(emGain, 4));
            ImageJUtils.log("  Total Gain = %s (Count/e-)", MathUtils.rounded(totalGain, 4));
        } else {
            settings.cameraGain = gain;
            ImageJUtils.log("  Gain = 1 / %s (Count/e-)", MathUtils.rounded(settings.cameraGain, 4));
        }
    } else {
        IJ.showStatus("Computing fit");
        // Sort
        final int[] indices = rank(mean);
        mean = reorder(mean, indices);
        variance = reorder(variance, indices);
        // Compute optimal coefficients.
        // a - b x
        final double[] init = { 0, 1 / gainStats.getMean() };
        final PolynomialCurveFitter fitter = PolynomialCurveFitter.create(2).withStartPoint(init);
        final double[] best = fitter.fit(obs.toList());
        // Construct the polynomial that best fits the data.
        final PolynomialFunction fitted = new PolynomialFunction(best);
        if (settings.showCharts) {
            // Plot mean verses variance. Gradient is gain in Count/e.
            final String title = TITLE + " results";
            final Plot plot = new Plot(title, "Mean", "Variance");
            final double[] xlimits = MathUtils.limits(mean);
            final double[] ylimits = MathUtils.limits(variance);
            double xrange = (xlimits[1] - xlimits[0]) * 0.05;
            if (xrange == 0) {
                xrange = 0.05;
            }
            double yrange = (ylimits[1] - ylimits[0]) * 0.05;
            if (yrange == 0) {
                yrange = 0.05;
            }
            plot.setLimits(xlimits[0] - xrange, xlimits[1] + xrange, ylimits[0] - yrange, ylimits[1] + yrange);
            plot.setColor(Color.blue);
            plot.addPoints(mean, variance, Plot.CROSS);
            plot.setColor(Color.red);
            plot.addPoints(new double[] { mean[0], mean[mean.length - 1] }, new double[] { fitted.value(mean[0]), fitted.value(mean[mean.length - 1]) }, Plot.LINE);
            ImageJUtils.display(title, plot);
        }
        final double avBiasNoise = Math.sqrt(noiseStats.getMean());
        ImageJUtils.log(TITLE);
        ImageJUtils.log("  Directory = %s", inputDirectory);
        ImageJUtils.log("  Bias = %s +/- %s (Count)", MathUtils.rounded(bias, 4), MathUtils.rounded(avBiasNoise, 4));
        ImageJUtils.log("  Variance = %s + %s * mean", MathUtils.rounded(best[0], 4), MathUtils.rounded(best[1], 4));
        if (emMode) {
            // The gradient is the observed gain of the noise.
            // In an EM-CCD there is a noise factor of 2.
            // Q. Is this true for a correct noise factor calibration:
            // double noiseFactor = (Read Noise EM-CCD) / (Read Noise CCD)
            // Em-gain is the observed gain divided by the noise factor multiplied by camera gain
            final double emGain = best[1] / (2 * settings.cameraGain);
            // Compute total gain
            final double totalGain = emGain * settings.cameraGain;
            final double readNoise = avBiasNoise / settings.cameraGain;
            // Effective noise is standard deviation of the bias image divided by the total gain (in
            // Count/e-)
            final double readNoiseE = avBiasNoise / totalGain;
            ImageJUtils.log("  Read Noise = %s (e-) [%s (Count)]", MathUtils.rounded(readNoise, 4), MathUtils.rounded(avBiasNoise, 4));
            ImageJUtils.log("  Gain = 1 / %s (Count/e-)", MathUtils.rounded(1 / settings.cameraGain, 4));
            ImageJUtils.log("  EM-Gain = %s", MathUtils.rounded(emGain, 4));
            ImageJUtils.log("  Total Gain = %s (Count/e-)", MathUtils.rounded(totalGain, 4));
            ImageJUtils.log("  Effective Read Noise = %s (e-) (Read Noise/Total Gain)", MathUtils.rounded(readNoiseE, 4));
        } else {
            // The gradient is the observed gain of the noise.
            settings.cameraGain = best[1];
            // Noise is standard deviation of the bias image divided by the gain (in Count/e-)
            final double readNoise = avBiasNoise / settings.cameraGain;
            ImageJUtils.log("  Read Noise = %s (e-) [%s (Count)]", MathUtils.rounded(readNoise, 4), MathUtils.rounded(avBiasNoise, 4));
            ImageJUtils.log("  Gain = 1 / %s (Count/e-)", MathUtils.rounded(1 / settings.cameraGain, 4));
        }
    }
    IJ.showStatus("");
}
Also used : Plot(ij.gui.Plot) StoredDataStatistics(uk.ac.sussex.gdsc.core.utils.StoredDataStatistics) PlotWindow(ij.gui.PlotWindow) HistogramPlotBuilder(uk.ac.sussex.gdsc.core.ij.HistogramPlot.HistogramPlotBuilder) PolynomialFunction(org.apache.commons.math3.analysis.polynomials.PolynomialFunction) SeriesOpener(uk.ac.sussex.gdsc.core.ij.SeriesOpener) WindowOrganiser(uk.ac.sussex.gdsc.core.ij.plugin.WindowOrganiser) Point(java.awt.Point) ImagePlus(ij.ImagePlus) StoredDataStatistics(uk.ac.sussex.gdsc.core.utils.StoredDataStatistics) Statistics(uk.ac.sussex.gdsc.core.utils.Statistics) Point(java.awt.Point) PolynomialCurveFitter(org.apache.commons.math3.fitting.PolynomialCurveFitter) WeightedObservedPoints(org.apache.commons.math3.fitting.WeightedObservedPoints) TextWindow(ij.text.TextWindow) GenericDialog(ij.gui.GenericDialog)

Aggregations

PlotWindow (ij.gui.PlotWindow)31 Plot2 (ij.gui.Plot2)17 Plot (ij.gui.Plot)14 Point (java.awt.Point)13 BasePoint (gdsc.core.match.BasePoint)9 PeakResultPoint (gdsc.smlm.ij.plugins.ResultsMatchCalculator.PeakResultPoint)6 WindowOrganiser (uk.ac.sussex.gdsc.core.ij.plugin.WindowOrganiser)4 FractionalAssignment (gdsc.core.match.FractionalAssignment)3 StoredDataStatistics (gdsc.core.utils.StoredDataStatistics)3 PeakFractionalAssignment (gdsc.smlm.results.filter.PeakFractionalAssignment)3 GenericDialog (ij.gui.GenericDialog)3 WindowOrganiser (ij.plugin.WindowOrganiser)3 LinearInterpolator (org.apache.commons.math3.analysis.interpolation.LinearInterpolator)3 PolynomialSplineFunction (org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction)3 BasePoint (uk.ac.sussex.gdsc.core.match.BasePoint)3 Statistics (gdsc.core.utils.Statistics)2 StoredData (gdsc.core.utils.StoredData)2 ImagePlus (ij.ImagePlus)2 ByteProcessor (ij.process.ByteProcessor)2 TextWindow (ij.text.TextWindow)2