Leptonica 1.68
C Image Processing Library
|
00001 /*====================================================================* 00002 - Copyright (C) 2001 Leptonica. All rights reserved. 00003 - This software is distributed in the hope that it will be 00004 - useful, but with NO WARRANTY OF ANY KIND. 00005 - No author or distributor accepts responsibility to anyone for the 00006 - consequences of using this software, or for whether it serves any 00007 - particular purpose or works at all, unless he or she says so in 00008 - writing. Everyone is granted permission to copy, modify and 00009 - redistribute this source code, for commercial or non-commercial 00010 - purposes, with the following restrictions: (1) the origin of this 00011 - source code must not be misrepresented; (2) modified versions must 00012 - be plainly marked as such; and (3) this notice may not be removed 00013 - or altered from any source or modified source distribution. 00014 *====================================================================*/ 00015 00016 /* 00017 * boxfunc3.c 00018 * 00019 * Boxa/Boxaa painting into pix 00020 * PIX *pixMaskConnComp() 00021 * PIX *pixMaskBoxa() 00022 * PIX *pixPaintBoxa() 00023 * PIX *pixSetWhiteOrBlackBoxa() 00024 * PIX *pixPaintBoxaRandom() 00025 * PIX *pixBlendBoxaRandom() 00026 * PIX *pixDrawBoxa() 00027 * PIX *pixDrawBoxaRandom() 00028 * PIX *boxaaDisplay() 00029 * 00030 * Split mask components into Boxa 00031 * BOXA *pixSplitIntoBoxa() 00032 * BOXA *pixSplitComponentIntoBoxa() 00033 * static l_int32 pixSearchForRectangle() 00034 * 00035 * See summary in pixPaintBoxa() of various ways to paint and draw 00036 * boxes on images. 00037 */ 00038 00039 #include "allheaders.h" 00040 00041 static l_int32 pixSearchForRectangle(PIX *pixs, BOX *boxs, l_int32 minsum, 00042 l_int32 skipdist, l_int32 delta, 00043 l_int32 maxbg, l_int32 sideflag, 00044 BOXA *boxat, NUMA *nascore); 00045 00046 #ifndef NO_CONSOLE_IO 00047 #define DEBUG_SPLIT 0 00048 #endif /* ~NO_CONSOLE_IO */ 00049 00050 00051 /*---------------------------------------------------------------------* 00052 * Boxa/Boxaa painting into Pix * 00053 *---------------------------------------------------------------------*/ 00054 /*! 00055 * pixMaskConnComp() 00056 * 00057 * Input: pixs (1 bpp) 00058 * connectivity (4 or 8) 00059 * &boxa (<optional return> bounding boxes of c.c.) 00060 * Return: pixd (1 bpp mask over the c.c.), or null on error 00061 * 00062 * Notes: 00063 * (1) This generates a mask image with ON pixels over the 00064 * b.b. of the c.c. in pixs. If there are no ON pixels in pixs, 00065 * pixd will also have no ON pixels. 00066 */ 00067 PIX * 00068 pixMaskConnComp(PIX *pixs, 00069 l_int32 connectivity, 00070 BOXA **pboxa) 00071 { 00072 BOXA *boxa; 00073 PIX *pixd; 00074 00075 PROCNAME("pixMaskConnComp"); 00076 00077 if (!pixs || pixGetDepth(pixs) != 1) 00078 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); 00079 if (connectivity != 4 && connectivity != 8) 00080 return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); 00081 00082 boxa = pixConnComp(pixs, NULL, connectivity); 00083 pixd = pixCreateTemplate(pixs); 00084 if (boxaGetCount(boxa) != 0) 00085 pixMaskBoxa(pixd, pixd, boxa, L_SET_PIXELS); 00086 if (pboxa) 00087 *pboxa = boxa; 00088 else 00089 boxaDestroy(&boxa); 00090 return pixd; 00091 } 00092 00093 00094 /*! 00095 * pixMaskBoxa() 00096 * 00097 * Input: pixd (<optional> may be null) 00098 * pixs (any depth; not cmapped) 00099 * boxa (of boxes, to paint) 00100 * op (L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS) 00101 * Return: pixd (with masking op over the boxes), or null on error 00102 * 00103 * Notes: 00104 * (1) This can be used with: 00105 * pixd = NULL (makes a new pixd) 00106 * pixd = pixs (in-place) 00107 * (2) If pixd == NULL, this first makes a copy of pixs, and then 00108 * bit-twiddles over the boxes. Otherwise, it operates directly 00109 * on pixs. 00110 * (3) This simple function is typically used with 1 bpp images. 00111 * It uses the 1-image rasterop function, rasteropUniLow(), 00112 * to set, clear or flip the pixels in pixd. 00113 * (4) If you want to generate a 1 bpp mask of ON pixels from the boxes 00114 * in a Boxa, in a pix of size (w,h): 00115 * pix = pixCreate(w, h, 1); 00116 * pixMaskBoxa(pix, pix, boxa, L_SET_PIXELS); 00117 */ 00118 PIX * 00119 pixMaskBoxa(PIX *pixd, 00120 PIX *pixs, 00121 BOXA *boxa, 00122 l_int32 op) 00123 { 00124 l_int32 i, n, x, y, w, h; 00125 BOX *box; 00126 00127 PROCNAME("pixMaskBoxa"); 00128 00129 if (!pixs) 00130 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00131 if (pixGetColormap(pixs)) 00132 return (PIX *)ERROR_PTR("pixs is cmapped", procName, NULL); 00133 if (pixd && (pixd != pixs)) 00134 return (PIX *)ERROR_PTR("if pixd, must be in-place", procName, NULL); 00135 if (!boxa) 00136 return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); 00137 if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) 00138 return (PIX *)ERROR_PTR("invalid op", procName, NULL); 00139 00140 pixd = pixCopy(pixd, pixs); 00141 if ((n = boxaGetCount(boxa)) == 0) { 00142 L_WARNING("no boxes to mask", procName); 00143 return pixd; 00144 } 00145 00146 for (i = 0; i < n; i++) { 00147 box = boxaGetBox(boxa, i, L_CLONE); 00148 boxGetGeometry(box, &x, &y, &w, &h); 00149 if (op == L_SET_PIXELS) 00150 pixRasterop(pixd, x, y, w, h, PIX_SET, NULL, 0, 0); 00151 else if (op == L_CLEAR_PIXELS) 00152 pixRasterop(pixd, x, y, w, h, PIX_CLR, NULL, 0, 0); 00153 else /* op == L_FLIP_PIXELS */ 00154 pixRasterop(pixd, x, y, w, h, PIX_NOT(PIX_DST), NULL, 0, 0); 00155 boxDestroy(&box); 00156 } 00157 00158 return pixd; 00159 } 00160 00161 00162 /*! 00163 * pixPaintBoxa() 00164 * 00165 * Input: pixs (any depth, can be cmapped) 00166 * boxa (of boxes, to paint) 00167 * val (rgba color to paint) 00168 * Return: pixd (with painted boxes), or null on error 00169 * 00170 * Notes: 00171 * (1) If pixs is 1 bpp or is colormapped, it is converted to 8 bpp 00172 * and the boxa is painted using a colormap; otherwise, 00173 * it is converted to 32 bpp rgb. 00174 * (2) There are several ways to display a box on an image: 00175 * * Paint it as a solid color 00176 * * Draw the outline 00177 * * Blend the outline or region with the existing image 00178 * We provide painting and drawing here; blending is in blend.c. 00179 * When painting or drawing, the result can be either a 00180 * cmapped image or an rgb image. The dest will be cmapped 00181 * if the src is either 1 bpp or has a cmap that is not full. 00182 * To force RGB output, use pixConvertTo8(pixs, FALSE) 00183 * before calling any of these paint and draw functions. 00184 */ 00185 PIX * 00186 pixPaintBoxa(PIX *pixs, 00187 BOXA *boxa, 00188 l_uint32 val) 00189 { 00190 l_int32 i, n, d, rval, gval, bval, newindex; 00191 l_int32 mapvacancy; /* true only if cmap and not full */ 00192 BOX *box; 00193 PIX *pixd; 00194 PIXCMAP *cmap; 00195 00196 PROCNAME("pixPaintBoxa"); 00197 00198 if (!pixs) 00199 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00200 if (!boxa) 00201 return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); 00202 00203 if ((n = boxaGetCount(boxa)) == 0) { 00204 L_WARNING("no boxes to paint; returning a copy", procName); 00205 return pixCopy(NULL, pixs); 00206 } 00207 00208 mapvacancy = FALSE; 00209 if ((cmap = pixGetColormap(pixs)) != NULL) { 00210 if (pixcmapGetCount(cmap) < 256) 00211 mapvacancy = TRUE; 00212 } 00213 if (pixGetDepth(pixs) == 1 || mapvacancy) 00214 pixd = pixConvertTo8(pixs, TRUE); 00215 else 00216 pixd = pixConvertTo32(pixs); 00217 if (!pixd) 00218 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00219 00220 d = pixGetDepth(pixd); 00221 if (d == 8) { /* colormapped */ 00222 cmap = pixGetColormap(pixd); 00223 extractRGBValues(val, &rval, &gval, &bval); 00224 if (pixcmapAddNewColor(cmap, rval, gval, bval, &newindex)) 00225 return (PIX *)ERROR_PTR("cmap full; can't add", procName, NULL); 00226 } 00227 00228 for (i = 0; i < n; i++) { 00229 box = boxaGetBox(boxa, i, L_CLONE); 00230 if (d == 8) 00231 pixSetInRectArbitrary(pixd, box, newindex); 00232 else 00233 pixSetInRectArbitrary(pixd, box, val); 00234 boxDestroy(&box); 00235 } 00236 00237 return pixd; 00238 } 00239 00240 00241 /*! 00242 * pixSetBlackOrWhiteBoxa() 00243 * 00244 * Input: pixs (any depth, can be cmapped) 00245 * boxa (<optional> of boxes, to clear or set) 00246 * op (L_SET_BLACK, L_SET_WHITE) 00247 * Return: pixd (with boxes filled with white or black), or null on error 00248 */ 00249 PIX * 00250 pixSetBlackOrWhiteBoxa(PIX *pixs, 00251 BOXA *boxa, 00252 l_int32 op) 00253 { 00254 l_int32 i, n, d, index; 00255 l_uint32 color; 00256 BOX *box; 00257 PIX *pixd; 00258 PIXCMAP *cmap; 00259 00260 PROCNAME("pixSetBlackOrWhiteBoxa"); 00261 00262 if (!pixs) 00263 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00264 if (!boxa) 00265 return pixCopy(NULL, pixs); 00266 if ((n = boxaGetCount(boxa)) == 0) 00267 return pixCopy(NULL, pixs); 00268 00269 pixd = pixCopy(NULL, pixs); 00270 d = pixGetDepth(pixd); 00271 if (d == 1) { 00272 for (i = 0; i < n; i++) { 00273 box = boxaGetBox(boxa, i, L_CLONE); 00274 if (op == L_SET_WHITE) 00275 pixClearInRect(pixd, box); 00276 else 00277 pixSetInRect(pixd, box); 00278 boxDestroy(&box); 00279 } 00280 return pixd; 00281 } 00282 00283 cmap = pixGetColormap(pixs); 00284 if (cmap) { 00285 color = (op == L_SET_WHITE) ? 1 : 0; 00286 pixcmapAddBlackOrWhite(cmap, color, &index); 00287 } 00288 else if (d == 8) 00289 color = (op == L_SET_WHITE) ? 0xff : 0x0; 00290 else if (d == 32) 00291 color = (op == L_SET_WHITE) ? 0xffffff00 : 0x0; 00292 else if (d == 2) 00293 color = (op == L_SET_WHITE) ? 0x3 : 0x0; 00294 else if (d == 4) 00295 color = (op == L_SET_WHITE) ? 0xf : 0x0; 00296 else if (d == 16) 00297 color = (op == L_SET_WHITE) ? 0xffff : 0x0; 00298 else { 00299 pixDestroy(&pixd); 00300 return (PIX *)ERROR_PTR("invalid depth", procName, NULL); 00301 } 00302 00303 for (i = 0; i < n; i++) { 00304 box = boxaGetBox(boxa, i, L_CLONE); 00305 if (cmap) 00306 pixSetInRectArbitrary(pixd, box, index); 00307 else 00308 pixSetInRectArbitrary(pixd, box, color); 00309 boxDestroy(&box); 00310 } 00311 00312 return pixd; 00313 } 00314 00315 00316 /*! 00317 * pixPaintBoxaRandom() 00318 * 00319 * Input: pixs (any depth, can be cmapped) 00320 * boxa (of boxes, to paint) 00321 * Return: pixd (with painted boxes), or null on error 00322 * 00323 * Notes: 00324 * (1) If pixs is 1 bpp, we paint the boxa using a colormap; 00325 * otherwise, we convert to 32 bpp. 00326 * (2) We use up to 254 different colors for painting the regions. 00327 * (3) If boxes overlap, the later ones paint over earlier ones. 00328 */ 00329 PIX * 00330 pixPaintBoxaRandom(PIX *pixs, 00331 BOXA *boxa) 00332 { 00333 l_int32 i, n, d, rval, gval, bval, index; 00334 l_uint32 val; 00335 BOX *box; 00336 PIX *pixd; 00337 PIXCMAP *cmap; 00338 00339 PROCNAME("pixPaintBoxaRandom"); 00340 00341 if (!pixs) 00342 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00343 if (!boxa) 00344 return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); 00345 00346 if ((n = boxaGetCount(boxa)) == 0) { 00347 L_WARNING("no boxes to paint; returning a copy", procName); 00348 return pixCopy(NULL, pixs); 00349 } 00350 00351 if (pixGetDepth(pixs) == 1) 00352 pixd = pixConvert1To8(NULL, pixs, 255, 0); 00353 else 00354 pixd = pixConvertTo32(pixs); 00355 if (!pixd) 00356 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00357 00358 cmap = pixcmapCreateRandom(8, 1, 1); 00359 d = pixGetDepth(pixd); 00360 if (d == 8) /* colormapped */ 00361 pixSetColormap(pixd, cmap); 00362 00363 for (i = 0; i < n; i++) { 00364 box = boxaGetBox(boxa, i, L_CLONE); 00365 index = 1 + (i % 254); 00366 if (d == 8) 00367 pixSetInRectArbitrary(pixd, box, index); 00368 else { /* d == 32 */ 00369 pixcmapGetColor(cmap, index, &rval, &gval, &bval); 00370 composeRGBPixel(rval, gval, bval, &val); 00371 pixSetInRectArbitrary(pixd, box, val); 00372 } 00373 boxDestroy(&box); 00374 } 00375 00376 if (d == 32) 00377 pixcmapDestroy(&cmap); 00378 return pixd; 00379 } 00380 00381 00382 /*! 00383 * pixBlendBoxaRandom() 00384 * 00385 * Input: pixs (any depth; can be cmapped) 00386 * boxa (of boxes, to blend/paint) 00387 * fract (of box color to use) 00388 * Return: pixd (32 bpp, with blend/painted boxes), or null on error 00389 * 00390 * Notes: 00391 * (1) pixs is converted to 32 bpp. 00392 * (2) This differs from pixPaintBoxaRandom(), in that the 00393 * colors here are blended with the color of pixs. 00394 * (3) We use up to 254 different colors for painting the regions. 00395 * (4) If boxes overlap, the final color depends only on the last 00396 * rect that is used. 00397 */ 00398 PIX * 00399 pixBlendBoxaRandom(PIX *pixs, 00400 BOXA *boxa, 00401 l_float32 fract) 00402 { 00403 l_int32 i, n, rval, gval, bval, index; 00404 l_uint32 val; 00405 BOX *box; 00406 PIX *pixd; 00407 PIXCMAP *cmap; 00408 00409 PROCNAME("pixBlendBoxaRandom"); 00410 00411 if (!pixs) 00412 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00413 if (!boxa) 00414 return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); 00415 if (fract < 0.0 || fract > 1.0) { 00416 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5", procName); 00417 fract = 0.5; 00418 } 00419 00420 if ((n = boxaGetCount(boxa)) == 0) { 00421 L_WARNING("no boxes to paint; returning a copy", procName); 00422 return pixCopy(NULL, pixs); 00423 } 00424 00425 if ((pixd = pixConvertTo32(pixs)) == NULL) 00426 return (PIX *)ERROR_PTR("pixd not defined", procName, NULL); 00427 00428 cmap = pixcmapCreateRandom(8, 1, 1); 00429 for (i = 0; i < n; i++) { 00430 box = boxaGetBox(boxa, i, L_CLONE); 00431 index = 1 + (i % 254); 00432 pixcmapGetColor(cmap, index, &rval, &gval, &bval); 00433 composeRGBPixel(rval, gval, bval, &val); 00434 pixBlendInRect(pixd, box, val, fract); 00435 boxDestroy(&box); 00436 } 00437 00438 pixcmapDestroy(&cmap); 00439 return pixd; 00440 } 00441 00442 00443 /*! 00444 * pixDrawBoxa() 00445 * 00446 * Input: pixs (any depth; can be cmapped) 00447 * boxa (of boxes, to draw) 00448 * width (of lines) 00449 * val (rgba color to draw) 00450 * Return: pixd (with outlines of boxes added), or null on error 00451 * 00452 * Notes: 00453 * (1) If pixs is 1 bpp or is colormapped, it is converted to 8 bpp 00454 * and the boxa is drawn using a colormap; otherwise, 00455 * it is converted to 32 bpp rgb. 00456 */ 00457 PIX * 00458 pixDrawBoxa(PIX *pixs, 00459 BOXA *boxa, 00460 l_int32 width, 00461 l_uint32 val) 00462 { 00463 l_int32 rval, gval, bval, newindex; 00464 l_int32 mapvacancy; /* true only if cmap and not full */ 00465 PIX *pixd; 00466 PIXCMAP *cmap; 00467 00468 PROCNAME("pixDrawBoxa"); 00469 00470 if (!pixs) 00471 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00472 if (!boxa) 00473 return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); 00474 if (width < 1) 00475 return (PIX *)ERROR_PTR("width must be >= 1", procName, NULL); 00476 00477 if (boxaGetCount(boxa) == 0) { 00478 L_WARNING("no boxes to draw; returning a copy", procName); 00479 return pixCopy(NULL, pixs); 00480 } 00481 00482 mapvacancy = FALSE; 00483 if ((cmap = pixGetColormap(pixs)) != NULL) { 00484 if (pixcmapGetCount(cmap) < 256) 00485 mapvacancy = TRUE; 00486 } 00487 if (pixGetDepth(pixs) == 1 || mapvacancy) 00488 pixd = pixConvertTo8(pixs, TRUE); 00489 else 00490 pixd = pixConvertTo32(pixs); 00491 if (!pixd) 00492 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00493 00494 extractRGBValues(val, &rval, &gval, &bval); 00495 if (pixGetDepth(pixd) == 8) { /* colormapped */ 00496 cmap = pixGetColormap(pixd); 00497 pixcmapAddNewColor(cmap, rval, gval, bval, &newindex); 00498 } 00499 00500 pixRenderBoxaArb(pixd, boxa, width, rval, gval, bval); 00501 return pixd; 00502 } 00503 00504 00505 /*! 00506 * pixDrawBoxaRandom() 00507 * 00508 * Input: pixs (any depth, can be cmapped) 00509 * boxa (of boxes, to draw) 00510 * width (thickness of line) 00511 * Return: pixd (with box outlines drawn), or null on error 00512 * 00513 * Notes: 00514 * (1) If pixs is 1 bpp, we draw the boxa using a colormap; 00515 * otherwise, we convert to 32 bpp. 00516 * (2) We use up to 254 different colors for drawing the boxes. 00517 * (3) If boxes overlap, the later ones draw over earlier ones. 00518 */ 00519 PIX * 00520 pixDrawBoxaRandom(PIX *pixs, 00521 BOXA *boxa, 00522 l_int32 width) 00523 { 00524 l_int32 i, n, rval, gval, bval, index; 00525 BOX *box; 00526 PIX *pixd; 00527 PIXCMAP *cmap; 00528 PTAA *ptaa; 00529 00530 PROCNAME("pixDrawBoxaRandom"); 00531 00532 if (!pixs) 00533 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00534 if (!boxa) 00535 return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); 00536 if (width < 1) 00537 return (PIX *)ERROR_PTR("width must be >= 1", procName, NULL); 00538 00539 if ((n = boxaGetCount(boxa)) == 0) { 00540 L_WARNING("no boxes to draw; returning a copy", procName); 00541 return pixCopy(NULL, pixs); 00542 } 00543 00544 /* Input depth = 1 bpp; generate cmapped output */ 00545 if (pixGetDepth(pixs) == 1) { 00546 ptaa = generatePtaaBoxa(boxa); 00547 pixd = pixRenderRandomCmapPtaa(pixs, ptaa, 1, width, 1); 00548 ptaaDestroy(&ptaa); 00549 return pixd; 00550 } 00551 00552 /* Generate rgb output */ 00553 pixd = pixConvertTo32(pixs); 00554 cmap = pixcmapCreateRandom(8, 1, 1); 00555 for (i = 0; i < n; i++) { 00556 box = boxaGetBox(boxa, i, L_CLONE); 00557 index = 1 + (i % 254); 00558 pixcmapGetColor(cmap, index, &rval, &gval, &bval); 00559 pixRenderBoxArb(pixd, box, width, rval, gval, bval); 00560 boxDestroy(&box); 00561 } 00562 pixcmapDestroy(&cmap); 00563 return pixd; 00564 } 00565 00566 00567 /*! 00568 * boxaaDisplay() 00569 * 00570 * Input: boxaa 00571 * linewba (line width to display boxa) 00572 * linewb (line width to display box) 00573 * colorba (color to display boxa) 00574 * colorb (color to display box) 00575 * w (of pix; use 0 if determined by boxaa) 00576 * h (of pix; use 0 if determined by boxaa) 00577 * Return: 0 if OK, 1 on error 00578 */ 00579 PIX * 00580 boxaaDisplay(BOXAA *boxaa, 00581 l_int32 linewba, 00582 l_int32 linewb, 00583 l_uint32 colorba, 00584 l_uint32 colorb, 00585 l_int32 w, 00586 l_int32 h) 00587 { 00588 l_int32 i, j, n, m, rbox, gbox, bbox, rboxa, gboxa, bboxa; 00589 BOX *box; 00590 BOXA *boxa; 00591 PIX *pix; 00592 PIXCMAP *cmap; 00593 00594 PROCNAME("boxaaDisplay"); 00595 00596 if (!boxaa) 00597 return (PIX *)ERROR_PTR("boxaa not defined", procName, NULL); 00598 if (w == 0 || h == 0) 00599 boxaaGetExtent(boxaa, &w, &h, NULL); 00600 00601 pix = pixCreate(w, h, 8); 00602 cmap = pixcmapCreate(8); 00603 pixSetColormap(pix, cmap); 00604 extractRGBValues(colorb, &rbox, &gbox, &bbox); 00605 extractRGBValues(colorba, &rboxa, &gboxa, &bboxa); 00606 pixcmapAddColor(cmap, 255, 255, 255); 00607 pixcmapAddColor(cmap, rbox, gbox, bbox); 00608 pixcmapAddColor(cmap, rboxa, gboxa, bboxa); 00609 00610 n = boxaaGetCount(boxaa); 00611 for (i = 0; i < n; i++) { 00612 boxa = boxaaGetBoxa(boxaa, i, L_CLONE); 00613 boxaGetExtent(boxa, NULL, NULL, &box); 00614 pixRenderBoxArb(pix, box, linewba, rboxa, gboxa, bboxa); 00615 boxDestroy(&box); 00616 m = boxaGetCount(boxa); 00617 for (j = 0; j < m; j++) { 00618 box = boxaGetBox(boxa, j, L_CLONE); 00619 pixRenderBoxArb(pix, box, linewb, rbox, gbox, bbox); 00620 boxDestroy(&box); 00621 } 00622 boxaDestroy(&boxa); 00623 } 00624 00625 return pix; 00626 } 00627 00628 00629 /*---------------------------------------------------------------------* 00630 * Split mask components into Boxa * 00631 *---------------------------------------------------------------------*/ 00632 /*! 00633 * pixSplitIntoBoxa() 00634 * 00635 * Input: pixs (1 bpp) 00636 * minsum (minimum pixels to trigger propagation) 00637 * skipdist (distance before computing sum for propagation) 00638 * delta (difference required to stop propagation) 00639 * maxbg (maximum number of allowed bg pixels in ref scan) 00640 * maxcomps (use 0 for unlimited number of subdivided components) 00641 * remainder (set to 1 to get b.b. of remaining stuff) 00642 * Return: boxa (of rectangles covering the fg of pixs), or null on error 00643 * 00644 * Notes: 00645 * (1) This generates a boxa of rectangles that covers 00646 * the fg of a mask. For each 8-connected component in pixs, 00647 * it does a greedy partitioning, choosing the largest 00648 * rectangle found from each of the four directions at each iter. 00649 * See pixSplitComponentsIntoBoxa() for details. 00650 * (2) The input parameters give some flexibility for boundary 00651 * noise. The resulting set of rectangles may cover some 00652 * bg pixels. 00653 * (3) This should be used when there are a small number of 00654 * mask components, each of which has sides that are close 00655 * to horizontal and vertical. The input parameters @delta 00656 * and @maxbg determine whether or not holes in the mask are covered. 00657 * (4) The parameter @maxcomps gives the maximum number of allowed 00658 * rectangles extracted from any single connected component. 00659 * Use 0 if no limit is to be applied. 00660 * (5) The flag @remainder specifies whether we take a final bounding 00661 * box for anything left after the maximum number of allowed 00662 * rectangle is extracted. 00663 */ 00664 BOXA * 00665 pixSplitIntoBoxa(PIX *pixs, 00666 l_int32 minsum, 00667 l_int32 skipdist, 00668 l_int32 delta, 00669 l_int32 maxbg, 00670 l_int32 maxcomps, 00671 l_int32 remainder) 00672 { 00673 l_int32 i, n; 00674 BOX *box; 00675 BOXA *boxa, *boxas, *boxad; 00676 PIX *pix; 00677 PIXA *pixas; 00678 00679 PROCNAME("pixSplitIntoBoxa"); 00680 00681 if (!pixs || pixGetDepth(pixs) != 1) 00682 return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); 00683 00684 boxas = pixConnComp(pixs, &pixas, 8); 00685 n = boxaGetCount(boxas); 00686 boxad = boxaCreate(0); 00687 for (i = 0; i < n; i++) { 00688 pix = pixaGetPix(pixas, i, L_CLONE); 00689 box = boxaGetBox(boxas, i, L_CLONE); 00690 boxa = pixSplitComponentIntoBoxa(pix, box, minsum, skipdist, 00691 delta, maxbg, maxcomps, remainder); 00692 boxaJoin(boxad, boxa, 0, 0); 00693 pixDestroy(&pix); 00694 boxDestroy(&box); 00695 boxaDestroy(&boxa); 00696 } 00697 00698 pixaDestroy(&pixas); 00699 boxaDestroy(&boxas); 00700 return boxad; 00701 } 00702 00703 00704 /*! 00705 * pixSplitComponentIntoBoxa() 00706 * 00707 * Input: pixs (1 bpp) 00708 * box (<optional> location of pixs w/rt an origin) 00709 * minsum (minimum pixels to trigger propagation) 00710 * skipdist (distance before computing sum for propagation) 00711 * delta (difference required to stop propagation) 00712 * maxbg (maximum number of allowed bg pixels in ref scan) 00713 * maxcomps (use 0 for unlimited number of subdivided components) 00714 * remainder (set to 1 to get b.b. of remaining stuff) 00715 * Return: boxa (of rectangles covering the fg of pixs), or null on error 00716 * 00717 * Notes: 00718 * (1) This generates a boxa of rectangles that covers 00719 * the fg of a mask. It does so by a greedy partitioning of 00720 * the mask, choosing the largest rectangle found from 00721 * each of the four directions at each step. 00722 * (2) The input parameters give some flexibility for boundary 00723 * noise. The resulting set of rectangles must cover all 00724 * the fg pixels and, in addition, may cover some bg pixels. 00725 * Using small input parameters on a noiseless mask (i.e., one 00726 * that has only large vertical and horizontal edges) will 00727 * result in a proper covering of only the fg pixels of the mask. 00728 * (3) The input is assumed to be a single connected component, that 00729 * may have holes. From each side, sweep inward, counting 00730 * the pixels. If the count becomes greater than @minsum, 00731 * and we have moved forward a further amount @skipdist, 00732 * record that count ('countref'), but don't accept if the scan 00733 * contains more than @maxbg bg pixels. Continue the scan 00734 * until we reach a count that differs from countref by at 00735 * least @delta, at which point the propagation stops. The box 00736 * swept out gets a score, which is the sum of fg pixels 00737 * minus a penalty. The penalty is the number of bg pixels 00738 * in the box. This is done from all four sides, and the 00739 * side with the largest score is saved as a rectangle. 00740 * The process repeats until there is either no rectangle 00741 * left, or there is one that can't be captured from any 00742 * direction. For the latter case, we simply accept the 00743 * last rectangle. 00744 * (4) The input box is only used to specify the location of 00745 * the UL corner of pixs, with respect to an origin that 00746 * typically represents the UL corner of an underlying image, 00747 * of which pixs is one component. If @box is null, 00748 * the UL corner is taken to be (0, 0). 00749 * (5) The parameter @maxcomps gives the maximum number of allowed 00750 * rectangles extracted from any single connected component. 00751 * Use 0 if no limit is to be applied. 00752 * (6) The flag @remainder specifies whether we take a final bounding 00753 * box for anything left after the maximum number of allowed 00754 * rectangle is extracted. 00755 * (7) So if @maxcomps > 0, it specifies that we want no more than 00756 * the first @maxcomps rectangles that satisfy the input 00757 * criteria. After this, we can get a final rectangle that 00758 * bounds everything left over by setting @remainder == 1. 00759 * If @remainder == 0, we only get rectangles that satisfy 00760 * the input criteria. 00761 * (8) It should be noted that the removal of rectangles can 00762 * break the original c.c. into several c.c. 00763 * (9) Summing up: 00764 * * If @maxcomp == 0, the splitting proceeds as far as possible. 00765 * * If @maxcomp > 0, the splitting stops when @maxcomps are 00766 * found, or earlier if no more components can be selected. 00767 * * If @remainder == 1 and components remain that cannot be 00768 * selected, they are returned as a single final rectangle; 00769 * otherwise, they are ignored. 00770 */ 00771 BOXA * 00772 pixSplitComponentIntoBoxa(PIX *pix, 00773 BOX *box, 00774 l_int32 minsum, 00775 l_int32 skipdist, 00776 l_int32 delta, 00777 l_int32 maxbg, 00778 l_int32 maxcomps, 00779 l_int32 remainder) 00780 { 00781 l_int32 i, w, h, boxx, boxy, bx, by, bw, bh, maxdir, maxscore; 00782 l_int32 iter; 00783 BOX *boxs; /* shrinks as rectangular regions are removed */ 00784 BOX *boxt1, *boxt2, *boxt3; 00785 BOXA *boxat; /* stores rectangle data for each side in an iteration */ 00786 BOXA *boxad; 00787 NUMA *nascore, *nas; 00788 PIX *pixs; 00789 00790 PROCNAME("pixSplitComponentIntoBoxa"); 00791 00792 if (!pix || pixGetDepth(pix) != 1) 00793 return (BOXA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL); 00794 00795 pixs = pixCopy(NULL, pix); 00796 pixGetDimensions(pixs, &w, &h, NULL); 00797 if (box) 00798 boxGetGeometry(box, &boxx, &boxy, NULL, NULL); 00799 else 00800 boxx = boxy = 0; 00801 boxs = boxCreate(0, 0, w, h); 00802 boxad = boxaCreate(0); 00803 00804 iter = 0; 00805 while (boxs != NULL) { 00806 boxGetGeometry(boxs, &bx, &by, &bw, &bh); 00807 boxat = boxaCreate(4); /* potential rectangular regions */ 00808 nascore = numaCreate(4); 00809 for (i = 0; i < 4; i++) { 00810 pixSearchForRectangle(pixs, boxs, minsum, skipdist, delta, maxbg, 00811 i, boxat, nascore); 00812 } 00813 nas = numaGetSortIndex(nascore, L_SORT_DECREASING); 00814 numaGetIValue(nas, 0, &maxdir); 00815 numaGetIValue(nascore, maxdir, &maxscore); 00816 #if DEBUG_SPLIT 00817 fprintf(stderr, "Iteration: %d\n", iter); 00818 boxPrintStreamInfo(stderr, boxs); 00819 boxaWriteStream(stderr, boxat); 00820 fprintf(stderr, "\nmaxdir = %d, maxscore = %d\n\n", maxdir, maxscore); 00821 #endif /* DEBUG_SPLIT */ 00822 if (maxscore > 0) { /* accept this */ 00823 boxt1 = boxaGetBox(boxat, maxdir, L_CLONE); 00824 boxt2 = boxTransform(boxt1, boxx, boxy, 1.0, 1.0); 00825 boxaAddBox(boxad, boxt2, L_INSERT); 00826 pixClearInRect(pixs, boxt1); 00827 boxDestroy(&boxt1); 00828 pixClipBoxToForeground(pixs, boxs, NULL, &boxt3); 00829 boxDestroy(&boxs); 00830 boxs = boxt3; 00831 if (boxs) { 00832 boxGetGeometry(boxs, NULL, NULL, &bw, &bh); 00833 if (bw < 2 || bh < 2) 00834 boxDestroy(&boxs); /* we're done */ 00835 } 00836 } 00837 else { /* no more valid rectangles can be found */ 00838 if (remainder == 1) { /* save the last box */ 00839 boxt1 = boxTransform(boxs, boxx, boxy, 1.0, 1.0); 00840 boxaAddBox(boxad, boxt1, L_INSERT); 00841 } 00842 boxDestroy(&boxs); /* we're done */ 00843 } 00844 boxaDestroy(&boxat); 00845 numaDestroy(&nascore); 00846 numaDestroy(&nas); 00847 00848 iter++; 00849 if ((iter == maxcomps) && boxs) { 00850 if (remainder == 1) { /* save the last box */ 00851 boxt1 = boxTransform(boxs, boxx, boxy, 1.0, 1.0); 00852 boxaAddBox(boxad, boxt1, L_INSERT); 00853 } 00854 boxDestroy(&boxs); /* we're done */ 00855 } 00856 } 00857 00858 pixDestroy(&pixs); 00859 return boxad; 00860 } 00861 00862 00863 /*! 00864 * pixSearchForRectangle() 00865 * 00866 * Input: pixs (1 bpp) 00867 * boxs (current region to investigate) 00868 * minsum (minimum pixels to trigger propagation) 00869 * skipdist (distance before computing sum for propagation) 00870 * delta (difference required to stop propagation) 00871 * maxbg (maximum number of allowed bg pixels in ref scan) 00872 * sideflag (side to search from) 00873 * boxat (add result of rectangular region found here) 00874 * nascore (add score for this rectangle here) 00875 * Return: 0 if OK, 1 on error 00876 * 00877 * Notes: 00878 * (1) See pixSplitByRectangles() for an explanation of the algorithm. 00879 * This does the sweep from a single side. For each iteration 00880 * in pixSplitByRectangles(), this will be called 4 times, 00881 * for @sideflag = {0, 1, 2, 3}. 00882 * (2) If a valid rectangle is not found, add a score of 0 and 00883 * input a minimum box. 00884 */ 00885 static l_int32 00886 pixSearchForRectangle(PIX *pixs, 00887 BOX *boxs, 00888 l_int32 minsum, 00889 l_int32 skipdist, 00890 l_int32 delta, 00891 l_int32 maxbg, 00892 l_int32 sideflag, 00893 BOXA *boxat, 00894 NUMA *nascore) 00895 { 00896 l_int32 bx, by, bw, bh, width, height, setref, atref; 00897 l_int32 minincol, maxincol, mininrow, maxinrow, minval, maxval, bgref; 00898 l_int32 x, y, x0, y0, xref, yref, colsum, rowsum, score, countref, diff; 00899 void **lines1; 00900 BOX *boxr; 00901 00902 PROCNAME("pixSearchForRectangle"); 00903 00904 if (!pixs || pixGetDepth(pixs) != 1) 00905 return ERROR_INT("pixs undefined or not 1 bpp", procName, 1); 00906 if (!boxs) 00907 return ERROR_INT("boxs not defined", procName, 1); 00908 if (!boxat) 00909 return ERROR_INT("boxat not defined", procName, 1); 00910 if (!nascore) 00911 return ERROR_INT("nascore not defined", procName, 1); 00912 00913 lines1 = pixGetLinePtrs(pixs, NULL); 00914 boxGetGeometry(boxs, &bx, &by, &bw, &bh); 00915 boxr = NULL; 00916 setref = 0; 00917 atref = 0; 00918 maxval = 0; 00919 minval = 100000; 00920 score = 0; /* sum of all (fg - bg) pixels seen in the scan */ 00921 xref = yref = 100000; /* init to impossibly big number */ 00922 if (sideflag == L_FROM_LEFT) { 00923 for (x = bx; x < bx + bw; x++) { 00924 colsum = 0; 00925 maxincol = 0; 00926 minincol = 100000; 00927 for (y = by; y < by + bh; y++) { 00928 if (GET_DATA_BIT(lines1[y], x)) { 00929 colsum++; 00930 if (y > maxincol) maxincol = y; 00931 if (y < minincol) minincol = y; 00932 } 00933 } 00934 score += colsum; 00935 00936 /* Enough fg to sweep out a rectangle? */ 00937 if (!setref && colsum >= minsum) { 00938 setref = 1; 00939 xref = x + 10; 00940 if (xref >= bx + bw) 00941 goto failure; 00942 } 00943 00944 /* Reached the reference line; save the count; 00945 * if there is too much bg, the rectangle is invalid. */ 00946 if (setref && x == xref) { 00947 atref = 1; 00948 countref = colsum; 00949 bgref = maxincol - minincol + 1 - countref; 00950 if (bgref > maxbg) 00951 goto failure; 00952 } 00953 00954 /* Have we left the rectangle? If so, save it along 00955 * with the score. */ 00956 if (atref) { 00957 diff = L_ABS(colsum - countref); 00958 if (diff >= delta || x == bx + bw - 1) { 00959 height = maxval - minval + 1; 00960 width = x - bx; 00961 if (x == bx + bw - 1) width = x - bx + 1; 00962 boxr = boxCreate(bx, minval, width, height); 00963 score = 2 * score - width * height; 00964 goto success; 00965 } 00966 } 00967 maxval = L_MAX(maxval, maxincol); 00968 minval = L_MIN(minval, minincol); 00969 } 00970 goto failure; 00971 } 00972 else if (sideflag == L_FROM_RIGHT) { 00973 for (x = bx + bw - 1; x >= bx; x--) { 00974 colsum = 0; 00975 maxincol = 0; 00976 minincol = 100000; 00977 for (y = by; y < by + bh; y++) { 00978 if (GET_DATA_BIT(lines1[y], x)) { 00979 colsum++; 00980 if (y > maxincol) maxincol = y; 00981 if (y < minincol) minincol = y; 00982 } 00983 } 00984 score += colsum; 00985 if (!setref && colsum >= minsum) { 00986 setref = 1; 00987 xref = x - 10; 00988 if (xref < bx) 00989 goto failure; 00990 } 00991 if (setref && x == xref) { 00992 atref = 1; 00993 countref = colsum; 00994 bgref = maxincol - minincol + 1 - countref; 00995 if (bgref > maxbg) 00996 goto failure; 00997 } 00998 if (atref) { 00999 diff = L_ABS(colsum - countref); 01000 if (diff >= delta || x == bx) { 01001 height = maxval - minval + 1; 01002 x0 = x + 1; 01003 if (x == bx) x0 = x; 01004 width = bx + bw - x0; 01005 boxr = boxCreate(x0, minval, width, height); 01006 score = 2 * score - width * height; 01007 goto success; 01008 } 01009 } 01010 maxval = L_MAX(maxval, maxincol); 01011 minval = L_MIN(minval, minincol); 01012 } 01013 goto failure; 01014 } 01015 else if (sideflag == L_FROM_TOP) { 01016 for (y = by; y < by + bh; y++) { 01017 rowsum = 0; 01018 maxinrow = 0; 01019 mininrow = 100000; 01020 for (x = bx; x < bx + bw; x++) { 01021 if (GET_DATA_BIT(lines1[y], x)) { 01022 rowsum++; 01023 if (x > maxinrow) maxinrow = x; 01024 if (x < mininrow) mininrow = x; 01025 } 01026 } 01027 score += rowsum; 01028 if (!setref && rowsum >= minsum) { 01029 setref = 1; 01030 yref = y + 10; 01031 if (yref >= by + bh) 01032 goto failure; 01033 } 01034 if (setref && y == yref) { 01035 atref = 1; 01036 countref = rowsum; 01037 bgref = maxinrow - mininrow + 1 - countref; 01038 if (bgref > maxbg) 01039 goto failure; 01040 } 01041 if (atref) { 01042 diff = L_ABS(rowsum - countref); 01043 if (diff >= delta || y == by + bh - 1) { 01044 width = maxval - minval + 1; 01045 height = y - by; 01046 if (y == by + bh - 1) height = y - by + 1; 01047 boxr = boxCreate(minval, by, width, height); 01048 score = 2 * score - width * height; 01049 goto success; 01050 } 01051 } 01052 maxval = L_MAX(maxval, maxinrow); 01053 minval = L_MIN(minval, mininrow); 01054 } 01055 goto failure; 01056 } else if (sideflag == L_FROM_BOTTOM) { 01057 for (y = by + bh - 1; y >= by; y--) { 01058 rowsum = 0; 01059 maxinrow = 0; 01060 mininrow = 100000; 01061 for (x = bx; x < bx + bw; x++) { 01062 if (GET_DATA_BIT(lines1[y], x)) { 01063 rowsum++; 01064 if (x > maxinrow) maxinrow = x; 01065 if (x < mininrow) mininrow = x; 01066 } 01067 } 01068 score += rowsum; 01069 if (!setref && rowsum >= minsum) { 01070 setref = 1; 01071 yref = y - 10; 01072 if (yref < by) 01073 goto failure; 01074 } 01075 if (setref && y == yref) { 01076 atref = 1; 01077 countref = rowsum; 01078 bgref = maxinrow - mininrow + 1 - countref; 01079 if (bgref > maxbg) 01080 goto failure; 01081 } 01082 if (atref) { 01083 diff = L_ABS(rowsum - countref); 01084 if (diff >= delta || y == by) { 01085 width = maxval - minval + 1; 01086 y0 = y + 1; 01087 if (y == by) y0 = y; 01088 height = by + bh - y0; 01089 boxr = boxCreate(minval, y0, width, height); 01090 score = 2 * score - width * height; 01091 goto success; 01092 } 01093 } 01094 maxval = L_MAX(maxval, maxinrow); 01095 minval = L_MIN(minval, mininrow); 01096 } 01097 goto failure; 01098 } 01099 01100 failure: 01101 numaAddNumber(nascore, 0); 01102 boxaAddBox(boxat, boxCreate(0, 0, 1, 1), L_INSERT); /* min box */ 01103 FREE(lines1); 01104 return 0; 01105 01106 success: 01107 numaAddNumber(nascore, score); 01108 boxaAddBox(boxat, boxr, L_INSERT); 01109 FREE(lines1); 01110 return 0; 01111 } 01112 01113