package gdsc.smlm.ij.plugins;
/*-----------------------------------------------------------------------------
* GDSC SMLM Software
*
* Copyright (C) 2013 Alex Herbert
* Genome Damage and Stability Centre
* University of Sussex, UK
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*---------------------------------------------------------------------------*/
import java.awt.Rectangle;
import gdsc.core.ij.Utils;
import gdsc.smlm.results.MemoryPeakResults;
import gdsc.smlm.results.PeakResult;
import ij.IJ;
import ij.gui.GenericDialog;
import ij.plugin.PlugIn;
import ij.text.TextWindow;
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
/**
* Produces a summary table of the results that are stored in memory.
*/
public class SummariseResults implements PlugIn
{
private static final String TITLE = "Summarise Results";
private static TextWindow summary = null;
/*
* (non-Javadoc)
*
* @see ij.plugin.PlugIn#run(java.lang.String)
*/
public void run(String arg)
{
SMLMUsageTracker.recordPlugin(this.getClass(), arg);
if (MemoryPeakResults.isMemoryEmpty())
{
IJ.error(TITLE, "There are no fitting results in memory");
clearSummaryTable();
return;
}
createSummaryTable();
StringBuilder sb = new StringBuilder();
int i = 0;
int nextFlush = 9;
for (MemoryPeakResults result : MemoryPeakResults.getAllResults())
{
addSummary(sb, result);
if (++i == nextFlush)
{
summary.append(sb.toString());
sb.setLength(0);
}
}
summary.append(sb.toString());
summary.append("");
summary.toFront();
}
/**
* Remove all entries in the summary table if it showing
*/
public static void clearSummaryTable()
{
if (summary != null && summary.isShowing())
summary.getTextPanel().clear();
}
private void createSummaryTable()
{
if (summary == null || !summary.isShowing())
{
StringBuilder sb = new StringBuilder("Dataset\tN\tFrames\tTime\tMemory\tBounds\tnm/pixel\tGain\tms/frame");
for (String statName : new String[] { "Precision (nm)", "SNR" })
{
sb.append("\tAv ").append(statName);
sb.append("\tMedian ").append(statName);
sb.append("\tMin ").append(statName);
sb.append("\tMax ").append(statName);
}
summary = new TextWindow("Peak Results Summary", sb.toString(), "", 800, 300);
summary.setVisible(true);
}
// This could be optional but at current there is no dialog and it seems unnecessary
clearSummaryTable();
}
private static int NO = -1, UNKNOWN = 0, YES = 1;
private int removeNullResults = UNKNOWN;
private void addSummary(StringBuilder sb, MemoryPeakResults result)
{
DescriptiveStatistics[] stats = new DescriptiveStatistics[2];
char[] suffix = new char[2];
for (int i = 0; i < stats.length; i++)
{
stats[i] = new DescriptiveStatistics();
suffix[i] = 0;
}
if (result.hasNullResults())
{
IJ.log("Null results in dataset: " + result.getName());
if (removeNullResults == UNKNOWN)
{
GenericDialog gd = new GenericDialog(TITLE);
gd.addMessage("There are invalid results in memory.\n \nClean these results?");
gd.enableYesNoCancel();
gd.hideCancelButton();
gd.showDialog();
removeNullResults = (gd.wasOKed()) ? YES : NO;
}
if (removeNullResults == NO)
result = result.copy();
result.removeNullResults();
}
final int size = result.size();
if (size > 0)
{
// Precision
// Check if we can use the stored precision
if (result.hasStoredPrecision())
{
suffix[0] = '*';
for (PeakResult peakResult : result.getResults())
{
stats[0].addValue(peakResult.getPrecision());
}
}
else if (result.isCalibratedForPrecision())
{
final double nmPerPixel = result.getNmPerPixel();
final double gain = result.getGain();
final boolean emCCD = result.isEMCCD();
for (PeakResult peakResult : result.getResults())
{
stats[0].addValue(peakResult.getPrecision(nmPerPixel, gain, emCCD));
}
}
// SNR requires noise
if (result.getHead().noise > 0)
{
for (PeakResult peakResult : result.getResults())
{
stats[1].addValue(peakResult.getSignal() / peakResult.noise);
}
}
}
sb.append(result.getName());
sb.append("\t").append(result.size());
int maxT = getMaxT(result);
sb.append("\t").append(maxT);
final double exposureTime = (result.getCalibration() != null) ? result.getCalibration().getExposureTime() : 0;
sb.append("\t").append(Utils.timeToString(maxT * exposureTime));
if (size > 0)
{
boolean includeDeviations = result.getHead().paramsStdDev != null;
long memorySize = MemoryPeakResults.estimateMemorySize(size, includeDeviations);
String memory = MemoryPeakResults.memorySizeString(memorySize);
sb.append("\t").append(memory);
}
else
{
sb.append("\t-");
}
Rectangle bounds = result.getBounds(true);
sb.append(String.format("\t%d,%d,%d,%d\t%s\t%s\t%s", bounds.x, bounds.y, bounds.x + bounds.width,
bounds.y + bounds.height, Utils.rounded(result.getNmPerPixel(), 4), Utils.rounded(result.getGain(), 4),
Utils.rounded(exposureTime, 4)));
for (int i = 0; i < stats.length; i++)
{
if (Double.isNaN(stats[i].getMean()))
{
sb.append("\t-\t-\t-\t-");
}
else
{
sb.append("\t").append(IJ.d2s(stats[i].getMean(), 3));
if (suffix[i] != 0)
sb.append(suffix[i]);
sb.append("\t").append(IJ.d2s(stats[i].getPercentile(50), 3));
sb.append("\t").append(IJ.d2s(stats[i].getMin(), 3));
sb.append("\t").append(IJ.d2s(stats[i].getMax(), 3));
}
}
sb.append("\n");
}
private int getMaxT(MemoryPeakResults result)
{
int maxT = 0;
for (PeakResult r : result.getResults())
if (maxT < r.getEndFrame())
maxT = r.getEndFrame();
return maxT;
}
}