use of uk.ac.sussex.gdsc.core.ij.plugin.WindowOrganiser in project GDSC-SMLM by aherbert.
the class TcPalmAnalysis method reportAnalysis.
/**
* Report statistics on the analysis results.
*
* @param settings the settings
* @param clusters the clusters
* @param calibration the data calibration
*/
private static void reportAnalysis(TcPalmAnalysisSettings settings, LocalList<ClusterData> clusters, DataCalibration calibration) {
final WindowOrganiser wo = new WindowOrganiser();
final Consumer<HistogramPlotBuilder> action = builder -> {
/* noop. */
};
plotHistogram(settings.getShowSizeHistogram(), wo, clusters, "Size", c -> c.results.size(), null, action);
plotHistogram(settings.getShowDurationHistogram(), wo, clusters, "Duration (" + calibration.getTimeUnitName() + ")", c -> calibration.timeConverter.convert(c.getDuration()), null, action);
plotHistogram(settings.getShowAreaHistogram(), wo, clusters, "Area (" + calibration.getDistanceUnitName() + "^2)", c -> calibration.convertArea(c.getArea()), null, action);
plotHistogram(settings.getShowDensityHistogram(), wo, clusters, "Density (" + calibration.getDistanceUnitName() + "^-2)", c -> c.results.size() / calibration.convertArea(c.getArea()), Double::isFinite, action);
wo.tile();
}
use of uk.ac.sussex.gdsc.core.ij.plugin.WindowOrganiser in project GDSC-SMLM by aherbert.
the class TraceLengthAnalysis method run.
@Override
public void run(String arg) {
SmlmUsageTracker.recordPlugin(this.getClass(), arg);
if (MemoryPeakResults.isMemoryEmpty()) {
IJ.error(TITLE, "No localisations in memory");
return;
}
if (!showDialog()) {
return;
}
// Load the results
MemoryPeakResults results = ResultsManager.loadInputResults(settings.inputOption, false, null, null);
if (MemoryPeakResults.isEmpty(results)) {
IJ.error(TITLE, "No results could be loaded");
return;
}
try {
distanceConverter = results.getDistanceConverter(DistanceUnit.UM);
timeConverter = results.getTimeConverter(TimeUnit.SECOND);
} catch (final Exception ex) {
IJ.error(TITLE, "Cannot convert units to um or seconds: " + ex.getMessage());
return;
}
// Get the localisation error (4s^2) in raw units^2
double precision = 0;
try {
final PrecisionResultProcedure p = new PrecisionResultProcedure(results);
p.getPrecision();
// Precision in nm using the median
precision = new Percentile().evaluate(p.precisions, 50);
// Convert from nm to um to raw units
final double rawPrecision = distanceConverter.convertBack(precision / 1e3);
// Get the localisation error (4s^2) in units^2
error = 4 * rawPrecision * rawPrecision;
} catch (final Exception ex) {
ImageJUtils.log(TITLE + " - Unable to compute precision: " + ex.getMessage());
}
// Analyse the track lengths
results = results.copy();
results.sort(IdFramePeakResultComparator.INSTANCE);
// Ensure the first result triggers an id change
lastid = results.getFirst().getId() - 1;
results.forEach(this::processTrackLength);
// For the final track
store();
msds = msdList.toArray();
lengths = lengthList.toArray();
ids = idList.toArray();
final int[] limits = MathUtils.limits(lengths);
h1 = new int[limits[1] + 1];
h2 = new int[h1.length];
x1 = SimpleArrayUtils.newArray(h1.length, 0, 1f);
y1 = new float[x1.length];
y2 = new float[x1.length];
// Sort by MSD
final int[] indices = SimpleArrayUtils.natural(msds.length);
SortUtils.sortIndices(indices, msds, false);
final double[] msds2 = msds.clone();
final int[] lengths2 = lengths.clone();
final int[] ids2 = ids.clone();
for (int i = 0; i < indices.length; i++) {
msds[i] = msds2[indices[i]];
lengths[i] = lengths2[indices[i]];
ids[i] = ids2[indices[i]];
}
// Interactive analysis
final NonBlockingExtendedGenericDialog gd = new NonBlockingExtendedGenericDialog(TITLE);
ImageJUtils.addMessage(gd, "Split traces into fixed or moving using the track diffusion coefficient (D).\n" + "Localisation error has been subtracted from jumps (%s nm).", MathUtils.rounded(precision));
final Statistics s = Statistics.create(msds);
final double av = s.getMean();
final String msg = String.format("Average D per track = %s um^2/s", MathUtils.rounded(av));
gd.addMessage(msg);
// Histogram the diffusion coefficients
final WindowOrganiser wo = new WindowOrganiser();
final HistogramPlot histogramPlot = new HistogramPlotBuilder("Trace diffusion coefficient", StoredData.create(msds), "D (um^2/s)").setRemoveOutliersOption(1).setPlotLabel(msg).build();
histogramPlot.show(wo);
final double[] xvalues = histogramPlot.getPlotXValues();
final double min = xvalues[0];
final double max = xvalues[xvalues.length - 1];
// see if we can build a nice slider range from the histogram limits
if (max - min < 5) {
// Because sliders are used when the range is <5 and floating point
gd.addSlider("D_threshold", min, max, settings.msdThreshold);
} else {
gd.addNumericField("D_threshold", settings.msdThreshold, 2, 6, "um^2/s");
}
gd.addCheckbox("Normalise", settings.normalise);
gd.addDialogListener((gd1, event) -> {
settings.msdThreshold = gd1.getNextNumber();
settings.normalise = gd1.getNextBoolean();
update();
return true;
});
if (ImageJUtils.isShowGenericDialog()) {
draw(wo);
wo.tile();
}
gd.setOKLabel("Save datasets");
gd.setCancelLabel("Close");
gd.addHelp(HelpUrls.getUrl("trace-length-analysis"));
gd.showDialog();
if (gd.wasCanceled()) {
return;
}
// Sort by ID
final PeakResult[] list = results.toArray();
Arrays.sort(list, IdFramePeakResultComparator.INSTANCE);
createResults(results, "Fixed", 0, lastIndex, list);
createResults(results, "Moving", lastIndex, msds.length, list);
}
use of uk.ac.sussex.gdsc.core.ij.plugin.WindowOrganiser in project GDSC-SMLM by aherbert.
the class TraceMolecules method summarise.
private void summarise(Consumer<String> output, Trace[] traces, int filtered, double distanceThreshold, double timeThreshold) {
IJ.showStatus("Calculating summary ...");
final Statistics[] stats = new Statistics[NAMES.length];
for (int i = 0; i < stats.length; i++) {
stats[i] = (settings.getShowHistograms() || settings.getSaveTraceData()) ? new StoredDataStatistics() : new Statistics();
}
int singles = 0;
for (final Trace trace : traces) {
final int nBlinks = trace.getBlinks() - 1;
stats[BLINKS].add(nBlinks);
final int[] onTimes = trace.getOnTimes();
final int[] offTimes = trace.getOffTimes();
double timeOn = 0;
for (final int t : onTimes) {
stats[T_ON].add(t * exposureTime);
timeOn += t * exposureTime;
}
stats[TOTAL_T_ON].add(timeOn);
if (offTimes != null) {
double timeOff = 0;
for (final int t : offTimes) {
stats[T_OFF].add(t * exposureTime);
timeOff += t * exposureTime;
}
stats[TOTAL_T_OFF].add(timeOff);
}
final double signal = trace.getSignal() / results.getGain();
stats[TOTAL_SIGNAL].add(signal);
stats[SIGNAL_PER_FRAME].add(signal / trace.size());
stats[DWELL_TIME].add((trace.getTail().getEndFrame() - trace.getHead().getFrame() + 1) * exposureTime);
if (trace.size() == 1) {
singles++;
}
}
// Add to the summary table
final StringBuilder sb = new StringBuilder();
sb.append(results.getName()).append('\t');
sb.append(outputName.equals("Cluster") ? getClusteringAlgorithm(settings.getClusteringAlgorithm()) : getTraceMode(settings.getTraceMode())).append('\t');
sb.append(MathUtils.rounded(getExposureTimeInMilliSeconds(), 3)).append('\t');
sb.append(MathUtils.rounded(distanceThreshold, 3)).append('\t');
sb.append(MathUtils.rounded(timeThreshold, 3));
if (settings.getSplitPulses()) {
sb.append(" *");
}
sb.append('\t');
sb.append(convertSecondsToFrames(timeThreshold)).append('\t');
sb.append(traces.length).append('\t');
sb.append(filtered).append('\t');
sb.append(singles).append('\t');
sb.append(traces.length - singles).append('\t');
for (int i = 0; i < stats.length; i++) {
sb.append(MathUtils.rounded(stats[i].getMean(), 3)).append('\t');
}
if (java.awt.GraphicsEnvironment.isHeadless()) {
IJ.log(sb.toString());
return;
}
output.accept(sb.toString());
if (settings.getShowHistograms()) {
IJ.showStatus("Calculating histograms ...");
final WindowOrganiser windowOrganiser = new WindowOrganiser();
final HistogramPlotBuilder builder = new HistogramPlotBuilder(pluginTitle).setNumberOfBins(settings.getHistogramBins());
for (int i = 0; i < NAMES.length; i++) {
if (pluginSettings.displayHistograms[i]) {
builder.setData((StoredDataStatistics) stats[i]).setName(NAMES[i]).setIntegerBins(integerDisplay[i]).setRemoveOutliersOption((settings.getRemoveOutliers() || alwaysRemoveOutliers[i]) ? 2 : 0).show(windowOrganiser);
}
}
windowOrganiser.tile();
}
if (settings.getSaveTraceData()) {
saveTraceData(stats);
}
IJ.showStatus("");
}
use of uk.ac.sussex.gdsc.core.ij.plugin.WindowOrganiser in project GDSC-SMLM by aherbert.
the class SpotAnalysis method showSpot.
private static ImagePlus showSpot(String title, ImageStack spot) {
final WindowOrganiser windowOrganiser = new WindowOrganiser();
final ImagePlus imp = ImageJUtils.display(title, spot, windowOrganiser);
if (windowOrganiser.isNotEmpty() || imp.getWindow().getCanvas().getMagnification() == 1) {
for (int i = 9; i-- > 0; ) {
imp.getWindow().getCanvas().zoomIn(imp.getWidth() / 2, imp.getHeight() / 2);
}
}
return imp;
}
use of uk.ac.sussex.gdsc.core.ij.plugin.WindowOrganiser in project GDSC-SMLM by aherbert.
the class TrackPopulationAnalysis method run.
@Override
public void run(String arg) {
SmlmUsageTracker.recordPlugin(this.getClass(), arg);
if (MemoryPeakResults.isMemoryEmpty()) {
IJ.error(TITLE, "No localisations in memory");
return;
}
settings = Settings.load();
// Saved by reference so just save now
settings.save();
// Read in multiple traced datasets
// All datasets must have the same pixel pitch and exposure time
// Get parameters
// Convert datasets to tracks
// For each track compute the 4 local track features using the configured window
//
// Optional:
// Fit a multi-variate Gaussian mixture model to the data
// (using the configured number of components/populations)
// Assign each point in the track using the model.
// Smooth the assignments.
//
// The alternative is to use the localisation category to assign populations.
//
// Plot histograms of each track parameter, coloured by component
final List<MemoryPeakResults> combinedResults = new LocalList<>();
if (!showInputDialog(combinedResults)) {
return;
}
final boolean hasCategory = showHasCategoryDialog(combinedResults);
if (!showDialog(hasCategory)) {
return;
}
ImageJUtils.log(TITLE + "...");
final List<Trace> tracks = getTracks(combinedResults, settings.window, settings.minTrackLength);
if (tracks.isEmpty()) {
IJ.error(TITLE, "No tracks. Please check the input data and min track length setting.");
return;
}
final Calibration cal = combinedResults.get(0).getCalibration();
final CalibrationReader cr = new CalibrationReader(cal);
// Use micrometer / second
final TypeConverter<DistanceUnit> distanceConverter = cr.getDistanceConverter(DistanceUnit.UM);
final double exposureTime = cr.getExposureTime() / 1000.0;
final Pair<int[], double[][]> trackData = extractTrackData(tracks, distanceConverter, exposureTime, hasCategory);
final double[][] data = trackData.getValue();
// Histogram the raw data.
final Array2DRowRealMatrix raw = new Array2DRowRealMatrix(data, false);
final WindowOrganiser wo = new WindowOrganiser();
// Store the histogram data for plotting the components
final double[][] columns = new double[FEATURE_NAMES.length][];
final double[][] limits = new double[FEATURE_NAMES.length][];
// Get column data
for (int i = 0; i < FEATURE_NAMES.length; i++) {
columns[i] = raw.getColumn(i);
if (i == FEATURE_D) {
// Plot using a logarithmic scale
SimpleArrayUtils.apply(columns[i], Math::log10);
}
limits[i] = MathUtils.limits(columns[i]);
}
// Compute histogram bins
final int[] bins = new int[FEATURE_NAMES.length];
if (settings.histogramBins > 0) {
Arrays.fill(bins, settings.histogramBins);
} else {
for (int i = 0; i < FEATURE_NAMES.length; i++) {
bins[i] = HistogramPlot.getBins(StoredData.create(columns[i]), BinMethod.FD);
}
// Use the maximum so all histograms look the same
Arrays.fill(bins, MathUtils.max(bins));
}
// Compute plots
final Plot[] plots = new Plot[FEATURE_NAMES.length];
for (int i = 0; i < FEATURE_NAMES.length; i++) {
final double[][] hist = HistogramPlot.calcHistogram(columns[i], limits[i][0], limits[i][1], bins[i]);
plots[i] = new Plot(TITLE + " " + FEATURE_NAMES[i], getFeatureLabel(i, i == FEATURE_D), "Frequency");
plots[i].addPoints(hist[0], hist[1], Plot.BAR);
ImageJUtils.display(plots[i].getTitle(), plots[i], ImageJUtils.NO_TO_FRONT, wo);
}
wo.tile();
// The component for each data point
int[] component;
// The number of components
int numComponents;
// Data used to fit the Gaussian mixture model
double[][] fitData;
// The fitted model
MixtureMultivariateGaussianDistribution model;
if (hasCategory) {
// Use the category as the component.
// No fit data and no output model
fitData = null;
model = null;
// The component is stored at the end of the raw track data.
final int end = data[0].length - 1;
component = Arrays.stream(data).mapToInt(d -> (int) d[end]).toArray();
numComponents = MathUtils.max(component) + 1;
// In the EM algorithm the probability of each data point is computed and normalised to
// sum to 1. The normalised probabilities are averaged to create the weights.
// Note the probability of each data point uses the previous weight and the algorithm
// iterates.
// This is not a fitted model but the input model so use
// zero weights to indicate no fitting was performed.
final double[] weights = new double[numComponents];
// Remove the trailing component to show the 'model' in a table.
createModelTable(Arrays.stream(data).map(d -> Arrays.copyOf(d, end)).toArray(double[][]::new), weights, component);
} else {
// Multivariate Gaussian mixture EM
// Provide option to not use the anomalous exponent in the population mix.
int sortDimension = SORT_DIMENSION;
if (settings.ignoreAlpha) {
// Remove index 0. This shifts the sort dimension.
sortDimension--;
fitData = Arrays.stream(data).map(d -> Arrays.copyOfRange(d, 1, d.length)).toArray(double[][]::new);
} else {
fitData = SimpleArrayUtils.deepCopy(data);
}
final MultivariateGaussianMixtureExpectationMaximization mixed = fitGaussianMixture(fitData, sortDimension);
if (mixed == null) {
IJ.error(TITLE, "Failed to fit a mixture model");
return;
}
model = sortComponents(mixed.getFittedModel(), sortDimension);
// For the best model, assign to the most likely population.
component = assignData(fitData, model);
// Table of the final model using the original data (i.e. not normalised)
final double[] weights = model.getWeights();
numComponents = weights.length;
createModelTable(data, weights, component);
}
// Output coloured histograms of the populations.
final LUT lut = LutHelper.createLut(settings.lutIndex);
IntFunction<Color> colourMap;
if (LutHelper.getColour(lut, 0).equals(Color.BLACK)) {
colourMap = i -> LutHelper.getNonZeroColour(lut, i, 0, numComponents - 1);
} else {
colourMap = i -> LutHelper.getColour(lut, i, 0, numComponents - 1);
}
for (int i = 0; i < FEATURE_NAMES.length; i++) {
// Extract the data for each component
final double[] col = columns[i];
final Plot plot = plots[i];
for (int n = 0; n < numComponents; n++) {
final StoredData feature = new StoredData();
for (int j = 0; j < component.length; j++) {
if (component[j] == n) {
feature.add(col[j]);
}
}
if (feature.size() == 0) {
continue;
}
final double[][] hist = HistogramPlot.calcHistogram(feature.values(), limits[i][0], limits[i][1], bins[i]);
// Colour the points
plot.setColor(colourMap.apply(n));
plot.addPoints(hist[0], hist[1], Plot.BAR);
}
plot.updateImage();
}
createTrackDataTable(tracks, trackData, fitData, model, component, cal, colourMap);
// Analysis.
// Assign the original localisations to their track component.
// Q. What about the start/end not covered by the window?
// Save tracks as a dataset labelled with the sub-track ID?
// Output for the bound component and free components track parameters.
// Compute dwell times.
// Other ...
// Track analysis plugin:
// Extract all continuous segments of the same component.
// Produce MSD plot with error bars.
// Fit using FBM model.
}
Aggregations