package ij.plugin.filter; import java.awt.*; import java.util.Vector; import java.util.Properties; import ij.*; import ij.gui.*; import ij.process.*; import ij.measure.*; import ij.text.*; import ij.plugin.MeasurementsWriter; import ij.plugin.Straightener; import ij.util.Tools; import ij.macro.Interpreter; /** This plugin implements ImageJ's Analyze/Measure and Analyze/Set Measurements commands. */ public class Analyzer implements PlugInFilter, Measurements { private String arg; private ImagePlus imp; private ResultsTable rt; private int measurements; private StringBuffer min,max,mean,sd; // Order must agree with order of checkboxes in Set Measurements dialog box private static final int[] list = {AREA,MEAN,STD_DEV,MODE,MIN_MAX, CENTROID,CENTER_OF_MASS,PERIMETER,RECT,ELLIPSE,SHAPE_DESCRIPTORS, FERET, INTEGRATED_DENSITY,MEDIAN,SKEWNESS,KURTOSIS,AREA_FRACTION,STACK_POSITION, LIMIT,LABELS,INVERT_Y,SCIENTIFIC_NOTATION,ADD_TO_OVERLAY}; private static final String MEASUREMENTS = "measurements"; private static final String MARK_WIDTH = "mark.width"; private static final String PRECISION = "precision"; //private static int counter; private static boolean unsavedMeasurements; public static Color darkBlue = new Color(0,0,160); private static int systemMeasurements = Prefs.getInt(MEASUREMENTS,AREA+MEAN+MIN_MAX); public static int markWidth = Prefs.getInt(MARK_WIDTH,0); public static int precision = Prefs.getInt(PRECISION,3); private static float[] umeans = new float[MAX_STANDARDS]; private static ResultsTable systemRT = new ResultsTable(); private static int redirectTarget; private static String redirectTitle = ""; private static ImagePlus redirectImage; // non-displayed images static int firstParticle, lastParticle; private static boolean summarized; private static boolean switchingModes; private static boolean showMin = true; public Analyzer() { rt = systemRT; rt.showRowNumbers(true); rt.setPrecision((systemMeasurements&SCIENTIFIC_NOTATION)!=0?-precision:precision); measurements = systemMeasurements; } /** Constructs a new Analyzer using the specified ImagePlus object and the current measurement options and default results table. */ public Analyzer(ImagePlus imp) { this(); this.imp = imp; } /** Construct a new Analyzer using an ImagePlus object and private measurement options and results table. */ public Analyzer(ImagePlus imp, int measurements, ResultsTable rt) { this.imp = imp; this.measurements = measurements; this.rt = rt; } public int setup(String arg, ImagePlus imp) { this.arg = arg; this.imp = imp; IJ.register(Analyzer.class); if (arg.equals("set")) {doSetDialog(); return DONE;} else if (arg.equals("sum")) {summarize(); return DONE;} else if (arg.equals("clear")) { if (IJ.macroRunning()) unsavedMeasurements = false; resetCounter(); return DONE; } else return DOES_ALL+NO_CHANGES; } public void run(ImageProcessor ip) { measure(); displayResults(); if ((measurements&ADD_TO_OVERLAY)!=0) addToOverlay(); } void addToOverlay() { Roi roi = imp.getRoi(); if (roi==null) return; if (imp.getStackSize()>1) { if (imp.isHyperStack()||imp.isComposite()) roi.setPosition(0, imp.getSlice(), imp.getFrame()); else roi.setPosition(imp.getCurrentSlice()); } //if (rt.getCounter()==1) // RoiProperties.setShowLabels(true); IJ.run(imp, "Add Selection...", ""); } void doSetDialog() { String NONE = "None"; String[] titles; int[] wList = WindowManager.getIDList(); if (wList==null) { titles = new String[1]; titles[0] = NONE; } else { titles = new String[wList.length+1]; titles[0] = NONE; for (int i=0; i<wList.length; i++) { ImagePlus imp = WindowManager.getImage(wList[i]); titles[i+1] = imp!=null?imp.getTitle():""; } } ImagePlus tImp = WindowManager.getImage(redirectTarget); String target = tImp!=null?tImp.getTitle():NONE; String macroOptions = Macro.getOptions(); if (macroOptions!=null && macroOptions.indexOf("circularity ")!=-1) Macro.setOptions(macroOptions.replaceAll("circularity ", "shape ")); if (macroOptions!=null && macroOptions.indexOf("slice ")!=-1) Macro.setOptions(macroOptions.replaceAll("slice ", "stack ")); GenericDialog gd = new GenericDialog("Set Measurements", IJ.getInstance()); String[] labels = new String[18]; boolean[] states = new boolean[18]; labels[0]="Area"; states[0]=(systemMeasurements&AREA)!=0; labels[1]="Mean gray value"; states[1]=(systemMeasurements&MEAN)!=0; labels[2]="Standard deviation"; states[2]=(systemMeasurements&STD_DEV)!=0; labels[3]="Modal gray value"; states[3]=(systemMeasurements&MODE)!=0; labels[4]="Min & max gray value"; states[4]=(systemMeasurements&MIN_MAX)!=0; labels[5]="Centroid"; states[5]=(systemMeasurements&CENTROID)!=0; labels[6]="Center of mass"; states[6]=(systemMeasurements&CENTER_OF_MASS)!=0; labels[7]="Perimeter"; states[7]=(systemMeasurements&PERIMETER)!=0; labels[8]="Bounding rectangle"; states[8]=(systemMeasurements&RECT)!=0; labels[9]="Fit ellipse"; states[9]=(systemMeasurements&ELLIPSE)!=0; labels[10]="Shape descriptors"; states[10]=(systemMeasurements&SHAPE_DESCRIPTORS)!=0; labels[11]="Feret's diameter"; states[11]=(systemMeasurements&FERET)!=0; labels[12]="Integrated density"; states[12]=(systemMeasurements&INTEGRATED_DENSITY)!=0; labels[13]="Median"; states[13]=(systemMeasurements&MEDIAN)!=0; labels[14]="Skewness"; states[14]=(systemMeasurements&SKEWNESS)!=0; labels[15]="Kurtosis"; states[15]=(systemMeasurements&KURTOSIS)!=0; labels[16]="Area_fraction"; states[16]=(systemMeasurements&AREA_FRACTION)!=0; labels[17]="Stack position"; states[17]=(systemMeasurements&STACK_POSITION)!=0; gd.setInsets(0, 0, 0); gd.addCheckboxGroup(10, 2, labels, states); labels = new String[5]; states = new boolean[5]; labels[0]="Limit to threshold"; states[0]=(systemMeasurements&LIMIT)!=0; labels[1]="Display label"; states[1]=(systemMeasurements&LABELS)!=0; labels[2]="Invert Y coordinates"; states[2]=(systemMeasurements&INVERT_Y)!=0; labels[3]="Scientific notation"; states[3]=(systemMeasurements&SCIENTIFIC_NOTATION)!=0;; labels[4]="Add to overlay"; states[4]=(systemMeasurements&ADD_TO_OVERLAY)!=0;; gd.setInsets(0, 0, 0); gd.addCheckboxGroup(3, 2, labels, states); gd.setInsets(15, 0, 0); gd.addChoice("Redirect to:", titles, target); gd.setInsets(5, 0, 0); gd.addNumericField("Decimal places (0-9):", precision, 0, 2, ""); gd.addHelp(IJ.URL+"/docs/menus/analyze.html#set"); gd.showDialog(); if (gd.wasCanceled()) return; int oldMeasurements = systemMeasurements; setOptions(gd); int index = gd.getNextChoiceIndex(); redirectTarget = index==0?0:wList[index-1]; redirectTitle = titles[index]; ImagePlus imp = WindowManager.getImage(redirectTarget); redirectImage = imp!=null && imp.getWindow()==null?imp:null; int prec = (int)gd.getNextNumber(); if (prec<0) prec = 0; if (prec>9) prec = 9; boolean notationChanged = (oldMeasurements&SCIENTIFIC_NOTATION)!=(systemMeasurements&SCIENTIFIC_NOTATION); if (prec!=precision || notationChanged) { precision = prec; rt.setPrecision((systemMeasurements&SCIENTIFIC_NOTATION)!=0?-precision:precision); if (rt.getCounter()>0) rt.show("Results"); } } void setOptions(GenericDialog gd) { int oldMeasurements = systemMeasurements; int previous = 0; boolean b = false; for (int i=0; i<list.length; i++) { //if (list[i]!=previous) b = gd.getNextBoolean(); previous = list[i]; if (b) systemMeasurements |= list[i]; else systemMeasurements &= ~list[i]; } if ((oldMeasurements&(~LIMIT)&(~SCIENTIFIC_NOTATION))!=(systemMeasurements&(~LIMIT)&(~SCIENTIFIC_NOTATION))&&IJ.isResultsWindow()) { rt.setPrecision((systemMeasurements&SCIENTIFIC_NOTATION)!=0?-precision:precision); rt.update(systemMeasurements, imp, null); } if ((systemMeasurements&LABELS)==0) systemRT.disableRowLabels(); } /** Measures the image or selection and adds the results to the default results table. */ public void measure() { String lastHdr = rt.getColumnHeading(ResultsTable.LAST_HEADING); if (lastHdr==null || lastHdr.charAt(0)!='S') { if (!reset()) return; } firstParticle = lastParticle = 0; Roi roi = imp.getRoi(); if (roi!=null && roi.getType()==Roi.POINT) { measurePoint(roi); return; } if (roi!=null && roi.isLine()) { measureLength(roi); return; } if (roi!=null && roi.getType()==Roi.ANGLE) { measureAngle(roi); return; } ImageStatistics stats; if (isRedirectImage()) { stats = getRedirectStats(measurements, roi); if (stats==null) return; } else stats = imp.getStatistics(measurements); if (!IJ.isResultsWindow() && IJ.getInstance()!=null) reset(); saveResults(stats, roi); } boolean reset() { boolean ok = true; if (rt.getCounter()>0) ok = resetCounter(); if (ok && rt.getColumnHeading(ResultsTable.LAST_HEADING)==null) rt.setDefaultHeadings(); return ok; } /** Returns <code>true</code> if an image is selected in the "Redirect To:" popup menu of the Analyze/Set Measurements dialog box. */ public static boolean isRedirectImage() { return redirectTarget!=0; } /** Set the "Redirect To" image. Pass 'null' as the argument to disable redirected sampling. */ public static void setRedirectImage(ImagePlus imp) { if (imp==null) { redirectTarget = 0; redirectTitle = null; redirectImage = null; } else { redirectTarget = imp.getID(); redirectTitle = imp.getTitle(); if (imp.getWindow()==null) redirectImage = imp; } } private ImagePlus getRedirectImageOrStack(ImagePlus cimp) { ImagePlus rimp = getRedirectImage(cimp); if (rimp!=null) { int depth = rimp.getStackSize(); if (depth>1 && depth==cimp.getStackSize() && rimp.getCurrentSlice()!=cimp.getCurrentSlice()) rimp.setSlice(cimp.getCurrentSlice()); } return rimp; } /** Returns the image selected in the "Redirect To:" popup menu of the Analyze/Set Measurements dialog, or null if "None" is selected, the image was not found or the image is not the same size as <code>currentImage</code>. */ public static ImagePlus getRedirectImage(ImagePlus cimp) { ImagePlus rimp = WindowManager.getImage(redirectTarget); if (rimp==null) rimp = redirectImage; if (rimp==null) { IJ.error("Analyzer", "Redirect image (\""+redirectTitle+"\")\n" + "not found."); redirectTarget = 0; Macro.abort(); return null; } if (rimp.getWidth()!=cimp.getWidth() || rimp.getHeight()!=cimp.getHeight()) { IJ.error("Analyzer", "Redirect image (\""+redirectTitle+"\") \n" + "is not the same size as the current image."); Macro.abort(); return null; } return rimp; } ImageStatistics getRedirectStats(int measurements, Roi roi) { ImagePlus redirectImp = getRedirectImageOrStack(imp); if (redirectImp==null) return null; ImageProcessor ip = redirectImp.getProcessor(); if (imp.getTitle().equals("mask") && imp.getBitDepth()==8) { ip.setMask(imp.getProcessor()); ip.setRoi(0, 0, imp.getWidth(), imp.getHeight()); } else ip.setRoi(roi); return ImageStatistics.getStatistics(ip, measurements, redirectImp.getCalibration()); } void measurePoint(Roi roi) { if (rt.getCounter()>0) { if (!IJ.isResultsWindow()) reset(); int index = rt.getColumnIndex("X"); if (index<0 || !rt.columnExists(index)) rt.update(measurements, imp, roi); } Polygon p = roi.getPolygon(); ImagePlus imp2 = isRedirectImage()?getRedirectImageOrStack(imp):null; if (imp2==null) imp2 = imp; for (int i=0; i<p.npoints; i++) { ImageProcessor ip = imp2.getProcessor(); ip.setRoi(p.xpoints[i], p.ypoints[i], 1, 1); ImageStatistics stats = ImageStatistics.getStatistics(ip, measurements, imp2.getCalibration()); saveResults(stats, new PointRoi(p.xpoints[i], p.ypoints[i])); if (i!=p.npoints-1) displayResults(); } } void measureAngle(Roi roi) { if (rt.getCounter()>0) { if (!IJ.isResultsWindow()) reset(); int index = rt.getColumnIndex("Angle"); if (index<0 || !rt.columnExists(index)) rt.update(measurements, imp, roi); } ImageProcessor ip = imp.getProcessor(); ip.setRoi(roi.getPolygon()); ImageStatistics stats = new ImageStatistics(); saveResults(stats, roi); } void measureLength(Roi roi) { ImagePlus imp2 = isRedirectImage()?getRedirectImageOrStack(imp):null; if (imp2!=null) imp2.setRoi(roi); else imp2 = imp; if (rt.getCounter()>0) { if (!IJ.isResultsWindow()) reset(); boolean update = false; int index = rt.getColumnIndex("Length"); if (index<0 || !rt.columnExists(index)) update=true; if (roi.getType()==Roi.LINE) { index = rt.getColumnIndex("Angle"); if (index<0 || !rt.columnExists(index)) update=true; } if (update) rt.update(measurements, imp2, roi); } boolean straightLine = roi.getType()==Roi.LINE; int lineWidth = (int)Math.round(roi.getStrokeWidth()); ImageProcessor ip2; Rectangle saveR = null; if (straightLine && lineWidth>1) { ip2 = imp2.getProcessor(); saveR = ip2.getRoi(); ip2.setRoi(roi.getPolygon()); } else if (lineWidth>1) { if ((measurements&AREA)!=0 || (measurements&MEAN)!=0) ip2 = (new Straightener()).straightenLine(imp2, lineWidth); else { saveResults(new ImageStatistics(), roi); return; } } else { ProfilePlot profile = new ProfilePlot(imp2); double[] values = profile.getProfile(); if (values==null) return; ip2 = new FloatProcessor(values.length, 1, values); if (straightLine) { Line l = (Line)roi; if ((l.y1==l.y2||l.x1==l.x2)&&l.x1==l.x1d&& l.y1==l.y1d&& l.x2==l.x2d&& l.y2==l.y2d) ip2.setRoi(0, 0, ip2.getWidth()-1, 1); } } ImageStatistics stats = ImageStatistics.getStatistics(ip2, AREA+MEAN+STD_DEV+MODE+MIN_MAX, imp2.getCalibration()); if (saveR!=null) ip2.setRoi(saveR); saveResults(stats, roi); } /** Saves the measurements specified in the "Set Measurements" dialog, or by calling setMeasurements(), in the default results table. */ public void saveResults(ImageStatistics stats, Roi roi) { if (rt.getColumnHeading(ResultsTable.LAST_HEADING)==null) reset(); incrementCounter(); int counter = rt.getCounter(); if (counter<=MAX_STANDARDS) { if (umeans==null) umeans = new float[MAX_STANDARDS]; umeans[counter-1] = (float)stats.umean; } if ((measurements&LABELS)!=0) rt.addLabel("Label", getFileName()); if ((measurements&AREA)!=0) rt.addValue(ResultsTable.AREA,stats.area); if ((measurements&MEAN)!=0) rt.addValue(ResultsTable.MEAN,stats.mean); if ((measurements&STD_DEV)!=0) rt.addValue(ResultsTable.STD_DEV,stats.stdDev); if ((measurements&MODE)!=0) rt.addValue(ResultsTable.MODE, stats.dmode); if ((measurements&MIN_MAX)!=0) { if (showMin) rt.addValue(ResultsTable.MIN,stats.min); rt.addValue(ResultsTable.MAX,stats.max); } if ((measurements&CENTROID)!=0) { rt.addValue(ResultsTable.X_CENTROID,stats.xCentroid); rt.addValue(ResultsTable.Y_CENTROID,stats.yCentroid); } if ((measurements&CENTER_OF_MASS)!=0) { rt.addValue(ResultsTable.X_CENTER_OF_MASS,stats.xCenterOfMass); rt.addValue(ResultsTable.Y_CENTER_OF_MASS,stats.yCenterOfMass); } if ((measurements&PERIMETER)!=0 || (measurements&SHAPE_DESCRIPTORS)!=0) { double perimeter; if (roi!=null) perimeter = roi.getLength(); else perimeter = imp!=null?imp.getWidth()*2+imp.getHeight()*2:0.0; if ((measurements&PERIMETER)!=0) rt.addValue(ResultsTable.PERIMETER,perimeter); if ((measurements&SHAPE_DESCRIPTORS)!=0) { double circularity = perimeter==0.0?0.0:4.0*Math.PI*(stats.area/(perimeter*perimeter)); if (circularity>1.0) circularity = 1.0; rt.addValue(ResultsTable.CIRCULARITY, circularity); Polygon ch = null; boolean isArea = roi==null || roi.isArea(); double convexArea = roi!=null?getArea(roi.getConvexHull()):stats.pixelCount; rt.addValue(ResultsTable.ASPECT_RATIO, isArea?stats.major/stats.minor:0.0); rt.addValue(ResultsTable.ROUNDNESS, isArea?4.0*stats.area/(Math.PI*stats.major*stats.major):0.0); rt.addValue(ResultsTable.SOLIDITY, isArea?stats.pixelCount/convexArea:Double.NaN); //rt.addValue(ResultsTable.CONVEXITY, getConvexPerimeter(roi, ch)/perimeter); } } if ((measurements&RECT)!=0) { if (roi!=null && roi.isLine()) { Rectangle bounds = roi.getBounds(); rt.addValue(ResultsTable.ROI_X, bounds.x); rt.addValue(ResultsTable.ROI_Y, bounds.y); rt.addValue(ResultsTable.ROI_WIDTH, bounds.width); rt.addValue(ResultsTable.ROI_HEIGHT, bounds.height); } else { rt.addValue(ResultsTable.ROI_X,stats.roiX); rt.addValue(ResultsTable.ROI_Y,stats.roiY); rt.addValue(ResultsTable.ROI_WIDTH,stats.roiWidth); rt.addValue(ResultsTable.ROI_HEIGHT,stats.roiHeight); } } if ((measurements&ELLIPSE)!=0) { rt.addValue(ResultsTable.MAJOR,stats.major); rt.addValue(ResultsTable.MINOR,stats.minor); rt.addValue(ResultsTable.ANGLE,stats.angle); } if ((measurements&FERET)!=0) { boolean extras = true; double FeretDiameter=Double.NaN, feretAngle=Double.NaN, minFeret=Double.NaN, feretX=Double.NaN, feretY=Double.NaN; Roi roi2 = roi; if (roi2==null && imp!=null) roi2 = new Roi(0, 0, imp.getWidth(), imp.getHeight()); if (roi2!=null) { double[] a = roi2.getFeretValues(); if (a!=null) { FeretDiameter = a[0]; feretAngle = a[1]; minFeret = a[2]; feretX = a[3]; feretY = a[4]; } } rt.addValue(ResultsTable.FERET, FeretDiameter); rt.addValue(ResultsTable.FERET_X, feretX); rt.addValue(ResultsTable.FERET_Y, feretY); rt.addValue(ResultsTable.FERET_ANGLE, feretAngle); rt.addValue(ResultsTable.MIN_FERET, minFeret); } if ((measurements&INTEGRATED_DENSITY)!=0) { rt.addValue(ResultsTable.INTEGRATED_DENSITY,stats.area*stats.mean); rt.addValue(ResultsTable.RAW_INTEGRATED_DENSITY,stats.pixelCount*stats.umean); } if ((measurements&MEDIAN)!=0) rt.addValue(ResultsTable.MEDIAN, stats.median); if ((measurements&SKEWNESS)!=0) rt.addValue(ResultsTable.SKEWNESS, stats.skewness); if ((measurements&KURTOSIS)!=0) rt.addValue(ResultsTable.KURTOSIS, stats.kurtosis); if ((measurements&AREA_FRACTION)!=0) rt.addValue(ResultsTable.AREA_FRACTION, stats.areaFraction); if ((measurements&STACK_POSITION)!=0) { boolean update = false; if (imp!=null && (imp.isHyperStack()||imp.isComposite())) { int[] position = imp.convertIndexToPosition(imp.getCurrentSlice()); if (imp.getNChannels()>1) { int index = rt.getColumnIndex("Ch"); if (index<0 || !rt.columnExists(index)) update=true; rt.addValue("Ch", position[0]); } if (imp.getNSlices()>1) { int index = rt.getColumnIndex("Slice"); if (index<0 || !rt.columnExists(index)) update=true; rt.addValue("Slice", position[1]); } if (imp.getNFrames()>1) { int index = rt.getColumnIndex("Frame"); if (index<0 || !rt.columnExists(index)) update=true; rt.addValue("Frame", position[2]); } } else { int index = rt.getColumnIndex("Slice"); if (index<0 || !rt.columnExists(index)) update=true; rt.addValue("Slice", imp!=null?imp.getCurrentSlice():1.0); } if (update && rt==systemRT) rt.update(measurements, imp, roi); } if (roi!=null) { if (roi.isLine()) { rt.addValue("Length", roi.getLength()); if (roi.getType()==Roi.LINE) { double angle = 0.0; Line l = (Line)roi; angle = roi.getAngle(l.x1, l.y1, l.x2, l.y2); rt.addValue("Angle", angle); } } else if (roi.getType()==Roi.ANGLE) { double angle = ((PolygonRoi)roi).getAngle(); if (Prefs.reflexAngle) angle = 360.0-angle; rt.addValue("Angle", angle); } else if (roi.getType()==Roi.POINT) savePoints(roi); } } final double getArea(Polygon p) { if (p==null) return Double.NaN; int carea = 0; int iminus1; for (int i=0; i<p.npoints; i++) { iminus1 = i-1; if (iminus1<0) iminus1=p.npoints-1; carea += (p.xpoints[i]+p.xpoints[iminus1])*(p.ypoints[i]-p.ypoints[iminus1]); } return (Math.abs(carea/2.0)); } /* final double getConvexPerimeter(Roi roi, Polygon ch) { if (roi==null || ch==null || !(roi instanceof PolygonRoi)) return 0.0; int[] xp = ((PolygonRoi)roi).getXCoordinates(); int[] yp = ((PolygonRoi)roi).getYCoordinates(); int n = ((PolygonRoi)roi).getNCoordinates(); double perim = getPerimeter(xp, yp, n); double convexPerim = getPerimeter(ch.xpoints, ch.ypoints, ch.npoints); return convexPerim; } final double getPerimeter(int[] xp, int yp[], int n) { double dx, dy, perim=0.0; for (int i=0; i<n-1; i++) { dx = xp[i+1]-xp[i]; dy = yp[i+1]-yp[i]; perim += Math.sqrt(dx*dx+dy*dy); } dx = xp[n-1] - xp[0]; dy = yp[n-1] - yp[0]; perim += Math.sqrt(dx*dx+dy*dy); return perim; } */ void savePoints(Roi roi) { if (imp==null) { rt.addValue("X", 0.0); rt.addValue("Y", 0.0); if (imp.getStackSize()>1) rt.addValue("Slice", 0.0); return; } if ((measurements&AREA)!=0) rt.addValue(ResultsTable.AREA,0); Polygon p = roi.getPolygon(); ImageProcessor ip = imp.getProcessor(); Calibration cal = imp.getCalibration(); int x = p.xpoints[0]; int y = p.ypoints[0]; double value = ip.getPixelValue(x,y); if (markWidth>0 && !Toolbar.getMultiPointMode()) { ip.setColor(Toolbar.getForegroundColor()); ip.setLineWidth(markWidth); ip.moveTo(x,y); ip.lineTo(x,y); imp.updateAndDraw(); ip.setLineWidth(Line.getWidth()); } rt.addValue("X", cal.getX(x)); rt.addValue("Y", cal.getY(y, imp.getHeight())); if (imp.isHyperStack() || imp.isComposite()) { if (imp.getNChannels()>1) rt.addValue("Ch", imp.getChannel()); if (imp.getNSlices()>1) rt.addValue("Slice", imp.getSlice()); if (imp.getNFrames()>1) rt.addValue("Frame", imp.getFrame()); } else if (imp.getStackSize()>1) rt.addValue("Slice", cal.getZ(imp.getCurrentSlice())); if (imp.getProperty("FHT")!=null) { double center = imp.getWidth()/2.0; y = imp.getHeight()-y-1; double r = Math.sqrt((x-center)*(x-center) + (y-center)*(y-center)); if (r<1.0) r = 1.0; double theta = Math.atan2(y-center, x-center); theta = theta*180.0/Math.PI; if (theta<0) theta = 360.0+theta; rt.addValue("R", (imp.getWidth()/r)*cal.pixelWidth); rt.addValue("Theta", theta); } //if ((measurements&MEAN)==0) // rt.addValue("Mean", value); } String getFileName() { String s = ""; if (imp!=null) { if (redirectTarget!=0) { ImagePlus rImp = WindowManager.getImage(redirectTarget); if (rImp==null) rImp = redirectImage; if (rImp!=null) s = rImp.getTitle(); } else s = imp.getTitle(); //int len = s.length(); //if (len>4 && s.charAt(len-4)=='.' && !Character.isDigit(s.charAt(len-1))) // s = s.substring(0,len-4); Roi roi = imp.getRoi(); String roiName = roi!=null?roi.getName():null; if (roiName!=null) s += ":"+roiName; if (imp.getStackSize()>1) { ImageStack stack = imp.getStack(); int currentSlice = imp.getCurrentSlice(); String label = stack.getShortSliceLabel(currentSlice); String colon = s.equals("")?"":":"; if (label!=null && !label.equals("")) s += colon+label; else s += colon+currentSlice; } } return s; } /** Writes the last row in the system results table to the Results window. */ public void displayResults() { int counter = rt.getCounter(); if (counter==1) IJ.setColumnHeadings(rt.getColumnHeadings()); IJ.write(rt.getRowAsString(counter-1)); } /** Redisplays the results table. */ public void updateHeadings() { rt.show("Results"); } /** Converts a number to a formatted string with a tab at the end. */ public String n(double n) { String s; if (Math.round(n)==n) s = ResultsTable.d2s(n,0); else s = ResultsTable.d2s(n,precision); return s+"\t"; } void incrementCounter() { //counter++; if (rt==null) rt = systemRT; rt.incrementCounter(); unsavedMeasurements = true; } public void summarize() { rt = systemRT; if (rt.getCounter()==0) return; if (summarized) rt.show("Results"); measurements = systemMeasurements; min = new StringBuffer(100); max = new StringBuffer(100); mean = new StringBuffer(100); sd = new StringBuffer(100); min.append("Min\t"); max.append("Max\t"); mean.append("Mean\t"); sd.append("SD\t"); if ((measurements&LABELS)!=0) { min.append("\t"); max.append("\t"); mean.append("\t"); sd.append("\t"); } summarizeAreas(); if ((measurements&ELLIPSE)==0) { int index = rt.getColumnIndex("Angle"); if (rt.columnExists(index)) add2(index); } int index = rt.getColumnIndex("Length"); if (rt.columnExists(index)) add2(index); TextPanel tp = IJ.getTextPanel(); if (tp!=null) { String worksheetHeadings = tp.getColumnHeadings(); if (worksheetHeadings.equals("")) IJ.setColumnHeadings(rt.getColumnHeadings()); } IJ.write(""); String meanS = new String(mean); String sdS = new String(sd); String minS = new String(min); String maxS =new String(max); if (meanS.endsWith("\t")) meanS = meanS.substring(0, meanS.length()-1); if (sdS.endsWith("\t")) sdS = sdS.substring(0, sdS.length()-1); if (minS.endsWith("\t")) minS = minS.substring(0, minS.length()-1); if (maxS.endsWith("\t")) maxS = maxS.substring(0, maxS.length()-1); IJ.write(meanS); IJ.write(sdS); IJ.write(minS); IJ.write(maxS); IJ.write(""); mean = null; sd = null; min = null; max = null; summarized = true; } void summarizeAreas() { if ((measurements&AREA)!=0) add2(ResultsTable.AREA); if ((measurements&MEAN)!=0) add2(ResultsTable.MEAN); if ((measurements&STD_DEV)!=0) add2(ResultsTable.STD_DEV); if ((measurements&MODE)!=0) add2(ResultsTable.MODE); if ((measurements&MIN_MAX)!=0) { if (showMin) add2(ResultsTable.MIN); add2(ResultsTable.MAX); } if ((measurements&CENTROID)!=0) { add2(ResultsTable.X_CENTROID); add2(ResultsTable.Y_CENTROID); } if ((measurements&CENTER_OF_MASS)!=0) { add2(ResultsTable.X_CENTER_OF_MASS); add2(ResultsTable.Y_CENTER_OF_MASS); } if ((measurements&PERIMETER)!=0) add2(ResultsTable.PERIMETER); if ((measurements&RECT)!=0) { add2(ResultsTable.ROI_X); add2(ResultsTable.ROI_Y); add2(ResultsTable.ROI_WIDTH); add2(ResultsTable.ROI_HEIGHT); } if ((measurements&ELLIPSE)!=0) { add2(ResultsTable.MAJOR); add2(ResultsTable.MINOR); add2(ResultsTable.ANGLE); } if ((measurements&SHAPE_DESCRIPTORS)!=0) add2(ResultsTable.CIRCULARITY); if ((measurements&FERET)!=0) add2(ResultsTable.FERET); if ((measurements&INTEGRATED_DENSITY)!=0) { add2(ResultsTable.INTEGRATED_DENSITY); add2(ResultsTable.RAW_INTEGRATED_DENSITY); } if ((measurements&MEDIAN)!=0) add2(ResultsTable.MEDIAN); if ((measurements&SKEWNESS)!=0) add2(ResultsTable.SKEWNESS); if ((measurements&KURTOSIS)!=0) add2(ResultsTable.KURTOSIS); if ((measurements&AREA_FRACTION)!=0) add2(ResultsTable.AREA_FRACTION); if ((measurements&STACK_POSITION)!=0) { int index = rt.getColumnIndex("Ch"); if (rt.columnExists(index)) add2(index); index = rt.getColumnIndex("Slice"); if (rt.columnExists(index)) add2(index); index = rt.getColumnIndex("Frame"); if (rt.columnExists(index)) add2(index); } if ((measurements&FERET)!=0) { add2(ResultsTable.FERET_X); add2(ResultsTable.FERET_Y); add2(ResultsTable.FERET_ANGLE); add2(ResultsTable.MIN_FERET); } if ((measurements&SHAPE_DESCRIPTORS)!=0) { add2(ResultsTable.ASPECT_RATIO); add2(ResultsTable.ROUNDNESS); add2(ResultsTable.SOLIDITY); } } private void add2(int column) { float[] c = column>=0?rt.getColumn(column):null; if (c!=null) { ImageProcessor ip = new FloatProcessor(c.length, 1, c, null); if (ip==null) return; ImageStatistics stats = new FloatStatistics(ip); if (stats==null) return; mean.append(n(stats.mean)); min.append(n(stats.min)); max.append(n(stats.max)); sd.append(n(stats.stdDev)); } else { mean.append("-\t"); min.append("-\t"); max.append("-\t"); sd.append("-\t"); } } /** Returns the current measurement count. */ public static int getCounter() { return systemRT.getCounter(); } /** Sets the measurement counter to zero. Displays a dialog that allows the user to save any existing measurements. Returns false if the user cancels the dialog. */ public synchronized static boolean resetCounter() { TextPanel tp = IJ.isResultsWindow()?IJ.getTextPanel():null; int counter = systemRT.getCounter(); int lineCount = tp!=null?IJ.getTextPanel().getLineCount():0; ImageJ ij = IJ.getInstance(); boolean macro = (IJ.macroRunning()&&!switchingModes) || Interpreter.isBatchMode(); switchingModes = false; if (counter>0 && lineCount>0 && unsavedMeasurements && !macro && ij!=null && !ij.quitting()) { YesNoCancelDialog d = new YesNoCancelDialog(ij, "ImageJ", "Save "+counter+" measurements?"); if (d.cancelPressed()) return false; else if (d.yesPressed()) { if (!(new MeasurementsWriter()).save("")) return false; } } umeans = null; systemRT.reset(); unsavedMeasurements = false; if (tp!=null) tp.clear(); summarized = false; return true; } public static void setUnsavedMeasurements(boolean b) { unsavedMeasurements = b; } // Returns the measurement options defined in the Set Measurements dialog. */ public static int getMeasurements() { return systemMeasurements; } /** Sets the system-wide measurement options. */ public static void setMeasurements(int measurements) { systemMeasurements = measurements; } /** Sets the specified system-wide measurement option. */ public static void setMeasurement(int option, boolean state) { if (state) systemMeasurements |= option; else systemMeasurements &= ~option; } /** Called once when ImageJ quits. */ public static void savePreferences(Properties prefs) { prefs.put(MEASUREMENTS, Integer.toString(systemMeasurements)); prefs.put(MARK_WIDTH, Integer.toString(markWidth)); prefs.put(PRECISION, Integer.toString(precision)); } /** Returns an array containing the first 20 uncalibrated means. */ public static float[] getUMeans() { return umeans; } /** Returns the default results table. This table should only be displayed in a the "Results" window. */ public static ResultsTable getResultsTable() { return systemRT; } /** Returns the number of digits displayed to the right of decimal point. */ public static int getPrecision() { return precision; } /** Sets the number of digits displayed to the right of decimal point. */ public static void setPrecision(int decimalPlaces) { if (decimalPlaces<0) decimalPlaces = 0; if (decimalPlaces>9) decimalPlaces = 9; precision = decimalPlaces; } /** Returns an updated Y coordinate based on the current "Invert Y Coordinates" flag. */ public static int updateY(int y, int imageHeight) { if ((systemMeasurements&INVERT_Y)!=0) y = imageHeight-y-1; return y; } /** Returns an updated Y coordinate based on the current "Invert Y Coordinates" flag. */ public static double updateY(double y, int imageHeight) { if ((systemMeasurements&INVERT_Y)!=0) y = imageHeight-y-1; return y; } /** Sets the default headings ("Area", "Mean", etc.). */ public static void setDefaultHeadings() { systemRT.setDefaultHeadings(); } public static void setOption(String option, boolean b) { if (option.indexOf("min")!=-1) showMin = b; } public static void setResultsTable(ResultsTable rt) { if (rt==null) rt = new ResultsTable(); systemRT = rt; } }