use of org.apache.commons.math3.analysis.function.Gaussian in project GDSC-SMLM by aherbert.
the class BaseFunctionSolverTest method drawGaussian.
/**
* Draw a Gaussian with Poisson shot noise and Gaussian read noise.
*
* @param params
* The Gaussian parameters
* @param noise
* The read noise
* @param noiseModel
* the noise model
* @return The data
*/
double[] drawGaussian(double[] params, double[] noise, NoiseModel noiseModel) {
double[] data = new double[size * size];
int n = params.length / 6;
Gaussian2DFunction f = GaussianFunctionFactory.create2D(n, size, size, flags, null);
f.initialise(params);
// Poisson noise
for (int i = 0; i < data.length; i++) {
CustomPoissonDistribution dist = new CustomPoissonDistribution(randomGenerator, 1);
double e = f.eval(i);
if (e > 0) {
dist.setMeanUnsafe(e);
data[i] = dist.sample();
}
}
// Simulate EM-gain
if (noiseModel == NoiseModel.EMCCD) {
// Use a gamma distribution
// Since the call random.nextGamma(...) creates a Gamma distribution
// which pre-calculates factors only using the scale parameter we
// create a custom gamma distribution where the shape can be set as a property.
CustomGammaDistribution dist = new CustomGammaDistribution(randomGenerator, 1, emGain);
for (int i = 0; i < data.length; i++) {
if (data[i] > 0) {
dist.setShapeUnsafe(data[i]);
// The sample will amplify the signal so we remap to the original scale
data[i] = dist.sample() / emGain;
}
}
}
// Read-noise
if (noise != null) {
for (int i = 0; i < data.length; i++) {
data[i] += randomGenerator.nextGaussian() * noise[i];
}
}
//gdsc.core.ij.Utils.display("Spot", data, size, size);
return data;
}
use of org.apache.commons.math3.analysis.function.Gaussian in project GDSC-SMLM by aherbert.
the class SCMOSLikelihoodWrapperTest method cumulativeProbabilityIsOneWithRealData.
private void cumulativeProbabilityIsOneWithRealData(final double mu, double min, double max, boolean test) {
double p = 0;
// Test using a standard Poisson-Gaussian convolution
//min = -max;
//final PoissonGaussianFunction pgf = PoissonGaussianFunction.createWithVariance(1, 1, VAR);
UnivariateIntegrator in = new SimpsonIntegrator();
p = in.integrate(20000, new UnivariateFunction() {
public double value(double x) {
double v;
v = SCMOSLikelihoodWrapper.likelihood(mu, VAR, G, O, x);
//System.out.printf("x=%f, v=%f\n", x, v);
return v;
}
}, min, max);
//System.out.printf("mu=%f, p=%f\n", mu, p);
if (test) {
Assert.assertEquals(String.format("mu=%f", mu), P_LIMIT, p, 0.02);
}
}
use of org.apache.commons.math3.analysis.function.Gaussian in project GDSC-SMLM by aherbert.
the class FIRE method runQEstimation.
private void runQEstimation() {
IJ.showStatus(TITLE + " ...");
if (!showQEstimationInputDialog())
return;
MemoryPeakResults results = ResultsManager.loadInputResults(inputOption, false);
if (results == null || results.size() == 0) {
IJ.error(TITLE, "No results could be loaded");
return;
}
if (results.getCalibration() == null) {
IJ.error(TITLE, "The results are not calibrated");
return;
}
results = cropToRoi(results);
if (results.size() < 2) {
IJ.error(TITLE, "No results within the crop region");
return;
}
initialise(results, null);
// We need localisation precision.
// Build a histogram of the localisation precision.
// Get the initial mean and SD and plot as a Gaussian.
PrecisionHistogram histogram = calculatePrecisionHistogram();
if (histogram == null) {
IJ.error(TITLE, "No localisation precision available.\n \nPlease choose " + PrecisionMethod.FIXED + " and enter a precision mean and SD.");
return;
}
StoredDataStatistics precision = histogram.precision;
//String name = results.getName();
double fourierImageScale = SCALE_VALUES[imageScaleIndex];
int imageSize = IMAGE_SIZE_VALUES[imageSizeIndex];
// Create the image and compute the numerator of FRC.
// Do not use the signal so results.size() is the number of localisations.
IJ.showStatus("Computing FRC curve ...");
FireImages images = createImages(fourierImageScale, imageSize, false);
// DEBUGGING - Save the two images to disk. Load the images into the Matlab
// code that calculates the Q-estimation and make this plugin match the functionality.
//IJ.save(new ImagePlus("i1", images.ip1), "/scratch/i1.tif");
//IJ.save(new ImagePlus("i2", images.ip2), "/scratch/i2.tif");
FRC frc = new FRC();
frc.progress = progress;
frc.setFourierMethod(fourierMethod);
frc.setSamplingMethod(samplingMethod);
frc.setPerimeterSamplingFactor(perimeterSamplingFactor);
FRCCurve frcCurve = frc.calculateFrcCurve(images.ip1, images.ip2, images.nmPerPixel);
if (frcCurve == null) {
IJ.error(TITLE, "Failed to compute FRC curve");
return;
}
IJ.showStatus("Running Q-estimation ...");
// Note:
// The method implemented here is based on Matlab code provided by Bernd Rieger.
// The idea is to compute the spurious correlation component of the FRC Numerator
// using an initial estimate of distribution of the localisation precision (assumed
// to be Gaussian). This component is the contribution of repeat localisations of
// the same molecule to the numerator and is modelled as an exponential decay
// (exp_decay). The component is scaled by the Q-value which
// is the average number of times a molecule is seen in addition to the first time.
// At large spatial frequencies the scaled component should match the numerator,
// i.e. at high resolution (low FIRE number) the numerator is made up of repeat
// localisations of the same molecule and not actual structure in the image.
// The best fit is where the numerator equals the scaled component, i.e. num / (q*exp_decay) == 1.
// The FRC Numerator is plotted and Q can be determined by
// adjusting Q and the precision mean and SD to maximise the cost function.
// This can be done interactively by the user with the effect on the FRC curve
// dynamically updated and displayed.
// Compute the scaled FRC numerator
double qNorm = (1 / frcCurve.mean1 + 1 / frcCurve.mean2);
double[] frcnum = new double[frcCurve.getSize()];
for (int i = 0; i < frcnum.length; i++) {
FRCCurveResult r = frcCurve.get(i);
frcnum[i] = qNorm * r.getNumerator() / r.getNumberOfSamples();
}
// Compute the spatial frequency and the region for curve fitting
double[] q = FRC.computeQ(frcCurve, false);
int low = 0, high = q.length;
while (high > 0 && q[high - 1] > maxQ) high--;
while (low < q.length && q[low] < minQ) low++;
// Require we fit at least 10% of the curve
if (high - low < q.length * 0.1) {
IJ.error(TITLE, "Not enough points for Q estimation");
return;
}
// Obtain initial estimate of Q plateau height and decay.
// This can be done by fitting the precision histogram and then fixing the mean and sigma.
// Or it can be done by allowing the precision to be sampled and the mean and sigma
// become parameters for fitting.
// Check if we can sample precision values
boolean sampleDecay = precision != null && FIRE.sampleDecay;
double[] exp_decay;
if (sampleDecay) {
// Random sample of precision values from the distribution is used to
// construct the decay curve
int[] sample = Random.sample(10000, precision.getN(), new Well19937c());
final double four_pi2 = 4 * Math.PI * Math.PI;
double[] pre = new double[q.length];
for (int i = 1; i < q.length; i++) pre[i] = -four_pi2 * q[i] * q[i];
// Sample
final int n = sample.length;
double[] hq = new double[n];
for (int j = 0; j < n; j++) {
// Scale to SR pixels
double s2 = precision.getValue(sample[j]) / images.nmPerPixel;
s2 *= s2;
for (int i = 1; i < q.length; i++) hq[i] += FastMath.exp(pre[i] * s2);
}
for (int i = 1; i < q.length; i++) hq[i] /= n;
exp_decay = new double[q.length];
exp_decay[0] = 1;
for (int i = 1; i < q.length; i++) {
double sinc_q = sinc(Math.PI * q[i]);
exp_decay[i] = sinc_q * sinc_q * hq[i];
}
} else {
// Note: The sigma mean and std should be in the units of super-resolution
// pixels so scale to SR pixels
exp_decay = computeExpDecay(histogram.mean / images.nmPerPixel, histogram.sigma / images.nmPerPixel, q);
}
// Smoothing
double[] smooth;
if (loessSmoothing) {
// Note: This computes the log then smooths it
double bandwidth = 0.1;
int robustness = 0;
double[] l = new double[exp_decay.length];
for (int i = 0; i < l.length; i++) {
// Original Matlab code computes the log for each array.
// This is equivalent to a single log on the fraction of the two.
// Perhaps the two log method is more numerically stable.
//l[i] = Math.log(Math.abs(frcnum[i])) - Math.log(exp_decay[i]);
l[i] = Math.log(Math.abs(frcnum[i] / exp_decay[i]));
}
try {
LoessInterpolator loess = new LoessInterpolator(bandwidth, robustness);
smooth = loess.smooth(q, l);
} catch (Exception e) {
IJ.error(TITLE, "LOESS smoothing failed");
return;
}
} else {
// Note: This smooths the curve before computing the log
double[] norm = new double[exp_decay.length];
for (int i = 0; i < norm.length; i++) {
norm[i] = frcnum[i] / exp_decay[i];
}
// Median window of 5 == radius of 2
MedianWindow mw = new MedianWindow(norm, 2);
smooth = new double[exp_decay.length];
for (int i = 0; i < norm.length; i++) {
smooth[i] = Math.log(Math.abs(mw.getMedian()));
mw.increment();
}
}
// Fit with quadratic to find the initial guess.
// Note: example Matlab code frc_Qcorrection7.m identifies regions of the
// smoothed log curve with low derivative and only fits those. The fit is
// used for the final estimate. Fitting a subset with low derivative is not
// implemented here since the initial estimate is subsequently optimised
// to maximise a cost function.
Quadratic curve = new Quadratic();
SimpleCurveFitter fit = SimpleCurveFitter.create(curve, new double[2]);
WeightedObservedPoints points = new WeightedObservedPoints();
for (int i = low; i < high; i++) points.add(q[i], smooth[i]);
double[] estimate = fit.fit(points.toList());
double qValue = FastMath.exp(estimate[0]);
//System.out.printf("Initial q-estimate = %s => %.3f\n", Arrays.toString(estimate), qValue);
// This could be made an option. Just use for debugging
boolean debug = false;
if (debug) {
// Plot the initial fit and the fit curve
double[] qScaled = FRC.computeQ(frcCurve, true);
double[] line = new double[q.length];
for (int i = 0; i < q.length; i++) line[i] = curve.value(q[i], estimate);
String title = TITLE + " Initial fit";
Plot2 plot = new Plot2(title, "Spatial Frequency (nm^-1)", "FRC Numerator");
String label = String.format("Q = %.3f", qValue);
plot.addPoints(qScaled, smooth, Plot.LINE);
plot.setColor(Color.red);
plot.addPoints(qScaled, line, Plot.LINE);
plot.setColor(Color.black);
plot.addLabel(0, 0, label);
Utils.display(title, plot, Utils.NO_TO_FRONT);
}
if (fitPrecision) {
// Q - Should this be optional?
if (sampleDecay) {
// If a sample of the precision was used to construct the data for the initial fit
// then update the estimate using the fit result since it will be a better start point.
histogram.sigma = precision.getStandardDeviation();
// Normalise sum-of-squares to the SR pixel size
double meanSumOfSquares = (precision.getSumOfSquares() / (images.nmPerPixel * images.nmPerPixel)) / precision.getN();
histogram.mean = images.nmPerPixel * Math.sqrt(meanSumOfSquares - estimate[1] / (4 * Math.PI * Math.PI));
}
// Do a multivariate fit ...
SimplexOptimizer opt = new SimplexOptimizer(1e-6, 1e-10);
PointValuePair p = null;
MultiPlateauness f = new MultiPlateauness(frcnum, q, low, high);
double[] initial = new double[] { histogram.mean / images.nmPerPixel, histogram.sigma / images.nmPerPixel, qValue };
p = findMin(p, opt, f, scale(initial, 0.1));
p = findMin(p, opt, f, scale(initial, 0.5));
p = findMin(p, opt, f, initial);
p = findMin(p, opt, f, scale(initial, 2));
p = findMin(p, opt, f, scale(initial, 10));
if (p != null) {
double[] point = p.getPointRef();
histogram.mean = point[0] * images.nmPerPixel;
histogram.sigma = point[1] * images.nmPerPixel;
qValue = point[2];
}
} else {
// If so then this should be optional.
if (sampleDecay) {
if (precisionMethod != PrecisionMethod.FIXED) {
histogram.sigma = precision.getStandardDeviation();
// Normalise sum-of-squares to the SR pixel size
double meanSumOfSquares = (precision.getSumOfSquares() / (images.nmPerPixel * images.nmPerPixel)) / precision.getN();
histogram.mean = images.nmPerPixel * Math.sqrt(meanSumOfSquares - estimate[1] / (4 * Math.PI * Math.PI));
}
exp_decay = computeExpDecay(histogram.mean / images.nmPerPixel, histogram.sigma / images.nmPerPixel, q);
}
// Estimate spurious component by promoting plateauness.
// The Matlab code used random initial points for a Simplex optimiser.
// A Brent line search should be pretty deterministic so do simple repeats.
// However it will proceed downhill so if the initial point is wrong then
// it will find a sub-optimal result.
UnivariateOptimizer o = new BrentOptimizer(1e-3, 1e-6);
Plateauness f = new Plateauness(frcnum, exp_decay, low, high);
UnivariatePointValuePair p = null;
p = findMin(p, o, f, qValue, 0.1);
p = findMin(p, o, f, qValue, 0.2);
p = findMin(p, o, f, qValue, 0.333);
p = findMin(p, o, f, qValue, 0.5);
// Do some Simplex repeats as well
SimplexOptimizer opt = new SimplexOptimizer(1e-6, 1e-10);
p = findMin(p, opt, f, qValue * 0.1);
p = findMin(p, opt, f, qValue * 0.5);
p = findMin(p, opt, f, qValue);
p = findMin(p, opt, f, qValue * 2);
p = findMin(p, opt, f, qValue * 10);
if (p != null)
qValue = p.getPoint();
}
QPlot qplot = new QPlot(frcCurve, qValue, low, high);
// Interactive dialog to estimate Q (blinking events per flourophore) using
// sliders for the mean and standard deviation of the localisation precision.
showQEstimationDialog(histogram, qplot, frcCurve, images.nmPerPixel);
IJ.showStatus(TITLE + " complete");
}
use of org.apache.commons.math3.analysis.function.Gaussian in project GDSC-SMLM by aherbert.
the class EMGainAnalysis method simulateFromPoissonGammaGaussian.
/**
* Randomly generate a histogram from poisson-gamma-gaussian samples
*
* @return The histogram
*/
private int[] simulateFromPoissonGammaGaussian() {
// Randomly sample
RandomGenerator random = new Well44497b(System.currentTimeMillis() + System.identityHashCode(this));
PoissonDistribution poisson = new PoissonDistribution(random, _photons, PoissonDistribution.DEFAULT_EPSILON, PoissonDistribution.DEFAULT_MAX_ITERATIONS);
CustomGammaDistribution gamma = new CustomGammaDistribution(random, _photons, _gain, GammaDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
final int steps = simulationSize;
int[] sample = new int[steps];
for (int n = 0; n < steps; n++) {
if (n % 64 == 0)
IJ.showProgress(n, steps);
// Poisson
double d = poisson.sample();
// Gamma
if (d > 0) {
gamma.setShapeUnsafe(d);
d = gamma.sample();
}
// Gaussian
d += _noise * random.nextGaussian();
// Convert the sample to a count
sample[n] = (int) Math.round(d + _bias);
}
int max = Maths.max(sample);
int[] h = new int[max + 1];
for (int s : sample) h[s]++;
return h;
}
use of org.apache.commons.math3.analysis.function.Gaussian in project GDSC-SMLM by aherbert.
the class FIRE method calculatePrecisionHistogram.
/**
* Calculate a histogram of the precision. The precision can be either stored in the results or calculated using the
* Mortensen formula. If the precision method for Q estimation is not fixed then the histogram is fitted with a
* Gaussian to create an initial estimate.
*
* @param precisionMethod
* the precision method
* @return The precision histogram
*/
private PrecisionHistogram calculatePrecisionHistogram() {
boolean logFitParameters = false;
String title = results.getName() + " Precision Histogram";
// Check if the results has the precision already or if it can be computed.
boolean canUseStored = canUseStoredPrecision(results);
boolean canCalculatePrecision = canCalculatePrecision(results);
// Set the method to compute a histogram. Default to the user selected option.
PrecisionMethod m = null;
if (canUseStored && precisionMethod == PrecisionMethod.STORED)
m = precisionMethod;
else if (canCalculatePrecision && precisionMethod == PrecisionMethod.CALCULATE)
m = precisionMethod;
if (m == null) {
// We only have two choices so if one is available then select it.
if (canUseStored)
m = PrecisionMethod.STORED;
else if (canCalculatePrecision)
m = PrecisionMethod.CALCULATE;
// If the user selected a method not available then log a warning
if (m != null && precisionMethod != PrecisionMethod.FIXED) {
IJ.log(String.format("%s : Selected precision method '%s' not available, switching to '%s'", TITLE, precisionMethod, m.getName()));
}
if (m == null) {
// This does not matter if the user has provide a fixed input.
if (precisionMethod == PrecisionMethod.FIXED) {
PrecisionHistogram histogram = new PrecisionHistogram(title);
histogram.mean = mean;
histogram.sigma = sigma;
return histogram;
}
// No precision
return null;
}
}
// We get here if we can compute precision.
// Build the histogram
StoredDataStatistics precision = new StoredDataStatistics(results.size());
if (m == PrecisionMethod.STORED) {
for (PeakResult r : results.getResults()) {
precision.add(r.getPrecision());
}
} else {
final double nmPerPixel = results.getNmPerPixel();
final double gain = results.getGain();
final boolean emCCD = results.isEMCCD();
for (PeakResult r : results.getResults()) {
precision.add(r.getPrecision(nmPerPixel, gain, emCCD));
}
}
//System.out.printf("Raw p = %f\n", precision.getMean());
double yMin = Double.NEGATIVE_INFINITY, yMax = 0;
// Set the min and max y-values using 1.5 x IQR
DescriptiveStatistics stats = precision.getStatistics();
double lower = stats.getPercentile(25);
double upper = stats.getPercentile(75);
if (Double.isNaN(lower) || Double.isNaN(upper)) {
if (logFitParameters)
Utils.log("Error computing IQR: %f - %f", lower, upper);
} else {
double iqr = upper - lower;
yMin = FastMath.max(lower - iqr, stats.getMin());
yMax = FastMath.min(upper + iqr, stats.getMax());
if (logFitParameters)
Utils.log(" Data range: %f - %f. Plotting 1.5x IQR: %f - %f", stats.getMin(), stats.getMax(), yMin, yMax);
}
if (yMin == Double.NEGATIVE_INFINITY) {
int n = 5;
yMin = Math.max(stats.getMin(), stats.getMean() - n * stats.getStandardDeviation());
yMax = Math.min(stats.getMax(), stats.getMean() + n * stats.getStandardDeviation());
if (logFitParameters)
Utils.log(" Data range: %f - %f. Plotting mean +/- %dxSD: %f - %f", stats.getMin(), stats.getMax(), n, yMin, yMax);
}
// Get the data within the range
double[] data = precision.getValues();
precision = new StoredDataStatistics(data.length);
for (double d : data) {
if (d < yMin || d > yMax)
continue;
precision.add(d);
}
int histogramBins = Utils.getBins(precision, Utils.BinMethod.SCOTT);
float[][] hist = Utils.calcHistogram(precision.getFloatValues(), yMin, yMax, histogramBins);
PrecisionHistogram histogram = new PrecisionHistogram(hist, precision, title);
if (precisionMethod == PrecisionMethod.FIXED) {
histogram.mean = mean;
histogram.sigma = sigma;
return histogram;
}
// Fitting of the histogram to produce the initial estimate
// Extract non-zero data
float[] x = Arrays.copyOf(hist[0], hist[0].length);
float[] y = hist[1];
int count = 0;
float dx = (x[1] - x[0]) * 0.5f;
for (int i = 0; i < y.length; i++) if (y[i] > 0) {
x[count] = x[i] + dx;
y[count] = y[i];
count++;
}
x = Arrays.copyOf(x, count);
y = Arrays.copyOf(y, count);
// Sense check to fitted data. Get mean and SD of histogram
double[] stats2 = Utils.getHistogramStatistics(x, y);
if (logFitParameters)
Utils.log(" Initial Statistics: %f +/- %f", stats2[0], stats2[1]);
histogram.mean = stats2[0];
histogram.sigma = stats2[1];
// Standard Gaussian fit
double[] parameters = fitGaussian(x, y);
if (parameters == null) {
Utils.log(" Failed to fit initial Gaussian");
return histogram;
}
double newMean = parameters[1];
double error = Math.abs(stats2[0] - newMean) / stats2[1];
if (error > 3) {
Utils.log(" Failed to fit Gaussian: %f standard deviations from histogram mean", error);
return histogram;
}
if (newMean < yMin || newMean > yMax) {
Utils.log(" Failed to fit Gaussian: %f outside data range %f - %f", newMean, yMin, yMax);
return histogram;
}
if (logFitParameters)
Utils.log(" Initial Gaussian: %f @ %f +/- %f", parameters[0], parameters[1], parameters[2]);
histogram.mean = parameters[1];
histogram.sigma = parameters[2];
return histogram;
}
Aggregations