All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.integratedmodelling.utils.image.processing.Mask Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 *  Copyright (C) 2007, 2014:
 *  
 *    - Ferdinando Villa 
 *    - integratedmodelling.org
 *    - any other authors listed in @author annotations
 *
 *    All rights reserved. This file is part of the k.LAB software suite,
 *    meant to enable modular, collaborative, integrated 
 *    development of interoperable data and model components. For
 *    details, see http://integratedmodelling.org.
 *    
 *    This program is free software; you can redistribute it and/or
 *    modify it under the terms of the Affero General Public License 
 *    Version 3 or any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but without any warranty; without even the implied warranty of
 *    merchantability or fitness for a particular purpose.  See the
 *    Affero General Public License for more details.
 *  
 *     You should have received a copy of the Affero General Public License
 *     along with this program; if not, write to the Free Software
 *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *     The license is also available at: https://www.gnu.org/licenses/agpl.html
 *******************************************************************************/
package org.integratedmodelling.utils.image.processing;

import java.awt.Dimension;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.Vector;

/**
 * Binary mask of a region/object
* It is represented internally in a set of polygons as follows:
* 1) Outmost polygons are boundaries of the region
* 2) Polygons contained by boundary polygons are holes
* 3) Polygons contained by holes are boundaries
* 4) Intersected polygons are treated the same as separated ones (avoid such situations if possible as holes in intersected part are not well handled) * @author Di Zhong (Columbia University) */ public class Mask implements Serializable { /** * */ private static final long serialVersionUID = 1L; /** * Minimum perimeter when extract polygons from binary mask
* If a region/hole is smaller than MIN_LINE, it will be ignored/filled
* Default is 8 */ public static int MIN_LINE = 8; /** * Minimum deviation in polygon fitting * Default is 1.0 */ public static float MIN_ACCURACY = (float) 0.5; /** * Minimum length of an edge in polygon fitting * Default is 3 */ public static int MIN_SEGMENT = 3; private int ulx, uly, lrx, lry; private final Vector polygons = new Vector(); private final Vector holes = new Vector(); /** * Create image mask from a rectangle * @param rect input rectangle */ public Mask(Rectangle rect) { ulx = rect.x; uly = rect.y; lrx = rect.x + rect.width - 1; lry = rect.y + rect.height - 1; XPolygon poly = new XPolygon(); poly.addPoint(ulx, uly); poly.addPoint(lrx, uly); poly.addPoint(lrx, lry); poly.addPoint(ulx, lry); polygons.addElement(poly); } /** * Create image mask from a polygon * @param poly input polygon */ public Mask(XPolygon poly) { Rectangle rect = poly.getBBox(); ulx = rect.x; uly = rect.y; lrx = rect.x + rect.width - 1; lry = rect.y + rect.height - 1; polygons.addElement(poly); } /** * Create imagemask from a array of polygons * @param poly input polygon */ public Mask(XPolygon[] poly) { setPolygons(poly); } /** * Create mask from a array of polygons * @param polys input polygon */ public Mask(Vector polys) { setPolygons(polys); } /** * Create mask from a boolean array. Note only the first(scan order) region border will be found. Holes are ignored. * @param mask binary mask * @param ul uppper-left corner (null for 0,0) */ public Mask(boolean[][] mask, Point ul) { Vector tmp = fitpolys(traceInnerBorder(mask), MIN_ACCURACY, MIN_SEGMENT); if (ul != null) { for (int i = 0; i < tmp.size(); i++) { XPolygon e = tmp.elementAt(i); e.translate(ul.x, ul.y); } } setPolygons(tmp); } /** * Create mask from a float array. Pixels with value larger than * threshold are masked * @param a 2 dimensional float array * @param th threshold * @param ul uppper-left corner (null for 0,0) */ public Mask(float[][] a, double th, Point ul) { boolean[][] mask = new boolean[a.length][a[0].length]; for (int i = 0; i < a.length; i++) { for (int j = 0; j < a[0].length; j++) { if (a[i][j] >= th) { mask[i][j] = true; } } } Vector tmp = fitpolys(traceInnerBorder(mask), MIN_ACCURACY, MIN_SEGMENT); if (ul != null) { for (int i = 0; i < tmp.size(); i++) { XPolygon e = tmp.elementAt(i); e.translate(ul.x, ul.y); } } setPolygons(tmp); } private final void setPolygons(Vector polys) { XPolygon[] parr = new XPolygon[polys.size()]; polys.copyInto(parr); setPolygons(parr); } private final void setPolygons(XPolygon[] polys) { polygons.removeAllElements(); holes.removeAllElements(); if (polys.length == 0) { return; } ulx = 999999; uly = 999999; lrx = 0; lry = 0; boolean[][] tag = new boolean[polys.length][polys.length]; for (int i = 0; i < polys.length; i++) { for (int j = 0; j < i; j++) { if (i != j) { tag[i][j] = polys[j].contains(polys[i]); tag[j][i] = polys[i].contains(polys[j]); } } } for (int i = 0; i < polys.length; i++) { XPolygon e = polys[i]; boolean contained = false; for (int j = 0; j < polys.length; j++) { if (tag[i][j]) { contained = !contained; } } if (contained) { holes.addElement(e); } else { polygons.addElement(e); } Rectangle rect = e.getBBox(); if (ulx > rect.x) { ulx = rect.x; } if (uly > rect.y) { uly = rect.y; } if (lrx < rect.x + rect.width - 1) { lrx = rect.x + rect.width - 1; } if (lry < rect.y + rect.height - 1) { lry = rect.y + rect.height - 1; } } } /** * Update internal data of the mask if returned polygons are modified. * */ public void update() { setPolygons(getAllBoundary()); } /** * Remove a polygon from the mask; can be used to remove holes * @param p the polygon to be removed */ public boolean remove(Polygon p) { boolean tag = false; tag = polygons.removeElement(p); if (!tag) { tag = holes.removeElement(p); } if (tag) { if (polygons.size() == 0) { ulx = 0; uly = 0; lrx = 0; lry = 0; if (holes.size() != 0) { holes.removeAllElements(); } } else { ulx = 999999; uly = 999999; lrx = 0; lry = 0; for (int i = 0; i < polygons.size(); i++) { XPolygon e = (polygons.elementAt(i)); Rectangle rect = e.getBBox(); if (ulx > rect.x) { ulx = rect.x; } if (uly > rect.y) { uly = rect.y; } if (lrx < rect.x + rect.width - 1) { lrx = rect.x + rect.width - 1; } if (lry < rect.y + rect.height - 1) { lry = rect.y + rect.height - 1; } } } } return tag; } /** * Add a array of polygons to the mask * @param p the array of polygons to be added */ public void add(XPolygon[] p) { XPolygon[] old = getAllBoundary(); XPolygon[] tmp = new XPolygon[old.length + p.length]; System.arraycopy(old, 0, tmp, 0, old.length); System.arraycopy(p, 0, tmp, old.length, p.length); setPolygons(tmp); } /** * Get binary mask as a 2-D array of boolean
* Return null if a mask contains no polygons * Note size of output arry is the size of mask's bounding box(height&width) */ public boolean[][] getMask() { if (polygons.size() == 0) { System.err.println("-- WARNING -- : null mask!"); return null; } int width = lrx - ulx + 1; int height = lry - uly + 1; if (width <= 0 || height <= 0) { System.err.println("-- WARNING --: invalid mask at " + ulx + "," + uly + " " + lrx + "," + lry); return null; } boolean[][] mask = new boolean[height][width]; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { int x = j + ulx; int y = i + uly; for (Enumeration e = polygons.elements(); e.hasMoreElements();) { if ((e.nextElement()).contains(x, y)) { mask[i][j] = !mask[i][j]; } } for (Enumeration e = holes.elements(); e.hasMoreElements();) { if ((e.nextElement()).contains(x, y)) { mask[i][j] = !mask[i][j]; } } } } return mask; } /** * save the mask to a PGM file: 255-masked pixels; 0-unmasked pixels * @param fname output file name */ public void savePGM(String fname) { int width = lrx - ulx + 1; int height = lry - uly + 1; int s = width * height; byte[] mask = new byte[s]; for (int i = 0, ii = 0; i < height; i++) { for (int j = 0; j < width; j++, ii++) { for (Enumeration e = polygons.elements(); e.hasMoreElements();) { if ((e.nextElement()).contains(j + ulx, i + uly)) { mask[ii] = (mask[ii] == (byte) 0) ? (byte) 255 : (byte) 0; } } for (Enumeration e = holes.elements(); e.hasMoreElements();) { if ((e.nextElement()).contains(j + ulx, i + uly)) { mask[ii] = (mask[ii] == (byte) 0) ? (byte) 255 : (byte) 0; } } } } try { FileOutputStream out = new FileOutputStream(fname); ImageIO.savePGM(out, mask, width, height); out.close(); } catch (IOException e) { System.err.println(e.getMessage()); } } /** * save a given binary array to a PGM file: 255-masked pixels; 0-unmasked pixels * @param fname output file name * @param m the binary mask */ public static void savePGM(String fname, boolean[][] m) { int width = m[0].length; int height = m.length; int s = width * height; byte[] mask = new byte[s]; for (int i = 0, ii = 0; i < height; i++) { for (int j = 0; j < width; j++, ii++) { mask[ii] = m[i][j] ? (byte) 255 : (byte) 0; } } try { FileOutputStream out = new FileOutputStream(fname); ImageIO.savePGM(out, mask, width, height); out.close(); } catch (IOException e) { System.err.println(e.getMessage()); } } /** * Save a binary array to a PGM file: 255-masked pixels; 0-unmasked pixels * @param fname output file name * @param m the binary mask * @param ul upper-left conner of the binary mask in frame * @param frame frame size */ public static void savePGM(String fname, boolean[][] m, Point ul, Dimension frame) { int width = m[0].length; int height = m.length; int s = frame.width * frame.height; byte[] mask = new byte[s]; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { if (i + ul.y < 0 || i + ul.y > frame.height - 1 || j + ul.x < 0 || j + ul.x > frame.width - 1) { continue; } int ii = (i + ul.y) * frame.width + j + ul.x; mask[ii] = m[i][j] ? (byte) 255 : (byte) 0; } } try { FileOutputStream out = new FileOutputStream(fname); ImageIO.savePGM(out, mask, frame.width, frame.height); out.close(); } catch (IOException e) { System.err.println(e.getMessage()); } } /** * Get outer border polygons of mask;
* Note returned polygons are references to internal representation, thus * modification of returned polygons will change the mask * */ public XPolygon[] getOutBoundary() { XPolygon[] p = new XPolygon[polygons.size()]; polygons.copyInto(p); return p; } /** * Get hole polygons of mask ;
* Note returned polygons are references to internal representation, thus * modification of returned polygons will change the mask */ public XPolygon[] getInBoundary() { XPolygon[] p = new XPolygon[holes.size()]; holes.copyInto(p); return p; } /** * Get border&hole polygons of the mask;
* Note returned polygons are references to internal representation, thus * modification of returned polygons will change the mask * */ public XPolygon[] getAllBoundary() { XPolygon[] p = new XPolygon[holes.size() + polygons.size()]; polygons.copyInto(p); int i = polygons.size(); for (Enumeration e = holes.elements(); e.hasMoreElements();) { p[i++] = (e.nextElement()); } return p; } /** * Get upper-left corner of the bounding box */ public Point getUL() { return new Point(ulx, uly); } /** * Get bounding box */ public Rectangle getBoundingBox() { return new Rectangle(ulx, uly, lrx - ulx + 1, lry - uly + 1); } /** * Test if a point is inside the bounding box * @param x x-coordinate relative to image (0,0) * @param y y-coordinate relative to image (0,0) */ public final boolean insideBoundingBox(int x, int y) { if (x < ulx || x > lrx || y < uly || y > lry) { return false; } return true; } /** * Test if a point is masked * @param x x-coordinate relative to image (0,0) * @param y y-coordinate relative to image (0,0) * * see Mask#xy */ public final boolean imgxy(int x, int y) { if (x < ulx || x > lrx || y < uly || y > lry) { return false; } boolean contained = false; for (Enumeration e = polygons.elements(); e.hasMoreElements();) { if ((e.nextElement()).contains(x, y)) { contained = !contained; } } for (Enumeration e = holes.elements(); e.hasMoreElements();) { if ((e.nextElement()).contains(x, y)) { contained = !contained; } } return contained; } /** * Test if a point is masked * @param x x-coordinate relative to upperleft corner of bounding box * @param y y-coordinate relative to upperleft corner of bounding box * * see Mask#imgxy */ public final boolean xy(int x, int y) { boolean contained = false; x += ulx; y += uly; for (Enumeration e = polygons.elements(); e.hasMoreElements();) { if ((e.nextElement()).contains(x, y)) { contained = !contained; } } for (Enumeration e = holes.elements(); e.hasMoreElements();) { if ((e.nextElement()).contains(x, y)) { contained = !contained; } } return contained; } /** * empty a filled binary mask to get its boundaries * @param mask original binary mask * * see Mask#link **/ public static final boolean[][] empty(boolean[][] mask) { int h = mask.length; int w = mask[0].length; boolean[][] nmask = new boolean[h][w]; for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { if (mask[i][j]) { if (i == 0 || i == h - 1 || j == 0 || j == w - 1) { nmask[i][j] = true; } else if ((!mask[i][j - 1]) || (!mask[i][j + 1]) || (!mask[i - 1][j]) || (!mask[i + 1][j])) { nmask[i][j] = true; } } } } return nmask; } private static boolean[][] cur_mask; /** * Scan the binary mask and link adjacent non-zero points into lists. * Note that the mask is modified as scanned edge points are zeroed. * Lists are returned in an array of polygons * * @param mask binary mask of an edge map * @param minlength minimum perimeter of returned polygons * * @see Mask#empty **/ public static final Vector link(boolean[][] mask, int minlength) { Vector edges = new Vector(); cur_mask = mask; for (int r = 0; r < cur_mask.length; r++) { for (int c = 0; c < cur_mask[0].length; c++) { if (mask[r][c]) { track(r, c, edges, minlength); } } } cur_mask = null; return edges; } /** * Start tracking an edge from the given position. As each point is added * to an edge, zero its pixel in the image. After the end of the edge * is reached, then reverse points in the array and start tracking from * the beginning in the other direction. Finally, the new edge is added to * the VEdges structure. **/ private static final void track(int row, int col, Vector edges, int minlength) { XPolygon pointlist = new XPolygon(); Point p = new Point(col, row); /* Put first point in PointData array. */ pointlist.addPoint(p.x, p.y); cur_mask[p.y][p.x] = false; /* Track to end of connected points in first direction. */ while (nextPoint(p)) { pointlist.addPoint(p.x, p.y); cur_mask[p.y][p.x] = false; } /* Reverse list of points by exchanging pairs of points. */ int pcn = pointlist.npoints; for (int i = 0; i < pcn / 2; i++) { Point tmp = new Point(pointlist.xpoints[i], pointlist.ypoints[i]); pointlist.xpoints[i] = pointlist.xpoints[pcn - i - 1]; pointlist.ypoints[i] = pointlist.ypoints[pcn - i - 1]; pointlist.xpoints[pcn - i - 1] = tmp.x; pointlist.ypoints[pcn - i - 1] = tmp.y; } /* Start tracking from original point in opposite direction. */ p.y = row; p.x = col; while (nextPoint(p)) { pointlist.addPoint(p.x, p.y); cur_mask[p.y][p.x] = false; } /* Add this edge to the set of edges. */ if (pointlist.npoints >= minlength) { edges.addElement(pointlist); } } /** * Given the location of an image point, find the best edge continuation * from this point to one of its neighbors. Return the position * of the new point. **/ private final static boolean nextPoint(Point p) { int i, r, c; /* These arrays give the row and column offsets for each of the eight neighbors of a point, starting with those that are 4-connected. */ int[] roff = { 1, 0, -1, 0, 1, 1, -1, -1 }; int[] coff = { 0, 1, 0, -1, 1, -1, -1, 1 }; /* Check each of the neighbors for a pixel above the low threshold. */ for (i = 0; i < 8; i++) { r = p.y + roff[i]; c = p.x + coff[i]; if (r >= 0 && c >= 0 && r < cur_mask.length && c < cur_mask[0].length) { if (cur_mask[r][c]) { p.y += roff[i]; p.x += coff[i]; return true; } } } return false; } private static int granularity; // Current granularity private static float sqaccuracy; /** * Breaks edges into straight-line segments, essentially by recursively
* subdividing at the point of maximum deviation from a straight line
* joining the edgepoints. Based on a method described in D.G. Lowe,
* `Three-dimensional object recognition from single two-dimensional
* images', Art. Intel. 31 (1987), pp. 355-395. * * @param src an array of orginal polygons (i.e. edge lists from edge following algorithm) * @param accuracy minimum deviation (~ several pixels) * @param g minimum length of edges * * @see Mask#link */ public static Vector fitpolys(Vector src, float accuracy, int g) { Vector result = new Vector(); sqaccuracy = accuracy * accuracy; /* square is used in calcs */ granularity = g; /* Segment each edge: */ for (Enumeration en = src.elements(); en.hasMoreElements();) { XPolygon e = (en.nextElement()); Vector breakpoints = new Vector(); breakpoints.addElement(new Integer(0)); if (e.npoints <= 4 * (g + 1)) { // don't fit, use orginal vertices for (int i = 1; i < e.npoints; i++) { breakpoints.addElement(new Integer(i)); } } else { splitSegment(e, breakpoints, 0, e.npoints - 1); breakpoints.addElement(new Integer(e.npoints - 1)); } XPolygon newe = new XPolygon(); for (Enumeration e1 = breakpoints.elements(); e1.hasMoreElements();) { int b0 = (e1.nextElement()).intValue(); newe.addPoint(e.xpoints[b0], e.ypoints[b0]); } result.addElement(newe); } return result; } /** * Recursively split an edge segment into shorter segments as long as * any shorter segment has a better length/deviation ratio. * * Returns the maximum length/deviation ratio attained for the segment. */ private static final float splitSegment(XPolygon e, Vector bpoints, int first, int last) { int maxp, save_nbreakpoints = bpoints.size(); float[] sig = new float[1]; float sig1, sig2, maxsig; /* If the segment is too short, don't split it further: */ if ((last - first) <= granularity) { return (float) 0.0; } /* Find the point of maximum deviation: */ maxp = maxPoint(e, first, last, sig); /* Create subsegments with recursive calls: */ sig1 = splitSegment(e, bpoints, first, maxp); bpoints.addElement(new Integer(maxp)); sig2 = splitSegment(e, bpoints, maxp, last); maxsig = Math.max(sig1, sig2); /* If the best of lower segments is better than the current segment then retain the breakpoints defining the lower segments, otherwise drop them: */ if (maxsig > sig[0]) { return maxsig; } else { int n = bpoints.size(); while (n > save_nbreakpoints) { bpoints.removeElementAt(n - 1); n = bpoints.size(); } return sig[0]; } } /** * Return the point with maximum perpendicular distance from the line * joining first and last. Also returns the square of the length/deviation * ratio via *sigp. */ private static int maxPoint(XPolygon e, int first, int last, float[] sigp) { int x0, y0, dx, dy, d; float dev, t, maxdev, px, py; int maxp, i; x0 = e.xpoints[first]; y0 = e.ypoints[first]; dx = e.xpoints[last] - x0; dy = e.ypoints[last] - y0; d = dx * dx + dy * dy; /* Locate the point of maximum deviation from a straight line through the first and last points. */ maxdev = (float) 0.0; for (i = maxp = first + 1; i < last; i++) { px = e.xpoints[i] - x0; py = e.ypoints[i] - y0; /* Compute squared distance to the line: */ t = (dx * px + dy * py) / d; px -= dx * t; py -= dy * t; dev = px * px + py * py; if (dev > maxdev) { maxdev = dev; maxp = i; } } /* The maximum deviation is assumed to be at least a couple of pixels in size to account for limitations on measurement accuracy. */ if (maxdev < sqaccuracy) { maxdev = sqaccuracy; } /* Significance equals length over maximum deviation. We return its square, which equals length squared over maxdev value above. */ sigp[0] = d / maxdev; return maxp; } /** * Trace all region borders of a given image mask;
* Image mask may have several separated regions and may also have holes;
* Return is a list of polygons; Each polygon represents a detected border (pixelwise, no fitting) */ public static Vector traceInnerBorder(boolean[][] mask) { if (mask == null || mask.length <= 0) { System.err.println("Warning: invalid mask passed to traceInnerBorder!"); return null; } int w = mask[0].length; int h = mask.length; int[] dx = { 1, 1, 0, -1, -1, -1, 0, 1 }; int[] dy = { 0, -1, -1, -1, 0, 1, 1, 1 }; int[] dx4 = { 1, 0, -1, 0 }; int[] dy4 = { 0, -1, 0, 1 }; boolean[][] border = new boolean[h][w]; Vector blist = new Vector(); while (true) { int i, j = 0; boolean found = false; boolean hole = false; for (i = 0; (i < h) && (!found); i++) { for (j = 0; j < w; j++) { if (mask[i][j] && (i == 0 || j == 0 || i == h - 1 || j == w - 1 || (i > 0 && !mask[i - 1][j]) || (i < h - 1 && !mask[i + 1][j]) || (j > 0 && !mask[i][j - 1]) || (j < w - 1 && !mask[i][j + 1]))) { hole = false; for (Enumeration e = blist.elements(); e.hasMoreElements();) { XPolygon p = (e.nextElement()); if (p.contains(j, i)) { hole = !hole; } } if (hole) { int tx = j; int ty = i; if ((j < w - 1) && (!mask[i][j + 1])) { tx++; } else if ((i < h - 1) && (!mask[i + 1][j])) { ty++; } if ((!mask[ty][tx]) && (!border[ty][tx])) { found = false; // Test is tx,ty is contained by at least one region for (Enumeration e = blist.elements(); e.hasMoreElements();) { XPolygon p = (e.nextElement()); if (p.contains(tx, ty)) { found = true; break; } } if (found) { i = ty; j = tx; break; } } } else { if (!border[i][j]) { found = true; break; } } } } } if (!found) { break; } i--; XPolygon edge = new XPolygon(); edge.addPoint(j, i); border[i][j] = true; if (!hole) { int dir = 7; // 8-connectivity while (found) { int k; if (dir % 2 == 1) { k = (dir + 6) % 8; } else { k = (dir + 7) % 8; } found = false; for (int kk = 0; kk < 8; kk++, k = (k + 1) % 8) { int x = j + dx[k]; int y = i + dy[k]; if (x >= 0 && y >= 0 && x < w && y < h && mask[y][x]) { i = y; j = x; dir = k; found = true; break; } } if (found) { if (edge.npoints >= 2 && edge.xpoints[1] == j && edge.ypoints[1] == i && edge.xpoints[0] == edge.xpoints[edge.npoints - 1] && edge.ypoints[0] == edge.ypoints[edge.npoints - 1]) { found = false; } else { edge.addPoint(j, i); border[i][j] = true; } } if (edge.npoints > 50000) { // maybe some error, print debug info System.out.println("already have " + blist.size()); for (int ni = 0; ni < blist.size(); ni++) { XPolygon tmpedge = blist.elementAt(ni); if (mask[tmpedge.ypoints[0]][tmpedge.xpoints[0]]) { System.out.print("OutBorder: "); } else { System.out.print("Hole: "); } for (int nj = 0; nj < tmpedge.npoints; nj++) { System.out .print("(" + tmpedge.xpoints[nj] + "," + tmpedge.ypoints[nj] + ") "); } System.out.println(""); } if (mask[edge.ypoints[0]][edge.xpoints[0]]) { System.out.print("OutBorder: "); } else { System.out.print("Hole: "); } for (int nj = 0; nj < 500; nj++) { System.out.print("(" + edge.xpoints[nj] + "," + edge.ypoints[nj] + ") "); } System.out.println(""); System.exit(1); } } } else { int dir = 3; // 4-connectivity while (found) { int k; k = (dir + 3) % 4; found = false; for (int kk = 0; kk < 4; kk++, k = (k + 1) % 4) { int x = j + dx4[k]; int y = i + dy4[k]; if (x >= 0 && y >= 0 && x < w && y < h && (!mask[y][x])) { i = y; j = x; dir = k; found = true; break; } } if (found) { if (edge.npoints >= 2 && edge.xpoints[1] == j && edge.ypoints[1] == i && edge.xpoints[0] == edge.xpoints[edge.npoints - 1] && edge.ypoints[0] == edge.ypoints[edge.npoints - 1]) { found = false; } else { edge.addPoint(j, i); border[i][j] = true; } } if (edge.npoints > 50000) { // maybe some error, print debug info System.out.println("already have " + blist.size()); for (int ni = 0; ni < blist.size(); ni++) { XPolygon tmpedge = blist.elementAt(ni); if (mask[tmpedge.ypoints[0]][tmpedge.xpoints[0]]) { System.out.print("OutBorder: "); } else { System.out.print("Hole: "); } for (int nj = 0; nj < tmpedge.npoints; nj++) { System.out .print("(" + tmpedge.xpoints[nj] + "," + tmpedge.ypoints[nj] + ") "); } System.out.println(""); } if (mask[edge.ypoints[0]][edge.xpoints[0]]) { System.out.print("OutBorder: "); } else { System.out.print("Hole: "); } for (int nj = 0; nj < 500; nj++) { System.out.print("(" + edge.xpoints[nj] + "," + edge.ypoints[nj] + ") "); } System.out.println(""); System.exit(1); } } } if (edge.npoints > 2) { edge.npoints--; } blist.addElement(edge); } /* System.out.println("# of polygons (traceInnerBorder): "+blist.size()) ; */ return blist; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy