/*
* This plugin implements the Edit/Selection/Create Selection command.
* It is based on a proposal by Tom Larkworthy.
* Written and public domained in June 2006 by Johannes E. Schindelin
*/
package ij.plugin.filter;
import ij.IJ;
import ij.ImagePlus;
import ij.gui.Roi;
import ij.gui.ShapeRoi;
import ij.process.*;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.geom.Area;
import java.awt.geom.GeneralPath;
import java.util.ArrayList;
public class ThresholdToSelection implements PlugInFilter {
ImagePlus image;
ImageProcessor ip;
float min, max;
int w, h;
boolean showStatus;
final static double PROGRESS_FRACTION_OUTLINING = 0.9; //fraction of progress bar for the first phase (tracing outlines)
public void run(ImageProcessor ip) {
showStatus = true;
image.setRoi(convert(ip));
}
/** Returns a selection created from the thresholded pixels in the
specified image, or null if there are no thresholded pixels. */
public static Roi run(ImagePlus imp) {
ThresholdToSelection tts = new ThresholdToSelection();
return tts.convert(imp.getProcessor());
}
/** Returns a selection created from the thresholded pixels in the
specified image, or null if there are no thresholded pixels. */
public Roi convert(ImageProcessor ip) {
this.ip = ip;
min = (float)ip.getMinThreshold();
max = (float)ip.getMaxThreshold();
w = ip.getWidth();
h = ip.getHeight();
return getRoi();
}
final boolean selected(int x, int y) {
float v = ip.getf(x,y);
return v>=min && v<=max;
}
/*
* This class implements a Cartesian polygon in progress.
* The edges are supposed to be of unit length, and parallel to
* one axis.
* It is implemented as a deque to be able to add points to both
* sides.
* The points should be added such that for each pair of consecutive
* points, the inner part is on the left.
*/
static class Outline {
int[] x, y;
int first, last, reserved;
final int GROW = 10;
public Outline() {
reserved = GROW;
x = new int[reserved];
y = new int[reserved];
first = last = GROW / 2;
}
private void needs(int newCount, int offset) {
if (newCount > reserved || (offset > first)) {
if (newCount < reserved + GROW + 1)
newCount = reserved + GROW + 1;
int[] newX = new int[newCount];
int[] newY = new int[newCount];
System.arraycopy(x, 0, newX, offset, last);
System.arraycopy(y, 0, newY, offset, last);
x = newX;
y = newY;
first += offset;
last += offset;
reserved = newCount;
}
}
public void push(int x, int y) {
needs(last + 1, 0);
this.x[last] = x;
this.y[last] = y;
last++;
}
public void shift(int x, int y) {
needs(last + 1, GROW);
first--;
this.x[first] = x;
this.y[first] = y;
}
public void push(Outline o) {
int count = o.last - o.first;
needs(last + count, 0);
System.arraycopy(o.x, o.first, x, last, count);
System.arraycopy(o.y, o.first, y, last, count);
last += count;
}
public void shift(Outline o) {
int count = o.last - o.first;
needs(last + count + GROW, count + GROW);
first -= count;
System.arraycopy(o.x, o.first, x, first, count);
System.arraycopy(o.y, o.first, y, first, count);
}
public Polygon getPolygon() {
// optimize out long straight lines
int i, j=first+1;
for (i=first+1; i+1 x.length) System.err.println("ERROR!");
for (int i = first; i < last && i < x.length; i++)
res += "(" + x[i] + "," + y[i] + ")";
return res + ")";
}
}
/*
* Construct all outlines simultaneously by traversing the rows
* from top to bottom.
*
* prevRow[x + 1] indicates if the pixel at (x, y - 1) is selected.
*
* outline[x] is the outline which is currently unclosed at the
* lower right corner of the previous row.
*/
Roi getRoi() {
if (showStatus)
IJ.showStatus("Converting threshold to selection");
boolean[] prevRow, thisRow;
ArrayList polygons = new ArrayList();
Outline[] outline;
int progressInc = Math.max(h/50, 1);
prevRow = new boolean[w + 2];
thisRow = new boolean[w + 2];
outline = new Outline[w + 1];
for (int y = 0; y <= h; y++) {
boolean[] b = prevRow; prevRow = thisRow; thisRow = b;
for (int x = 0; x <= w; x++) {
if (y < h && x < w)
thisRow[x + 1] = selected(x, y);
else
thisRow[x + 1] = false;
if (thisRow[x + 1]) {
if (!prevRow[x + 1]) {
// upper edge:
// - left and right are null: new outline
// - left null: push
// - right null: shift
// - left == right: close
// - left != right: merge
if (outline[x] == null) {
if (outline[x + 1] == null) {
outline[x + 1] = outline[x] = new Outline();
outline[x].push(x + 1, y);
outline[x].push(x, y);
} else {
outline[x] = outline[x + 1];
outline[x + 1] = null;
outline[x].push(x, y);
}
} else {
if (outline[x + 1] == null) {
outline[x + 1] = outline[x];
outline[x] = null;
outline[x + 1].shift(x + 1, y);
} else if (outline[x + 1] == outline[x]) {
//System.err.println("subtract " + outline[x]);
polygons.add(outline[x].getPolygon()); // MINUS
outline[x] = outline[x + 1] = null;
} else {
outline[x].shift(outline[x + 1]);
for (int x1 = 0; x1 <= w; x1++)
if (x1 != x + 1 && outline[x1] == outline[x + 1]) {
outline[x1] = outline[x];
outline[x] = outline[x + 1] = null;
break;
}
if (outline[x] != null)
throw new RuntimeException("assertion failed");
}
}
}
if (!thisRow[x]) {
// left edge
if (outline[x] == null)
throw new RuntimeException("assertion failed!");
outline[x].push(x, y + 1);
}
} else {
if (prevRow[x + 1]) {
// lower edge
// - bot null: new outline
// - left == null: shift
// - right == null: push
// - right == left: close
// - right != left: push
if (outline[x] == null) {
if (outline[x + 1] == null) {
outline[x] = outline[x + 1] = new Outline();
outline[x].push(x, y);
outline[x].push(x + 1, y);
} else {
outline[x] = outline[x + 1];
outline[x + 1] = null;
outline[x].shift(x, y);
}
} else if (outline[x + 1] == null) {
outline[x + 1] = outline[x];
outline[x] = null;
outline[x + 1].push(x + 1, y);
} else if (outline[x + 1] == outline[x]) {
//System.err.println("add " + outline[x]);
polygons.add(outline[x].getPolygon()); // PLUS
outline[x] = outline[x + 1] = null;
} else {
outline[x].push(outline[x + 1]);
for (int x1 = 0; x1 <= w; x1++)
if (x1 != x + 1 && outline[x1] == outline[x + 1]) {
outline[x1] = outline[x];
outline[x] = outline[x + 1] = null;
break;
}
if (outline[x] != null)
throw new RuntimeException("assertion failed");
}
}
if (thisRow[x]) {
// right edge
if (outline[x] == null)
throw new RuntimeException("assertion failed");
outline[x].shift(x, y + 1);
}
}
}
if (y%progressInc==0) {
if (Thread.currentThread().isInterrupted()) return null;
if (showStatus)
IJ.showProgress(y*(PROGRESS_FRACTION_OUTLINING/h));
}
}
if (polygons.size()==0)
return null;
if (showStatus) IJ.showStatus("Converting threshold to selection...");
GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
progressInc = Math.max(polygons.size()/10, 1);
for (int i = 0; i < polygons.size(); i++) {
path.append((Polygon)polygons.get(i), false);
if (Thread.currentThread().isInterrupted()) return null;
if (showStatus && i%progressInc==0)
IJ.showProgress(PROGRESS_FRACTION_OUTLINING + i*(1.-PROGRESS_FRACTION_OUTLINING)/polygons.size());
}
ShapeRoi shape = new ShapeRoi(path);
Roi roi = shape!=null?shape.shapeToRoi():null; // try to convert to non-composite ROI
if (showStatus)
IJ.showProgress(1.0);
if (roi!=null)
return roi;
else
return shape;
}
public int setup(String arg, ImagePlus imp) {
image = imp;
return DOES_8G | DOES_16 | DOES_32 | NO_CHANGES;
}
/** Determines whether to show status messages and a progress bar */
public void showStatus(boolean showStatus) {
this.showStatus = showStatus;
}
}
Ads help maintain this website.