/*! * wshedRenderFill() * * Input: wshed * Return: pixd (initial image with all basins filled), or null on error */ PIX * wshedRenderFill(L_WSHED *wshed) { l_int32 i, n, level, bx, by; NUMA *na; PIX *pix, *pixd; PIXA *pixa; PROCNAME("wshedRenderFill"); if (!wshed) return (PIX *) ERROR_PTR("wshed not defined", procName, NULL); wshedBasins(wshed, &pixa, &na); pixd = pixCopy(NULL, wshed->pixs); n = pixaGetCount(pixa); for (i = 0; i < n; i++) { pix = pixaGetPix(pixa, i, L_CLONE); pixaGetBoxGeometry(pixa, i, &bx, &by, NULL, NULL); numaGetIValue(na, i, &level); pixPaintThroughMask(pixd, pix, bx, by, level); pixDestroy(&pix); } pixaDestroy(&pixa); numaDestroy(&na); return pixd; }
/*! * \brief pixConnCompTransform() * * \param[in] pixs 1 bpp * \param[in] connect connectivity: 4 or 8 * \param[in] depth of pixd: 8 or 16 bpp; use 0 for auto determination * \return pixd 8, 16 or 32 bpp, or NULL on error * * <pre> * Notes: * (1) pixd is 8, 16 or 32 bpp, and the pixel values label the * fg component, starting with 1. Pixels in the bg are labelled 0. * (2) If %depth = 0, the depth of pixd is 8 if the number of c.c. * is less than 254, 16 if the number of c.c is less than 0xfffe, * and 32 otherwise. * (3) If %depth = 8, the assigned label for the n-th component is * 1 + n % 254. We use mod 254 because 0 is uniquely assigned * to black: e.g., see pixcmapCreateRandom(). Likewise, * if %depth = 16, the assigned label uses mod(2^16 - 2), and * if %depth = 32, no mod is taken. * </pre> */ PIX * pixConnCompTransform(PIX *pixs, l_int32 connect, l_int32 depth) { l_int32 i, n, index, w, h, xb, yb, wb, hb; BOXA *boxa; PIX *pix1, *pix2, *pixd; PIXA *pixa; PROCNAME("pixConnCompTransform"); if (!pixs || pixGetDepth(pixs) != 1) return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); if (connect != 4 && connect != 8) return (PIX *)ERROR_PTR("connectivity must be 4 or 8", procName, NULL); if (depth != 0 && depth != 8 && depth != 16 && depth != 32) return (PIX *)ERROR_PTR("depth must be 0, 8, 16 or 32", procName, NULL); boxa = pixConnComp(pixs, &pixa, connect); n = pixaGetCount(pixa); boxaDestroy(&boxa); pixGetDimensions(pixs, &w, &h, NULL); if (depth == 0) { if (n < 254) depth = 8; else if (n < 0xfffe) depth = 16; else depth = 32; } pixd = pixCreate(w, h, depth); pixSetSpp(pixd, 1); if (n == 0) { /* no fg */ pixaDestroy(&pixa); return pixd; } /* Label each component and blit it in */ for (i = 0; i < n; i++) { pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb); pix1 = pixaGetPix(pixa, i, L_CLONE); if (depth == 8) { index = 1 + (i % 254); pix2 = pixConvert1To8(NULL, pix1, 0, index); } else if (depth == 16) { index = 1 + (i % 0xfffe); pix2 = pixConvert1To16(NULL, pix1, 0, index); } else { /* depth == 32 */ index = 1 + i; pix2 = pixConvert1To32(NULL, pix1, 0, index); } pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pix2, 0, 0); pixDestroy(&pix1); pixDestroy(&pix2); } pixaDestroy(&pixa); return pixd; }
/*! * pixaDisplay() * * Input: pixa * w, h (if set to 0, determines the size from the * b.b. of the components in pixa) * Return: pix, or null on error * * Notes: * (1) This uses the boxes to place each pix in the rendered composite. * (2) Set w = h = 0 to use the b.b. of the components to determine * the size of the returned pix. * (3) Uses the first pix in pixa to determine the depth. * (4) The background is written "white". On 1 bpp, each successive * pix is "painted" (adding foreground), whereas for grayscale * or color each successive pix is blitted with just the src. * (5) If the pixa is empty, returns an empty 1 bpp pix. */ PIX * pixaDisplay(PIXA *pixa, l_int32 w, l_int32 h) { l_int32 i, n, d, xb, yb, wb, hb; BOXA *boxa; PIX *pixt, *pixd; PROCNAME("pixaDisplay"); if (!pixa) return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); n = pixaGetCount(pixa); if (n == 0 && w == 0 && h == 0) return (PIX *)ERROR_PTR("no components; no size", procName, NULL); if (n == 0) { L_WARNING("no components; returning empty 1 bpp pix", procName); return pixCreate(w, h, 1); } /* If w and h not input, determine the minimum size required * to contain the origin and all c.c. */ if (w == 0 || h == 0) { boxa = pixaGetBoxa(pixa, L_CLONE); boxaGetExtent(boxa, &w, &h, NULL); boxaDestroy(&boxa); } /* Use the first pix in pixa to determine the depth. */ pixt = pixaGetPix(pixa, 0, L_CLONE); d = pixGetDepth(pixt); pixDestroy(&pixt); if ((pixd = pixCreate(w, h, d)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); if (d > 1) pixSetAll(pixd); for (i = 0; i < n; i++) { if (pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb)) { L_WARNING("no box found!", procName); continue; } pixt = pixaGetPix(pixa, i, L_CLONE); if (d == 1) pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pixt, 0, 0); else pixRasterop(pixd, xb, yb, wb, hb, PIX_SRC, pixt, 0, 0); pixDestroy(&pixt); } return pixd; }
/*! * pixaDisplayRandomCmap() * * Input: pixa (of 1 bpp components, with boxa) * w, h (if set to 0, determines the size from the * b.b. of the components in pixa) * Return: pix (8 bpp, cmapped, with random colors on the components), * or null on error * * Notes: * (1) This uses the boxes to place each pix in the rendered composite. * (2) By default, the background color is: black, cmap index 0. * This can be changed by pixcmapResetColor() */ PIX * pixaDisplayRandomCmap(PIXA *pixa, l_int32 w, l_int32 h) { l_int32 i, n, d, index, xb, yb, wb, hb; BOXA *boxa; PIX *pixs, *pixt, *pixd; PIXCMAP *cmap; PROCNAME("pixaDisplayRandomCmap"); if (!pixa) return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); n = pixaGetCount(pixa); if (n == 0) return (PIX *)ERROR_PTR("no components", procName, NULL); /* Use the first pix in pixa to verify depth is 1 bpp */ pixs = pixaGetPix(pixa, 0, L_CLONE); d = pixGetDepth(pixs); pixDestroy(&pixs); if (d != 1) return (PIX *)ERROR_PTR("components not 1 bpp", procName, NULL); /* If w and h not input, determine the minimum size required * to contain the origin and all c.c. */ if (w == 0 || h == 0) { boxa = pixaGetBoxa(pixa, L_CLONE); boxaGetExtent(boxa, &w, &h, NULL); boxaDestroy(&boxa); } /* Set up an 8 bpp dest pix, with a colormap with 254 random colors */ if ((pixd = pixCreate(w, h, 8)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); cmap = pixcmapCreateRandom(8, 1, 1); pixSetColormap(pixd, cmap); /* Color each component and blit it in */ for (i = 0; i < n; i++) { index = 1 + (i % 254); pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb); pixs = pixaGetPix(pixa, i, L_CLONE); pixt = pixConvert1To8(NULL, pixs, 0, index); pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pixt, 0, 0); pixDestroy(&pixs); pixDestroy(&pixt); } return pixd; }
/*! * pixConnCompAreaTransform() * * Input: pixs (1 bpp) * connect (connectivity: 4 or 8) * Return: pixd (16 bpp), or null on error * * Notes: * (1) The pixel values in pixd label the area of the fg component * to which the pixel belongs. Pixels in the bg are labelled 0. * (2) The pixel values cannot exceed 2^16 - 1, even if the area * of the c.c. is larger. * (3) For purposes of visualization, the output can be converted * to 8 bpp, using pixConvert16To8() or pixMaxDynamicRange(). */ PIX * pixConnCompAreaTransform(PIX *pixs, l_int32 connect) { l_int32 i, n, npix, w, h, xb, yb, wb, hb; l_int32 *tab8; BOXA *boxa; PIX *pix1, *pix2, *pixd; PIXA *pixa; PROCNAME("pixConnCompTransform"); if (!pixs || pixGetDepth(pixs) != 1) return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); if (connect != 4 && connect != 8) return (PIX *)ERROR_PTR("connectivity must be 4 or 8", procName, NULL); boxa = pixConnComp(pixs, &pixa, connect); n = pixaGetCount(pixa); boxaDestroy(&boxa); pixGetDimensions(pixs, &w, &h, NULL); pixd = pixCreate(w, h, 16); if (n == 0) { /* no fg */ pixaDestroy(&pixa); return pixd; } /* Label each component and blit it in */ tab8 = makePixelSumTab8(); for (i = 0; i < n; i++) { pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb); pix1 = pixaGetPix(pixa, i, L_CLONE); pixCountPixels(pix1, &npix, tab8); npix = L_MIN(npix, 0xffff); pix2 = pixConvert1To16(NULL, pix1, 0, npix); pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pix2, 0, 0); pixDestroy(&pix1); pixDestroy(&pix2); } pixaDestroy(&pixa); FREE(tab8); return pixd; }
jboolean Java_com_googlecode_leptonica_android_Pixa_nativeGetBoxGeometry(JNIEnv *env, jclass clazz, jint nativePixa, jint index, jintArray dimensions) { PIXA *pixa = (PIXA *) nativePixa; jint *dimensionArray = env->GetIntArrayElements(dimensions, NULL); l_int32 x, y, w, h; if (pixaGetBoxGeometry(pixa, (l_int32) index, &x, &y, &w, &h)) { return JNI_FALSE; } dimensionArray[0] = x; dimensionArray[1] = y; dimensionArray[2] = w; dimensionArray[3] = h; env->ReleaseIntArrayElements(dimensions, dimensionArray, 0); return JNI_TRUE; }
/*! * pixSaveTiledOutline() * * Input: pixs (1, 2, 4, 8, 32 bpp) * pixa (the pix are accumulated here) * scalefactor (0.0 to disable; otherwise this is a scale factor) * newrow (0 if placed on the same row as previous; 1 otherwise) * space (horizontal and vertical spacing, in pixels) * linewidth (width of added outline for image; 0 for no outline) * dp (depth of pixa; 8 or 32 bpp; only used on first call) * Return: 0 if OK, 1 on error. * * Notes: * (1) Before calling this function for the first time, use * pixaCreate() to make the @pixa that will accumulate the pix. * This is passed in each time pixSaveTiled() is called. * (2) @scalefactor scales the input image. After scaling and * possible depth conversion, the image is saved in the input * pixa, along with a box that specifies the location to * place it when tiled later. Disable saving the pix by * setting @scalefactor == 0.0. * (3) @newrow and @space specify the location of the new pix * with respect to the last one(s) that were entered. * (4) @dp specifies the depth at which all pix are saved. It can * be only 8 or 32 bpp. Any colormap is removed. This is only * used at the first invocation. * (5) This function uses two variables from call to call. * If they were static, the function would not be .so or thread * safe, and furthermore, there would be interference with two or * more pixa accumulating images at a time. Consequently, * we use the first pix in the pixa to store and obtain both * the depth and the current position of the bottom (one pixel * below the lowest image raster line when laid out using * the boxa). The bottom variable is stored in the input format * field, which is the only field available for storing an int. */ l_int32 pixSaveTiledOutline(PIX *pixs, PIXA *pixa, l_float32 scalefactor, l_int32 newrow, l_int32 space, l_int32 linewidth, l_int32 dp) { l_int32 n, top, left, bx, by, bw, w, h, depth, bottom; BOX *box; PIX *pix1, *pix2, *pix3, *pix4; PROCNAME("pixSaveTiledOutline"); if (scalefactor == 0.0) return 0; if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); if (n == 0) { bottom = 0; if (dp != 8 && dp != 32) { L_WARNING("dp not 8 or 32 bpp; using 32\n", procName); depth = 32; } else { depth = dp; } } else { /* extract the depth and bottom params from the first pix */ pix1 = pixaGetPix(pixa, 0, L_CLONE); depth = pixGetDepth(pix1); bottom = pixGetInputFormat(pix1); /* not typical usage! */ pixDestroy(&pix1); } /* Remove colormap if it exists; otherwise a copy. This * guarantees that pix4 is not a clone of pixs. */ pix1 = pixRemoveColormapGeneral(pixs, REMOVE_CMAP_BASED_ON_SRC, L_COPY); /* Scale and convert to output depth */ if (scalefactor == 1.0) { pix2 = pixClone(pix1); } else if (scalefactor > 1.0) { pix2 = pixScale(pix1, scalefactor, scalefactor); } else if (scalefactor < 1.0) { if (pixGetDepth(pix1) == 1) pix2 = pixScaleToGray(pix1, scalefactor); else pix2 = pixScale(pix1, scalefactor, scalefactor); } pixDestroy(&pix1); if (depth == 8) pix3 = pixConvertTo8(pix2, 0); else pix3 = pixConvertTo32(pix2); pixDestroy(&pix2); /* Add black outline */ if (linewidth > 0) pix4 = pixAddBorder(pix3, linewidth, 0); else pix4 = pixClone(pix3); pixDestroy(&pix3); /* Find position of current pix (UL corner plus size) */ if (n == 0) { top = 0; left = 0; } else if (newrow == 1) { top = bottom + space; left = 0; } else if (n > 0) { pixaGetBoxGeometry(pixa, n - 1, &bx, &by, &bw, NULL); top = by; left = bx + bw + space; } pixGetDimensions(pix4, &w, &h, NULL); bottom = L_MAX(bottom, top + h); box = boxCreate(left, top, w, h); pixaAddPix(pixa, pix4, L_INSERT); pixaAddBox(pixa, box, L_INSERT); /* Save the new bottom value */ pix1 = pixaGetPix(pixa, 0, L_CLONE); pixSetInputFormat(pix1, bottom); /* not typical usage! */ pixDestroy(&pix1); return 0; }
/*! * pixSaveTiledOutline() * * Input: pixs (1, 2, 4, 8, 32 bpp) * pixa (the pix are accumulated here) * reduction (0 to disable; otherwise this is a reduction factor) * newrow (0 if placed on the same row as previous; 1 otherwise) * space (horizontal and vertical spacing, in pixels) * linewidth (width of added outline for image; 0 for no outline) * dp (depth of pixa; 8 or 32 bpp; only used on first call) * Return: 0 if OK, 1 on error. * * Notes: * (1) Before calling this function for the first time, use * pixaCreate() to make the @pixa that will accumulate the pix. * This is passed in each time pixSaveTiled() is called. * (2) @reduction is the integer reduction factor for the input * image. After reduction and possible depth conversion, * the image is saved in the input pixa, along with a box * that specifies the location to place it when tiled later. * Disable saving the pix by setting reduction == 0. * (3) @newrow and @space specify the location of the new pix * with respect to the last one(s) that were entered. * (4) @dp specifies the depth at which all pix are saved. It can * be only 8 or 32 bpp. Any colormap is removed. This is only * used at the first invocation. * (5) This function uses two variables from call to call. * If they were static, the function would not be .so or thread * safe, and furthermore, there would be interference with two or * more pixa accumulating images at a time. Consequently, * we use the first pix in the pixa to store and obtain both * the depth and the current position of the bottom (one pixel * below the lowest image raster line when laid out using * the boxa). The bottom variable is stored in the input format * field, which is the only field available for storing an int. */ l_int32 pixSaveTiledOutline(PIX *pixs, PIXA *pixa, l_int32 reduction, l_int32 newrow, l_int32 space, l_int32 linewidth, l_int32 dp) { l_int32 n, top, left, bx, by, bw, w, h, depth, bottom; l_float32 scale; BOX *box; PIX *pix, *pixt1, *pixt2, *pixt3; PROCNAME("pixSaveTiledOutline"); if (reduction == 0) return 0; if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); if (n == 0) { bottom = 0; if (dp != 8 && dp != 32) { L_WARNING("dp not 8 or 32 bpp; using 32", procName); depth = 32; } else depth = dp; } else { /* extract the depth and bottom params from the first pix */ pix = pixaGetPix(pixa, 0, L_CLONE); depth = pixGetDepth(pix); bottom = pixGetInputFormat(pix); /* not typical usage! */ pixDestroy(&pix); } /* Scale and convert to output depth */ if (reduction == 1) pixt1 = pixClone(pixs); else { scale = 1. / (l_float32)reduction; if (pixGetDepth(pixs) == 1) pixt1 = pixScaleToGray(pixs, scale); else pixt1 = pixScale(pixs, scale, scale); } if (depth == 8) pixt2 = pixConvertTo8(pixt1, 0); else pixt2 = pixConvertTo32(pixt1); pixDestroy(&pixt1); /* Add black outline */ if (linewidth > 0) pixt3 = pixAddBorder(pixt2, linewidth, 0); else pixt3 = pixClone(pixt2); pixDestroy(&pixt2); /* Find position of current pix (UL corner plus size) */ if (n == 0) { top = 0; left = 0; } else if (newrow == 1) { top = bottom + space; left = 0; } else if (n > 0) { pixaGetBoxGeometry(pixa, n - 1, &bx, &by, &bw, NULL); top = by; left = bx + bw + space; } pixGetDimensions(pixt3, &w, &h, NULL); bottom = L_MAX(bottom, top + h); box = boxCreate(left, top, w, h); pixaAddPix(pixa, pixt3, L_INSERT); pixaAddBox(pixa, box, L_INSERT); /* Save the new bottom value */ pix = pixaGetPix(pixa, 0, L_CLONE); pixSetInputFormat(pix, bottom); /* not typical usage! */ pixDestroy(&pix); return 0; }
/*! * pixaaDisplay() * * Input: pixaa * w, h (if set to 0, determines the size from the * b.b. of the components in pixaa) * Return: pix, or null on error * * Notes: * (1) Each pix of the pixaa is displayed at the location given by * its box, translated by the box of the containing pixa * if it exists. */ PIX * pixaaDisplay(PIXAA *pixaa, l_int32 w, l_int32 h) { l_int32 i, j, n, nbox, na, d, wmax, hmax, x, y, xb, yb, wb, hb; BOXA *boxa1; /* top-level boxa */ BOXA *boxa; PIX *pixt, *pixd; PIXA *pixa; PROCNAME("pixaaDisplay"); if (!pixaa) return (PIX *)ERROR_PTR("pixaa not defined", procName, NULL); n = pixaaGetCount(pixaa); if (n == 0) return (PIX *)ERROR_PTR("no components", procName, NULL); /* If w and h not input, determine the minimum size required * to contain the origin and all c.c. */ boxa1 = pixaaGetBoxa(pixaa, L_CLONE); nbox = boxaGetCount(boxa1); if (w == 0 || h == 0) { if (nbox == n) boxaGetExtent(boxa1, &w, &h, NULL); else { /* have to use the lower-level boxa for each pixa */ wmax = hmax = 0; for (i = 0; i < n; i++) { pixa = pixaaGetPixa(pixaa, i, L_CLONE); boxa = pixaGetBoxa(pixa, L_CLONE); boxaGetExtent(boxa, &w, &h, NULL); wmax = L_MAX(wmax, w); hmax = L_MAX(hmax, h); pixaDestroy(&pixa); boxaDestroy(&boxa); } w = wmax; h = hmax; } } /* Get depth from first pix */ pixa = pixaaGetPixa(pixaa, 0, L_CLONE); pixt = pixaGetPix(pixa, 0, L_CLONE); d = pixGetDepth(pixt); pixaDestroy(&pixa); pixDestroy(&pixt); if ((pixd = pixCreate(w, h, d)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); x = y = 0; for (i = 0; i < n; i++) { pixa = pixaaGetPixa(pixaa, i, L_CLONE); if (nbox == n) boxaGetBoxGeometry(boxa1, i, &x, &y, NULL, NULL); na = pixaGetCount(pixa); for (j = 0; j < na; j++) { pixaGetBoxGeometry(pixa, j, &xb, &yb, &wb, &hb); pixt = pixaGetPix(pixa, j, L_CLONE); pixRasterop(pixd, x + xb, y + yb, wb, hb, PIX_PAINT, pixt, 0, 0); pixDestroy(&pixt); } pixaDestroy(&pixa); } boxaDestroy(&boxa1); return pixd; }
/*! * pixaDisplayOnColor() * * Input: pixa * w, h (if set to 0, determines the size from the * b.b. of the components in pixa) * color (background color to use) * Return: pix, or null on error * * Notes: * (1) This uses the boxes to place each pix in the rendered composite. * (2) Set w = h = 0 to use the b.b. of the components to determine * the size of the returned pix. * (3) If any pix in @pixa are colormapped, or if the pix have * different depths, it returns a 32 bpp pix. Otherwise, * the depth of the returned pixa equals that of the pix in @pixa. * (4) If the pixa is empty, return null. */ PIX * pixaDisplayOnColor(PIXA *pixa, l_int32 w, l_int32 h, l_uint32 bgcolor) { l_int32 i, n, xb, yb, wb, hb, hascmap, maxdepth, same; BOXA *boxa; PIX *pixt1, *pixt2, *pixd; PIXA *pixat; PROCNAME("pixaDisplayOnColor"); if (!pixa) return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); if ((n = pixaGetCount(pixa)) == 0) return (PIX *)ERROR_PTR("no components", procName, NULL); /* If w and h are not input, determine the minimum size * required to contain the origin and all c.c. */ if (w == 0 || h == 0) { boxa = pixaGetBoxa(pixa, L_CLONE); boxaGetExtent(boxa, &w, &h, NULL); boxaDestroy(&boxa); } /* If any pix have colormaps, or if they have different depths, * generate rgb */ pixaAnyColormaps(pixa, &hascmap); pixaGetDepthInfo(pixa, &maxdepth, &same); if (hascmap || !same) { maxdepth = 32; pixat = pixaCreate(n); for (i = 0; i < n; i++) { pixt1 = pixaGetPix(pixa, i, L_CLONE); pixt2 = pixConvertTo32(pixt1); pixaAddPix(pixat, pixt2, L_INSERT); pixDestroy(&pixt1); } } else pixat = pixaCopy(pixa, L_CLONE); /* Make the output pix and set the background color */ if ((pixd = pixCreate(w, h, maxdepth)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); if ((maxdepth == 1 && bgcolor > 0) || (maxdepth == 2 && bgcolor >= 0x3) || (maxdepth == 4 && bgcolor >= 0xf) || (maxdepth == 8 && bgcolor >= 0xff) || (maxdepth == 16 && bgcolor >= 0xffff) || (maxdepth == 32 && bgcolor >= 0xffffff00)) { pixSetAll(pixd); } else if (bgcolor > 0) pixSetAllArbitrary(pixd, bgcolor); /* Blit each pix into its place */ for (i = 0; i < n; i++) { if (pixaGetBoxGeometry(pixat, i, &xb, &yb, &wb, &hb)) { L_WARNING("no box found!", procName); continue; } pixt1 = pixaGetPix(pixat, i, L_CLONE); pixRasterop(pixd, xb, yb, wb, hb, PIX_SRC, pixt1, 0, 0); pixDestroy(&pixt1); } pixaDestroy(&pixat); return pixd; }
/*! * pixGetTextlineCenters() * * Input: pixs (1 bpp) * debugflag (1 for debug output) * Return: ptaa (of center values of textlines) * * Notes: * (1) This in general does not have a point for each value * of x, because there will be gaps between words. * It doesn't matter because we will fit a quadratic to the * points that we do have. */ PTAA * pixGetTextlineCenters(PIX *pixs, l_int32 debugflag) { l_int32 i, w, h, bx, by, nsegs; BOXA *boxa; PIX *pix, *pixt1, *pixt2, *pixt3; PIXA *pixa1, *pixa2; PTA *pta; PTAA *ptaa; PROCNAME("pixGetTextlineCenters"); if (!pixs || pixGetDepth(pixs) != 1) return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); pixGetDimensions(pixs, &w, &h, NULL); /* Filter to solidify the text lines within the x-height region, * and to remove most of the ascenders and descenders. */ pixt1 = pixMorphSequence(pixs, "c15.1 + o15.1 + c30.1", 0); pixDisplayWithTitle(pixt1, 0, 800, "pix1", debugflag); /* Get the 8-connected components ... */ boxa = pixConnComp(pixt1, &pixa1, 8); pixDestroy(&pixt1); boxaDestroy(&boxa); if (pixaGetCount(pixa1) == 0) { pixaDestroy(&pixa1); return NULL; } /* ... and remove the short and thin c.c */ pixa2 = pixaSelectBySize(pixa1, 100, 4, L_SELECT_IF_BOTH, L_SELECT_IF_GT, 0); if ((nsegs = pixaGetCount(pixa2)) == 0) { pixaDestroy(&pixa2); return NULL; } if (debugflag) { pixt2 = pixaDisplay(pixa2, w, h); pixDisplayWithTitle(pixt2, 800, 800, "pix2", 1); pixDestroy(&pixt2); } /* For each c.c., get the weighted center of each vertical column. * The result is a set of points going approximately through * the center of the x-height part of the text line. */ ptaa = ptaaCreate(nsegs); for (i = 0; i < nsegs; i++) { pixaGetBoxGeometry(pixa2, i, &bx, &by, NULL, NULL); pix = pixaGetPix(pixa2, i, L_CLONE); pta = pixGetMeanVerticals(pix, bx, by); ptaaAddPta(ptaa, pta, L_INSERT); pixDestroy(&pix); } if (debugflag) { pixt3 = pixCreateTemplate(pixt2); pix = pixDisplayPtaa(pixt3, ptaa); pixDisplayWithTitle(pix, 0, 1400, "pix3", 1); pixDestroy(&pix); pixDestroy(&pixt3); } pixaDestroy(&pixa1); pixaDestroy(&pixa2); return ptaa; }
/*! * pixSplitIntoCharacters() * * Input: pixs (1 bpp, contains only deskewed text) * minw (minimum component width for initial filtering; typ. 4) * minh (minimum component height for initial filtering; typ. 4) * &boxa (<optional return> character bounding boxes) * &pixa (<optional return> character images) * &pixdebug (<optional return> showing splittings) * * Return: 0 if OK, 1 on error * * Notes: * (1) This is a simple function that attempts to find split points * based on vertical pixel profiles. * (2) It should be given an image that has an arbitrary number * of text characters. * (3) The returned pixa includes the boxes from which the * (possibly split) components are extracted. */ l_int32 pixSplitIntoCharacters(PIX *pixs, l_int32 minw, l_int32 minh, BOXA **pboxa, PIXA **ppixa, PIX **ppixdebug) { l_int32 ncomp, i, xoff, yoff; BOXA *boxa1, *boxa2, *boxat1, *boxat2, *boxad; BOXAA *baa; PIX *pix, *pix1, *pix2, *pixdb; PIXA *pixa1, *pixadb; PROCNAME("pixSplitIntoCharacters"); if (pboxa) *pboxa = NULL; if (ppixa) *ppixa = NULL; if (ppixdebug) *ppixdebug = NULL; if (!pixs || pixGetDepth(pixs) != 1) return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); /* Remove the small stuff */ pix1 = pixSelectBySize(pixs, minw, minh, 8, L_SELECT_IF_BOTH, L_SELECT_IF_GT, NULL); /* Small vertical close for consolidation */ pix2 = pixMorphSequence(pix1, "c1.10", 0); pixDestroy(&pix1); /* Get the 8-connected components */ boxa1 = pixConnComp(pix2, &pixa1, 8); pixDestroy(&pix2); boxaDestroy(&boxa1); /* Split the components if obvious */ ncomp = pixaGetCount(pixa1); boxa2 = boxaCreate(ncomp); pixadb = (ppixdebug) ? pixaCreate(ncomp) : NULL; for (i = 0; i < ncomp; i++) { pix = pixaGetPix(pixa1, i, L_CLONE); if (ppixdebug) { boxat1 = pixSplitComponentWithProfile(pix, 10, 7, &pixdb); if (pixdb) pixaAddPix(pixadb, pixdb, L_INSERT); } else { boxat1 = pixSplitComponentWithProfile(pix, 10, 7, NULL); } pixaGetBoxGeometry(pixa1, i, &xoff, &yoff, NULL, NULL); boxat2 = boxaTransform(boxat1, xoff, yoff, 1.0, 1.0); boxaJoin(boxa2, boxat2, 0, -1); pixDestroy(&pix); boxaDestroy(&boxat1); boxaDestroy(&boxat2); } pixaDestroy(&pixa1); /* Generate the debug image */ if (ppixdebug) { if (pixaGetCount(pixadb) > 0) { *ppixdebug = pixaDisplayTiledInRows(pixadb, 32, 1500, 1.0, 0, 20, 1); } pixaDestroy(&pixadb); } /* Do a 2D sort on the bounding boxes, and flatten the result to 1D */ baa = boxaSort2d(boxa2, NULL, 0, 0, 5); boxad = boxaaFlattenToBoxa(baa, NULL, L_CLONE); boxaaDestroy(&baa); boxaDestroy(&boxa2); /* Optionally extract the pieces from the input image */ if (ppixa) *ppixa = pixClipRectangles(pixs, boxad); if (pboxa) *pboxa = boxad; else boxaDestroy(&boxad); return 0; }