/*! * \brief pixCountConnComp() * * \param[in] pixs 1 bpp * \param[in] connectivity 4 or 8 * \param[out] pcount * \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 *stack, *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); pixt = NULL; stack = NULL; pixZero(pixs, &iszero); if (iszero) return 0; if ((pixt = pixCopy(NULL, pixs)) == NULL) return ERROR_INT("pixt not made", procName, 1); h = pixGetHeight(pixs); if ((stack = lstackCreate(h)) == NULL) { L_ERROR("stack not made\n", procName); goto cleanup; } auxstack = lstackCreate(0); stack->auxstack = auxstack; xstart = 0; ystart = 0; while (1) { if (!nextOnPixelInRaster(pixt, xstart, ystart, &x, &y)) break; pixSeedfill(pixt, stack, x, y, connectivity); (*pcount)++; xstart = x; ystart = y; } /* Cleanup, freeing the fillsegs on each stack */ cleanup: lstackDestroy(&stack, TRUE); pixDestroy(&pixt); return 0; }
/*! * pixSubsampleBoundaryPixels() * * Input: pixs (1 bpp, with only boundary pixels in fg) * skip (number to skip between samples as you traverse boundary) * Return: pta, or null on error * * Notes: * (1) If skip = 0, we take all the fg pixels. * (2) We try to traverse the boundaries in a regular way. * Some pixels may be missed, and these are then subsampled * randomly with a fraction determined by 'skip'. * (3) The most natural approach is to use a depth first (stack-based) * method to find the fg pixels. However, the pixel runs are * 4-connected and there are relatively few branches. So * instead of doing a proper depth-first search, we get nearly * the same result using two nested while loops: the outer * one continues a raster-based search for the next fg pixel, * and the inner one does a reasonable job running along * each 4-connected coutour. */ PTA * pixSubsampleBoundaryPixels(PIX *pixs, l_int32 skip) { l_int32 x, y, xn, yn, xs, ys, xa, ya, count; PIX *pixt; PTA *pta; PROCNAME("pixSubsampleBoundaryPixels"); if (!pixs) return (PTA *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 1) return (PTA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); if (skip < 0) return (PTA *)ERROR_PTR("skip < 0", procName, NULL); if (skip == 0) return ptaGetPixelsFromPix(pixs, NULL); pta = ptaCreate(0); pixt = pixCopy(NULL, pixs); xs = ys = 0; while (nextOnPixelInRaster(pixt, xs, ys, &xn, &yn)) { /* new series */ xs = xn; ys = yn; /* Add first point in this series */ ptaAddPt(pta, xs, ys); /* Trace out boundary, erasing all and saving every (skip + 1)th */ x = xs; y = ys; pixSetPixel(pixt, x, y, 0); count = 0; while (adjacentOnPixelInRaster(pixt, x, y, &xa, &ya)) { x = xa; y = ya; pixSetPixel(pixt, x, y, 0); if (count == skip) { ptaAddPt(pta, x, y); count = 0; } else { count++; } } } pixDestroy(&pixt); return pta; }
/*! * \brief pixConnCompBB() * * \param[in] pixs 1 bpp * \param[in] connectivity 4 or 8 * \return boxa, or NULL on error * * <pre> * 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. * </pre> */ 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 *stack, *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); boxa = NULL; pixt = NULL; stack = 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 ((stack = lstackCreate(h)) == NULL) { L_ERROR("stack not made\n", procName); goto cleanup; } auxstack = lstackCreate(0); stack->auxstack = auxstack; boxa = boxaCreate(0); xstart = 0; ystart = 0; while (1) { if (!nextOnPixelInRaster(pixt, xstart, ystart, &x, &y)) break; if ((box = pixSeedfillBB(pixt, stack, x, y, connectivity)) == NULL) { L_ERROR("box not made\n", procName); boxaDestroy(&boxa); goto cleanup; } 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 */ cleanup: lstackDestroy(&stack, TRUE); pixDestroy(&pixt); return boxa; }
/*! * \brief pixConnCompPixa() * * \param[in] pixs 1 bpp * \param[out] ppixa pixa of each c.c. * \param[in] connectivity 4 or 8 * \return boxa, or NULL on error * * <pre> * 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. * </pre> */ BOXA * pixConnCompPixa(PIX *pixs, PIXA **ppixa, l_int32 connectivity) { l_int32 h, iszero; l_int32 x, y, xstart, ystart; PIX *pix1, *pix2, *pix3, *pix4; PIXA *pixa; BOX *box; BOXA *boxa; L_STACK *stack, *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); boxa = NULL; pix1 = pix2 = pix3 = pix4 = NULL; stack = NULL; pixZero(pixs, &iszero); if (iszero) return boxaCreate(1); /* return empty boxa */ pix1 = pixCopy(NULL, pixs); pix2 = pixCopy(NULL, pixs); if (!pix1 || !pix2) { L_ERROR("pix1 or pix2 not made\n", procName); goto cleanup; } h = pixGetHeight(pixs); if ((stack = lstackCreate(h)) == NULL) { L_ERROR("stack not made\n", procName); goto cleanup; } auxstack = lstackCreate(0); stack->auxstack = auxstack; pixa = pixaCreate(0); boxa = boxaCreate(0); xstart = 0; ystart = 0; while (1) { if (!nextOnPixelInRaster(pix1, xstart, ystart, &x, &y)) break; if ((box = pixSeedfillBB(pix1, stack, x, y, connectivity)) == NULL) { L_ERROR("box not made\n", procName); pixaDestroy(&pixa); boxaDestroy(&boxa); goto cleanup; } boxaAddBox(boxa, box, L_INSERT); /* Save the c.c. and remove from pix2 as well */ pix3 = pixClipRectangle(pix1, box, NULL); pix4 = pixClipRectangle(pix2, box, NULL); pixXor(pix3, pix3, pix4); pixRasterop(pix2, box->x, box->y, box->w, box->h, PIX_SRC ^ PIX_DST, pix3, 0, 0); pixaAddPix(pixa, pix3, L_INSERT); pixDestroy(&pix4); xstart = x; ystart = y; } #if DEBUG pixCountPixels(pix1, &iszero, NULL); fprintf(stderr, "Number of remaining pixels = %d\n", iszero); pixWrite("junkremain", pix1, IFF_PNG); #endif /* DEBUG */ /* Remove old boxa of pixa and replace with a clone copy */ boxaDestroy(&pixa->boxa); pixa->boxa = boxaCopy(boxa, L_CLONE); *ppixa = pixa; /* Cleanup, freeing the fillsegs on each stack */ cleanup: lstackDestroy(&stack, TRUE); pixDestroy(&pix1); pixDestroy(&pix2); return boxa; }