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 /* 00018 * morphapp.c 00019 * 00020 * These are some useful and/or interesting composite 00021 * image processing operations, of the type that are often 00022 * useful in applications. Most are morphological in 00023 * nature. 00024 * 00025 * Extraction of boundary pixels 00026 * PIX *pixExtractBoundary() 00027 * 00028 * Selective morph sequence operation under mask 00029 * PIX *pixMorphSequenceMasked() 00030 * 00031 * Selective morph sequence operation on each component 00032 * PIX *pixMorphSequenceByComponent() 00033 * PIXA *pixaMorphSequenceByComponent() 00034 * 00035 * Selective morph sequence operation on each region 00036 * PIX *pixMorphSequenceByRegion() 00037 * PIXA *pixaMorphSequenceByRegion() 00038 * 00039 * Union and intersection of parallel composite operations 00040 * PIX *pixUnionOfMorphOps() 00041 * PIX *pixIntersectionOfMorphOps() 00042 * 00043 * Selective connected component filling 00044 * PIX *pixSelectiveConnCompFill() 00045 * 00046 * Removal of matched patterns 00047 * PIX *pixRemoveMatchedPattern() 00048 * 00049 * Display of matched patterns 00050 * PIX *pixDisplayMatchedPattern() 00051 * 00052 * Iterative morphological seed filling (don't use for real work) 00053 * PIX *pixSeedfillMorph() 00054 * 00055 * Granulometry on binary images 00056 * NUMA *pixRunHistogramMorph() 00057 * 00058 * Composite operations on grayscale images 00059 * PIX *pixTophat() 00060 * PIX *pixHDome() 00061 * PIX *pixFastTophat() 00062 * PIX *pixMorphGradient() 00063 * 00064 * Centroid of component 00065 * PTA *pixaCentroids() 00066 * l_int32 pixCentroid() 00067 */ 00068 00069 #include "allheaders.h" 00070 00071 #define SWAP(x, y) {temp = (x); (x) = (y); (y) = temp;} 00072 00073 00074 /*-----------------------------------------------------------------* 00075 * Extraction of boundary pixels * 00076 *-----------------------------------------------------------------*/ 00077 /*! 00078 * pixExtractBoundary() 00079 * 00080 * Input: pixs (1 bpp) 00081 * type (0 for background pixels; 1 for foreground pixels) 00082 * Return: pixd, or null on error 00083 * 00084 * Notes: 00085 * (1) Extracts the fg or bg boundary pixels for each component. 00086 * Components are assumed to end at the boundary of pixs. 00087 */ 00088 PIX * 00089 pixExtractBoundary(PIX *pixs, 00090 l_int32 type) 00091 { 00092 PIX *pixd; 00093 00094 PROCNAME("pixExtractBoundary"); 00095 00096 if (!pixs) 00097 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00098 00099 if (type == 0) 00100 pixd = pixDilateBrick(NULL, pixs, 3, 3); 00101 else 00102 pixd = pixErodeBrick(NULL, pixs, 3, 3); 00103 pixXor(pixd, pixd, pixs); 00104 return pixd; 00105 } 00106 00107 00108 /*-----------------------------------------------------------------* 00109 * Selective morph sequence operation under mask * 00110 *-----------------------------------------------------------------*/ 00111 /*! 00112 * pixMorphSequenceMasked() 00113 * 00114 * Input: pixs (1 bpp) 00115 * pixm (<optional> 1 bpp mask) 00116 * sequence (string specifying sequence of operations) 00117 * dispsep (horizontal separation in pixels between 00118 * successive displays; use zero to suppress display) 00119 * Return: pixd, or null on error 00120 * 00121 * Notes: 00122 * (1) This applies the morph sequence to the image, but only allows 00123 * changes in pixs for pixels under the background of pixm. 00124 * (5) If pixm is NULL, this is just pixMorphSequence(). 00125 */ 00126 PIX * 00127 pixMorphSequenceMasked(PIX *pixs, 00128 PIX *pixm, 00129 const char *sequence, 00130 l_int32 dispsep) 00131 { 00132 PIX *pixd; 00133 00134 PROCNAME("pixMorphSequenceMasked"); 00135 00136 if (!pixs) 00137 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00138 if (!sequence) 00139 return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); 00140 00141 pixd = pixMorphSequence(pixs, sequence, dispsep); 00142 pixCombineMasked(pixd, pixs, pixm); /* restore src pixels under mask fg */ 00143 return pixd; 00144 } 00145 00146 00147 /*-----------------------------------------------------------------* 00148 * Morph sequence operation on each component * 00149 *-----------------------------------------------------------------*/ 00150 /*! 00151 * pixMorphSequenceByComponent() 00152 * 00153 * Input: pixs (1 bpp) 00154 * sequence (string specifying sequence) 00155 * connectivity (4 or 8) 00156 * minw (minimum width to consider; use 0 or 1 for any width) 00157 * minh (minimum height to consider; use 0 or 1 for any height) 00158 * &boxa (<optional> return boxa of c.c. in pixs) 00159 * Return: pixd, or null on error 00160 * 00161 * Notes: 00162 * (1) See pixMorphSequence() for composing operation sequences. 00163 * (2) This operates separately on each c.c. in the input pix. 00164 * (3) The dilation does NOT increase the c.c. size; it is clipped 00165 * to the size of the original c.c. This is necessary to 00166 * keep the c.c. independent after the operation. 00167 * (4) You can specify that the width and/or height must equal 00168 * or exceed a minimum size for the operation to take place. 00169 * (5) Use NULL for boxa to avoid returning the boxa. 00170 */ 00171 PIX * 00172 pixMorphSequenceByComponent(PIX *pixs, 00173 const char *sequence, 00174 l_int32 connectivity, 00175 l_int32 minw, 00176 l_int32 minh, 00177 BOXA **pboxa) 00178 { 00179 l_int32 n, i, x, y, w, h; 00180 BOXA *boxa; 00181 PIX *pix, *pixd; 00182 PIXA *pixas, *pixad; 00183 00184 PROCNAME("pixMorphSequenceByComponent"); 00185 00186 if (!pixs) 00187 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00188 if (!sequence) 00189 return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); 00190 00191 if (minw <= 0) minw = 1; 00192 if (minh <= 0) minh = 1; 00193 00194 /* Get the c.c. */ 00195 if ((boxa = pixConnComp(pixs, &pixas, connectivity)) == NULL) 00196 return (PIX *)ERROR_PTR("boxa not made", procName, NULL); 00197 00198 /* Operate on each c.c. independently */ 00199 pixad = pixaMorphSequenceByComponent(pixas, sequence, minw, minh); 00200 pixaDestroy(&pixas); 00201 boxaDestroy(&boxa); 00202 if (!pixad) 00203 return (PIX *)ERROR_PTR("pixad not made", procName, NULL); 00204 00205 /* Display the result out into pixd */ 00206 pixd = pixCreateTemplate(pixs); 00207 n = pixaGetCount(pixad); 00208 for (i = 0; i < n; i++) { 00209 pixaGetBoxGeometry(pixad, i, &x, &y, &w, &h); 00210 pix = pixaGetPix(pixad, i, L_CLONE); 00211 pixRasterop(pixd, x, y, w, h, PIX_PAINT, pix, 0, 0); 00212 pixDestroy(&pix); 00213 } 00214 00215 if (pboxa) 00216 *pboxa = pixaGetBoxa(pixad, L_CLONE); 00217 pixaDestroy(&pixad); 00218 return pixd; 00219 } 00220 00221 00222 /*! 00223 * pixaMorphSequenceByComponent() 00224 * 00225 * Input: pixas (of 1 bpp pix) 00226 * sequence (string specifying sequence) 00227 * minw (minimum width to consider; use 0 or 1 for any width) 00228 * minh (minimum height to consider; use 0 or 1 for any height) 00229 * Return: pixad, or null on error 00230 * 00231 * Notes: 00232 * (1) See pixMorphSequence() for composing operation sequences. 00233 * (2) This operates separately on each c.c. in the input pixa. 00234 * (3) You can specify that the width and/or height must equal 00235 * or exceed a minimum size for the operation to take place. 00236 * (4) The input pixa should have a boxa giving the locations 00237 * of the pix components. 00238 */ 00239 PIXA * 00240 pixaMorphSequenceByComponent(PIXA *pixas, 00241 const char *sequence, 00242 l_int32 minw, 00243 l_int32 minh) 00244 { 00245 l_int32 n, i, w, h, d; 00246 BOX *box; 00247 PIX *pixt1, *pixt2; 00248 PIXA *pixad; 00249 00250 PROCNAME("pixaMorphSequenceByComponent"); 00251 00252 if (!pixas) 00253 return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); 00254 if ((n = pixaGetCount(pixas)) == 0) 00255 return (PIXA *)ERROR_PTR("no pix in pixas", procName, NULL); 00256 if (n != pixaGetBoxaCount(pixas)) 00257 L_WARNING("boxa size != n", procName); 00258 pixaGetPixDimensions(pixas, 0, NULL, NULL, &d); 00259 if (d != 1) 00260 return (PIXA *)ERROR_PTR("depth not 1 bpp", procName, NULL); 00261 00262 if (!sequence) 00263 return (PIXA *)ERROR_PTR("sequence not defined", procName, NULL); 00264 if (minw <= 0) minw = 1; 00265 if (minh <= 0) minh = 1; 00266 00267 if ((pixad = pixaCreate(n)) == NULL) 00268 return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); 00269 for (i = 0; i < n; i++) { 00270 pixaGetPixDimensions(pixas, i, &w, &h, NULL); 00271 if (w >= minw && h >= minh) { 00272 if ((pixt1 = pixaGetPix(pixas, i, L_CLONE)) == NULL) 00273 return (PIXA *)ERROR_PTR("pixt1 not found", procName, NULL); 00274 if ((pixt2 = pixMorphCompSequence(pixt1, sequence, 0)) == NULL) 00275 return (PIXA *)ERROR_PTR("pixt2 not made", procName, NULL); 00276 pixaAddPix(pixad, pixt2, L_INSERT); 00277 box = pixaGetBox(pixas, i, L_COPY); 00278 pixaAddBox(pixad, box, L_INSERT); 00279 pixDestroy(&pixt1); 00280 } 00281 } 00282 00283 return pixad; 00284 } 00285 00286 00287 /*-----------------------------------------------------------------* 00288 * Morph sequence operation on each region * 00289 *-----------------------------------------------------------------*/ 00290 /*! 00291 * pixMorphSequenceByRegion() 00292 * 00293 * Input: pixs (1 bpp) 00294 * pixm (mask specifying regions) 00295 * sequence (string specifying sequence) 00296 * connectivity (4 or 8, used on mask) 00297 * minw (minimum width to consider; use 0 or 1 for any width) 00298 * minh (minimum height to consider; use 0 or 1 for any height) 00299 * &boxa (<optional> return boxa of c.c. in pixm) 00300 * Return: pixd, or null on error 00301 * 00302 * Notes: 00303 * (1) See pixMorphCompSequence() for composing operation sequences. 00304 * (2) This operates separately on the region in pixs corresponding 00305 * to each c.c. in the mask pixm. It differs from 00306 * pixMorphSequenceByComponent() in that the latter does not have 00307 * a pixm (mask), but instead operates independently on each 00308 * component in pixs. 00309 * (3) Dilation will NOT increase the region size; the result 00310 * is clipped to the size of the mask region. This is necessary 00311 * to make regions independent after the operation. 00312 * (4) You can specify that the width and/or height of a region must 00313 * equal or exceed a minimum size for the operation to take place. 00314 * (5) Use NULL for @pboxa to avoid returning the boxa. 00315 */ 00316 PIX * 00317 pixMorphSequenceByRegion(PIX *pixs, 00318 PIX *pixm, 00319 const char *sequence, 00320 l_int32 connectivity, 00321 l_int32 minw, 00322 l_int32 minh, 00323 BOXA **pboxa) 00324 { 00325 l_int32 n, i, x, y, w, h; 00326 BOXA *boxa; 00327 PIX *pix, *pixd; 00328 PIXA *pixam, *pixad; 00329 00330 PROCNAME("pixMorphSequenceByRegion"); 00331 00332 if (!pixs) 00333 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00334 if (!pixm) 00335 return (PIX *)ERROR_PTR("pixm not defined", procName, NULL); 00336 if (pixGetDepth(pixs) != 1 || pixGetDepth(pixm) != 1) 00337 return (PIX *)ERROR_PTR("pixs and pixm not both 1 bpp", procName, NULL); 00338 if (!sequence) 00339 return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); 00340 00341 if (minw <= 0) minw = 1; 00342 if (minh <= 0) minh = 1; 00343 00344 /* Get the c.c. of the mask */ 00345 if ((boxa = pixConnComp(pixm, &pixam, connectivity)) == NULL) 00346 return (PIX *)ERROR_PTR("boxa not made", procName, NULL); 00347 00348 /* Operate on each region in pixs independently */ 00349 pixad = pixaMorphSequenceByRegion(pixs, pixam, sequence, minw, minh); 00350 pixaDestroy(&pixam); 00351 boxaDestroy(&boxa); 00352 if (!pixad) 00353 return (PIX *)ERROR_PTR("pixad not made", procName, NULL); 00354 00355 /* Display the result out into pixd */ 00356 pixd = pixCreateTemplate(pixs); 00357 n = pixaGetCount(pixad); 00358 for (i = 0; i < n; i++) { 00359 pixaGetBoxGeometry(pixad, i, &x, &y, &w, &h); 00360 pix = pixaGetPix(pixad, i, L_CLONE); 00361 pixRasterop(pixd, x, y, w, h, PIX_PAINT, pix, 0, 0); 00362 pixDestroy(&pix); 00363 } 00364 00365 if (pboxa) 00366 *pboxa = pixaGetBoxa(pixad, L_CLONE); 00367 pixaDestroy(&pixad); 00368 return pixd; 00369 } 00370 00371 00372 /*! 00373 * pixaMorphSequenceByRegion() 00374 * 00375 * Input: pixs (1 bpp) 00376 * pixam (of 1 bpp mask elements) 00377 * sequence (string specifying sequence) 00378 * minw (minimum width to consider; use 0 or 1 for any width) 00379 * minh (minimum height to consider; use 0 or 1 for any height) 00380 * Return: pixad, or null on error 00381 * 00382 * Notes: 00383 * (1) See pixMorphSequence() for composing operation sequences. 00384 * (2) This operates separately on each region in the input pixs 00385 * defined by the components in pixam. 00386 * (3) You can specify that the width and/or height of a mask 00387 * component must equal or exceed a minimum size for the 00388 * operation to take place. 00389 * (4) The input pixam should have a boxa giving the locations 00390 * of the regions in pixs. 00391 */ 00392 PIXA * 00393 pixaMorphSequenceByRegion(PIX *pixs, 00394 PIXA *pixam, 00395 const char *sequence, 00396 l_int32 minw, 00397 l_int32 minh) 00398 { 00399 l_int32 n, i, w, h, d; 00400 BOX *box; 00401 PIX *pixt1, *pixt2, *pixt3; 00402 PIXA *pixad; 00403 00404 PROCNAME("pixaMorphSequenceByRegion"); 00405 00406 if (!pixs) 00407 return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); 00408 if (pixGetDepth(pixs) != 1) 00409 return (PIXA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); 00410 if (!pixam) 00411 return (PIXA *)ERROR_PTR("pixam not defined", procName, NULL); 00412 pixaGetPixDimensions(pixam, 0, NULL, NULL, &d); 00413 if (d != 1) 00414 return (PIXA *)ERROR_PTR("mask depth not 1 bpp", procName, NULL); 00415 if ((n = pixaGetCount(pixam)) == 0) 00416 return (PIXA *)ERROR_PTR("no regions specified", procName, NULL); 00417 if (n != pixaGetBoxaCount(pixam)) 00418 L_WARNING("boxa size != n", procName); 00419 if (!sequence) 00420 return (PIXA *)ERROR_PTR("sequence not defined", procName, NULL); 00421 00422 if (minw <= 0) minw = 1; 00423 if (minh <= 0) minh = 1; 00424 00425 if ((pixad = pixaCreate(n)) == NULL) 00426 return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); 00427 00428 /* Use the rectangle to remove the appropriate part of pixs; 00429 * then AND with the mask component to get the actual fg 00430 * of pixs that is under the mask component. */ 00431 for (i = 0; i < n; i++) { 00432 pixaGetPixDimensions(pixam, i, &w, &h, NULL); 00433 if (w >= minw && h >= minh) { 00434 if ((pixt1 = pixaGetPix(pixam, i, L_CLONE)) == NULL) 00435 return (PIXA *)ERROR_PTR("pixt1 not found", procName, NULL); 00436 box = pixaGetBox(pixam, i, L_COPY); 00437 pixt2 = pixClipRectangle(pixs, box, NULL); 00438 pixAnd(pixt2, pixt2, pixt1); 00439 if ((pixt3 = pixMorphCompSequence(pixt2, sequence, 0)) == NULL) 00440 return (PIXA *)ERROR_PTR("pixt3 not made", procName, NULL); 00441 pixaAddPix(pixad, pixt3, L_INSERT); 00442 pixaAddBox(pixad, box, L_INSERT); 00443 pixDestroy(&pixt1); 00444 pixDestroy(&pixt2); 00445 } 00446 } 00447 00448 return pixad; 00449 } 00450 00451 00452 /*-----------------------------------------------------------------* 00453 * Union and intersection of parallel composite operations * 00454 *-----------------------------------------------------------------*/ 00455 /*! 00456 * pixUnionOfMorphOps() 00457 * 00458 * Input: pixs (binary) 00459 * sela 00460 * type (L_MORPH_DILATE, etc.) 00461 * Return: pixd (union of the specified morphological operation 00462 * on pixs for each Sel in the Sela), or null on error 00463 */ 00464 PIX * 00465 pixUnionOfMorphOps(PIX *pixs, 00466 SELA *sela, 00467 l_int32 type) 00468 { 00469 l_int32 n, i; 00470 PIX *pixt, *pixd; 00471 SEL *sel; 00472 00473 PROCNAME("pixUnionOfMorphOps"); 00474 00475 if (!pixs || pixGetDepth(pixs) != 1) 00476 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); 00477 if (!sela) 00478 return (PIX *)ERROR_PTR("sela not defined", procName, NULL); 00479 n = selaGetCount(sela); 00480 if (n == 0) 00481 return (PIX *)ERROR_PTR("no sels in sela", procName, NULL); 00482 if (type != L_MORPH_DILATE && type != L_MORPH_ERODE && 00483 type != L_MORPH_OPEN && type != L_MORPH_CLOSE && 00484 type != L_MORPH_HMT) 00485 return (PIX *)ERROR_PTR("invalid type", procName, NULL); 00486 00487 pixd = pixCreateTemplate(pixs); 00488 for (i = 0; i < n; i++) { 00489 sel = selaGetSel(sela, i); 00490 if (type == L_MORPH_DILATE) 00491 pixt = pixDilate(NULL, pixs, sel); 00492 else if (type == L_MORPH_ERODE) 00493 pixt = pixErode(NULL, pixs, sel); 00494 else if (type == L_MORPH_OPEN) 00495 pixt = pixOpen(NULL, pixs, sel); 00496 else if (type == L_MORPH_CLOSE) 00497 pixt = pixClose(NULL, pixs, sel); 00498 else /* type == L_MORPH_HMT */ 00499 pixt = pixHMT(NULL, pixs, sel); 00500 pixOr(pixd, pixd, pixt); 00501 pixDestroy(&pixt); 00502 } 00503 00504 return pixd; 00505 } 00506 00507 00508 /*! 00509 * pixIntersectionOfMorphOps() 00510 * 00511 * Input: pixs (binary) 00512 * sela 00513 * type (L_MORPH_DILATE, etc.) 00514 * Return: pixd (intersection of the specified morphological operation 00515 * on pixs for each Sel in the Sela), or null on error 00516 */ 00517 PIX * 00518 pixIntersectionOfMorphOps(PIX *pixs, 00519 SELA *sela, 00520 l_int32 type) 00521 { 00522 l_int32 n, i; 00523 PIX *pixt, *pixd; 00524 SEL *sel; 00525 00526 PROCNAME("pixIntersectionOfMorphOps"); 00527 00528 if (!pixs || pixGetDepth(pixs) != 1) 00529 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); 00530 if (!sela) 00531 return (PIX *)ERROR_PTR("sela not defined", procName, NULL); 00532 n = selaGetCount(sela); 00533 if (n == 0) 00534 return (PIX *)ERROR_PTR("no sels in sela", procName, NULL); 00535 if (type != L_MORPH_DILATE && type != L_MORPH_ERODE && 00536 type != L_MORPH_OPEN && type != L_MORPH_CLOSE && 00537 type != L_MORPH_HMT) 00538 return (PIX *)ERROR_PTR("invalid type", procName, NULL); 00539 00540 pixd = pixCreateTemplate(pixs); 00541 pixSetAll(pixd); 00542 for (i = 0; i < n; i++) { 00543 sel = selaGetSel(sela, i); 00544 if (type == L_MORPH_DILATE) 00545 pixt = pixDilate(NULL, pixs, sel); 00546 else if (type == L_MORPH_ERODE) 00547 pixt = pixErode(NULL, pixs, sel); 00548 else if (type == L_MORPH_OPEN) 00549 pixt = pixOpen(NULL, pixs, sel); 00550 else if (type == L_MORPH_CLOSE) 00551 pixt = pixClose(NULL, pixs, sel); 00552 else /* type == L_MORPH_HMT */ 00553 pixt = pixHMT(NULL, pixs, sel); 00554 pixAnd(pixd, pixd, pixt); 00555 pixDestroy(&pixt); 00556 } 00557 00558 return pixd; 00559 } 00560 00561 00562 00563 /*-----------------------------------------------------------------* 00564 * Selective connected component filling * 00565 *-----------------------------------------------------------------*/ 00566 /*! 00567 * pixSelectiveConnCompFill() 00568 * 00569 * Input: pixs (binary) 00570 * connectivity (4 or 8) 00571 * minw (minimum width to consider; use 0 or 1 for any width) 00572 * minh (minimum height to consider; use 0 or 1 for any height) 00573 * Return: pix (with holes filled in selected c.c.), or null on error 00574 */ 00575 PIX * 00576 pixSelectiveConnCompFill(PIX *pixs, 00577 l_int32 connectivity, 00578 l_int32 minw, 00579 l_int32 minh) 00580 { 00581 l_int32 n, i, x, y, w, h; 00582 BOXA *boxa; 00583 PIX *pixt1, *pixt2, *pixd; 00584 PIXA *pixa; 00585 00586 PROCNAME("pixSelectiveConnCompFill"); 00587 00588 if (!pixs) 00589 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00590 if (pixGetDepth(pixs) != 1) 00591 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); 00592 00593 if (minw <= 0) minw = 1; 00594 if (minh <= 0) minh = 1; 00595 00596 if ((pixd = pixCopy(NULL, pixs)) == NULL) 00597 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00598 00599 if ((boxa = pixConnComp(pixs, &pixa, connectivity)) == NULL) 00600 return (PIX *)ERROR_PTR("boxa not made", procName, NULL); 00601 n = boxaGetCount(boxa); 00602 for (i = 0; i < n; i++) { 00603 boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); 00604 if (w >= minw && h >= minh) { 00605 if ((pixt1 = pixaGetPix(pixa, i, L_CLONE)) == NULL) 00606 return (PIX *)ERROR_PTR("pixt1 not found", procName, NULL); 00607 if ((pixt2 = pixHolesByFilling(pixt1, 12 - connectivity)) == NULL) 00608 return (PIX *)ERROR_PTR("pixt2 not made", procName, NULL); 00609 pixRasterop(pixd, x, y, w, h, PIX_PAINT, pixt2, 0, 0); 00610 pixDestroy(&pixt1); 00611 pixDestroy(&pixt2); 00612 } 00613 } 00614 pixaDestroy(&pixa); 00615 boxaDestroy(&boxa); 00616 00617 return pixd; 00618 } 00619 00620 00621 /*-----------------------------------------------------------------* 00622 * Removal of matched patterns * 00623 *-----------------------------------------------------------------*/ 00624 /*! 00625 * pixRemoveMatchedPattern() 00626 * 00627 * Input: pixs (input image, 1 bpp) 00628 * pixp (pattern to be removed from image, 1 bpp) 00629 * pixe (image after erosion by Sel that approximates pixp, 1 bpp) 00630 * x0, y0 (center of Sel) 00631 * dsize (number of pixels on each side by which pixp is 00632 * dilated before being subtracted from pixs; 00633 * valid values are {0, 1, 2, 3, 4}) 00634 * Return: 0 if OK, 1 on error 00635 * 00636 * Notes: 00637 * (1) This is in-place. 00638 * (2) You can use various functions in selgen to create a Sel 00639 * that is used to generate pixe from pixs. 00640 * (3) This function is applied after pixe has been computed. 00641 * It finds the centroid of each c.c., and subtracts 00642 * (the appropriately dilated version of) pixp, with the center 00643 * of the Sel used to align pixp with pixs. 00644 */ 00645 l_int32 00646 pixRemoveMatchedPattern(PIX *pixs, 00647 PIX *pixp, 00648 PIX *pixe, 00649 l_int32 x0, 00650 l_int32 y0, 00651 l_int32 dsize) 00652 { 00653 l_int32 i, nc, x, y, w, h, xb, yb; 00654 BOXA *boxa; 00655 PIX *pixt1, *pixt2; 00656 PIXA *pixa; 00657 PTA *pta; 00658 SEL *sel; 00659 00660 PROCNAME("pixRemoveMatchedPattern"); 00661 00662 if (!pixs) 00663 return ERROR_INT("pixs not defined", procName, 1); 00664 if (!pixp) 00665 return ERROR_INT("pixp not defined", procName, 1); 00666 if (!pixe) 00667 return ERROR_INT("pixe not defined", procName, 1); 00668 if (pixGetDepth(pixs) != 1 || pixGetDepth(pixp) != 1 || 00669 pixGetDepth(pixe) != 1) 00670 return ERROR_INT("all input pix not 1 bpp", procName, 1); 00671 if (dsize < 0 || dsize > 4) 00672 return ERROR_INT("dsize not in {0,1,2,3,4}", procName, 1); 00673 00674 /* Find the connected components and their centroids */ 00675 boxa = pixConnComp(pixe, &pixa, 8); 00676 if ((nc = boxaGetCount(boxa)) == 0) { 00677 L_WARNING("no matched patterns", procName); 00678 boxaDestroy(&boxa); 00679 pixaDestroy(&pixa); 00680 return 0; 00681 } 00682 pta = pixaCentroids(pixa); 00683 00684 /* Optionally dilate the pattern, first adding a border that 00685 * is large enough to accommodate the dilated pixels */ 00686 sel = NULL; 00687 if (dsize > 0) { 00688 sel = selCreateBrick(2 * dsize + 1, 2 * dsize + 1, dsize, dsize, 00689 SEL_HIT); 00690 pixt1 = pixAddBorder(pixp, dsize, 0); 00691 pixt2 = pixDilate(NULL, pixt1, sel); 00692 selDestroy(&sel); 00693 pixDestroy(&pixt1); 00694 } 00695 else 00696 pixt2 = pixClone(pixp); 00697 00698 /* Subtract out each dilated pattern. The centroid of each 00699 * component is located at: 00700 * (box->x + x, box->y + y) 00701 * and the 'center' of the pattern used in making pixe is located at 00702 * (x0 + dsize, (y0 + dsize) 00703 * relative to the UL corner of the pattern. The center of the 00704 * pattern is placed at the center of the component. */ 00705 w = pixGetWidth(pixt2); 00706 h = pixGetHeight(pixt2); 00707 for (i = 0; i < nc; i++) { 00708 ptaGetIPt(pta, i, &x, &y); 00709 boxaGetBoxGeometry(boxa, i, &xb, &yb, NULL, NULL); 00710 pixRasterop(pixs, xb + x - x0 - dsize, yb + y - y0 - dsize, 00711 w, h, PIX_DST & PIX_NOT(PIX_SRC), pixt2, 0, 0); 00712 } 00713 00714 boxaDestroy(&boxa); 00715 pixaDestroy(&pixa); 00716 ptaDestroy(&pta); 00717 pixDestroy(&pixt2); 00718 return 0; 00719 } 00720 00721 00722 /*-----------------------------------------------------------------* 00723 * Display of matched patterns * 00724 *-----------------------------------------------------------------*/ 00725 /*! 00726 * pixDisplayMatchedPattern() 00727 * 00728 * Input: pixs (input image, 1 bpp) 00729 * pixp (pattern to be removed from image, 1 bpp) 00730 * pixe (image after erosion by Sel that approximates pixp, 1 bpp) 00731 * x0, y0 (center of Sel) 00732 * color (to paint the matched patterns; 0xrrggbb00) 00733 * scale (reduction factor for output pixd) 00734 * nlevels (if scale < 1.0, threshold to this number of levels) 00735 * Return: pixd (8 bpp, colormapped), or null on error 00736 * 00737 * Notes: 00738 * (1) A 4 bpp colormapped image is generated. 00739 * (2) If scale <= 1.0, do scale to gray for the output, and threshold 00740 * to nlevels of gray. 00741 * (3) You can use various functions in selgen to create a Sel 00742 * that will generate pixe from pixs. 00743 * (4) This function is applied after pixe has been computed. 00744 * It finds the centroid of each c.c., and colors the output 00745 * pixels using pixp (appropriately aligned) as a stencil. 00746 * Alignment is done using the origin of the Sel and the 00747 * centroid of the eroded image to place the stencil pixp. 00748 */ 00749 PIX * 00750 pixDisplayMatchedPattern(PIX *pixs, 00751 PIX *pixp, 00752 PIX *pixe, 00753 l_int32 x0, 00754 l_int32 y0, 00755 l_uint32 color, 00756 l_float32 scale, 00757 l_int32 nlevels) 00758 { 00759 l_int32 i, nc, xb, yb, x, y, xi, yi, rval, gval, bval; 00760 BOXA *boxa; 00761 PIX *pixd, *pixt, *pixps; 00762 PIXA *pixa; 00763 PTA *pta; 00764 PIXCMAP *cmap; 00765 00766 PROCNAME("pixDisplayMatchedPattern"); 00767 00768 if (!pixs) 00769 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00770 if (!pixp) 00771 return (PIX *)ERROR_PTR("pixp not defined", procName, NULL); 00772 if (!pixe) 00773 return (PIX *)ERROR_PTR("pixe not defined", procName, NULL); 00774 if (pixGetDepth(pixs) != 1 || pixGetDepth(pixp) != 1 || 00775 pixGetDepth(pixe) != 1) 00776 return (PIX *)ERROR_PTR("all input pix not 1 bpp", procName, NULL); 00777 if (scale > 1.0 || scale <= 0.0) { 00778 L_WARNING("scale > 1.0 or < 0.0; setting to 1.0", procName); 00779 scale = 1.0; 00780 } 00781 00782 /* Find the connected components and their centroids */ 00783 boxa = pixConnComp(pixe, &pixa, 8); 00784 if ((nc = boxaGetCount(boxa)) == 0) { 00785 L_WARNING("no matched patterns", procName); 00786 boxaDestroy(&boxa); 00787 pixaDestroy(&pixa); 00788 return 0; 00789 } 00790 pta = pixaCentroids(pixa); 00791 00792 rval = GET_DATA_BYTE(&color, COLOR_RED); 00793 gval = GET_DATA_BYTE(&color, COLOR_GREEN); 00794 bval = GET_DATA_BYTE(&color, COLOR_BLUE); 00795 if (scale == 1.0) { /* output 4 bpp at full resolution */ 00796 pixd = pixConvert1To4(NULL, pixs, 0, 1); 00797 cmap = pixcmapCreate(4); 00798 pixcmapAddColor(cmap, 255, 255, 255); 00799 pixcmapAddColor(cmap, 0, 0, 0); 00800 pixSetColormap(pixd, cmap); 00801 00802 /* Paint through pixp for each match location. The centroid of each 00803 * component in pixe is located at: 00804 * (box->x + x, box->y + y) 00805 * and the 'center' of the pattern used in making pixe is located at 00806 * (x0, y0) 00807 * relative to the UL corner of the pattern. The center of the 00808 * pattern is placed at the center of the component. */ 00809 for (i = 0; i < nc; i++) { 00810 ptaGetIPt(pta, i, &x, &y); 00811 boxaGetBoxGeometry(boxa, i, &xb, &yb, NULL, NULL); 00812 pixSetMaskedCmap(pixd, pixp, xb + x - x0, yb + y - y0, 00813 rval, gval, bval); 00814 } 00815 } 00816 else { /* output 4 bpp downscaled */ 00817 pixt = pixScaleToGray(pixs, scale); 00818 pixd = pixThresholdTo4bpp(pixt, nlevels, 1); 00819 pixps = pixScaleBySampling(pixp, scale, scale); 00820 00821 for (i = 0; i < nc; i++) { 00822 ptaGetIPt(pta, i, &x, &y); 00823 boxaGetBoxGeometry(boxa, i, &xb, &yb, NULL, NULL); 00824 xi = (l_int32)(scale * (xb + x - x0)); 00825 yi = (l_int32)(scale * (yb + y - y0)); 00826 pixSetMaskedCmap(pixd, pixps, xi, yi, rval, gval, bval); 00827 } 00828 pixDestroy(&pixt); 00829 pixDestroy(&pixps); 00830 } 00831 00832 boxaDestroy(&boxa); 00833 pixaDestroy(&pixa); 00834 ptaDestroy(&pta); 00835 return pixd; 00836 } 00837 00838 00839 /*-----------------------------------------------------------------* 00840 * Iterative morphological seed filling * 00841 *-----------------------------------------------------------------*/ 00842 /*! 00843 * pixSeedfillMorph() 00844 * 00845 * Input: pixs (seed) 00846 * pixm (mask) 00847 * connectivity (4 or 8) 00848 * Return: pix where seed has been grown to completion 00849 * into the mask, or null on error 00850 * 00851 * Notes: 00852 * (1) This is in general a very inefficient method for filling 00853 * from a seed into a mask. I've included it here for 00854 * pedagogical reasons, but it should NEVER be used if 00855 * efficiency is any consideration -- use pixSeedfillBinary()! 00856 * (2) We use a 3x3 brick SEL for 8-cc filling and a 3x3 plus SEL for 4-cc. 00857 */ 00858 PIX * 00859 pixSeedfillMorph(PIX *pixs, 00860 PIX *pixm, 00861 l_int32 connectivity) 00862 { 00863 l_int32 same, iter; 00864 PIX *pixt1, *pixd, *temp; 00865 SEL *sel_3; 00866 00867 PROCNAME("pixSeedfillMorph"); 00868 00869 if (!pixs) 00870 return (PIX *)ERROR_PTR("seed pix not defined", procName, NULL); 00871 if (!pixm) 00872 return (PIX *)ERROR_PTR("mask pix not defined", procName, NULL); 00873 if (connectivity != 4 && connectivity != 8) 00874 return (PIX *)ERROR_PTR("connectivity not in {4,8}", procName, NULL); 00875 00876 if (pixSizesEqual(pixs, pixm) == 0) 00877 return (PIX *)ERROR_PTR("pix sizes unequal", procName, NULL); 00878 if (pixGetDepth(pixs) != 1) 00879 return (PIX *)ERROR_PTR("pix not binary", procName, NULL); 00880 00881 if ((sel_3 = selCreateBrick(3, 3, 1, 1, 1)) == NULL) 00882 return (PIX *)ERROR_PTR("sel_3 not made", procName, NULL); 00883 if (connectivity == 4) { /* remove corner hits to make a '+' */ 00884 selSetElement(sel_3, 0, 0, SEL_DONT_CARE); 00885 selSetElement(sel_3, 2, 2, SEL_DONT_CARE); 00886 selSetElement(sel_3, 2, 0, SEL_DONT_CARE); 00887 selSetElement(sel_3, 0, 2, SEL_DONT_CARE); 00888 } 00889 00890 if ((pixt1 = pixCopy(NULL, pixs)) == NULL) 00891 return (PIX *)ERROR_PTR("pixt1 not made", procName, NULL); 00892 if ((pixd = pixCreateTemplate(pixs)) == NULL) 00893 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00894 00895 iter = 0; 00896 while (1) { 00897 iter++; 00898 pixDilate(pixd, pixt1, sel_3); 00899 pixAnd(pixd, pixd, pixm); 00900 pixEqual(pixd, pixt1, &same); 00901 if (same) 00902 break; 00903 else 00904 SWAP(pixt1, pixd); 00905 } 00906 fprintf(stderr, " Num iters in binary reconstruction = %d\n", iter); 00907 00908 pixDestroy(&pixt1); 00909 selDestroy(&sel_3); 00910 00911 return pixd; 00912 } 00913 00914 00915 00916 /*-----------------------------------------------------------------* 00917 * Granulometry on binary images * 00918 *-----------------------------------------------------------------*/ 00919 /*! 00920 * pixRunHistogramMorph() 00921 * 00922 * Input: pixs 00923 * runtype (L_RUN_OFF, L_RUN_ON) 00924 * direction (L_HORIZ, L_VERT) 00925 * maxsize (size of largest runlength counted) 00926 * Return: numa of run-lengths 00927 */ 00928 NUMA * 00929 pixRunHistogramMorph(PIX *pixs, 00930 l_int32 runtype, 00931 l_int32 direction, 00932 l_int32 maxsize) 00933 { 00934 l_int32 count, i; 00935 l_float32 val; 00936 NUMA *na, *nah; 00937 PIX *pixt1, *pixt2, *pixt3; 00938 SEL *sel_2a; 00939 00940 PROCNAME("pixRunHistogramMorph"); 00941 00942 if (!pixs) 00943 return (NUMA *)ERROR_PTR("seed pix not defined", procName, NULL); 00944 if (runtype != L_RUN_OFF && runtype != L_RUN_ON) 00945 return (NUMA *)ERROR_PTR("invalid run type", procName, NULL); 00946 if (direction != L_HORIZ && direction != L_VERT) 00947 return (NUMA *)ERROR_PTR("direction not in {L_HORIZ, L_VERT}", 00948 procName, NULL); 00949 00950 if (pixGetDepth(pixs) != 1) 00951 return (NUMA *)ERROR_PTR("pixs must be binary", procName, NULL); 00952 00953 if ((na = numaCreate(0)) == NULL) 00954 return (NUMA *)ERROR_PTR("na not made", procName, NULL); 00955 00956 if (direction == L_HORIZ) 00957 sel_2a = selCreateBrick(1, 2, 0, 0, 1); 00958 else /* direction == L_VERT */ 00959 sel_2a = selCreateBrick(2, 1, 0, 0, 1); 00960 if (!sel_2a) 00961 return (NUMA *)ERROR_PTR("sel_2a not made", procName, NULL); 00962 00963 if (runtype == L_RUN_OFF) { 00964 if ((pixt1 = pixCopy(NULL, pixs)) == NULL) 00965 return (NUMA *)ERROR_PTR("pix1 not made", procName, NULL); 00966 pixInvert(pixt1, pixt1); 00967 } 00968 else /* runtype == L_RUN_ON */ 00969 pixt1 = pixClone(pixs); 00970 00971 if ((pixt2 = pixCreateTemplate(pixs)) == NULL) 00972 return (NUMA *)ERROR_PTR("pix2 not made", procName, NULL); 00973 if ((pixt3 = pixCreateTemplate(pixs)) == NULL) 00974 return (NUMA *)ERROR_PTR("pix3 not made", procName, NULL); 00975 00976 /* Get pixel counts at different stages of erosion */ 00977 pixCountPixels(pixt1, &count, NULL); 00978 numaAddNumber(na, count); 00979 pixErode(pixt2, pixt1, sel_2a); 00980 pixCountPixels(pixt2, &count, NULL); 00981 numaAddNumber(na, count); 00982 for (i = 0; i < maxsize / 2; i++) { 00983 pixErode(pixt3, pixt2, sel_2a); 00984 pixCountPixels(pixt3, &count, NULL); 00985 numaAddNumber(na, count); 00986 pixErode(pixt2, pixt3, sel_2a); 00987 pixCountPixels(pixt2, &count, NULL); 00988 numaAddNumber(na, count); 00989 } 00990 00991 /* Compute length histogram */ 00992 if ((nah = numaCreate(na->n)) == NULL) 00993 return (NUMA *)ERROR_PTR("nah not made", procName, NULL); 00994 numaAddNumber(nah, 0); /* number at length 0 */ 00995 for (i = 1; i < na->n - 1; i++) { 00996 val = na->array[i+1] - 2 * na->array[i] + na->array[i-1]; 00997 numaAddNumber(nah, val); 00998 } 00999 01000 pixDestroy(&pixt1); 01001 pixDestroy(&pixt2); 01002 pixDestroy(&pixt3); 01003 selDestroy(&sel_2a); 01004 numaDestroy(&na); 01005 01006 return nah; 01007 } 01008 01009 01010 /*-----------------------------------------------------------------* 01011 * Composite operations on grayscale images * 01012 *-----------------------------------------------------------------*/ 01013 /*! 01014 * pixTophat() 01015 * 01016 * Input: pixs 01017 * hsize (of Sel; must be odd; origin implicitly in center) 01018 * vsize (ditto) 01019 * type (L_TOPHAT_WHITE: image - opening 01020 * L_TOPHAT_BLACK: closing - image) 01021 * Return: pixd, or null on error 01022 * 01023 * Notes: 01024 * (1) Sel is a brick with all elements being hits 01025 * (2) If hsize = vsize = 1, returns an image with all 0 data. 01026 * (3) The L_TOPHAT_WHITE flag emphasizes small bright regions, 01027 * whereas the L_TOPHAT_BLACK flag emphasizes small dark regions. 01028 * The L_TOPHAT_WHITE tophat can be accomplished by doing a 01029 * L_TOPHAT_BLACK tophat on the inverse, or v.v. 01030 */ 01031 PIX * 01032 pixTophat(PIX *pixs, 01033 l_int32 hsize, 01034 l_int32 vsize, 01035 l_int32 type) 01036 { 01037 PIX *pixt, *pixd; 01038 01039 PROCNAME("pixTophat"); 01040 01041 if (!pixs) 01042 return (PIX *)ERROR_PTR("seed pix not defined", procName, NULL); 01043 if (pixGetDepth(pixs) != 8) 01044 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 01045 if (hsize < 1 || vsize < 1) 01046 return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); 01047 if ((hsize & 1) == 0 ) { 01048 L_WARNING("horiz sel size must be odd; increasing by 1", procName); 01049 hsize++; 01050 } 01051 if ((vsize & 1) == 0 ) { 01052 L_WARNING("vert sel size must be odd; increasing by 1", procName); 01053 vsize++; 01054 } 01055 if (type != L_TOPHAT_WHITE && type != L_TOPHAT_BLACK) 01056 return (PIX *)ERROR_PTR("type must be L_TOPHAT_BLACK or L_TOPHAT_WHITE", 01057 procName, NULL); 01058 01059 if (hsize == 1 && vsize == 1) 01060 return pixCreateTemplate(pixs); 01061 01062 switch (type) 01063 { 01064 case L_TOPHAT_WHITE: 01065 if ((pixt = pixOpenGray(pixs, hsize, vsize)) == NULL) 01066 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 01067 pixd = pixSubtractGray(NULL, pixs, pixt); 01068 pixDestroy(&pixt); 01069 break; 01070 case L_TOPHAT_BLACK: 01071 if ((pixd = pixCloseGray(pixs, hsize, vsize)) == NULL) 01072 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 01073 pixSubtractGray(pixd, pixd, pixs); 01074 break; 01075 default: 01076 return (PIX *)ERROR_PTR("invalid type", procName, NULL); 01077 } 01078 01079 return pixd; 01080 } 01081 01082 01083 /*! 01084 * pixHDome() 01085 * 01086 * Input: pixs (8 bpp, filling mask) 01087 * height (of seed below the filling maskhdome; must be >= 0) 01088 * connectivity (4 or 8) 01089 * Return: pixd (8 bpp), or null on error 01090 * 01091 * Notes: 01092 * (1) It is more efficient to use a connectivity of 4 for the fill. 01093 * (2) This fills bumps to some level, and extracts the unfilled 01094 * part of the bump. To extract the troughs of basins, first 01095 * invert pixs and then apply pixHDome(). 01096 * (3) It is useful to compare the HDome operation with the TopHat. 01097 * The latter extracts peaks or valleys that have a width 01098 * not exceeding the size of the structuring element used 01099 * in the opening or closing, rsp. The height of the peak is 01100 * irrelevant. By contrast, for the HDome, the gray seedfill 01101 * is used to extract all peaks that have a height not exceeding 01102 * a given value, regardless of their width! 01103 * (4) Slightly more precisely, suppose you set 'height' = 40. 01104 * Then all bumps in pixs with a height greater than or equal 01105 * to 40 become, in pixd, bumps with a max value of exactly 40. 01106 * All shorter bumps have a max value in pixd equal to the height 01107 * of the bump. 01108 * (5) The method: the filling mask, pixs, is the image whose peaks 01109 * are to be extracted. The height of a peak is the distance 01110 * between the top of the peak and the highest "leak" to the 01111 * outside -- think of a sombrero, where the leak occurs 01112 * at the highest point on the rim. 01113 * (a) Generate a seed, pixd, by subtracting some value, p, from 01114 * each pixel in the filling mask, pixs. The value p is 01115 * the 'height' input to this function. 01116 * (b) Fill in pixd starting with this seed, clipping by pixs, 01117 * in the way described in seedfillGrayLow(). The filling 01118 * stops before the peaks in pixs are filled. 01119 * For peaks that have a height > p, pixd is filled to 01120 * the level equal to the (top-of-the-peak - p). 01121 * For peaks of height < p, the peak is left unfilled 01122 * from its highest saddle point (the leak to the outside). 01123 * (c) Subtract the filled seed (pixd) from the filling mask (pixs). 01124 * Note that in this procedure, everything is done starting 01125 * with the filling mask, pixs. 01126 * (6) For segmentation, the resulting image, pixd, can be thresholded 01127 * and used as a seed for another filling operation. 01128 */ 01129 PIX * 01130 pixHDome(PIX *pixs, 01131 l_int32 height, 01132 l_int32 connectivity) 01133 { 01134 PIX *pixsd, *pixd; 01135 01136 PROCNAME("pixHDome"); 01137 01138 if (!pixs) 01139 return (PIX *)ERROR_PTR("src pix not defined", procName, NULL); 01140 if (pixGetDepth(pixs) != 8) 01141 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 01142 if (height < 0) 01143 return (PIX *)ERROR_PTR("height not >= 0", procName, NULL); 01144 if (height == 0) 01145 return pixCreateTemplate(pixs); 01146 01147 if ((pixsd = pixCopy(NULL, pixs)) == NULL) 01148 return (PIX *)ERROR_PTR("pixsd not made", procName, NULL); 01149 pixAddConstantGray(pixsd, -height); 01150 pixSeedfillGray(pixsd, pixs, connectivity); 01151 pixd = pixSubtractGray(NULL, pixs, pixsd); 01152 01153 pixDestroy(&pixsd); 01154 return pixd; 01155 } 01156 01157 01158 /*! 01159 * pixFastTophat() 01160 * 01161 * Input: pixs 01162 * xsize (width of max/min op, smoothing; any integer >= 1) 01163 * ysize (height of max/min op, smoothing; any integer >= 1) 01164 * type (L_TOPHAT_WHITE: image - min 01165 * L_TOPHAT_BLACK: max - image) 01166 * Return: pixd, or null on error 01167 * 01168 * Notes: 01169 * (1) Don't be fooled. This is NOT a tophat. It is a tophat-like 01170 * operation, where the result is similar to what you'd get 01171 * if you used an erosion instead of an opening, or a dilation 01172 * instead of a closing. 01173 * (2) Instead of opening or closing at full resolution, it does 01174 * a fast downscale/minmax operation, then a quick small smoothing 01175 * at low res, a replicative expansion of the "background" 01176 * to full res, and finally a removal of the background level 01177 * from the input image. The smoothing step may not be important. 01178 * (3) It does not remove noise as well as a tophat, but it is 01179 * 5 to 10 times faster. 01180 * If you need the preciseness of the tophat, don't use this. 01181 * (4) The L_TOPHAT_WHITE flag emphasizes small bright regions, 01182 * whereas the L_TOPHAT_BLACK flag emphasizes small dark regions. 01183 */ 01184 PIX * 01185 pixFastTophat(PIX *pixs, 01186 l_int32 xsize, 01187 l_int32 ysize, 01188 l_int32 type) 01189 { 01190 PIX *pixt1, *pixt2, *pixt3, *pixd; 01191 01192 PROCNAME("pixFastTophat"); 01193 01194 if (!pixs) 01195 return (PIX *)ERROR_PTR("seed pix not defined", procName, NULL); 01196 if (pixGetDepth(pixs) != 8) 01197 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 01198 if (xsize < 1 || ysize < 1) 01199 return (PIX *)ERROR_PTR("size < 1", procName, NULL); 01200 if (type != L_TOPHAT_WHITE && type != L_TOPHAT_BLACK) 01201 return (PIX *)ERROR_PTR("type must be L_TOPHAT_BLACK or L_TOPHAT_WHITE", 01202 procName, NULL); 01203 01204 if (xsize == 1 && ysize == 1) 01205 return pixCreateTemplate(pixs); 01206 01207 switch (type) 01208 { 01209 case L_TOPHAT_WHITE: 01210 if ((pixt1 = pixScaleGrayMinMax(pixs, xsize, ysize, L_CHOOSE_MIN)) 01211 == NULL) 01212 return (PIX *)ERROR_PTR("pixt1 not made", procName, NULL); 01213 pixt2 = pixBlockconv(pixt1, 1, 1); /* small smoothing */ 01214 pixt3 = pixScaleBySampling(pixt2, xsize, ysize); 01215 pixd = pixSubtractGray(NULL, pixs, pixt3); 01216 pixDestroy(&pixt3); 01217 break; 01218 case L_TOPHAT_BLACK: 01219 if ((pixt1 = pixScaleGrayMinMax(pixs, xsize, ysize, L_CHOOSE_MAX)) 01220 == NULL) 01221 return (PIX *)ERROR_PTR("pixt1 not made", procName, NULL); 01222 pixt2 = pixBlockconv(pixt1, 1, 1); /* small smoothing */ 01223 pixd = pixScaleBySampling(pixt2, xsize, ysize); 01224 pixSubtractGray(pixd, pixd, pixs); 01225 break; 01226 default: 01227 return (PIX *)ERROR_PTR("invalid type", procName, NULL); 01228 } 01229 01230 pixDestroy(&pixt1); 01231 pixDestroy(&pixt2); 01232 return pixd; 01233 } 01234 01235 01236 /*! 01237 * pixMorphGradient() 01238 * 01239 * Input: pixs 01240 * hsize (of Sel; must be odd; origin implicitly in center) 01241 * vsize (ditto) 01242 * smoothing (half-width of convolution smoothing filter. 01243 * The width is (2 * smoothing + 1), so 0 is no-op. 01244 * Return: pixd, or null on error 01245 */ 01246 PIX * 01247 pixMorphGradient(PIX *pixs, 01248 l_int32 hsize, 01249 l_int32 vsize, 01250 l_int32 smoothing) 01251 { 01252 PIX *pixg, *pixd; 01253 01254 PROCNAME("pixMorphGradient"); 01255 01256 if (!pixs) 01257 return (PIX *)ERROR_PTR("seed pix not defined", procName, NULL); 01258 if (pixGetDepth(pixs) != 8) 01259 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 01260 if (hsize < 1 || vsize < 1) 01261 return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); 01262 if ((hsize & 1) == 0 ) { 01263 L_WARNING("horiz sel size must be odd; increasing by 1", procName); 01264 hsize++; 01265 } 01266 if ((vsize & 1) == 0 ) { 01267 L_WARNING("vert sel size must be odd; increasing by 1", procName); 01268 vsize++; 01269 } 01270 01271 /* Optionally smooth first to remove noise. 01272 * If smoothing is 0, just get a copy */ 01273 pixg = pixBlockconvGray(pixs, NULL, smoothing, smoothing); 01274 01275 /* This gives approximately the gradient of a transition */ 01276 pixd = pixDilateGray(pixg, hsize, vsize); 01277 pixSubtractGray(pixd, pixd, pixg); 01278 pixDestroy(&pixg); 01279 return pixd; 01280 } 01281 01282 01283 /*-----------------------------------------------------------------* 01284 * Centroid of component * 01285 *-----------------------------------------------------------------*/ 01286 /*! 01287 * pixaCentroids() 01288 * 01289 * Input: pixa of components (1 or 8 bpp) 01290 * Return: pta of centroids relative to the UL corner of 01291 * each pix, or null on error 01292 * 01293 * Notes: 01294 * (1) An error message is returned if any pix has something other 01295 * than 1 bpp or 8 bpp depth, and the centroid from that pix 01296 * is saved as (0, 0). 01297 */ 01298 PTA * 01299 pixaCentroids(PIXA *pixa) 01300 { 01301 l_int32 i, n; 01302 l_int32 *centtab = NULL; 01303 l_int32 *sumtab = NULL; 01304 l_float32 x, y; 01305 PIX *pix; 01306 PTA *pta; 01307 01308 PROCNAME("pixaCentroids"); 01309 01310 if (!pixa) 01311 return (PTA *)ERROR_PTR("pixa not defined", procName, NULL); 01312 if ((n = pixaGetCount(pixa)) == 0) 01313 return (PTA *)ERROR_PTR("no pix in pixa", procName, NULL); 01314 01315 if ((pta = ptaCreate(n)) == NULL) 01316 return (PTA *)ERROR_PTR("pta not defined", procName, NULL); 01317 centtab = makePixelCentroidTab8(); 01318 sumtab = makePixelSumTab8(); 01319 01320 for (i = 0; i < n; i++) { 01321 pix = pixaGetPix(pixa, i, L_CLONE); 01322 if (pixCentroid(pix, centtab, sumtab, &x, &y) == 1) 01323 L_ERROR_INT("centroid failure for pix %d", procName, i); 01324 pixDestroy(&pix); 01325 ptaAddPt(pta, x, y); 01326 } 01327 01328 FREE(centtab); 01329 FREE(sumtab); 01330 return pta; 01331 } 01332 01333 01334 /*! 01335 * pixCentroid() 01336 * 01337 * Input: pix (1 or 8 bpp) 01338 * centtab (<optional> table for finding centroids; can be null) 01339 * sumtab (<optional> table for finding pixel sums; can be null) 01340 * &xave, &yave (<return> coordinates of centroid, relative to 01341 * the UL corner of the pix) 01342 * Return: 0 if OK, 1 on error 01343 * 01344 * Notes: 01345 * (1) Any table not passed in will be made internally and destroyed 01346 * after use. 01347 */ 01348 l_int32 01349 pixCentroid(PIX *pix, 01350 l_int32 *centtab, 01351 l_int32 *sumtab, 01352 l_float32 *pxave, 01353 l_float32 *pyave) 01354 { 01355 l_int32 w, h, d, i, j, wpl, pixsum, rowsum, val; 01356 l_float32 xsum, ysum; 01357 l_uint32 *data, *line; 01358 l_uint32 word; 01359 l_uint8 byte; 01360 l_int32 *ctab, *stab; 01361 01362 PROCNAME("pixCentroid"); 01363 01364 if (!pxave || !pyave) 01365 return ERROR_INT("&pxave and &pyave not defined", procName, 1); 01366 *pxave = *pyave = 0.0; 01367 if (!pix) 01368 return ERROR_INT("pix not defined", procName, 1); 01369 pixGetDimensions(pix, &w, &h, &d); 01370 if (d != 1 && d != 8) 01371 return ERROR_INT("pix not 1 or 8 bpp", procName, 1); 01372 01373 if (!centtab) 01374 ctab = makePixelCentroidTab8(); 01375 else 01376 ctab = centtab; 01377 if (!sumtab) 01378 stab = makePixelSumTab8(); 01379 else 01380 stab = sumtab; 01381 01382 data = pixGetData(pix); 01383 wpl = pixGetWpl(pix); 01384 xsum = ysum = 0.0; 01385 pixsum = 0; 01386 if (d == 1) { 01387 for (i = 0; i < h; i++) { 01388 /* The body of this loop computes the sum of the set 01389 * (1) bits on this row, weighted by their distance 01390 * from the left edge of pix, and accumulates that into 01391 * xsum; it accumulates their distance from the top 01392 * edge of pix into ysum, and their total count into 01393 * pixsum. It's equivalent to 01394 * for (j = 0; j < w; j++) { 01395 * if (GET_DATA_BIT(line, j)) { 01396 * xsum += j; 01397 * ysum += i; 01398 * pixsum++; 01399 * } 01400 * } 01401 */ 01402 line = data + wpl * i; 01403 rowsum = 0; 01404 for (j = 0; j < wpl; j++) { 01405 word = line[j]; 01406 if (word) { 01407 byte = word & 0xff; 01408 rowsum += stab[byte]; 01409 xsum += ctab[byte] + (j * 32 + 24) * stab[byte]; 01410 byte = (word >> 8) & 0xff; 01411 rowsum += stab[byte]; 01412 xsum += ctab[byte] + (j * 32 + 16) * stab[byte]; 01413 byte = (word >> 16) & 0xff; 01414 rowsum += stab[byte]; 01415 xsum += ctab[byte] + (j * 32 + 8) * stab[byte]; 01416 byte = (word >> 24) & 0xff; 01417 rowsum += stab[byte]; 01418 xsum += ctab[byte] + j * 32 * stab[byte]; 01419 } 01420 } 01421 pixsum += rowsum; 01422 ysum += rowsum * i; 01423 } 01424 if (pixsum == 0) 01425 L_WARNING("no ON pixels in pix", procName); 01426 else { 01427 *pxave = xsum / (l_float32)pixsum; 01428 *pyave = ysum / (l_float32)pixsum; 01429 } 01430 } 01431 else { /* d == 8 */ 01432 for (i = 0; i < h; i++) { 01433 line = data + wpl * i; 01434 for (j = 0; j < w; j++) { 01435 val = GET_DATA_BYTE(line, j); 01436 xsum += val * j; 01437 ysum += val * i; 01438 pixsum += val; 01439 } 01440 } 01441 if (pixsum == 0) 01442 L_WARNING("all pixels are 0", procName); 01443 else { 01444 *pxave = xsum / (l_float32)pixsum; 01445 *pyave = ysum / (l_float32)pixsum; 01446 } 01447 } 01448 01449 if (!centtab) FREE(ctab); 01450 if (!sumtab) FREE(stab); 01451 return 0; 01452 } 01453 01454