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 * pixafunc1.c 00018 * 00019 * Filters 00020 * PIX *pixSelectBySize() 00021 * PIXA *pixaSelectBySize() 00022 * PIX *pixSelectByAreaPerimRatio() 00023 * PIXA *pixaSelectByAreaPerimRatio() 00024 * PIX *pixSelectByAreaFraction() 00025 * PIXA *pixaSelectByAreaFraction() 00026 * PIX *pixSelectByWidthHeightRatio() 00027 * PIXA *pixaSelectByWidthHeightRatio() 00028 * PIXA *pixaSelectWithIndicator() 00029 * l_int32 pixRemoveWithIndicator() 00030 * l_int32 pixAddWithIndicator() 00031 * 00032 * Sort functions 00033 * PIXA *pixaSort() 00034 * PIXA *pixaBinSort() 00035 * PIXA *pixaSortByIndex() 00036 * PIXAA *pixaSort2dByIndex() 00037 * 00038 * Miscellaneous 00039 * PIXA *pixaAddBorderGeneral() 00040 * PIXA *pixaaFlattenToPixa() 00041 * l_int32 pixaSizeRange() 00042 * PIXA *pixaClipToPix() 00043 * l_int32 pixaAnyColormaps() 00044 * l_int32 pixaGetDepthInfo() 00045 * l_int32 pixaEqual() 00046 */ 00047 00048 #include <string.h> 00049 #include "allheaders.h" 00050 00051 /* For more than this number of c.c. in a binarized image of 00052 * semi-perimeter (w + h) about 5000 or less, the O(n) binsort 00053 * is faster than the O(nlogn) shellsort. */ 00054 static const l_int32 MIN_COMPS_FOR_BIN_SORT = 500; 00055 00056 00057 /*---------------------------------------------------------------------* 00058 * Filters * 00059 *---------------------------------------------------------------------*/ 00060 /* 00061 * These filters work on the connected components of 1 bpp images. 00062 * They are typically used on pixa that have been generated from a Pix 00063 * using pixConnComp(), so that the corresponding Boxa is available. 00064 * 00065 * The filters remove or retain c.c. based on these properties: 00066 * (a) size [pixaFindDimensions()] 00067 * (b) area-to-perimeter ratio [pixaFindAreaPerimRatio()] 00068 * (c) foreground area as a fraction of bounding box area (w * h) 00069 * [pixaFindForegroundArea()] 00070 * (d) number of foreground pixels [pixaCountPixels()] 00071 * (e) width/height aspect ratio [pixFindWidthHeightRatio()] 00072 * 00073 * We provide two different high-level interfaces: 00074 * (1) Functions that use one of the filters on either 00075 * a pix or the pixa of components. 00076 * (2) A general method that generates numas of indicator functions, 00077 * logically combines them, and efficiently removes or adds 00078 * the selected components. 00079 * 00080 * For interface (1), the filtering is performed with a single function call. 00081 * This is the easiest way to do simple filtering. These functions 00082 * are named pixSelectBy*() and pixaSelectBy*(), where the '*' is one of: 00083 * Size 00084 * AreaPerimRatio 00085 * AreaFraction 00086 * WidthHeightRatio 00087 * 00088 * For more complicated filtering, use the general method (2). 00089 * The numa indicator functions for a pixa are generated by these functions: 00090 * pixaFindDimensions() 00091 * pixaFindAreaPerimRatio() 00092 * pixaFindAreaFraction() 00093 * pixaCountPixels() 00094 * pixaFindWidthHeightRatio() 00095 * pixaFindWidthHeightProduct() 00096 * 00097 * Here is an illustration using the general method. Suppose you want 00098 * all 8-connected components that have a height greater than 40 pixels, 00099 * a width not more than 30 pixels, between 150 and 300 fg pixels, 00100 * and an area-to-perimeter ratio between 2.5 and 4.0. 00101 * 00102 * // Generate the pixa of 8 cc pieces. 00103 * boxa = pixConnComp(pixs, &pixa, 8); 00104 * 00105 * // Extract the data we need about each component. 00106 * pixaFindDimensions(pixa, &naw, &nah); 00107 * nas = pixaCountPixels(pixa); 00108 * nar = pixaFindAreaPerimRatio(pixa); 00109 * 00110 * // Build the indicator arrays for the set of components, 00111 * // based on thresholds and selection criteria. 00112 * na1 = numaMakeThresholdIndicator(nah, 40, L_SELECT_IF_GT); 00113 * na2 = numaMakeThresholdIndicator(naw, 30, L_SELECT_IF_LTE); 00114 * na3 = numaMakeThresholdIndicator(nas, 150, L_SELECT_IF_GTE); 00115 * na4 = numaMakeThresholdIndicator(nas, 300, L_SELECT_IF_LTE); 00116 * na5 = numaMakeThresholdIndicator(nar, 2.5, L_SELECT_IF_GTE); 00117 * na6 = numaMakeThresholdIndicator(nar, 4.0, L_SELECT_IF_LGE); 00118 * 00119 * // Combine the indicator arrays logically to find 00120 * // the components that will be retained. 00121 * nad = numaLogicalOp(NULL, na1, na2, L_INTERSECTION); 00122 * numaLogicalOp(nad, nad, na3, L_INTERSECTION); 00123 * numaLogicalOp(nad, nad, na4, L_INTERSECTION); 00124 * numaLogicalOp(nad, nad, na5, L_INTERSECTION); 00125 * numaLogicalOp(nad, nad, na6, L_INTERSECTION); 00126 * 00127 * // Invert to get the components that will be removed. 00128 * numaInvert(nad, nad); 00129 * 00130 * // Remove the components, in-place. 00131 * pixRemoveWithIndicator(pixs, pixa, nad); 00132 */ 00133 00134 00135 /*! 00136 * pixSelectBySize() 00137 * 00138 * Input: pixs (1 bpp) 00139 * width, height (threshold dimensions) 00140 * connectivity (4 or 8) 00141 * type (L_SELECT_WIDTH, L_SELECT_HEIGHT, 00142 * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH) 00143 * relation (L_SELECT_IF_LT, L_SELECT_IF_GT, 00144 * L_SELECT_IF_LTE, L_SELECT_IF_GTE) 00145 * &changed (<optional return> 1 if changed; 0 otherwise) 00146 * Return: filtered pixd, or null on error 00147 * 00148 * Notes: 00149 * (1) The args specify constraints on the size of the 00150 * components that are kept. 00151 * (2) If unchanged, returns a copy of pixs. Otherwise, 00152 * returns a new pix with the filtered components. 00153 * (3) If the selection type is L_SELECT_WIDTH, the input 00154 * height is ignored, and v.v. 00155 * (4) To keep small components, use relation = L_SELECT_IF_LT or 00156 * L_SELECT_IF_LTE. 00157 * To keep large components, use relation = L_SELECT_IF_GT or 00158 * L_SELECT_IF_GTE. 00159 */ 00160 PIX * 00161 pixSelectBySize(PIX *pixs, 00162 l_int32 width, 00163 l_int32 height, 00164 l_int32 connectivity, 00165 l_int32 type, 00166 l_int32 relation, 00167 l_int32 *pchanged) 00168 { 00169 l_int32 w, h, empty, changed, count; 00170 BOXA *boxa; 00171 PIX *pixd; 00172 PIXA *pixas, *pixad; 00173 00174 PROCNAME("pixSelectBySize"); 00175 00176 if (!pixs) 00177 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00178 if (connectivity != 4 && connectivity != 8) 00179 return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); 00180 if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && 00181 type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) 00182 return (PIX *)ERROR_PTR("invalid type", procName, NULL); 00183 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && 00184 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) 00185 return (PIX *)ERROR_PTR("invalid relation", procName, NULL); 00186 if (pchanged) *pchanged = FALSE; 00187 00188 /* Check if any components exist */ 00189 pixZero(pixs, &empty); 00190 if (empty) 00191 return pixCopy(NULL, pixs); 00192 00193 /* Identify and select the components */ 00194 boxa = pixConnComp(pixs, &pixas, connectivity); 00195 pixad = pixaSelectBySize(pixas, width, height, type, relation, &changed); 00196 boxaDestroy(&boxa); 00197 pixaDestroy(&pixas); 00198 00199 /* Render the result */ 00200 if (!changed) { 00201 pixaDestroy(&pixad); 00202 return pixCopy(NULL, pixs); 00203 } 00204 else { 00205 if (pchanged) *pchanged = TRUE; 00206 pixGetDimensions(pixs, &w, &h, NULL); 00207 count = pixaGetCount(pixad); 00208 if (count == 0) /* return empty pix */ 00209 pixd = pixCreateTemplate(pixs); 00210 else { 00211 pixd = pixaDisplay(pixad, w, h); 00212 pixCopyResolution(pixd, pixs); 00213 pixCopyColormap(pixd, pixs); 00214 pixCopyText(pixd, pixs); 00215 pixCopyInputFormat(pixd, pixs); 00216 } 00217 pixaDestroy(&pixad); 00218 return pixd; 00219 } 00220 } 00221 00222 00223 /*! 00224 * pixaSelectBySize() 00225 * 00226 * Input: pixas 00227 * width, height (threshold dimensions) 00228 * type (L_SELECT_WIDTH, L_SELECT_HEIGHT, 00229 * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH) 00230 * relation (L_SELECT_IF_LT, L_SELECT_IF_GT, 00231 * L_SELECT_IF_LTE, L_SELECT_IF_GTE) 00232 * &changed (<optional return> 1 if changed; 0 otherwise) 00233 * Return: pixad, or null on error 00234 * 00235 * Notes: 00236 * (1) The args specify constraints on the size of the 00237 * components that are kept. 00238 * (2) Uses pix and box clones in the new pixa. 00239 * (3) If the selection type is L_SELECT_WIDTH, the input 00240 * height is ignored, and v.v. 00241 * (4) To keep small components, use relation = L_SELECT_IF_LT or 00242 * L_SELECT_IF_LTE. 00243 * To keep large components, use relation = L_SELECT_IF_GT or 00244 * L_SELECT_IF_GTE. 00245 */ 00246 PIXA * 00247 pixaSelectBySize(PIXA *pixas, 00248 l_int32 width, 00249 l_int32 height, 00250 l_int32 type, 00251 l_int32 relation, 00252 l_int32 *pchanged) 00253 { 00254 BOXA *boxa; 00255 NUMA *na; 00256 PIXA *pixad; 00257 00258 PROCNAME("pixaSelectBySize"); 00259 00260 if (!pixas) 00261 return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); 00262 if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && 00263 type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) 00264 return (PIXA *)ERROR_PTR("invalid type", procName, NULL); 00265 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && 00266 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) 00267 return (PIXA *)ERROR_PTR("invalid relation", procName, NULL); 00268 00269 /* Compute the indicator array for saving components */ 00270 boxa = pixaGetBoxa(pixas, L_CLONE); 00271 na = boxaMakeSizeIndicator(boxa, width, height, type, relation); 00272 boxaDestroy(&boxa); 00273 00274 /* Filter to get output */ 00275 pixad = pixaSelectWithIndicator(pixas, na, pchanged); 00276 00277 numaDestroy(&na); 00278 return pixad; 00279 } 00280 00281 00282 /*! 00283 * pixSelectByAreaPerimRatio() 00284 * 00285 * Input: pixs (1 bpp) 00286 * thresh (threshold ratio of interior/boundary pixels) 00287 * connectivity (4 or 8) 00288 * type (L_SELECT_IF_LT, L_SELECT_IF_GT, 00289 * L_SELECT_IF_LTE, L_SELECT_IF_GTE) 00290 * &changed (<optional return> 1 if changed; 0 if clone returned) 00291 * Return: pixd, or null on error 00292 * 00293 * Notes: 00294 * (1) The args specify constraints on the size of the 00295 * components that are kept. 00296 * (2) If unchanged, returns a copy of pixs. Otherwise, 00297 * returns a new pix with the filtered components. 00298 * (3) This filters "thin" components, where a thin component 00299 * is defined to have a ratio of interior to boundary pixels 00300 * that is smaller than a given threshold value. 00301 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save the thin 00302 * components, and L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. 00303 */ 00304 PIX * 00305 pixSelectByAreaPerimRatio(PIX *pixs, 00306 l_float32 thresh, 00307 l_int32 connectivity, 00308 l_int32 type, 00309 l_int32 *pchanged) 00310 { 00311 l_int32 w, h, empty, changed, count; 00312 BOXA *boxa; 00313 PIX *pixd; 00314 PIXA *pixas, *pixad; 00315 00316 PROCNAME("pixSelectByAreaPerimRatio"); 00317 00318 if (!pixs) 00319 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00320 if (connectivity != 4 && connectivity != 8) 00321 return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); 00322 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && 00323 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) 00324 return (PIX *)ERROR_PTR("invalid type", procName, NULL); 00325 if (pchanged) *pchanged = FALSE; 00326 00327 /* Check if any components exist */ 00328 pixZero(pixs, &empty); 00329 if (empty) 00330 return pixCopy(NULL, pixs); 00331 00332 /* Filter thin components */ 00333 boxa = pixConnComp(pixs, &pixas, connectivity); 00334 pixad = pixaSelectByAreaPerimRatio(pixas, thresh, type, &changed); 00335 boxaDestroy(&boxa); 00336 pixaDestroy(&pixas); 00337 00338 /* Render the result */ 00339 if (!changed) { 00340 pixaDestroy(&pixad); 00341 return pixCopy(NULL, pixs); 00342 } 00343 else { 00344 if (pchanged) *pchanged = TRUE; 00345 pixGetDimensions(pixs, &w, &h, NULL); 00346 count = pixaGetCount(pixad); 00347 if (count == 0) /* return empty pix */ 00348 pixd = pixCreateTemplate(pixs); 00349 else { 00350 pixd = pixaDisplay(pixad, w, h); 00351 pixCopyResolution(pixd, pixs); 00352 pixCopyColormap(pixd, pixs); 00353 pixCopyText(pixd, pixs); 00354 pixCopyInputFormat(pixd, pixs); 00355 } 00356 pixaDestroy(&pixad); 00357 return pixd; 00358 } 00359 } 00360 00361 00362 /*! 00363 * pixaSelectByAreaPerimRatio() 00364 * 00365 * Input: pixas 00366 * thresh (threshold ratio of interior/boundary pixels) 00367 * type (L_SELECT_IF_LT, L_SELECT_IF_GT, 00368 * L_SELECT_IF_LTE, L_SELECT_IF_GTE) 00369 * &changed (<optional return> 1 if changed; 0 if clone returned) 00370 * Return: pixad, or null on error 00371 * 00372 * Notes: 00373 * (1) Returns a pixa clone if no components are removed. 00374 * (2) Uses pix and box clones in the new pixa. 00375 * (3) We define a thin component here to be one with a ratio 00376 * of interior to boundary pixels that is smaller than a given 00377 * threshold value. 00378 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save the thin 00379 * components, and L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. 00380 */ 00381 PIXA * 00382 pixaSelectByAreaPerimRatio(PIXA *pixas, 00383 l_float32 thresh, 00384 l_int32 type, 00385 l_int32 *pchanged) 00386 { 00387 NUMA *na, *nai; 00388 PIXA *pixad; 00389 00390 PROCNAME("pixaSelectByAreaPerimRatio"); 00391 00392 if (!pixas) 00393 return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); 00394 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && 00395 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) 00396 return (PIXA *)ERROR_PTR("invalid type", procName, NULL); 00397 00398 /* Compute component ratios. */ 00399 na = pixaFindAreaPerimRatio(pixas); 00400 00401 /* Generate indicator array for elements to be saved. */ 00402 nai = numaMakeThresholdIndicator(na, thresh, type); 00403 numaDestroy(&na); 00404 00405 /* Filter to get output */ 00406 pixad = pixaSelectWithIndicator(pixas, nai, pchanged); 00407 00408 numaDestroy(&nai); 00409 return pixad; 00410 } 00411 00412 00413 /*! 00414 * pixSelectByAreaFraction() 00415 * 00416 * Input: pixs (1 bpp) 00417 * thresh (threshold ratio of fg pixels to (w * h)) 00418 * connectivity (4 or 8) 00419 * type (L_SELECT_IF_LT, L_SELECT_IF_GT, 00420 * L_SELECT_IF_LTE, L_SELECT_IF_GTE) 00421 * &changed (<optional return> 1 if changed; 0 if clone returned) 00422 * Return: pixd, or null on error 00423 * 00424 * Notes: 00425 * (1) The args specify constraints on the amount of foreground 00426 * coverage of the components that are kept. 00427 * (2) If unchanged, returns a copy of pixs. Otherwise, 00428 * returns a new pix with the filtered components. 00429 * (3) This filters components based on the fraction of fg pixels 00430 * of the component in its bounding box. 00431 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components 00432 * with less than the threshold fraction of foreground, and 00433 * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. 00434 */ 00435 PIX * 00436 pixSelectByAreaFraction(PIX *pixs, 00437 l_float32 thresh, 00438 l_int32 connectivity, 00439 l_int32 type, 00440 l_int32 *pchanged) 00441 { 00442 l_int32 w, h, empty, changed, count; 00443 BOXA *boxa; 00444 PIX *pixd; 00445 PIXA *pixas, *pixad; 00446 00447 PROCNAME("pixSelectByAreaFraction"); 00448 00449 if (!pixs) 00450 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00451 if (connectivity != 4 && connectivity != 8) 00452 return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); 00453 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && 00454 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) 00455 return (PIX *)ERROR_PTR("invalid type", procName, NULL); 00456 if (pchanged) *pchanged = FALSE; 00457 00458 /* Check if any components exist */ 00459 pixZero(pixs, &empty); 00460 if (empty) 00461 return pixCopy(NULL, pixs); 00462 00463 /* Filter components */ 00464 boxa = pixConnComp(pixs, &pixas, connectivity); 00465 pixad = pixaSelectByAreaFraction(pixas, thresh, type, &changed); 00466 boxaDestroy(&boxa); 00467 pixaDestroy(&pixas); 00468 00469 /* Render the result */ 00470 if (!changed) { 00471 pixaDestroy(&pixad); 00472 return pixCopy(NULL, pixs); 00473 } 00474 else { 00475 if (pchanged) *pchanged = TRUE; 00476 pixGetDimensions(pixs, &w, &h, NULL); 00477 count = pixaGetCount(pixad); 00478 if (count == 0) /* return empty pix */ 00479 pixd = pixCreateTemplate(pixs); 00480 else { 00481 pixd = pixaDisplay(pixad, w, h); 00482 pixCopyResolution(pixd, pixs); 00483 pixCopyColormap(pixd, pixs); 00484 pixCopyText(pixd, pixs); 00485 pixCopyInputFormat(pixd, pixs); 00486 } 00487 pixaDestroy(&pixad); 00488 return pixd; 00489 } 00490 } 00491 00492 00493 /*! 00494 * pixaSelectByAreaFraction() 00495 * 00496 * Input: pixas 00497 * thresh (threshold ratio of fg pixels to (w * h)) 00498 * type (L_SELECT_IF_LT, L_SELECT_IF_GT, 00499 * L_SELECT_IF_LTE, L_SELECT_IF_GTE) 00500 * &changed (<optional return> 1 if changed; 0 if clone returned) 00501 * Return: pixad, or null on error 00502 * 00503 * Notes: 00504 * (1) Returns a pixa clone if no components are removed. 00505 * (2) Uses pix and box clones in the new pixa. 00506 * (3) This filters components based on the fraction of fg pixels 00507 * of the component in its bounding box. 00508 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components 00509 * with less than the threshold fraction of foreground, and 00510 * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. 00511 */ 00512 PIXA * 00513 pixaSelectByAreaFraction(PIXA *pixas, 00514 l_float32 thresh, 00515 l_int32 type, 00516 l_int32 *pchanged) 00517 { 00518 NUMA *na, *nai; 00519 PIXA *pixad; 00520 00521 PROCNAME("pixaSelectByAreaFraction"); 00522 00523 if (!pixas) 00524 return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); 00525 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && 00526 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) 00527 return (PIXA *)ERROR_PTR("invalid type", procName, NULL); 00528 00529 /* Compute component ratios. */ 00530 na = pixaFindAreaFraction(pixas); 00531 00532 /* Generate indicator array for elements to be saved. */ 00533 nai = numaMakeThresholdIndicator(na, thresh, type); 00534 numaDestroy(&na); 00535 00536 /* Filter to get output */ 00537 pixad = pixaSelectWithIndicator(pixas, nai, pchanged); 00538 00539 numaDestroy(&nai); 00540 return pixad; 00541 } 00542 00543 00544 /*! 00545 * pixSelectByWidthHeightRatio() 00546 * 00547 * Input: pixs (1 bpp) 00548 * thresh (threshold ratio of width/height) 00549 * connectivity (4 or 8) 00550 * type (L_SELECT_IF_LT, L_SELECT_IF_GT, 00551 * L_SELECT_IF_LTE, L_SELECT_IF_GTE) 00552 * &changed (<optional return> 1 if changed; 0 if clone returned) 00553 * Return: pixd, or null on error 00554 * 00555 * Notes: 00556 * (1) The args specify constraints on the width-to-height ratio 00557 * for components that are kept. 00558 * (2) If unchanged, returns a copy of pixs. Otherwise, 00559 * returns a new pix with the filtered components. 00560 * (3) This filters components based on the width-to-height ratios. 00561 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components 00562 * with less than the threshold ratio, and 00563 * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. 00564 */ 00565 PIX * 00566 pixSelectByWidthHeightRatio(PIX *pixs, 00567 l_float32 thresh, 00568 l_int32 connectivity, 00569 l_int32 type, 00570 l_int32 *pchanged) 00571 { 00572 l_int32 w, h, empty, changed, count; 00573 BOXA *boxa; 00574 PIX *pixd; 00575 PIXA *pixas, *pixad; 00576 00577 PROCNAME("pixSelectByWidthHeightRatio"); 00578 00579 if (!pixs) 00580 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00581 if (connectivity != 4 && connectivity != 8) 00582 return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); 00583 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && 00584 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) 00585 return (PIX *)ERROR_PTR("invalid type", procName, NULL); 00586 if (pchanged) *pchanged = FALSE; 00587 00588 /* Check if any components exist */ 00589 pixZero(pixs, &empty); 00590 if (empty) 00591 return pixCopy(NULL, pixs); 00592 00593 /* Filter components */ 00594 boxa = pixConnComp(pixs, &pixas, connectivity); 00595 pixad = pixaSelectByWidthHeightRatio(pixas, thresh, type, &changed); 00596 boxaDestroy(&boxa); 00597 pixaDestroy(&pixas); 00598 00599 /* Render the result */ 00600 if (!changed) { 00601 pixaDestroy(&pixad); 00602 return pixCopy(NULL, pixs); 00603 } 00604 else { 00605 if (pchanged) *pchanged = TRUE; 00606 pixGetDimensions(pixs, &w, &h, NULL); 00607 count = pixaGetCount(pixad); 00608 if (count == 0) /* return empty pix */ 00609 pixd = pixCreateTemplate(pixs); 00610 else { 00611 pixd = pixaDisplay(pixad, w, h); 00612 pixCopyResolution(pixd, pixs); 00613 pixCopyColormap(pixd, pixs); 00614 pixCopyText(pixd, pixs); 00615 pixCopyInputFormat(pixd, pixs); 00616 } 00617 pixaDestroy(&pixad); 00618 return pixd; 00619 } 00620 } 00621 00622 00623 /*! 00624 * pixaSelectByWidthHeightRatio() 00625 * 00626 * Input: pixas 00627 * thresh (threshold ratio of width/height) 00628 * type (L_SELECT_IF_LT, L_SELECT_IF_GT, 00629 * L_SELECT_IF_LTE, L_SELECT_IF_GTE) 00630 * &changed (<optional return> 1 if changed; 0 if clone returned) 00631 * Return: pixad, or null on error 00632 * 00633 * Notes: 00634 * (1) Returns a pixa clone if no components are removed. 00635 * (2) Uses pix and box clones in the new pixa. 00636 * (3) This filters components based on the width-to-height ratio 00637 * of each pix. 00638 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components 00639 * with less than the threshold ratio, and 00640 * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. 00641 */ 00642 PIXA * 00643 pixaSelectByWidthHeightRatio(PIXA *pixas, 00644 l_float32 thresh, 00645 l_int32 type, 00646 l_int32 *pchanged) 00647 { 00648 NUMA *na, *nai; 00649 PIXA *pixad; 00650 00651 PROCNAME("pixaSelectByWidthHeightRatio"); 00652 00653 if (!pixas) 00654 return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); 00655 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && 00656 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) 00657 return (PIXA *)ERROR_PTR("invalid type", procName, NULL); 00658 00659 /* Compute component ratios. */ 00660 na = pixaFindWidthHeightRatio(pixas); 00661 00662 /* Generate indicator array for elements to be saved. */ 00663 nai = numaMakeThresholdIndicator(na, thresh, type); 00664 numaDestroy(&na); 00665 00666 /* Filter to get output */ 00667 pixad = pixaSelectWithIndicator(pixas, nai, pchanged); 00668 00669 numaDestroy(&nai); 00670 return pixad; 00671 } 00672 00673 00674 /*! 00675 * pixaSelectWithIndicator() 00676 * 00677 * Input: pixas 00678 * na (indicator numa) 00679 * &changed (<optional return> 1 if changed; 0 if clone returned) 00680 * Return: pixad, or null on error 00681 * 00682 * Notes: 00683 * (1) Returns a pixa clone if no components are removed. 00684 * (2) Uses pix and box clones in the new pixa. 00685 * (3) The indicator numa has values 0 (ignore) and 1 (accept). 00686 */ 00687 PIXA * 00688 pixaSelectWithIndicator(PIXA *pixas, 00689 NUMA *na, 00690 l_int32 *pchanged) 00691 { 00692 l_int32 i, n, ival, nsave; 00693 BOX *box; 00694 PIX *pixt; 00695 PIXA *pixad; 00696 00697 PROCNAME("pixaSelectWithIndicator"); 00698 00699 if (!pixas) 00700 return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); 00701 if (!na) 00702 return (PIXA *)ERROR_PTR("na not defined", procName, NULL); 00703 00704 nsave = 0; 00705 n = numaGetCount(na); 00706 for (i = 0; i < n; i++) { 00707 numaGetIValue(na, i, &ival); 00708 if (ival == 1) nsave++; 00709 } 00710 00711 if (nsave == n) { 00712 if (pchanged) *pchanged = FALSE; 00713 return pixaCopy(pixas, L_CLONE); 00714 } 00715 if (pchanged) *pchanged = TRUE; 00716 pixad = pixaCreate(nsave); 00717 for (i = 0; i < n; i++) { 00718 numaGetIValue(na, i, &ival); 00719 if (ival == 0) continue; 00720 pixt = pixaGetPix(pixas, i, L_CLONE); 00721 box = pixaGetBox(pixas, i, L_CLONE); 00722 pixaAddPix(pixad, pixt, L_INSERT); 00723 pixaAddBox(pixad, box, L_INSERT); 00724 } 00725 00726 return pixad; 00727 } 00728 00729 00730 /*! 00731 * pixRemoveWithIndicator() 00732 * 00733 * Input: pixs (1 bpp pix from which components are removed; in-place) 00734 * pixa (of connected components in pixs) 00735 * na (numa indicator: remove components corresponding to 1s) 00736 * Return: 0 if OK, 1 on error 00737 * 00738 * Notes: 00739 * (1) This complements pixAddWithIndicator(). Here, the selected 00740 * components are set subtracted from pixs. 00741 */ 00742 l_int32 00743 pixRemoveWithIndicator(PIX *pixs, 00744 PIXA *pixa, 00745 NUMA *na) 00746 { 00747 l_int32 i, n, ival, x, y, w, h; 00748 BOX *box; 00749 PIX *pix; 00750 00751 PROCNAME("pixRemoveWithIndicator"); 00752 00753 if (!pixs) 00754 return ERROR_INT("pixs not defined", procName, 1); 00755 if (!pixa) 00756 return ERROR_INT("pixa not defined", procName, 1); 00757 if (!na) 00758 return ERROR_INT("na not defined", procName, 1); 00759 n = pixaGetCount(pixa); 00760 if (n != numaGetCount(na)) 00761 return ERROR_INT("pixa and na sizes not equal", procName, 1); 00762 00763 for (i = 0; i < n; i++) { 00764 numaGetIValue(na, i, &ival); 00765 if (ival == 1) { 00766 pix = pixaGetPix(pixa, i, L_CLONE); 00767 box = pixaGetBox(pixa, i, L_CLONE); 00768 boxGetGeometry(box, &x, &y, &w, &h); 00769 pixRasterop(pixs, x, y, w, h, PIX_DST & PIX_NOT(PIX_SRC), 00770 pix, 0, 0); 00771 boxDestroy(&box); 00772 pixDestroy(&pix); 00773 } 00774 } 00775 00776 return 0; 00777 } 00778 00779 00780 /*! 00781 * pixAddWithIndicator() 00782 * 00783 * Input: pixs (1 bpp pix from which components are added; in-place) 00784 * pixa (of connected components, some of which will be put 00785 * into pixs) 00786 * na (numa indicator: add components corresponding to 1s) 00787 * Return: 0 if OK, 1 on error 00788 * 00789 * Notes: 00790 * (1) This complements pixRemoveWithIndicator(). Here, the selected 00791 * components are added to pixs. 00792 */ 00793 l_int32 00794 pixAddWithIndicator(PIX *pixs, 00795 PIXA *pixa, 00796 NUMA *na) 00797 { 00798 l_int32 i, n, ival, x, y, w, h; 00799 BOX *box; 00800 PIX *pix; 00801 00802 PROCNAME("pixAddWithIndicator"); 00803 00804 if (!pixs) 00805 return ERROR_INT("pixs not defined", procName, 1); 00806 if (!pixa) 00807 return ERROR_INT("pixa not defined", procName, 1); 00808 if (!na) 00809 return ERROR_INT("na not defined", procName, 1); 00810 n = pixaGetCount(pixa); 00811 if (n != numaGetCount(na)) 00812 return ERROR_INT("pixa and na sizes not equal", procName, 1); 00813 00814 for (i = 0; i < n; i++) { 00815 numaGetIValue(na, i, &ival); 00816 if (ival == 1) { 00817 pix = pixaGetPix(pixa, i, L_CLONE); 00818 box = pixaGetBox(pixa, i, L_CLONE); 00819 boxGetGeometry(box, &x, &y, &w, &h); 00820 pixRasterop(pixs, x, y, w, h, PIX_SRC | PIX_DST, pix, 0, 0); 00821 boxDestroy(&box); 00822 pixDestroy(&pix); 00823 } 00824 } 00825 00826 return 0; 00827 } 00828 00829 00830 /*---------------------------------------------------------------------* 00831 * Sort functions * 00832 *---------------------------------------------------------------------*/ 00833 /*! 00834 * pixaSort() 00835 * 00836 * Input: pixas 00837 * sorttype (L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH, 00838 * L_SORT_BY_HEIGHT, L_SORT_BY_MIN_DIMENSION, 00839 * L_SORT_BY_MAX_DIMENSION, L_SORT_BY_PERIMETER, 00840 * L_SORT_BY_AREA, L_SORT_BY_ASPECT_RATIO) 00841 * sortorder (L_SORT_INCREASING, L_SORT_DECREASING) 00842 * &naindex (<optional return> index of sorted order into 00843 * original array) 00844 * copyflag (L_COPY, L_CLONE) 00845 * Return: pixad (sorted version of pixas), or null on error 00846 * 00847 * Notes: 00848 * (1) This sorts based on the data in the boxa. If the boxa 00849 * count is not the same as the pixa count, this returns an error. 00850 * (2) The copyflag refers to the pix and box copies that are 00851 * inserted into the sorted pixa. These are either L_COPY 00852 * or L_CLONE. 00853 */ 00854 PIXA * 00855 pixaSort(PIXA *pixas, 00856 l_int32 sorttype, 00857 l_int32 sortorder, 00858 NUMA **pnaindex, 00859 l_int32 copyflag) 00860 { 00861 l_int32 i, n, x, y, w, h; 00862 BOXA *boxa; 00863 NUMA *na, *naindex; 00864 PIXA *pixad; 00865 00866 PROCNAME("pixaSort"); 00867 00868 if (pnaindex) *pnaindex = NULL; 00869 if (!pixas) 00870 return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); 00871 if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && 00872 sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT && 00873 sorttype != L_SORT_BY_MIN_DIMENSION && 00874 sorttype != L_SORT_BY_MAX_DIMENSION && 00875 sorttype != L_SORT_BY_PERIMETER && 00876 sorttype != L_SORT_BY_AREA && 00877 sorttype != L_SORT_BY_ASPECT_RATIO) 00878 return (PIXA *)ERROR_PTR("invalid sort type", procName, NULL); 00879 if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) 00880 return (PIXA *)ERROR_PTR("invalid sort order", procName, NULL); 00881 if (copyflag != L_COPY && copyflag != L_CLONE) 00882 return (PIXA *)ERROR_PTR("invalid copy flag", procName, NULL); 00883 00884 if ((boxa = pixas->boxa) == NULL) /* not owned; do not destroy */ 00885 return (PIXA *)ERROR_PTR("boxa not found", procName, NULL); 00886 n = pixaGetCount(pixas); 00887 if (boxaGetCount(boxa) != n) 00888 return (PIXA *)ERROR_PTR("boxa and pixa counts differ", procName, NULL); 00889 00890 /* Use O(n) binsort if possible */ 00891 if (n > MIN_COMPS_FOR_BIN_SORT && 00892 ((sorttype == L_SORT_BY_X) || (sorttype == L_SORT_BY_Y) || 00893 (sorttype == L_SORT_BY_WIDTH) || (sorttype == L_SORT_BY_HEIGHT) || 00894 (sorttype == L_SORT_BY_PERIMETER))) 00895 return pixaBinSort(pixas, sorttype, sortorder, pnaindex, copyflag); 00896 00897 /* Build up numa of specific data */ 00898 if ((na = numaCreate(n)) == NULL) 00899 return (PIXA *)ERROR_PTR("na not made", procName, NULL); 00900 for (i = 0; i < n; i++) { 00901 boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); 00902 switch (sorttype) 00903 { 00904 case L_SORT_BY_X: 00905 numaAddNumber(na, x); 00906 break; 00907 case L_SORT_BY_Y: 00908 numaAddNumber(na, y); 00909 break; 00910 case L_SORT_BY_WIDTH: 00911 numaAddNumber(na, w); 00912 break; 00913 case L_SORT_BY_HEIGHT: 00914 numaAddNumber(na, h); 00915 break; 00916 case L_SORT_BY_MIN_DIMENSION: 00917 numaAddNumber(na, L_MIN(w, h)); 00918 break; 00919 case L_SORT_BY_MAX_DIMENSION: 00920 numaAddNumber(na, L_MAX(w, h)); 00921 break; 00922 case L_SORT_BY_PERIMETER: 00923 numaAddNumber(na, w + h); 00924 break; 00925 case L_SORT_BY_AREA: 00926 numaAddNumber(na, w * h); 00927 break; 00928 case L_SORT_BY_ASPECT_RATIO: 00929 numaAddNumber(na, (l_float32)w / (l_float32)h); 00930 break; 00931 default: 00932 L_WARNING("invalid sort type", procName); 00933 } 00934 } 00935 00936 /* Get the sort index for data array */ 00937 if ((naindex = numaGetSortIndex(na, sortorder)) == NULL) 00938 return (PIXA *)ERROR_PTR("naindex not made", procName, NULL); 00939 00940 /* Build up sorted pixa using sort index */ 00941 if ((pixad = pixaSortByIndex(pixas, naindex, copyflag)) == NULL) 00942 return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); 00943 00944 if (pnaindex) 00945 *pnaindex = naindex; 00946 else 00947 numaDestroy(&naindex); 00948 numaDestroy(&na); 00949 return pixad; 00950 } 00951 00952 00953 /*! 00954 * pixaBinSort() 00955 * 00956 * Input: pixas 00957 * sorttype (L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH, 00958 * L_SORT_BY_HEIGHT, L_SORT_BY_PERIMETER) 00959 * sortorder (L_SORT_INCREASING, L_SORT_DECREASING) 00960 * &naindex (<optional return> index of sorted order into 00961 * original array) 00962 * copyflag (L_COPY, L_CLONE) 00963 * Return: pixad (sorted version of pixas), or null on error 00964 * 00965 * Notes: 00966 * (1) This sorts based on the data in the boxa. If the boxa 00967 * count is not the same as the pixa count, this returns an error. 00968 * (2) The copyflag refers to the pix and box copies that are 00969 * inserted into the sorted pixa. These are either L_COPY 00970 * or L_CLONE. 00971 * (3) For a large number of boxes (say, greater than 1000), this 00972 * O(n) binsort is much faster than the O(nlogn) shellsort. 00973 * For 5000 components, this is over 20x faster than boxaSort(). 00974 * (4) Consequently, pixaSort() calls this function if it will 00975 * likely go much faster. 00976 */ 00977 PIXA * 00978 pixaBinSort(PIXA *pixas, 00979 l_int32 sorttype, 00980 l_int32 sortorder, 00981 NUMA **pnaindex, 00982 l_int32 copyflag) 00983 { 00984 l_int32 i, n, x, y, w, h; 00985 BOXA *boxa; 00986 NUMA *na, *naindex; 00987 PIXA *pixad; 00988 00989 PROCNAME("pixaBinSort"); 00990 00991 if (pnaindex) *pnaindex = NULL; 00992 if (!pixas) 00993 return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); 00994 if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && 00995 sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT && 00996 sorttype != L_SORT_BY_PERIMETER) 00997 return (PIXA *)ERROR_PTR("invalid sort type", procName, NULL); 00998 if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) 00999 return (PIXA *)ERROR_PTR("invalid sort order", procName, NULL); 01000 if (copyflag != L_COPY && copyflag != L_CLONE) 01001 return (PIXA *)ERROR_PTR("invalid copy flag", procName, NULL); 01002 01003 /* Verify that the pixa and its boxa have the same count */ 01004 if ((boxa = pixas->boxa) == NULL) /* not owned; do not destroy */ 01005 return (PIXA *)ERROR_PTR("boxa not found", procName, NULL); 01006 n = pixaGetCount(pixas); 01007 if (boxaGetCount(boxa) != n) 01008 return (PIXA *)ERROR_PTR("boxa and pixa counts differ", procName, NULL); 01009 01010 /* Generate Numa of appropriate box dimensions */ 01011 if ((na = numaCreate(n)) == NULL) 01012 return (PIXA *)ERROR_PTR("na not made", procName, NULL); 01013 for (i = 0; i < n; i++) { 01014 boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); 01015 switch (sorttype) 01016 { 01017 case L_SORT_BY_X: 01018 numaAddNumber(na, x); 01019 break; 01020 case L_SORT_BY_Y: 01021 numaAddNumber(na, y); 01022 break; 01023 case L_SORT_BY_WIDTH: 01024 numaAddNumber(na, w); 01025 break; 01026 case L_SORT_BY_HEIGHT: 01027 numaAddNumber(na, h); 01028 break; 01029 case L_SORT_BY_PERIMETER: 01030 numaAddNumber(na, w + h); 01031 break; 01032 default: 01033 L_WARNING("invalid sort type", procName); 01034 } 01035 } 01036 01037 /* Get the sort index for data array */ 01038 if ((naindex = numaGetBinSortIndex(na, sortorder)) == NULL) 01039 return (PIXA *)ERROR_PTR("naindex not made", procName, NULL); 01040 01041 /* Build up sorted pixa using sort index */ 01042 if ((pixad = pixaSortByIndex(pixas, naindex, copyflag)) == NULL) 01043 return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); 01044 01045 if (pnaindex) 01046 *pnaindex = naindex; 01047 else 01048 numaDestroy(&naindex); 01049 numaDestroy(&na); 01050 return pixad; 01051 } 01052 01053 01054 /*! 01055 * pixaSortByIndex() 01056 * 01057 * Input: pixas 01058 * naindex (na that maps from the new pixa to the input pixa) 01059 * copyflag (L_COPY, L_CLONE) 01060 * Return: pixad (sorted), or null on error 01061 */ 01062 PIXA * 01063 pixaSortByIndex(PIXA *pixas, 01064 NUMA *naindex, 01065 l_int32 copyflag) 01066 { 01067 l_int32 i, n, index; 01068 BOX *box; 01069 PIX *pix; 01070 PIXA *pixad; 01071 01072 PROCNAME("pixaSortByIndex"); 01073 01074 if (!pixas) 01075 return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); 01076 if (!naindex) 01077 return (PIXA *)ERROR_PTR("naindex not defined", procName, NULL); 01078 if (copyflag != L_CLONE && copyflag != L_COPY) 01079 return (PIXA *)ERROR_PTR("invalid copyflag", procName, NULL); 01080 01081 n = pixaGetCount(pixas); 01082 pixad = pixaCreate(n); 01083 for (i = 0; i < n; i++) { 01084 numaGetIValue(naindex, i, &index); 01085 pix = pixaGetPix(pixas, index, copyflag); 01086 box = pixaGetBox(pixas, index, copyflag); 01087 pixaAddPix(pixad, pix, L_INSERT); 01088 pixaAddBox(pixad, box, L_INSERT); 01089 } 01090 01091 return pixad; 01092 } 01093 01094 01095 /*! 01096 * pixaSort2dByIndex() 01097 * 01098 * Input: pixas 01099 * naa (numaa that maps from the new pixaa to the input pixas) 01100 * copyflag (L_CLONE or L_COPY) 01101 * Return: pixaa (sorted), or null on error 01102 */ 01103 PIXAA * 01104 pixaSort2dByIndex(PIXA *pixas, 01105 NUMAA *naa, 01106 l_int32 copyflag) 01107 { 01108 l_int32 pixtot, ntot, i, j, n, nn, index; 01109 BOX *box; 01110 NUMA *na; 01111 PIX *pix; 01112 PIXA *pixa; 01113 PIXAA *pixaa; 01114 01115 PROCNAME("pixaSort2dByIndex"); 01116 01117 if (!pixas) 01118 return (PIXAA *)ERROR_PTR("pixas not defined", procName, NULL); 01119 if (!naa) 01120 return (PIXAA *)ERROR_PTR("naindex not defined", procName, NULL); 01121 01122 /* Check counts */ 01123 ntot = numaaGetNumberCount(naa); 01124 pixtot = pixaGetCount(pixas); 01125 if (ntot != pixtot) 01126 return (PIXAA *)ERROR_PTR("element count mismatch", procName, NULL); 01127 01128 n = numaaGetCount(naa); 01129 pixaa = pixaaCreate(n); 01130 for (i = 0; i < n; i++) { 01131 na = numaaGetNuma(naa, i, L_CLONE); 01132 nn = numaGetCount(na); 01133 pixa = pixaCreate(nn); 01134 for (j = 0; j < nn; j++) { 01135 numaGetIValue(na, j, &index); 01136 pix = pixaGetPix(pixas, index, copyflag); 01137 box = pixaGetBox(pixas, index, copyflag); 01138 pixaAddPix(pixa, pix, L_INSERT); 01139 pixaAddBox(pixa, box, L_INSERT); 01140 } 01141 pixaaAddPixa(pixaa, pixa, L_INSERT); 01142 numaDestroy(&na); 01143 } 01144 01145 return pixaa; 01146 } 01147 01148 01149 01150 /*---------------------------------------------------------------------* 01151 * Miscellaneous functions * 01152 *---------------------------------------------------------------------*/ 01153 /*! 01154 * pixaAddBorderGeneral() 01155 * 01156 * Input: pixad (can be null or equal to pixas) 01157 * pixas (containing pix of all depths; colormap ok) 01158 * left, right, top, bot (number of pixels added) 01159 * val (value of added border pixels) 01160 * Return: pixad (with border added to each pix), including on error 01161 * 01162 * Notes: 01163 * (1) For binary images: 01164 * white: val = 0 01165 * black: val = 1 01166 * For grayscale images: 01167 * white: val = 2 ** d - 1 01168 * black: val = 0 01169 * For rgb color images: 01170 * white: val = 0xffffff00 01171 * black: val = 0 01172 * For colormapped images, use 'index' found this way: 01173 * white: pixcmapGetRankIntensity(cmap, 1.0, &index); 01174 * black: pixcmapGetRankIntensity(cmap, 0.0, &index); 01175 * (2) For in-place replacement of each pix with a bordered version, 01176 * use @pixad = @pixas. To make a new pixa, use @pixad = NULL. 01177 * (3) In both cases, the boxa has sides adjusted as if it were 01178 * expanded by the border. 01179 */ 01180 PIXA * 01181 pixaAddBorderGeneral(PIXA *pixad, 01182 PIXA *pixas, 01183 l_int32 left, 01184 l_int32 right, 01185 l_int32 top, 01186 l_int32 bot, 01187 l_uint32 val) 01188 { 01189 l_int32 i, n, nbox; 01190 BOX *box; 01191 BOXA *boxad; 01192 PIX *pixs, *pixd; 01193 01194 PROCNAME("pixaAddBorderGeneral"); 01195 01196 if (!pixas) 01197 return (PIXA *)ERROR_PTR("pixas not defined", procName, pixad); 01198 if (left < 0 || right < 0 || top < 0 || bot < 0) 01199 return (PIXA *)ERROR_PTR("negative border added!", procName, pixad); 01200 if (pixad && (pixad != pixas)) 01201 return (PIXA *)ERROR_PTR("pixad defined but != pixas", procName, pixad); 01202 01203 n = pixaGetCount(pixas); 01204 if (!pixad) 01205 pixad = pixaCreate(n); 01206 for (i = 0; i < n; i++) { 01207 pixs = pixaGetPix(pixas, i, L_CLONE); 01208 pixd = pixAddBorderGeneral(pixs, left, right, top, bot, val); 01209 if (pixad == pixas) /* replace */ 01210 pixaReplacePix(pixad, i, pixd, NULL); 01211 else 01212 pixaAddPix(pixad, pixd, L_INSERT); 01213 pixDestroy(&pixs); 01214 } 01215 01216 nbox = pixaGetBoxaCount(pixas); 01217 boxad = pixaGetBoxa(pixad, L_CLONE); 01218 for (i = 0; i < nbox; i++) { 01219 if ((box = pixaGetBox(pixas, i, L_COPY)) == NULL) { 01220 L_WARNING_INT("box %d not found", procName, i); 01221 break; 01222 } 01223 boxAdjustSides(box, box, -left, right, -top, bot); 01224 if (pixad == pixas) /* replace */ 01225 boxaReplaceBox(boxad, i, box); 01226 else 01227 boxaAddBox(boxad, box, L_INSERT); 01228 } 01229 boxaDestroy(&boxad); 01230 01231 return pixad; 01232 } 01233 01234 01235 /*! 01236 * pixaaFlattenToPixa() 01237 * 01238 * Input: pixaa 01239 * &naindex (<optional return> the pixa index in the pixaa) 01240 * copyflag (L_COPY or L_CLONE) 01241 * Return: pixa, or null on error 01242 * 01243 * Notes: 01244 * (1) This 'flattens' the pixaa to a pixa, taking the pix in 01245 * order in the first pixa, then the second, etc. 01246 * (2) If &naindex is defined, we generate a Numa that gives, for 01247 * each pix in the pixaa, the index of the pixa to which it belongs. 01248 */ 01249 PIXA * 01250 pixaaFlattenToPixa(PIXAA *pixaa, 01251 NUMA **pnaindex, 01252 l_int32 copyflag) 01253 { 01254 l_int32 i, j, m, n; 01255 BOX *box; 01256 NUMA *naindex; 01257 PIX *pix; 01258 PIXA *pixa, *pixat; 01259 01260 PROCNAME("pixaaFlattenToPixa"); 01261 01262 if (pnaindex) *pnaindex = NULL; 01263 if (!pixaa) 01264 return (PIXA *)ERROR_PTR("pixaa not defined", procName, NULL); 01265 if (copyflag != L_COPY && copyflag != L_CLONE) 01266 return (PIXA *)ERROR_PTR("invalid copyflag", procName, NULL); 01267 01268 if (pnaindex) { 01269 naindex = numaCreate(0); 01270 *pnaindex = naindex; 01271 } 01272 01273 n = pixaaGetCount(pixaa); 01274 pixa = pixaCreate(n); 01275 for (i = 0; i < n; i++) { 01276 pixat = pixaaGetPixa(pixaa, i, L_CLONE); 01277 m = pixaGetCount(pixat); 01278 for (j = 0; j < m; j++) { 01279 pix = pixaGetPix(pixat, j, copyflag); 01280 box = pixaGetBox(pixat, j, copyflag); 01281 pixaAddPix(pixa, pix, L_INSERT); 01282 pixaAddBox(pixa, box, L_INSERT); 01283 if (pnaindex) 01284 numaAddNumber(naindex, i); /* save 'row' number */ 01285 } 01286 pixaDestroy(&pixat); 01287 } 01288 01289 return pixa; 01290 } 01291 01292 01293 /*! 01294 * pixaSizeRange() 01295 * 01296 * Input: pixa 01297 * &minw, &minh, &maxw, &maxh (<optional return> range of 01298 * dimensions of pix in the array) 01299 * Return: 0 if OK, 1 on error 01300 */ 01301 l_int32 01302 pixaSizeRange(PIXA *pixa, 01303 l_int32 *pminw, 01304 l_int32 *pminh, 01305 l_int32 *pmaxw, 01306 l_int32 *pmaxh) 01307 { 01308 l_int32 minw, minh, maxw, maxh, i, n, w, h; 01309 PIX *pix; 01310 01311 PROCNAME("pixaSizeRange"); 01312 01313 if (!pixa) 01314 return ERROR_INT("pixa not defined", procName, 1); 01315 if (!pminw && !pmaxw && !pminh && !pmaxh) 01316 return ERROR_INT("no data can be returned", procName, 1); 01317 01318 minw = minh = 1000000; 01319 maxw = maxh = 0; 01320 n = pixaGetCount(pixa); 01321 for (i = 0; i < n; i++) { 01322 pix = pixaGetPix(pixa, i, L_CLONE); 01323 w = pixGetWidth(pix); 01324 h = pixGetHeight(pix); 01325 if (w < minw) 01326 minw = w; 01327 if (h < minh) 01328 minh = h; 01329 if (w > maxw) 01330 maxw = w; 01331 if (h > maxh) 01332 maxh = h; 01333 pixDestroy(&pix); 01334 } 01335 01336 if (pminw) *pminw = minw; 01337 if (pminh) *pminh = minh; 01338 if (pmaxw) *pmaxw = maxw; 01339 if (pmaxh) *pmaxh = maxh; 01340 01341 return 0; 01342 } 01343 01344 01345 /*! 01346 * pixaClipToPix() 01347 * 01348 * Input: pixas 01349 * pixs 01350 * Return: pixad, or null on error 01351 * 01352 * Notes: 01353 * (1) This is intended for use in situations where pixas 01354 * was originally generated from the input pixs. 01355 * (2) Returns a pixad where each pix in pixas is ANDed 01356 * with its associated region of the input pixs. This 01357 * region is specified by the the box that is associated 01358 * with the pix. 01359 * (3) In a typical application of this function, pixas has 01360 * a set of region masks, so this generates a pixa of 01361 * the parts of pixs that correspond to each region 01362 * mask component, along with the bounding box for 01363 * the region. 01364 */ 01365 PIXA * 01366 pixaClipToPix(PIXA *pixas, 01367 PIX *pixs) 01368 { 01369 l_int32 i, n; 01370 BOX *box; 01371 PIX *pix, *pixc; 01372 PIXA *pixad; 01373 01374 PROCNAME("pixaClipToPix"); 01375 01376 if (!pixas) 01377 return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); 01378 if (!pixs) 01379 return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); 01380 01381 n = pixaGetCount(pixas); 01382 if ((pixad = pixaCreate(n)) == NULL) 01383 return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); 01384 01385 for (i = 0; i < n; i++) { 01386 pix = pixaGetPix(pixas, i, L_CLONE); 01387 box = pixaGetBox(pixas, i, L_COPY); 01388 pixc = pixClipRectangle(pixs, box, NULL); 01389 pixAnd(pixc, pixc, pix); 01390 pixaAddPix(pixad, pixc, L_INSERT); 01391 pixaAddBox(pixad, box, L_INSERT); 01392 pixDestroy(&pix); 01393 } 01394 01395 return pixad; 01396 } 01397 01398 01399 /*! 01400 * pixaAnyColormaps() 01401 * 01402 * Input: pixa 01403 * &hascmap (<return> 1 if any pix has a colormap; 0 otherwise) 01404 * Return: 0 if OK; 1 on error 01405 */ 01406 l_int32 01407 pixaAnyColormaps(PIXA *pixa, 01408 l_int32 *phascmap) 01409 { 01410 l_int32 i, n; 01411 PIX *pix; 01412 PIXCMAP *cmap; 01413 01414 PROCNAME("pixaAnyColormaps"); 01415 01416 if (!phascmap) 01417 return ERROR_INT("&hascmap not defined", procName, 1); 01418 *phascmap = 0; 01419 if (!pixa) 01420 return ERROR_INT("pixa not defined", procName, 1); 01421 01422 n = pixaGetCount(pixa); 01423 for (i = 0; i < n; i++) { 01424 pix = pixaGetPix(pixa, i, L_CLONE); 01425 cmap = pixGetColormap(pix); 01426 pixDestroy(&pix); 01427 if (cmap) { 01428 *phascmap = 1; 01429 return 0; 01430 } 01431 } 01432 01433 return 0; 01434 } 01435 01436 01437 /*! 01438 * pixaGetDepthInfo() 01439 * 01440 * Input: pixa 01441 * &maxdepth (<optional return> max pixel depth of pix in pixa) 01442 * &same (<optional return> true if all depths are equal) 01443 * Return: 0 if OK; 1 on error 01444 */ 01445 l_int32 01446 pixaGetDepthInfo(PIXA *pixa, 01447 l_int32 *pmaxdepth, 01448 l_int32 *psame) 01449 { 01450 l_int32 i, n, d, d0; 01451 l_int32 maxd, same; /* depth info */ 01452 01453 PROCNAME("pixaGetDepthInfo"); 01454 01455 if (!pmaxdepth && !psame) return 0; 01456 if (pmaxdepth) *pmaxdepth = 0; 01457 if (psame) *psame = TRUE; 01458 if (!pixa) 01459 return ERROR_INT("pixa not defined", procName, 1); 01460 if ((n = pixaGetCount(pixa)) == 0) 01461 return ERROR_INT("pixa is empty", procName, 1); 01462 01463 same = TRUE; 01464 maxd = 0; 01465 for (i = 0; i < n; i++) { 01466 pixaGetPixDimensions(pixa, i, NULL, NULL, &d); 01467 if (i == 0) 01468 d0 = d; 01469 else if (d != d0) 01470 same = FALSE; 01471 if (d > maxd) maxd = d; 01472 } 01473 01474 if (pmaxdepth) *pmaxdepth = maxd; 01475 if (psame) *psame = same; 01476 return 0; 01477 } 01478 01479 01480 /*! 01481 * pixaEqual() 01482 * 01483 * Input: pixa1 01484 * pixa2 01485 * maxdist 01486 * &naindex (<optional return> index array of correspondences 01487 * &same (<return> 1 if equal; 0 otherwise) 01488 * Return 0 if OK, 1 on error 01489 * 01490 * Notes: 01491 * (1) The two pixa are the "same" if they contain the same 01492 * boxa and the same ordered set of pix. However, if they 01493 * have boxa, the pix in each pixa can differ in ordering 01494 * by an amount given by the parameter @maxdist. If they 01495 * don't have a boxa, the @maxdist parameter is ignored, 01496 * and the ordering must be identical. 01497 * (2) This applies only to boxa geometry, pixels and ordering; 01498 * other fields in the pix are ignored. 01499 * (3) naindex[i] gives the position of the box in pixa2 that 01500 * corresponds to box i in pixa1. It is only returned if the 01501 * pixa have boxa and the boxa are equal. 01502 * (4) In situations where the ordering is very different, so that 01503 * a large @maxdist is required for "equality", this should be 01504 * implemented with a hash function for efficiency. 01505 */ 01506 l_int32 01507 pixaEqual(PIXA *pixa1, 01508 PIXA *pixa2, 01509 l_int32 maxdist, 01510 NUMA **pnaindex, 01511 l_int32 *psame) 01512 { 01513 l_int32 i, j, n, same, sameboxa; 01514 BOXA *boxa1, *boxa2; 01515 NUMA *na; 01516 PIX *pix1, *pix2; 01517 01518 PROCNAME("pixaEqual"); 01519 01520 if (!psame) 01521 return ERROR_INT("&same not defined", procName, 1); 01522 *psame = 0; 01523 sameboxa = 0; 01524 na = NULL; 01525 if (!pixa1 || !pixa2) 01526 return ERROR_INT("pixa1 and pixa2 not both defined", procName, 1); 01527 n = pixaGetCount(pixa1); 01528 if (n != pixaGetCount(pixa2)) 01529 return 0; 01530 boxa1 = pixaGetBoxa(pixa1, L_CLONE); 01531 boxa2 = pixaGetBoxa(pixa2, L_CLONE); 01532 if (!boxa1 && !boxa2) 01533 maxdist = 0; /* exact ordering required */ 01534 if (boxa1 && !boxa2) { 01535 boxaDestroy(&boxa1); 01536 return 0; 01537 } 01538 if (!boxa1 && boxa2) { 01539 boxaDestroy(&boxa2); 01540 return 0; 01541 } 01542 if (boxa1 && boxa2) { 01543 boxaEqual(boxa1, boxa2, maxdist, &na, &sameboxa); 01544 boxaDestroy(&boxa1); 01545 boxaDestroy(&boxa2); 01546 if (!sameboxa) { 01547 numaDestroy(&na); 01548 return 0; 01549 } 01550 } 01551 01552 for (i = 0; i < n; i++) { 01553 pix1 = pixaGetPix(pixa1, i, L_CLONE); 01554 if (na) 01555 numaGetIValue(na, i, &j); 01556 else 01557 j = i; 01558 pix2 = pixaGetPix(pixa2, j, L_CLONE); 01559 pixEqual(pix1, pix2, &same); 01560 pixDestroy(&pix1); 01561 pixDestroy(&pix2); 01562 if (!same) { 01563 numaDestroy(&na); 01564 return 0; 01565 } 01566 } 01567 01568 *psame = 1; 01569 if (pnaindex) 01570 *pnaindex = na; 01571 else 01572 numaDestroy(&na); 01573 return 0; 01574 } 01575 01576