/* * pushWSPixel() * * Input: lh (priority queue) * stack (of reusable WSPixels) * val (pixel value: used for ordering the heap) * x, y (pixel coordinates) * index (label for set to which pixel belongs) * Return: void * * Notes: * (1) This is a wrapper for adding a WSPixel to a heap. It * uses the storage stack to retrieve a WSPixel. */ static void pushWSPixel(L_HEAP *lh, L_STACK *stack, l_int32 val, l_int32 x, l_int32 y, l_int32 index) { L_WSPIXEL *wsp; PROCNAME("pushWSPixel"); if (!lh) { L_ERROR("heap not defined\n", procName); return; } if (!stack) { L_ERROR("stack not defined\n", procName); return; } /* Get a wspixel to use */ if (lstackGetCount(stack) > 0) wsp = (L_WSPIXEL *) lstackRemove(stack); else wsp = (L_WSPIXEL *) CALLOC(1, sizeof(L_WSPIXEL)); wsp->val = (l_float32) val; wsp->x = x; wsp->y = y; wsp->index = index; lheapAdd(lh, wsp); return; }
/* * pushNewPixel() * * Input: lqueue * x, y (pixel coordinates) * &minx, &maxx, &miny, &maxy (<return> bounding box update) * Return: void * * Notes: * (1) This is a wrapper for adding a NewPixel to a queue, which * updates the bounding box for all pixels on that queue and * uses the storage stack to retrieve a NewPixel. */ static void pushNewPixel(L_QUEUE *lq, l_int32 x, l_int32 y, l_int32 *pminx, l_int32 *pmaxx, l_int32 *pminy, l_int32 *pmaxy) { L_NEWPIXEL *np; PROCNAME("pushNewPixel"); if (!lq) { L_ERROR("queue not defined\n", procName); return; } /* Adjust bounding box */ *pminx = L_MIN(*pminx, x); *pmaxx = L_MAX(*pmaxx, x); *pminy = L_MIN(*pminy, y); *pmaxy = L_MAX(*pmaxy, y); /* Get a newpixel to use */ if (lstackGetCount(lq->stack) > 0) np = (L_NEWPIXEL *) lstackRemove(lq->stack); else np = (L_NEWPIXEL *) CALLOC(1, sizeof(L_NEWPIXEL)); np->x = x; np->y = y; lqueueAdd(lq, np); return; }
/*! * \brief pushFillsegBB() * * \param[in] stack * \param[in] xleft, xright * \param[in] y * \param[in] dy * \param[in] ymax * \param[out] pminx minimum x * \param[out] pmaxx maximum x * \param[out] pminy minimum y * \param[out] pmaxy maximum y * \return void * * <pre> * 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. * </pre> */ static void pushFillsegBB(L_STACK *stack, 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 (!stack) { L_ERROR("stack not defined\n", procName); return; } *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 = stack->auxstack) == NULL) { L_ERROR("auxstack not defined\n", procName); return; } /* Get a fillseg to use */ if (lstackGetCount(auxstack) > 0) { fseg = (FILLSEG *)lstackRemove(auxstack); } else { if ((fseg = (FILLSEG *)LEPT_CALLOC(1, sizeof(FILLSEG))) == NULL) { L_ERROR("fillseg not made\n", procName); return; } } fseg->xleft = xleft; fseg->xright = xright; fseg->y = y; fseg->dy = dy; lstackAdd(stack, 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) { L_ERROR(procName, "lstack not defined"); return; } if (y + dy >= 0 && y + dy <= ymax) { if ((auxstack = lstack->auxstack) == NULL) { L_ERROR("auxstack not defined", procName); return; } /* Get a fillseg to use */ if (lstackGetCount(auxstack) > 0) fseg = (FILLSEG *)lstackRemove(auxstack); else { if ((fseg = (FILLSEG *)CALLOC(1, sizeof(FILLSEG))) == NULL) { L_ERROR("fillseg not made", procName); return; } } fseg->xleft = xleft; fseg->xright = xright; fseg->y = y; fseg->dy = dy; lstackAdd(lstack, fseg); } return; }
/*! * \brief pixSeedfill8() * * \param[in] pixs 1 bpp * \param[in] stack for holding fillsegs * \param[in] x,y location of seed pixel * \return 0 if OK, 1 on error * * <pre> * 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() * </pre> */ l_int32 pixSeedfill8(PIX *pixs, L_STACK *stack, 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 (!stack) return ERROR_INT("stack not defined", procName, 1); if (!stack->auxstack) stack->auxstack = lstackCreate(0); 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(stack, x, x, y, 1, ymax); pushFillseg(stack, x, x, y + 1, -1, ymax); while (lstackGetCount(stack) > 0) { /* Pop segment off stack and fill a neighboring scan line */ popFillseg(stack, &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(stack, 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(stack, xstart, x - 1, y, dy, ymax); if (x > x2) /* leak on right? */ pushFillseg(stack, 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; }
/*! * \brief pixSeedfill8BB() * * \param[in] pixs 1 bpp * \param[in] stack for holding fillsegs * \param[in] x,y location of seed pixel * \return box or NULL on error. * * <pre> * 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. * </pre> */ BOX * pixSeedfill8BB(PIX *pixs, L_STACK *stack, 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 (!stack) return (BOX *)ERROR_PTR("stack not defined", procName, NULL); if (!stack->auxstack) stack->auxstack = lstackCreate(0); 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(stack, x, x, y, 1, ymax, &minx, &maxx, &miny, &maxy); pushFillsegBB(stack, x, x, y + 1, -1, ymax, &minx, &maxx, &miny, &maxy); minx = maxx = x; miny = maxy = y; while (lstackGetCount(stack) > 0) { /* Pop segment off stack and fill a neighboring scan line */ popFillseg(stack, &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(stack, 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(stack, xstart, x - 1, y, dy, ymax, &minx, &maxx, &miny, &maxy); if (x > x2) /* leak on right? */ pushFillsegBB(stack, 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; }