use of uk.ac.sussex.gdsc.core.ij.HistogramPlot.HistogramPlotBuilder in project GDSC-SMLM by aherbert.
the class BenchmarkSpotFilter method showFailuresPlot.
private void showFailuresPlot(BenchmarkSpotFilterResult filterResult) {
final double[][] h = filterResult.cumul;
final StoredData data = filterResult.stats;
final String xTitle = "Failures";
new HistogramPlotBuilder(TITLE, data, xTitle).setMinBinWidth(1).show(windowOrganiser);
final String title = TITLE + " " + xTitle + " Cumulative";
final Plot plot = new Plot(title, xTitle, "Frequency");
plot.setColor(Color.blue);
plot.addPoints(h[0], h[1], Plot.BAR);
ImageJUtils.display(title, plot, windowOrganiser);
}
use of uk.ac.sussex.gdsc.core.ij.HistogramPlot.HistogramPlotBuilder in project GDSC-SMLM by aherbert.
the class DiffusionRateTest method plotJumpDistances.
/**
* Plot a cumulative histogram and standard histogram of the jump distances.
*
* @param title the title
* @param jumpDistances the jump distances
* @param dimensions the number of dimensions for the jumps
*/
private void plotJumpDistances(String title, DoubleData jumpDistances, int dimensions) {
// Cumulative histogram
// --------------------
final double[] values = jumpDistances.values();
String title2 = title + " Cumulative Jump Distance " + dimensions + "D";
final double[][] jdHistogram = JumpDistanceAnalysis.cumulativeHistogram(values);
Plot jdPlot = new Plot(title2, "Distance (um^2)", "Cumulative Probability");
jdPlot.addPoints(jdHistogram[0], jdHistogram[1], Plot.LINE);
ImageJUtils.display(title2, jdPlot, windowOrganiser);
// Plot the expected function
// This is the Chi-squared distribution: The sum of the squares of k independent
// standard normal random variables with k = dimensions. It is a special case of
// the gamma distribution. If the normals have non-unit variance the distribution
// is scaled.
// Chi ~ Gamma(k/2, 2) // using the scale parameterisation of the gamma
// s^2 * Chi ~ Gamma(k/2, 2*s^2)
// So if s^2 = 2D:
// 2D * Chi ~ Gamma(k/2, 4D)
final double estimatedD = pluginSettings.simpleD * pluginSettings.simpleSteps;
final double max = MathUtils.max(values);
final double[] x = SimpleArrayUtils.newArray(1000, 0, max / 1000);
final double k = dimensions / 2.0;
final double mean = 4 * estimatedD;
final GammaDistribution dist = new GammaDistribution(null, k, mean);
final double[] y = new double[x.length];
for (int i = 0; i < x.length; i++) {
y[i] = dist.cumulativeProbability(x[i]);
}
jdPlot.setColor(Color.red);
jdPlot.addPoints(x, y, Plot.LINE);
ImageJUtils.display(title2, jdPlot);
// Histogram
// ---------
title2 = title + " Jump " + dimensions + "D";
final HistogramPlot histogramPlot = new HistogramPlotBuilder(title2, jumpDistances, "Distance (um^2)").build();
// Assume the plot works
histogramPlot.show(windowOrganiser);
// Recompute the expected function
for (int i = 0; i < x.length; i++) {
y[i] = dist.density(x[i]);
}
// Scale to have the same area
final double[] xvalues = histogramPlot.getPlotXValues();
if (xvalues.length > 1) {
final double area1 = jumpDistances.size() * (xvalues[1] - xvalues[0]);
final double area2 = dist.cumulativeProbability(x[x.length - 1]);
final double scale = area1 / area2;
for (int i = 0; i < y.length; i++) {
y[i] *= scale;
}
}
jdPlot = histogramPlot.getPlot();
jdPlot.setColor(Color.red);
jdPlot.addPoints(x, y, Plot.LINE);
ImageJUtils.display(histogramPlot.getPlotTitle(), jdPlot);
}
use of uk.ac.sussex.gdsc.core.ij.HistogramPlot.HistogramPlotBuilder in project GDSC-SMLM by aherbert.
the class PsfEstimator method calculateStatistics.
private boolean calculateStatistics(PeakFit fitter, double[] params, double[] paramsDev) {
debug(" Fitting PSF");
swapStatistics();
// Create the fit engine using the PeakFit plugin
final FitConfiguration fitConfig = config.getFitConfiguration();
fitConfig.setInitialPeakStdDev0((float) params[1]);
try {
fitConfig.setInitialPeakStdDev1((float) params[2]);
fitConfig.setInitialAngle((float) Math.toRadians(params[0]));
} catch (IllegalStateException ex) {
// Ignore this as the current PSF is not a 2 axis and theta Gaussian PSF
}
final ImageStack stack = imp.getImageStack();
final Rectangle roi = stack.getProcessor(1).getRoi();
ImageSource source = new IJImageSource(imp);
// Allow interlaced data by wrapping the image source
if (interlacedData) {
source = new InterlacedImageSource(source, dataStart, dataBlock, dataSkip);
}
// Allow frame aggregation by wrapping the image source
if (integrateFrames > 1) {
source = new AggregatedImageSource(source, integrateFrames);
}
fitter.initialiseImage(source, roi, true);
fitter.addPeakResults(this);
fitter.initialiseFitting();
final FitEngine engine = fitter.createFitEngine();
// Use random slices
final int[] slices = new int[stack.getSize()];
for (int i = 0; i < slices.length; i++) {
slices[i] = i + 1;
}
RandomUtils.shuffle(slices, UniformRandomProviders.create());
IJ.showStatus("Fitting ...");
// Use multi-threaded code for speed
int sliceIndex;
for (sliceIndex = 0; sliceIndex < slices.length; sliceIndex++) {
final int slice = slices[sliceIndex];
IJ.showProgress(size(), settings.getNumberOfPeaks());
final ImageProcessor ip = stack.getProcessor(slice);
// stack processor does not set the bounds required by ImageConverter
ip.setRoi(roi);
final FitJob job = new FitJob(slice, ImageJImageConverter.getData(ip), roi);
engine.run(job);
if (sampleSizeReached() || ImageJUtils.isInterrupted()) {
break;
}
}
if (ImageJUtils.isInterrupted()) {
IJ.showProgress(1);
engine.end(true);
return false;
}
// Wait until we have enough results
while (!sampleSizeReached() && !engine.isQueueEmpty()) {
IJ.showProgress(size(), settings.getNumberOfPeaks());
try {
Thread.sleep(50);
} catch (final InterruptedException ex) {
Thread.currentThread().interrupt();
throw new ConcurrentRuntimeException("Unexpected interruption", ex);
}
}
// End now if we have enough samples
engine.end(sampleSizeReached());
ImageJUtils.finished();
// This count will be an over-estimate given that the provider is ahead of the consumer
// in this multi-threaded system
debug(" Processed %d/%d slices (%d peaks)", sliceIndex, slices.length, size());
setParams(ANGLE, params, paramsDev, sampleNew[ANGLE]);
setParams(X, params, paramsDev, sampleNew[X]);
setParams(Y, params, paramsDev, sampleNew[Y]);
if (settings.getShowHistograms()) {
final HistogramPlotBuilder builder = new HistogramPlotBuilder(TITLE).setNumberOfBins(settings.getHistogramBins());
final WindowOrganiser wo = new WindowOrganiser();
for (int ii = 0; ii < 3; ii++) {
if (sampleNew[ii].getN() == 0) {
continue;
}
final StoredDataStatistics stats = StoredDataStatistics.create(sampleNew[ii].getValues());
builder.setData(stats).setName(NAMES[ii]).setPlotLabel("Mean = " + MathUtils.rounded(stats.getMean()) + ". Median = " + MathUtils.rounded(sampleNew[ii].getPercentile(50))).show(wo);
}
wo.tile();
}
if (size() < 2) {
log("ERROR: Insufficient number of fitted peaks, terminating ...");
return false;
}
return true;
}
use of uk.ac.sussex.gdsc.core.ij.HistogramPlot.HistogramPlotBuilder 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("");
}
Aggregations