package cz.cuni.lf1.lge.ThunderSTORM.results;
import ij.IJ;
import ij.ImagePlus;
import ij.gui.GenericDialog;
import ij.plugin.PlugIn;
import ij.process.FloatProcessor;
import ij.process.ImageProcessor;
import ij.process.ImageStatistics;
import ij.process.StackStatistics;
import ij.util.Tools;
import java.awt.Checkbox;
import java.awt.TextField;
import java.awt.event.TextEvent;
import java.awt.event.TextListener;
import java.util.Vector;
/**
* This plugin implements the Analyze/Distribution command. It reads the data
* from the ResultsTable and plots a frequency histogram.
*
* @author G. Landini at bham. ac. uk
*/
public class IJDistribution implements PlugIn, TextListener {
static String parameter = "Area";
static boolean autoBinning = true;
static int nBins = 10;
static String range = "0-0";
Checkbox checkbox;
TextField nBinsField, rangeField;
String defaultNBins, defaultRange;
@Override
public void run(String arg) {
GenericTable table;
if("ground-truth".equals(arg)) {
table = IJGroundTruthTable.getGroundTruthTable();
} else {
table = IJResultsTable.getResultsTable();
}
int count = table.getRowCount();
if(count == 0) {
IJ.error("ThunderSTORM: Distribution", "The \"" + table.getFrameTitle() + "\" table is empty");
return;
}
//IJ.log(head);
String[] strings = (String[])table.getColumnNames().toArray(new String[0]);
defaultNBins = "" + nBins;
defaultRange = range;
GenericDialog gd = new GenericDialog("Distribution");
gd.addChoice("Parameter: ", strings, strings[getIndex(strings)]);
gd.addMessage("Data points: " + count);
gd.addCheckbox("Automatic binning", autoBinning);
gd.addNumericField("or specify bins:", nBins, 0);
gd.addStringField("and range:", range);
Vector v = gd.getNumericFields();
nBinsField = (TextField) v.elementAt(0);
nBinsField.addTextListener(this);
v = gd.getStringFields();
rangeField = (TextField) v.elementAt(0);
rangeField.addTextListener(this);
checkbox = (Checkbox) (gd.getCheckboxes().elementAt(0));
gd.showDialog();
if(gd.wasCanceled()) {
return;
}
parameter = gd.getNextChoice();
autoBinning = gd.getNextBoolean();
double nMin = 0.0, nMax = 0.0;
if(!autoBinning) {
nBins = (int) gd.getNextNumber();
range = gd.getNextString();
String[] minAndMax = Tools.split(range, " -");
nMin = Tools.parseDouble(minAndMax[0]);
nMax = minAndMax.length == 2 ? Tools.parseDouble(minAndMax[1]) : Double.NaN;
if(Double.isNaN(nMin) || Double.isNaN(nMax)) {
nMin = 0.0;
nMax = 0.0;
range = "0-0";
}
}
double[] data;
if(table.columnExists(parameter)) {
data = table.getColumnAsDoubles(parameter);
} else {
IJ.error("Distribution", "No available results: \"" + parameter + "\"");
return;
}
double[] pars = new double[11];
stats(count, data, pars);
if(autoBinning) {
//sd = 7, min = 3, max = 4
// use Scott's method (1979 Biometrika, 66:605-610) for optimal binning: 3.49*sd*N^-1/3
float binWidth = (float) (3.49 * pars[7] * (float) Math.pow((float) count, -1.0 / 3.0));
nBins = (int) Math.floor(((pars[4] - pars[3]) / binWidth) + .5);
if(nBins < 2) {
nBins = 2;
}
}
ImageProcessor ip = new FloatProcessor(count, 1, data);
ImagePlus imp = new ImagePlus("", ip);
ImageStatistics stats = new StackStatistics(imp, nBins, nMin, nMax);
int maxCount = 0;
for(int i = 0; i < stats.histogram.length; i++) {
if(stats.histogram[i] > maxCount) {
maxCount = stats.histogram[i];
}
}
stats.histYMax = maxCount;
new IJHistogramWindow(parameter, parameter + " Distribution", imp, stats);
}
int getIndex(String[] strings) {
for(int i = 0; i < strings.length; i++) {
if(strings[i].equals(parameter)) {
return i;
}
}
return 0;
}
@Override
public void textValueChanged(TextEvent e) {
if(!defaultNBins.equals(nBinsField.getText())) {
checkbox.setState(false);
}
if(!defaultRange.equals(rangeField.getText())) {
checkbox.setState(false);
}
}
void stats(int nc, double[] data, double[] pars) {
// ("\tPoints\tEdges_n\tGraph_Length\tMin\tMax\tMean\tAvDev\tSDev\tVar\tSkew\tKurt");
int i;
double s = 0, min = Float.MAX_VALUE, max = -Float.MAX_VALUE, totl = 0, ave = 0, adev = 0, sdev = 0, var = 0, skew = 0, kurt = 0, p;
for(i = 0; i < nc; i++) {
totl += data[i];
//tot& = tot& + 1
if(data[i] < min) {
min = data[i];
}
if(data[i] > max) {
max = data[i];
}
}
ave = totl / nc;
for(i = 0; i < nc; i++) {
s = data[i] - ave;
adev += Math.abs(s);
p = s * s;
var += p;
p *= s;
skew += p;
p *= s;
kurt += p;
}
adev /= nc;
var /= nc - 1;
sdev = (float) Math.sqrt(var);
if(var > 0) {
skew = (float) skew / (nc * (float) Math.pow(sdev, 3));
kurt = (float) kurt / (nc * (float) Math.pow(var, 2)) - 3;
}
pars[1] = (float) nc;
pars[2] = totl;
pars[3] = min;
pars[4] = max;
pars[5] = ave;
pars[6] = adev;
pars[7] = sdev;
pars[8] = var;
pars[9] = skew;
pars[10] = kurt;
}
}