package ij.gui; import java.awt.*; import ij.*; import ij.process.*; import ij.util.*; import ij.measure.*; import ij.plugin.Straightener; /** Creates a density profile plot of a rectangular selection or line selection. */ public class ProfilePlot { static final int MIN_WIDTH = 350; static final double ASPECT_RATIO = 0.5; private double min, max; private boolean minAndMaxCalculated; private static double fixedMin = Prefs.getDouble("pp.min",0.0); private static double fixedMax = Prefs.getDouble("pp.max",0.0); protected ImagePlus imp; protected double[] profile; protected double magnification; protected double xInc; protected String units; protected String yLabel; protected float[] xValues; public ProfilePlot() { } public ProfilePlot(ImagePlus imp) { this(imp, false); } public ProfilePlot(ImagePlus imp, boolean averageHorizontally) { this.imp = imp; Roi roi = imp.getRoi(); if (roi==null) { IJ.error("Profile Plot", "Selection required."); return; } int roiType = roi.getType(); if (!(roi.isLine() || roiType==Roi.RECTANGLE)) { IJ.error("Line or rectangular selection required."); return; } Calibration cal = imp.getCalibration(); xInc = cal.pixelWidth; units = cal.getUnits(); yLabel = cal.getValueUnit(); ImageProcessor ip = imp.getProcessor(); //ip.setCalibrationTable(cal.getCTable()); if (roiType==Roi.LINE) profile = getStraightLineProfile(roi, cal, ip); else if (roiType==Roi.POLYLINE || roiType==Roi.FREELINE) { int lineWidth = (int)Math.round(roi.getStrokeWidth()); if (lineWidth==1) profile = getIrregularProfile(roi, ip, cal); else profile = getWideLineProfile(imp, lineWidth); } else if (averageHorizontally) profile = getRowAverageProfile(roi.getBounds(), cal, ip); else profile = getColumnAverageProfile(roi.getBounds(), ip); ip.setCalibrationTable(null); ImageCanvas ic = imp.getCanvas(); if (ic!=null) magnification = ic.getMagnification(); else magnification = 1.0; } //void calibrate(Calibration cal) { // float[] cTable = cal.getCTable(); // if (cTable!=null) // for () // profile[i] = profile[i]; // //} /** Returns the size of the plot that createWindow() creates. */ public Dimension getPlotSize() { if (profile==null) return null; int width = (int)(profile.length*magnification); int height = (int)(width*ASPECT_RATIO); if (width<MIN_WIDTH) { width = MIN_WIDTH; height = (int)(width*ASPECT_RATIO); } Dimension screen = IJ.getScreenSize(); int maxWidth = Math.min(screen.width-200, 1000); if (width>maxWidth) { width = maxWidth; height = (int)(width*ASPECT_RATIO); } return new Dimension(width, height); } /** Displays this profile plot in a window. */ public void createWindow() { Plot plot = getPlot(); if (plot==null) return; plot.setSourceImageID(imp.getID()); plot.show(); } Plot getPlot() { if (profile==null) return null; Dimension d = getPlotSize(); String xLabel = "Distance ("+units+")"; int n = profile.length; if (xValues==null) { xValues = new float[n]; for (int i=0; i<n; i++) xValues[i] = (float)(i*xInc); } float[] yValues = new float[n]; for (int i=0; i<n; i++) yValues[i] = (float)profile[i]; boolean fixedYScale = fixedMin!=0.0 || fixedMax!=0.0; Plot plot = new Plot("Plot of "+getShortTitle(imp), xLabel, yLabel, xValues, yValues); if (fixedYScale) { double[] a = Tools.getMinMax(xValues); plot.setLimits(a[0],a[1],fixedMin,fixedMax); } return plot; } String getShortTitle(ImagePlus imp) { String title = imp.getTitle(); int index = title.lastIndexOf('.'); if (index>0 && (title.length()-index)<=5) title = title.substring(0, index); return title; } /** Returns the profile plot data. */ public double[] getProfile() { return profile; } /** Returns the calculated minimum value. */ public double getMin() { if (!minAndMaxCalculated) findMinAndMax(); return min; } /** Returns the calculated maximum value. */ public double getMax() { if (!minAndMaxCalculated) findMinAndMax(); return max; } /** Sets the y-axis min and max. Specify (0,0) to autoscale. */ public static void setMinAndMax(double min, double max) { fixedMin = min; fixedMax = max; IJ.register(ProfilePlot.class); } /** Returns the profile plot y-axis min. Auto-scaling is used if min=max=0. */ public static double getFixedMin() { return fixedMin; } /** Returns the profile plot y-axis max. Auto-scaling is used if min=max=0. */ public static double getFixedMax() { return fixedMax; } double[] getStraightLineProfile(Roi roi, Calibration cal, ImageProcessor ip) { ip.setInterpolate(PlotWindow.interpolate); Line line = (Line)roi; double[] values = line.getPixels(); if (values==null) return null; if (cal!=null && cal.pixelWidth!=cal.pixelHeight) { double dx = cal.pixelWidth*(line.x2 - line.x1); double dy = cal.pixelHeight*(line.y2 - line.y1); double length = Math.round(Math.sqrt(dx*dx + dy*dy)); if (values.length>1) xInc = length/(values.length-1); } return values; } double[] getRowAverageProfile(Rectangle rect, Calibration cal, ImageProcessor ip) { double[] profile = new double[rect.height]; double[] aLine; ip.setInterpolate(false); for (int x=rect.x; x<rect.x+rect.width; x++) { aLine = ip.getLine(x, rect.y, x, rect.y+rect.height-1); for (int i=0; i<rect.height; i++) profile[i] += aLine[i]; } for (int i=0; i<rect.height; i++) profile[i] /= rect.width; if (cal!=null) xInc = cal.pixelHeight; return profile; } double[] getColumnAverageProfile(Rectangle rect, ImageProcessor ip) { double[] profile = new double[rect.width]; double[] aLine; ip.setInterpolate(false); for (int y=rect.y; y<rect.y+rect.height; y++) { aLine = ip.getLine(rect.x, y, rect.x+rect.width-1, y); for (int i=0; i<rect.width; i++) profile[i] += aLine[i]; } for (int i=0; i<rect.width; i++) profile[i] /= rect.height; return profile; } double[] getIrregularProfile(Roi roi, ImageProcessor ip, Calibration cal) { boolean interpolate = PlotWindow.interpolate; boolean calcXValues = cal!=null && cal.pixelWidth!=cal.pixelHeight; int n = ((PolygonRoi)roi).getNCoordinates(); int[] x = ((PolygonRoi)roi).getXCoordinates(); int[] y = ((PolygonRoi)roi).getYCoordinates(); Rectangle r = roi.getBounds(); int xbase = r.x; int ybase = r.y; double length = 0.0; double segmentLength; int xdelta, ydelta, iLength; double[] segmentLengths = new double[n]; int[] dx = new int[n]; int[] dy = new int[n]; for (int i=0; i<(n-1); i++) { xdelta = x[i+1] - x[i]; ydelta = y[i+1] - y[i]; segmentLength = Math.sqrt(xdelta*xdelta+ydelta*ydelta); length += segmentLength; segmentLengths[i] = segmentLength; dx[i] = xdelta; dy[i] = ydelta; } double[] values = new double[(int)length]; if (calcXValues) xValues = new float[(int)length]; double leftOver = 1.0; double distance = 0.0; int index; double oldrx=0.0, oldry=0.0, xvalue=0.0; for (int i=0; i<n; i++) { double len = segmentLengths[i]; if (len==0.0) continue; double xinc = dx[i]/len; double yinc = dy[i]/len; double start = 1.0-leftOver; double rx = xbase+x[i]+start*xinc; double ry = ybase+y[i]+start*yinc; double len2 = len - start; int n2 = (int)len2; for (int j=0; j<=n2; j++) { index = (int)distance+j; //IJ.log(i+" "+index+" "+distance+" "+j); if (index<values.length) { if (interpolate) values[index] = ip.getInterpolatedValue(rx, ry); else values[index] = ip.getPixelValue((int)(rx+0.5), (int)(ry+0.5)); if (calcXValues && index>0) { double deltax = cal.pixelWidth*(rx-oldrx); double deltay = cal.pixelHeight*(ry-oldry); xvalue += Math.sqrt(deltax*deltax + deltay*deltay); xValues[index] = (float)xvalue; } oldrx = rx; oldry=ry; } rx += xinc; ry += yinc; } distance += len; leftOver = len2 - n2; } return values; } double[] getWideLineProfile(ImagePlus imp, int lineWidth) { Roi roi = (Roi)imp.getRoi().clone(); ImageProcessor ip2 = (new Straightener()).straightenLine(imp, lineWidth); int width = ip2.getWidth(); int height = ip2.getHeight(); profile = new double[width]; double[] aLine; ip2.setInterpolate(false); for (int y=0; y<height; y++) { aLine = ip2.getLine(0, y, width-1, y); for (int i=0; i<width; i++) profile[i] += aLine[i]; } for (int i=0; i<width; i++) profile[i] /= height; imp.setRoi(roi); if (roi.getType()==Roi.POLYLINE&& !((PolygonRoi)roi).isSplineFit()) { ((PolygonRoi)roi).fitSpline(); imp.draw(); } return profile; } void findMinAndMax() { if (profile==null) return; double min = Double.MAX_VALUE; double max = -Double.MAX_VALUE; double value; for (int i=0; i<profile.length; i++) { value = profile[i]; if (value<min) min=value; if (value>max) max=value; } this.min = min; this.max = max; } }