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; } }