Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/morphapp.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /*====================================================================* | |
| 2 - Copyright (C) 2001 Leptonica. All rights reserved. | |
| 3 - | |
| 4 - Redistribution and use in source and binary forms, with or without | |
| 5 - modification, are permitted provided that the following conditions | |
| 6 - are met: | |
| 7 - 1. Redistributions of source code must retain the above copyright | |
| 8 - notice, this list of conditions and the following disclaimer. | |
| 9 - 2. Redistributions in binary form must reproduce the above | |
| 10 - copyright notice, this list of conditions and the following | |
| 11 - disclaimer in the documentation and/or other materials | |
| 12 - provided with the distribution. | |
| 13 - | |
| 14 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 15 - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 16 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 17 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY | |
| 18 - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| 19 - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 20 - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| 21 - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
| 22 - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
| 23 - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
| 24 - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 25 *====================================================================*/ | |
| 26 | |
| 27 | |
| 28 /*! | |
| 29 * \file morphapp.c | |
| 30 * <pre> | |
| 31 * | |
| 32 * These are some useful and/or interesting composite | |
| 33 * image processing operations, of the type that are often | |
| 34 * useful in applications. Most are morphological in | |
| 35 * nature. | |
| 36 * | |
| 37 * Extraction of boundary pixels | |
| 38 * PIX *pixExtractBoundary() | |
| 39 * | |
| 40 * Selective morph sequence operation under mask | |
| 41 * PIX *pixMorphSequenceMasked() | |
| 42 * | |
| 43 * Selective morph sequence operation on each component | |
| 44 * PIX *pixMorphSequenceByComponent() | |
| 45 * PIXA *pixaMorphSequenceByComponent() | |
| 46 * | |
| 47 * Selective morph sequence operation on each region | |
| 48 * PIX *pixMorphSequenceByRegion() | |
| 49 * PIXA *pixaMorphSequenceByRegion() | |
| 50 * | |
| 51 * Union and intersection of parallel composite operations | |
| 52 * PIX *pixUnionOfMorphOps() | |
| 53 * PIX *pixIntersectionOfMorphOps() | |
| 54 * | |
| 55 * Selective connected component filling | |
| 56 * PIX *pixSelectiveConnCompFill() | |
| 57 * | |
| 58 * Removal of matched patterns | |
| 59 * PIX *pixRemoveMatchedPattern() | |
| 60 * | |
| 61 * Display of matched patterns | |
| 62 * PIX *pixDisplayMatchedPattern() | |
| 63 * | |
| 64 * Extension of pixa by iterative erosion or dilation (and by scaling) | |
| 65 * PIXA *pixaExtendByMorph() | |
| 66 * PIXA *pixaExtendByScaling() | |
| 67 * | |
| 68 * Iterative morphological seed filling (don't use for real work) | |
| 69 * PIX *pixSeedfillMorph() | |
| 70 * | |
| 71 * Granulometry on binary images | |
| 72 * NUMA *pixRunHistogramMorph() | |
| 73 * | |
| 74 * Composite operations on grayscale images | |
| 75 * PIX *pixTophat() | |
| 76 * PIX *pixHDome() | |
| 77 * PIX *pixFastTophat() | |
| 78 * PIX *pixMorphGradient() | |
| 79 * | |
| 80 * Centroid of component | |
| 81 * PTA *pixaCentroids() | |
| 82 * l_int32 pixCentroid() | |
| 83 * </pre> | |
| 84 */ | |
| 85 | |
| 86 #ifdef HAVE_CONFIG_H | |
| 87 #include <config_auto.h> | |
| 88 #endif /* HAVE_CONFIG_H */ | |
| 89 | |
| 90 #include "allheaders.h" | |
| 91 #include "array_internal.h" | |
| 92 | |
| 93 #define SWAP(x, y) {temp = (x); (x) = (y); (y) = temp;} | |
| 94 | |
| 95 /*-----------------------------------------------------------------* | |
| 96 * Extraction of boundary pixels * | |
| 97 *-----------------------------------------------------------------*/ | |
| 98 /*! | |
| 99 * \brief pixExtractBoundary() | |
| 100 * | |
| 101 * \param[in] pixs 1 bpp | |
| 102 * \param[in] type 0 for background pixels; 1 for foreground pixels | |
| 103 * \return pixd, or NULL on error | |
| 104 * | |
| 105 * <pre> | |
| 106 * Notes: | |
| 107 * (1) Extracts the fg or bg boundary pixels for each component. | |
| 108 * Components are assumed to end at the boundary of pixs. | |
| 109 * </pre> | |
| 110 */ | |
| 111 PIX * | |
| 112 pixExtractBoundary(PIX *pixs, | |
| 113 l_int32 type) | |
| 114 { | |
| 115 PIX *pixd; | |
| 116 | |
| 117 if (!pixs) | |
| 118 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 119 | |
| 120 if (type == 0) | |
| 121 pixd = pixDilateBrick(NULL, pixs, 3, 3); | |
| 122 else | |
| 123 pixd = pixErodeBrick(NULL, pixs, 3, 3); | |
| 124 pixXor(pixd, pixd, pixs); | |
| 125 return pixd; | |
| 126 } | |
| 127 | |
| 128 | |
| 129 /*-----------------------------------------------------------------* | |
| 130 * Selective morph sequence operation under mask * | |
| 131 *-----------------------------------------------------------------*/ | |
| 132 /*! | |
| 133 * \brief pixMorphSequenceMasked() | |
| 134 * | |
| 135 * \param[in] pixs 1 bpp | |
| 136 * \param[in] pixm [optional] 1 bpp mask | |
| 137 * \param[in] sequence string specifying sequence of operations | |
| 138 * \param[in] dispsep horizontal separation in pixels between | |
| 139 * successive displays; use zero to suppress display | |
| 140 * \return pixd, or NULL on error | |
| 141 * | |
| 142 * <pre> | |
| 143 * Notes: | |
| 144 * (1) This applies the morph sequence to the image, but only allows | |
| 145 * changes in pixs for pixels under the background of pixm. | |
| 146 * (5) If pixm is NULL, this is just pixMorphSequence(). | |
| 147 * </pre> | |
| 148 */ | |
| 149 PIX * | |
| 150 pixMorphSequenceMasked(PIX *pixs, | |
| 151 PIX *pixm, | |
| 152 const char *sequence, | |
| 153 l_int32 dispsep) | |
| 154 { | |
| 155 PIX *pixd; | |
| 156 | |
| 157 if (!pixs) | |
| 158 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 159 if (!sequence) | |
| 160 return (PIX *)ERROR_PTR("sequence not defined", __func__, NULL); | |
| 161 | |
| 162 pixd = pixMorphSequence(pixs, sequence, dispsep); | |
| 163 pixCombineMasked(pixd, pixs, pixm); /* restore src pixels under mask fg */ | |
| 164 return pixd; | |
| 165 } | |
| 166 | |
| 167 | |
| 168 /*-----------------------------------------------------------------* | |
| 169 * Morph sequence operation on each component * | |
| 170 *-----------------------------------------------------------------*/ | |
| 171 /*! | |
| 172 * \brief pixMorphSequenceByComponent() | |
| 173 * | |
| 174 * \param[in] pixs 1 bpp | |
| 175 * \param[in] sequence string specifying sequence | |
| 176 * \param[in] connectivity 4 or 8 | |
| 177 * \param[in] minw min width to consider; use 0 or 1 for any width | |
| 178 * \param[in] minh min height to consider; use 0 or 1 for any height | |
| 179 * \param[out] pboxa [optional] return boxa of c.c. in pixs | |
| 180 * \return pixd, or NULL on error | |
| 181 * | |
| 182 * <pre> | |
| 183 * Notes: | |
| 184 * (1) See pixMorphSequence() for composing operation sequences. | |
| 185 * (2) This operates separately on each c.c. in the input pix. | |
| 186 * (3) The dilation does NOT increase the c.c. size; it is clipped | |
| 187 * to the size of the original c.c. This is necessary to | |
| 188 * keep the c.c. independent after the operation. | |
| 189 * (4) You can specify that the width and/or height must equal | |
| 190 * or exceed a minimum size for the operation to take place. | |
| 191 * (5) Use NULL for boxa to avoid returning the boxa. | |
| 192 * </pre> | |
| 193 */ | |
| 194 PIX * | |
| 195 pixMorphSequenceByComponent(PIX *pixs, | |
| 196 const char *sequence, | |
| 197 l_int32 connectivity, | |
| 198 l_int32 minw, | |
| 199 l_int32 minh, | |
| 200 BOXA **pboxa) | |
| 201 { | |
| 202 l_int32 n, i, x, y, w, h; | |
| 203 BOXA *boxa; | |
| 204 PIX *pix, *pixd; | |
| 205 PIXA *pixas, *pixad; | |
| 206 | |
| 207 if (!pixs) | |
| 208 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 209 if (!sequence) | |
| 210 return (PIX *)ERROR_PTR("sequence not defined", __func__, NULL); | |
| 211 | |
| 212 if (minw <= 0) minw = 1; | |
| 213 if (minh <= 0) minh = 1; | |
| 214 | |
| 215 /* Get the c.c. */ | |
| 216 if ((boxa = pixConnComp(pixs, &pixas, connectivity)) == NULL) | |
| 217 return (PIX *)ERROR_PTR("boxa not made", __func__, NULL); | |
| 218 | |
| 219 /* Operate on each c.c. independently */ | |
| 220 pixad = pixaMorphSequenceByComponent(pixas, sequence, minw, minh); | |
| 221 pixaDestroy(&pixas); | |
| 222 boxaDestroy(&boxa); | |
| 223 if (!pixad) | |
| 224 return (PIX *)ERROR_PTR("pixad not made", __func__, NULL); | |
| 225 | |
| 226 /* Display the result out into pixd */ | |
| 227 pixd = pixCreateTemplate(pixs); | |
| 228 n = pixaGetCount(pixad); | |
| 229 for (i = 0; i < n; i++) { | |
| 230 pixaGetBoxGeometry(pixad, i, &x, &y, &w, &h); | |
| 231 pix = pixaGetPix(pixad, i, L_CLONE); | |
| 232 pixRasterop(pixd, x, y, w, h, PIX_PAINT, pix, 0, 0); | |
| 233 pixDestroy(&pix); | |
| 234 } | |
| 235 | |
| 236 if (pboxa) | |
| 237 *pboxa = pixaGetBoxa(pixad, L_CLONE); | |
| 238 pixaDestroy(&pixad); | |
| 239 return pixd; | |
| 240 } | |
| 241 | |
| 242 | |
| 243 /*! | |
| 244 * \brief pixaMorphSequenceByComponent() | |
| 245 * | |
| 246 * \param[in] pixas of 1 bpp pix | |
| 247 * \param[in] sequence string specifying sequence | |
| 248 * \param[in] minw min width to consider; use 0 or 1 for any width | |
| 249 * \param[in] minh min height to consider; use 0 or 1 for any height | |
| 250 * \return pixad, or NULL on error | |
| 251 * | |
| 252 * <pre> | |
| 253 * Notes: | |
| 254 * (1) See pixMorphSequence() for composing operation sequences. | |
| 255 * (2) This operates separately on each c.c. in the input pixa. | |
| 256 * (3) You can specify that the width and/or height must equal | |
| 257 * or exceed a minimum size for the operation to take place. | |
| 258 * (4) The input pixa should have a boxa giving the locations | |
| 259 * of the pix components. | |
| 260 * </pre> | |
| 261 */ | |
| 262 PIXA * | |
| 263 pixaMorphSequenceByComponent(PIXA *pixas, | |
| 264 const char *sequence, | |
| 265 l_int32 minw, | |
| 266 l_int32 minh) | |
| 267 { | |
| 268 l_int32 n, i, w, h, d; | |
| 269 BOX *box; | |
| 270 PIX *pix1, *pix2; | |
| 271 PIXA *pixad; | |
| 272 | |
| 273 if (!pixas) | |
| 274 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 275 if ((n = pixaGetCount(pixas)) == 0) | |
| 276 return (PIXA *)ERROR_PTR("no pix in pixas", __func__, NULL); | |
| 277 if (n != pixaGetBoxaCount(pixas)) | |
| 278 L_WARNING("boxa size != n\n", __func__); | |
| 279 pixaGetPixDimensions(pixas, 0, NULL, NULL, &d); | |
| 280 if (d != 1) | |
| 281 return (PIXA *)ERROR_PTR("depth not 1 bpp", __func__, NULL); | |
| 282 | |
| 283 if (!sequence) | |
| 284 return (PIXA *)ERROR_PTR("sequence not defined", __func__, NULL); | |
| 285 if (minw <= 0) minw = 1; | |
| 286 if (minh <= 0) minh = 1; | |
| 287 | |
| 288 if ((pixad = pixaCreate(n)) == NULL) | |
| 289 return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL); | |
| 290 for (i = 0; i < n; i++) { | |
| 291 pixaGetPixDimensions(pixas, i, &w, &h, NULL); | |
| 292 if (w >= minw && h >= minh) { | |
| 293 if ((pix1 = pixaGetPix(pixas, i, L_CLONE)) == NULL) { | |
| 294 pixaDestroy(&pixad); | |
| 295 return (PIXA *)ERROR_PTR("pix1 not found", __func__, NULL); | |
| 296 } | |
| 297 if ((pix2 = pixMorphCompSequence(pix1, sequence, 0)) == NULL) { | |
| 298 pixaDestroy(&pixad); | |
| 299 return (PIXA *)ERROR_PTR("pix2 not made", __func__, NULL); | |
| 300 } | |
| 301 pixaAddPix(pixad, pix2, L_INSERT); | |
| 302 box = pixaGetBox(pixas, i, L_COPY); | |
| 303 pixaAddBox(pixad, box, L_INSERT); | |
| 304 pixDestroy(&pix1); | |
| 305 } | |
| 306 } | |
| 307 | |
| 308 return pixad; | |
| 309 } | |
| 310 | |
| 311 | |
| 312 /*-----------------------------------------------------------------* | |
| 313 * Morph sequence operation on each region * | |
| 314 *-----------------------------------------------------------------*/ | |
| 315 /*! | |
| 316 * \brief pixMorphSequenceByRegion() | |
| 317 * | |
| 318 * \param[in] pixs 1 bpp | |
| 319 * \param[in] pixm mask specifying regions | |
| 320 * \param[in] sequence string specifying sequence | |
| 321 * \param[in] connectivity 4 or 8, used on mask | |
| 322 * \param[in] minw min width to consider; use 0 or 1 for any width | |
| 323 * \param[in] minh min height to consider; use 0 or 1 for any height | |
| 324 * \param[out] pboxa [optional] return boxa of c.c. in pixm | |
| 325 * \return pixd, or NULL on error | |
| 326 * | |
| 327 * <pre> | |
| 328 * Notes: | |
| 329 * (1) See pixMorphCompSequence() for composing operation sequences. | |
| 330 * (2) This operates separately on the region in pixs corresponding | |
| 331 * to each c.c. in the mask pixm. It differs from | |
| 332 * pixMorphSequenceByComponent() in that the latter does not have | |
| 333 * a pixm (mask), but instead operates independently on each | |
| 334 * component in pixs. | |
| 335 * (3) Dilation will NOT increase the region size; the result | |
| 336 * is clipped to the size of the mask region. This is necessary | |
| 337 * to make regions independent after the operation. | |
| 338 * (4) You can specify that the width and/or height of a region must | |
| 339 * equal or exceed a minimum size for the operation to take place. | |
| 340 * (5) Use NULL for %pboxa to avoid returning the boxa. | |
| 341 * </pre> | |
| 342 */ | |
| 343 PIX * | |
| 344 pixMorphSequenceByRegion(PIX *pixs, | |
| 345 PIX *pixm, | |
| 346 const char *sequence, | |
| 347 l_int32 connectivity, | |
| 348 l_int32 minw, | |
| 349 l_int32 minh, | |
| 350 BOXA **pboxa) | |
| 351 { | |
| 352 l_int32 n, i, x, y, w, h; | |
| 353 BOXA *boxa; | |
| 354 PIX *pix, *pixd; | |
| 355 PIXA *pixam, *pixad; | |
| 356 | |
| 357 if (pboxa) *pboxa = NULL; | |
| 358 if (!pixs) | |
| 359 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 360 if (!pixm) | |
| 361 return (PIX *)ERROR_PTR("pixm not defined", __func__, NULL); | |
| 362 if (pixGetDepth(pixs) != 1 || pixGetDepth(pixm) != 1) | |
| 363 return (PIX *)ERROR_PTR("pixs and pixm not both 1 bpp", __func__, NULL); | |
| 364 if (!sequence) | |
| 365 return (PIX *)ERROR_PTR("sequence not defined", __func__, NULL); | |
| 366 | |
| 367 if (minw <= 0) minw = 1; | |
| 368 if (minh <= 0) minh = 1; | |
| 369 | |
| 370 /* Get the c.c. of the mask */ | |
| 371 if ((boxa = pixConnComp(pixm, &pixam, connectivity)) == NULL) | |
| 372 return (PIX *)ERROR_PTR("boxa not made", __func__, NULL); | |
| 373 | |
| 374 /* Operate on each region in pixs independently */ | |
| 375 pixad = pixaMorphSequenceByRegion(pixs, pixam, sequence, minw, minh); | |
| 376 pixaDestroy(&pixam); | |
| 377 boxaDestroy(&boxa); | |
| 378 if (!pixad) | |
| 379 return (PIX *)ERROR_PTR("pixad not made", __func__, NULL); | |
| 380 | |
| 381 /* Display the result out into pixd */ | |
| 382 pixd = pixCreateTemplate(pixs); | |
| 383 n = pixaGetCount(pixad); | |
| 384 for (i = 0; i < n; i++) { | |
| 385 pixaGetBoxGeometry(pixad, i, &x, &y, &w, &h); | |
| 386 pix = pixaGetPix(pixad, i, L_CLONE); | |
| 387 pixRasterop(pixd, x, y, w, h, PIX_PAINT, pix, 0, 0); | |
| 388 pixDestroy(&pix); | |
| 389 } | |
| 390 | |
| 391 if (pboxa) | |
| 392 *pboxa = pixaGetBoxa(pixad, L_CLONE); | |
| 393 pixaDestroy(&pixad); | |
| 394 return pixd; | |
| 395 } | |
| 396 | |
| 397 | |
| 398 /*! | |
| 399 * \brief pixaMorphSequenceByRegion() | |
| 400 * | |
| 401 * \param[in] pixs 1 bpp | |
| 402 * \param[in] pixam of 1 bpp mask elements | |
| 403 * \param[in] sequence string specifying sequence | |
| 404 * \param[in] minw min width to consider; use 0 or 1 for any width | |
| 405 * \param[in] minh min height to consider; use 0 or 1 for any height | |
| 406 * \return pixad, or NULL on error | |
| 407 * | |
| 408 * <pre> | |
| 409 * Notes: | |
| 410 * (1) See pixMorphSequence() for composing operation sequences. | |
| 411 * (2) This operates separately on each region in the input pixs | |
| 412 * defined by the components in pixam. | |
| 413 * (3) You can specify that the width and/or height of a mask | |
| 414 * component must equal or exceed a minimum size for the | |
| 415 * operation to take place. | |
| 416 * (4) The input pixam should have a boxa giving the locations | |
| 417 * of the regions in pixs. | |
| 418 * </pre> | |
| 419 */ | |
| 420 PIXA * | |
| 421 pixaMorphSequenceByRegion(PIX *pixs, | |
| 422 PIXA *pixam, | |
| 423 const char *sequence, | |
| 424 l_int32 minw, | |
| 425 l_int32 minh) | |
| 426 { | |
| 427 l_int32 n, i, w, h, same, maxd, fullpa, fullba; | |
| 428 BOX *box; | |
| 429 PIX *pix1, *pix2, *pix3; | |
| 430 PIXA *pixad; | |
| 431 | |
| 432 if (!pixs) | |
| 433 return (PIXA *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 434 if (pixGetDepth(pixs) != 1) | |
| 435 return (PIXA *)ERROR_PTR("pixs not 1 bpp", __func__, NULL); | |
| 436 if (!sequence) | |
| 437 return (PIXA *)ERROR_PTR("sequence not defined", __func__, NULL); | |
| 438 if (!pixam) | |
| 439 return (PIXA *)ERROR_PTR("pixam not defined", __func__, NULL); | |
| 440 pixaVerifyDepth(pixam, &same, &maxd); | |
| 441 if (maxd != 1) | |
| 442 return (PIXA *)ERROR_PTR("mask depth not 1 bpp", __func__, NULL); | |
| 443 pixaIsFull(pixam, &fullpa, &fullba); | |
| 444 if (!fullpa || !fullba) | |
| 445 return (PIXA *)ERROR_PTR("missing comps in pixam", __func__, NULL); | |
| 446 n = pixaGetCount(pixam); | |
| 447 if (minw <= 0) minw = 1; | |
| 448 if (minh <= 0) minh = 1; | |
| 449 | |
| 450 if ((pixad = pixaCreate(n)) == NULL) | |
| 451 return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL); | |
| 452 | |
| 453 /* Use the rectangle to remove the appropriate part of pixs; | |
| 454 * then AND with the mask component to get the actual fg | |
| 455 * of pixs that is under the mask component. */ | |
| 456 for (i = 0; i < n; i++) { | |
| 457 pixaGetPixDimensions(pixam, i, &w, &h, NULL); | |
| 458 if (w >= minw && h >= minh) { | |
| 459 pix1 = pixaGetPix(pixam, i, L_CLONE); | |
| 460 box = pixaGetBox(pixam, i, L_COPY); | |
| 461 pix2 = pixClipRectangle(pixs, box, NULL); | |
| 462 pixAnd(pix2, pix2, pix1); | |
| 463 pix3 = pixMorphCompSequence(pix2, sequence, 0); | |
| 464 pixDestroy(&pix1); | |
| 465 pixDestroy(&pix2); | |
| 466 if (!pix3) { | |
| 467 boxDestroy(&box); | |
| 468 pixaDestroy(&pixad); | |
| 469 L_ERROR("pix3 not made in iter %d; aborting\n", __func__, i); | |
| 470 break; | |
| 471 } | |
| 472 pixaAddPix(pixad, pix3, L_INSERT); | |
| 473 pixaAddBox(pixad, box, L_INSERT); | |
| 474 } | |
| 475 } | |
| 476 | |
| 477 return pixad; | |
| 478 } | |
| 479 | |
| 480 | |
| 481 /*-----------------------------------------------------------------* | |
| 482 * Union and intersection of parallel composite operations * | |
| 483 *-----------------------------------------------------------------*/ | |
| 484 /*! | |
| 485 * \brief pixUnionOfMorphOps() | |
| 486 * | |
| 487 * \param[in] pixs 1 bpp | |
| 488 * \param[in] sela | |
| 489 * \param[in] type L_MORPH_DILATE, etc. | |
| 490 * \return pixd union of the specified morphological operation | |
| 491 * on pixs for each Sel in the Sela, or NULL on error | |
| 492 */ | |
| 493 PIX * | |
| 494 pixUnionOfMorphOps(PIX *pixs, | |
| 495 SELA *sela, | |
| 496 l_int32 type) | |
| 497 { | |
| 498 l_int32 n, i; | |
| 499 PIX *pixt, *pixd; | |
| 500 SEL *sel; | |
| 501 | |
| 502 if (!pixs || pixGetDepth(pixs) != 1) | |
| 503 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL); | |
| 504 if (!sela) | |
| 505 return (PIX *)ERROR_PTR("sela not defined", __func__, NULL); | |
| 506 n = selaGetCount(sela); | |
| 507 if (n == 0) | |
| 508 return (PIX *)ERROR_PTR("no sels in sela", __func__, NULL); | |
| 509 if (type != L_MORPH_DILATE && type != L_MORPH_ERODE && | |
| 510 type != L_MORPH_OPEN && type != L_MORPH_CLOSE && | |
| 511 type != L_MORPH_HMT) | |
| 512 return (PIX *)ERROR_PTR("invalid type", __func__, NULL); | |
| 513 | |
| 514 pixd = pixCreateTemplate(pixs); | |
| 515 for (i = 0; i < n; i++) { | |
| 516 sel = selaGetSel(sela, i); | |
| 517 if (type == L_MORPH_DILATE) | |
| 518 pixt = pixDilate(NULL, pixs, sel); | |
| 519 else if (type == L_MORPH_ERODE) | |
| 520 pixt = pixErode(NULL, pixs, sel); | |
| 521 else if (type == L_MORPH_OPEN) | |
| 522 pixt = pixOpen(NULL, pixs, sel); | |
| 523 else if (type == L_MORPH_CLOSE) | |
| 524 pixt = pixClose(NULL, pixs, sel); | |
| 525 else /* type == L_MORPH_HMT */ | |
| 526 pixt = pixHMT(NULL, pixs, sel); | |
| 527 pixOr(pixd, pixd, pixt); | |
| 528 pixDestroy(&pixt); | |
| 529 } | |
| 530 | |
| 531 return pixd; | |
| 532 } | |
| 533 | |
| 534 | |
| 535 /*! | |
| 536 * \brief pixIntersectionOfMorphOps() | |
| 537 * | |
| 538 * \param[in] pixs 1 bpp | |
| 539 * \param[in] sela | |
| 540 * \param[in] type L_MORPH_DILATE, etc. | |
| 541 * \return pixd intersection of the specified morphological operation | |
| 542 * on pixs for each Sel in the Sela, or NULL on error | |
| 543 */ | |
| 544 PIX * | |
| 545 pixIntersectionOfMorphOps(PIX *pixs, | |
| 546 SELA *sela, | |
| 547 l_int32 type) | |
| 548 { | |
| 549 l_int32 n, i; | |
| 550 PIX *pixt, *pixd; | |
| 551 SEL *sel; | |
| 552 | |
| 553 if (!pixs || pixGetDepth(pixs) != 1) | |
| 554 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL); | |
| 555 if (!sela) | |
| 556 return (PIX *)ERROR_PTR("sela not defined", __func__, NULL); | |
| 557 n = selaGetCount(sela); | |
| 558 if (n == 0) | |
| 559 return (PIX *)ERROR_PTR("no sels in sela", __func__, NULL); | |
| 560 if (type != L_MORPH_DILATE && type != L_MORPH_ERODE && | |
| 561 type != L_MORPH_OPEN && type != L_MORPH_CLOSE && | |
| 562 type != L_MORPH_HMT) | |
| 563 return (PIX *)ERROR_PTR("invalid type", __func__, NULL); | |
| 564 | |
| 565 pixd = pixCreateTemplate(pixs); | |
| 566 pixSetAll(pixd); | |
| 567 for (i = 0; i < n; i++) { | |
| 568 sel = selaGetSel(sela, i); | |
| 569 if (type == L_MORPH_DILATE) | |
| 570 pixt = pixDilate(NULL, pixs, sel); | |
| 571 else if (type == L_MORPH_ERODE) | |
| 572 pixt = pixErode(NULL, pixs, sel); | |
| 573 else if (type == L_MORPH_OPEN) | |
| 574 pixt = pixOpen(NULL, pixs, sel); | |
| 575 else if (type == L_MORPH_CLOSE) | |
| 576 pixt = pixClose(NULL, pixs, sel); | |
| 577 else /* type == L_MORPH_HMT */ | |
| 578 pixt = pixHMT(NULL, pixs, sel); | |
| 579 pixAnd(pixd, pixd, pixt); | |
| 580 pixDestroy(&pixt); | |
| 581 } | |
| 582 | |
| 583 return pixd; | |
| 584 } | |
| 585 | |
| 586 | |
| 587 | |
| 588 /*-----------------------------------------------------------------* | |
| 589 * Selective connected component filling * | |
| 590 *-----------------------------------------------------------------*/ | |
| 591 /*! | |
| 592 * \brief pixSelectiveConnCompFill() | |
| 593 * | |
| 594 * \param[in] pixs 1 bpp | |
| 595 * \param[in] connectivity 4 or 8 | |
| 596 * \param[in] minw min width to consider; use 0 or 1 for any width | |
| 597 * \param[in] minh min height to consider; use 0 or 1 for any height | |
| 598 * \return pix with holes filled in selected c.c., or NULL on error | |
| 599 */ | |
| 600 PIX * | |
| 601 pixSelectiveConnCompFill(PIX *pixs, | |
| 602 l_int32 connectivity, | |
| 603 l_int32 minw, | |
| 604 l_int32 minh) | |
| 605 { | |
| 606 l_int32 n, i, x, y, w, h; | |
| 607 BOXA *boxa; | |
| 608 PIX *pix1, *pix2, *pixd; | |
| 609 PIXA *pixa; | |
| 610 | |
| 611 if (!pixs) | |
| 612 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 613 if (pixGetDepth(pixs) != 1) | |
| 614 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, NULL); | |
| 615 if (minw <= 0) minw = 1; | |
| 616 if (minh <= 0) minh = 1; | |
| 617 | |
| 618 if ((boxa = pixConnComp(pixs, &pixa, connectivity)) == NULL) | |
| 619 return (PIX *)ERROR_PTR("boxa not made", __func__, NULL); | |
| 620 n = boxaGetCount(boxa); | |
| 621 pixd = pixCopy(NULL, pixs); | |
| 622 for (i = 0; i < n; i++) { | |
| 623 boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); | |
| 624 if (w >= minw && h >= minh) { | |
| 625 pix1 = pixaGetPix(pixa, i, L_CLONE); | |
| 626 if ((pix2 = pixHolesByFilling(pix1, 12 - connectivity)) == NULL) { | |
| 627 L_ERROR("pix2 not made in iter %d\n", __func__, i); | |
| 628 pixDestroy(&pix1); | |
| 629 continue; | |
| 630 } | |
| 631 pixRasterop(pixd, x, y, w, h, PIX_PAINT, pix2, 0, 0); | |
| 632 pixDestroy(&pix1); | |
| 633 pixDestroy(&pix2); | |
| 634 } | |
| 635 } | |
| 636 pixaDestroy(&pixa); | |
| 637 boxaDestroy(&boxa); | |
| 638 | |
| 639 return pixd; | |
| 640 } | |
| 641 | |
| 642 | |
| 643 /*-----------------------------------------------------------------* | |
| 644 * Removal of matched patterns * | |
| 645 *-----------------------------------------------------------------*/ | |
| 646 /*! | |
| 647 * \brief pixRemoveMatchedPattern() | |
| 648 * | |
| 649 * \param[in] pixs input image, 1 bpp | |
| 650 * \param[in] pixp pattern to be removed from image, 1 bpp | |
| 651 * \param[in] pixe image after erosion by Sel that approximates pixp | |
| 652 * \param[in] x0, y0 center of Sel | |
| 653 * \param[in] dsize number of pixels on each side by which pixp is | |
| 654 * dilated before being subtracted from pixs; | |
| 655 * valid values are {0, 1, 2, 3, 4} | |
| 656 * \return 0 if OK, 1 on error | |
| 657 * | |
| 658 * <pre> | |
| 659 * Notes: | |
| 660 * (1) This is in-place. | |
| 661 * (2) You can use various functions in selgen to create a Sel | |
| 662 * that is used to generate pixe from pixs. | |
| 663 * (3) This function is applied after pixe has been computed. | |
| 664 * It finds the centroid of each c.c., and subtracts | |
| 665 * (the appropriately dilated version of) pixp, with the center | |
| 666 * of the Sel used to align pixp with pixs. | |
| 667 * </pre> | |
| 668 */ | |
| 669 l_ok | |
| 670 pixRemoveMatchedPattern(PIX *pixs, | |
| 671 PIX *pixp, | |
| 672 PIX *pixe, | |
| 673 l_int32 x0, | |
| 674 l_int32 y0, | |
| 675 l_int32 dsize) | |
| 676 { | |
| 677 l_int32 i, nc, x, y, w, h, xb, yb; | |
| 678 BOXA *boxa; | |
| 679 PIX *pix1, *pix2; | |
| 680 PIXA *pixa; | |
| 681 PTA *pta; | |
| 682 SEL *sel; | |
| 683 | |
| 684 if (!pixs) | |
| 685 return ERROR_INT("pixs not defined", __func__, 1); | |
| 686 if (!pixp) | |
| 687 return ERROR_INT("pixp not defined", __func__, 1); | |
| 688 if (!pixe) | |
| 689 return ERROR_INT("pixe not defined", __func__, 1); | |
| 690 if (pixGetDepth(pixs) != 1 || pixGetDepth(pixp) != 1 || | |
| 691 pixGetDepth(pixe) != 1) | |
| 692 return ERROR_INT("all input pix not 1 bpp", __func__, 1); | |
| 693 if (dsize < 0 || dsize > 4) | |
| 694 return ERROR_INT("dsize not in {0,1,2,3,4}", __func__, 1); | |
| 695 | |
| 696 /* Find the connected components and their centroids */ | |
| 697 boxa = pixConnComp(pixe, &pixa, 8); | |
| 698 if ((nc = boxaGetCount(boxa)) == 0) { | |
| 699 L_WARNING("no matched patterns\n", __func__); | |
| 700 boxaDestroy(&boxa); | |
| 701 pixaDestroy(&pixa); | |
| 702 return 0; | |
| 703 } | |
| 704 pta = pixaCentroids(pixa); | |
| 705 pixaDestroy(&pixa); | |
| 706 | |
| 707 /* Optionally dilate the pattern, first adding a border that | |
| 708 * is large enough to accommodate the dilated pixels */ | |
| 709 sel = NULL; | |
| 710 if (dsize > 0) { | |
| 711 sel = selCreateBrick(2 * dsize + 1, 2 * dsize + 1, dsize, dsize, | |
| 712 SEL_HIT); | |
| 713 pix1 = pixAddBorder(pixp, dsize, 0); | |
| 714 pix2 = pixDilate(NULL, pix1, sel); | |
| 715 selDestroy(&sel); | |
| 716 pixDestroy(&pix1); | |
| 717 } else { | |
| 718 pix2 = pixClone(pixp); | |
| 719 } | |
| 720 | |
| 721 /* Subtract out each dilated pattern. The centroid of each | |
| 722 * component is located at: | |
| 723 * (box->x + x, box->y + y) | |
| 724 * and the 'center' of the pattern used in making pixe is located at | |
| 725 * (x0 + dsize, (y0 + dsize) | |
| 726 * relative to the UL corner of the pattern. The center of the | |
| 727 * pattern is placed at the center of the component. */ | |
| 728 pixGetDimensions(pix2, &w, &h, NULL); | |
| 729 for (i = 0; i < nc; i++) { | |
| 730 ptaGetIPt(pta, i, &x, &y); | |
| 731 boxaGetBoxGeometry(boxa, i, &xb, &yb, NULL, NULL); | |
| 732 pixRasterop(pixs, xb + x - x0 - dsize, yb + y - y0 - dsize, | |
| 733 w, h, PIX_DST & PIX_NOT(PIX_SRC), pix2, 0, 0); | |
| 734 } | |
| 735 | |
| 736 boxaDestroy(&boxa); | |
| 737 ptaDestroy(&pta); | |
| 738 pixDestroy(&pix2); | |
| 739 return 0; | |
| 740 } | |
| 741 | |
| 742 | |
| 743 /*-----------------------------------------------------------------* | |
| 744 * Display of matched patterns * | |
| 745 *-----------------------------------------------------------------*/ | |
| 746 /*! | |
| 747 * \brief pixDisplayMatchedPattern() | |
| 748 * | |
| 749 * \param[in] pixs input image, 1 bpp | |
| 750 * \param[in] pixp pattern to be removed from image, 1 bpp | |
| 751 * \param[in] pixe image after erosion by Sel that approximates pixp | |
| 752 * \param[in] x0, y0 center of Sel | |
| 753 * \param[in] color to paint the matched patterns; 0xrrggbb00 | |
| 754 * \param[in] scale reduction factor for output pixd | |
| 755 * \param[in] nlevels if scale < 1.0, threshold to this number of levels | |
| 756 * \return pixd 8 bpp, colormapped, or NULL on error | |
| 757 * | |
| 758 * <pre> | |
| 759 * Notes: | |
| 760 * (1) A 4 bpp colormapped image is generated. | |
| 761 * (2) If scale <= 1.0, do scale to gray for the output, and threshold | |
| 762 * to nlevels of gray. | |
| 763 * (3) You can use various functions in selgen to create a Sel | |
| 764 * that will generate pixe from pixs. | |
| 765 * (4) This function is applied after pixe has been computed. | |
| 766 * It finds the centroid of each c.c., and colors the output | |
| 767 * pixels using pixp (appropriately aligned) as a stencil. | |
| 768 * Alignment is done using the origin of the Sel and the | |
| 769 * centroid of the eroded image to place the stencil pixp. | |
| 770 * </pre> | |
| 771 */ | |
| 772 PIX * | |
| 773 pixDisplayMatchedPattern(PIX *pixs, | |
| 774 PIX *pixp, | |
| 775 PIX *pixe, | |
| 776 l_int32 x0, | |
| 777 l_int32 y0, | |
| 778 l_uint32 color, | |
| 779 l_float32 scale, | |
| 780 l_int32 nlevels) | |
| 781 { | |
| 782 l_int32 i, nc, xb, yb, x, y, xi, yi, rval, gval, bval; | |
| 783 BOXA *boxa; | |
| 784 PIX *pixd, *pixt, *pixps; | |
| 785 PIXA *pixa; | |
| 786 PTA *pta; | |
| 787 PIXCMAP *cmap; | |
| 788 | |
| 789 if (!pixs) | |
| 790 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 791 if (!pixp) | |
| 792 return (PIX *)ERROR_PTR("pixp not defined", __func__, NULL); | |
| 793 if (!pixe) | |
| 794 return (PIX *)ERROR_PTR("pixe not defined", __func__, NULL); | |
| 795 if (pixGetDepth(pixs) != 1 || pixGetDepth(pixp) != 1 || | |
| 796 pixGetDepth(pixe) != 1) | |
| 797 return (PIX *)ERROR_PTR("all input pix not 1 bpp", __func__, NULL); | |
| 798 if (scale > 1.0 || scale <= 0.0) { | |
| 799 L_WARNING("scale > 1.0 or < 0.0; setting to 1.0\n", __func__); | |
| 800 scale = 1.0; | |
| 801 } | |
| 802 | |
| 803 /* Find the connected components and their centroids */ | |
| 804 boxa = pixConnComp(pixe, &pixa, 8); | |
| 805 if ((nc = boxaGetCount(boxa)) == 0) { | |
| 806 L_WARNING("no matched patterns\n", __func__); | |
| 807 boxaDestroy(&boxa); | |
| 808 pixaDestroy(&pixa); | |
| 809 return 0; | |
| 810 } | |
| 811 pta = pixaCentroids(pixa); | |
| 812 | |
| 813 extractRGBValues(color, &rval, &gval, &bval); | |
| 814 if (scale == 1.0) { /* output 4 bpp at full resolution */ | |
| 815 pixd = pixConvert1To4(NULL, pixs, 0, 1); | |
| 816 cmap = pixcmapCreate(4); | |
| 817 pixcmapAddColor(cmap, 255, 255, 255); | |
| 818 pixcmapAddColor(cmap, 0, 0, 0); | |
| 819 pixSetColormap(pixd, cmap); | |
| 820 | |
| 821 /* Paint through pixp for each match location. The centroid of each | |
| 822 * component in pixe is located at: | |
| 823 * (box->x + x, box->y + y) | |
| 824 * and the 'center' of the pattern used in making pixe is located at | |
| 825 * (x0, y0) | |
| 826 * relative to the UL corner of the pattern. The center of the | |
| 827 * pattern is placed at the center of the component. */ | |
| 828 for (i = 0; i < nc; i++) { | |
| 829 ptaGetIPt(pta, i, &x, &y); | |
| 830 boxaGetBoxGeometry(boxa, i, &xb, &yb, NULL, NULL); | |
| 831 pixSetMaskedCmap(pixd, pixp, xb + x - x0, yb + y - y0, | |
| 832 rval, gval, bval); | |
| 833 } | |
| 834 } else { /* output 4 bpp downscaled */ | |
| 835 pixt = pixScaleToGray(pixs, scale); | |
| 836 pixd = pixThresholdTo4bpp(pixt, nlevels, 1); | |
| 837 pixps = pixScaleBySampling(pixp, scale, scale); | |
| 838 | |
| 839 for (i = 0; i < nc; i++) { | |
| 840 ptaGetIPt(pta, i, &x, &y); | |
| 841 boxaGetBoxGeometry(boxa, i, &xb, &yb, NULL, NULL); | |
| 842 xi = (l_int32)(scale * (xb + x - x0)); | |
| 843 yi = (l_int32)(scale * (yb + y - y0)); | |
| 844 pixSetMaskedCmap(pixd, pixps, xi, yi, rval, gval, bval); | |
| 845 } | |
| 846 pixDestroy(&pixt); | |
| 847 pixDestroy(&pixps); | |
| 848 } | |
| 849 | |
| 850 boxaDestroy(&boxa); | |
| 851 pixaDestroy(&pixa); | |
| 852 ptaDestroy(&pta); | |
| 853 return pixd; | |
| 854 } | |
| 855 | |
| 856 | |
| 857 /*------------------------------------------------------------------------* | |
| 858 * Extension of pixa by iterative erosion or dilation (and by scaling) * | |
| 859 *------------------------------------------------------------------------*/ | |
| 860 /*! | |
| 861 * \brief pixaExtendByMorph() | |
| 862 * | |
| 863 * \param[in] pixas | |
| 864 * \param[in] type L_MORPH_DILATE, L_MORPH_ERODE | |
| 865 * \param[in] niters | |
| 866 * \param[in] sel used for dilation, erosion; uses 2x2 if null | |
| 867 * \param[in] include 1 to include a copy of the input pixas in pixad; | |
| 868 * 0 to omit | |
| 869 * \return pixad with derived pix, using all iterations, or NULL on error | |
| 870 * | |
| 871 * <pre> | |
| 872 * Notes: | |
| 873 * (1) This dilates or erodes every pix in %pixas, iteratively, | |
| 874 * using the input Sel (or, if null, a 2x2 Sel by default), | |
| 875 * and puts the results in %pixad. | |
| 876 * (2) If %niters <= 0, this is a no-op; it returns a clone of pixas. | |
| 877 * (3) If %include == 1, the output %pixad contains all the pix | |
| 878 * in %pixas. Otherwise, it doesn't, but pixaJoin() can be | |
| 879 * used later to join pixas with pixad. | |
| 880 * </pre> | |
| 881 */ | |
| 882 PIXA * | |
| 883 pixaExtendByMorph(PIXA *pixas, | |
| 884 l_int32 type, | |
| 885 l_int32 niters, | |
| 886 SEL *sel, | |
| 887 l_int32 include) | |
| 888 { | |
| 889 l_int32 maxdepth, i, j, n; | |
| 890 PIX *pix0, *pix1, *pix2; | |
| 891 SEL *selt; | |
| 892 PIXA *pixad; | |
| 893 | |
| 894 if (!pixas) | |
| 895 return (PIXA *)ERROR_PTR("pixas undefined", __func__, NULL); | |
| 896 if (niters <= 0) { | |
| 897 L_INFO("niters = %d; nothing to do\n", __func__, niters); | |
| 898 return pixaCopy(pixas, L_CLONE); | |
| 899 } | |
| 900 if (type != L_MORPH_DILATE && type != L_MORPH_ERODE) | |
| 901 return (PIXA *)ERROR_PTR("invalid type", __func__, NULL); | |
| 902 pixaGetDepthInfo(pixas, &maxdepth, NULL); | |
| 903 if (maxdepth > 1) | |
| 904 return (PIXA *)ERROR_PTR("some pix have bpp > 1", __func__, NULL); | |
| 905 | |
| 906 if (!sel) | |
| 907 selt = selCreateBrick(2, 2, 0, 0, SEL_HIT); /* default */ | |
| 908 else | |
| 909 selt = selCopy(sel); | |
| 910 n = pixaGetCount(pixas); | |
| 911 pixad = pixaCreate(n * niters); | |
| 912 for (i = 0; i < n; i++) { | |
| 913 pix1 = pixaGetPix(pixas, i, L_CLONE); | |
| 914 if (include) pixaAddPix(pixad, pix1, L_COPY); | |
| 915 pix0 = pix1; /* need to keep the handle to destroy the clone */ | |
| 916 for (j = 0; j < niters; j++) { | |
| 917 if (type == L_MORPH_DILATE) { | |
| 918 pix2 = pixDilate(NULL, pix1, selt); | |
| 919 } else { /* L_MORPH_ERODE */ | |
| 920 pix2 = pixErode(NULL, pix1, selt); | |
| 921 } | |
| 922 pixaAddPix(pixad, pix2, L_INSERT); | |
| 923 pix1 = pix2; /* owned by pixad; do not destroy */ | |
| 924 } | |
| 925 pixDestroy(&pix0); | |
| 926 } | |
| 927 | |
| 928 selDestroy(&selt); | |
| 929 return pixad; | |
| 930 } | |
| 931 | |
| 932 | |
| 933 /*! | |
| 934 * \brief pixaExtendByScaling() | |
| 935 * | |
| 936 * \param[in] pixas | |
| 937 * \param[in] nasc numa of scaling factors | |
| 938 * \param[in] type L_HORIZ, L_VERT, L_BOTH_DIRECTIONS | |
| 939 * \param[in] include 1 to include a copy of the input pixas in pixad; | |
| 940 * 0 to omit | |
| 941 * \return pixad with derived pix, using all scalings, or NULL on error | |
| 942 * | |
| 943 * <pre> | |
| 944 * Notes: | |
| 945 * (1) This scales every pix in %pixas by each factor in %nasc. | |
| 946 * and puts the results in %pixad. | |
| 947 * (2) If %include == 1, the output %pixad contains all the pix | |
| 948 * in %pixas. Otherwise, it doesn't, but pixaJoin() can be | |
| 949 * used later to join pixas with pixad. | |
| 950 * </pre> | |
| 951 */ | |
| 952 PIXA * | |
| 953 pixaExtendByScaling(PIXA *pixas, | |
| 954 NUMA *nasc, | |
| 955 l_int32 type, | |
| 956 l_int32 include) | |
| 957 { | |
| 958 l_int32 i, j, n, nsc, w, h, scalew, scaleh; | |
| 959 l_float32 scalefact; | |
| 960 PIX *pix1, *pix2; | |
| 961 PIXA *pixad; | |
| 962 | |
| 963 if (!pixas) | |
| 964 return (PIXA *)ERROR_PTR("pixas undefined", __func__, NULL); | |
| 965 if (!nasc || numaGetCount(nasc) == 0) | |
| 966 return (PIXA *)ERROR_PTR("nasc undefined or empty", __func__, NULL); | |
| 967 if (type != L_HORIZ && type != L_VERT && type != L_BOTH_DIRECTIONS) | |
| 968 return (PIXA *)ERROR_PTR("invalid type", __func__, NULL); | |
| 969 | |
| 970 n = pixaGetCount(pixas); | |
| 971 nsc = numaGetCount(nasc); | |
| 972 if ((pixad = pixaCreate(n * (nsc + 1))) == NULL) { | |
| 973 L_ERROR("pixad not made: n = %d, nsc = %d\n", __func__, n, nsc); | |
| 974 return NULL; | |
| 975 } | |
| 976 for (i = 0; i < n; i++) { | |
| 977 pix1 = pixaGetPix(pixas, i, L_CLONE); | |
| 978 if (include) pixaAddPix(pixad, pix1, L_COPY); | |
| 979 pixGetDimensions(pix1, &w, &h, NULL); | |
| 980 for (j = 0; j < nsc; j++) { | |
| 981 numaGetFValue(nasc, j, &scalefact); | |
| 982 scalew = w; | |
| 983 scaleh = h; | |
| 984 if (type == L_HORIZ || type == L_BOTH_DIRECTIONS) | |
| 985 scalew = w * scalefact; | |
| 986 if (type == L_VERT || type == L_BOTH_DIRECTIONS) | |
| 987 scaleh = h * scalefact; | |
| 988 pix2 = pixScaleToSize(pix1, scalew, scaleh); | |
| 989 pixaAddPix(pixad, pix2, L_INSERT); | |
| 990 } | |
| 991 pixDestroy(&pix1); | |
| 992 } | |
| 993 return pixad; | |
| 994 } | |
| 995 | |
| 996 | |
| 997 /*-----------------------------------------------------------------* | |
| 998 * Iterative morphological seed filling * | |
| 999 *-----------------------------------------------------------------*/ | |
| 1000 /*! | |
| 1001 * \brief pixSeedfillMorph() | |
| 1002 * | |
| 1003 * \param[in] pixs seed | |
| 1004 * \param[in] pixm mask | |
| 1005 * \param[in] maxiters use 0 to go to completion | |
| 1006 * \param[in] connectivity 4 or 8 | |
| 1007 * \return pixd after filling into the mask or NULL on error | |
| 1008 * | |
| 1009 * <pre> | |
| 1010 * Notes: | |
| 1011 * (1) This is in general a very inefficient method for filling | |
| 1012 * from a seed into a mask. Use it for a small number of iterations, | |
| 1013 * but if you expect more than a few iterations, use | |
| 1014 * pixSeedfillBinary(). | |
| 1015 * (2) We use a 3x3 brick SEL for 8-cc filling and a 3x3 plus SEL for 4-cc. | |
| 1016 * </pre> | |
| 1017 */ | |
| 1018 PIX * | |
| 1019 pixSeedfillMorph(PIX *pixs, | |
| 1020 PIX *pixm, | |
| 1021 l_int32 maxiters, | |
| 1022 l_int32 connectivity) | |
| 1023 { | |
| 1024 l_int32 same, i; | |
| 1025 PIX *pixt, *pixd, *temp; | |
| 1026 SEL *sel_3; | |
| 1027 | |
| 1028 if (!pixs || pixGetDepth(pixs) != 1) | |
| 1029 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL); | |
| 1030 if (!pixm) | |
| 1031 return (PIX *)ERROR_PTR("mask pix not defined", __func__, NULL); | |
| 1032 if (connectivity != 4 && connectivity != 8) | |
| 1033 return (PIX *)ERROR_PTR("connectivity not in {4,8}", __func__, NULL); | |
| 1034 if (maxiters <= 0) maxiters = 1000; | |
| 1035 if (pixSizesEqual(pixs, pixm) == 0) | |
| 1036 return (PIX *)ERROR_PTR("pix sizes unequal", __func__, NULL); | |
| 1037 | |
| 1038 if ((sel_3 = selCreateBrick(3, 3, 1, 1, SEL_HIT)) == NULL) | |
| 1039 return (PIX *)ERROR_PTR("sel_3 not made", __func__, NULL); | |
| 1040 if (connectivity == 4) { /* remove corner hits to make a '+' */ | |
| 1041 selSetElement(sel_3, 0, 0, SEL_DONT_CARE); | |
| 1042 selSetElement(sel_3, 2, 2, SEL_DONT_CARE); | |
| 1043 selSetElement(sel_3, 2, 0, SEL_DONT_CARE); | |
| 1044 selSetElement(sel_3, 0, 2, SEL_DONT_CARE); | |
| 1045 } | |
| 1046 | |
| 1047 pixt = pixCopy(NULL, pixs); | |
| 1048 pixd = pixCreateTemplate(pixs); | |
| 1049 for (i = 1; i <= maxiters; i++) { | |
| 1050 pixDilate(pixd, pixt, sel_3); | |
| 1051 pixAnd(pixd, pixd, pixm); | |
| 1052 pixEqual(pixd, pixt, &same); | |
| 1053 if (same || i == maxiters) | |
| 1054 break; | |
| 1055 else | |
| 1056 SWAP(pixt, pixd); | |
| 1057 } | |
| 1058 lept_stderr(" Num iters in binary reconstruction = %d\n", i); | |
| 1059 | |
| 1060 pixDestroy(&pixt); | |
| 1061 selDestroy(&sel_3); | |
| 1062 return pixd; | |
| 1063 } | |
| 1064 | |
| 1065 | |
| 1066 /*-----------------------------------------------------------------* | |
| 1067 * Granulometry on binary images * | |
| 1068 *-----------------------------------------------------------------*/ | |
| 1069 /*! | |
| 1070 * \brief pixRunHistogramMorph() | |
| 1071 * | |
| 1072 * \param[in] pixs 1 bpp | |
| 1073 * \param[in] runtype L_RUN_OFF, L_RUN_ON | |
| 1074 * \param[in] direction L_HORIZ, L_VERT | |
| 1075 * \param[in] maxsize size of largest runlength counted | |
| 1076 * \return numa of run-lengths | |
| 1077 */ | |
| 1078 NUMA * | |
| 1079 pixRunHistogramMorph(PIX *pixs, | |
| 1080 l_int32 runtype, | |
| 1081 l_int32 direction, | |
| 1082 l_int32 maxsize) | |
| 1083 { | |
| 1084 l_int32 count, i, size; | |
| 1085 l_float32 val; | |
| 1086 NUMA *na, *nah; | |
| 1087 PIX *pix1, *pix2, *pix3; | |
| 1088 SEL *sel_2a; | |
| 1089 | |
| 1090 if (!pixs) | |
| 1091 return (NUMA *)ERROR_PTR("seed pix not defined", __func__, NULL); | |
| 1092 if (runtype != L_RUN_OFF && runtype != L_RUN_ON) | |
| 1093 return (NUMA *)ERROR_PTR("invalid run type", __func__, NULL); | |
| 1094 if (direction != L_HORIZ && direction != L_VERT) | |
| 1095 return (NUMA *)ERROR_PTR("direction not in {L_HORIZ, L_VERT}", | |
| 1096 __func__, NULL); | |
| 1097 if (pixGetDepth(pixs) != 1) | |
| 1098 return (NUMA *)ERROR_PTR("pixs must be binary", __func__, NULL); | |
| 1099 | |
| 1100 if (direction == L_HORIZ) | |
| 1101 sel_2a = selCreateBrick(1, 2, 0, 0, SEL_HIT); | |
| 1102 else /* direction == L_VERT */ | |
| 1103 sel_2a = selCreateBrick(2, 1, 0, 0, SEL_HIT); | |
| 1104 if (!sel_2a) | |
| 1105 return (NUMA *)ERROR_PTR("sel_2a not made", __func__, NULL); | |
| 1106 | |
| 1107 if (runtype == L_RUN_OFF) { | |
| 1108 if ((pix1 = pixCopy(NULL, pixs)) == NULL) { | |
| 1109 selDestroy(&sel_2a); | |
| 1110 return (NUMA *)ERROR_PTR("pix1 not made", __func__, NULL); | |
| 1111 } | |
| 1112 pixInvert(pix1, pix1); | |
| 1113 } else { /* runtype == L_RUN_ON */ | |
| 1114 pix1 = pixClone(pixs); | |
| 1115 } | |
| 1116 | |
| 1117 /* Get pixel counts at different stages of erosion */ | |
| 1118 na = numaCreate(0); | |
| 1119 pix2 = pixCreateTemplate(pixs); | |
| 1120 pix3 = pixCreateTemplate(pixs); | |
| 1121 pixCountPixels(pix1, &count, NULL); | |
| 1122 numaAddNumber(na, count); | |
| 1123 pixErode(pix2, pix1, sel_2a); | |
| 1124 pixCountPixels(pix2, &count, NULL); | |
| 1125 numaAddNumber(na, count); | |
| 1126 for (i = 0; i < maxsize / 2; i++) { | |
| 1127 pixErode(pix3, pix2, sel_2a); | |
| 1128 pixCountPixels(pix3, &count, NULL); | |
| 1129 numaAddNumber(na, count); | |
| 1130 pixErode(pix2, pix3, sel_2a); | |
| 1131 pixCountPixels(pix2, &count, NULL); | |
| 1132 numaAddNumber(na, count); | |
| 1133 } | |
| 1134 | |
| 1135 /* Compute length histogram */ | |
| 1136 size = numaGetCount(na); | |
| 1137 nah = numaCreate(size); | |
| 1138 numaAddNumber(nah, 0); /* number at length 0 */ | |
| 1139 for (i = 1; i < size - 1; i++) { | |
| 1140 val = na->array[i+1] - 2 * na->array[i] + na->array[i-1]; | |
| 1141 numaAddNumber(nah, val); | |
| 1142 } | |
| 1143 | |
| 1144 pixDestroy(&pix1); | |
| 1145 pixDestroy(&pix2); | |
| 1146 pixDestroy(&pix3); | |
| 1147 selDestroy(&sel_2a); | |
| 1148 numaDestroy(&na); | |
| 1149 return nah; | |
| 1150 } | |
| 1151 | |
| 1152 | |
| 1153 /*-----------------------------------------------------------------* | |
| 1154 * Composite operations on grayscale images * | |
| 1155 *-----------------------------------------------------------------*/ | |
| 1156 /*! | |
| 1157 * \brief pixTophat() | |
| 1158 * | |
| 1159 * \param[in] pixs 1 bpp | |
| 1160 * \param[in] hsize of Sel; must be odd; origin implicitly in center | |
| 1161 * \param[in] vsize ditto | |
| 1162 * \param[in] type L_TOPHAT_WHITE: image - opening | |
| 1163 * L_TOPHAT_BLACK: closing - image | |
| 1164 * \return pixd, or NULL on error | |
| 1165 * | |
| 1166 * <pre> | |
| 1167 * Notes: | |
| 1168 * (1) Sel is a brick with all elements being hits | |
| 1169 * (2) If hsize = vsize = 1, returns an image with all 0 data. | |
| 1170 * (3) The L_TOPHAT_WHITE flag emphasizes small bright regions, | |
| 1171 * whereas the L_TOPHAT_BLACK flag emphasizes small dark regions. | |
| 1172 * The L_TOPHAT_WHITE tophat can be accomplished by doing a | |
| 1173 * L_TOPHAT_BLACK tophat on the inverse, or v.v. | |
| 1174 * </pre> | |
| 1175 */ | |
| 1176 PIX * | |
| 1177 pixTophat(PIX *pixs, | |
| 1178 l_int32 hsize, | |
| 1179 l_int32 vsize, | |
| 1180 l_int32 type) | |
| 1181 { | |
| 1182 PIX *pixt, *pixd; | |
| 1183 | |
| 1184 if (!pixs) | |
| 1185 return (PIX *)ERROR_PTR("seed pix not defined", __func__, NULL); | |
| 1186 if (pixGetDepth(pixs) != 8) | |
| 1187 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); | |
| 1188 if (hsize < 1 || vsize < 1) | |
| 1189 return (PIX *)ERROR_PTR("hsize or vsize < 1", __func__, NULL); | |
| 1190 if ((hsize & 1) == 0 ) { | |
| 1191 L_WARNING("horiz sel size must be odd; increasing by 1\n", __func__); | |
| 1192 hsize++; | |
| 1193 } | |
| 1194 if ((vsize & 1) == 0 ) { | |
| 1195 L_WARNING("vert sel size must be odd; increasing by 1\n", __func__); | |
| 1196 vsize++; | |
| 1197 } | |
| 1198 if (type != L_TOPHAT_WHITE && type != L_TOPHAT_BLACK) | |
| 1199 return (PIX *)ERROR_PTR("type must be L_TOPHAT_BLACK or L_TOPHAT_WHITE", | |
| 1200 __func__, NULL); | |
| 1201 | |
| 1202 if (hsize == 1 && vsize == 1) | |
| 1203 return pixCreateTemplate(pixs); | |
| 1204 | |
| 1205 switch (type) | |
| 1206 { | |
| 1207 case L_TOPHAT_WHITE: | |
| 1208 if ((pixt = pixOpenGray(pixs, hsize, vsize)) == NULL) | |
| 1209 return (PIX *)ERROR_PTR("pixt not made", __func__, NULL); | |
| 1210 pixd = pixSubtractGray(NULL, pixs, pixt); | |
| 1211 pixDestroy(&pixt); | |
| 1212 break; | |
| 1213 case L_TOPHAT_BLACK: | |
| 1214 if ((pixd = pixCloseGray(pixs, hsize, vsize)) == NULL) | |
| 1215 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 1216 pixSubtractGray(pixd, pixd, pixs); | |
| 1217 break; | |
| 1218 default: | |
| 1219 return (PIX *)ERROR_PTR("invalid type", __func__, NULL); | |
| 1220 } | |
| 1221 | |
| 1222 return pixd; | |
| 1223 } | |
| 1224 | |
| 1225 | |
| 1226 /*! | |
| 1227 * \brief pixHDome() | |
| 1228 * | |
| 1229 * \param[in] pixs 8 bpp, filling mask | |
| 1230 * \param[in] height of seed below the filling maskhdome; must be >= 0 | |
| 1231 * \param[in] connectivity 4 or 8 | |
| 1232 * \return pixd 8 bpp, or NULL on error | |
| 1233 * | |
| 1234 * <pre> | |
| 1235 * Notes: | |
| 1236 * (1) It is more efficient to use a connectivity of 4 for the fill. | |
| 1237 * (2) This fills bumps to some level, and extracts the unfilled | |
| 1238 * part of the bump. To extract the troughs of basins, first | |
| 1239 * invert pixs and then apply pixHDome(). | |
| 1240 * (3) It is useful to compare the HDome operation with the TopHat. | |
| 1241 * The latter extracts peaks or valleys that have a width | |
| 1242 * not exceeding the size of the structuring element used | |
| 1243 * in the opening or closing, rsp. The height of the peak is | |
| 1244 * irrelevant. By contrast, for the HDome, the gray seedfill | |
| 1245 * is used to extract all peaks that have a height not exceeding | |
| 1246 * a given value, regardless of their width! | |
| 1247 * (4) Slightly more precisely, suppose you set 'height' = 40. | |
| 1248 * Then all bumps in pixs with a height greater than or equal | |
| 1249 * to 40 become, in pixd, bumps with a max value of exactly 40. | |
| 1250 * All shorter bumps have a max value in pixd equal to the height | |
| 1251 * of the bump. | |
| 1252 * (5) The method: the filling mask, pixs, is the image whose peaks | |
| 1253 * are to be extracted. The height of a peak is the distance | |
| 1254 * between the top of the peak and the highest "leak" to the | |
| 1255 * outside -- think of a sombrero, where the leak occurs | |
| 1256 * at the highest point on the rim. | |
| 1257 * (a) Generate a seed, pixd, by subtracting some value, p, from | |
| 1258 * each pixel in the filling mask, pixs. The value p is | |
| 1259 * the 'height' input to this function. | |
| 1260 * (b) Fill in pixd starting with this seed, clipping by pixs, | |
| 1261 * in the way described in seedfillGrayLow(). The filling | |
| 1262 * stops before the peaks in pixs are filled. | |
| 1263 * For peaks that have a height > p, pixd is filled to | |
| 1264 * the level equal to the (top-of-the-peak - p). | |
| 1265 * For peaks of height < p, the peak is left unfilled | |
| 1266 * from its highest saddle point (the leak to the outside). | |
| 1267 * (c) Subtract the filled seed (pixd) from the filling mask (pixs). | |
| 1268 * Note that in this procedure, everything is done starting | |
| 1269 * with the filling mask, pixs. | |
| 1270 * (6) For segmentation, the resulting image, pixd, can be thresholded | |
| 1271 * and used as a seed for another filling operation. | |
| 1272 * </pre> | |
| 1273 */ | |
| 1274 PIX * | |
| 1275 pixHDome(PIX *pixs, | |
| 1276 l_int32 height, | |
| 1277 l_int32 connectivity) | |
| 1278 { | |
| 1279 PIX *pixsd, *pixd; | |
| 1280 | |
| 1281 if (!pixs) | |
| 1282 return (PIX *)ERROR_PTR("src pix not defined", __func__, NULL); | |
| 1283 if (pixGetDepth(pixs) != 8) | |
| 1284 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); | |
| 1285 if (height < 0) | |
| 1286 return (PIX *)ERROR_PTR("height not >= 0", __func__, NULL); | |
| 1287 if (height == 0) | |
| 1288 return pixCreateTemplate(pixs); | |
| 1289 | |
| 1290 if ((pixsd = pixCopy(NULL, pixs)) == NULL) | |
| 1291 return (PIX *)ERROR_PTR("pixsd not made", __func__, NULL); | |
| 1292 pixAddConstantGray(pixsd, -height); | |
| 1293 pixSeedfillGray(pixsd, pixs, connectivity); | |
| 1294 pixd = pixSubtractGray(NULL, pixs, pixsd); | |
| 1295 pixDestroy(&pixsd); | |
| 1296 return pixd; | |
| 1297 } | |
| 1298 | |
| 1299 | |
| 1300 /*! | |
| 1301 * \brief pixFastTophat() | |
| 1302 * | |
| 1303 * \param[in] pixs 8 bpp | |
| 1304 * \param[in] xsize width of max/min op, smoothing; any integer >= 1 | |
| 1305 * \param[in] ysize height of max/min op, smoothing; any integer >= 1 | |
| 1306 * \param[in] type L_TOPHAT_WHITE: image - min | |
| 1307 * L_TOPHAT_BLACK: max - image | |
| 1308 * \return pixd, or NULL on error | |
| 1309 * | |
| 1310 * <pre> | |
| 1311 * Notes: | |
| 1312 * (1) Don't be fooled. This is NOT a tophat. It is a tophat-like | |
| 1313 * operation, where the result is similar to what you'd get | |
| 1314 * if you used an erosion instead of an opening, or a dilation | |
| 1315 * instead of a closing. | |
| 1316 * (2) Instead of opening or closing at full resolution, it does | |
| 1317 * a fast downscale/minmax operation, then a quick small smoothing | |
| 1318 * at low res, a replicative expansion of the "background" | |
| 1319 * to full res, and finally a removal of the background level | |
| 1320 * from the input image. The smoothing step may not be important. | |
| 1321 * (3) It does not remove noise as well as a tophat, but it is | |
| 1322 * 5 to 10 times faster. | |
| 1323 * If you need the preciseness of the tophat, don't use this. | |
| 1324 * (4) The L_TOPHAT_WHITE flag emphasizes small bright regions, | |
| 1325 * whereas the L_TOPHAT_BLACK flag emphasizes small dark regions. | |
| 1326 * </pre> | |
| 1327 */ | |
| 1328 PIX * | |
| 1329 pixFastTophat(PIX *pixs, | |
| 1330 l_int32 xsize, | |
| 1331 l_int32 ysize, | |
| 1332 l_int32 type) | |
| 1333 { | |
| 1334 PIX *pix1, *pix2, *pix3, *pixd; | |
| 1335 | |
| 1336 if (!pixs) | |
| 1337 return (PIX *)ERROR_PTR("seed pix not defined", __func__, NULL); | |
| 1338 if (pixGetDepth(pixs) != 8) | |
| 1339 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); | |
| 1340 if (xsize < 1 || ysize < 1) | |
| 1341 return (PIX *)ERROR_PTR("size < 1", __func__, NULL); | |
| 1342 if (type != L_TOPHAT_WHITE && type != L_TOPHAT_BLACK) | |
| 1343 return (PIX *)ERROR_PTR("type must be L_TOPHAT_BLACK or L_TOPHAT_WHITE", | |
| 1344 __func__, NULL); | |
| 1345 | |
| 1346 if (xsize == 1 && ysize == 1) | |
| 1347 return pixCreateTemplate(pixs); | |
| 1348 | |
| 1349 switch (type) | |
| 1350 { | |
| 1351 case L_TOPHAT_WHITE: | |
| 1352 if ((pix1 = pixScaleGrayMinMax(pixs, xsize, ysize, L_CHOOSE_MIN)) | |
| 1353 == NULL) | |
| 1354 return (PIX *)ERROR_PTR("pix1 not made", __func__, NULL); | |
| 1355 pix2 = pixBlockconv(pix1, 1, 1); /* small smoothing */ | |
| 1356 pix3 = pixScaleBySampling(pix2, xsize, ysize); | |
| 1357 pixd = pixSubtractGray(NULL, pixs, pix3); | |
| 1358 pixDestroy(&pix3); | |
| 1359 break; | |
| 1360 case L_TOPHAT_BLACK: | |
| 1361 if ((pix1 = pixScaleGrayMinMax(pixs, xsize, ysize, L_CHOOSE_MAX)) | |
| 1362 == NULL) | |
| 1363 return (PIX *)ERROR_PTR("pix1 not made", __func__, NULL); | |
| 1364 pix2 = pixBlockconv(pix1, 1, 1); /* small smoothing */ | |
| 1365 pixd = pixScaleBySampling(pix2, xsize, ysize); | |
| 1366 pixSubtractGray(pixd, pixd, pixs); | |
| 1367 break; | |
| 1368 default: | |
| 1369 return (PIX *)ERROR_PTR("invalid type", __func__, NULL); | |
| 1370 } | |
| 1371 | |
| 1372 pixDestroy(&pix1); | |
| 1373 pixDestroy(&pix2); | |
| 1374 return pixd; | |
| 1375 } | |
| 1376 | |
| 1377 | |
| 1378 /*! | |
| 1379 * \brief pixMorphGradient() | |
| 1380 * | |
| 1381 * \param[in] pixs 8 bpp | |
| 1382 * \param[in] hsize sel width; must be odd; origin implicitly in center | |
| 1383 * \param[in] vsize sel height | |
| 1384 * \param[in] smoothing half-width of convolution smoothing filter. | |
| 1385 * The width is (2 * smoothing + 1, so 0 is no-op. | |
| 1386 * \return pixd, or NULL on error | |
| 1387 */ | |
| 1388 PIX * | |
| 1389 pixMorphGradient(PIX *pixs, | |
| 1390 l_int32 hsize, | |
| 1391 l_int32 vsize, | |
| 1392 l_int32 smoothing) | |
| 1393 { | |
| 1394 PIX *pixg, *pixd; | |
| 1395 | |
| 1396 if (!pixs) | |
| 1397 return (PIX *)ERROR_PTR("seed pix not defined", __func__, NULL); | |
| 1398 if (pixGetDepth(pixs) != 8) | |
| 1399 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); | |
| 1400 if (hsize < 1 || vsize < 1) | |
| 1401 return (PIX *)ERROR_PTR("hsize or vsize < 1", __func__, NULL); | |
| 1402 if ((hsize & 1) == 0 ) { | |
| 1403 L_WARNING("horiz sel size must be odd; increasing by 1\n", __func__); | |
| 1404 hsize++; | |
| 1405 } | |
| 1406 if ((vsize & 1) == 0 ) { | |
| 1407 L_WARNING("vert sel size must be odd; increasing by 1\n", __func__); | |
| 1408 vsize++; | |
| 1409 } | |
| 1410 | |
| 1411 /* Optionally smooth first to remove noise. | |
| 1412 * If smoothing is 0, just get a copy */ | |
| 1413 pixg = pixBlockconvGray(pixs, NULL, smoothing, smoothing); | |
| 1414 | |
| 1415 /* This gives approximately the gradient of a transition */ | |
| 1416 pixd = pixDilateGray(pixg, hsize, vsize); | |
| 1417 pixSubtractGray(pixd, pixd, pixg); | |
| 1418 pixDestroy(&pixg); | |
| 1419 return pixd; | |
| 1420 } | |
| 1421 | |
| 1422 | |
| 1423 /*-----------------------------------------------------------------* | |
| 1424 * Centroid of component * | |
| 1425 *-----------------------------------------------------------------*/ | |
| 1426 /*! | |
| 1427 * \brief pixaCentroids() | |
| 1428 * | |
| 1429 * \param[in] pixa of components; 1 or 8 bpp | |
| 1430 * \return pta of centroids relative to the UL corner of | |
| 1431 * each pix, or NULL on error | |
| 1432 * | |
| 1433 * <pre> | |
| 1434 * Notes: | |
| 1435 * (1) An error message is returned if any pix has something other | |
| 1436 * than 1 bpp or 8 bpp depth, and the centroid from that pix | |
| 1437 * is saved as (0, 0). | |
| 1438 * </pre> | |
| 1439 */ | |
| 1440 PTA * | |
| 1441 pixaCentroids(PIXA *pixa) | |
| 1442 { | |
| 1443 l_int32 i, n; | |
| 1444 l_int32 *centtab = NULL; | |
| 1445 l_int32 *sumtab = NULL; | |
| 1446 l_float32 x, y; | |
| 1447 PIX *pix; | |
| 1448 PTA *pta; | |
| 1449 | |
| 1450 if (!pixa) | |
| 1451 return (PTA *)ERROR_PTR("pixa not defined", __func__, NULL); | |
| 1452 if ((n = pixaGetCount(pixa)) == 0) | |
| 1453 return (PTA *)ERROR_PTR("no pix in pixa", __func__, NULL); | |
| 1454 | |
| 1455 if ((pta = ptaCreate(n)) == NULL) | |
| 1456 return (PTA *)ERROR_PTR("pta not defined", __func__, NULL); | |
| 1457 centtab = makePixelCentroidTab8(); | |
| 1458 sumtab = makePixelSumTab8(); | |
| 1459 | |
| 1460 for (i = 0; i < n; i++) { | |
| 1461 pix = pixaGetPix(pixa, i, L_CLONE); | |
| 1462 if (pixCentroid(pix, centtab, sumtab, &x, &y) == 1) | |
| 1463 L_ERROR("centroid failure for pix %d\n", __func__, i); | |
| 1464 pixDestroy(&pix); | |
| 1465 ptaAddPt(pta, x, y); | |
| 1466 } | |
| 1467 | |
| 1468 LEPT_FREE(centtab); | |
| 1469 LEPT_FREE(sumtab); | |
| 1470 return pta; | |
| 1471 } | |
| 1472 | |
| 1473 | |
| 1474 /*! | |
| 1475 * \brief pixCentroid() | |
| 1476 * | |
| 1477 * \param[in] pix 1 or 8 bpp | |
| 1478 * \param[in] centtab [optional] table for finding centroids; can be null | |
| 1479 * \param[in] sumtab [optional] table for finding pixel sums; can be null | |
| 1480 * \param[out] pxave x coordinate of centroid, relative to the UL corner | |
| 1481 * of the pix | |
| 1482 * \param[out] pyave y coordinate of centroid, relative to the UL corner | |
| 1483 * of the pix | |
| 1484 * \return 0 if OK, 1 on error | |
| 1485 * | |
| 1486 * <pre> | |
| 1487 * Notes: | |
| 1488 * (1) The sum and centroid tables are only used for 1 bpp. | |
| 1489 * (2) Any table not passed in will be made internally and destroyed | |
| 1490 * after use. | |
| 1491 * </pre> | |
| 1492 */ | |
| 1493 l_ok | |
| 1494 pixCentroid(PIX *pix, | |
| 1495 l_int32 *centtab, | |
| 1496 l_int32 *sumtab, | |
| 1497 l_float32 *pxave, | |
| 1498 l_float32 *pyave) | |
| 1499 { | |
| 1500 l_int32 w, h, d, i, j, wpl, pixsum, rowsum, val; | |
| 1501 l_float32 xsum, ysum; | |
| 1502 l_uint32 *data, *line; | |
| 1503 l_uint32 word; | |
| 1504 l_uint8 byte; | |
| 1505 l_int32 *ctab, *stab; | |
| 1506 | |
| 1507 if (!pxave || !pyave) | |
| 1508 return ERROR_INT("&pxave and &pyave not defined", __func__, 1); | |
| 1509 *pxave = *pyave = 0.0; | |
| 1510 if (!pix) | |
| 1511 return ERROR_INT("pix not defined", __func__, 1); | |
| 1512 pixGetDimensions(pix, &w, &h, &d); | |
| 1513 if (d != 1 && d != 8) | |
| 1514 return ERROR_INT("pix not 1 or 8 bpp", __func__, 1); | |
| 1515 | |
| 1516 ctab = centtab; | |
| 1517 stab = sumtab; | |
| 1518 if (d == 1) { | |
| 1519 pixSetPadBits(pix, 0); | |
| 1520 if (!centtab) | |
| 1521 ctab = makePixelCentroidTab8(); | |
| 1522 if (!sumtab) | |
| 1523 stab = makePixelSumTab8(); | |
| 1524 } | |
| 1525 | |
| 1526 data = pixGetData(pix); | |
| 1527 wpl = pixGetWpl(pix); | |
| 1528 xsum = ysum = 0.0; | |
| 1529 pixsum = 0; | |
| 1530 if (d == 1) { | |
| 1531 for (i = 0; i < h; i++) { | |
| 1532 /* The body of this loop computes the sum of the set | |
| 1533 * (1) bits on this row, weighted by their distance | |
| 1534 * from the left edge of pix, and accumulates that into | |
| 1535 * xsum; it accumulates their distance from the top | |
| 1536 * edge of pix into ysum, and their total count into | |
| 1537 * pixsum. It's equivalent to | |
| 1538 * for (j = 0; j < w; j++) { | |
| 1539 * if (GET_DATA_BIT(line, j)) { | |
| 1540 * xsum += j; | |
| 1541 * ysum += i; | |
| 1542 * pixsum++; | |
| 1543 * } | |
| 1544 * } | |
| 1545 */ | |
| 1546 line = data + wpl * i; | |
| 1547 rowsum = 0; | |
| 1548 for (j = 0; j < wpl; j++) { | |
| 1549 word = line[j]; | |
| 1550 if (word) { | |
| 1551 byte = word & 0xff; | |
| 1552 rowsum += stab[byte]; | |
| 1553 xsum += ctab[byte] + (j * 32 + 24) * stab[byte]; | |
| 1554 byte = (word >> 8) & 0xff; | |
| 1555 rowsum += stab[byte]; | |
| 1556 xsum += ctab[byte] + (j * 32 + 16) * stab[byte]; | |
| 1557 byte = (word >> 16) & 0xff; | |
| 1558 rowsum += stab[byte]; | |
| 1559 xsum += ctab[byte] + (j * 32 + 8) * stab[byte]; | |
| 1560 byte = (word >> 24) & 0xff; | |
| 1561 rowsum += stab[byte]; | |
| 1562 xsum += ctab[byte] + j * 32 * stab[byte]; | |
| 1563 } | |
| 1564 } | |
| 1565 pixsum += rowsum; | |
| 1566 ysum += rowsum * i; | |
| 1567 } | |
| 1568 if (pixsum == 0) { | |
| 1569 L_WARNING("no ON pixels in pix\n", __func__); | |
| 1570 } else { | |
| 1571 *pxave = xsum / (l_float32)pixsum; | |
| 1572 *pyave = ysum / (l_float32)pixsum; | |
| 1573 } | |
| 1574 } else { /* d == 8 */ | |
| 1575 for (i = 0; i < h; i++) { | |
| 1576 line = data + wpl * i; | |
| 1577 for (j = 0; j < w; j++) { | |
| 1578 val = GET_DATA_BYTE(line, j); | |
| 1579 xsum += val * j; | |
| 1580 ysum += val * i; | |
| 1581 pixsum += val; | |
| 1582 } | |
| 1583 } | |
| 1584 if (pixsum == 0) { | |
| 1585 L_WARNING("all pixels are 0\n", __func__); | |
| 1586 } else { | |
| 1587 *pxave = xsum / (l_float32)pixsum; | |
| 1588 *pyave = ysum / (l_float32)pixsum; | |
| 1589 } | |
| 1590 } | |
| 1591 | |
| 1592 if (d == 1) { | |
| 1593 if (!centtab) LEPT_FREE(ctab); | |
| 1594 if (!sumtab) LEPT_FREE(stab); | |
| 1595 } | |
| 1596 return 0; | |
| 1597 } |
