|
PolygonRoi |
|
package ij.gui; import java.awt.*; import java.awt.image.*; import ij.*; import ij.process.*; import ij.measure.*; import ij.plugin.frame.Recorder; /** This class represents a polygon region of interest or polyline of interest. */ public class PolygonRoi extends Roi { protected int maxPoints = 1000; // will be increased if necessary protected int[] xp, yp; // image coordinates relative to origin of roi bounding box protected int[] xp2, yp2; // absolute screen coordinates protected int nPoints; protected int[] xSpline,ySpline; // relative image coordinates protected int[] xScreenSpline,yScreenSpline; // absolute screen coordinates protected int splinePoints = 200; Rectangle clip; private double angle1, degrees=Double.NaN; private int xClipMin, yClipMin, xClipMax, yClipMax; private boolean userCreated; long mouseUpTime = 0; /** Creates a new polygon or polyline ROI from x and y coordinate arrays. Type must be Roi.POLYGON, Roi.FREEROI, Roi.TRACED_ROI, Roi.POLYLINE, Roi.FREELINE or Roi.ANGLE.*/ public PolygonRoi(int[] xPoints, int[] yPoints, int nPoints, int type) { super(0, 0, null); if (type==POLYGON) this.type = POLYGON; else if (type==FREEROI) this.type = FREEROI; else if (type==TRACED_ROI) this.type = TRACED_ROI; else if (type==POLYLINE) this.type = POLYLINE; else if (type==FREELINE) this.type = FREELINE; else if (type==ANGLE) this.type = ANGLE; else if (type==POINT) this.type = POINT; else throw new IllegalArgumentException("Invalid type"); maxPoints = nPoints; this.nPoints = nPoints; xp = xPoints; yp = yPoints; if (type!=TRACED_ROI) { xp = new int[nPoints]; yp = new int[nPoints]; for (int i=0; i<nPoints; i++) { xp[i] = xPoints[i]; yp[i] = yPoints[i]; } } xp2 = new int[nPoints]; yp2 = new int[nPoints]; if (type==ANGLE && nPoints==3) getAngleAsString(); finishPolygon(); } /** Creates a new polygon or polyline ROI from a Polygon. Type must be Roi.POLYGON, Roi.FREEROI, Roi.TRACED_ROI, Roi.POLYLINE, Roi.FREELINE or Roi.ANGLE.*/ public PolygonRoi(Polygon p, int type) { this(p.xpoints, p.ypoints, p.npoints, type); } /** Obsolete */ public PolygonRoi(int[] xPoints, int[] yPoints, int nPoints, ImagePlus imp, int type) { this(xPoints, yPoints, nPoints, type); setImage(imp); } /** Starts the process of creating a new user-generated polygon or polyline ROI. */ public PolygonRoi(int sx, int sy, ImagePlus imp) { super(sx, sy, imp); int tool = Toolbar.getToolId(); if (tool==Toolbar.POLYGON) type = POLYGON; else if (tool==Toolbar.ANGLE) type = ANGLE; else type = POLYLINE; xp = new int[maxPoints]; yp = new int[maxPoints]; xp2 = new int[maxPoints]; yp2 = new int[maxPoints]; nPoints = 2; x = ic.offScreenX(sx); y = ic.offScreenY(sy); width=1; height=1; clipX = x; clipY = y; clipWidth = 1; clipHeight = 1; state = CONSTRUCTING; userCreated = true; } private void drawStartBox(Graphics g) { if (type!=ANGLE) g.drawRect(ic.screenX(startX)-4, ic.screenY(startY)-4, 8, 8); } public void draw(Graphics g) { updatePolygon(); g.setColor(instanceColor!=null?instanceColor:ROIColor); if (xSpline!=null) { if (type==POLYLINE || type==FREELINE) g.drawPolyline(xScreenSpline, yScreenSpline, splinePoints); else g.drawPolygon(xScreenSpline, yScreenSpline, splinePoints); } else { if (type==POLYLINE || type==FREELINE || type==ANGLE || state==CONSTRUCTING) g.drawPolyline(xp2, yp2, nPoints); else g.drawPolygon(xp2, yp2, nPoints); if (state==CONSTRUCTING && type!=FREEROI && type!=FREELINE) drawStartBox(g); } //g.drawRect(clip.x, clip.y, clip.width, clip.height); if ((xSpline!=null||type==POLYGON||type==POLYLINE||type==ANGLE) && state!=CONSTRUCTING && clipboard==null) { if (ic!=null) mag = ic.getMagnification(); int size2 = HANDLE_SIZE/2; if (activeHandle>0) drawHandle(g, xp2[activeHandle-1]-size2, yp2[activeHandle-1]-size2); if (activeHandle<nPoints-1) drawHandle(g, xp2[activeHandle+1]-size2, yp2[activeHandle+1]-size2); handleColor=instanceColor!=null?instanceColor:ROIColor; drawHandle(g, xp2[0]-size2, yp2[0]-size2); handleColor=Color.white; for (int i=1; i<nPoints; i++) drawHandle(g, xp2[i]-size2, yp2[i]-size2); } drawPreviousRoi(g); if (!(state==MOVING_HANDLE||state==CONSTRUCTING||state==NORMAL)) showStatus(); if (updateFullWindow) {updateFullWindow = false; imp.draw();} } public void drawPixels(ImageProcessor ip) { if (xSpline!=null) { ip.moveTo(x+xSpline[0], y+ySpline[0]); for (int i=1; i<splinePoints; i++) ip.lineTo(x+xSpline[i], y+ySpline[i]); if (type==POLYGON || type==FREEROI || type==TRACED_ROI) ip.lineTo(x+xSpline[0], y+ySpline[0]); } else { ip.moveTo(x+xp[0], y+yp[0]); for (int i=1; i<nPoints; i++) ip.lineTo(x+xp[i], y+yp[i]); if (type==POLYGON || type==FREEROI || type==TRACED_ROI) ip.lineTo(x+xp[0], y+yp[0]); } if (xSpline!=null || Line.getWidth()>1) updateFullWindow = true; } protected void grow(int sx, int sy) { // Overrides grow() in Roi class } protected void updatePolygon() { if (ic==null) return; Rectangle srcRect = ic.getSrcRect(); if (ic.getMagnification()==1.0 && srcRect.x==0 && srcRect.y==0) { for (int i=0; i<nPoints; i++) { xp2[i] = xp[i]+x; yp2[i] = yp[i]+y; } } else { for (int i=0; i<nPoints; i++) { xp2[i] = ic.screenX(xp[i]+x); yp2[i] = ic.screenY(yp[i]+y); } } if (xSpline!=null) { for (int i=0; i<splinePoints; i++) { xScreenSpline[i] = ic.screenX(xSpline[i]+x); yScreenSpline[i] = ic.screenY(ySpline[i]+y); } } } void handleMouseMove(int ox, int oy) { // Do rubber banding int tool = Toolbar.getToolId(); if (!(tool==Toolbar.POLYGON || tool==Toolbar.POLYLINE || tool==Toolbar.ANGLE)) { imp.killRoi(); imp.draw(); return; } drawRubberBand(ox, oy); degrees = Double.NaN; double len = -1; if (nPoints>1) { int x1 = xp[nPoints-2]; int y1 = yp[nPoints-2]; int x2 = xp[nPoints-1]; int y2 = yp[nPoints-1]; degrees = getAngle(x1, y1, x2, y2); if (tool!=Toolbar.ANGLE) { Calibration cal = imp.getCalibration(); double pw=cal.pixelWidth, ph=cal.pixelHeight; if (IJ.altKeyDown()) {pw=1.0; ph=1.0;} len = Math.sqrt((x2-x1)*pw*(x2-x1)*pw + (y2-y1)*ph*(y2-y1)*ph); } } if (tool==Toolbar.ANGLE) { if (nPoints==2) angle1 = degrees; else if (nPoints==3) { double angle2 = getAngle(xp[1], yp[1], xp[2], yp[2]); degrees = Math.abs(180-Math.abs(angle1-angle2)); if (degrees>180.0) degrees = 360.0-degrees; } } String length = len!=-1?", length=" + IJ.d2s(len):""; String angle = !Double.isNaN(degrees)?", angle=" + IJ.d2s(degrees):""; IJ.showStatus(imp.getLocationAsString(ox,oy) + length + angle); } void drawRubberBand(int ox, int oy) { int x1 = xp[nPoints-2]+x; int y1 = yp[nPoints-2]+y; int x2 = xp[nPoints-1]+x; int y2 = yp[nPoints-1]+y; int xmin=9999, ymin=9999, xmax=0, ymax=0; if (x1<xmin) xmin=x1; if (x2<xmin) xmin=x2; if (ox<xmin) xmin=ox; if (x1>xmax) xmax=x1; if (x2>xmax) xmax=x2; if (ox>xmax) xmax=ox; if (y1<ymin) ymin=y1; if (y2<ymin) ymin=y2; if (oy<ymin) ymin=oy; if (y1>ymax) ymax=y1; if (y2>ymax) ymax=y2; if (oy>ymax) ymax=oy; //clip = new Rectangle(xmin, ymin, xmax-xmin, ymax-ymin); int margin = 4; if (ic!=null) { double mag = ic.getMagnification(); if (mag<1.0) margin = (int)(margin/mag); } xp[nPoints-1] = ox-x; yp[nPoints-1] = oy-y; imp.draw(xmin-margin, ymin-margin, (xmax-xmin)+margin*2, (ymax-ymin)+margin*2); } void finishPolygon() { Polygon poly = new Polygon(xp, yp, nPoints); Rectangle r = poly.getBounds(); x = r.x; y = r.y; width = r.width; height = r.height; if (nPoints<2 || (!(type==FREELINE||type==POLYLINE||type==ANGLE) && (nPoints<3||width==0||height==0))) { if (imp!=null) imp.killRoi(); if (type!=POINT) return; } for (int i=0; i<nPoints; i++) { xp[i] = xp[i]-x; yp[i] = yp[i]-y; } state = NORMAL; if (imp!=null && !(type==TRACED_ROI)) imp.draw(x-5, y-5, width+10, height+10); oldX=x; oldY=y; oldWidth=width; oldHeight=height; if (Recorder.record && userCreated && (type==POLYGON||type==POLYLINE||type==ANGLE)) Recorder.recordRoi(getPolygon(), type); if (type!=POINT) modifyRoi(); } protected void moveHandle(int sx, int sy) { if (clipboard!=null) return; int ox = ic.offScreenX(sx); int oy = ic.offScreenY(sy); xp[activeHandle] = ox-x; yp[activeHandle] = oy-y; if (xSpline!=null) { fitSpline(splinePoints); updateClipRect(); imp.draw(clipX, clipY, clipWidth, clipHeight); oldX = x; oldY = y; oldWidth = width; oldHeight = height; } else { resetBoundingRect(); updateClipRectAndDraw(); } String angle = type==ANGLE?getAngleAsString():""; IJ.showStatus(imp.getLocationAsString(ox,oy) + angle); } /** After handle is moved, find clip rect and repaint. */ void updateClipRectAndDraw() { int xmin=Integer.MAX_VALUE, ymin=Integer.MAX_VALUE, xmax=0, ymax=0; int x2, y2; if (activeHandle>0) {x2=x+xp[activeHandle-1]; y2=y+yp[activeHandle-1];} else {x2=x+xp[nPoints-1]; y2=y+yp[nPoints-1];} if (x2<xmin) xmin = x2; if (y2<ymin) ymin = y2; if (x2>xmax) xmax = x2; if (y2>ymax) ymax = y2; x2=x+xp[activeHandle]; y2=y+yp[activeHandle]; if (x2<xmin) xmin = x2; if (y2<ymin) ymin = y2; if (x2>xmax) xmax = x2; if (y2>ymax) ymax = y2; if (activeHandle<nPoints-1) {x2=x+xp[activeHandle+1]; y2=y+yp[activeHandle+1];} else {x2=x+xp[0]; y2=y+yp[0];} if (x2<xmin) xmin = x2; if (y2<ymin) ymin = y2; if (x2>xmax) xmax = x2; if (y2>ymax) ymax = y2; int xmin2=xmin, ymin2=ymin, xmax2=xmax, ymax2=ymax; if (xClipMin<xmin2) xmin2 = xClipMin; if (yClipMin<ymin2) ymin2 = yClipMin; if (xClipMax>xmax2) xmax2 = xClipMax; if (yClipMax>ymax2) ymax2 = yClipMax; xClipMin=xmin; yClipMin=ymin; xClipMax=xmax; yClipMax=ymax; double mag = ic.getMagnification(); int handleSize = type==POINT?HANDLE_SIZE+8:HANDLE_SIZE; int m = mag<1.0?(int)(handleSize/mag):handleSize; imp.draw(xmin2-m, ymin2-m, xmax2-xmin2+m*2, ymax2-ymin2+m*2); } void resetBoundingRect() { int xmin=Integer.MAX_VALUE, xmax=-xmin, ymin=xmin, ymax=xmax; int xx, yy; for(int i=0; i<nPoints; i++) { xx = xp[i]; if (xx<xmin) xmin=xx; if (xx>xmax) xmax=xx; yy = yp[i]; if (yy<ymin) ymin=yy; if (yy>ymax) ymax=yy; } if (xmin!=0) for (int i=0; i<nPoints; i++) xp[i] -= xmin; if (ymin!=0) for (int i=0; i<nPoints; i++) yp[i] -= ymin; //IJ.log("reset: "+ymin+" "+before+" "+yp[0]); x+=xmin; y+=ymin; width=xmax-xmin; height=ymax-ymin; } String getAngleAsString() { double angle1 = getAngle(xp[0], yp[0], xp[1], yp[1]); double angle2 = getAngle(xp[1], yp[1], xp[2], yp[2]); degrees = Math.abs(180-Math.abs(angle1-angle2)); if (degrees>180.0) degrees = 360.0-degrees; return ", angle=" + IJ.d2s(degrees); } protected void mouseDownInHandle(int handle, int sx, int sy) { if (state==CONSTRUCTING) return; int ox=ic.offScreenX(sx), oy=ic.offScreenY(sy); if (IJ.altKeyDown() && !(nPoints<=3 && type!=POINT)) { deleteHandle(ox, oy); return; } else if (IJ.shiftKeyDown() && type!=POINT) { addHandle(ox, oy); return; } state = MOVING_HANDLE; activeHandle = handle; int m = (int)(10.0/ic.getMagnification()); xClipMin=ox-m; yClipMin=oy-m; xClipMax=ox+m; yClipMax=oy+m; } void deleteHandle(int ox, int oy) { if (imp==null) return; if (nPoints<=1) {imp.killRoi(); return;} boolean splineFit = xSpline != null; xSpline = null; Polygon points = getPolygon(); modState = NO_MODS; if (previousRoi!=null) previousRoi.modState = NO_MODS; int pointToDelete = getClosestPoint(ox, oy, points); Polygon points2 = new Polygon(); for (int i=0; i<points.npoints; i++) { if (i!=pointToDelete) points2.addPoint(points.xpoints[i], points.ypoints[i]); } if (type==POINT) imp.setRoi(new PointRoi(points2.xpoints, points2.ypoints, points2.npoints)); else { imp.setRoi(new PolygonRoi(points2, type)); if (splineFit) ((PolygonRoi)imp.getRoi()).fitSpline(splinePoints); } } void addHandle(int ox, int oy) { if (imp==null || type==ANGLE) return; boolean splineFit = xSpline != null; xSpline = null; Polygon points = getPolygon(); int n = points.npoints; modState = NO_MODS; if (previousRoi!=null) previousRoi.modState = NO_MODS; int pointToDuplicate = getClosestPoint(ox, oy, points); Polygon points2 = new Polygon(); for (int i2=0; i2<n; i2++) { if (i2==pointToDuplicate) { int i1 = i2-1; if (i1==-1) i1 = isLine()?i2:n-1; int i3 = i2+1; if (i3==n) i3 = isLine()?i2:0; int x1 = points.xpoints[i1] + 2*(points.xpoints[i2] - points.xpoints[i1])/3; int y1 = points.ypoints[i1] + 2*(points.ypoints[i2] - points.ypoints[i1])/3; int x2 = points.xpoints[i2] + (points.xpoints[i3] - points.xpoints[i2])/3; int y2 = points.ypoints[i2] + (points.ypoints[i3] - points.ypoints[i2])/3; points2.addPoint(x1, y1); points2.addPoint(x2, y2); } else points2.addPoint(points.xpoints[i2], points.ypoints[i2]); } if (type==POINT) imp.setRoi(new PointRoi(points2.xpoints, points2.ypoints, points2.npoints)); else { imp.setRoi(new PolygonRoi(points2, type)); if (splineFit) ((PolygonRoi)imp.getRoi()).fitSpline(splinePoints); } } int getClosestPoint(int x, int y, Polygon points) { int index = 0; double distance = Double.MAX_VALUE; for (int i=0; i<points.npoints; i++) { double dx = points.xpoints[i] - x; double dy = points.ypoints[i] - y; double distance2 = Math.sqrt(dx*dx+dy*dy); if (distance2<distance) { distance = distance2; index = i; } } return index; } public void fitSpline(int evaluationPoints) { if (xSpline==null || splinePoints!=evaluationPoints) { splinePoints = evaluationPoints; xSpline = new int[splinePoints]; ySpline = new int[splinePoints]; xScreenSpline = new int[splinePoints]; yScreenSpline = new int[splinePoints]; } int nNodes = nPoints; if (type==POLYGON) { nNodes++; if (nNodes>=xp.length) enlargeArrays(); xp[nNodes-1] = xp[0]; yp[nNodes-1] = yp[0]; } int[] xindex = new int[nNodes]; for(int i=0; i<nNodes; i++) xindex[i] = i; SplineFitter sfx = new SplineFitter(xindex, xp, nNodes); SplineFitter sfy = new SplineFitter(xindex, yp, nNodes); // Evaluate the splines at all points double scale = (double)(nNodes-1)/(splinePoints-1); int xs=0, ys=0; int xmin=Integer.MAX_VALUE, xmax=-xmin, ymin=xmin, ymax=xmax; for(int i=0; i<splinePoints; i++) { double xvalue = i*scale; xs = (int) Math.floor(sfx.evalSpline(xindex, xp, nNodes, xvalue) + 0.5); if (xs<xmin) xmin=xs; if (xs>xmax) xmax=xs; xSpline[i] = xs; ys = (int) Math.floor(sfy.evalSpline(xindex, yp, nNodes, xvalue) + 0.5); if (ys<ymin) ymin=ys; if (ys>ymax) ymax=ys; ySpline[i] = ys; } if (xmin!=0) { for (int i=0; i<nPoints; i++) xp[i] -= xmin; for (int i=0; i<splinePoints; i++) xSpline[i] -= xmin; } if (ymin!=0) { for (int i=0; i<nPoints; i++) yp[i] -= ymin; for (int i=0; i<splinePoints; i++) ySpline[i] -= ymin; } //IJ.log("reset: "+ymin+" "+before+" "+yp[0]); x+=xmin; y+=ymin; width=xmax-xmin; height=ymax-ymin; cachedMask = null; } /* double getSplineLength() { int nNodes = nPoints; if (type==POLYGON) { nNodes++; if (nNodes==xp.length) enlargeArrays(); xp[nNodes-1] = xp[0]; yp[nNodes-1] = yp[0]; } int[] xindex = new int[nNodes]; for(int i=0; i<nNodes; i++) xindex[i] = i; SplineFitter sfx = new SplineFitter(xindex, xp, nNodes); SplineFitter sfy = new SplineFitter(xindex, yp, nNodes); double scale = (double)(nNodes-1)/(splinePoints-1); double xs=0.0, ys=0.0; double length = 0.0; for(int i=0; i<splinePoints; i++) { double xvalue = i*scale; xs = sfx.evalSpline(xindex, xp, nNodes, xvalue); ys = sfy.evalSpline(xindex, yp, nNodes, xvalue); length += Math.sqrt(xs*xs + ys*ys); } //if (type==POLYGON) return length; } */ protected void handleMouseUp(int sx, int sy) { if (state==MOVING) {state = NORMAL; return;} if (state==MOVING_HANDLE) { cachedMask = null; //mask is no longer valid state = NORMAL; updateClipRect(); oldX=x; oldY=y; oldWidth=width; oldHeight=height; return; } if (state!=CONSTRUCTING) return; if (IJ.spaceBarDown()) // is user scrolling image? return; boolean samePoint = (xp[nPoints-2]==xp[nPoints-1] && yp[nPoints-2]==yp[nPoints-1]); Rectangle biggerStartBox = new Rectangle(ic.screenX(startX)-5, ic.screenY(startY)-5, 10, 10); if (nPoints>2 && (biggerStartBox.contains(sx, sy) || (ic.offScreenX(sx)==startX && ic.offScreenY(sy)==startY) || (samePoint && (System.currentTimeMillis()-mouseUpTime)<=500))) { nPoints--; addOffset(); finishPolygon(); return; } else if (!samePoint) { mouseUpTime = System.currentTimeMillis(); if (type==ANGLE && nPoints==3) { addOffset(); finishPolygon(); return; } //add point to polygon xp[nPoints] = xp[nPoints-1]; yp[nPoints] = yp[nPoints-1]; nPoints++; if (nPoints==xp.length) enlargeArrays(); } } protected void addOffset() { for (int i=0; i<nPoints; i++) { xp[i] = xp[i]+x; yp[i] = yp[i]+y; } } public boolean contains(int x, int y) { if (!super.contains(x, y)) return false; if (xScreenSpline!=null) { Polygon poly = new Polygon(xSpline, ySpline, splinePoints); return poly.contains(x-this.x, y-this.y); } else { Polygon poly = new Polygon(xp, yp, nPoints); return poly.contains(x-this.x, y-this.y); } } /** Returns a handle number if the specified screen coordinates are inside or near a handle, otherwise returns -1. */ public int isHandle(int sx, int sy) { if (!(xSpline!=null||type==POLYGON||type==POLYLINE||type==ANGLE||type==POINT)||clipboard!=null) return -1; int size = HANDLE_SIZE+5; int halfSize = size/2; int handle = -1; int sx2, sy2; for (int i=0; i<nPoints; i++) { sx2 = xp2[i]-halfSize; sy2=yp2[i]-halfSize; if (sx>=sx2 && sx<=sx2+size && sy>=sy2 && sy<=sy2+size) { handle = i; break; } } return handle; } /** Override Roi.nudge() to support splines. */ //public void nudge(int key) { // super.nudge(key); // if (xSpline!=null) { // fitSpline(); // updateFullWindow = true; // imp.draw(); // } //} public ImageProcessor getMask() { if (cachedMask!=null && cachedMask.getPixels()!=null) return cachedMask; PolygonFiller pf = new PolygonFiller(); if (xSpline!=null) pf.setPolygon(xSpline, ySpline, splinePoints); else pf.setPolygon(xp, yp, nPoints); cachedMask = pf.getMask(width, height); return cachedMask; } /** Returns the length of this line selection after smoothing using a 3-point running average.*/ double getSmoothedLineLength() { double length = 0.0; double w2 = 1.0; double h2 = 1.0; double dx, dy; if (imp!=null) { Calibration cal = imp.getCalibration(); w2 = cal.pixelWidth*cal.pixelWidth; h2 = cal.pixelHeight*cal.pixelHeight; } double x1,x2,x3,x4,y1,y2,y3,y4; x2=xp[0]; x3=xp[0]; x4=xp[1]; y2=yp[0]; y3=yp[0]; y4=yp[1]; for (int i=0; i<(nPoints-1); i++) { x1=x2; x2=x3; x3=x4; y1=y2; y2=y3; y3=y4;; if ((i+2)<nPoints) { x4=xp[i+2]; y4=yp[i+2]; } dx = (x4-x1)/3.0; // = (x2+x3+x4)/3-(x1+x2+x3)/3 dy = (y4-y1)/3.0; // = (y2+y3+y4)/3-(y1+y2+y3)/3 length += Math.sqrt(dx*dx*w2+dy*dy*h2); } return length; } /** Returns the perimeter of this ROIs after smoothing using a 3-point running average.*/ double getSmoothedPerimeter() { double length = 0.0; double w2 = 1.0; double h2 = 1.0; double dx, dy; if (imp!=null) { Calibration cal = imp.getCalibration(); w2 = cal.pixelWidth*cal.pixelWidth; h2 = cal.pixelHeight*cal.pixelHeight; } double x1,x2,x3,x4,y1,y2,y3,y4; x2=xp[nPoints-1]; x3=xp[0]; x4=xp[1]; y2=yp[nPoints-1]; y3=yp[0]; y4=yp[1]; for (int i=0; i<(nPoints-1); i++) { x1=x2; x2=x3; x3=x4; y1=y2; y2=y3; y3=y4;; if ((i+2)<nPoints) { x4=xp[i+2]; y4=yp[i+2]; } else { x4=xp[0]; y4=yp[0]; } dx = (x4-x1)/3.0; // = (x2+x3+x4)/3-(x1+x2+x3)/3 dy = (y4-y1)/3.0; // = (y2+y3+y4)/3-(y1+y2+y3)/3 length += Math.sqrt(dx*dx*w2+dy*dy*h2); } x1=x2; x2=x3; x3=x4; x4=xp[1]; y1=y2; y2=y3; y3=y4; y4=yp[1]; dx = (x4-x1)/3.0; dy = (y4-y1)/3.0; length += Math.sqrt(dx*dx*w2+dy*dy*h2); return length; } /** Returns the perimeter length of ROIs created using the wand tool and the particle analyzer. The algorithm counts edge pixels as 1 and corner pixels as sqrt(2). It does this by calculating the total length of the ROI boundary and subtracting 2-sqrt(2) for each non-adjacent corner. For example, a 1x1 pixel ROI has a boundary length of 4 and 2 non-adjacent edges so the perimeter is 4-2*(2-sqrt(2)). A 2x2 pixel ROI has a boundary length of 8 and 4 non-adjacent edges so the perimeter is 8-4*(2-sqrt(2)). */ double getTracedPerimeter() { int sumdx = 0; int sumdy = 0; int nCorners = 0; int dx1 = xp[0] - xp[nPoints-1]; int dy1 = yp[0] - yp[nPoints-1]; int side1 = Math.abs(dx1) + Math.abs(dy1); //one of these is 0 boolean corner = false; int nexti, dx2, dy2, side2; for (int i=0; i<nPoints; i++) { nexti = i+1; if (nexti==nPoints) nexti = 0; dx2 = xp[nexti] - xp[i]; dy2 = yp[nexti] - yp[i]; sumdx += Math.abs(dx1); sumdy += Math.abs(dy1); side2 = Math.abs(dx2) + Math.abs(dy2); if (side1>1 || !corner) { corner = true; nCorners++; } else corner = false; dx1 = dx2; dy1 = dy2; side1 = side2; } double w=1.0,h=1.0; if (imp!=null) { Calibration cal = imp.getCalibration(); w = cal.pixelWidth; h = cal.pixelHeight; } return sumdx*w+sumdy*h-(nCorners*((w+h)-Math.sqrt(w*w+h*h))); } /** Returns the perimeter (for ROIs) or length (for lines).*/ public double getLength() { if (type==TRACED_ROI) return getTracedPerimeter(); if (nPoints>2) { if (type==FREEROI) return getSmoothedPerimeter(); else if (type==FREELINE && !(width==0 || height==0)) return getSmoothedLineLength(); } double length = 0.0; int dx, dy; double w2=1.0, h2=1.0; if (imp!=null) { Calibration cal = imp.getCalibration(); w2 = cal.pixelWidth*cal.pixelWidth; h2 = cal.pixelHeight*cal.pixelHeight; } if (xSpline!=null) { for (int i=0; i<(splinePoints-1); i++) { dx = xSpline[i+1]-xSpline[i]; dy = ySpline[i+1]-ySpline[i]; length += Math.sqrt(dx*dx*w2+dy*dy*h2); } if (type==POLYGON) { dx = xSpline[0]-xSpline[splinePoints-1]; dy = ySpline[0]-ySpline[splinePoints-1]; length += Math.sqrt(dx*dx*w2+dy*dy*h2); } } else { for (int i=0; i<(nPoints-1); i++) { dx = xp[i+1]-xp[i]; dy = yp[i+1]-yp[i]; length += Math.sqrt(dx*dx*w2+dy*dy*h2); } if (type==POLYGON) { dx = xp[0]-xp[nPoints-1]; dy = yp[0]-yp[nPoints-1]; length += Math.sqrt(dx*dx*w2+dy*dy*h2); } } return length; } /** Returns Feret's diameter, the greatest distance between any two points along the ROI boundary. */ public double getFeretsDiameter() { double w2=1.0, h2=1.0; if (imp!=null) { Calibration cal = imp.getCalibration(); w2 = cal.pixelWidth*cal.pixelWidth; h2 = cal.pixelHeight*cal.pixelHeight; } double diameter = 0.0, dx, dy, d; for (int i=0; i<nPoints; i++) { for (int j=i; j<nPoints; j++) { dx = xp[i] - xp[j]; dy = yp[i] - yp[j]; d = Math.sqrt(dx*dx*w2 + dy*dy*h2); if (d>diameter) diameter = d; } } return diameter; } /** Returns the angle in degrees between the first two segments of this polyline.*/ public double getAngle() { return degrees; } /** Returns the number of XY coordinates. */ public int getNCoordinates() { if (xSpline!=null) return splinePoints; else return nPoints; } /** Returns this ROI's X-coordinates, which are relative to origin of the bounding box. */ public int[] getXCoordinates() { if (xSpline!=null) return xSpline; else return xp; } /** Returns this ROI's Y-coordinates, which are relative to origin of the bounding box. */ public int[] getYCoordinates() { if (xSpline!=null) return ySpline; else return yp; } /** Returns this PolygonRoi as a Polygon. @see ij.process.ImageProcessor#setRoi @see ij.process.ImageProcessor#drawPolygon @see ij.process.ImageProcessor#fillPolygon */ public Polygon getPolygon() { int n; int[] xpoints1, ypoints1; if (xSpline!=null) { n = splinePoints; xpoints1 = xSpline; ypoints1 = ySpline; } else { n = nPoints; xpoints1 = xp; ypoints1 = yp; } int[] xpoints2 = new int[n]; int[] ypoints2 = new int[n]; for (int i=0; i<n; i++) { xpoints2[i] = xpoints1[i] + x; ypoints2[i] = ypoints1[i] + y; } return new Polygon(xpoints2, ypoints2, n); } /** Returns a copy of this PolygonRoi. */ public synchronized Object clone() { PolygonRoi r = (PolygonRoi)super.clone(); r.xp = new int[maxPoints]; r.yp = new int[maxPoints]; r.xp2 = new int[maxPoints]; r.yp2 = new int[maxPoints]; for (int i=0; i<nPoints; i++) { r.xp[i] = xp[i]; r.yp[i] = yp[i]; r.xp2[i] = xp2[i]; r.yp2[i] = yp2[i]; } if (xSpline!=null) { r.xSpline = null; r.fitSpline(splinePoints); } return r; } void enlargeArrays() { int[] xptemp = new int[maxPoints*2]; int[] yptemp = new int[maxPoints*2]; int[] xp2temp = new int[maxPoints*2]; int[] yp2temp = new int[maxPoints*2]; System.arraycopy(xp, 0, xptemp, 0, maxPoints); System.arraycopy(yp, 0, yptemp, 0, maxPoints); System.arraycopy(xp2, 0, xp2temp, 0, maxPoints); System.arraycopy(yp2, 0, yp2temp, 0, maxPoints); xp=xptemp; yp=yptemp; xp2=xp2temp; yp2=yp2temp; if (IJ.debugMode) IJ.log("PolygonRoi: "+maxPoints+" points"); maxPoints *= 2; } }
|
PolygonRoi |
|