/*====================================================================* - 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. *====================================================================*/ /* * boxfunc3.c * * Boxa/Boxaa painting into pix * PIX *pixMaskConnComp() * PIX *pixMaskBoxa() * PIX *pixPaintBoxa() * PIX *pixPaintBoxaRandom() * PIX *pixBlendBoxaRandom() * PIX *pixDrawBoxa() * PIX *pixDrawBoxaRandom() * PIX *boxaaDisplay() * * Split mask components into Boxa * BOXA *pixSplitIntoBoxa() * BOXA *pixSplitComponentIntoBoxa() * static l_int32 pixSearchForRectangle() * * See summary in pixPaintBoxa() of various ways to paint and draw * boxes on images. */ #include <stdio.h> #include <stdlib.h> #include "allheaders.h" static l_int32 pixSearchForRectangle(PIX *pixs, BOX *boxs, l_int32 minsum, l_int32 skipdist, l_int32 delta, l_int32 maxbg, l_int32 sideflag, BOXA *boxat, NUMA *nascore); #ifndef NO_CONSOLE_IO #define DEBUG_SPLIT 0 #endif /* ~NO_CONSOLE_IO */ /*---------------------------------------------------------------------* * Boxa/Boxaa painting into Pix * *---------------------------------------------------------------------*/ /*! * pixMaskConnComp() * * Input: pixs (1 bpp) * connectivity (4 or 8) * &boxa (<optional return> bounding boxes of c.c.) * Return: pixd (1 bpp mask over the c.c.), or null on error * * Notes: * (1) This generates a mask image with ON pixels over the * b.b. of the c.c. in pixs. If there are no ON pixels in pixs, * pixd will also have no ON pixels. */ PIX * pixMaskConnComp(PIX *pixs, l_int32 connectivity, BOXA **pboxa) { BOXA *boxa; PIX *pixd; PROCNAME("pixMaskConnComp"); if (!pixs || pixGetDepth(pixs) != 1) return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); if (connectivity != 4 && connectivity != 8) return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); boxa = pixConnComp(pixs, NULL, connectivity); pixd = pixCreateTemplate(pixs); if (boxaGetCount(boxa) != 0) pixMaskBoxa(pixd, pixd, boxa, L_SET_PIXELS); if (pboxa) *pboxa = boxa; else boxaDestroy(&boxa); return pixd; } /*! * pixMaskBoxa() * * Input: pixd (<optional> may be null) * pixs (any depth; not cmapped) * boxa (of boxes, to paint) * op (L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS) * Return: pixd (with masking op over the boxes), or null on error * * Notes: * (1) This can be used with: * pixd = NULL (makes a new pixd) * pixd = pixs (in-place) * (2) If pixd == NULL, this first makes a copy of pixs, and then * bit-twiddles over the boxes. Otherwise, it operates directly * on pixs. * (3) This simple function is typically used with 1 bpp images. * It uses the 1-image rasterop function, rasteropUniLow(), * to set, clear or flip the pixels in pixd. * (4) If you want to generate a 1 bpp mask of ON pixels from the boxes * in a Boxa, in a pix of size (w,h): * pix = pixCreate(w, h, 1); * pixMaskBoxa(pix, pix, boxa, L_SET_PIXELS); */ PIX * pixMaskBoxa(PIX *pixd, PIX *pixs, BOXA *boxa, l_int32 op) { l_int32 i, n, x, y, w, h; BOX *box; PROCNAME("pixMaskBoxa"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetColormap(pixs)) return (PIX *)ERROR_PTR("pixs is cmapped", procName, NULL); if (pixd && (pixd != pixs)) return (PIX *)ERROR_PTR("if pixd, must be in-place", procName, NULL); if (!boxa) return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) return (PIX *)ERROR_PTR("invalid op", procName, NULL); pixd = pixCopy(pixd, pixs); if ((n = boxaGetCount(boxa)) == 0) { L_WARNING("no boxes to mask", procName); return pixd; } for (i = 0; i < n; i++) { box = boxaGetBox(boxa, i, L_CLONE); boxGetGeometry(box, &x, &y, &w, &h); if (op == L_SET_PIXELS) pixRasterop(pixd, x, y, w, h, PIX_SET, NULL, 0, 0); else if (op == L_CLEAR_PIXELS) pixRasterop(pixd, x, y, w, h, PIX_CLR, NULL, 0, 0); else /* op == L_FLIP_PIXELS */ pixRasterop(pixd, x, y, w, h, PIX_NOT(PIX_DST), NULL, 0, 0); boxDestroy(&box); } return pixd; } /*! * pixPaintBoxa() * * Input: pixs (any depth, can be cmapped) * boxa (of boxes, to paint) * val (rgba color to paint) * Return: pixd (with painted boxes), or null on error * * Notes: * (1) If pixs is 1 bpp or is colormapped, it is converted to 8 bpp * and the boxa is painted using a colormap; otherwise, * it is converted to 32 bpp rgb. * (2) There are several ways to display a box on an image: * * Paint it as a solid color * * Draw the outline * * Blend the outline or region with the existing image * We provide painting and drawing here; blending is in blend.c. * When painting or drawing, the result can be either a * cmapped image or an rgb image. The dest will be cmapped * if the src is either 1 bpp or has a cmap that is not full. * To force RGB output, use pixConvertTo8(pixs, FALSE) * before calling any of these paint and draw functions. */ PIX * pixPaintBoxa(PIX *pixs, BOXA *boxa, l_uint32 val) { l_int32 i, n, d, rval, gval, bval, newindex; l_int32 mapvacancy; /* true only if cmap and not full */ BOX *box; PIX *pixd; PIXCMAP *cmap; PROCNAME("pixPaintBoxa"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (!boxa) return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); if ((n = boxaGetCount(boxa)) == 0) { L_WARNING("no boxes to paint; returning a copy", procName); return pixCopy(NULL, pixs); } mapvacancy = FALSE; if ((cmap = pixGetColormap(pixs)) != NULL) { if (pixcmapGetCount(cmap) < 256) mapvacancy = TRUE; } if (pixGetDepth(pixs) == 1 || mapvacancy) pixd = pixConvertTo8(pixs, TRUE); else pixd = pixConvertTo32(pixs); if (!pixd) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); d = pixGetDepth(pixd); if (d == 8) { /* colormapped */ cmap = pixGetColormap(pixd); extractRGBValues(val, &rval, &gval, &bval); if (pixcmapAddNewColor(cmap, rval, gval, bval, &newindex)) return (PIX *)ERROR_PTR("cmap full; can't add", procName, NULL); } for (i = 0; i < n; i++) { box = boxaGetBox(boxa, i, L_CLONE); if (d == 8) pixSetInRectArbitrary(pixd, box, newindex); else pixSetInRectArbitrary(pixd, box, val); boxDestroy(&box); } return pixd; } /*! * pixPaintBoxaRandom() * * Input: pixs (any depth, can be cmapped) * boxa (of boxes, to paint) * Return: pixd (with painted boxes), or null on error * * Notes: * (1) If pixs is 1 bpp, we paint the boxa using a colormap; * otherwise, we convert to 32 bpp. * (2) We use up to 254 different colors for painting the regions. * (3) If boxes overlap, the later ones paint over earlier ones. */ PIX * pixPaintBoxaRandom(PIX *pixs, BOXA *boxa) { l_int32 i, n, d, rval, gval, bval, index; l_uint32 val; BOX *box; PIX *pixd; PIXCMAP *cmap; PROCNAME("pixPaintBoxaRandom"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (!boxa) return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); if ((n = boxaGetCount(boxa)) == 0) { L_WARNING("no boxes to paint; returning a copy", procName); return pixCopy(NULL, pixs); } if (pixGetDepth(pixs) == 1) pixd = pixConvert1To8(NULL, pixs, 255, 0); else pixd = pixConvertTo32(pixs); if (!pixd) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); cmap = pixcmapCreateRandom(8, 1, 1); d = pixGetDepth(pixd); if (d == 8) /* colormapped */ pixSetColormap(pixd, cmap); for (i = 0; i < n; i++) { box = boxaGetBox(boxa, i, L_CLONE); index = 1 + (i % 254); if (d == 8) pixSetInRectArbitrary(pixd, box, index); else { /* d == 32 */ pixcmapGetColor(cmap, index, &rval, &gval, &bval); composeRGBPixel(rval, gval, bval, &val); pixSetInRectArbitrary(pixd, box, val); } boxDestroy(&box); } if (d == 32) pixcmapDestroy(&cmap); return pixd; } /*! * pixBlendBoxaRandom() * * Input: pixs (any depth; can be cmapped) * boxa (of boxes, to blend/paint) * fract (of box color to use) * Return: pixd (32 bpp, with blend/painted boxes), or null on error * * Notes: * (1) pixs is converted to 32 bpp. * (2) This differs from pixPaintBoxaRandom(), in that the * colors here are blended with the color of pixs. * (3) We use up to 254 different colors for painting the regions. * (4) If boxes overlap, the final color depends only on the last * rect that is used. */ PIX * pixBlendBoxaRandom(PIX *pixs, BOXA *boxa, l_float32 fract) { l_int32 i, n, rval, gval, bval, index; l_uint32 val; BOX *box; PIX *pixd; PIXCMAP *cmap; PROCNAME("pixBlendBoxaRandom"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (!boxa) return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); if (fract < 0.0 || fract > 1.0) { L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5", procName); fract = 0.5; } if ((n = boxaGetCount(boxa)) == 0) { L_WARNING("no boxes to paint; returning a copy", procName); return pixCopy(NULL, pixs); } if ((pixd = pixConvertTo32(pixs)) == NULL) return (PIX *)ERROR_PTR("pixd not defined", procName, NULL); cmap = pixcmapCreateRandom(8, 1, 1); for (i = 0; i < n; i++) { box = boxaGetBox(boxa, i, L_CLONE); index = 1 + (i % 254); pixcmapGetColor(cmap, index, &rval, &gval, &bval); composeRGBPixel(rval, gval, bval, &val); pixBlendInRect(pixd, box, val, fract); boxDestroy(&box); } pixcmapDestroy(&cmap); return pixd; } /*! * pixDrawBoxa() * * Input: pixs (any depth; can be cmapped) * boxa (of boxes, to draw) * width (of lines) * val (rgba color to draw) * Return: pixd (with outlines of boxes added), or null on error * * Notes: * (1) If pixs is 1 bpp or is colormapped, it is converted to 8 bpp * and the boxa is drawn using a colormap; otherwise, * it is converted to 32 bpp rgb. */ PIX * pixDrawBoxa(PIX *pixs, BOXA *boxa, l_int32 width, l_uint32 val) { l_int32 rval, gval, bval, newindex; l_int32 mapvacancy; /* true only if cmap and not full */ PIX *pixd; PIXCMAP *cmap; PROCNAME("pixDrawBoxa"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (!boxa) return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); if (width < 1) return (PIX *)ERROR_PTR("width must be >= 1", procName, NULL); if (boxaGetCount(boxa) == 0) { L_WARNING("no boxes to draw; returning a copy", procName); return pixCopy(NULL, pixs); } mapvacancy = FALSE; if ((cmap = pixGetColormap(pixs)) != NULL) { if (pixcmapGetCount(cmap) < 256) mapvacancy = TRUE; } if (pixGetDepth(pixs) == 1 || mapvacancy) pixd = pixConvertTo8(pixs, TRUE); else pixd = pixConvertTo32(pixs); if (!pixd) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); extractRGBValues(val, &rval, &gval, &bval); if (pixGetDepth(pixd) == 8) { /* colormapped */ cmap = pixGetColormap(pixd); pixcmapAddNewColor(cmap, rval, gval, bval, &newindex); } pixRenderBoxaArb(pixd, boxa, width, rval, gval, bval); return pixd; } /*! * pixDrawBoxaRandom() * * Input: pixs (any depth, can be cmapped) * boxa (of boxes, to draw) * width (thickness of line) * Return: pixd (with box outlines drawn), or null on error * * Notes: * (1) If pixs is 1 bpp, we draw the boxa using a colormap; * otherwise, we convert to 32 bpp. * (2) We use up to 254 different colors for drawing the boxes. * (3) If boxes overlap, the later ones draw over earlier ones. */ PIX * pixDrawBoxaRandom(PIX *pixs, BOXA *boxa, l_int32 width) { l_int32 i, n, rval, gval, bval, index; BOX *box; PIX *pixd; PIXCMAP *cmap; PTAA *ptaa; PROCNAME("pixDrawBoxaRandom"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (!boxa) return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); if (width < 1) return (PIX *)ERROR_PTR("width must be >= 1", procName, NULL); if ((n = boxaGetCount(boxa)) == 0) { L_WARNING("no boxes to draw; returning a copy", procName); return pixCopy(NULL, pixs); } /* Input depth = 1 bpp; generate cmapped output */ if (pixGetDepth(pixs) == 1) { ptaa = generatePtaaBoxa(boxa); pixd = pixRenderRandomCmapPtaa(pixs, ptaa, 1, width, 1); ptaaDestroy(&ptaa); return pixd; } /* Generate rgb output */ pixd = pixConvertTo32(pixs); cmap = pixcmapCreateRandom(8, 1, 1); for (i = 0; i < n; i++) { box = boxaGetBox(boxa, i, L_CLONE); index = 1 + (i % 254); pixcmapGetColor(cmap, index, &rval, &gval, &bval); pixRenderBoxArb(pixd, box, width, rval, gval, bval); boxDestroy(&box); } pixcmapDestroy(&cmap); return pixd; } /*! * boxaaDisplay() * * Input: boxaa * linewba (line width to display boxa) * linewb (line width to display box) * colorba (color to display boxa) * colorb (color to display box) * w (of pix; use 0 if determined by boxaa) * h (of pix; use 0 if determined by boxaa) * Return: 0 if OK, 1 on error */ PIX * boxaaDisplay(BOXAA *boxaa, l_int32 linewba, l_int32 linewb, l_uint32 colorba, l_uint32 colorb, l_int32 w, l_int32 h) { l_int32 i, j, n, m, rbox, gbox, bbox, rboxa, gboxa, bboxa; BOX *box; BOXA *boxa; PIX *pix; PIXCMAP *cmap; PROCNAME("boxaaDisplay"); if (!boxaa) return (PIX *)ERROR_PTR("boxaa not defined", procName, NULL); if (w == 0 || h == 0) boxaaGetExtent(boxaa, &w, &h, NULL); pix = pixCreate(w, h, 8); cmap = pixcmapCreate(8); pixSetColormap(pix, cmap); extractRGBValues(colorb, &rbox, &gbox, &bbox); extractRGBValues(colorba, &rboxa, &gboxa, &bboxa); pixcmapAddColor(cmap, 255, 255, 255); pixcmapAddColor(cmap, rbox, gbox, bbox); pixcmapAddColor(cmap, rboxa, gboxa, bboxa); n = boxaaGetCount(boxaa); for (i = 0; i < n; i++) { boxa = boxaaGetBoxa(boxaa, i, L_CLONE); boxaGetExtent(boxa, NULL, NULL, &box); pixRenderBoxArb(pix, box, linewba, rboxa, gboxa, bboxa); boxDestroy(&box); m = boxaGetCount(boxa); for (j = 0; j < m; j++) { box = boxaGetBox(boxa, j, L_CLONE); pixRenderBoxArb(pix, box, linewb, rbox, gbox, bbox); boxDestroy(&box); } boxaDestroy(&boxa); } return pix; } /*---------------------------------------------------------------------* * Split mask components into Boxa * *---------------------------------------------------------------------*/ /*! * pixSplitIntoBoxa() * * Input: pixs (1 bpp) * minsum (minimum pixels to trigger propagation) * skipdist (distance before computing sum for propagation) * delta (difference required to stop propagation) * maxbg (maximum number of allowed bg pixels in ref scan) * maxcomps (use 0 for unlimited number of subdivided components) * remainder (set to 1 to get b.b. of remaining stuff) * Return: boxa (of rectangles covering the fg of pixs), or null on error * * Notes: * (1) This generates a boxa of rectangles that covers * the fg of a mask. For each 8-connected component in pixs, * it does a greedy partitioning, choosing the largest * rectangle found from each of the four directions at each iter. * See pixSplitComponentsIntoBoxa() for details. * (2) The input parameters give some flexibility for boundary * noise. The resulting set of rectangles may cover some * bg pixels. * (3) This should be used when there are a small number of * mask components, each of which has sides that are close * to horizontal and vertical. The input parameters @delta * and @maxbg determine whether or not holes in the mask are covered. * (4) The parameter @maxcomps gives the maximum number of allowed * rectangles extracted from any single connected component. * Use 0 if no limit is to be applied. * (5) The flag @remainder specifies whether we take a final bounding * box for anything left after the maximum number of allowed * rectangle is extracted. */ BOXA * pixSplitIntoBoxa(PIX *pixs, l_int32 minsum, l_int32 skipdist, l_int32 delta, l_int32 maxbg, l_int32 maxcomps, l_int32 remainder) { l_int32 i, n; BOX *box; BOXA *boxa, *boxas, *boxad; PIX *pix; PIXA *pixas; PROCNAME("pixSplitIntoBoxa"); if (!pixs || pixGetDepth(pixs) != 1) return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); boxas = pixConnComp(pixs, &pixas, 8); n = boxaGetCount(boxas); boxad = boxaCreate(0); for (i = 0; i < n; i++) { pix = pixaGetPix(pixas, i, L_CLONE); box = boxaGetBox(boxas, i, L_CLONE); boxa = pixSplitComponentIntoBoxa(pix, box, minsum, skipdist, delta, maxbg, maxcomps, remainder); boxaJoin(boxad, boxa, 0, 0); pixDestroy(&pix); boxDestroy(&box); boxaDestroy(&boxa); } pixaDestroy(&pixas); boxaDestroy(&boxas); return boxad; } /*! * pixSplitComponentIntoBoxa() * * Input: pixs (1 bpp) * box (<optional> location of pixs w/rt an origin) * minsum (minimum pixels to trigger propagation) * skipdist (distance before computing sum for propagation) * delta (difference required to stop propagation) * maxbg (maximum number of allowed bg pixels in ref scan) * maxcomps (use 0 for unlimited number of subdivided components) * remainder (set to 1 to get b.b. of remaining stuff) * Return: boxa (of rectangles covering the fg of pixs), or null on error * * Notes: * (1) This generates a boxa of rectangles that covers * the fg of a mask. It does so by a greedy partitioning of * the mask, choosing the largest rectangle found from * each of the four directions at each step. * (2) The input parameters give some flexibility for boundary * noise. The resulting set of rectangles must cover all * the fg pixels and, in addition, may cover some bg pixels. * Using small input parameters on a noiseless mask (i.e., one * that has only large vertical and horizontal edges) will * result in a proper covering of only the fg pixels of the mask. * (3) The input is assumed to be a single connected component, that * may have holes. From each side, sweep inward, counting * the pixels. If the count becomes greater than @minsum, * and we have moved forward a further amount @skipdist, * record that count ('countref'), but don't accept if the scan * contains more than @maxbg bg pixels. Continue the scan * until we reach a count that differs from countref by at * least @delta, at which point the propagation stops. The box * swept out gets a score, which is the sum of fg pixels * minus a penalty. The penalty is the number of bg pixels * in the box. This is done from all four sides, and the * side with the largest score is saved as a rectangle. * The process repeats until there is either no rectangle * left, or there is one that can't be captured from any * direction. For the latter case, we simply accept the * last rectangle. * (4) The input box is only used to specify the location of * the UL corner of pixs, with respect to an origin that * typically represents the UL corner of an underlying image, * of which pixs is one component. If @box is null, * the UL corner is taken to be (0, 0). * (5) The parameter @maxcomps gives the maximum number of allowed * rectangles extracted from any single connected component. * Use 0 if no limit is to be applied. * (6) The flag @remainder specifies whether we take a final bounding * box for anything left after the maximum number of allowed * rectangle is extracted. * (7) So if @maxcomps > 0, it specifies that we want no more than * the first @maxcomps rectangles that satisfy the input * criteria. After this, we can get a final rectangle that * bounds everything left over by setting @remainder == 1. * If @remainder == 0, we only get rectangles that satisfy * the input criteria. * (8) It should be noted that the removal of rectangles can * break the original c.c. into several c.c. * (9) Summing up: * * If @maxcomp == 0, the splitting proceeds as far as possible. * * If @maxcomp > 0, the splitting stops when @maxcomps are * found, or earlier if no more components can be selected. * * If @remainder == 1 and components remain that cannot be * selected, they are returned as a single final rectangle; * otherwise, they are ignored. */ BOXA * pixSplitComponentIntoBoxa(PIX *pix, BOX *box, l_int32 minsum, l_int32 skipdist, l_int32 delta, l_int32 maxbg, l_int32 maxcomps, l_int32 remainder) { l_int32 i, w, h, boxx, boxy, bx, by, bw, bh, maxdir, maxscore; l_int32 iter; BOX *boxs; /* shrinks as rectangular regions are removed */ BOX *boxt1, *boxt2, *boxt3; BOXA *boxat; /* stores rectangle data for each side in an iteration */ BOXA *boxad; NUMA *nascore, *nas; PIX *pixs; PROCNAME("pixSplitComponentIntoBoxa"); if (!pix || pixGetDepth(pix) != 1) return (BOXA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL); pixs = pixCopy(NULL, pix); pixGetDimensions(pixs, &w, &h, NULL); if (box) boxGetGeometry(box, &boxx, &boxy, NULL, NULL); else boxx = boxy = 0; boxs = boxCreate(0, 0, w, h); boxad = boxaCreate(0); iter = 0; while (boxs != NULL) { boxGetGeometry(boxs, &bx, &by, &bw, &bh); boxat = boxaCreate(4); /* potential rectangular regions */ nascore = numaCreate(4); for (i = 0; i < 4; i++) { pixSearchForRectangle(pixs, boxs, minsum, skipdist, delta, maxbg, i, boxat, nascore); } nas = numaGetSortIndex(nascore, L_SORT_DECREASING); numaGetIValue(nas, 0, &maxdir); numaGetIValue(nascore, maxdir, &maxscore); #if DEBUG_SPLIT fprintf(stderr, "Iteration: %d\n", iter); boxPrintStreamInfo(stderr, boxs); boxaWriteStream(stderr, boxat); fprintf(stderr, "\nmaxdir = %d, maxscore = %d\n\n", maxdir, maxscore); #endif /* DEBUG_SPLIT */ if (maxscore > 0) { /* accept this */ boxt1 = boxaGetBox(boxat, maxdir, L_CLONE); boxt2 = boxTransform(boxt1, boxx, boxy, 1.0, 1.0); boxaAddBox(boxad, boxt2, L_INSERT); pixClearInRect(pixs, boxt1); boxDestroy(&boxt1); pixClipBoxToForeground(pixs, boxs, NULL, &boxt3); boxDestroy(&boxs); boxs = boxt3; if (boxs) { boxGetGeometry(boxs, NULL, NULL, &bw, &bh); if (bw < 2 || bh < 2) boxDestroy(&boxs); /* we're done */ } } else { /* no more valid rectangles can be found */ if (remainder == 1) { /* save the last box */ boxt1 = boxTransform(boxs, boxx, boxy, 1.0, 1.0); boxaAddBox(boxad, boxt1, L_INSERT); } boxDestroy(&boxs); /* we're done */ } boxaDestroy(&boxat); numaDestroy(&nascore); numaDestroy(&nas); iter++; if ((iter == maxcomps) && boxs) { if (remainder == 1) { /* save the last box */ boxt1 = boxTransform(boxs, boxx, boxy, 1.0, 1.0); boxaAddBox(boxad, boxt1, L_INSERT); } boxDestroy(&boxs); /* we're done */ } } pixDestroy(&pixs); return boxad; } /*! * pixSearchForRectangle() * * Input: pixs (1 bpp) * boxs (current region to investigate) * minsum (minimum pixels to trigger propagation) * skipdist (distance before computing sum for propagation) * delta (difference required to stop propagation) * maxbg (maximum number of allowed bg pixels in ref scan) * sideflag (side to search from) * boxat (add result of rectangular region found here) * nascore (add score for this rectangle here) * Return: 0 if OK, 1 on error * * Notes: * (1) See pixSplitByRectangles() for an explanation of the algorithm. * This does the sweep from a single side. For each iteration * in pixSplitByRectangles(), this will be called 4 times, * for @sideflag = {0, 1, 2, 3}. * (2) If a valid rectangle is not found, add a score of 0 and * input a minimum box. */ static l_int32 pixSearchForRectangle(PIX *pixs, BOX *boxs, l_int32 minsum, l_int32 skipdist, l_int32 delta, l_int32 maxbg, l_int32 sideflag, BOXA *boxat, NUMA *nascore) { l_int32 bx, by, bw, bh, width, height, setref, atref; l_int32 minincol, maxincol, mininrow, maxinrow, minval, maxval, bgref; l_int32 x, y, x0, y0, xref, yref, colsum, rowsum, score, countref, diff; void **lines1; BOX *boxr; PROCNAME("pixSearchForRectangle"); if (!pixs || pixGetDepth(pixs) != 1) return ERROR_INT("pixs undefined or not 1 bpp", procName, 1); if (!boxs) return ERROR_INT("boxs not defined", procName, 1); if (!boxat) return ERROR_INT("boxat not defined", procName, 1); if (!nascore) return ERROR_INT("nascore not defined", procName, 1); lines1 = pixGetLinePtrs(pixs, NULL); boxGetGeometry(boxs, &bx, &by, &bw, &bh); boxr = NULL; setref = 0; atref = 0; maxval = 0; minval = 100000; score = 0; /* sum of all (fg - bg) pixels seen in the scan */ xref = yref = 100000; /* init to impossibly big number */ if (sideflag == L_FROM_LEFT) { for (x = bx; x < bx + bw; x++) { colsum = 0; maxincol = 0; minincol = 100000; for (y = by; y < by + bh; y++) { if (GET_DATA_BIT(lines1[y], x)) { colsum++; if (y > maxincol) maxincol = y; if (y < minincol) minincol = y; } } score += colsum; /* Enough fg to sweep out a rectangle? */ if (!setref && colsum >= minsum) { setref = 1; xref = x + 10; if (xref >= bx + bw) goto failure; } /* Reached the reference line; save the count; * if there is too much bg, the rectangle is invalid. */ if (setref && x == xref) { atref = 1; countref = colsum; bgref = maxincol - minincol + 1 - countref; if (bgref > maxbg) goto failure; } /* Have we left the rectangle? If so, save it along * with the score. */ if (atref) { diff = L_ABS(colsum - countref); if (diff >= delta || x == bx + bw - 1) { height = maxval - minval + 1; width = x - bx; if (x == bx + bw - 1) width = x - bx + 1; boxr = boxCreate(bx, minval, width, height); score = 2 * score - width * height; goto success; } } maxval = L_MAX(maxval, maxincol); minval = L_MIN(minval, minincol); } goto failure; } else if (sideflag == L_FROM_RIGHT) { for (x = bx + bw - 1; x >= bx; x--) { colsum = 0; maxincol = 0; minincol = 100000; for (y = by; y < by + bh; y++) { if (GET_DATA_BIT(lines1[y], x)) { colsum++; if (y > maxincol) maxincol = y; if (y < minincol) minincol = y; } } score += colsum; if (!setref && colsum >= minsum) { setref = 1; xref = x - 10; if (xref < bx) goto failure; } if (setref && x == xref) { atref = 1; countref = colsum; bgref = maxincol - minincol + 1 - countref; if (bgref > maxbg) goto failure; } if (atref) { diff = L_ABS(colsum - countref); if (diff >= delta || x == bx) { height = maxval - minval + 1; x0 = x + 1; if (x == bx) x0 = x; width = bx + bw - x0; boxr = boxCreate(x0, minval, width, height); score = 2 * score - width * height; goto success; } } maxval = L_MAX(maxval, maxincol); minval = L_MIN(minval, minincol); } goto failure; } else if (sideflag == L_FROM_TOP) { for (y = by; y < by + bh; y++) { rowsum = 0; maxinrow = 0; mininrow = 100000; for (x = bx; x < bx + bw; x++) { if (GET_DATA_BIT(lines1[y], x)) { rowsum++; if (x > maxinrow) maxinrow = x; if (x < mininrow) mininrow = x; } } score += rowsum; if (!setref && rowsum >= minsum) { setref = 1; yref = y + 10; if (yref >= by + bh) goto failure; } if (setref && y == yref) { atref = 1; countref = rowsum; bgref = maxinrow - mininrow + 1 - countref; if (bgref > maxbg) goto failure; } if (atref) { diff = L_ABS(rowsum - countref); if (diff >= delta || y == by + bh - 1) { width = maxval - minval + 1; height = y - by; if (y == by + bh - 1) height = y - by + 1; boxr = boxCreate(minval, by, width, height); score = 2 * score - width * height; goto success; } } maxval = L_MAX(maxval, maxinrow); minval = L_MIN(minval, mininrow); } goto failure; } else if (sideflag == L_FROM_BOTTOM) { for (y = by + bh - 1; y >= by; y--) { rowsum = 0; maxinrow = 0; mininrow = 100000; for (x = bx; x < bx + bw; x++) { if (GET_DATA_BIT(lines1[y], x)) { rowsum++; if (x > maxinrow) maxinrow = x; if (x < mininrow) mininrow = x; } } score += rowsum; if (!setref && rowsum >= minsum) { setref = 1; yref = y - 10; if (yref < by) goto failure; } if (setref && y == yref) { atref = 1; countref = rowsum; bgref = maxinrow - mininrow + 1 - countref; if (bgref > maxbg) goto failure; } if (atref) { diff = L_ABS(rowsum - countref); if (diff >= delta || y == by) { width = maxval - minval + 1; y0 = y + 1; if (y == by) y0 = y; height = by + bh - y0; boxr = boxCreate(minval, y0, width, height); score = 2 * score - width * height; goto success; } } maxval = L_MAX(maxval, maxinrow); minval = L_MIN(minval, mininrow); } goto failure; } failure: numaAddNumber(nascore, 0); boxaAddBox(boxat, boxCreate(0, 0, 1, 1), L_INSERT); /* min box */ FREE(lines1); return 0; success: numaAddNumber(nascore, score); boxaAddBox(boxat, boxr, L_INSERT); FREE(lines1); return 0; }