package ij.plugin;
import ij.*;
import ij.gui.*;
import ij.process.*;
import ij.plugin.PlugIn;
import ij.measure.*;
import ij.util.Tools;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
public class Distribution 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;
public void run(String arg) {
ResultsTable rt=ResultsTable.getResultsTable();
if (rt.size()==0) {
IJ.error("Distribution", "The \"Results\" table is empty");
return;
}
run(rt);
}
public void run(ResultsTable rt) {
if (rt==null)
return;
int count = rt.size();
String head = rt.getColumnHeadings();
StringTokenizer t = new StringTokenizer(head, "\t");
int tokens = t.countTokens()-1;
String[] strings = new String[tokens];
strings[0] = t.nextToken(); for(int i=0; i<tokens; i++)
strings[i] = t.nextToken();
defaultNBins = ""+nBins;
defaultRange = range;
GenericDialog gd = new GenericDialog("Distribution");
gd.addChoice("Parameter: ", strings, strings[getIndex(strings)]);
gd.setInsets(0, 40, 0);
gd.addMessage(count+" data points", null, Color.darkGray);
gd.addCheckbox("Automatic binning", autoBinning);
gd.addNumericField ("or specify bins:", nBins, 0);
gd.addStringField ("and range:", range);
Vector v = gd.getNumericFields();
if (v!=null) {
nBinsField = (TextField)v.elementAt(0);
nBinsField.addTextListener(this);
}
v = gd.getStringFields();
if (v!=null) {
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 = range.replaceAll("([0-9.])[\t ]*-", "$1,").split(",");
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";}
}
float[] data = null;
int index = rt.getColumnIndex(parameter);
if (index>=0)
data = rt.getColumn(index);
if (data==null) {
IJ.error("Distribution", "No available results: \""+parameter+"\"");
return;
}
float [] pars = new float [11];
stats(count, data, pars);
if (autoBinning) {
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, null);
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 HistogramWindow(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;
}
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, float[] data, float[] pars){
int i;
float 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];
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;
}
}