| /*====================================================================* |
| - Copyright (C) 2001 Leptonica. All rights reserved. |
| - This software is distributed in the hope that it will be |
| - useful, but with NO WARRANTY OF ANY KIND. |
| - No author or distributor accepts responsibility to anyone for the |
| - consequences of using this software, or for whether it serves any |
| - particular purpose or works at all, unless he or she says so in |
| - writing. Everyone is granted permission to copy, modify and |
| - redistribute this source code, for commercial or non-commercial |
| - purposes, with the following restrictions: (1) the origin of this |
| - source code must not be misrepresented; (2) modified versions must |
| - be plainly marked as such; and (3) this notice may not be removed |
| - or altered from any source or modified source distribution. |
| *====================================================================*/ |
| |
| /* |
| * conncomp.c |
| * |
| * Connected component counting and extraction, using Heckbert's |
| * stack-based filling algorithm. |
| * |
| * 4- and 8-connected components: counts, bounding boxes and images |
| * |
| * Top-level calls: |
| * BOXA *pixConnComp() |
| * BOXA *pixConnCompPixa() |
| * BOXA *pixConnCompBB() |
| * l_int32 pixCountConnComp() |
| * |
| * Identify the next c.c. to be erased: |
| * l_int32 nextOnPixelInRaster() |
| * l_int32 nextOnPixelInRasterLow() |
| * |
| * Erase the c.c., saving the b.b.: |
| * BOX *pixSeedfillBB() |
| * BOX *pixSeedfill4BB() |
| * BOX *pixSeedfill8BB() |
| * |
| * Just erase the c.c.: |
| * l_int32 pixSeedfill() |
| * l_int32 pixSeedfill4() |
| * l_int32 pixSeedfill8() |
| * |
| * Static stack helper functions for single raster line seedfill: |
| * static void pushFillsegBB() |
| * static void pushFillseg() |
| * static void popFillseg() |
| * |
| * The basic method in pixConnCompBB() is very simple. We scan the |
| * image in raster order, looking for the next ON pixel. When it |
| * is found, we erase it and every pixel of the 4- or 8-connected |
| * component to which it belongs, using Heckbert's seedfill |
| * algorithm. As pixels are erased, we keep track of the |
| * minimum rectangle that encloses all erased pixels; after |
| * the connected component has been erased, we save its |
| * bounding box in an array of boxes. When all pixels in the |
| * image have been erased, we have an array that describes every |
| * 4- or 8-connected component in terms of its bounding box. |
| * |
| * pixConnCompPixa() is a slight variation on pixConnCompBB(), |
| * where we additionally save an array of images (in a Pixa) |
| * of each of the 4- or 8-connected components. This is done trivially |
| * by maintaining two temporary images. We erase a component from one, |
| * and use the bounding box to extract the pixels within the b.b. |
| * from each of the two images. An XOR between these subimages |
| * gives the erased component. Then we erase the component from the |
| * second image using the XOR again, with the extracted component |
| * placed on the second image at the location of the bounding box. |
| * Rasterop does all the work. At the end, we have an array |
| * of the 4- or 8-connected components, as well as an array of the |
| * bounding boxes that describe where they came from in the original image. |
| * |
| * If you just want the number of connected components, pixCountConnComp() |
| * is a bit faster than pixConnCompBB(), because it doesn't have to |
| * keep track of the bounding rectangles for each c.c. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include "allheaders.h" |
| |
| |
| /* |
| * The struct FillSeg is used by the Heckbert seedfill algorithm to |
| * hold information about image segments that are waiting to be |
| * investigated. We use two Stacks, one to hold the FillSegs in use, |
| * and an auxiliary Stack as a reservoir to hold FillSegs for re-use. |
| */ |
| struct FillSeg |
| { |
| l_int32 xleft; /* left edge of run */ |
| l_int32 xright; /* right edge of run */ |
| l_int32 y; /* run y */ |
| l_int32 dy; /* parent segment direction: 1 above, -1 below) */ |
| }; |
| typedef struct FillSeg FILLSEG; |
| |
| |
| /* Static accessors for FillSegs on a stack */ |
| static void pushFillsegBB(L_STACK *lstack, l_int32 xleft, l_int32 xright, |
| l_int32 y, l_int32 dy, l_int32 ymax, |
| l_int32 *pminx, l_int32 *pmaxx, |
| l_int32 *pminy, l_int32 *pmaxy); |
| static void pushFillseg(L_STACK *lstack, l_int32 xleft, l_int32 xright, |
| l_int32 y, l_int32 dy, l_int32 ymax); |
| static void popFillseg(L_STACK *lstack, l_int32 *pxleft, l_int32 *pxright, |
| l_int32 *py, l_int32 *pdy); |
| |
| |
| #ifndef NO_CONSOLE_IO |
| #define DEBUG 0 |
| #endif /* ~NO_CONSOLE_IO */ |
| |
| |
| /*-----------------------------------------------------------------------* |
| * Bounding boxes of 4 Connected Components * |
| *-----------------------------------------------------------------------*/ |
| /*! |
| * pixConnComp() |
| * |
| * Input: pixs (1 bpp) |
| * &pixa (<optional return> pixa of each c.c.) |
| * connectivity (4 or 8) |
| * Return: boxa, or null on error |
| * |
| * Notes: |
| * (1) This is the top-level call for getting bounding boxes or |
| * a pixa of the components, and it can be used instead |
| * of either pixConnCompBB() or pixConnCompPixa(), rsp. |
| */ |
| BOXA * |
| pixConnComp(PIX *pixs, |
| PIXA **ppixa, |
| l_int32 connectivity) |
| { |
| |
| PROCNAME("pixConnComp"); |
| |
| if (ppixa) *ppixa = NULL; |
| if (!pixs) |
| return (BOXA *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 1) |
| return (BOXA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); |
| if (connectivity != 4 && connectivity != 8) |
| return (BOXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); |
| |
| if (!ppixa) |
| return pixConnCompBB(pixs, connectivity); |
| else |
| return pixConnCompPixa(pixs, ppixa, connectivity); |
| } |
| |
| |
| /*! |
| * pixConnCompPixa() |
| * |
| * Input: pixs (1 bpp) |
| * &pixa (<return> pixa of each c.c.) |
| * connectivity (4 or 8) |
| * Return: boxa, or null on error |
| * |
| * Notes: |
| * (1) This finds bounding boxes of 4- or 8-connected components |
| * in a binary image, and saves images of each c.c |
| * in a pixa array. |
| * (2) It sets up 2 temporary pix, and for each c.c. that is |
| * located in raster order, it erases the c.c. from one pix, |
| * then uses the b.b. to extract the c.c. from the two pix using |
| * an XOR, and finally erases the c.c. from the second pix. |
| * (3) A clone of the returned boxa (where all boxes in the array |
| * are clones) is inserted into the pixa. |
| * (4) If the input is valid, this always returns a boxa and a pixa. |
| * If pixs is empty, the boxa and pixa will be empty. |
| */ |
| BOXA * |
| pixConnCompPixa(PIX *pixs, |
| PIXA **ppixa, |
| l_int32 connectivity) |
| { |
| l_int32 h, iszero; |
| l_int32 x, y, xstart, ystart; |
| PIX *pixt1, *pixt2, *pixt3, *pixt4; |
| PIXA *pixa; |
| BOX *box; |
| BOXA *boxa; |
| L_STACK *lstack, *auxstack; |
| |
| PROCNAME("pixConnCompPixa"); |
| |
| if (!ppixa) |
| return (BOXA *)ERROR_PTR("&pixa not defined", procName, NULL); |
| *ppixa = NULL; |
| if (!pixs || pixGetDepth(pixs) != 1) |
| return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); |
| if (connectivity != 4 && connectivity != 8) |
| return (BOXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); |
| |
| pixa = pixaCreate(0); |
| *ppixa = pixa; |
| pixZero(pixs, &iszero); |
| if (iszero) |
| return boxaCreate(1); /* return empty boxa */ |
| |
| if ((pixt1 = pixCopy(NULL, pixs)) == NULL) |
| return (BOXA *)ERROR_PTR("pixt1 not made", procName, NULL); |
| if ((pixt2 = pixCopy(NULL, pixs)) == NULL) |
| return (BOXA *)ERROR_PTR("pixt2 not made", procName, NULL); |
| |
| h = pixGetHeight(pixs); |
| if ((lstack = lstackCreate(h)) == NULL) |
| return (BOXA *)ERROR_PTR("lstack not made", procName, NULL); |
| if ((auxstack = lstackCreate(0)) == NULL) |
| return (BOXA *)ERROR_PTR("auxstack not made", procName, NULL); |
| lstack->auxstack = auxstack; |
| if ((boxa = boxaCreate(0)) == NULL) |
| return (BOXA *)ERROR_PTR("boxa not made", procName, NULL); |
| |
| xstart = 0; |
| ystart = 0; |
| while (1) |
| { |
| if (!nextOnPixelInRaster(pixt1, xstart, ystart, &x, &y)) |
| break; |
| |
| if ((box = pixSeedfillBB(pixt1, lstack, x, y, connectivity)) == NULL) |
| return (BOXA *)ERROR_PTR("box not made", procName, NULL); |
| boxaAddBox(boxa, box, L_INSERT); |
| |
| /* Save the c.c. and remove from pixt2 as well */ |
| pixt3 = pixClipRectangle(pixt1, box, NULL); |
| pixt4 = pixClipRectangle(pixt2, box, NULL); |
| pixXor(pixt3, pixt3, pixt4); |
| pixRasterop(pixt2, box->x, box->y, box->w, box->h, PIX_SRC ^ PIX_DST, |
| pixt3, 0, 0); |
| pixaAddPix(pixa, pixt3, L_INSERT); |
| pixDestroy(&pixt4); |
| |
| xstart = x; |
| ystart = y; |
| } |
| |
| #if DEBUG |
| pixCountPixels(pixt1, &iszero, NULL); |
| fprintf(stderr, "Number of remaining pixels = %d\n", iszero); |
| pixWrite("junkremain", pixt1, IFF_PNG); |
| #endif /* DEBUG */ |
| |
| /* Remove old boxa of pixa and replace with a clone copy */ |
| boxaDestroy(&pixa->boxa); |
| pixa->boxa = boxaCopy(boxa, L_CLONE); |
| |
| /* Cleanup, freeing the fillsegs on each stack */ |
| lstackDestroy(&lstack, TRUE); |
| pixDestroy(&pixt1); |
| pixDestroy(&pixt2); |
| |
| return boxa; |
| } |
| |
| |
| /*! |
| * pixConnCompBB() |
| * |
| * Input: pixs (1 bpp) |
| * connectivity (4 or 8) |
| * Return: boxa, or null on error |
| * |
| * Notes: |
| * (1) Finds bounding boxes of 4- or 8-connected components |
| * in a binary image. |
| * (2) This works on a copy of the input pix. The c.c. are located |
| * in raster order and erased one at a time. In the process, |
| * the b.b. is computed and saved. |
| */ |
| BOXA * |
| pixConnCompBB(PIX *pixs, |
| l_int32 connectivity) |
| { |
| l_int32 h, iszero; |
| l_int32 x, y, xstart, ystart; |
| PIX *pixt; |
| BOX *box; |
| BOXA *boxa; |
| L_STACK *lstack, *auxstack; |
| |
| PROCNAME("pixConnCompBB"); |
| |
| if (!pixs || pixGetDepth(pixs) != 1) |
| return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); |
| if (connectivity != 4 && connectivity != 8) |
| return (BOXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); |
| |
| pixZero(pixs, &iszero); |
| if (iszero) |
| return boxaCreate(1); /* return empty boxa */ |
| |
| if ((pixt = pixCopy(NULL, pixs)) == NULL) |
| return (BOXA *)ERROR_PTR("pixt not made", procName, NULL); |
| |
| h = pixGetHeight(pixs); |
| if ((lstack = lstackCreate(h)) == NULL) |
| return (BOXA *)ERROR_PTR("lstack not made", procName, NULL); |
| if ((auxstack = lstackCreate(0)) == NULL) |
| return (BOXA *)ERROR_PTR("auxstack not made", procName, NULL); |
| lstack->auxstack = auxstack; |
| if ((boxa = boxaCreate(0)) == NULL) |
| return (BOXA *)ERROR_PTR("boxa not made", procName, NULL); |
| |
| xstart = 0; |
| ystart = 0; |
| while (1) |
| { |
| if (!nextOnPixelInRaster(pixt, xstart, ystart, &x, &y)) |
| break; |
| |
| if ((box = pixSeedfillBB(pixt, lstack, x, y, connectivity)) == NULL) |
| return (BOXA *)ERROR_PTR("box not made", procName, NULL); |
| boxaAddBox(boxa, box, L_INSERT); |
| |
| xstart = x; |
| ystart = y; |
| } |
| |
| #if DEBUG |
| pixCountPixels(pixt, &iszero, NULL); |
| fprintf(stderr, "Number of remaining pixels = %d\n", iszero); |
| pixWrite("junkremain", pixt1, IFF_PNG); |
| #endif /* DEBUG */ |
| |
| /* Cleanup, freeing the fillsegs on each stack */ |
| lstackDestroy(&lstack, TRUE); |
| pixDestroy(&pixt); |
| |
| return boxa; |
| } |
| |
| |
| /*! |
| * pixCountConnComp() |
| * |
| * Input: pixs (1 bpp) |
| * connectivity (4 or 8) |
| * &count (<return> |
| * Return: 0 if OK, 1 on error |
| * |
| * Notes: |
| * (1) This is the top-level call for getting the number of |
| * 4- or 8-connected components in a 1 bpp image. |
| * (2) It works on a copy of the input pix. The c.c. are located |
| * in raster order and erased one at a time. |
| */ |
| l_int32 |
| pixCountConnComp(PIX *pixs, |
| l_int32 connectivity, |
| l_int32 *pcount) |
| { |
| l_int32 h, iszero; |
| l_int32 x, y, xstart, ystart; |
| PIX *pixt; |
| L_STACK *lstack, *auxstack; |
| |
| PROCNAME("pixCountConnComp"); |
| |
| if (!pcount) |
| return ERROR_INT("&count not defined", procName, 1); |
| *pcount = 0; /* initialize the count to 0 */ |
| if (!pixs || pixGetDepth(pixs) != 1) |
| return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); |
| if (connectivity != 4 && connectivity != 8) |
| return ERROR_INT("connectivity not 4 or 8", procName, 1); |
| |
| pixZero(pixs, &iszero); |
| if (iszero) |
| return 0; |
| |
| if ((pixt = pixCopy(NULL, pixs)) == NULL) |
| return ERROR_INT("pixt not made", procName, 1); |
| |
| h = pixGetDepth(pixs); |
| if ((lstack = lstackCreate(h)) == NULL) |
| return ERROR_INT("lstack not made", procName, 1); |
| if ((auxstack = lstackCreate(0)) == NULL) |
| return ERROR_INT("auxstack not made", procName, 1); |
| lstack->auxstack = auxstack; |
| |
| xstart = 0; |
| ystart = 0; |
| while (1) |
| { |
| if (!nextOnPixelInRaster(pixt, xstart, ystart, &x, &y)) |
| break; |
| |
| pixSeedfill(pixt, lstack, x, y, connectivity); |
| (*pcount)++; |
| xstart = x; |
| ystart = y; |
| } |
| |
| /* Cleanup, freeing the fillsegs on each stack */ |
| lstackDestroy(&lstack, TRUE); |
| pixDestroy(&pixt); |
| |
| return 0; |
| } |
| |
| |
| /*! |
| * nextOnPixelInRaster() |
| * |
| * Input: pixs (1 bpp) |
| * xstart, ystart (starting point for search) |
| * &x, &y (<return> coord value of next ON pixel) |
| * Return: 1 if a pixel is found; 0 otherwise or on error |
| */ |
| l_int32 |
| nextOnPixelInRaster(PIX *pixs, |
| l_int32 xstart, |
| l_int32 ystart, |
| l_int32 *px, |
| l_int32 *py) |
| { |
| l_int32 w, h, d, wpl; |
| l_uint32 *data; |
| |
| PROCNAME("nextOnPixelInRaster"); |
| |
| if (!pixs) |
| return ERROR_INT("pixs not defined", procName, 0); |
| pixGetDimensions(pixs, &w, &h, &d); |
| if (d != 1) |
| return ERROR_INT("pixs not 1 bpp", procName, 0); |
| |
| wpl = pixGetWpl(pixs); |
| data = pixGetData(pixs); |
| return nextOnPixelInRasterLow(data, w, h, wpl, xstart, ystart, px, py); |
| } |
| |
| |
| l_int32 |
| nextOnPixelInRasterLow(l_uint32 *data, |
| l_int32 w, |
| l_int32 h, |
| l_int32 wpl, |
| l_int32 xstart, |
| l_int32 ystart, |
| l_int32 *px, |
| l_int32 *py) |
| { |
| l_int32 i, x, y, xend, startword; |
| l_uint32 *line, *pword; |
| |
| /* Look at the first word */ |
| line = data + ystart * wpl; |
| pword = line + (xstart / 32); |
| if (*pword) { |
| xend = xstart - (xstart % 32) + 31; |
| for (x = xstart; x <= xend && x < w; x++) { |
| if (GET_DATA_BIT(line, x)) { |
| *px = x; |
| *py = ystart; |
| return 1; |
| } |
| } |
| } |
| |
| /* Continue with the rest of the line */ |
| startword = (xstart / 32) + 1; |
| x = 32 * startword; |
| for (pword = line + startword; x < w; pword++, x += 32) { |
| if (*pword) { |
| for (i = 0; i < 32 && x < w; i++, x++) { |
| if (GET_DATA_BIT(line, x)) { |
| *px = x; |
| *py = ystart; |
| return 1; |
| } |
| } |
| } |
| } |
| |
| /* Continue with following lines */ |
| for (y = ystart + 1; y < h; y++) { |
| line = data + y * wpl; |
| for (pword = line, x = 0; x < w; pword++, x += 32) { |
| if (*pword) { |
| for (i = 0; i < 32 && x < w; i++, x++) { |
| if (GET_DATA_BIT(line, x)) { |
| *px = x; |
| *py = y; |
| return 1; |
| } |
| } |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| |
| /*! |
| * pixSeedfillBB() |
| * |
| * Input: pixs (1 bpp) |
| * lstack (for holding fillsegs) |
| * x,y (location of seed pixel) |
| * connectivity (4 or 8) |
| * Return: box or null on error |
| * |
| * Notes: |
| * (1) This is the high-level interface to Paul Heckbert's |
| * stack-based seedfill algorithm. |
| */ |
| BOX * |
| pixSeedfillBB(PIX *pixs, |
| L_STACK *lstack, |
| l_int32 x, |
| l_int32 y, |
| l_int32 connectivity) |
| { |
| BOX *box; |
| |
| PROCNAME("pixSeedfillBB"); |
| |
| if (!pixs || pixGetDepth(pixs) != 1) |
| return (BOX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); |
| if (!lstack) |
| return (BOX *)ERROR_PTR("lstack not defined", procName, NULL); |
| if (connectivity != 4 && connectivity != 8) |
| return (BOX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); |
| |
| if (connectivity == 4) { |
| if ((box = pixSeedfill4BB(pixs, lstack, x, y)) == NULL) |
| return (BOX *)ERROR_PTR("box not made", procName, NULL); |
| } |
| else if (connectivity == 8) { |
| if ((box = pixSeedfill8BB(pixs, lstack, x, y)) == NULL) |
| return (BOX *)ERROR_PTR("box not made", procName, NULL); |
| } |
| else |
| return (BOX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); |
| |
| return box; |
| } |
| |
| |
| /*! |
| * pixSeedfill4BB() |
| * |
| * Input: pixs (1 bpp) |
| * lstack (for holding fillsegs) |
| * x,y (location of seed pixel) |
| * Return: box or null on error. |
| * |
| * Notes: |
| * (1) This is Paul Heckbert's stack-based 4-cc seedfill algorithm. |
| * (2) This operates on the input 1 bpp pix to remove the fg seed |
| * pixel, at (x,y), and all pixels that are 4-connected to it. |
| * The seed pixel at (x,y) must initially be ON. |
| * (3) Returns the bounding box of the erased 4-cc component. |
| * (4) Reference: see Paul Heckbert's stack-based seed fill algorithm |
| * in "Graphic Gems", ed. Andrew Glassner, Academic |
| * Press, 1990. The algorithm description is given |
| * on pp. 275-277; working C code is on pp. 721-722.) |
| * The code here follows Heckbert's exactly, except |
| * we use function calls instead of macros for |
| * pushing data on and popping data off the stack. |
| * This makes sense to do because Heckbert's fixed-size |
| * stack with macros is dangerous: images exist that |
| * will overrun the stack and crash. The stack utility |
| * here grows dynamically as needed, and the fillseg |
| * structures that are not in use are stored in another |
| * stack for reuse. It should be noted that the |
| * overhead in the function calls (vs. macros) is negligible. |
| */ |
| BOX * |
| pixSeedfill4BB(PIX *pixs, |
| L_STACK *lstack, |
| l_int32 x, |
| l_int32 y) |
| { |
| l_int32 w, h, xstart, wpl, x1, x2, dy; |
| l_int32 xmax, ymax; |
| l_int32 minx, maxx, miny, maxy; /* for bounding box of this c.c. */ |
| l_uint32 *data, *line; |
| BOX *box; |
| |
| PROCNAME("pixSeedfill4BB"); |
| |
| if (!pixs || pixGetDepth(pixs) != 1) |
| return (BOX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); |
| if (!lstack) |
| return (BOX *)ERROR_PTR("lstack not defined", procName, NULL); |
| |
| pixGetDimensions(pixs, &w, &h, NULL); |
| xmax = w - 1; |
| ymax = h - 1; |
| data = pixGetData(pixs); |
| wpl = pixGetWpl(pixs); |
| line = data + y * wpl; |
| |
| /* Check pix value of seed; must be within the image and ON */ |
| if (x < 0 || x > xmax || y < 0 || y > ymax || (GET_DATA_BIT(line, x) == 0)) |
| return NULL; |
| |
| /* Init stack to seed: |
| * Must first init b.b. values to prevent valgrind from complaining; |
| * then init b.b. boundaries correctly to seed. */ |
| minx = miny = 100000; |
| maxx = maxy = 0; |
| pushFillsegBB(lstack, x, x, y, 1, ymax, &minx, &maxx, &miny, &maxy); |
| pushFillsegBB(lstack, x, x, y + 1, -1, ymax, &minx, &maxx, &miny, &maxy); |
| minx = maxx = x; |
| miny = maxy = y; |
| |
| while (lstackGetCount(lstack) > 0) |
| { |
| /* Pop segment off stack and fill a neighboring scan line */ |
| popFillseg(lstack, &x1, &x2, &y, &dy); |
| line = data + y * wpl; |
| |
| /* A segment of scanline y - dy for x1 <= x <= x2 was |
| * previously filled. We now explore adjacent pixels |
| * in scan line y. There are three regions: to the |
| * left of x1 - 1, between x1 and x2, and to the right of x2. |
| * These regions are handled differently. Leaks are |
| * possible expansions beyond the previous segment and |
| * going back in the -dy direction. These can happen |
| * for x < x1 - 1 and for x > x2 + 1. Any "leak" segments |
| * are plugged with a push in the -dy (opposite) direction. |
| * And any segments found anywhere are always extended |
| * in the +dy direction. */ |
| for (x = x1; x >= 0 && (GET_DATA_BIT(line, x) == 1); x--) |
| CLEAR_DATA_BIT(line,x); |
| if (x >= x1) /* pix at x1 was off and was not cleared */ |
| goto skip; |
| xstart = x + 1; |
| if (xstart < x1 - 1) /* leak on left? */ |
| pushFillsegBB(lstack, xstart, x1 - 1, y, -dy, |
| ymax, &minx, &maxx, &miny, &maxy); |
| |
| x = x1 + 1; |
| do { |
| for (; x <= xmax && (GET_DATA_BIT(line, x) == 1); x++) |
| CLEAR_DATA_BIT(line, x); |
| pushFillsegBB(lstack, xstart, x - 1, y, dy, |
| ymax, &minx, &maxx, &miny, &maxy); |
| if (x > x2 + 1) /* leak on right? */ |
| pushFillsegBB(lstack, x2 + 1, x - 1, y, -dy, |
| ymax, &minx, &maxx, &miny, &maxy); |
| skip: for (x++; x <= x2 && |
| x <= xmax && |
| (GET_DATA_BIT(line, x) == 0); x++) |
| ; |
| xstart = x; |
| } while (x <= x2 && x <= xmax); |
| } |
| |
| if ((box = boxCreate(minx, miny, maxx - minx + 1, maxy - miny + 1)) |
| == NULL) |
| return (BOX *)ERROR_PTR("box not made", procName, NULL); |
| return box; |
| } |
| |
| |
| /*! |
| * pixSeedfill8BB() |
| * |
| * Input: pixs (1 bpp) |
| * lstack (for holding fillsegs) |
| * x,y (location of seed pixel) |
| * Return: box or null on error. |
| * |
| * Notes: |
| * (1) This is Paul Heckbert's stack-based 8-cc seedfill algorithm. |
| * (2) This operates on the input 1 bpp pix to remove the fg seed |
| * pixel, at (x,y), and all pixels that are 8-connected to it. |
| * The seed pixel at (x,y) must initially be ON. |
| * (3) Returns the bounding box of the erased 8-cc component. |
| * (4) Reference: see Paul Heckbert's stack-based seed fill algorithm |
| * in "Graphic Gems", ed. Andrew Glassner, Academic |
| * Press, 1990. The algorithm description is given |
| * on pp. 275-277; working C code is on pp. 721-722.) |
| * The code here follows Heckbert's closely, except |
| * the leak checks are changed for 8 connectivity. |
| * See comments on pixSeedfill4BB() for more details. |
| */ |
| BOX * |
| pixSeedfill8BB(PIX *pixs, |
| L_STACK *lstack, |
| l_int32 x, |
| l_int32 y) |
| { |
| l_int32 w, h, xstart, wpl, x1, x2, dy; |
| l_int32 xmax, ymax; |
| l_int32 minx, maxx, miny, maxy; /* for bounding box of this c.c. */ |
| l_uint32 *data, *line; |
| BOX *box; |
| |
| PROCNAME("pixSeedfill8BB"); |
| |
| if (!pixs || pixGetDepth(pixs) != 1) |
| return (BOX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); |
| if (!lstack) |
| return (BOX *)ERROR_PTR("lstack not defined", procName, NULL); |
| |
| pixGetDimensions(pixs, &w, &h, NULL); |
| xmax = w - 1; |
| ymax = h - 1; |
| data = pixGetData(pixs); |
| wpl = pixGetWpl(pixs); |
| line = data + y * wpl; |
| |
| /* Check pix value of seed; must be ON */ |
| if (x < 0 || x > xmax || y < 0 || y > ymax || (GET_DATA_BIT(line, x) == 0)) |
| return NULL; |
| |
| /* Init stack to seed: |
| * Must first init b.b. values to prevent valgrind from complaining; |
| * then init b.b. boundaries correctly to seed. */ |
| minx = miny = 100000; |
| maxx = maxy = 0; |
| pushFillsegBB(lstack, x, x, y, 1, ymax, &minx, &maxx, &miny, &maxy); |
| pushFillsegBB(lstack, x, x, y + 1, -1, ymax, &minx, &maxx, &miny, &maxy); |
| minx = maxx = x; |
| miny = maxy = y; |
| |
| while (lstackGetCount(lstack) > 0) |
| { |
| /* Pop segment off stack and fill a neighboring scan line */ |
| popFillseg(lstack, &x1, &x2, &y, &dy); |
| line = data + y * wpl; |
| |
| /* A segment of scanline y - dy for x1 <= x <= x2 was |
| * previously filled. We now explore adjacent pixels |
| * in scan line y. There are three regions: to the |
| * left of x1, between x1 and x2, and to the right of x2. |
| * These regions are handled differently. Leaks are |
| * possible expansions beyond the previous segment and |
| * going back in the -dy direction. These can happen |
| * for x < x1 and for x > x2. Any "leak" segments |
| * are plugged with a push in the -dy (opposite) direction. |
| * And any segments found anywhere are always extended |
| * in the +dy direction. */ |
| for (x = x1 - 1; x >= 0 && (GET_DATA_BIT(line, x) == 1); x--) |
| CLEAR_DATA_BIT(line,x); |
| if (x >= x1 - 1) /* pix at x1 - 1 was off and was not cleared */ |
| goto skip; |
| xstart = x + 1; |
| if (xstart < x1) /* leak on left? */ |
| pushFillsegBB(lstack, xstart, x1 - 1, y, -dy, |
| ymax, &minx, &maxx, &miny, &maxy); |
| |
| x = x1; |
| do { |
| for (; x <= xmax && (GET_DATA_BIT(line, x) == 1); x++) |
| CLEAR_DATA_BIT(line, x); |
| pushFillsegBB(lstack, xstart, x - 1, y, dy, |
| ymax, &minx, &maxx, &miny, &maxy); |
| if (x > x2) /* leak on right? */ |
| pushFillsegBB(lstack, x2 + 1, x - 1, y, -dy, |
| ymax, &minx, &maxx, &miny, &maxy); |
| skip: for (x++; x <= x2 + 1 && |
| x <= xmax && |
| (GET_DATA_BIT(line, x) == 0); x++) |
| ; |
| xstart = x; |
| } while (x <= x2 + 1 && x <= xmax); |
| } |
| |
| if ((box = boxCreate(minx, miny, maxx - minx + 1, maxy - miny + 1)) |
| == NULL) |
| return (BOX *)ERROR_PTR("box not made", procName, NULL); |
| return box; |
| } |
| |
| |
| /*! |
| * pixSeedfill() |
| * |
| * Input: pixs (1 bpp) |
| * lstack (for holding fillsegs) |
| * x,y (location of seed pixel) |
| * connectivity (4 or 8) |
| * Return: 0 if OK, 1 on error |
| * |
| * Notes: |
| * (1) This removes the component from pixs with a fg pixel at (x,y). |
| * (2) See pixSeedfill4() and pixSeedfill8() for details. |
| */ |
| l_int32 |
| pixSeedfill(PIX *pixs, |
| L_STACK *lstack, |
| l_int32 x, |
| l_int32 y, |
| l_int32 connectivity) |
| { |
| l_int32 retval; |
| |
| PROCNAME("pixSeedfill"); |
| |
| if (!pixs || pixGetDepth(pixs) != 1) |
| return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); |
| if (!lstack) |
| return ERROR_INT("lstack not defined", procName, 1); |
| if (connectivity != 4 && connectivity != 8) |
| return ERROR_INT("connectivity not 4 or 8", procName, 1); |
| |
| if (connectivity == 4) |
| retval = pixSeedfill4(pixs, lstack, x, y); |
| else /* connectivity == 8 */ |
| retval = pixSeedfill8(pixs, lstack, x, y); |
| |
| return retval; |
| } |
| |
| |
| /*! |
| * pixSeedfill4() |
| * |
| * Input: pixs (1 bpp) |
| * lstack (for holding fillsegs) |
| * x,y (location of seed pixel) |
| * Return: 0 if OK, 1 on error |
| * |
| * Notes: |
| * (1) This is Paul Heckbert's stack-based 4-cc seedfill algorithm. |
| * (2) This operates on the input 1 bpp pix to remove the fg seed |
| * pixel, at (x,y), and all pixels that are 4-connected to it. |
| * The seed pixel at (x,y) must initially be ON. |
| * (3) Reference: see pixSeedFill4BB() |
| */ |
| l_int32 |
| pixSeedfill4(PIX *pixs, |
| L_STACK *lstack, |
| l_int32 x, |
| l_int32 y) |
| { |
| l_int32 w, h, xstart, wpl, x1, x2, dy; |
| l_int32 xmax, ymax; |
| l_uint32 *data, *line; |
| |
| PROCNAME("pixSeedfill4"); |
| |
| if (!pixs || pixGetDepth(pixs) != 1) |
| return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); |
| if (!lstack) |
| return ERROR_INT("lstack not defined", procName, 1); |
| |
| pixGetDimensions(pixs, &w, &h, NULL); |
| xmax = w - 1; |
| ymax = h - 1; |
| data = pixGetData(pixs); |
| wpl = pixGetWpl(pixs); |
| line = data + y * wpl; |
| |
| /* Check pix value of seed; must be within the image and ON */ |
| if (x < 0 || x > xmax || y < 0 || y > ymax || (GET_DATA_BIT(line, x) == 0)) |
| return 0; |
| |
| /* Init stack to seed */ |
| pushFillseg(lstack, x, x, y, 1, ymax); |
| pushFillseg(lstack, x, x, y + 1, -1, ymax); |
| |
| while (lstackGetCount(lstack) > 0) |
| { |
| /* Pop segment off stack and fill a neighboring scan line */ |
| popFillseg(lstack, &x1, &x2, &y, &dy); |
| line = data + y * wpl; |
| |
| /* A segment of scanline y - dy for x1 <= x <= x2 was |
| * previously filled. We now explore adjacent pixels |
| * in scan line y. There are three regions: to the |
| * left of x1 - 1, between x1 and x2, and to the right of x2. |
| * These regions are handled differently. Leaks are |
| * possible expansions beyond the previous segment and |
| * going back in the -dy direction. These can happen |
| * for x < x1 - 1 and for x > x2 + 1. Any "leak" segments |
| * are plugged with a push in the -dy (opposite) direction. |
| * And any segments found anywhere are always extended |
| * in the +dy direction. */ |
| for (x = x1; x >= 0 && (GET_DATA_BIT(line, x) == 1); x--) |
| CLEAR_DATA_BIT(line,x); |
| if (x >= x1) /* pix at x1 was off and was not cleared */ |
| goto skip; |
| xstart = x + 1; |
| if (xstart < x1 - 1) /* leak on left? */ |
| pushFillseg(lstack, xstart, x1 - 1, y, -dy, ymax); |
| |
| x = x1 + 1; |
| do { |
| for (; x <= xmax && (GET_DATA_BIT(line, x) == 1); x++) |
| CLEAR_DATA_BIT(line, x); |
| pushFillseg(lstack, xstart, x - 1, y, dy, ymax); |
| if (x > x2 + 1) /* leak on right? */ |
| pushFillseg(lstack, x2 + 1, x - 1, y, -dy, ymax); |
| skip: for (x++; x <= x2 && |
| x <= xmax && |
| (GET_DATA_BIT(line, x) == 0); x++) |
| ; |
| xstart = x; |
| } while (x <= x2 && x <= xmax); |
| } |
| |
| return 0; |
| } |
| |
| |
| /*! |
| * pixSeedfill8() |
| * |
| * Input: pixs (1 bpp) |
| * lstack (for holding fillsegs) |
| * x,y (location of seed pixel) |
| * Return: 0 if OK, 1 on error |
| * |
| * Notes: |
| * (1) This is Paul Heckbert's stack-based 8-cc seedfill algorithm. |
| * (2) This operates on the input 1 bpp pix to remove the fg seed |
| * pixel, at (x,y), and all pixels that are 8-connected to it. |
| * The seed pixel at (x,y) must initially be ON. |
| * (3) Reference: see pixSeedFill8BB() |
| */ |
| l_int32 |
| pixSeedfill8(PIX *pixs, |
| L_STACK *lstack, |
| l_int32 x, |
| l_int32 y) |
| { |
| l_int32 w, h, xstart, wpl, x1, x2, dy; |
| l_int32 xmax, ymax; |
| l_uint32 *data, *line; |
| |
| PROCNAME("pixSeedfill8"); |
| |
| if (!pixs || pixGetDepth(pixs) != 1) |
| return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); |
| if (!lstack) |
| return ERROR_INT("lstack not defined", procName, 1); |
| |
| pixGetDimensions(pixs, &w, &h, NULL); |
| xmax = w - 1; |
| ymax = h - 1; |
| data = pixGetData(pixs); |
| wpl = pixGetWpl(pixs); |
| line = data + y * wpl; |
| |
| /* Check pix value of seed; must be ON */ |
| if (x < 0 || x > xmax || y < 0 || y > ymax || (GET_DATA_BIT(line, x) == 0)) |
| return 0; |
| |
| /* Init stack to seed */ |
| pushFillseg(lstack, x, x, y, 1, ymax); |
| pushFillseg(lstack, x, x, y + 1, -1, ymax); |
| |
| while (lstackGetCount(lstack) > 0) |
| { |
| /* Pop segment off stack and fill a neighboring scan line */ |
| popFillseg(lstack, &x1, &x2, &y, &dy); |
| line = data + y * wpl; |
| |
| /* A segment of scanline y - dy for x1 <= x <= x2 was |
| * previously filled. We now explore adjacent pixels |
| * in scan line y. There are three regions: to the |
| * left of x1, between x1 and x2, and to the right of x2. |
| * These regions are handled differently. Leaks are |
| * possible expansions beyond the previous segment and |
| * going back in the -dy direction. These can happen |
| * for x < x1 and for x > x2. Any "leak" segments |
| * are plugged with a push in the -dy (opposite) direction. |
| * And any segments found anywhere are always extended |
| * in the +dy direction. */ |
| for (x = x1 - 1; x >= 0 && (GET_DATA_BIT(line, x) == 1); x--) |
| CLEAR_DATA_BIT(line,x); |
| if (x >= x1 - 1) /* pix at x1 - 1 was off and was not cleared */ |
| goto skip; |
| xstart = x + 1; |
| if (xstart < x1) /* leak on left? */ |
| pushFillseg(lstack, xstart, x1 - 1, y, -dy, ymax); |
| |
| x = x1; |
| do { |
| for (; x <= xmax && (GET_DATA_BIT(line, x) == 1); x++) |
| CLEAR_DATA_BIT(line, x); |
| pushFillseg(lstack, xstart, x - 1, y, dy, ymax); |
| if (x > x2) /* leak on right? */ |
| pushFillseg(lstack, x2 + 1, x - 1, y, -dy, ymax); |
| skip: for (x++; x <= x2 + 1 && |
| x <= xmax && |
| (GET_DATA_BIT(line, x) == 0); x++) |
| ; |
| xstart = x; |
| } while (x <= x2 + 1 && x <= xmax); |
| } |
| |
| return 0; |
| } |
| |
| |
| |
| /*-----------------------------------------------------------------------* |
| * Static stack helper functions: push and pop fillsegs * |
| *-----------------------------------------------------------------------*/ |
| /*! |
| * pushFillsegBB() |
| * |
| * Input: lstack |
| * xleft, xright |
| * y |
| * dy |
| * ymax, |
| * &minx (<return>) |
| * &maxx (<return>) |
| * &miny (<return>) |
| * &maxy (<return>) |
| * Return: void |
| * |
| * Notes: |
| * (1) This adds a line segment to the stack, and returns its size. |
| * (2) The auxiliary stack is used as a storage area to recycle |
| * fillsegs that are no longer in use. We only calloc new |
| * fillsegs if the auxiliary stack is empty. |
| */ |
| static void |
| pushFillsegBB(L_STACK *lstack, |
| l_int32 xleft, |
| l_int32 xright, |
| l_int32 y, |
| l_int32 dy, |
| l_int32 ymax, |
| l_int32 *pminx, |
| l_int32 *pmaxx, |
| l_int32 *pminy, |
| l_int32 *pmaxy) |
| { |
| FILLSEG *fseg; |
| L_STACK *auxstack; |
| |
| PROCNAME("pushFillsegBB"); |
| |
| if (!lstack) |
| return ERROR_VOID(procName, "lstack not defined"); |
| |
| *pminx = L_MIN(*pminx, xleft); |
| *pmaxx = L_MAX(*pmaxx, xright); |
| *pminy = L_MIN(*pminy, y); |
| *pmaxy = L_MAX(*pmaxy, y); |
| |
| if (y + dy >= 0 && y + dy <= ymax) { |
| if ((auxstack = lstack->auxstack) == NULL) |
| return ERROR_VOID("auxstack not defined", procName); |
| |
| /* Get a fillseg to use */ |
| if (lstackGetCount(auxstack) > 0) |
| fseg = (FILLSEG *)lstackRemove(auxstack); |
| else { |
| if ((fseg = (FILLSEG *)CALLOC(1, sizeof(FILLSEG))) == NULL) |
| return ERROR_VOID("fillseg not made", procName); |
| } |
| |
| fseg->xleft = xleft; |
| fseg->xright = xright; |
| fseg->y = y; |
| fseg->dy = dy; |
| lstackAdd(lstack, fseg); |
| } |
| return; |
| } |
| |
| |
| /*! |
| * pushFillseg() |
| * |
| * Input: lstack |
| * xleft, xright |
| * y |
| * dy |
| * ymax |
| * Return: void |
| * |
| * Notes: |
| * (1) This adds a line segment to the stack. |
| * (2) The auxiliary stack is used as a storage area to recycle |
| * fillsegs that are no longer in use. We only calloc new |
| * fillsegs if the auxiliary stack is empty. |
| */ |
| static void |
| pushFillseg(L_STACK *lstack, |
| l_int32 xleft, |
| l_int32 xright, |
| l_int32 y, |
| l_int32 dy, |
| l_int32 ymax) |
| { |
| FILLSEG *fseg; |
| L_STACK *auxstack; |
| |
| PROCNAME("pushFillseg"); |
| |
| if (!lstack) |
| return ERROR_VOID(procName, "lstack not defined"); |
| |
| if (y + dy >= 0 && y + dy <= ymax) { |
| if ((auxstack = lstack->auxstack) == NULL) |
| return ERROR_VOID("auxstack not defined", procName); |
| |
| /* Get a fillseg to use */ |
| if (lstackGetCount(auxstack) > 0) |
| fseg = (FILLSEG *)lstackRemove(auxstack); |
| else { |
| if ((fseg = (FILLSEG *)CALLOC(1, sizeof(FILLSEG))) == NULL) |
| return ERROR_VOID("fillseg not made", procName); |
| } |
| |
| fseg->xleft = xleft; |
| fseg->xright = xright; |
| fseg->y = y; |
| fseg->dy = dy; |
| lstackAdd(lstack, fseg); |
| } |
| return; |
| } |
| |
| |
| /*! |
| * popFillseg() |
| * |
| * Input: lstack |
| * &xleft (<return>) |
| * &xright (<return>) |
| * &y (<return>) |
| * &dy (<return>) |
| * Return: void |
| * |
| * Notes: |
| * (1) This removes a line segment from the stack, and returns its size. |
| * (2) The surplussed fillseg is placed on the auxiliary stack |
| * for future use. |
| */ |
| static void |
| popFillseg(L_STACK *lstack, |
| l_int32 *pxleft, |
| l_int32 *pxright, |
| l_int32 *py, |
| l_int32 *pdy) |
| { |
| FILLSEG *fseg; |
| L_STACK *auxstack; |
| |
| PROCNAME("popFillseg"); |
| |
| if (!lstack) |
| return ERROR_VOID("lstack not defined", procName); |
| if ((auxstack = lstack->auxstack) == NULL) |
| return ERROR_VOID("auxstack not defined", procName); |
| |
| if ((fseg = (FILLSEG *)lstackRemove(lstack)) == NULL) |
| return; |
| |
| *pxleft = fseg->xleft; |
| *pxright = fseg->xright; |
| *py = fseg->y + fseg->dy; /* this now points to the new line */ |
| *pdy = fseg->dy; |
| |
| /* Save it for re-use */ |
| lstackAdd(auxstack, fseg); |
| return; |
| } |
| |
| |
| |