C++程序  |  1172行  |  38 KB

/*====================================================================*
 -  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;
}