use of uk.ac.sussex.gdsc.core.ij.plugin.WindowOrganiser in project GDSC-SMLM by aherbert.
the class CreateData method showSummary.
private double showSummary(List<? extends FluorophoreSequenceModel> fluorophores, List<LocalisationModel> localisations) {
IJ.showStatus("Calculating statistics ...");
final Statistics[] stats = new Statistics[NAMES.length];
for (int i = 0; i < stats.length; i++) {
stats[i] = (settings.getShowHistograms() || alwaysRemoveOutliers[i]) ? new StoredDataStatistics() : new Statistics();
}
// Find the largest timepoint
final ImagePlus outputImp = WindowManager.getImage(benchmarkImageId);
int frameCount;
if (outputImp == null) {
sortLocalisationsByTime(localisations);
frameCount = localisations.get(localisations.size() - 1).getTime();
} else {
frameCount = outputImp.getStackSize();
}
final int[] countHistogram = new int[frameCount + 1];
// Use the localisations that were drawn to create the sampled on/off times
rebuildNeighbours(localisations);
// Assume that there is at least one localisation
final LocalisationModel first = localisations.get(0);
// The current localisation
int currentId = first.getId();
// The last time this localisation was on
int lastT = first.getTime();
// Number of blinks
int blinks = 0;
// On-time of current pulse
int currentT = 0;
double signal = 0;
final double centreOffset = settings.getSize() * 0.5;
// Used to convert the sampled times in frames into seconds
final double framesPerSecond = 1000.0 / settings.getExposureTime();
// final double gain = new CreateDataSettingsHelper(settings).getTotalGainSafe();
for (final LocalisationModel l : localisations) {
final double[] data = l.getData();
if (data == null) {
throw new IllegalStateException("No localisation data. This should not happen!");
}
final double noise = data[1];
final double sx = data[2];
final double sy = data[3];
final double intensityInPhotons = data[4];
// Q. What if the noise is zero, i.e. no background photon / read noise?
// Just ignore it at current. This is only an approximation to the SNR estimate
// if this is not a Gaussian spot.
final double snr = Gaussian2DPeakResultHelper.getMeanSignalUsingP05(intensityInPhotons, sx, sy) / noise;
stats[SIGNAL].add(intensityInPhotons);
stats[NOISE].add(noise);
if (noise != 0) {
stats[SNR].add(snr);
}
// if (l.isContinuous())
if (l.getNext() != null && l.getPrevious() != null) {
stats[SIGNAL_CONTINUOUS].add(intensityInPhotons);
if (noise != 0) {
stats[SNR_CONTINUOUS].add(snr);
}
}
final int id = l.getId();
// Check if this a new fluorophore
if (currentId != id) {
// Add previous fluorophore
stats[SAMPLED_BLINKS].add(blinks);
stats[SAMPLED_T_ON].add(currentT / framesPerSecond);
stats[TOTAL_SIGNAL].add(signal);
// Reset
blinks = 0;
currentT = 1;
currentId = id;
signal = intensityInPhotons;
} else {
signal += intensityInPhotons;
// Check if the current fluorophore pulse is broken (i.e. a blink)
if (l.getTime() - 1 > lastT) {
blinks++;
stats[SAMPLED_T_ON].add(currentT / framesPerSecond);
currentT = 1;
stats[SAMPLED_T_OFF].add(((l.getTime() - 1) - lastT) / framesPerSecond);
} else {
// Continuous on-time
currentT++;
}
}
lastT = l.getTime();
countHistogram[lastT]++;
stats[X].add((l.getX() - centreOffset) * settings.getPixelPitch());
stats[Y].add((l.getY() - centreOffset) * settings.getPixelPitch());
stats[Z].add(l.getZ() * settings.getPixelPitch());
}
// Final fluorophore
stats[SAMPLED_BLINKS].add(blinks);
stats[SAMPLED_T_ON].add(currentT / framesPerSecond);
stats[TOTAL_SIGNAL].add(signal);
// Samples per frame
for (int t = 1; t < countHistogram.length; t++) {
stats[SAMPLES].add(countHistogram[t]);
}
if (fluorophores != null) {
for (final FluorophoreSequenceModel f : fluorophores) {
stats[BLINKS].add(f.getNumberOfBlinks());
// On-time
for (final double t : f.getOnTimes()) {
stats[T_ON].add(t);
}
// Off-time
for (final double t : f.getOffTimes()) {
stats[T_OFF].add(t);
}
}
} else {
// show no blinks
stats[BLINKS].add(0);
stats[T_ON].add(1);
}
if (results != null) {
// Convert depth-of-field to pixels
final double depth = settings.getDepthOfField() / settings.getPixelPitch();
try {
// Get widths
final WidthResultProcedure wp = new WidthResultProcedure(results, DistanceUnit.PIXEL);
wp.getW();
stats[WIDTH].add(wp.wx);
} catch (final DataException ex) {
ImageJUtils.log("Unable to compute width: " + ex.getMessage());
}
try {
// Get z depth
final StandardResultProcedure sp = new StandardResultProcedure(results, DistanceUnit.PIXEL);
sp.getXyz();
// Get precision
final PrecisionResultProcedure pp = new PrecisionResultProcedure(results);
pp.getPrecision();
stats[PRECISION].add(pp.precisions);
for (int i = 0; i < pp.size(); i++) {
if (Math.abs(sp.z[i]) < depth) {
stats[PRECISION_IN_FOCUS].add(pp.precisions[i]);
}
}
} catch (final DataException ex) {
ImageJUtils.log("Unable to compute LSE precision: " + ex.getMessage());
}
// Compute density per frame. Multi-thread for speed
if (settings.getDensityRadius() > 0) {
final int threadCount = Prefs.getThreads();
final Ticker ticker = ImageJUtils.createTicker(results.getLastFrame(), threadCount, "Calculating density ...");
final ExecutorService threadPool = Executors.newFixedThreadPool(threadCount);
final List<Future<?>> futures = new LinkedList<>();
final TFloatArrayList coordsX = new TFloatArrayList();
final TFloatArrayList coordsY = new TFloatArrayList();
final Statistics densityStats = stats[DENSITY];
final float radius = (float) (settings.getDensityRadius() * getHwhm());
final Rectangle bounds = results.getBounds();
final double area = (double) bounds.width * bounds.height;
// Store the density for each result.
final int[] allDensity = new int[results.size()];
final FrameCounter counter = results.newFrameCounter();
results.forEach((PeakResultProcedure) result -> {
if (counter.advance(result.getFrame())) {
counter.increment(runDensityCalculation(threadPool, futures, coordsX, coordsY, densityStats, radius, area, allDensity, counter.getCount(), ticker));
}
coordsX.add(result.getXPosition());
coordsY.add(result.getYPosition());
});
runDensityCalculation(threadPool, futures, coordsX, coordsY, densityStats, radius, area, allDensity, counter.getCount(), ticker);
ConcurrencyUtils.waitForCompletionUnchecked(futures);
threadPool.shutdown();
ImageJUtils.finished();
// Split results into singles (density = 0) and clustered (density > 0)
final MemoryPeakResults singles = copyMemoryPeakResults("No Density");
final MemoryPeakResults clustered = copyMemoryPeakResults("Density");
counter.reset();
results.forEach((PeakResultProcedure) result -> {
final int density = allDensity[counter.getAndIncrement()];
result.setOrigValue(density);
if (density == 0) {
singles.add(result);
} else {
clustered.add(result);
}
});
}
}
final StringBuilder sb = new StringBuilder();
sb.append(datasetNumber).append('\t');
if (settings.getCameraType() == CameraType.SCMOS) {
sb.append("sCMOS (").append(settings.getCameraModelName()).append(") ");
final Rectangle bounds = cameraModel.getBounds();
sb.append(" ").append(bounds.x).append(",").append(bounds.y);
final int size = settings.getSize();
sb.append(" ").append(size).append("x").append(size);
} else if (CalibrationProtosHelper.isCcdCameraType(settings.getCameraType())) {
sb.append(CalibrationProtosHelper.getName(settings.getCameraType()));
final int size = settings.getSize();
sb.append(" ").append(size).append("x").append(size);
if (settings.getCameraType() == CameraType.EMCCD) {
sb.append(" EM=").append(settings.getEmGain());
}
sb.append(" CG=").append(settings.getCameraGain());
sb.append(" RN=").append(settings.getReadNoise());
sb.append(" B=").append(settings.getBias());
} else {
throw new IllegalStateException();
}
sb.append(" QE=").append(settings.getQuantumEfficiency()).append('\t');
sb.append(settings.getPsfModel());
if (psfModelType == PSF_MODEL_IMAGE) {
sb.append(" Image").append(settings.getPsfImageName());
} else if (psfModelType == PSF_MODEL_ASTIGMATISM) {
sb.append(" model=").append(settings.getAstigmatismModel());
} else {
sb.append(" DoF=").append(MathUtils.rounded(settings.getDepthOfFocus()));
if (settings.getEnterWidth()) {
sb.append(" SD=").append(MathUtils.rounded(settings.getPsfSd()));
} else {
sb.append(" λ=").append(MathUtils.rounded(settings.getWavelength()));
sb.append(" NA=").append(MathUtils.rounded(settings.getNumericalAperture()));
}
}
sb.append('\t');
sb.append((fluorophores == null) ? localisations.size() : fluorophores.size()).append('\t');
sb.append(stats[SAMPLED_BLINKS].getN() + (int) stats[SAMPLED_BLINKS].getSum()).append('\t');
sb.append(localisations.size()).append('\t');
sb.append(frameCount).append('\t');
sb.append(MathUtils.rounded(areaInUm)).append('\t');
sb.append(MathUtils.rounded(localisations.size() / (areaInUm * frameCount), 4)).append('\t');
sb.append(MathUtils.rounded(getHwhm(), 4)).append('\t');
double sd = getPsfSd();
sb.append(MathUtils.rounded(sd, 4)).append('\t');
sd *= settings.getPixelPitch();
final double sa = PsfCalculator.squarePixelAdjustment(sd, settings.getPixelPitch()) / settings.getPixelPitch();
sb.append(MathUtils.rounded(sa, 4)).append('\t');
// Width not valid for the Image PSF.
// Q. Is this true? We can approximate the FHWM for a spot-like image PSF.
final int nStats = (psfModelType == PSF_MODEL_IMAGE) ? stats.length - 1 : stats.length;
for (int i = 0; i < nStats; i++) {
final double centre = (alwaysRemoveOutliers[i]) ? ((StoredDataStatistics) stats[i]).getStatistics().getPercentile(50) : stats[i].getMean();
sb.append(MathUtils.rounded(centre, 4)).append('\t');
}
createSummaryTable().accept(sb.toString());
// Show histograms
if (settings.getShowHistograms() && !java.awt.GraphicsEnvironment.isHeadless()) {
IJ.showStatus("Calculating histograms ...");
final boolean[] chosenHistograms = getChoosenHistograms();
final WindowOrganiser wo = new WindowOrganiser();
final HistogramPlotBuilder builder = new HistogramPlotBuilder(TITLE);
for (int i = 0; i < NAMES.length; i++) {
if (chosenHistograms[i]) {
builder.setData((StoredDataStatistics) stats[i]).setName(NAMES[i]).setIntegerBins(integerDisplay[i]).setRemoveOutliersOption((settings.getRemoveOutliers() || alwaysRemoveOutliers[i]) ? 2 : 0).setNumberOfBins(settings.getHistogramBins()).show(wo);
}
}
wo.tile();
}
IJ.showStatus("");
return stats[SIGNAL].getMean();
}
use of uk.ac.sussex.gdsc.core.ij.plugin.WindowOrganiser in project GDSC-SMLM by aherbert.
the class BenchmarkSpotFilter method run.
@Override
public void run(String arg) {
SmlmUsageTracker.recordPlugin(this.getClass(), arg);
extraOptions = ImageJUtils.isExtraOptions();
batchMode = "batch".equals(arg);
simulationParameters = CreateData.getSimulationParameters();
if (simulationParameters == null) {
IJ.error(TITLE, "No benchmark spot parameters in memory");
return;
}
imp = CreateData.getImage();
if (imp == null) {
IJ.error(TITLE, "No benchmark image");
return;
}
results = CreateData.getResults();
if (results == null) {
IJ.error(TITLE, "No benchmark results in memory");
return;
}
// Set-up for the simulation
try {
if (results.getCalibration() == null) {
throw new ConfigurationException("Require calibrated results");
}
if (results.getCalibrationReader().getDistanceUnit() != DistanceUnit.PIXEL) {
throw new ConfigurationException("Require results in pixel distance units");
}
if (results.getCalibrationReader().getIntensityUnit() != IntensityUnit.PHOTON) {
throw new ConfigurationException("Require results in photon units");
}
// This plugin is heavily reliant on the results being represented as a
// Gaussian2D function.
final int flags = Gaussian2DPeakResultHelper.AMPLITUDE | Gaussian2DPeakResultHelper.PIXEL_AMPLITUDE;
calculator = Gaussian2DPeakResultHelper.create(results.getPsf(), results.getCalibration(), flags);
cameraModel = CreateData.getCameraModel(simulationParameters);
} catch (final ConfigurationException ex) {
IJ.error(TITLE, "Bad configuration: " + ex.getMessage());
return;
}
if (!showDialog()) {
return;
}
// Get the simulation results into a list per frame
simulationCoords = getSimulationCoordinates();
// Clear old results to free memory
BenchmarkSpotFilterResult localFilterResult;
filterResult.set(null);
// For graphs
windowOrganiser = new WindowOrganiser();
if (batchMode) {
// Clear the cached results if the setting changed
final SettingsList settingList = new SettingsList(simulationParameters.id, settings.filterRelativeDistances, // search, maxSearch, // Ignore search distance for smart caching
settings.border, settings.scoreRelativeDistances, settings.analysisBorder, settings.hardBorder, settings.matchingMethod, settings.upperDistance, settings.lowerDistance, settings.upperSignalFactor, settings.lowerSignalFactor, settings.recallFraction);
final ArrayList<BatchResult[]> cachedResults = getCachedBatchResults(settingList);
// Batch mode to test enumeration of filters
final double sd = simulationParameters.sd / simulationParameters.pixelPitch;
final int limit = (int) Math.floor(3 * sd);
// This should be in integers otherwise we may repeat search box sizes
final int[] searchParam = SimpleArrayUtils.newArray(settings.maxSearch - settings.minSearch + 1, settings.minSearch, 1);
// Continuous parameters
final double[] pEmpty = new double[0];
final double[] mParam = (settings.batchMean) ? getRange(limit, 0.05) : pEmpty;
final double[] gParam = (settings.batchGaussian) ? getRange(limit, 0.05) : pEmpty;
// Less continuous parameters
final double[] cParam = (settings.batchCircular) ? getRange(limit, 0.5) : pEmpty;
// Discrete parameters
final double[] medParam = (settings.batchMedian) ? getRange(limit, 1) : pEmpty;
setupProgress((long) imp.getImageStackSize() * searchParam.length * (mParam.length + gParam.length + cParam.length + medParam.length), "Frame");
ArrayList<BatchResult[]> batchResults = new ArrayList<>(cachedResults.size());
double param2 = 0;
if (settings.differenceFilter && settings.differenceSmooth > 0) {
if (settings.filterRelativeDistances) {
// Convert to absolute for batch run
param2 = MathUtils.roundUsingDecimalPlaces(settings.differenceSmooth * config.getHwhmMin(), 3);
} else {
// Already an absolute value
param2 = settings.differenceSmooth;
}
config.setDataFilterType(DataFilterType.DIFFERENCE);
} else {
config.setDataFilterType(DataFilterType.SINGLE);
}
for (final int search : searchParam) {
// Batch runs use absolute distance
config.setSearch(search, true);
// Allow re-use of these if they are cached to allow quick reanalysis of results.
if (settings.batchMean) {
batchResults.add(getOrCompute(cachedResults, DataFilterMethod.MEAN, mParam, search, param2));
}
if (settings.batchGaussian) {
batchResults.add(getOrCompute(cachedResults, DataFilterMethod.GAUSSIAN, gParam, search, param2));
}
if (settings.batchCircular) {
batchResults.add(getOrCompute(cachedResults, DataFilterMethod.CIRCULAR_MEAN, cParam, search, param2));
}
if (settings.batchMean) {
batchResults.add(getOrCompute(cachedResults, DataFilterMethod.MEDIAN, medParam, search, param2));
}
}
IJ.showProgress(-1);
IJ.showStatus("");
if (ImageJUtils.isInterrupted()) {
return;
}
// Save the results in a cache
setCachedBatchResults(settingList, cachedResults);
// Analysis options
final ExtendedGenericDialog gd = new ExtendedGenericDialog(TITLE);
final boolean haveCached = cachedResults.size() > batchResults.size();
if (haveCached) {
gd.addCheckbox("Use_cached_results", settings.useCached);
}
gd.addMessage("Choose performance plots:");
for (int i = 0; i < settings.batchPlot.length; i++) {
gd.addCheckbox(Settings.batchPlotNames[i], settings.batchPlot[i]);
}
gd.addChoice("Selection", Settings.SELECTION_METHOD, settings.selectionMethod);
gd.addCheckbox("Show_plots", settings.showPlot);
gd.addCheckbox("Plot_rank_by_intensity", settings.rankByIntensity);
gd.addCheckbox("Show_failures_plots", settings.showFailuresPlot);
gd.addCheckbox("Show_TP", settings.showTP);
gd.addCheckbox("Show_FP", settings.showFP);
gd.addCheckbox("Show_FN", settings.showFN);
gd.addHelp(HelpUrls.getUrl("filter-spot-data-batch"));
gd.showDialog();
if (gd.wasCanceled()) {
return;
}
if (haveCached) {
settings.useCached = gd.getNextBoolean();
if (settings.useCached) {
batchResults = cachedResults;
}
}
for (int i = 0; i < settings.batchPlot.length; i++) {
settings.batchPlot[i] = gd.getNextBoolean();
}
settings.selectionMethod = gd.getNextChoiceIndex();
settings.showPlot = gd.getNextBoolean();
settings.rankByIntensity = gd.getNextBoolean();
settings.showFailuresPlot = gd.getNextBoolean();
settings.showTP = gd.getNextBoolean();
settings.showFP = gd.getNextBoolean();
settings.showFN = gd.getNextBoolean();
// Plot charts
for (int i = 0; i < settings.batchPlot.length; i++) {
plot(i, batchResults);
}
// Store in global singleton
localFilterResult = analyse(batchResults);
} else {
// Single filter mode
setupProgress(imp.getImageStackSize(), "Frame");
localFilterResult = runAnalysis(config);
}
ImageJUtils.clearSlowProgress();
IJ.showStatus("");
if (localFilterResult == null) {
return;
}
// Store the latest result
filterResult.set(localFilterResult);
// Debugging the matches
if (settings.debug) {
addSpotsToMemory(localFilterResult.filterResults);
}
if (settings.showFailuresPlot) {
showFailuresPlot(localFilterResult);
}
if (settings.showPlot) {
showPlot(localFilterResult);
}
if (isShowOverlay()) {
showOverlay(imp, localFilterResult);
}
windowOrganiser.tile();
}
use of uk.ac.sussex.gdsc.core.ij.plugin.WindowOrganiser in project GDSC-SMLM by aherbert.
the class SpotFinderPreview method run.
private void run(ImageProcessor ip, MaximaSpotFilter filter) {
if (refreshing) {
return;
}
currentSlice = imp.getCurrentSlice();
final Rectangle bounds = ip.getRoi();
// Crop to the ROI
FloatProcessor fp = ip.crop().toFloat(0, null);
float[] data = (float[]) fp.getPixels();
final int width = fp.getWidth();
final int height = fp.getHeight();
// Store the mean bias and gain of the region data.
// This is used to correctly overlay the filtered data on the original image.
double bias = 0;
double gain = 1;
boolean adjust = false;
// Set weights
final CameraModel cameraModel = fitConfig.getCameraModel();
if (!(cameraModel instanceof FakePerPixelCameraModel)) {
// This should be done on the normalised data
final float[] w = cameraModel.getNormalisedWeights(bounds);
filter.setWeights(w, width, height);
data = data.clone();
if (data.length < ip.getPixelCount()) {
adjust = true;
bias = MathUtils.sum(cameraModel.getBias(bounds)) / data.length;
gain = MathUtils.sum(cameraModel.getGain(bounds)) / data.length;
}
cameraModel.removeBiasAndGain(bounds, data);
}
final Spot[] spots = filter.rank(data, width, height);
data = filter.getPreprocessedData();
final int size = spots.length;
if (topNScrollBar != null) {
topNScrollBar.setMaximum(size);
selectScrollBar.setMaximum(size);
}
fp = new FloatProcessor(width, height, data);
final FloatProcessor out = new FloatProcessor(ip.getWidth(), ip.getHeight());
out.copyBits(ip, 0, 0, Blitter.COPY);
if (adjust) {
fp.multiply(gain);
fp.add(bias);
}
out.insert(fp, bounds.x, bounds.y);
final double min = fp.getMin();
final double max = fp.getMax();
out.setMinAndMax(min, max);
final Overlay o = new Overlay();
o.add(new ImageRoi(0, 0, out));
if (label != null) {
// Get results for frame
final Coordinate[] actual = ResultsMatchCalculator.getCoordinates(actualCoordinates, imp.getCurrentSlice());
final Coordinate[] predicted = new Coordinate[size];
for (int i = 0; i < size; i++) {
predicted[i] = new BasePoint(spots[i].x + bounds.x, spots[i].y + bounds.y);
}
// Compute assignments
final LocalList<FractionalAssignment> fractionalAssignments = new LocalList<>(3 * predicted.length);
final double matchDistance = settings.distance * fitConfig.getInitialPeakStdDev();
final RampedScore score = RampedScore.of(matchDistance, matchDistance * settings.lowerDistance / 100, false);
final double dmin = matchDistance * matchDistance;
final int nActual = actual.length;
final int nPredicted = predicted.length;
for (int j = 0; j < nPredicted; j++) {
// Centre in the middle of the pixel
final float x = predicted[j].getX() + 0.5f;
final float y = predicted[j].getY() + 0.5f;
// Any spots that match
for (int i = 0; i < nActual; i++) {
final double dx = (x - actual[i].getX());
final double dy = (y - actual[i].getY());
final double d2 = dx * dx + dy * dy;
if (d2 <= dmin) {
final double d = Math.sqrt(d2);
final double s = score.score(d);
if (s == 0) {
continue;
}
double distance = 1 - s;
if (distance == 0) {
// In the case of a match below the distance thresholds
// the distance will be 0. To distinguish between candidates all below
// the thresholds just take the closest.
// We know d2 is below dmin so we subtract the delta.
distance -= (dmin - d2);
}
// Store the match
fractionalAssignments.add(new ImmutableFractionalAssignment(i, j, distance, s));
}
}
}
final FractionalAssignment[] assignments = fractionalAssignments.toArray(new FractionalAssignment[0]);
// Compute matches
final RankedScoreCalculator calc = RankedScoreCalculator.create(assignments, nActual - 1, nPredicted - 1);
final boolean save = settings.showTP || settings.showFP;
final double[] calcScore = calc.score(nPredicted, settings.multipleMatches, save);
final ClassificationResult result = RankedScoreCalculator.toClassificationResult(calcScore, nActual);
// Compute AUC and max jaccard (and plot)
final double[][] curve = RankedScoreCalculator.getPrecisionRecallCurve(assignments, nActual, nPredicted);
final double[] precision = curve[0];
final double[] recall = curve[1];
final double[] jaccard = curve[2];
final double auc = AucCalculator.auc(precision, recall);
// Show scores
final String scoreLabel = String.format("Slice=%d, AUC=%s, R=%s, Max J=%s", imp.getCurrentSlice(), MathUtils.rounded(auc), MathUtils.rounded(result.getRecall()), MathUtils.rounded(MathUtils.maxDefault(0, jaccard)));
setLabel(scoreLabel);
// Plot
String title = TITLE + " Performance";
Plot plot = new Plot(title, "Spot Rank", "");
final double[] rank = SimpleArrayUtils.newArray(precision.length, 0, 1.0);
plot.setLimits(0, nPredicted, 0, 1.05);
plot.setColor(Color.blue);
plot.addPoints(rank, precision, Plot.LINE);
plot.setColor(Color.red);
plot.addPoints(rank, recall, Plot.LINE);
plot.setColor(Color.black);
plot.addPoints(rank, jaccard, Plot.LINE);
plot.setColor(Color.black);
plot.addLabel(0, 0, scoreLabel);
final WindowOrganiser windowOrganiser = new WindowOrganiser();
ImageJUtils.display(title, plot, 0, windowOrganiser);
title = TITLE + " Precision-Recall";
plot = new Plot(title, "Recall", "Precision");
plot.setLimits(0, 1, 0, 1.05);
plot.setColor(Color.red);
plot.addPoints(recall, precision, Plot.LINE);
plot.drawLine(recall[recall.length - 1], precision[recall.length - 1], recall[recall.length - 1], 0);
plot.setColor(Color.black);
plot.addLabel(0, 0, scoreLabel);
ImageJUtils.display(title, plot, 0, windowOrganiser);
windowOrganiser.tile();
// Create Rois for TP and FP
if (save) {
final double[] matchScore = RankedScoreCalculator.getMatchScore(calc.getScoredAssignments(), nPredicted);
int matches = 0;
for (int i = 0; i < matchScore.length; i++) {
if (matchScore[i] != 0) {
matches++;
}
}
if (settings.showTP) {
final float[] x = new float[matches];
final float[] y = new float[x.length];
int count = 0;
for (int i = 0; i < matchScore.length; i++) {
if (matchScore[i] != 0) {
final BasePoint p = (BasePoint) predicted[i];
x[count] = p.getX() + 0.5f;
y[count] = p.getY() + 0.5f;
count++;
}
}
addRoi(0, o, x, y, count, Color.green);
}
if (settings.showFP) {
final float[] x = new float[nPredicted - matches];
final float[] y = new float[x.length];
int count = 0;
for (int i = 0; i < matchScore.length; i++) {
if (matchScore[i] == 0) {
final BasePoint p = (BasePoint) predicted[i];
x[count] = p.getX() + 0.5f;
y[count] = p.getY() + 0.5f;
count++;
}
}
addRoi(0, o, x, y, count, Color.red);
}
}
} else {
final WindowOrganiser wo = new WindowOrganiser();
// Option to show the number of neighbours within a set pixel box radius
final int[] count = spotFilterHelper.countNeighbours(spots, width, height, settings.neighbourRadius);
// Show as histogram the totals...
new HistogramPlotBuilder(TITLE, StoredData.create(count), "Neighbours").setIntegerBins(true).setPlotLabel("Radius = " + settings.neighbourRadius).show(wo);
// TODO - Draw n=0, n=1 on the image overlay
final LUT lut = LutHelper.createLut(LutColour.FIRE_LIGHT);
// These are copied by the ROI
final float[] x = new float[1];
final float[] y = new float[1];
// Plot the intensity
final double[] intensity = new double[size];
final double[] rank = SimpleArrayUtils.newArray(size, 1, 1.0);
final int top = (settings.topN > 0) ? settings.topN : size;
final int size_1 = size - 1;
for (int i = 0; i < size; i++) {
intensity[i] = spots[i].intensity;
if (i < top) {
x[0] = spots[i].x + bounds.x + 0.5f;
y[0] = spots[i].y + bounds.y + 0.5f;
final Color c = LutHelper.getColour(lut, size_1 - i, size);
addRoi(0, o, x, y, 1, c, 2, 1);
}
}
final String title = TITLE + " Intensity";
final Plot plot = new Plot(title, "Rank", "Intensity");
plot.setColor(Color.blue);
plot.addPoints(rank, intensity, Plot.LINE);
if (settings.topN > 0 && settings.topN < size) {
plot.setColor(Color.magenta);
plot.drawLine(settings.topN, 0, settings.topN, intensity[settings.topN - 1]);
}
if (settings.select > 0 && settings.select < size) {
plot.setColor(Color.yellow);
final int index = settings.select - 1;
final double in = intensity[index];
plot.drawLine(settings.select, 0, settings.select, in);
x[0] = spots[index].x + bounds.x + 0.5f;
y[0] = spots[index].y + bounds.y + 0.5f;
final Color c = LutHelper.getColour(lut, size_1 - settings.select, size);
addRoi(0, o, x, y, 1, c, 3, 3);
plot.setColor(Color.black);
plot.addLabel(0, 0, "Selected spot intensity = " + MathUtils.rounded(in));
}
ImageJUtils.display(title, plot, 0, wo);
wo.tile();
}
imp.setOverlay(o);
}
use of uk.ac.sussex.gdsc.core.ij.plugin.WindowOrganiser in project GDSC-SMLM by aherbert.
the class EmGainAnalysis method plotPmf.
@SuppressWarnings("unused")
private void plotPmf() {
if (!showPmfDialog()) {
return;
}
final double step = getStepSize(settings.settingPhotons, settings.settingGain, settings.settingNoise);
final Pdf pdf = pdf(0, step, settings.settingPhotons, settings.settingGain, settings.settingNoise);
double[] pmf = pdf.probability;
double yMax = MathUtils.max(pmf);
// Get the approximation
LikelihoodFunction fun;
switch(settings.approximationType) {
case 3:
fun = new PoissonFunction(1.0 / settings.settingGain);
break;
case 2:
// Use adaptive normalisation
fun = PoissonGaussianFunction2.createWithStandardDeviation(1.0 / settings.settingGain, settings.settingNoise);
break;
case 1:
// Create Poisson-Gamma (no Gaussian noise)
fun = createPoissonGammaGaussianFunction(0);
break;
case 0:
default:
fun = createPoissonGammaGaussianFunction(settings.settingNoise);
}
double expected = settings.settingPhotons;
if (settings.settingOffset != 0) {
expected += settings.settingOffset * expected / 100.0;
}
// Normalise
final boolean normalise = false;
if (normalise) {
final double sum = MathUtils.sum(pmf);
for (int i = pmf.length; i-- > 0; ) {
pmf[i] /= sum;
}
}
// Get CDF
double sum = 0;
double sum2 = 0;
double[] x = pdf.x;
double[] fvalues = new double[x.length];
double[] cdf1 = new double[pmf.length];
double[] cdf2 = new double[pmf.length];
for (int i = 0; i < cdf1.length; i++) {
sum += pmf[i] * step;
cdf1[i] = sum;
fvalues[i] = fun.likelihood(x[i], expected);
sum2 += fvalues[i] * step;
cdf2[i] = sum2;
}
// Truncate x for plotting
int max = 0;
double plimit = 1 - settings.tail;
while (sum < plimit && max < pmf.length) {
sum += pmf[max] * step;
if (sum > 0.5 && pmf[max] == 0) {
break;
}
max++;
}
int min = pmf.length;
sum = 0;
plimit = 1 - settings.head;
while (sum < plimit && min > 0) {
min--;
sum += pmf[min] * step;
if (sum > 0.5 && pmf[min] == 0) {
break;
}
}
pmf = Arrays.copyOfRange(pmf, min, max);
x = Arrays.copyOfRange(x, min, max);
fvalues = Arrays.copyOfRange(fvalues, min, max);
if (settings.showApproximation) {
yMax = MathUtils.maxDefault(yMax, fvalues);
}
final String label = String.format("Gain=%s, noise=%s, photons=%s", MathUtils.rounded(settings.settingGain), MathUtils.rounded(settings.settingNoise), MathUtils.rounded(settings.settingPhotons));
final Plot plot = new Plot("PMF", "ADUs", "p");
plot.setLimits(x[0], x[x.length - 1], 0, yMax);
plot.setColor(Color.red);
plot.addPoints(x, pmf, Plot.LINE);
if (settings.showApproximation) {
plot.setColor(Color.blue);
plot.addPoints(x, fvalues, Plot.LINE);
}
plot.setColor(Color.magenta);
plot.drawLine(settings.settingPhotons * settings.settingGain, 0, settings.settingPhotons * settings.settingGain, yMax);
plot.setColor(Color.black);
plot.addLabel(0, 0, label);
final PlotWindow win1 = ImageJUtils.display("PMF", plot);
// Plot the difference between the actual and approximation
final double[] delta = new double[fvalues.length];
for (int i = 0; i < fvalues.length; i++) {
if (pmf[i] == 0 && fvalues[i] == 0) {
continue;
}
if (settings.relativeDelta) {
delta[i] = DoubleEquality.relativeError(fvalues[i], pmf[i]) * Math.signum(fvalues[i] - pmf[i]);
} else {
delta[i] = fvalues[i] - pmf[i];
}
}
final Plot plot2 = new Plot("PMF delta", "ADUs", (settings.relativeDelta) ? "Relative delta" : "delta");
final double[] limits = MathUtils.limits(delta);
plot2.setLimits(x[0], x[x.length - 1], limits[0], limits[1]);
plot2.setColor(Color.red);
plot2.addPoints(x, delta, Plot.LINE);
plot2.setColor(Color.magenta);
plot2.drawLine(settings.settingPhotons * settings.settingGain, limits[0], settings.settingPhotons * settings.settingGain, limits[1]);
plot2.setColor(Color.black);
plot2.addLabel(0, 0, label + ((settings.settingOffset == 0) ? "" : ", expected = " + MathUtils.rounded(expected / settings.settingGain)));
final WindowOrganiser wo = new WindowOrganiser();
final PlotWindow win2 = ImageJUtils.display("PMF delta", plot2, wo);
if (wo.isNotEmpty()) {
final Point p2 = win1.getLocation();
p2.y += win1.getHeight();
win2.setLocation(p2);
}
// Plot the CDF of each distribution.
// Compute the Kolmogorov distance as the supremum (maximum)
// difference between the two cumulative probability distributions.
// https://en.wikipedia.org/wiki/Kolmogorov%E2%80%93Smirnov_test
double kolmogorovDistance = 0;
double xd = x[0];
for (int i = 0; i < cdf1.length; i++) {
final double dist = Math.abs(cdf1[i] - cdf2[i]);
if (kolmogorovDistance < dist) {
kolmogorovDistance = dist;
xd = pdf.x[i];
}
}
cdf1 = Arrays.copyOfRange(cdf1, min, max);
cdf2 = Arrays.copyOfRange(cdf2, min, max);
final Plot plot3 = new Plot("CDF", "ADUs", "p");
yMax = 1.05;
plot3.setLimits(x[0], x[x.length - 1], 0, yMax);
plot3.setColor(Color.red);
plot3.addPoints(x, cdf1, Plot.LINE);
plot3.setColor(Color.blue);
plot3.addPoints(x, cdf2, Plot.LINE);
plot3.setColor(Color.magenta);
plot3.drawLine(settings.settingPhotons * settings.settingGain, 0, settings.settingPhotons * settings.settingGain, yMax);
plot3.drawDottedLine(xd, 0, xd, yMax, 2);
plot3.setColor(Color.black);
plot3.addLabel(0, 0, label + ", Kolmogorov distance = " + MathUtils.rounded(kolmogorovDistance) + " @ " + xd);
plot3.addLegend("CDF\nApprox");
final int size = wo.size();
final PlotWindow win3 = ImageJUtils.display("CDF", plot3, wo);
if (size != wo.size()) {
final Point p2 = win1.getLocation();
p2.x += win1.getWidth();
win3.setLocation(p2);
}
}
use of uk.ac.sussex.gdsc.core.ij.plugin.WindowOrganiser in project GDSC-SMLM by aherbert.
the class CmosAnalysis method runAnalysis.
private void runAnalysis() {
final long start = System.currentTimeMillis();
// Create thread pool and workers. The system is likely to be IO limited
// so reduce the computation threads to allow the reading thread in the
// SeriesImageSource to run.
// If the images are small enough to fit into memory then 3 threads are used,
// otherwise it is 1.
final int nThreads = Math.max(1, getThreads() - 3);
final ExecutorService executor = Executors.newFixedThreadPool(nThreads);
final LocalList<Future<?>> futures = new LocalList<>(nThreads);
final LocalList<ImageWorker> workers = new LocalList<>(nThreads);
final double[][] data = new double[subDirs.size() * 2][];
double[] pixelOffset = null;
double[] pixelVariance = null;
Statistics statsOffset = null;
Statistics statsVariance = null;
// For each sub-directory compute the mean and variance
final int nSubDirs = subDirs.size();
boolean error = false;
int width = 0;
int height = 0;
for (int n = 0; n < nSubDirs; n++) {
ImageJUtils.showSlowProgress(n, nSubDirs);
final SubDir sd = subDirs.unsafeGet(n);
ImageJUtils.showStatus(() -> "Analysing " + sd.name);
final StopWatch sw = StopWatch.createStarted();
// Option to reuse data
final File file = new File(settings.directory, "perPixel" + sd.name + ".tif");
boolean found = false;
if (settings.reuseProcessedData && file.exists()) {
final Opener opener = new Opener();
opener.setSilentMode(true);
final ImagePlus imp = opener.openImage(file.getPath());
if (imp != null && imp.getStackSize() == 2 && imp.getBitDepth() == 32) {
if (n == 0) {
width = imp.getWidth();
height = imp.getHeight();
} else if (width != imp.getWidth() || height != imp.getHeight()) {
error = true;
IJ.error(TITLE, "Image width/height mismatch in image series: " + file.getPath() + String.format("\n \nExpected %dx%d, Found %dx%d", width, height, imp.getWidth(), imp.getHeight()));
break;
}
final ImageStack stack = imp.getImageStack();
data[2 * n] = SimpleArrayUtils.toDouble((float[]) stack.getPixels(1));
data[2 * n + 1] = SimpleArrayUtils.toDouble((float[]) stack.getPixels(2));
found = true;
}
}
if (!found) {
// Open the series
final SeriesImageSource source = new SeriesImageSource(sd.name, sd.path.getPath());
if (!source.open()) {
error = true;
IJ.error(TITLE, "Failed to open image series: " + sd.path.getPath());
break;
}
if (n == 0) {
width = source.getWidth();
height = source.getHeight();
} else if (width != source.getWidth() || height != source.getHeight()) {
error = true;
IJ.error(TITLE, "Image width/height mismatch in image series: " + sd.path.getPath() + String.format("\n \nExpected %dx%d, Found %dx%d", width, height, source.getWidth(), source.getHeight()));
break;
}
// So the bar remains at 99% when workers have finished use frames + 1
final Ticker ticker = ImageJUtils.createTicker(source.getFrames() + 1L, nThreads);
// Open the first frame to get the bit depth.
// Assume the first pixels are not empty as the source is open.
Object pixels = source.nextRaw();
final int bitDepth = ImageJUtils.getBitDepth(pixels);
ArrayMoment moment;
if (settings.rollingAlgorithm) {
moment = new RollingArrayMoment();
// We assume 16-bit camera at the maximum
} else if (bitDepth <= 16 && IntegerArrayMoment.isValid(IntegerType.UNSIGNED_16, source.getFrames())) {
moment = new IntegerArrayMoment();
} else {
moment = new SimpleArrayMoment();
}
final BlockingQueue<Object> jobs = new ArrayBlockingQueue<>(nThreads * 2);
for (int i = 0; i < nThreads; i++) {
final ImageWorker worker = new ImageWorker(ticker, jobs, moment);
workers.add(worker);
futures.add(executor.submit(worker));
}
// Process the raw pixel data
long lastTime = 0;
while (pixels != null) {
final long time = System.currentTimeMillis();
if (time - lastTime > 150) {
if (ImageJUtils.isInterrupted()) {
error = true;
break;
}
lastTime = time;
IJ.showStatus("Analysing " + sd.name + " Frame " + source.getStartFrameNumber());
}
put(jobs, pixels);
pixels = source.nextRaw();
}
source.close();
if (error) {
// Kill the workers
workers.stream().forEach(worker -> worker.finished = true);
// Clear the queue
jobs.clear();
// Signal any waiting workers
workers.stream().forEach(worker -> jobs.add(ImageWorker.STOP_SIGNAL));
// Cancel by interruption. We set the finished flag so the ImageWorker should
// ignore the interrupt.
futures.stream().forEach(future -> future.cancel(true));
break;
}
// Finish all the worker threads cleanly
workers.stream().forEach(worker -> jobs.add(ImageWorker.STOP_SIGNAL));
// Wait for all to finish
ConcurrencyUtils.waitForCompletionUnchecked(futures);
// Create the final aggregate statistics
for (final ImageWorker w : workers) {
moment.add(w.moment);
}
data[2 * n] = moment.getMean();
data[2 * n + 1] = moment.getVariance();
// Get the processing speed.
sw.stop();
// ticker holds the number of number of frames processed
final double bits = (double) bitDepth * source.getFrames() * source.getWidth() * source.getHeight();
final double bps = bits / sw.getTime(TimeUnit.SECONDS);
final SiPrefix prefix = SiPrefix.getSiPrefix(bps);
ImageJUtils.log("Processed %d frames. Time = %s. Rate = %s %sbits/s", moment.getN(), sw.toString(), MathUtils.rounded(prefix.convert(bps)), prefix.getPrefix());
// Reset
futures.clear();
workers.clear();
final ImageStack stack = new ImageStack(width, height);
stack.addSlice("Mean", SimpleArrayUtils.toFloat(data[2 * n]));
stack.addSlice("Variance", SimpleArrayUtils.toFloat(data[2 * n + 1]));
IJ.save(new ImagePlus("PerPixel", stack), file.getPath());
}
final Statistics s = Statistics.create(data[2 * n]);
if (pixelOffset != null) {
// Compute mean ADU
final Statistics signal = new Statistics();
final double[] mean = data[2 * n];
for (int i = 0; i < pixelOffset.length; i++) {
signal.add(mean[i] - pixelOffset[i]);
}
ImageJUtils.log("%s Mean = %s +/- %s. Signal = %s +/- %s ADU", sd.name, MathUtils.rounded(s.getMean()), MathUtils.rounded(s.getStandardDeviation()), MathUtils.rounded(signal.getMean()), MathUtils.rounded(signal.getStandardDeviation()));
} else {
// Set the offset assuming the first sub-directory is the bias image
pixelOffset = data[0];
pixelVariance = data[1];
statsOffset = s;
statsVariance = Statistics.create(pixelVariance);
ImageJUtils.log("%s Offset = %s +/- %s. Variance = %s +/- %s", sd.name, MathUtils.rounded(s.getMean()), MathUtils.rounded(s.getStandardDeviation()), MathUtils.rounded(statsVariance.getMean()), MathUtils.rounded(statsVariance.getStandardDeviation()));
}
IJ.showProgress(1);
}
ImageJUtils.clearSlowProgress();
if (error) {
executor.shutdownNow();
IJ.showStatus(TITLE + " cancelled");
return;
}
executor.shutdown();
if (pixelOffset == null || pixelVariance == null) {
IJ.showStatus(TITLE + " error: no bias image");
return;
}
// Compute the gain
ImageJUtils.showStatus("Computing gain");
final double[] pixelGain = new double[pixelOffset.length];
final double[] bibiT = new double[pixelGain.length];
final double[] biaiT = new double[pixelGain.length];
// Ignore first as this is the 0 exposure image
for (int n = 1; n < nSubDirs; n++) {
// Use equation 2.5 from the Huang et al paper.
final double[] b = data[2 * n];
final double[] a = data[2 * n + 1];
for (int i = 0; i < pixelGain.length; i++) {
final double bi = b[i] - pixelOffset[i];
final double ai = a[i] - pixelVariance[i];
bibiT[i] += bi * bi;
biaiT[i] += bi * ai;
}
}
for (int i = 0; i < pixelGain.length; i++) {
pixelGain[i] = biaiT[i] / bibiT[i];
}
final Statistics statsGain = Statistics.create(pixelGain);
ImageJUtils.log("Gain Mean = %s +/- %s", MathUtils.rounded(statsGain.getMean()), MathUtils.rounded(statsGain.getStandardDeviation()));
// Histogram of offset, variance and gain
final int bins = 2 * HistogramPlot.getBinsSturgesRule(pixelGain.length);
final WindowOrganiser wo = new WindowOrganiser();
showHistogram("Offset (ADU)", pixelOffset, bins, statsOffset, wo);
showHistogram("Variance (ADU^2)", pixelVariance, bins, statsVariance, wo);
showHistogram("Gain (ADU/e)", pixelGain, bins, statsGain, wo);
wo.tile();
// Save
final float[] bias = SimpleArrayUtils.toFloat(pixelOffset);
final float[] variance = SimpleArrayUtils.toFloat(pixelVariance);
final float[] gain = SimpleArrayUtils.toFloat(pixelGain);
measuredStack = new ImageStack(width, height);
measuredStack.addSlice("Offset", bias);
measuredStack.addSlice("Variance", variance);
measuredStack.addSlice("Gain", gain);
final ExtendedGenericDialog egd = new ExtendedGenericDialog(TITLE);
egd.addMessage("Save the sCMOS camera model?");
if (settings.modelDirectory == null) {
settings.modelDirectory = settings.directory;
settings.modelName = "sCMOS Camera";
}
egd.addStringField("Model_name", settings.modelName, 30);
egd.addDirectoryField("Model_directory", settings.modelDirectory);
egd.showDialog();
if (!egd.wasCanceled()) {
settings.modelName = egd.getNextString();
settings.modelDirectory = egd.getNextString();
saveCameraModel(width, height, bias, gain, variance);
}
// Remove the status from the ij.io.ImageWriter class
IJ.showStatus("");
ImageJUtils.log("Analysis time = " + TextUtils.millisToString(System.currentTimeMillis() - start));
}
Aggregations