/// <summary> /// Converts the specified BitMatrix into a list of Polygons. /// </summary> /// <param name='bm'> /// The BitMatrix to convert from. /// </param> public static Paths Polygonize(BitMatrix bm) { #region pixels grouping // group pixels into separate areas, keep in mind that a polygon only stores points here Paths areas = new Paths(); // current scanline BitArray currLineBuf = new BitArray(bm.width); Path[] prevLine = new Path[bm.width]; for (int i = 0; i < bm.width; i++) { prevLine[i] = null; } // the area that current pixel belongs to Path currArea, prevArea; int left; // counter for checking pixels on the left side for (int y = 0; y < bm.height; y++) { // scan current line for (int x = 0; x < bm.width; x++) { // store current pixel in the buffer currLineBuf[x] = bm.Get(x, y); // index of the left pixel left = x - 1; // pixel connects with the pixel above it if (currLineBuf[x]) { //Debug.WriteLine("{0}, {1}: active", y, x); // check pixels on top of it currArea = prevLine[x]; if (currArea != null) { //Debug.WriteLine("Join top"); // join current pixel into top area currArea.Add(new IntPoint(x, y)); // Checks the pixels on the left side // if they are connected to current pixel, // if they belong to different area // change all of them to current area, and merge that area into current area // else do nothing // else do nothing // merge left area into top one if (left >= 0) { prevArea = prevLine[left]; if (prevArea != null && prevArea != currArea) { //Debug.WriteLine("Merging left to top"); prevArea = prevLine[left]; while (left >= 0) { // update references of pixels on the left side if (prevLine[left] == prevArea) { prevLine[left] = currArea; } left--; } // merge area into current one areas.Remove(prevArea); currArea.AddRange(prevArea); } } } else { // Since no active pixel on top, check left side // if left side is active // simply join to the left area // else // create a new area if (left >= 0 && prevLine[left] != null) { //Debug.WriteLine("Join left"); prevLine[left].Add(new IntPoint(x, y)); prevLine[x] = prevLine[left]; } else { //Debug.WriteLine("Create new"); Path newArea = new Path(); newArea.Add(new IntPoint(x, y)); areas.Add(newArea); prevLine[x] = newArea; } } // pixel not activated } else { //Debug.WriteLine("{0},{1}: not active", x, y); prevLine[x] = null; } } } #endregion #region polygonize each group of pixels into contour pixels Path p; //Create Polygons from given areas for (int i = 0; i < areas.Count; i++) { // dequeue a group of points p = areas[0]; areas.RemoveAt(0); // convert to contour p = Polygonizer.FromGroup(p); // enqueue back areas.Add(p); } #endregion return(areas); }
/// <summary> /// Create Polygon from a group of points private static Path FromGroup(Path area) { Rect box = BoundBox(area); BitMatrix bm = Polygonizer.DrawPoints(area, box); // The matrix holding the points Path polygon = new Path(); // the result // find highest & leftmost point int i = 0; for (i = 0; i < bm.bits.Length; i++) { if (bm.bits[i]) { break; } i++; } IntPoint currP = new IntPoint(); IntPoint nextP = new IntPoint(); IntPoint guessP = new IntPoint(); Direction currDirBack = Direction.NE; // since the first point is uppermost & leftmost, prevDirBack can never be W Direction nextDirBack = Direction.E; // a random direction different from prevDirBack // extract the coordinate currP.X = i % bm.width; currP.Y = i / bm.width; IntPoint startP = new IntPoint(currP); // the position to look at based on the curr point // NE SE SE // NE CT SW // NW NW SW do { // make guessP the next moore neighbor switch (currDirBack) { case Direction.N: goto case Direction.NE; case Direction.NE: guessP.X = currP.X + 1; guessP.Y = currP.Y + 1; break; case Direction.E: goto case Direction.SE; case Direction.SE: guessP.X = currP.X - 1; guessP.Y = currP.Y + 1; break; case Direction.S: goto case Direction.SW; case Direction.SW: guessP.X = currP.X - 1; guessP.Y = currP.Y - 1; break; case Direction.W: goto case Direction.NW; case Direction.NW: guessP.X = currP.X + 1; guessP.Y = currP.Y - 1; break; } nextP = bm.nextNeighbour(currP, guessP); nextDirBack = BitMatrix.getDirection(nextP, currP); // only add the curr point if it is a true vertex if (nextDirBack != currDirBack) { polygon.Add(new IntPoint(currP.X + box.xMin, currP.Y + box.yMin)); currDirBack = nextDirBack; } currP = nextP; } while (nextP != startP); RDP(polygon); return(polygon); }