use of gdsc.core.utils.StoredData in project GDSC-SMLM by aherbert.
the class DiffusionRateTest method run.
/*
* (non-Javadoc)
*
* @see ij.plugin.PlugIn#run(java.lang.String)
*/
public void run(String arg) {
SMLMUsageTracker.recordPlugin(this.getClass(), arg);
if (IJ.controlKeyDown()) {
simpleTest();
return;
}
extraOptions = Utils.isExtraOptions();
if (!showDialog())
return;
lastSimulatedDataset[0] = lastSimulatedDataset[1] = "";
lastSimulatedPrecision = 0;
final int totalSteps = (int) Math.ceil(settings.seconds * settings.stepsPerSecond);
conversionFactor = 1000000.0 / (settings.pixelPitch * settings.pixelPitch);
// Diffusion rate is um^2/sec. Convert to pixels per simulation frame.
final double diffusionRateInPixelsPerSecond = settings.diffusionRate * conversionFactor;
final double diffusionRateInPixelsPerStep = diffusionRateInPixelsPerSecond / settings.stepsPerSecond;
final double precisionInPixels = myPrecision / settings.pixelPitch;
final boolean addError = myPrecision != 0;
Utils.log(TITLE + " : D = %s um^2/sec, Precision = %s nm", Utils.rounded(settings.diffusionRate, 4), Utils.rounded(myPrecision, 4));
Utils.log("Mean-displacement per dimension = %s nm/sec", Utils.rounded(1e3 * ImageModel.getRandomMoveDistance(settings.diffusionRate), 4));
if (extraOptions)
Utils.log("Step size = %s, precision = %s", Utils.rounded(ImageModel.getRandomMoveDistance(diffusionRateInPixelsPerStep)), Utils.rounded(precisionInPixels));
// Convert diffusion co-efficient into the standard deviation for the random walk
final double diffusionSigma = (settings.getDiffusionType() == DiffusionType.LINEAR_WALK) ? // Q. What should this be? At the moment just do 1D diffusion on a random vector
ImageModel.getRandomMoveDistance(diffusionRateInPixelsPerStep) : ImageModel.getRandomMoveDistance(diffusionRateInPixelsPerStep);
Utils.log("Simulation step-size = %s nm", Utils.rounded(settings.pixelPitch * diffusionSigma, 4));
// Move the molecules and get the diffusion rate
IJ.showStatus("Simulating ...");
final long start = System.nanoTime();
final long seed = System.currentTimeMillis() + System.identityHashCode(this);
RandomGenerator[] random = new RandomGenerator[3];
RandomGenerator[] random2 = new RandomGenerator[3];
for (int i = 0; i < 3; i++) {
random[i] = new Well19937c(seed + i * 12436);
random2[i] = new Well19937c(seed + i * 678678 + 3);
}
Statistics[] stats2D = new Statistics[totalSteps];
Statistics[] stats3D = new Statistics[totalSteps];
StoredDataStatistics jumpDistances2D = new StoredDataStatistics(totalSteps);
StoredDataStatistics jumpDistances3D = new StoredDataStatistics(totalSteps);
for (int j = 0; j < totalSteps; j++) {
stats2D[j] = new Statistics();
stats3D[j] = new Statistics();
}
SphericalDistribution dist = new SphericalDistribution(settings.confinementRadius / settings.pixelPitch);
Statistics asymptote = new Statistics();
// Save results to memory
MemoryPeakResults results = new MemoryPeakResults(totalSteps);
Calibration cal = new Calibration(settings.pixelPitch, 1, 1000.0 / settings.stepsPerSecond);
results.setCalibration(cal);
results.setName(TITLE);
int peak = 0;
// Store raw coordinates
ArrayList<Point> points = new ArrayList<Point>(totalSteps);
StoredData totalJumpDistances1D = new StoredData(settings.particles);
StoredData totalJumpDistances2D = new StoredData(settings.particles);
StoredData totalJumpDistances3D = new StoredData(settings.particles);
for (int i = 0; i < settings.particles; i++) {
if (i % 16 == 0) {
IJ.showProgress(i, settings.particles);
if (Utils.isInterrupted())
return;
}
// Increment the frame so that tracing analysis can distinguish traces
peak++;
double[] origin = new double[3];
final int id = i + 1;
MoleculeModel m = new MoleculeModel(id, origin.clone());
if (addError)
origin = addError(origin, precisionInPixels, random);
if (useConfinement) {
// Note: When using confinement the average displacement should asymptote
// at the average distance of a point from the centre of a ball. This is 3r/4.
// See: http://answers.yahoo.com/question/index?qid=20090131162630AAMTUfM
// The equivalent in 2D is 2r/3. However although we are plotting 2D distance
// this is a projection of the 3D position onto the plane and so the particles
// will not be evenly spread (there will be clustering at centre caused by the
// poles)
final double[] axis = (settings.getDiffusionType() == DiffusionType.LINEAR_WALK) ? nextVector() : null;
for (int j = 0; j < totalSteps; j++) {
double[] xyz = m.getCoordinates();
double[] originalXyz = xyz.clone();
for (int n = confinementAttempts; n-- > 0; ) {
if (settings.getDiffusionType() == DiffusionType.GRID_WALK)
m.walk(diffusionSigma, random);
else if (settings.getDiffusionType() == DiffusionType.LINEAR_WALK)
m.slide(diffusionSigma, axis, random[0]);
else
m.move(diffusionSigma, random);
if (!dist.isWithin(m.getCoordinates())) {
// Reset position
for (int k = 0; k < 3; k++) xyz[k] = originalXyz[k];
} else {
// The move was allowed
break;
}
}
points.add(new Point(id, xyz));
if (addError)
xyz = addError(xyz, precisionInPixels, random2);
peak = record(xyz, id, peak, stats2D[j], stats3D[j], jumpDistances2D, jumpDistances3D, origin, results);
}
asymptote.add(distance(m.getCoordinates()));
} else {
if (settings.getDiffusionType() == DiffusionType.GRID_WALK) {
for (int j = 0; j < totalSteps; j++) {
m.walk(diffusionSigma, random);
double[] xyz = m.getCoordinates();
points.add(new Point(id, xyz));
if (addError)
xyz = addError(xyz, precisionInPixels, random2);
peak = record(xyz, id, peak, stats2D[j], stats3D[j], jumpDistances2D, jumpDistances3D, origin, results);
}
} else if (settings.getDiffusionType() == DiffusionType.LINEAR_WALK) {
final double[] axis = nextVector();
for (int j = 0; j < totalSteps; j++) {
m.slide(diffusionSigma, axis, random[0]);
double[] xyz = m.getCoordinates();
points.add(new Point(id, xyz));
if (addError)
xyz = addError(xyz, precisionInPixels, random2);
peak = record(xyz, id, peak, stats2D[j], stats3D[j], jumpDistances2D, jumpDistances3D, origin, results);
}
} else {
for (int j = 0; j < totalSteps; j++) {
m.move(diffusionSigma, random);
double[] xyz = m.getCoordinates();
points.add(new Point(id, xyz));
if (addError)
xyz = addError(xyz, precisionInPixels, random2);
peak = record(xyz, id, peak, stats2D[j], stats3D[j], jumpDistances2D, jumpDistances3D, origin, results);
}
}
}
// Debug: record all the particles so they can be analysed
// System.out.printf("%f %f %f\n", m.getX(), m.getY(), m.getZ());
final double[] xyz = m.getCoordinates();
double d2 = 0;
totalJumpDistances1D.add(d2 = xyz[0] * xyz[0]);
totalJumpDistances2D.add(d2 += xyz[1] * xyz[1]);
totalJumpDistances3D.add(d2 += xyz[2] * xyz[2]);
}
final double time = (System.nanoTime() - start) / 1000000.0;
IJ.showProgress(1);
MemoryPeakResults.addResults(results);
lastSimulatedDataset[0] = results.getName();
lastSimulatedPrecision = myPrecision;
// Convert pixels^2/step to um^2/sec
final double msd2D = (jumpDistances2D.getMean() / conversionFactor) / (results.getCalibration().getExposureTime() / 1000);
final double msd3D = (jumpDistances3D.getMean() / conversionFactor) / (results.getCalibration().getExposureTime() / 1000);
Utils.log("Raw data D=%s um^2/s, Precision = %s nm, N=%d, step=%s s, mean2D=%s um^2, MSD 2D = %s um^2/s, mean3D=%s um^2, MSD 3D = %s um^2/s", Utils.rounded(settings.diffusionRate), Utils.rounded(myPrecision), jumpDistances2D.getN(), Utils.rounded(results.getCalibration().getExposureTime() / 1000), Utils.rounded(jumpDistances2D.getMean() / conversionFactor), Utils.rounded(msd2D), Utils.rounded(jumpDistances3D.getMean() / conversionFactor), Utils.rounded(msd3D));
aggregateIntoFrames(points, addError, precisionInPixels, random2);
IJ.showStatus("Analysing results ...");
if (showDiffusionExample) {
showExample(totalSteps, diffusionSigma, random);
}
// Plot a graph of mean squared distance
double[] xValues = new double[stats2D.length];
double[] yValues2D = new double[stats2D.length];
double[] yValues3D = new double[stats3D.length];
double[] upper2D = new double[stats2D.length];
double[] lower2D = new double[stats2D.length];
double[] upper3D = new double[stats3D.length];
double[] lower3D = new double[stats3D.length];
SimpleRegression r2D = new SimpleRegression(false);
SimpleRegression r3D = new SimpleRegression(false);
final int firstN = (useConfinement) ? fitN : totalSteps;
for (int j = 0; j < totalSteps; j++) {
// Convert steps to seconds
xValues[j] = (double) (j + 1) / settings.stepsPerSecond;
// Convert values in pixels^2 to um^2
final double mean2D = stats2D[j].getMean() / conversionFactor;
final double mean3D = stats3D[j].getMean() / conversionFactor;
final double sd2D = stats2D[j].getStandardDeviation() / conversionFactor;
final double sd3D = stats3D[j].getStandardDeviation() / conversionFactor;
yValues2D[j] = mean2D;
yValues3D[j] = mean3D;
upper2D[j] = mean2D + sd2D;
lower2D[j] = mean2D - sd2D;
upper3D[j] = mean3D + sd3D;
lower3D[j] = mean3D - sd3D;
if (j < firstN) {
r2D.addData(xValues[j], yValues2D[j]);
r3D.addData(xValues[j], yValues3D[j]);
}
}
// TODO - Fit using the equation for 2D confined diffusion:
// MSD = 4s^2 + R^2 (1 - 0.99e^(-1.84^2 Dt / R^2)
// s = localisation precision
// R = confinement radius
// D = 2D diffusion coefficient
// t = time
final PolynomialFunction fitted2D, fitted3D;
if (r2D.getN() > 0) {
// Do linear regression to get diffusion rate
final double[] best2D = new double[] { r2D.getIntercept(), r2D.getSlope() };
fitted2D = new PolynomialFunction(best2D);
final double[] best3D = new double[] { r3D.getIntercept(), r3D.getSlope() };
fitted3D = new PolynomialFunction(best3D);
// For 2D diffusion: d^2 = 4D
// where: d^2 = mean-square displacement
double D = best2D[1] / 4.0;
String msg = "2D Diffusion rate = " + Utils.rounded(D, 4) + " um^2 / sec (" + Utils.timeToString(time) + ")";
IJ.showStatus(msg);
Utils.log(msg);
D = best3D[1] / 6.0;
Utils.log("3D Diffusion rate = " + Utils.rounded(D, 4) + " um^2 / sec (" + Utils.timeToString(time) + ")");
} else {
fitted2D = fitted3D = null;
}
// Create plots
plotMSD(totalSteps, xValues, yValues2D, lower2D, upper2D, fitted2D, 2);
plotMSD(totalSteps, xValues, yValues3D, lower3D, upper3D, fitted3D, 3);
plotJumpDistances(TITLE, jumpDistances2D, 2, 1);
plotJumpDistances(TITLE, jumpDistances3D, 3, 1);
if (idCount > 0)
new WindowOrganiser().tileWindows(idList);
if (useConfinement)
Utils.log("3D asymptote distance = %s nm (expected %.2f)", Utils.rounded(asymptote.getMean() * settings.pixelPitch, 4), 3 * settings.confinementRadius / 4);
}
use of gdsc.core.utils.StoredData in project GDSC-SMLM by aherbert.
the class DarkTimeAnalysis method analyse.
private void analyse(MemoryPeakResults results) {
// Find min and max time frames
results.sort();
int min = results.getHead().getFrame();
int max = results.getTail().getEndFrame();
// Trace results
double d = searchDistance / results.getCalibration().getNmPerPixel();
int range = max - min + 1;
if (maxDarkTime > 0)
range = FastMath.max(1, (int) Math.round(maxDarkTime * 1000 / msPerFrame));
IJTrackProgress tracker = new IJTrackProgress();
tracker.status("Analysing ...");
tracker.log("Analysing (d=%s nm (%s px) t=%s s (%d frames)) ...", Utils.rounded(searchDistance), Utils.rounded(d), Utils.rounded(range * msPerFrame / 1000.0), range);
Trace[] traces;
if (method == 0) {
TraceManager tm = new TraceManager(results);
tm.setTracker(tracker);
tm.traceMolecules(d, range);
traces = tm.getTraces();
} else {
ClusteringEngine engine = new ClusteringEngine(Prefs.getThreads(), algorithms[method - 1], tracker);
List<PeakResult> peakResults = results.getResults();
ArrayList<Cluster> clusters = engine.findClusters(TraceMolecules.convertToClusterPoints(peakResults), d, range);
traces = TraceMolecules.convertToTraces(peakResults, clusters);
}
tracker.status("Computing histogram ...");
// Build dark-time histogram
int[] times = new int[range];
StoredData stats = new StoredData();
for (Trace trace : traces) {
if (trace.getNBlinks() > 1) {
for (int t : trace.getOffTimes()) {
times[t]++;
}
stats.add(trace.getOffTimes());
}
}
plotDarkTimeHistogram(stats);
// Cumulative histogram
for (int i = 1; i < times.length; i++) times[i] += times[i - 1];
int total = times[times.length - 1];
// Plot dark-time up to 100%
double[] x = new double[range];
double[] y = new double[range];
int truncate = 0;
for (int i = 0; i < x.length; i++) {
x[i] = i * msPerFrame;
y[i] = (100.0 * times[i]) / total;
if (// 100%
times[i] == total) {
truncate = i + 1;
break;
}
}
if (truncate > 0) {
x = Arrays.copyOf(x, truncate);
y = Arrays.copyOf(y, truncate);
}
String title = "Cumulative Dark-time";
Plot2 plot = new Plot2(title, "Time (ms)", "Percentile", x, y);
Utils.display(title, plot);
// Report percentile
for (int i = 0; i < y.length; i++) {
if (y[i] >= percentile) {
Utils.log("Dark-time Percentile %.1f @ %s ms = %s s", percentile, Utils.rounded(x[i]), Utils.rounded(x[i] / 1000));
break;
}
}
tracker.status("");
}
use of gdsc.core.utils.StoredData in project GDSC-SMLM by aherbert.
the class CMOSAnalysis method showHistogram.
private void showHistogram(String name, double[] values, int bins, Statistics stats, WindowOrganiser wo) {
DoubleData data = new StoredData(values, false);
double minWidth = 0;
int removeOutliers = 0;
// Plot2.BAR; // A bar chart confuses the log plot since it plots lines to zero.
int shape = Plot.CIRCLE;
String label = String.format("Mean = %s +/- %s", Utils.rounded(stats.getMean()), Utils.rounded(stats.getStandardDeviation()));
int id = Utils.showHistogram(TITLE, data, name, minWidth, removeOutliers, bins, shape, label);
if (Utils.isNewWindow())
wo.add(id);
// Redraw using a log scale. This requires a non-zero y-min
Plot plot = Utils.plot;
double[] limits = plot.getLimits();
plot.setLimits(limits[0], limits[1], 1, limits[3]);
plot.setAxisYLog(true);
Utils.plot.updateImage();
}
use of gdsc.core.utils.StoredData in project GDSC-SMLM by aherbert.
the class BenchmarkSpotFilter method showFailuresPlot.
private void showFailuresPlot(BenchmarkFilterResult filterResult) {
double[][] h = filterResult.cumul;
StoredData data = filterResult.stats;
String xTitle = "Failures";
final int id = Utils.showHistogram(TITLE, data, xTitle, 1, 0, 0);
if (Utils.isNewWindow())
windowOrganiser.add(id);
String title = TITLE + " " + xTitle + " Cumulative";
Plot2 plot = new Plot2(title, xTitle, "Frequency");
double xMin = (data.size() == 0) ? 1 : h[0][0];
double xMax = (data.size() == 0) ? 1 : h[0][h[0].length - 1] + 1;
double xPadding = 0.05 * (xMax - xMin);
plot.setLimits(xMin - xPadding, xMax, 0, 1.05);
plot.setColor(Color.blue);
plot.addPoints(h[0], h[1], Plot2.BAR);
PlotWindow pw = Utils.display(title, plot);
if (Utils.isNewWindow())
windowOrganiser.add(pw);
}
use of gdsc.core.utils.StoredData in project GDSC-SMLM by aherbert.
the class PCPALMMolecules method runSimulation.
private void runSimulation(boolean resultsAvailable) {
if (resultsAvailable && !showSimulationDialog())
return;
startLog();
log("Simulation parameters");
if (blinkingDistribution == 3) {
log(" - Clusters = %d", nMolecules);
log(" - Simulation size = %s um", Utils.rounded(simulationSize, 4));
log(" - Molecules/cluster = %s", Utils.rounded(blinkingRate, 4));
log(" - Blinking distribution = %s", BLINKING_DISTRIBUTION[blinkingDistribution]);
log(" - p-Value = %s", Utils.rounded(p, 4));
} else {
log(" - Molecules = %d", nMolecules);
log(" - Simulation size = %s um", Utils.rounded(simulationSize, 4));
log(" - Blinking rate = %s", Utils.rounded(blinkingRate, 4));
log(" - Blinking distribution = %s", BLINKING_DISTRIBUTION[blinkingDistribution]);
}
log(" - Average precision = %s nm", Utils.rounded(sigmaS, 4));
log(" - Clusters simulation = " + CLUSTER_SIMULATION[clusterSimulation]);
if (clusterSimulation > 0) {
log(" - Cluster number = %s +/- %s", Utils.rounded(clusterNumber, 4), Utils.rounded(clusterNumberSD, 4));
log(" - Cluster radius = %s nm", Utils.rounded(clusterRadius, 4));
}
final double nmPerPixel = 100;
double width = simulationSize * 1000.0;
// Allow a border of 3 x sigma for +/- precision
//if (blinkingRate > 1)
width -= 3 * sigmaS;
RandomGenerator randomGenerator = new Well19937c(System.currentTimeMillis() + System.identityHashCode(this));
RandomDataGenerator dataGenerator = new RandomDataGenerator(randomGenerator);
UniformDistribution dist = new UniformDistribution(null, new double[] { width, width, 0 }, randomGenerator.nextInt());
molecules = new ArrayList<Molecule>(nMolecules);
// Create some dummy results since the calibration is required for later analysis
results = new MemoryPeakResults();
results.setCalibration(new gdsc.smlm.results.Calibration(nmPerPixel, 1, 100));
results.setSource(new NullSource("Molecule Simulation"));
results.begin();
int count = 0;
// Generate a sequence of coordinates
ArrayList<double[]> xyz = new ArrayList<double[]>((int) (nMolecules * 1.1));
Statistics statsRadius = new Statistics();
Statistics statsSize = new Statistics();
String maskTitle = TITLE + " Cluster Mask";
ByteProcessor bp = null;
double maskScale = 0;
if (clusterSimulation > 0) {
// Simulate clusters.
// Note: In the Veatch et al. paper (Plos 1, e31457) correlation functions are built using circles
// with small radii of 4-8 Arbitrary Units (AU) or large radii of 10-30 AU. A fluctuations model is
// created at T = 1.075 Tc. It is not clear exactly how the particles are distributed.
// It may be that a mask is created first using the model. The particles are placed on the mask using
// a specified density. This simulation produces a figure to show either a damped cosine function
// (circles) or an exponential (fluctuations). The number of particles in each circle may be randomly
// determined just by density. The figure does not discuss the derivation of the cluster size
// statistic.
//
// If this plugin simulation is run with a uniform distribution and blinking rate of 1 then the damped
// cosine function is reproduced. The curve crosses g(r)=1 at a value equivalent to the average
// distance to the centre-of-mass of each drawn cluster, not the input cluster radius parameter (which
// is a hard upper limit on the distance to centre).
final int maskSize = lowResolutionImageSize;
int[] mask = null;
// scale is in nm/pixel
maskScale = width / maskSize;
ArrayList<double[]> clusterCentres = new ArrayList<double[]>();
int totalSteps = 1 + (int) Math.ceil(nMolecules / clusterNumber);
if (clusterSimulation == 2 || clusterSimulation == 3) {
// Clusters are non-overlapping circles
// Ensure the circles do not overlap by using an exclusion mask that accumulates
// out-of-bounds pixels by drawing the last cluster (plus some border) on an image. When no
// more pixels are available then stop generating molecules.
// This is done by cumulatively filling a mask and using the MaskDistribution to select
// a new point. This may be slow but it works.
// TODO - Allow clusters of different sizes...
mask = new int[maskSize * maskSize];
Arrays.fill(mask, 255);
MaskDistribution maskDistribution = new MaskDistribution(mask, maskSize, maskSize, 0, maskScale, maskScale, randomGenerator);
double[] centre;
IJ.showStatus("Computing clusters mask");
int roiRadius = (int) Math.round((clusterRadius * 2) / maskScale);
if (clusterSimulation == 3) {
// Generate a mask of circles then sample from that.
// If we want to fill the mask completely then adjust the total steps to be the number of
// circles that can fit inside the mask.
totalSteps = (int) (maskSize * maskSize / (Math.PI * Math.pow(clusterRadius / maskScale, 2)));
}
while ((centre = maskDistribution.next()) != null && clusterCentres.size() < totalSteps) {
IJ.showProgress(clusterCentres.size(), totalSteps);
// The mask returns the coordinates with the centre of the image at 0,0
centre[0] += width / 2;
centre[1] += width / 2;
clusterCentres.add(centre);
// Fill in the mask around the centre to exclude any more circles that could overlap
double cx = centre[0] / maskScale;
double cy = centre[1] / maskScale;
fillMask(mask, maskSize, (int) cx, (int) cy, roiRadius, 0);
//Utils.display("Mask", new ColorProcessor(maskSize, maskSize, mask));
try {
maskDistribution = new MaskDistribution(mask, maskSize, maskSize, 0, maskScale, maskScale, randomGenerator);
} catch (IllegalArgumentException e) {
// This can happen when there are no more non-zero pixels
log("WARNING: No more room for clusters on the mask area (created %d of estimated %d)", clusterCentres.size(), totalSteps);
break;
}
}
IJ.showProgress(1);
IJ.showStatus("");
} else {
// Pick centres randomly from the distribution
while (clusterCentres.size() < totalSteps) clusterCentres.add(dist.next());
}
if (showClusterMask || clusterSimulation == 3) {
// Show the mask for the clusters
if (mask == null)
mask = new int[maskSize * maskSize];
else
Arrays.fill(mask, 0);
int roiRadius = (int) Math.round((clusterRadius) / maskScale);
for (double[] c : clusterCentres) {
double cx = c[0] / maskScale;
double cy = c[1] / maskScale;
fillMask(mask, maskSize, (int) cx, (int) cy, roiRadius, 1);
}
if (clusterSimulation == 3) {
// We have the mask. Now pick points at random from the mask.
MaskDistribution maskDistribution = new MaskDistribution(mask, maskSize, maskSize, 0, maskScale, maskScale, randomGenerator);
// Allocate each molecule position to a parent circle so defining clusters.
int[][] clusters = new int[clusterCentres.size()][];
int[] clusterSize = new int[clusters.length];
for (int i = 0; i < nMolecules; i++) {
double[] centre = maskDistribution.next();
// The mask returns the coordinates with the centre of the image at 0,0
centre[0] += width / 2;
centre[1] += width / 2;
xyz.add(centre);
// Output statistics on cluster size and number.
// TODO - Finding the closest cluster could be done better than an all-vs-all comparison
double max = distance2(centre, clusterCentres.get(0));
int cluster = 0;
for (int j = 1; j < clusterCentres.size(); j++) {
double d2 = distance2(centre, clusterCentres.get(j));
if (d2 < max) {
max = d2;
cluster = j;
}
}
// Assign point i to cluster
centre[2] = cluster;
if (clusterSize[cluster] == 0) {
clusters[cluster] = new int[10];
}
if (clusters[cluster].length <= clusterSize[cluster]) {
clusters[cluster] = Arrays.copyOf(clusters[cluster], (int) (clusters[cluster].length * 1.5));
}
clusters[cluster][clusterSize[cluster]++] = i;
}
// Generate real cluster size statistics
for (int j = 0; j < clusterSize.length; j++) {
final int size = clusterSize[j];
if (size == 0)
continue;
statsSize.add(size);
if (size == 1) {
statsRadius.add(0);
continue;
}
// Find centre of cluster and add the distance to each point
double[] com = new double[2];
for (int n = 0; n < size; n++) {
double[] xy = xyz.get(clusters[j][n]);
for (int k = 0; k < 2; k++) com[k] += xy[k];
}
for (int k = 0; k < 2; k++) com[k] /= size;
for (int n = 0; n < size; n++) {
double dx = xyz.get(clusters[j][n])[0] - com[0];
double dy = xyz.get(clusters[j][n])[1] - com[1];
statsRadius.add(Math.sqrt(dx * dx + dy * dy));
}
}
}
if (showClusterMask) {
bp = new ByteProcessor(maskSize, maskSize);
for (int i = 0; i < mask.length; i++) if (mask[i] != 0)
bp.set(i, 128);
Utils.display(maskTitle, bp);
}
}
// Use the simulated cluster centres to create clusters of the desired size
if (clusterSimulation == 1 || clusterSimulation == 2) {
for (double[] clusterCentre : clusterCentres) {
int clusterN = (int) Math.round((clusterNumberSD > 0) ? dataGenerator.nextGaussian(clusterNumber, clusterNumberSD) : clusterNumber);
if (clusterN < 1)
continue;
//double[] clusterCentre = dist.next();
if (clusterN == 1) {
// No need for a cluster around a point
xyz.add(clusterCentre);
statsRadius.add(0);
statsSize.add(1);
} else {
// Generate N random points within a circle of the chosen cluster radius.
// Locate the centre-of-mass and the average distance to the centre.
double[] com = new double[3];
int j = 0;
while (j < clusterN) {
// Generate a random point within a circle uniformly
// http://stackoverflow.com/questions/5837572/generate-a-random-point-within-a-circle-uniformly
double t = 2.0 * Math.PI * randomGenerator.nextDouble();
double u = randomGenerator.nextDouble() + randomGenerator.nextDouble();
double r = clusterRadius * ((u > 1) ? 2 - u : u);
double x = r * Math.cos(t);
double y = r * Math.sin(t);
double[] xy = new double[] { clusterCentre[0] + x, clusterCentre[1] + y };
xyz.add(xy);
for (int k = 0; k < 2; k++) com[k] += xy[k];
j++;
}
// Add the distance of the points from the centre of the cluster.
// Note this does not account for the movement due to precision.
statsSize.add(j);
if (j == 1) {
statsRadius.add(0);
} else {
for (int k = 0; k < 2; k++) com[k] /= j;
while (j > 0) {
double dx = xyz.get(xyz.size() - j)[0] - com[0];
double dy = xyz.get(xyz.size() - j)[1] - com[1];
statsRadius.add(Math.sqrt(dx * dx + dy * dy));
j--;
}
}
}
}
}
} else {
// Random distribution
for (int i = 0; i < nMolecules; i++) xyz.add(dist.next());
}
// The Gaussian sigma should be applied so the overall distance from the centre
// ( sqrt(x^2+y^2) ) has a standard deviation of sigmaS?
final double sigma1D = sigmaS / Math.sqrt(2);
// Show optional histograms
StoredDataStatistics intraDistances = null;
StoredData blinks = null;
if (showHistograms) {
int capacity = (int) (xyz.size() * blinkingRate);
intraDistances = new StoredDataStatistics(capacity);
blinks = new StoredData(capacity);
}
Statistics statsSigma = new Statistics();
for (int i = 0; i < xyz.size(); i++) {
int nOccurrences = getBlinks(dataGenerator, blinkingRate);
if (showHistograms)
blinks.add(nOccurrences);
final int size = molecules.size();
// Get coordinates in nm
final double[] moleculeXyz = xyz.get(i);
if (bp != null && nOccurrences > 0) {
bp.putPixel((int) Math.round(moleculeXyz[0] / maskScale), (int) Math.round(moleculeXyz[1] / maskScale), 255);
}
while (nOccurrences-- > 0) {
final double[] localisationXy = Arrays.copyOf(moleculeXyz, 2);
// Add random precision
if (sigma1D > 0) {
final double dx = dataGenerator.nextGaussian(0, sigma1D);
final double dy = dataGenerator.nextGaussian(0, sigma1D);
localisationXy[0] += dx;
localisationXy[1] += dy;
if (!dist.isWithinXY(localisationXy))
continue;
// Calculate mean-squared displacement
statsSigma.add(dx * dx + dy * dy);
}
final double x = localisationXy[0];
final double y = localisationXy[1];
molecules.add(new Molecule(x, y, i, 1));
// Store in pixels
float[] params = new float[7];
params[Gaussian2DFunction.X_POSITION] = (float) (x / nmPerPixel);
params[Gaussian2DFunction.Y_POSITION] = (float) (y / nmPerPixel);
results.addf(i + 1, (int) x, (int) y, 0, 0, 0, params, null);
}
if (molecules.size() > size) {
count++;
if (showHistograms) {
int newCount = molecules.size() - size;
if (newCount == 1) {
//intraDistances.add(0);
continue;
}
// Get the distance matrix between these molecules
double[][] matrix = new double[newCount][newCount];
for (int ii = size, x = 0; ii < molecules.size(); ii++, x++) {
for (int jj = size + 1, y = 1; jj < molecules.size(); jj++, y++) {
final double d2 = molecules.get(ii).distance2(molecules.get(jj));
matrix[x][y] = matrix[y][x] = d2;
}
}
// Get the maximum distance for particle linkage clustering of this molecule
double max = 0;
for (int x = 0; x < newCount; x++) {
// Compare to all-other molecules and get the minimum distance
// needed to join at least one
double linkDistance = Double.POSITIVE_INFINITY;
for (int y = 0; y < newCount; y++) {
if (x == y)
continue;
if (matrix[x][y] < linkDistance)
linkDistance = matrix[x][y];
}
// Check if this is larger
if (max < linkDistance)
max = linkDistance;
}
intraDistances.add(Math.sqrt(max));
}
}
}
results.end();
if (bp != null)
Utils.display(maskTitle, bp);
// Used for debugging
//System.out.printf(" * Molecules = %d (%d activated)\n", xyz.size(), count);
//if (clusterSimulation > 0)
// System.out.printf(" * Cluster number = %s +/- %s. Radius = %s +/- %s\n",
// Utils.rounded(statsSize.getMean(), 4), Utils.rounded(statsSize.getStandardDeviation(), 4),
// Utils.rounded(statsRadius.getMean(), 4), Utils.rounded(statsRadius.getStandardDeviation(), 4));
log("Simulation results");
log(" * Molecules = %d (%d activated)", xyz.size(), count);
log(" * Blinking rate = %s", Utils.rounded((double) molecules.size() / xyz.size(), 4));
log(" * Precision (Mean-displacement) = %s nm", (statsSigma.getN() > 0) ? Utils.rounded(Math.sqrt(statsSigma.getMean()), 4) : "0");
if (showHistograms) {
if (intraDistances.getN() == 0) {
log(" * Mean Intra-Molecule particle linkage distance = 0 nm");
log(" * Fraction of inter-molecule particle linkage @ 0 nm = 0 %%");
} else {
plot(blinks, "Blinks/Molecule", true);
double[][] intraHist = plot(intraDistances, "Intra-molecule particle linkage distance", false);
// Determine 95th and 99th percentile
int p99 = intraHist[0].length - 1;
double limit1 = 0.99 * intraHist[1][p99];
double limit2 = 0.95 * intraHist[1][p99];
while (intraHist[1][p99] > limit1 && p99 > 0) p99--;
int p95 = p99;
while (intraHist[1][p95] > limit2 && p95 > 0) p95--;
log(" * Mean Intra-Molecule particle linkage distance = %s nm (95%% = %s, 99%% = %s, 100%% = %s)", Utils.rounded(intraDistances.getMean(), 4), Utils.rounded(intraHist[0][p95], 4), Utils.rounded(intraHist[0][p99], 4), Utils.rounded(intraHist[0][intraHist[0].length - 1], 4));
if (distanceAnalysis) {
performDistanceAnalysis(intraHist, p99);
}
}
}
if (clusterSimulation > 0) {
log(" * Cluster number = %s +/- %s", Utils.rounded(statsSize.getMean(), 4), Utils.rounded(statsSize.getStandardDeviation(), 4));
log(" * Cluster radius = %s +/- %s nm (mean distance to centre-of-mass)", Utils.rounded(statsRadius.getMean(), 4), Utils.rounded(statsRadius.getStandardDeviation(), 4));
}
}
Aggregations