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