use of ij.gui.Plot in project GDSC-SMLM by aherbert.
the class TcPalmAnalysis method analyseRois.
/**
* Analyses all the ROIs in the ROI manager.
*
* @param event the event
*/
private void analyseRois(ActionEvent event) {
final RoiManager manager = RoiManager.getInstance();
if (manager == null) {
IJ.error(TITLE, "ROI manager is not open");
return;
}
final LocalList<Roi> rois = Arrays.stream(manager.getRoisAsArray()).filter(Roi::isArea).collect(LocalCollectors.toLocalList());
if (rois.isEmpty()) {
IJ.error(TITLE, "No area ROIs");
return;
}
// Check for overlaps.
if (!settings.getDisableOverlapCheck() && anyOverlap(rois)) {
final GenericDialog gd = new GenericDialog(TITLE);
gd.addMessage(TextUtils.wrap("WARNING - Bounding rectangles of ROIs overlap. You can verify " + "the ROIs on the image using the ROI manager 'Show all' function.", 80));
gd.setOKLabel("Continue");
gd.showDialog();
if (gd.wasCanceled()) {
return;
}
}
// For each ROI:
// - Extract the current groups
// - Build the cumulative count plot
// - Identify the bursts
// - Extract ClusterData for each burst
final TcPalmAnalysisSettings settings = this.settings.build();
final LocalList<ClusterData> allClusters = rois.parallelStream().map(roi -> {
final Rectangle2D scaledBounds = createScaledBounds(roi);
final BiPredicate<ClusterData, Rectangle2D> filter = createSelectionFilter(roi, settings);
// Filter all cluster groups
final LocalList<ClusterData> clusters = new LocalList<>();
clusterData.forEach(c -> {
if (filter.test(c, scaledBounds)) {
clusters.add(c);
}
});
// Extract activation bursts
final CumulativeCountData countData = createCumulativeCountData(clusters, false);
final LocalList<int[]> bursts = runBurstAnalysis(settings, countData);
final LocalList<LocalList<PeakResult>> burstLocalisations = createBurstLocalisations(clusters, bursts);
clusters.clear();
burstLocalisations.forEach(list -> {
final ClusterData d = new ClusterData(clusters.size() + 1, list);
// Save this for analysis
d.sourceRoi = roi;
d.getArea();
clusters.add(d);
});
return clusters;
}).collect(LocalList::new, LocalList::addAll, LocalList::addAll);
// Reorder
final Counter count = new Counter();
allClusters.forEach(c -> c.id = count.incrementAndGet());
// Display in a table
final ClusterDataTableModelFrame frame = createAllClustersTable();
frame.getModel().setData(allClusters, dataCalibration);
// Allow the results to be repeated
frame.selectedAction = clusters -> {
// Expecting a single cluster. No clusters occurs when the table (and selection) is cleared.
if (clusters.size() == 1) {
final ClusterData c = clusters.get(0);
// Push the correct ROI and settings to the analysis action.
// We do not directly update the ROI or dialog settings as
// these trigger events that are processed to add work with a delay.
// Updating them at the end should generate events that are
// ignored when finally executed as the ROI/settings should be the same.
addWork(0, c.sourceRoi, settings, () -> {
// When analysis has finished update the settings and image ROI.
image.getImagePlus().setRoi(c.sourceRoi);
darkTimeToleranceTextField.setText(Integer.toString(settings.getDarkTimeTolerance()));
minClusterSizeTextField.setText(Integer.toString(settings.getMinClusterSize()));
// When analysis has finished the cluster should be selected in the
// current clusters table.
final ClusterDataTableModelFrame currentClusters = currentClustersTable.get();
if (currentClusters != null) {
currentClusters.select(c);
}
});
}
};
// Show histogram of cluster size/duration
reportAnalysis(settings, allClusters, dataCalibration);
// Save clusters to memory
final Trace[] traces = allClusters.stream().map(c -> {
final Trace t = new Trace();
t.setId(c.id);
c.results.forEach(t::add);
return t;
}).toArray(Trace[]::new);
TraceMolecules.saveResults(results, traces, "TC PALM");
IJ.showStatus(TITLE + ": " + TextUtils.pleural(allClusters.size(), "cluster"));
}
use of ij.gui.Plot in project GDSC-SMLM by aherbert.
the class TcPalmAnalysis method runBurstPlotSelection.
/**
* Select the activation bursts on the cumulative activations plot. Also stores the bursts in the
* class property for future plot updates with the same selection.
*
* <p>This is synchronized so that the table selection events do not clash with the background
* analysis jobs.
*
* @param bursts the bursts
*/
private synchronized void runBurstPlotSelection(LocalList<int[]> bursts) {
this.bursts = bursts;
final Plot plot = activationsPlotData.plot;
plot.restorePlotObjects();
if (!bursts.isEmpty()) {
final int[] plotFrames = activationsPlotData.data.plotFrames;
final int[] plotCounts = activationsPlotData.data.plotCounts;
// For each cluster add to the plot.
final TIntArrayList tmpFrames = new TIntArrayList();
final TIntArrayList tmpCounts = new TIntArrayList();
for (int i = 0; i < bursts.size(); i++) {
// Find the start and end points on the plotted data.
final int[] range = bursts.unsafeGet(i);
final int is = Arrays.binarySearch(plotFrames, range[0]);
final int ie = Arrays.binarySearch(plotFrames, range[1]);
// Pad the line with zeros at the end
tmpFrames.resetQuick();
tmpCounts.resetQuick();
int[] frames = Arrays.copyOfRange(plotFrames, is, ie + 1);
tmpFrames.add(frames[0]);
tmpFrames.add(frames);
tmpFrames.add(frames[frames.length - 1]);
tmpCounts.add(0);
int[] counts = Arrays.copyOfRange(plotCounts, is, ie + 1);
tmpCounts.add(counts);
tmpCounts.add(0);
frames = tmpFrames.toArray();
counts = tmpCounts.toArray();
// Add to the plot.
plot.setColor(colourMap.getColour(i));
plot.addPoints(activationsPlotData.timeConverter.apply(SimpleArrayUtils.toFloat(frames)), SimpleArrayUtils.toFloat(counts), Plot.LINE);
}
}
plot.updateImage();
}
use of ij.gui.Plot in project GDSC-SMLM by aherbert.
the class PulseActivationAnalysis method runCrosstalkAnalysis.
private void runCrosstalkAnalysis() {
if (!showCrossTalkAnalysisDialog()) {
return;
}
final double[] crosstalk = computeCrosstalk(counts, settings.targetChannel - 1);
// Store the cross talk.
// Crosstalk from M into N is defined as the number of times the molecule that should be
// activated by a pulse from channel M is activated by a pulse from channel N.
// targetChannel = M
// activationChannel = N
int index1;
int index2 = -1;
if (settings.channels == 2) {
if (settings.targetChannel == 1) {
index1 = setCrosstalk(Settings.C12, crosstalk[1]);
} else {
index1 = setCrosstalk(Settings.C21, crosstalk[0]);
}
// 3-channel
} else if (settings.targetChannel == 1) {
index1 = setCrosstalk(Settings.C12, crosstalk[1]);
index2 = setCrosstalk(Settings.C13, crosstalk[2]);
} else if (settings.targetChannel == 2) {
index1 = setCrosstalk(Settings.C21, crosstalk[0]);
index2 = setCrosstalk(Settings.C23, crosstalk[2]);
} else {
index1 = setCrosstalk(Settings.C31, crosstalk[0]);
index2 = setCrosstalk(Settings.C32, crosstalk[1]);
}
// Show fraction activations histogram. So we have to set the sum to 1
final double sum = MathUtils.sum(crosstalk);
for (int i = 0; i < crosstalk.length; i++) {
crosstalk[i] /= sum;
}
// Plot a histogram
final double[] x = SimpleArrayUtils.newArray(settings.channels, 1.0, 1);
final double[] y = crosstalk;
final Plot plot = new Plot(title, "Channel", "Fraction activations");
plot.setLimits(0, settings.channels + 1.0, 0, 1);
plot.setXMinorTicks(false);
plot.addPoints(x, y, Plot.BAR);
String label = String.format("Crosstalk %s = %s", Settings.ctNames[index1], MathUtils.round(settings.ct[index1]));
if (index2 > -1) {
label += String.format(", %s = %s", Settings.ctNames[index2], MathUtils.round(settings.ct[index2]));
}
plot.addLabel(0, 0, label);
ImageJUtils.display(title, plot);
}
use of ij.gui.Plot 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 ij.gui.Plot in project GDSC-SMLM by aherbert.
the class PsfCreator method runUsingFitting.
private void runUsingFitting() {
if (!showFittingDialog()) {
return;
}
if (!loadConfiguration()) {
return;
}
final BasePoint[] spots = getSpots(0, true);
if (spots.length == 0) {
IJ.error(TITLE, "No spots without neighbours within " + (boxRadius * 2) + "px");
return;
}
final ImageStack stack = getImageStack();
final int width = imp.getWidth();
final int height = imp.getHeight();
final int currentSlice = imp.getSlice();
// Adjust settings for a single maxima
config.setIncludeNeighbours(false);
final ArrayList<double[]> centres = new ArrayList<>(spots.length);
final int iterations = 1;
final LoessInterpolator loess = new LoessInterpolator(settings.getSmoothing(), iterations);
// TODO - The fitting routine may not produce many points. In this instance the LOESS
// interpolator
// fails to smooth the data very well. A higher bandwidth helps this but perhaps
// try a different smoothing method.
// For each spot
ImageJUtils.log(TITLE + ": " + imp.getTitle());
ImageJUtils.log("Finding spot locations...");
ImageJUtils.log(" %d spot%s without neighbours within %dpx", spots.length, ((spots.length == 1) ? "" : "s"), (boxRadius * 2));
final StoredDataStatistics averageSd = new StoredDataStatistics();
final StoredDataStatistics averageA = new StoredDataStatistics();
final Statistics averageRange = new Statistics();
final MemoryPeakResults allResults = new MemoryPeakResults();
allResults.setCalibration(fitConfig.getCalibration());
allResults.setPsf(fitConfig.getPsf());
allResults.setName(TITLE);
allResults.setBounds(new Rectangle(0, 0, width, height));
MemoryPeakResults.addResults(allResults);
for (int n = 1; n <= spots.length; n++) {
final BasePoint spot = spots[n - 1];
final int x = (int) spot.getX();
final int y = (int) spot.getY();
final MemoryPeakResults results = fitSpot(stack, width, height, x, y);
allResults.add(results);
if (results.size() < 5) {
ImageJUtils.log(" Spot %d: Not enough fit results %d", n, results.size());
continue;
}
// Get the results for the spot centre and width
final double[] z = new double[results.size()];
final double[] xCoord = new double[z.length];
final double[] yCoord = new double[z.length];
final double[] sd;
final double[] a;
final Counter counter = new Counter();
// We have fit the results so they will be in the preferred units
results.forEach(new PeakResultProcedure() {
@Override
public void execute(PeakResult peak) {
final int i = counter.getAndIncrement();
z[i] = peak.getFrame();
xCoord[i] = peak.getXPosition() - x;
yCoord[i] = peak.getYPosition() - y;
}
});
final WidthResultProcedure wp = new WidthResultProcedure(results, DistanceUnit.PIXEL);
wp.getW();
sd = SimpleArrayUtils.toDouble(wp.wx);
final HeightResultProcedure hp = new HeightResultProcedure(results, IntensityUnit.COUNT);
hp.getH();
a = SimpleArrayUtils.toDouble(hp.heights);
// Smooth the amplitude plot
final double[] smoothA = loess.smooth(z, a);
// Find the maximum amplitude
int maximumIndex = findMaximumIndex(smoothA);
// Find the range at a fraction of the max. This is smoothed to find the X/Y centre
int start = 0;
int stop = smoothA.length - 1;
final double limit = smoothA[maximumIndex] * settings.getAmplitudeFraction();
for (int j = 0; j < smoothA.length; j++) {
if (smoothA[j] > limit) {
start = j;
break;
}
}
for (int j = smoothA.length; j-- > 0; ) {
if (smoothA[j] > limit) {
stop = j;
break;
}
}
averageRange.add(stop - start + 1);
// Extract xy centre coords and smooth
double[] smoothX = new double[stop - start + 1];
double[] smoothY = new double[smoothX.length];
double[] smoothSd = new double[smoothX.length];
final double[] newZ = new double[smoothX.length];
for (int j = start, k = 0; j <= stop; j++, k++) {
smoothX[k] = xCoord[j];
smoothY[k] = yCoord[j];
smoothSd[k] = sd[j];
newZ[k] = z[j];
}
smoothX = loess.smooth(newZ, smoothX);
smoothY = loess.smooth(newZ, smoothY);
smoothSd = loess.smooth(newZ, smoothSd);
// Since the amplitude is not very consistent move from this peak to the
// lowest width which is the in-focus spot.
maximumIndex = findMinimumIndex(smoothSd, maximumIndex - start);
// Find the centre at the amplitude peak
final double cx = smoothX[maximumIndex] + x;
final double cy = smoothY[maximumIndex] + y;
int cz = (int) newZ[maximumIndex];
double csd = smoothSd[maximumIndex];
double ca = smoothA[maximumIndex + start];
// The average should weight the SD using the signal for each spot
averageSd.add(smoothSd[maximumIndex]);
averageA.add(ca);
if (ignoreSpot(n, z, a, smoothA, xCoord, yCoord, sd, newZ, smoothX, smoothY, smoothSd, cx, cy, cz, csd)) {
ImageJUtils.log(" Spot %d was ignored", n);
continue;
}
// Store result - it may have been moved interactively
maximumIndex += this.slice - cz;
cz = (int) newZ[maximumIndex];
csd = smoothSd[maximumIndex];
ca = smoothA[maximumIndex + start];
ImageJUtils.log(" Spot %d => x=%.2f, y=%.2f, z=%d, sd=%.2f, A=%.2f", n, cx, cy, cz, csd, ca);
centres.add(new double[] { cx, cy, cz, csd, n });
}
if (settings.getInteractiveMode()) {
imp.setSlice(currentSlice);
imp.setOverlay(null);
// Hide the amplitude and spot plots
ImageJUtils.hide(TITLE_AMPLITUDE);
ImageJUtils.hide(TITLE_PSF_PARAMETERS);
}
if (centres.isEmpty()) {
final String msg = "No suitable spots could be identified";
ImageJUtils.log(msg);
IJ.error(TITLE, msg);
return;
}
// Find the limits of the z-centre
int minz = (int) centres.get(0)[2];
int maxz = minz;
for (final double[] centre : centres) {
if (minz > centre[2]) {
minz = (int) centre[2];
} else if (maxz < centre[2]) {
maxz = (int) centre[2];
}
}
IJ.showStatus("Creating PSF image");
// Create a stack that can hold all the data.
final ImageStack psf = createStack(stack, minz, maxz, settings.getMagnification());
// For each spot
final Statistics stats = new Statistics();
boolean ok = true;
for (int i = 0; ok && i < centres.size(); i++) {
final double increment = 1.0 / (stack.getSize() * centres.size());
setProgress((double) i / centres.size());
final double[] centre = centres.get(i);
// Extract the spot
final float[][] spot = new float[stack.getSize()][];
Rectangle regionBounds = null;
for (int slice = 1; slice <= stack.getSize(); slice++) {
final ImageExtractor ie = ImageExtractor.wrap((float[]) stack.getPixels(slice), width, height);
if (regionBounds == null) {
regionBounds = ie.getBoxRegionBounds((int) centre[0], (int) centre[1], boxRadius);
}
spot[slice - 1] = ie.crop(regionBounds);
}
if (regionBounds == null) {
// Empty stack
continue;
}
final int n = (int) centre[4];
final float b = getBackground(n, spot);
if (!subtractBackgroundAndWindow(spot, b, regionBounds.width, regionBounds.height, centre, loess)) {
ImageJUtils.log(" Spot %d was ignored", n);
continue;
}
stats.add(b);
// Adjust the centre using the crop
centre[0] -= regionBounds.x;
centre[1] -= regionBounds.y;
// This takes a long time so this should track progress
ok = addToPsf(maxz, settings.getMagnification(), psf, centre, spot, regionBounds, increment, settings.getCentreEachSlice());
}
if (settings.getInteractiveMode()) {
ImageJUtils.hide(TITLE_INTENSITY);
}
IJ.showProgress(1);
if (!ok || stats.getN() == 0) {
return;
}
final double avSd = getAverage(averageSd, averageA, 2);
ImageJUtils.log(" Average background = %.2f, Av. SD = %s px", stats.getMean(), MathUtils.rounded(avSd, 4));
normalise(psf, maxz, avSd * settings.getMagnification(), false);
IJ.showProgress(1);
psfImp = ImageJUtils.display(TITLE_PSF, psf);
psfImp.setSlice(maxz);
psfImp.resetDisplayRange();
psfImp.updateAndDraw();
final double[][] fitCom = new double[2][psf.getSize()];
Arrays.fill(fitCom[0], Double.NaN);
Arrays.fill(fitCom[1], Double.NaN);
final double fittedSd = fitPsf(psf, loess, maxz, averageRange.getMean(), fitCom);
// Compute the drift in the PSF:
// - Use fitted centre if available; otherwise find CoM for each frame
// - express relative to the average centre
final double[][] com = calculateCentreOfMass(psf, fitCom, nmPerPixel / settings.getMagnification());
final double[] slice = SimpleArrayUtils.newArray(psf.getSize(), 1, 1.0);
final String title = TITLE + " CoM Drift";
final Plot plot = new Plot(title, "Slice", "Drift (nm)");
plot.addLabel(0, 0, "Red = X; Blue = Y");
// double[] limitsX = Maths.limits(com[0]);
// double[] limitsY = Maths.limits(com[1]);
final double[] limitsX = getLimits(com[0]);
final double[] limitsY = getLimits(com[1]);
plot.setLimits(1, psf.getSize(), Math.min(limitsX[0], limitsY[0]), Math.max(limitsX[1], limitsY[1]));
plot.setColor(Color.red);
plot.addPoints(slice, com[0], Plot.DOT);
plot.addPoints(slice, loess.smooth(slice, com[0]), Plot.LINE);
plot.setColor(Color.blue);
plot.addPoints(slice, com[1], Plot.DOT);
plot.addPoints(slice, loess.smooth(slice, com[1]), Plot.LINE);
ImageJUtils.display(title, plot);
// TODO - Redraw the PSF with drift correction applied.
// This means that the final image should have no drift.
// This is relevant when combining PSF images. It doesn't matter too much for simulations
// unless the drift is large.
// Add Image properties containing the PSF details
final double fwhm = getFwhm(psf, maxz);
psfImp.setProperty("Info", ImagePsfHelper.toString(ImagePsfHelper.create(maxz, nmPerPixel / settings.getMagnification(), settings.getNmPerSlice(), stats.getN(), fwhm, createNote())));
ImageJUtils.log("%s : z-centre = %d, nm/Pixel = %s, nm/Slice = %s, %d images, " + "PSF SD = %s nm, FWHM = %s px\n", psfImp.getTitle(), maxz, MathUtils.rounded(nmPerPixel / settings.getMagnification(), 3), MathUtils.rounded(settings.getNmPerSlice(), 3), stats.getN(), MathUtils.rounded(fittedSd * nmPerPixel, 4), MathUtils.rounded(fwhm));
createInteractivePlots(psf, maxz, nmPerPixel / settings.getMagnification(), fittedSd * nmPerPixel);
IJ.showStatus("");
}
Aggregations