Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/morph.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 * \file morph.c | |
| 29 * <pre> | |
| 30 * | |
| 31 * Generic binary morphological ops implemented with rasterop | |
| 32 * PIX *pixDilate() | |
| 33 * PIX *pixErode() | |
| 34 * PIX *pixHMT() | |
| 35 * PIX *pixOpen() | |
| 36 * PIX *pixClose() | |
| 37 * PIX *pixCloseSafe() | |
| 38 * PIX *pixOpenGeneralized() | |
| 39 * PIX *pixCloseGeneralized() | |
| 40 * | |
| 41 * Binary morphological (raster) ops with brick Sels | |
| 42 * PIX *pixDilateBrick() | |
| 43 * PIX *pixErodeBrick() | |
| 44 * PIX *pixOpenBrick() | |
| 45 * PIX *pixCloseBrick() | |
| 46 * PIX *pixCloseSafeBrick() | |
| 47 * | |
| 48 * Binary composed morphological (raster) ops with brick Sels | |
| 49 * l_int32 selectComposableSels() | |
| 50 * l_int32 selectComposableSizes() | |
| 51 * PIX *pixDilateCompBrick() | |
| 52 * PIX *pixErodeCompBrick() | |
| 53 * PIX *pixOpenCompBrick() | |
| 54 * PIX *pixCloseCompBrick() | |
| 55 * PIX *pixCloseSafeCompBrick() | |
| 56 * | |
| 57 * Functions associated with boundary conditions | |
| 58 * void resetMorphBoundaryCondition() | |
| 59 * l_int32 getMorphBorderPixelColor() | |
| 60 * | |
| 61 * Static helpers for arg processing | |
| 62 * static PIX *processMorphArgs1() | |
| 63 * static PIX *processMorphArgs2() | |
| 64 * | |
| 65 * You are provided with many simple ways to do binary morphology. | |
| 66 * In particular, if you are using brick Sels, there are six | |
| 67 * convenient methods, all specially tailored for separable operations | |
| 68 * on brick Sels. A "brick" Sel is a Sel that is a rectangle | |
| 69 * of solid SEL_HITs with the origin at or near the center. | |
| 70 * Note that a brick Sel can have one dimension of size 1. | |
| 71 * This is very common. All the brick Sel operations are | |
| 72 * separable, meaning the operation is done first in the horizontal | |
| 73 * direction and then in the vertical direction. If one of the | |
| 74 * dimensions is 1, this is a special case where the operation is | |
| 75 * only performed in the other direction. | |
| 76 * | |
| 77 * These six brick Sel methods are enumerated as follows: | |
| 78 * | |
| 79 * (1) Brick Sels: pix*Brick(), where * = {Dilate, Erode, Open, Close}. | |
| 80 * These are separable rasterop implementations. The Sels are | |
| 81 * automatically generated, used, and destroyed at the end. | |
| 82 * You can get the result as a new Pix, in-place back into the src Pix, | |
| 83 * or written to another existing Pix. | |
| 84 * | |
| 85 * (2) Brick Sels: pix*CompBrick(), where * = {Dilate, Erode, Open, Close}. | |
| 86 * These are separable, 2-way composite, rasterop implementations. | |
| 87 * The Sels are automatically generated, used, and destroyed at the end. | |
| 88 * You can get the result as a new Pix, in-place back into the src Pix, | |
| 89 * or written to another existing Pix. For large Sels, these are | |
| 90 * considerably faster than the corresponding pix*Brick() functions. | |
| 91 * N.B.: The size of the Sels that are actually used are typically | |
| 92 * close to, but not exactly equal to, the size input to the function. | |
| 93 * | |
| 94 * (3) Brick Sels: pix*BrickDwa(), where * = {Dilate, Erode, Open, Close}. | |
| 95 * These are separable dwa (destination word accumulation) | |
| 96 * implementations. They use auto-gen'd dwa code. You can get | |
| 97 * the result as a new Pix, in-place back into the src Pix, | |
| 98 * or written to another existing Pix. This is typically | |
| 99 * about 3x faster than the analogous rasterop pix*Brick() | |
| 100 * function, but it has the limitation that the Sel size must | |
| 101 * be less than 63. This is pre-set to work on a number | |
| 102 * of pre-generated Sels. If you want to use other Sels, the | |
| 103 * code can be auto-gen'd for them; see the instructions in morphdwa.c. | |
| 104 * | |
| 105 * (4) Same as (1), but you run it through pixMorphSequence(), with | |
| 106 * the sequence string either compiled in or generated using snprintf. | |
| 107 * All intermediate images and Sels are created, used and destroyed. | |
| 108 * You always get the result as a new Pix. For example, you can | |
| 109 * specify a separable 11 x 17 brick opening as "o11.17", | |
| 110 * or you can specify the horizontal and vertical operations | |
| 111 * explicitly as "o11.1 + o1.11". See morphseq.c for details. | |
| 112 * | |
| 113 * (5) Same as (2), but you run it through pixMorphCompSequence(), with | |
| 114 * the sequence string either compiled in or generated using snprintf. | |
| 115 * All intermediate images and Sels are created, used and destroyed. | |
| 116 * You always get the result as a new Pix. See morphseq.c for details. | |
| 117 * | |
| 118 * (6) Same as (3), but you run it through pixMorphSequenceDwa(), with | |
| 119 * the sequence string either compiled in or generated using snprintf. | |
| 120 * All intermediate images and Sels are created, used and destroyed. | |
| 121 * You always get the result as a new Pix. See morphseq.c for details. | |
| 122 * | |
| 123 * If you are using Sels that are not bricks, you have two choices: | |
| 124 * (a) simplest: use the basic rasterop implementations (pixDilate(), ...) | |
| 125 * (b) fastest: generate the destination word accumumlation (dwa) | |
| 126 * code for your Sels and compile it with the library. | |
| 127 * | |
| 128 * For an example, see flipdetect.c, which gives implementations | |
| 129 * using hit-miss Sels with both the rasterop and dwa versions. | |
| 130 * For the latter, the dwa code resides in fliphmtgen.c, and it | |
| 131 * was generated by prog/flipselgen.c. Both the rasterop and dwa | |
| 132 * implementations are tested by prog/fliptest.c. | |
| 133 * | |
| 134 * A global constant MORPH_BC is used to set the boundary conditions | |
| 135 * for rasterop-based binary morphology. MORPH_BC, in morph.c, | |
| 136 * is set by default to ASYMMETRIC_MORPH_BC for a non-symmetric | |
| 137 * convention for boundary pixels in dilation and erosion: | |
| 138 * All pixels outside the image are assumed to be OFF | |
| 139 * for both dilation and erosion. | |
| 140 * To use a symmetric definition, see comments in pixErode() | |
| 141 * and reset MORPH_BC to SYMMETRIC_MORPH_BC, using | |
| 142 * resetMorphBoundaryCondition(). | |
| 143 * | |
| 144 * Boundary artifacts are possible in closing when the non-symmetric | |
| 145 * boundary conditions are used, because foreground pixels very close | |
| 146 * to the edge can be removed. This can be avoided by using either | |
| 147 * the symmetric boundary conditions or the function pixCloseSafe(), | |
| 148 * which adds a border before the operation and removes it afterwards. | |
| 149 * | |
| 150 * The hit-miss transform (HMT) is the bit-and of 2 erosions: | |
| 151 * (erosion of the src by the hits) & (erosion of the bit-inverted | |
| 152 * src by the misses) | |
| 153 * | |
| 154 * The 'generalized opening' is an HMT followed by a dilation that uses | |
| 155 * only the hits of the hit-miss Sel. | |
| 156 * The 'generalized closing' is a dilation (again, with the hits | |
| 157 * of a hit-miss Sel), followed by the HMT. | |
| 158 * Both of these 'generalized' functions are idempotent. | |
| 159 * | |
| 160 * These functions are extensively tested in prog/binmorph1_reg.c, | |
| 161 * prog/binmorph2_reg.c, and prog/binmorph3_reg.c. | |
| 162 * </pre> | |
| 163 */ | |
| 164 | |
| 165 #ifdef HAVE_CONFIG_H | |
| 166 #include <config_auto.h> | |
| 167 #endif /* HAVE_CONFIG_H */ | |
| 168 | |
| 169 #include <math.h> | |
| 170 #include "allheaders.h" | |
| 171 | |
| 172 /* Global constant; initialized here; must be declared extern | |
| 173 * in other files to access it directly. However, in most | |
| 174 * cases that is not necessary, because it can be reset | |
| 175 * using resetMorphBoundaryCondition(). */ | |
| 176 LEPT_DLL l_int32 MORPH_BC = ASYMMETRIC_MORPH_BC; | |
| 177 | |
| 178 /* We accept this cost in extra rasterops for decomposing exactly. */ | |
| 179 static const l_int32 ACCEPTABLE_COST = 5; | |
| 180 | |
| 181 /* Static helpers for arg processing */ | |
| 182 static PIX * processMorphArgs1(PIX *pixd, PIX *pixs, SEL *sel, PIX **ppixt); | |
| 183 static PIX * processMorphArgs2(PIX *pixd, PIX *pixs, SEL *sel); | |
| 184 | |
| 185 | |
| 186 /*-----------------------------------------------------------------* | |
| 187 * Generic binary morphological ops implemented with rasterop * | |
| 188 *-----------------------------------------------------------------*/ | |
| 189 /*! | |
| 190 * \brief pixDilate() | |
| 191 * | |
| 192 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 193 * or different from pixs | |
| 194 * \param[in] pixs 1 bpp | |
| 195 * \param[in] sel | |
| 196 * \return pixd | |
| 197 * | |
| 198 * <pre> | |
| 199 * Notes: | |
| 200 * (1) This dilates src using hits in Sel. | |
| 201 * (2) There are three cases: | |
| 202 * (a) pixd == null (result into new pixd) | |
| 203 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 204 * (c) pixd != pixs (puts result into existing pixd) | |
| 205 * (3) For clarity, if the case is known, use these patterns: | |
| 206 * (a) pixd = pixDilate(NULL, pixs, ...); | |
| 207 * (b) pixDilate(pixs, pixs, ...); | |
| 208 * (c) pixDilate(pixd, pixs, ...); | |
| 209 * (4) The size of the result is determined by pixs. | |
| 210 * </pre> | |
| 211 */ | |
| 212 PIX * | |
| 213 pixDilate(PIX *pixd, | |
| 214 PIX *pixs, | |
| 215 SEL *sel) | |
| 216 { | |
| 217 l_int32 i, j, w, h, sx, sy, cx, cy, seldata; | |
| 218 PIX *pixt; | |
| 219 | |
| 220 if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL) | |
| 221 return (PIX *)ERROR_PTR("processMorphArgs1 failed", __func__, pixd); | |
| 222 | |
| 223 pixGetDimensions(pixs, &w, &h, NULL); | |
| 224 selGetParameters(sel, &sy, &sx, &cy, &cx); | |
| 225 pixClearAll(pixd); | |
| 226 for (i = 0; i < sy; i++) { | |
| 227 for (j = 0; j < sx; j++) { | |
| 228 seldata = sel->data[i][j]; | |
| 229 if (seldata == 1) { /* src | dst */ | |
| 230 pixRasterop(pixd, j - cx, i - cy, w, h, PIX_SRC | PIX_DST, | |
| 231 pixt, 0, 0); | |
| 232 } | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 pixDestroy(&pixt); | |
| 237 return pixd; | |
| 238 } | |
| 239 | |
| 240 | |
| 241 /*! | |
| 242 * \brief pixErode() | |
| 243 * | |
| 244 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 245 * or different from pixs | |
| 246 * \param[in] pixs 1 bpp | |
| 247 * \param[in] sel | |
| 248 * \return pixd | |
| 249 * | |
| 250 * <pre> | |
| 251 * Notes: | |
| 252 * (1) This erodes src using hits in Sel. | |
| 253 * (2) There are three cases: | |
| 254 * (a) pixd == null (result into new pixd) | |
| 255 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 256 * (c) pixd != pixs (puts result into existing pixd) | |
| 257 * (3) For clarity, if the case is known, use these patterns: | |
| 258 * (a) pixd = pixErode(NULL, pixs, ...); | |
| 259 * (b) pixErode(pixs, pixs, ...); | |
| 260 * (c) pixErode(pixd, pixs, ...); | |
| 261 * (4) The size of the result is determined by pixs. | |
| 262 * </pre> | |
| 263 */ | |
| 264 PIX * | |
| 265 pixErode(PIX *pixd, | |
| 266 PIX *pixs, | |
| 267 SEL *sel) | |
| 268 { | |
| 269 l_int32 i, j, w, h, sx, sy, cx, cy, seldata; | |
| 270 l_int32 xp, yp, xn, yn; | |
| 271 PIX *pixt; | |
| 272 | |
| 273 if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL) | |
| 274 return (PIX *)ERROR_PTR("processMorphArgs1 failed", __func__, pixd); | |
| 275 | |
| 276 pixGetDimensions(pixs, &w, &h, NULL); | |
| 277 selGetParameters(sel, &sy, &sx, &cy, &cx); | |
| 278 pixSetAll(pixd); | |
| 279 for (i = 0; i < sy; i++) { | |
| 280 for (j = 0; j < sx; j++) { | |
| 281 seldata = sel->data[i][j]; | |
| 282 if (seldata == 1) { /* src & dst */ | |
| 283 pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC & PIX_DST, | |
| 284 pixt, 0, 0); | |
| 285 } | |
| 286 } | |
| 287 } | |
| 288 | |
| 289 /* Clear near edges. We do this for the asymmetric boundary | |
| 290 * condition convention that implements erosion assuming all | |
| 291 * pixels surrounding the image are OFF. If you use a | |
| 292 * use a symmetric b.c. convention, where the erosion is | |
| 293 * implemented assuming pixels surrounding the image | |
| 294 * are ON, these operations are omitted. */ | |
| 295 if (MORPH_BC == ASYMMETRIC_MORPH_BC) { | |
| 296 selFindMaxTranslations(sel, &xp, &yp, &xn, &yn); | |
| 297 if (xp > 0) | |
| 298 pixRasterop(pixd, 0, 0, xp, h, PIX_CLR, NULL, 0, 0); | |
| 299 if (xn > 0) | |
| 300 pixRasterop(pixd, w - xn, 0, xn, h, PIX_CLR, NULL, 0, 0); | |
| 301 if (yp > 0) | |
| 302 pixRasterop(pixd, 0, 0, w, yp, PIX_CLR, NULL, 0, 0); | |
| 303 if (yn > 0) | |
| 304 pixRasterop(pixd, 0, h - yn, w, yn, PIX_CLR, NULL, 0, 0); | |
| 305 } | |
| 306 | |
| 307 pixDestroy(&pixt); | |
| 308 return pixd; | |
| 309 } | |
| 310 | |
| 311 | |
| 312 /*! | |
| 313 * \brief pixHMT() | |
| 314 * | |
| 315 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 316 * or different from pixs | |
| 317 * \param[in] pixs 1 bpp | |
| 318 * \param[in] sel | |
| 319 * \return pixd | |
| 320 * | |
| 321 * <pre> | |
| 322 * Notes: | |
| 323 * (1) The hit-miss transform erodes the src, using both hits | |
| 324 * and misses in the Sel. It ANDs the shifted src for hits | |
| 325 * and ANDs the inverted shifted src for misses. | |
| 326 * (2) There are three cases: | |
| 327 * (a) pixd == null (result into new pixd) | |
| 328 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 329 * (c) pixd != pixs (puts result into existing pixd) | |
| 330 * (3) For clarity, if the case is known, use these patterns: | |
| 331 * (a) pixd = pixHMT(NULL, pixs, ...); | |
| 332 * (b) pixHMT(pixs, pixs, ...); | |
| 333 * (c) pixHMT(pixd, pixs, ...); | |
| 334 * (4) The size of the result is determined by pixs. | |
| 335 * </pre> | |
| 336 */ | |
| 337 PIX * | |
| 338 pixHMT(PIX *pixd, | |
| 339 PIX *pixs, | |
| 340 SEL *sel) | |
| 341 { | |
| 342 l_int32 i, j, w, h, sx, sy, cx, cy, firstrasterop, seldata; | |
| 343 l_int32 xp, yp, xn, yn; | |
| 344 PIX *pixt; | |
| 345 | |
| 346 if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL) | |
| 347 return (PIX *)ERROR_PTR("processMorphArgs1 failed", __func__, pixd); | |
| 348 | |
| 349 pixGetDimensions(pixs, &w, &h, NULL); | |
| 350 selGetParameters(sel, &sy, &sx, &cy, &cx); | |
| 351 firstrasterop = TRUE; | |
| 352 for (i = 0; i < sy; i++) { | |
| 353 for (j = 0; j < sx; j++) { | |
| 354 seldata = sel->data[i][j]; | |
| 355 if (seldata == 1) { /* hit */ | |
| 356 if (firstrasterop == TRUE) { /* src only */ | |
| 357 pixClearAll(pixd); | |
| 358 pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC, | |
| 359 pixt, 0, 0); | |
| 360 firstrasterop = FALSE; | |
| 361 } else { /* src & dst */ | |
| 362 pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC & PIX_DST, | |
| 363 pixt, 0, 0); | |
| 364 } | |
| 365 } else if (seldata == 2) { /* miss */ | |
| 366 if (firstrasterop == TRUE) { /* ~src only */ | |
| 367 pixSetAll(pixd); | |
| 368 pixRasterop(pixd, cx - j, cy - i, w, h, PIX_NOT(PIX_SRC), | |
| 369 pixt, 0, 0); | |
| 370 firstrasterop = FALSE; | |
| 371 } else { /* ~src & dst */ | |
| 372 pixRasterop(pixd, cx - j, cy - i, w, h, | |
| 373 PIX_NOT(PIX_SRC) & PIX_DST, | |
| 374 pixt, 0, 0); | |
| 375 } | |
| 376 } | |
| 377 } | |
| 378 } | |
| 379 | |
| 380 /* Clear near edges */ | |
| 381 selFindMaxTranslations(sel, &xp, &yp, &xn, &yn); | |
| 382 if (xp > 0) | |
| 383 pixRasterop(pixd, 0, 0, xp, h, PIX_CLR, NULL, 0, 0); | |
| 384 if (xn > 0) | |
| 385 pixRasterop(pixd, w - xn, 0, xn, h, PIX_CLR, NULL, 0, 0); | |
| 386 if (yp > 0) | |
| 387 pixRasterop(pixd, 0, 0, w, yp, PIX_CLR, NULL, 0, 0); | |
| 388 if (yn > 0) | |
| 389 pixRasterop(pixd, 0, h - yn, w, yn, PIX_CLR, NULL, 0, 0); | |
| 390 | |
| 391 pixDestroy(&pixt); | |
| 392 return pixd; | |
| 393 } | |
| 394 | |
| 395 | |
| 396 /*! | |
| 397 * \brief pixOpen() | |
| 398 * | |
| 399 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 400 * or different from pixs | |
| 401 * \param[in] pixs 1 bpp | |
| 402 * \param[in] sel | |
| 403 * \return pixd | |
| 404 * | |
| 405 * <pre> | |
| 406 * Notes: | |
| 407 * (1) Generic morphological opening, using hits in the Sel. | |
| 408 * (2) There are three cases: | |
| 409 * (a) pixd == null (result into new pixd) | |
| 410 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 411 * (c) pixd != pixs (puts result into existing pixd) | |
| 412 * (3) For clarity, if the case is known, use these patterns: | |
| 413 * (a) pixd = pixOpen(NULL, pixs, ...); | |
| 414 * (b) pixOpen(pixs, pixs, ...); | |
| 415 * (c) pixOpen(pixd, pixs, ...); | |
| 416 * (4) The size of the result is determined by pixs. | |
| 417 * </pre> | |
| 418 */ | |
| 419 PIX * | |
| 420 pixOpen(PIX *pixd, | |
| 421 PIX *pixs, | |
| 422 SEL *sel) | |
| 423 { | |
| 424 PIX *pixt; | |
| 425 | |
| 426 if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL) | |
| 427 return (PIX *)ERROR_PTR("pixd not returned", __func__, pixd); | |
| 428 | |
| 429 if ((pixt = pixErode(NULL, pixs, sel)) == NULL) | |
| 430 return (PIX *)ERROR_PTR("pixt not made", __func__, pixd); | |
| 431 pixDilate(pixd, pixt, sel); | |
| 432 pixDestroy(&pixt); | |
| 433 | |
| 434 return pixd; | |
| 435 } | |
| 436 | |
| 437 | |
| 438 /*! | |
| 439 * \brief pixClose() | |
| 440 * | |
| 441 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 442 * or different from pixs | |
| 443 * \param[in] pixs 1 bpp | |
| 444 * \param[in] sel | |
| 445 * \return pixd | |
| 446 * | |
| 447 * <pre> | |
| 448 * Notes: | |
| 449 * (1) Generic morphological closing, using hits in the Sel. | |
| 450 * (2) This implementation is a strict dual of the opening if | |
| 451 * symmetric boundary conditions are used (see notes at top | |
| 452 * of this file). | |
| 453 * (3) There are three cases: | |
| 454 * (a) pixd == null (result into new pixd) | |
| 455 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 456 * (c) pixd != pixs (puts result into existing pixd) | |
| 457 * (4) For clarity, if the case is known, use these patterns: | |
| 458 * (a) pixd = pixClose(NULL, pixs, ...); | |
| 459 * (b) pixClose(pixs, pixs, ...); | |
| 460 * (c) pixClose(pixd, pixs, ...); | |
| 461 * (5) The size of the result is determined by pixs. | |
| 462 * </pre> | |
| 463 */ | |
| 464 PIX * | |
| 465 pixClose(PIX *pixd, | |
| 466 PIX *pixs, | |
| 467 SEL *sel) | |
| 468 { | |
| 469 PIX *pixt; | |
| 470 | |
| 471 if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL) | |
| 472 return (PIX *)ERROR_PTR("pixd not returned", __func__, pixd); | |
| 473 | |
| 474 if ((pixt = pixDilate(NULL, pixs, sel)) == NULL) | |
| 475 return (PIX *)ERROR_PTR("pixt not made", __func__, pixd); | |
| 476 pixErode(pixd, pixt, sel); | |
| 477 pixDestroy(&pixt); | |
| 478 | |
| 479 return pixd; | |
| 480 } | |
| 481 | |
| 482 | |
| 483 /*! | |
| 484 * \brief pixCloseSafe() | |
| 485 * | |
| 486 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 487 * or different from pixs | |
| 488 * \param[in] pixs 1 bpp | |
| 489 * \param[in] sel | |
| 490 * \return pixd | |
| 491 * | |
| 492 * <pre> | |
| 493 * Notes: | |
| 494 * (1) Generic morphological closing, using hits in the Sel. | |
| 495 * (2) If non-symmetric boundary conditions are used, this | |
| 496 * function adds a border of OFF pixels that is of | |
| 497 * sufficient size to avoid losing pixels from the dilation, | |
| 498 * and it removes the border after the operation is finished. | |
| 499 * It thus enforces a correct extensive result for closing. | |
| 500 * (3) If symmetric b.c. are used, it is not necessary to add | |
| 501 * and remove this border. | |
| 502 * (4) There are three cases: | |
| 503 * (a) pixd == null (result into new pixd) | |
| 504 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 505 * (c) pixd != pixs (puts result into existing pixd) | |
| 506 * (5) For clarity, if the case is known, use these patterns: | |
| 507 * (a) pixd = pixCloseSafe(NULL, pixs, ...); | |
| 508 * (b) pixCloseSafe(pixs, pixs, ...); | |
| 509 * (c) pixCloseSafe(pixd, pixs, ...); | |
| 510 * (6) The size of the result is determined by pixs. | |
| 511 * </pre> | |
| 512 */ | |
| 513 PIX * | |
| 514 pixCloseSafe(PIX *pixd, | |
| 515 PIX *pixs, | |
| 516 SEL *sel) | |
| 517 { | |
| 518 l_int32 xp, yp, xn, yn, xmax, xbord; | |
| 519 PIX *pixt1, *pixt2; | |
| 520 | |
| 521 if (!pixs) | |
| 522 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 523 if (!sel) | |
| 524 return (PIX *)ERROR_PTR("sel not defined", __func__, pixd); | |
| 525 if (pixGetDepth(pixs) != 1) | |
| 526 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 527 | |
| 528 /* Symmetric b.c. handles correctly without added pixels */ | |
| 529 if (MORPH_BC == SYMMETRIC_MORPH_BC) | |
| 530 return pixClose(pixd, pixs, sel); | |
| 531 | |
| 532 selFindMaxTranslations(sel, &xp, &yp, &xn, &yn); | |
| 533 xmax = L_MAX(xp, xn); | |
| 534 xbord = 32 * ((xmax + 31) / 32); /* full 32 bit words */ | |
| 535 | |
| 536 if ((pixt1 = pixAddBorderGeneral(pixs, xbord, xbord, yp, yn, 0)) == NULL) | |
| 537 return (PIX *)ERROR_PTR("pixt1 not made", __func__, pixd); | |
| 538 pixClose(pixt1, pixt1, sel); | |
| 539 if ((pixt2 = pixRemoveBorderGeneral(pixt1, xbord, xbord, yp, yn)) == NULL) | |
| 540 return (PIX *)ERROR_PTR("pixt2 not made", __func__, pixd); | |
| 541 pixDestroy(&pixt1); | |
| 542 | |
| 543 if (!pixd) | |
| 544 return pixt2; | |
| 545 | |
| 546 pixCopy(pixd, pixt2); | |
| 547 pixDestroy(&pixt2); | |
| 548 return pixd; | |
| 549 } | |
| 550 | |
| 551 | |
| 552 /*! | |
| 553 * \brief pixOpenGeneralized() | |
| 554 * | |
| 555 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 556 * or different from pixs | |
| 557 * \param[in] pixs 1 bpp | |
| 558 * \param[in] sel | |
| 559 * \return pixd | |
| 560 * | |
| 561 * <pre> | |
| 562 * Notes: | |
| 563 * (1) Generalized morphological opening, using both hits and | |
| 564 * misses in the Sel. | |
| 565 * (2) This does a hit-miss transform, followed by a dilation | |
| 566 * using the hits. | |
| 567 * (3) There are three cases: | |
| 568 * (a) pixd == null (result into new pixd) | |
| 569 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 570 * (c) pixd != pixs (puts result into existing pixd) | |
| 571 * (4) For clarity, if the case is known, use these patterns: | |
| 572 * (a) pixd = pixOpenGeneralized(NULL, pixs, ...); | |
| 573 * (b) pixOpenGeneralized(pixs, pixs, ...); | |
| 574 * (c) pixOpenGeneralized(pixd, pixs, ...); | |
| 575 * (5) The size of the result is determined by pixs. | |
| 576 * </pre> | |
| 577 */ | |
| 578 PIX * | |
| 579 pixOpenGeneralized(PIX *pixd, | |
| 580 PIX *pixs, | |
| 581 SEL *sel) | |
| 582 { | |
| 583 PIX *pixt; | |
| 584 | |
| 585 if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL) | |
| 586 return (PIX *)ERROR_PTR("pixd not returned", __func__, pixd); | |
| 587 | |
| 588 if ((pixt = pixHMT(NULL, pixs, sel)) == NULL) | |
| 589 return (PIX *)ERROR_PTR("pixt not made", __func__, pixd); | |
| 590 pixDilate(pixd, pixt, sel); | |
| 591 pixDestroy(&pixt); | |
| 592 return pixd; | |
| 593 } | |
| 594 | |
| 595 | |
| 596 /*! | |
| 597 * \brief pixCloseGeneralized() | |
| 598 * | |
| 599 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 600 * or different from pixs | |
| 601 * \param[in] pixs 1 bpp | |
| 602 * \param[in] sel | |
| 603 * \return pixd | |
| 604 * | |
| 605 * <pre> | |
| 606 * Notes: | |
| 607 * (1) Generalized morphological closing, using both hits and | |
| 608 * misses in the Sel. | |
| 609 * (2) This does a dilation using the hits, followed by a | |
| 610 * hit-miss transform. | |
| 611 * (3) This operation is a dual of the generalized opening. | |
| 612 * (4) There are three cases: | |
| 613 * (a) pixd == null (result into new pixd) | |
| 614 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 615 * (c) pixd != pixs (puts result into existing pixd) | |
| 616 * (5) For clarity, if the case is known, use these patterns: | |
| 617 * (a) pixd = pixCloseGeneralized(NULL, pixs, ...); | |
| 618 * (b) pixCloseGeneralized(pixs, pixs, ...); | |
| 619 * (c) pixCloseGeneralized(pixd, pixs, ...); | |
| 620 * (6) The size of the result is determined by pixs. | |
| 621 * </pre> | |
| 622 */ | |
| 623 PIX * | |
| 624 pixCloseGeneralized(PIX *pixd, | |
| 625 PIX *pixs, | |
| 626 SEL *sel) | |
| 627 { | |
| 628 PIX *pixt; | |
| 629 | |
| 630 if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL) | |
| 631 return (PIX *)ERROR_PTR("pixd not returned", __func__, pixd); | |
| 632 | |
| 633 if ((pixt = pixDilate(NULL, pixs, sel)) == NULL) | |
| 634 return (PIX *)ERROR_PTR("pixt not made", __func__, pixd); | |
| 635 pixHMT(pixd, pixt, sel); | |
| 636 pixDestroy(&pixt); | |
| 637 | |
| 638 return pixd; | |
| 639 } | |
| 640 | |
| 641 | |
| 642 /*-----------------------------------------------------------------* | |
| 643 * Binary morphological (raster) ops with brick Sels * | |
| 644 *-----------------------------------------------------------------*/ | |
| 645 /*! | |
| 646 * \brief pixDilateBrick() | |
| 647 * | |
| 648 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 649 * or different from pixs | |
| 650 * \param[in] pixs 1 bpp | |
| 651 * \param[in] hsize width of brick Sel | |
| 652 * \param[in] vsize height of brick Sel | |
| 653 * \return pixd | |
| 654 * | |
| 655 * <pre> | |
| 656 * Notes: | |
| 657 * (1) Sel is a brick with all elements being hits | |
| 658 * (2) The origin is at (x, y) = (hsize/2, vsize/2) | |
| 659 * (3) Do separably if both hsize and vsize are > 1. | |
| 660 * (4) There are three cases: | |
| 661 * (a) pixd == null (result into new pixd) | |
| 662 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 663 * (c) pixd != pixs (puts result into existing pixd) | |
| 664 * (5) For clarity, if the case is known, use these patterns: | |
| 665 * (a) pixd = pixDilateBrick(NULL, pixs, ...); | |
| 666 * (b) pixDilateBrick(pixs, pixs, ...); | |
| 667 * (c) pixDilateBrick(pixd, pixs, ...); | |
| 668 * (6) The size of the result is determined by pixs. | |
| 669 * </pre> | |
| 670 */ | |
| 671 PIX * | |
| 672 pixDilateBrick(PIX *pixd, | |
| 673 PIX *pixs, | |
| 674 l_int32 hsize, | |
| 675 l_int32 vsize) | |
| 676 { | |
| 677 PIX *pixt; | |
| 678 SEL *sel, *selh, *selv; | |
| 679 | |
| 680 if (!pixs) | |
| 681 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 682 if (pixGetDepth(pixs) != 1) | |
| 683 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 684 if (hsize < 1 || vsize < 1) | |
| 685 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 686 | |
| 687 if (hsize == 1 && vsize == 1) | |
| 688 return pixCopy(pixd, pixs); | |
| 689 if (hsize == 1 || vsize == 1) { /* no intermediate result */ | |
| 690 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); | |
| 691 if (!sel) | |
| 692 return (PIX *)ERROR_PTR("sel not made", __func__, pixd); | |
| 693 pixd = pixDilate(pixd, pixs, sel); | |
| 694 selDestroy(&sel); | |
| 695 } else { | |
| 696 if ((selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT)) == NULL) | |
| 697 return (PIX *)ERROR_PTR("selh not made", __func__, pixd); | |
| 698 if ((selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT)) == NULL) { | |
| 699 selDestroy(&selh); | |
| 700 return (PIX *)ERROR_PTR("selv not made", __func__, pixd); | |
| 701 } | |
| 702 pixt = pixDilate(NULL, pixs, selh); | |
| 703 pixd = pixDilate(pixd, pixt, selv); | |
| 704 pixDestroy(&pixt); | |
| 705 selDestroy(&selh); | |
| 706 selDestroy(&selv); | |
| 707 } | |
| 708 | |
| 709 return pixd; | |
| 710 } | |
| 711 | |
| 712 | |
| 713 /*! | |
| 714 * \brief pixErodeBrick() | |
| 715 * | |
| 716 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 717 * or different from pixs | |
| 718 * \param[in] pixs 1 bpp | |
| 719 * \param[in] hsize width of brick Sel | |
| 720 * \param[in] vsize height of brick Sel | |
| 721 * \return pixd | |
| 722 * | |
| 723 * <pre> | |
| 724 * Notes: | |
| 725 * (1) Sel is a brick with all elements being hits | |
| 726 * (2) The origin is at (x, y) = (hsize/2, vsize/2) | |
| 727 * (3) Do separably if both hsize and vsize are > 1. | |
| 728 * (4) There are three cases: | |
| 729 * (a) pixd == null (result into new pixd) | |
| 730 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 731 * (c) pixd != pixs (puts result into existing pixd) | |
| 732 * (5) For clarity, if the case is known, use these patterns: | |
| 733 * (a) pixd = pixErodeBrick(NULL, pixs, ...); | |
| 734 * (b) pixErodeBrick(pixs, pixs, ...); | |
| 735 * (c) pixErodeBrick(pixd, pixs, ...); | |
| 736 * (6) The size of the result is determined by pixs. | |
| 737 * </pre> | |
| 738 */ | |
| 739 PIX * | |
| 740 pixErodeBrick(PIX *pixd, | |
| 741 PIX *pixs, | |
| 742 l_int32 hsize, | |
| 743 l_int32 vsize) | |
| 744 { | |
| 745 PIX *pixt; | |
| 746 SEL *sel, *selh, *selv; | |
| 747 | |
| 748 if (!pixs) | |
| 749 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 750 if (pixGetDepth(pixs) != 1) | |
| 751 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 752 if (hsize < 1 || vsize < 1) | |
| 753 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 754 | |
| 755 if (hsize == 1 && vsize == 1) | |
| 756 return pixCopy(pixd, pixs); | |
| 757 if (hsize == 1 || vsize == 1) { /* no intermediate result */ | |
| 758 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); | |
| 759 if (!sel) | |
| 760 return (PIX *)ERROR_PTR("sel not made", __func__, pixd); | |
| 761 pixd = pixErode(pixd, pixs, sel); | |
| 762 selDestroy(&sel); | |
| 763 } else { | |
| 764 if ((selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT)) == NULL) | |
| 765 return (PIX *)ERROR_PTR("selh not made", __func__, pixd); | |
| 766 if ((selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT)) == NULL) { | |
| 767 selDestroy(&selh); | |
| 768 return (PIX *)ERROR_PTR("selv not made", __func__, pixd); | |
| 769 } | |
| 770 pixt = pixErode(NULL, pixs, selh); | |
| 771 pixd = pixErode(pixd, pixt, selv); | |
| 772 pixDestroy(&pixt); | |
| 773 selDestroy(&selh); | |
| 774 selDestroy(&selv); | |
| 775 } | |
| 776 | |
| 777 return pixd; | |
| 778 } | |
| 779 | |
| 780 | |
| 781 /*! | |
| 782 * \brief pixOpenBrick() | |
| 783 * | |
| 784 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 785 * or different from pixs | |
| 786 * \param[in] pixs 1 bpp | |
| 787 * \param[in] hsize width of brick Sel | |
| 788 * \param[in] vsize height of brick Sel | |
| 789 * \return pixd, or NULL on error | |
| 790 * | |
| 791 * <pre> | |
| 792 * Notes: | |
| 793 * (1) Sel is a brick with all elements being hits | |
| 794 * (2) The origin is at (x, y) = (hsize/2, vsize/2) | |
| 795 * (3) Do separably if both hsize and vsize are > 1. | |
| 796 * (4) There are three cases: | |
| 797 * (a) pixd == null (result into new pixd) | |
| 798 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 799 * (c) pixd != pixs (puts result into existing pixd) | |
| 800 * (5) For clarity, if the case is known, use these patterns: | |
| 801 * (a) pixd = pixOpenBrick(NULL, pixs, ...); | |
| 802 * (b) pixOpenBrick(pixs, pixs, ...); | |
| 803 * (c) pixOpenBrick(pixd, pixs, ...); | |
| 804 * (6) The size of the result is determined by pixs. | |
| 805 * </pre> | |
| 806 */ | |
| 807 PIX * | |
| 808 pixOpenBrick(PIX *pixd, | |
| 809 PIX *pixs, | |
| 810 l_int32 hsize, | |
| 811 l_int32 vsize) | |
| 812 { | |
| 813 PIX *pixt; | |
| 814 SEL *sel, *selh, *selv; | |
| 815 | |
| 816 if (!pixs) | |
| 817 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 818 if (pixGetDepth(pixs) != 1) | |
| 819 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 820 if (hsize < 1 || vsize < 1) | |
| 821 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 822 | |
| 823 if (hsize == 1 && vsize == 1) | |
| 824 return pixCopy(pixd, pixs); | |
| 825 if (hsize == 1 || vsize == 1) { /* no intermediate result */ | |
| 826 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); | |
| 827 if (!sel) | |
| 828 return (PIX *)ERROR_PTR("sel not made", __func__, pixd); | |
| 829 pixd = pixOpen(pixd, pixs, sel); | |
| 830 selDestroy(&sel); | |
| 831 } else { /* do separably */ | |
| 832 if ((selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT)) == NULL) | |
| 833 return (PIX *)ERROR_PTR("selh not made", __func__, pixd); | |
| 834 if ((selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT)) == NULL) { | |
| 835 selDestroy(&selh); | |
| 836 return (PIX *)ERROR_PTR("selv not made", __func__, pixd); | |
| 837 } | |
| 838 pixt = pixErode(NULL, pixs, selh); | |
| 839 pixd = pixErode(pixd, pixt, selv); | |
| 840 pixDilate(pixt, pixd, selh); | |
| 841 pixDilate(pixd, pixt, selv); | |
| 842 pixDestroy(&pixt); | |
| 843 selDestroy(&selh); | |
| 844 selDestroy(&selv); | |
| 845 } | |
| 846 | |
| 847 return pixd; | |
| 848 } | |
| 849 | |
| 850 | |
| 851 /*! | |
| 852 * \brief pixCloseBrick() | |
| 853 * | |
| 854 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 855 * or different from pixs | |
| 856 * \param[in] pixs 1 bpp | |
| 857 * \param[in] hsize width of brick Sel | |
| 858 * \param[in] vsize height of brick Sel | |
| 859 * \return pixd, or NULL on error | |
| 860 * | |
| 861 * <pre> | |
| 862 * Notes: | |
| 863 * (1) Sel is a brick with all elements being hits | |
| 864 * (2) The origin is at (x, y) = (hsize/2, vsize/2) | |
| 865 * (3) Do separably if both hsize and vsize are > 1. | |
| 866 * (4) There are three cases: | |
| 867 * (a) pixd == null (result into new pixd) | |
| 868 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 869 * (c) pixd != pixs (puts result into existing pixd) | |
| 870 * (5) For clarity, if the case is known, use these patterns: | |
| 871 * (a) pixd = pixCloseBrick(NULL, pixs, ...); | |
| 872 * (b) pixCloseBrick(pixs, pixs, ...); | |
| 873 * (c) pixCloseBrick(pixd, pixs, ...); | |
| 874 * (6) The size of the result is determined by pixs. | |
| 875 * </pre> | |
| 876 */ | |
| 877 PIX * | |
| 878 pixCloseBrick(PIX *pixd, | |
| 879 PIX *pixs, | |
| 880 l_int32 hsize, | |
| 881 l_int32 vsize) | |
| 882 { | |
| 883 PIX *pixt; | |
| 884 SEL *sel, *selh, *selv; | |
| 885 | |
| 886 if (!pixs) | |
| 887 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 888 if (pixGetDepth(pixs) != 1) | |
| 889 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 890 if (hsize < 1 || vsize < 1) | |
| 891 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 892 | |
| 893 if (hsize == 1 && vsize == 1) | |
| 894 return pixCopy(pixd, pixs); | |
| 895 if (hsize == 1 || vsize == 1) { /* no intermediate result */ | |
| 896 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); | |
| 897 if (!sel) | |
| 898 return (PIX *)ERROR_PTR("sel not made", __func__, pixd); | |
| 899 pixd = pixClose(pixd, pixs, sel); | |
| 900 selDestroy(&sel); | |
| 901 } else { /* do separably */ | |
| 902 if ((selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT)) == NULL) | |
| 903 return (PIX *)ERROR_PTR("selh not made", __func__, pixd); | |
| 904 if ((selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT)) == NULL) { | |
| 905 selDestroy(&selh); | |
| 906 return (PIX *)ERROR_PTR("selv not made", __func__, pixd); | |
| 907 } | |
| 908 pixt = pixDilate(NULL, pixs, selh); | |
| 909 pixd = pixDilate(pixd, pixt, selv); | |
| 910 pixErode(pixt, pixd, selh); | |
| 911 pixErode(pixd, pixt, selv); | |
| 912 pixDestroy(&pixt); | |
| 913 selDestroy(&selh); | |
| 914 selDestroy(&selv); | |
| 915 } | |
| 916 | |
| 917 return pixd; | |
| 918 } | |
| 919 | |
| 920 | |
| 921 /*! | |
| 922 * \brief pixCloseSafeBrick() | |
| 923 * | |
| 924 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 925 * or different from pixs | |
| 926 * \param[in] pixs 1 bpp | |
| 927 * \param[in] hsize width of brick Sel | |
| 928 * \param[in] vsize height of brick Sel | |
| 929 * \return pixd, or NULL on error | |
| 930 * | |
| 931 * <pre> | |
| 932 * Notes: | |
| 933 * (1) Sel is a brick with all elements being hits | |
| 934 * (2) The origin is at (x, y) = (hsize/2, vsize/2) | |
| 935 * (3) Do separably if both hsize and vsize are > 1. | |
| 936 * (4) Safe closing adds a border of 0 pixels, of sufficient size so | |
| 937 * that all pixels in input image are processed within | |
| 938 * 32-bit words in the expanded image. As a result, there is | |
| 939 * no special processing for pixels near the boundary, and there | |
| 940 * are no boundary effects. The border is removed at the end. | |
| 941 * (5) There are three cases: | |
| 942 * (a) pixd == null (result into new pixd) | |
| 943 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 944 * (c) pixd != pixs (puts result into existing pixd) | |
| 945 * (6) For clarity, if the case is known, use these patterns: | |
| 946 * (a) pixd = pixCloseBrick(NULL, pixs, ...); | |
| 947 * (b) pixCloseBrick(pixs, pixs, ...); | |
| 948 * (c) pixCloseBrick(pixd, pixs, ...); | |
| 949 * (7) The size of the result is determined by pixs. | |
| 950 * </pre> | |
| 951 */ | |
| 952 PIX * | |
| 953 pixCloseSafeBrick(PIX *pixd, | |
| 954 PIX *pixs, | |
| 955 l_int32 hsize, | |
| 956 l_int32 vsize) | |
| 957 { | |
| 958 l_int32 maxtrans, bordsize; | |
| 959 PIX *pixsb, *pixt, *pixdb; | |
| 960 SEL *sel, *selh, *selv; | |
| 961 | |
| 962 if (!pixs) | |
| 963 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 964 if (pixGetDepth(pixs) != 1) | |
| 965 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 966 if (hsize < 1 || vsize < 1) | |
| 967 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 968 | |
| 969 if (hsize == 1 && vsize == 1) | |
| 970 return pixCopy(pixd, pixs); | |
| 971 | |
| 972 /* Symmetric b.c. handles correctly without added pixels */ | |
| 973 if (MORPH_BC == SYMMETRIC_MORPH_BC) | |
| 974 return pixCloseBrick(pixd, pixs, hsize, vsize); | |
| 975 | |
| 976 maxtrans = L_MAX(hsize / 2, vsize / 2); | |
| 977 bordsize = 32 * ((maxtrans + 31) / 32); /* full 32 bit words */ | |
| 978 pixsb = pixAddBorder(pixs, bordsize, 0); | |
| 979 | |
| 980 if (hsize == 1 || vsize == 1) { /* no intermediate result */ | |
| 981 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); | |
| 982 if (!sel) { | |
| 983 pixDestroy(&pixsb); | |
| 984 return (PIX *)ERROR_PTR("sel not made", __func__, pixd); | |
| 985 } | |
| 986 pixdb = pixClose(NULL, pixsb, sel); | |
| 987 selDestroy(&sel); | |
| 988 } else { /* do separably */ | |
| 989 selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT); | |
| 990 selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT); | |
| 991 if (!selh || !selv) { | |
| 992 selDestroy(&selh); | |
| 993 selDestroy(&selv); | |
| 994 pixDestroy(&pixsb); | |
| 995 return (PIX *)ERROR_PTR("selh and selv not both made", | |
| 996 __func__, pixd); | |
| 997 } | |
| 998 pixt = pixDilate(NULL, pixsb, selh); | |
| 999 pixdb = pixDilate(NULL, pixt, selv); | |
| 1000 pixErode(pixt, pixdb, selh); | |
| 1001 pixErode(pixdb, pixt, selv); | |
| 1002 pixDestroy(&pixt); | |
| 1003 selDestroy(&selh); | |
| 1004 selDestroy(&selv); | |
| 1005 } | |
| 1006 | |
| 1007 pixt = pixRemoveBorder(pixdb, bordsize); | |
| 1008 pixDestroy(&pixsb); | |
| 1009 pixDestroy(&pixdb); | |
| 1010 | |
| 1011 if (!pixd) { | |
| 1012 pixd = pixt; | |
| 1013 } else { | |
| 1014 pixCopy(pixd, pixt); | |
| 1015 pixDestroy(&pixt); | |
| 1016 } | |
| 1017 return pixd; | |
| 1018 } | |
| 1019 | |
| 1020 | |
| 1021 /*-----------------------------------------------------------------* | |
| 1022 * Binary composed morphological (raster) ops with brick Sels * | |
| 1023 *-----------------------------------------------------------------*/ | |
| 1024 /* \brief selectComposableSels() | |
| 1025 * | |
| 1026 * \param[in] size of composed sel | |
| 1027 * \param[in] direction L_HORIZ, L_VERT | |
| 1028 * \param[out] psel1 [optional] contiguous sel; can be null | |
| 1029 * \param[out] psel2 [optional] comb sel; can be null | |
| 1030 * \return 0 if OK, 1 on error | |
| 1031 * | |
| 1032 * <pre> | |
| 1033 * Notes: | |
| 1034 * (1) When using composable Sels, where the original Sel is | |
| 1035 * decomposed into two, the best you can do in terms | |
| 1036 * of reducing the computation is by a factor: | |
| 1037 * | |
| 1038 * 2 * sqrt(size) / size | |
| 1039 * | |
| 1040 * In practice, you get quite close to this. E.g., | |
| 1041 * | |
| 1042 * Sel size | Optimum reduction factor | |
| 1043 * -------- ------------------------ | |
| 1044 * 36 | 1/3 | |
| 1045 * 64 | 1/4 | |
| 1046 * 144 | 1/6 | |
| 1047 * 256 | 1/8 | |
| 1048 * </pre> | |
| 1049 */ | |
| 1050 l_int32 | |
| 1051 selectComposableSels(l_int32 size, | |
| 1052 l_int32 direction, | |
| 1053 SEL **psel1, | |
| 1054 SEL **psel2) | |
| 1055 { | |
| 1056 l_int32 factor1, factor2; | |
| 1057 | |
| 1058 if (!psel1 && !psel2) | |
| 1059 return ERROR_INT("neither &sel1 nor &sel2 are defined", __func__, 1); | |
| 1060 if (psel1) *psel1 = NULL; | |
| 1061 if (psel2) *psel2 = NULL; | |
| 1062 if (size < 1 || size > 10000) | |
| 1063 return ERROR_INT("size < 1 or size > 10000", __func__, 1); | |
| 1064 if (direction != L_HORIZ && direction != L_VERT) | |
| 1065 return ERROR_INT("invalid direction", __func__, 1); | |
| 1066 | |
| 1067 if (selectComposableSizes(size, &factor1, &factor2)) | |
| 1068 return ERROR_INT("factors not found", __func__, 1); | |
| 1069 | |
| 1070 if (psel1) { | |
| 1071 if (direction == L_HORIZ) | |
| 1072 *psel1 = selCreateBrick(1, factor1, 0, factor1 / 2, SEL_HIT); | |
| 1073 else | |
| 1074 *psel1 = selCreateBrick(factor1, 1, factor1 / 2 , 0, SEL_HIT); | |
| 1075 } | |
| 1076 if (psel2) | |
| 1077 *psel2 = selCreateComb(factor1, factor2, direction); | |
| 1078 return 0; | |
| 1079 } | |
| 1080 | |
| 1081 | |
| 1082 /*! | |
| 1083 * \brief selectComposableSizes() | |
| 1084 * | |
| 1085 * \param[in] size of sel to be decomposed | |
| 1086 * \param[out] pfactor1 larger factor | |
| 1087 * \param[out] pfactor2 smaller factor | |
| 1088 * \return 0 if OK, 1 on error | |
| 1089 * | |
| 1090 * <pre> | |
| 1091 * Notes: | |
| 1092 * (1) This works for Sel sizes up to 10000, which seems sufficient. | |
| 1093 * (2) The composable sel size is typically within +- 1 of | |
| 1094 * the requested size. Up to size = 300, the maximum difference | |
| 1095 * is +- 2. | |
| 1096 * (3) We choose an overall cost function where the penalty for | |
| 1097 * the size difference between input and actual is 4 times | |
| 1098 * the penalty for additional rasterops. | |
| 1099 * (4) Returned values: factor1 >= factor2 | |
| 1100 * If size > 1, then factor1 > 1. | |
| 1101 * </pre> | |
| 1102 */ | |
| 1103 l_ok | |
| 1104 selectComposableSizes(l_int32 size, | |
| 1105 l_int32 *pfactor1, | |
| 1106 l_int32 *pfactor2) | |
| 1107 { | |
| 1108 l_int32 i, midval, val1, val2m, val2p; | |
| 1109 l_int32 index, prodm, prodp; | |
| 1110 l_int32 mincost, totcost, rastcostm, rastcostp, diffm, diffp; | |
| 1111 l_int32 lowval[256]; | |
| 1112 l_int32 hival[256]; | |
| 1113 l_int32 rastcost[256]; /* excess in sum of sizes (extra rasterops) */ | |
| 1114 l_int32 diff[256]; /* diff between product (sel size) and input size */ | |
| 1115 | |
| 1116 if (size < 1 || size > 10000) | |
| 1117 return ERROR_INT("size < 1 or size > 10000", __func__, 1); | |
| 1118 if (!pfactor1 || !pfactor2) | |
| 1119 return ERROR_INT("&factor1 or &factor2 not defined", __func__, 1); | |
| 1120 | |
| 1121 midval = (l_int32)(sqrt((l_float64)size) + 0.001); | |
| 1122 if (midval * midval == size) { | |
| 1123 *pfactor1 = *pfactor2 = midval; | |
| 1124 return 0; | |
| 1125 } | |
| 1126 | |
| 1127 /* Set up arrays. For each val1, optimize for lowest diff, | |
| 1128 * and save the rastcost, the diff, and the two factors. */ | |
| 1129 for (val1 = midval + 1, i = 0; val1 > 0; val1--, i++) { | |
| 1130 val2m = size / val1; | |
| 1131 val2p = val2m + 1; | |
| 1132 prodm = val1 * val2m; | |
| 1133 prodp = val1 * val2p; | |
| 1134 rastcostm = val1 + val2m - 2 * midval; | |
| 1135 rastcostp = val1 + val2p - 2 * midval; | |
| 1136 diffm = L_ABS(size - prodm); | |
| 1137 diffp = L_ABS(size - prodp); | |
| 1138 if (diffm <= diffp) { | |
| 1139 lowval[i] = L_MIN(val1, val2m); | |
| 1140 hival[i] = L_MAX(val1, val2m); | |
| 1141 rastcost[i] = rastcostm; | |
| 1142 diff[i] = diffm; | |
| 1143 } else { | |
| 1144 lowval[i] = L_MIN(val1, val2p); | |
| 1145 hival[i] = L_MAX(val1, val2p); | |
| 1146 rastcost[i] = rastcostp; | |
| 1147 diff[i] = diffp; | |
| 1148 } | |
| 1149 } | |
| 1150 | |
| 1151 /* Choose the optimum factors; use cost ratio 4 on diff */ | |
| 1152 mincost = 10000; | |
| 1153 index = 1; /* unimportant initial value */ | |
| 1154 for (i = 0; i < midval + 1; i++) { | |
| 1155 if (diff[i] == 0 && rastcost[i] < ACCEPTABLE_COST) { | |
| 1156 *pfactor1 = hival[i]; | |
| 1157 *pfactor2 = lowval[i]; | |
| 1158 return 0; | |
| 1159 } | |
| 1160 totcost = 4 * diff[i] + rastcost[i]; | |
| 1161 if (totcost < mincost) { | |
| 1162 mincost = totcost; | |
| 1163 index = i; | |
| 1164 } | |
| 1165 } | |
| 1166 *pfactor1 = hival[index]; | |
| 1167 *pfactor2 = lowval[index]; | |
| 1168 | |
| 1169 return 0; | |
| 1170 } | |
| 1171 | |
| 1172 | |
| 1173 /*! | |
| 1174 * \brief pixDilateCompBrick() | |
| 1175 * | |
| 1176 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 1177 * or different from pixs | |
| 1178 * \param[in] pixs 1 bpp | |
| 1179 * \param[in] hsize width of brick Sel | |
| 1180 * \param[in] vsize height of brick Sel | |
| 1181 * \return pixd, or NULL on error | |
| 1182 * | |
| 1183 * <pre> | |
| 1184 * Notes: | |
| 1185 * (1) Sel is a brick with all elements being hits | |
| 1186 * (2) The origin is at (x, y) = (hsize/2, vsize/2) | |
| 1187 * (3) Do compositely for each dimension > 1. | |
| 1188 * (4) Do separably if both hsize and vsize are > 1. | |
| 1189 * (5) There are three cases: | |
| 1190 * (a) pixd == null (result into new pixd) | |
| 1191 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 1192 * (c) pixd != pixs (puts result into existing pixd) | |
| 1193 * (6) For clarity, if the case is known, use these patterns: | |
| 1194 * (a) pixd = pixDilateCompBrick(NULL, pixs, ...); | |
| 1195 * (b) pixDilateCompBrick(pixs, pixs, ...); | |
| 1196 * (c) pixDilateCompBrick(pixd, pixs, ...); | |
| 1197 * (7) The dimensions of the resulting image are determined by pixs. | |
| 1198 * (8) CAUTION: both hsize and vsize are being decomposed. | |
| 1199 * The decomposer chooses a product of sizes (call them | |
| 1200 * 'terms') for each that is close to the input size, | |
| 1201 * but not necessarily equal to it. It attempts to optimize: | |
| 1202 * (a) for consistency with the input values: the product | |
| 1203 * of terms is close to the input size | |
| 1204 * (b) for efficiency of the operation: the sum of the | |
| 1205 * terms is small; ideally about twice the square | |
| 1206 * root of the input size. | |
| 1207 * So, for example, if the input hsize = 37, which is | |
| 1208 * a prime number, the decomposer will break this into two | |
| 1209 * terms, 6 and 6, so that the net result is a dilation | |
| 1210 * with hsize = 36. | |
| 1211 * </pre> | |
| 1212 */ | |
| 1213 PIX * | |
| 1214 pixDilateCompBrick(PIX *pixd, | |
| 1215 PIX *pixs, | |
| 1216 l_int32 hsize, | |
| 1217 l_int32 vsize) | |
| 1218 { | |
| 1219 PIX *pix1, *pix2, *pix3; | |
| 1220 SEL *selh1 = NULL; | |
| 1221 SEL *selh2 = NULL; | |
| 1222 SEL *selv1 = NULL; | |
| 1223 SEL *selv2 = NULL; | |
| 1224 | |
| 1225 if (!pixs) | |
| 1226 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 1227 if (pixGetDepth(pixs) != 1) | |
| 1228 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 1229 if (hsize < 1 || vsize < 1) | |
| 1230 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 1231 | |
| 1232 if (hsize == 1 && vsize == 1) | |
| 1233 return pixCopy(pixd, pixs); | |
| 1234 if (hsize > 1) { | |
| 1235 if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) { | |
| 1236 selDestroy(&selh1); | |
| 1237 selDestroy(&selh2); | |
| 1238 return (PIX *)ERROR_PTR("horiz sels not made", __func__, pixd); | |
| 1239 } | |
| 1240 } | |
| 1241 if (vsize > 1) { | |
| 1242 if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) { | |
| 1243 selDestroy(&selh1); | |
| 1244 selDestroy(&selh2); | |
| 1245 selDestroy(&selv1); | |
| 1246 selDestroy(&selv2); | |
| 1247 return (PIX *)ERROR_PTR("vert sels not made", __func__, pixd); | |
| 1248 } | |
| 1249 } | |
| 1250 | |
| 1251 pix1 = pixAddBorder(pixs, 32, 0); | |
| 1252 if (vsize == 1) { | |
| 1253 pix2 = pixDilate(NULL, pix1, selh1); | |
| 1254 pix3 = pixDilate(NULL, pix2, selh2); | |
| 1255 } else if (hsize == 1) { | |
| 1256 pix2 = pixDilate(NULL, pix1, selv1); | |
| 1257 pix3 = pixDilate(NULL, pix2, selv2); | |
| 1258 } else { | |
| 1259 pix2 = pixDilate(NULL, pix1, selh1); | |
| 1260 pix3 = pixDilate(NULL, pix2, selh2); | |
| 1261 pixDilate(pix2, pix3, selv1); | |
| 1262 pixDilate(pix3, pix2, selv2); | |
| 1263 } | |
| 1264 pixDestroy(&pix1); | |
| 1265 pixDestroy(&pix2); | |
| 1266 | |
| 1267 selDestroy(&selh1); | |
| 1268 selDestroy(&selh2); | |
| 1269 selDestroy(&selv1); | |
| 1270 selDestroy(&selv2); | |
| 1271 | |
| 1272 pix1 = pixRemoveBorder(pix3, 32); | |
| 1273 pixDestroy(&pix3); | |
| 1274 if (!pixd) | |
| 1275 return pix1; | |
| 1276 pixCopy(pixd, pix1); | |
| 1277 pixDestroy(&pix1); | |
| 1278 return pixd; | |
| 1279 } | |
| 1280 | |
| 1281 | |
| 1282 /*! | |
| 1283 * \brief pixErodeCompBrick() | |
| 1284 * | |
| 1285 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 1286 * or different from pixs | |
| 1287 * \param[in] pixs 1 bpp | |
| 1288 * \param[in] hsize width of brick Sel | |
| 1289 * \param[in] vsize height of brick Sel | |
| 1290 * \return pixd, or NULL on error | |
| 1291 * | |
| 1292 * <pre> | |
| 1293 * Notes: | |
| 1294 * (1) Sel is a brick with all elements being hits | |
| 1295 * (2) The origin is at (x, y) = (hsize/2, vsize/2) | |
| 1296 * (3) Do compositely for each dimension > 1. | |
| 1297 * (4) Do separably if both hsize and vsize are > 1. | |
| 1298 * (5) There are three cases: | |
| 1299 * (a) pixd == null (result into new pixd) | |
| 1300 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 1301 * (c) pixd != pixs (puts result into existing pixd) | |
| 1302 * (6) For clarity, if the case is known, use these patterns: | |
| 1303 * (a) pixd = pixErodeCompBrick(NULL, pixs, ...); | |
| 1304 * (b) pixErodeCompBrick(pixs, pixs, ...); | |
| 1305 * (c) pixErodeCompBrick(pixd, pixs, ...); | |
| 1306 * (7) The dimensions of the resulting image are determined by pixs. | |
| 1307 * (8) CAUTION: both hsize and vsize are being decomposed. | |
| 1308 * The decomposer chooses a product of sizes (call them | |
| 1309 * 'terms') for each that is close to the input size, | |
| 1310 * but not necessarily equal to it. It attempts to optimize: | |
| 1311 * (a) for consistency with the input values: the product | |
| 1312 * of terms is close to the input size | |
| 1313 * (b) for efficiency of the operation: the sum of the | |
| 1314 * terms is small; ideally about twice the square | |
| 1315 * root of the input size. | |
| 1316 * So, for example, if the input hsize = 37, which is | |
| 1317 * a prime number, the decomposer will break this into two | |
| 1318 * terms, 6 and 6, so that the net result is a dilation | |
| 1319 * with hsize = 36. | |
| 1320 * </pre> | |
| 1321 */ | |
| 1322 PIX * | |
| 1323 pixErodeCompBrick(PIX *pixd, | |
| 1324 PIX *pixs, | |
| 1325 l_int32 hsize, | |
| 1326 l_int32 vsize) | |
| 1327 { | |
| 1328 PIX *pixt; | |
| 1329 SEL *selh1 = NULL; | |
| 1330 SEL *selh2 = NULL; | |
| 1331 SEL *selv1 = NULL; | |
| 1332 SEL *selv2 = NULL; | |
| 1333 | |
| 1334 if (!pixs) | |
| 1335 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 1336 if (pixGetDepth(pixs) != 1) | |
| 1337 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 1338 if (hsize < 1 || vsize < 1) | |
| 1339 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 1340 | |
| 1341 if (hsize == 1 && vsize == 1) | |
| 1342 return pixCopy(pixd, pixs); | |
| 1343 if (hsize > 1) { | |
| 1344 if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) { | |
| 1345 selDestroy(&selh1); | |
| 1346 selDestroy(&selh2); | |
| 1347 return (PIX *)ERROR_PTR("horiz sels not made", __func__, pixd); | |
| 1348 } | |
| 1349 } | |
| 1350 if (vsize > 1) { | |
| 1351 if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) { | |
| 1352 selDestroy(&selh1); | |
| 1353 selDestroy(&selh2); | |
| 1354 selDestroy(&selv1); | |
| 1355 selDestroy(&selv2); | |
| 1356 return (PIX *)ERROR_PTR("vert sels not made", __func__, pixd); | |
| 1357 } | |
| 1358 } | |
| 1359 | |
| 1360 if (vsize == 1) { | |
| 1361 pixt = pixErode(NULL, pixs, selh1); | |
| 1362 pixd = pixErode(pixd, pixt, selh2); | |
| 1363 } else if (hsize == 1) { | |
| 1364 pixt = pixErode(NULL, pixs, selv1); | |
| 1365 pixd = pixErode(pixd, pixt, selv2); | |
| 1366 } else { | |
| 1367 pixt = pixErode(NULL, pixs, selh1); | |
| 1368 pixd = pixErode(pixd, pixt, selh2); | |
| 1369 pixErode(pixt, pixd, selv1); | |
| 1370 pixErode(pixd, pixt, selv2); | |
| 1371 } | |
| 1372 pixDestroy(&pixt); | |
| 1373 | |
| 1374 selDestroy(&selh1); | |
| 1375 selDestroy(&selh2); | |
| 1376 selDestroy(&selv1); | |
| 1377 selDestroy(&selv2); | |
| 1378 return pixd; | |
| 1379 } | |
| 1380 | |
| 1381 | |
| 1382 /*! | |
| 1383 * \brief pixOpenCompBrick() | |
| 1384 * | |
| 1385 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 1386 * or different from pixs | |
| 1387 * \param[in] pixs 1 bpp | |
| 1388 * \param[in] hsize width of brick Sel | |
| 1389 * \param[in] vsize height of brick Sel | |
| 1390 * \return pixd, or NULL on error | |
| 1391 * | |
| 1392 * <pre> | |
| 1393 * Notes: | |
| 1394 * (1) Sel is a brick with all elements being hits | |
| 1395 * (2) The origin is at (x, y) = (hsize/2, vsize/2) | |
| 1396 * (3) Do compositely for each dimension > 1. | |
| 1397 * (4) Do separably if both hsize and vsize are > 1. | |
| 1398 * (5) There are three cases: | |
| 1399 * (a) pixd == null (result into new pixd) | |
| 1400 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 1401 * (c) pixd != pixs (puts result into existing pixd) | |
| 1402 * (6) For clarity, if the case is known, use these patterns: | |
| 1403 * (a) pixd = pixOpenCompBrick(NULL, pixs, ...); | |
| 1404 * (b) pixOpenCompBrick(pixs, pixs, ...); | |
| 1405 * (c) pixOpenCompBrick(pixd, pixs, ...); | |
| 1406 * (7) The dimensions of the resulting image are determined by pixs. | |
| 1407 * (8) CAUTION: both hsize and vsize are being decomposed. | |
| 1408 * The decomposer chooses a product of sizes (call them | |
| 1409 * 'terms') for each that is close to the input size, | |
| 1410 * but not necessarily equal to it. It attempts to optimize: | |
| 1411 * (a) for consistency with the input values: the product | |
| 1412 * of terms is close to the input size | |
| 1413 * (b) for efficiency of the operation: the sum of the | |
| 1414 * terms is small; ideally about twice the square | |
| 1415 * root of the input size. | |
| 1416 * So, for example, if the input hsize = 37, which is | |
| 1417 * a prime number, the decomposer will break this into two | |
| 1418 * terms, 6 and 6, so that the net result is a dilation | |
| 1419 * with hsize = 36. | |
| 1420 * </pre> | |
| 1421 */ | |
| 1422 PIX * | |
| 1423 pixOpenCompBrick(PIX *pixd, | |
| 1424 PIX *pixs, | |
| 1425 l_int32 hsize, | |
| 1426 l_int32 vsize) | |
| 1427 { | |
| 1428 PIX *pixt; | |
| 1429 SEL *selh1 = NULL; | |
| 1430 SEL *selh2 = NULL; | |
| 1431 SEL *selv1 = NULL; | |
| 1432 SEL *selv2 = NULL; | |
| 1433 | |
| 1434 if (!pixs) | |
| 1435 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 1436 if (pixGetDepth(pixs) != 1) | |
| 1437 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 1438 if (hsize < 1 || vsize < 1) | |
| 1439 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 1440 | |
| 1441 if (hsize == 1 && vsize == 1) | |
| 1442 return pixCopy(pixd, pixs); | |
| 1443 if (hsize > 1) { | |
| 1444 if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) { | |
| 1445 selDestroy(&selh1); | |
| 1446 selDestroy(&selh2); | |
| 1447 return (PIX *)ERROR_PTR("horiz sels not made", __func__, pixd); | |
| 1448 } | |
| 1449 } | |
| 1450 if (vsize > 1) { | |
| 1451 if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) { | |
| 1452 selDestroy(&selh1); | |
| 1453 selDestroy(&selh2); | |
| 1454 selDestroy(&selv1); | |
| 1455 selDestroy(&selv2); | |
| 1456 return (PIX *)ERROR_PTR("vert sels not made", __func__, pixd); | |
| 1457 } | |
| 1458 } | |
| 1459 | |
| 1460 if (vsize == 1) { | |
| 1461 pixt = pixErode(NULL, pixs, selh1); | |
| 1462 pixd = pixErode(pixd, pixt, selh2); | |
| 1463 pixDilate(pixt, pixd, selh1); | |
| 1464 pixDilate(pixd, pixt, selh2); | |
| 1465 } else if (hsize == 1) { | |
| 1466 pixt = pixErode(NULL, pixs, selv1); | |
| 1467 pixd = pixErode(pixd, pixt, selv2); | |
| 1468 pixDilate(pixt, pixd, selv1); | |
| 1469 pixDilate(pixd, pixt, selv2); | |
| 1470 } else { /* do separably */ | |
| 1471 pixt = pixErode(NULL, pixs, selh1); | |
| 1472 pixd = pixErode(pixd, pixt, selh2); | |
| 1473 pixErode(pixt, pixd, selv1); | |
| 1474 pixErode(pixd, pixt, selv2); | |
| 1475 pixDilate(pixt, pixd, selh1); | |
| 1476 pixDilate(pixd, pixt, selh2); | |
| 1477 pixDilate(pixt, pixd, selv1); | |
| 1478 pixDilate(pixd, pixt, selv2); | |
| 1479 } | |
| 1480 pixDestroy(&pixt); | |
| 1481 | |
| 1482 selDestroy(&selh1); | |
| 1483 selDestroy(&selh2); | |
| 1484 selDestroy(&selv1); | |
| 1485 selDestroy(&selv2); | |
| 1486 return pixd; | |
| 1487 } | |
| 1488 | |
| 1489 | |
| 1490 /*! | |
| 1491 * \brief pixCloseCompBrick() | |
| 1492 * | |
| 1493 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 1494 * or different from pixs | |
| 1495 * \param[in] pixs 1 bpp | |
| 1496 * \param[in] hsize width of brick Sel | |
| 1497 * \param[in] vsize height of brick Sel | |
| 1498 * \return pixd, or NULL on error | |
| 1499 * | |
| 1500 * <pre> | |
| 1501 * Notes: | |
| 1502 * (1) Sel is a brick with all elements being hits | |
| 1503 * (2) The origin is at (x, y) = (hsize/2, vsize/2) | |
| 1504 * (3) Do compositely for each dimension > 1. | |
| 1505 * (4) Do separably if both hsize and vsize are > 1. | |
| 1506 * (5) There are three cases: | |
| 1507 * (a) pixd == null (result into new pixd) | |
| 1508 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 1509 * (c) pixd != pixs (puts result into existing pixd) | |
| 1510 * (6) For clarity, if the case is known, use these patterns: | |
| 1511 * (a) pixd = pixCloseCompBrick(NULL, pixs, ...); | |
| 1512 * (b) pixCloseCompBrick(pixs, pixs, ...); | |
| 1513 * (c) pixCloseCompBrick(pixd, pixs, ...); | |
| 1514 * (7) The dimensions of the resulting image are determined by pixs. | |
| 1515 * (8) CAUTION: both hsize and vsize are being decomposed. | |
| 1516 * The decomposer chooses a product of sizes (call them | |
| 1517 * 'terms') for each that is close to the input size, | |
| 1518 * but not necessarily equal to it. It attempts to optimize: | |
| 1519 * (a) for consistency with the input values: the product | |
| 1520 * of terms is close to the input size | |
| 1521 * (b) for efficiency of the operation: the sum of the | |
| 1522 * terms is small; ideally about twice the square | |
| 1523 * root of the input size. | |
| 1524 * So, for example, if the input hsize = 37, which is | |
| 1525 * a prime number, the decomposer will break this into two | |
| 1526 * terms, 6 and 6, so that the net result is a dilation | |
| 1527 * with hsize = 36. | |
| 1528 * </pre> | |
| 1529 */ | |
| 1530 PIX * | |
| 1531 pixCloseCompBrick(PIX *pixd, | |
| 1532 PIX *pixs, | |
| 1533 l_int32 hsize, | |
| 1534 l_int32 vsize) | |
| 1535 { | |
| 1536 PIX *pixt; | |
| 1537 SEL *selh1 = NULL; | |
| 1538 SEL *selh2 = NULL; | |
| 1539 SEL *selv1 = NULL; | |
| 1540 SEL *selv2 = NULL; | |
| 1541 | |
| 1542 if (!pixs) | |
| 1543 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 1544 if (pixGetDepth(pixs) != 1) | |
| 1545 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 1546 if (hsize < 1 || vsize < 1) | |
| 1547 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 1548 | |
| 1549 if (hsize == 1 && vsize == 1) | |
| 1550 return pixCopy(pixd, pixs); | |
| 1551 if (hsize > 1) { | |
| 1552 if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) { | |
| 1553 selDestroy(&selh1); | |
| 1554 selDestroy(&selh2); | |
| 1555 return (PIX *)ERROR_PTR("horiz sels not made", __func__, pixd); | |
| 1556 } | |
| 1557 } | |
| 1558 if (vsize > 1) { | |
| 1559 if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) { | |
| 1560 selDestroy(&selh1); | |
| 1561 selDestroy(&selh2); | |
| 1562 selDestroy(&selv1); | |
| 1563 selDestroy(&selv2); | |
| 1564 return (PIX *)ERROR_PTR("vert sels not made", __func__, pixd); | |
| 1565 } | |
| 1566 } | |
| 1567 | |
| 1568 if (vsize == 1) { | |
| 1569 pixt = pixDilate(NULL, pixs, selh1); | |
| 1570 pixd = pixDilate(pixd, pixt, selh2); | |
| 1571 pixErode(pixt, pixd, selh1); | |
| 1572 pixErode(pixd, pixt, selh2); | |
| 1573 } else if (hsize == 1) { | |
| 1574 pixt = pixDilate(NULL, pixs, selv1); | |
| 1575 pixd = pixDilate(pixd, pixt, selv2); | |
| 1576 pixErode(pixt, pixd, selv1); | |
| 1577 pixErode(pixd, pixt, selv2); | |
| 1578 } else { /* do separably */ | |
| 1579 pixt = pixDilate(NULL, pixs, selh1); | |
| 1580 pixd = pixDilate(pixd, pixt, selh2); | |
| 1581 pixDilate(pixt, pixd, selv1); | |
| 1582 pixDilate(pixd, pixt, selv2); | |
| 1583 pixErode(pixt, pixd, selh1); | |
| 1584 pixErode(pixd, pixt, selh2); | |
| 1585 pixErode(pixt, pixd, selv1); | |
| 1586 pixErode(pixd, pixt, selv2); | |
| 1587 } | |
| 1588 pixDestroy(&pixt); | |
| 1589 | |
| 1590 selDestroy(&selh1); | |
| 1591 selDestroy(&selh2); | |
| 1592 selDestroy(&selv1); | |
| 1593 selDestroy(&selv2); | |
| 1594 return pixd; | |
| 1595 } | |
| 1596 | |
| 1597 | |
| 1598 /*! | |
| 1599 * \brief pixCloseSafeCompBrick() | |
| 1600 * | |
| 1601 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 1602 * or different from pixs | |
| 1603 * \param[in] pixs 1 bpp | |
| 1604 * \param[in] hsize width of brick Sel | |
| 1605 * \param[in] vsize height of brick Sel | |
| 1606 * \return pixd, or NULL on error | |
| 1607 * | |
| 1608 * <pre> | |
| 1609 * Notes: | |
| 1610 * (1) Sel is a brick with all elements being hits | |
| 1611 * (2) The origin is at (x, y) = (hsize/2, vsize/2) | |
| 1612 * (3) Do compositely for each dimension > 1. | |
| 1613 * (4) Do separably if both hsize and vsize are > 1. | |
| 1614 * (5) Safe closing adds a border of 0 pixels, of sufficient size so | |
| 1615 * that all pixels in input image are processed within | |
| 1616 * 32-bit words in the expanded image. As a result, there is | |
| 1617 * no special processing for pixels near the boundary, and there | |
| 1618 * are no boundary effects. The border is removed at the end. | |
| 1619 * (6) There are three cases: | |
| 1620 * (a) pixd == null (result into new pixd) | |
| 1621 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 1622 * (c) pixd != pixs (puts result into existing pixd) | |
| 1623 * (7) For clarity, if the case is known, use these patterns: | |
| 1624 * (a) pixd = pixCloseSafeCompBrick(NULL, pixs, ...); | |
| 1625 * (b) pixCloseSafeCompBrick(pixs, pixs, ...); | |
| 1626 * (c) pixCloseSafeCompBrick(pixd, pixs, ...); | |
| 1627 * (8) The dimensions of the resulting image are determined by pixs. | |
| 1628 * (9) CAUTION: both hsize and vsize are being decomposed. | |
| 1629 * The decomposer chooses a product of sizes (call them | |
| 1630 * 'terms') for each that is close to the input size, | |
| 1631 * but not necessarily equal to it. It attempts to optimize: | |
| 1632 * (a) for consistency with the input values: the product | |
| 1633 * of terms is close to the input size | |
| 1634 * (b) for efficiency of the operation: the sum of the | |
| 1635 * terms is small; ideally about twice the square | |
| 1636 * root of the input size. | |
| 1637 * So, for example, if the input hsize = 37, which is | |
| 1638 * a prime number, the decomposer will break this into two | |
| 1639 * terms, 6 and 6, so that the net result is a dilation | |
| 1640 * with hsize = 36. | |
| 1641 * </pre> | |
| 1642 */ | |
| 1643 PIX * | |
| 1644 pixCloseSafeCompBrick(PIX *pixd, | |
| 1645 PIX *pixs, | |
| 1646 l_int32 hsize, | |
| 1647 l_int32 vsize) | |
| 1648 { | |
| 1649 l_int32 maxtrans, bordsize; | |
| 1650 PIX *pixsb, *pixt, *pixdb; | |
| 1651 SEL *selh1 = NULL; | |
| 1652 SEL *selh2 = NULL; | |
| 1653 SEL *selv1 = NULL; | |
| 1654 SEL *selv2 = NULL; | |
| 1655 | |
| 1656 if (!pixs) | |
| 1657 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 1658 if (pixGetDepth(pixs) != 1) | |
| 1659 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 1660 if (hsize < 1 || vsize < 1) | |
| 1661 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 1662 | |
| 1663 if (hsize == 1 && vsize == 1) | |
| 1664 return pixCopy(pixd, pixs); | |
| 1665 | |
| 1666 /* Symmetric b.c. handles correctly without added pixels */ | |
| 1667 if (MORPH_BC == SYMMETRIC_MORPH_BC) | |
| 1668 return pixCloseCompBrick(pixd, pixs, hsize, vsize); | |
| 1669 | |
| 1670 if (hsize > 1) { | |
| 1671 if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) { | |
| 1672 selDestroy(&selh1); | |
| 1673 selDestroy(&selh2); | |
| 1674 return (PIX *)ERROR_PTR("horiz sels not made", __func__, pixd); | |
| 1675 } | |
| 1676 } | |
| 1677 if (vsize > 1) { | |
| 1678 if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) { | |
| 1679 selDestroy(&selh1); | |
| 1680 selDestroy(&selh2); | |
| 1681 selDestroy(&selv1); | |
| 1682 selDestroy(&selv2); | |
| 1683 return (PIX *)ERROR_PTR("vert sels not made", __func__, pixd); | |
| 1684 } | |
| 1685 } | |
| 1686 | |
| 1687 maxtrans = L_MAX(hsize / 2, vsize / 2); | |
| 1688 bordsize = 32 * ((maxtrans + 31) / 32); /* full 32 bit words */ | |
| 1689 pixsb = pixAddBorder(pixs, bordsize, 0); | |
| 1690 | |
| 1691 if (vsize == 1) { | |
| 1692 pixt = pixDilate(NULL, pixsb, selh1); | |
| 1693 pixdb = pixDilate(NULL, pixt, selh2); | |
| 1694 pixErode(pixt, pixdb, selh1); | |
| 1695 pixErode(pixdb, pixt, selh2); | |
| 1696 } else if (hsize == 1) { | |
| 1697 pixt = pixDilate(NULL, pixsb, selv1); | |
| 1698 pixdb = pixDilate(NULL, pixt, selv2); | |
| 1699 pixErode(pixt, pixdb, selv1); | |
| 1700 pixErode(pixdb, pixt, selv2); | |
| 1701 } else { /* do separably */ | |
| 1702 pixt = pixDilate(NULL, pixsb, selh1); | |
| 1703 pixdb = pixDilate(NULL, pixt, selh2); | |
| 1704 pixDilate(pixt, pixdb, selv1); | |
| 1705 pixDilate(pixdb, pixt, selv2); | |
| 1706 pixErode(pixt, pixdb, selh1); | |
| 1707 pixErode(pixdb, pixt, selh2); | |
| 1708 pixErode(pixt, pixdb, selv1); | |
| 1709 pixErode(pixdb, pixt, selv2); | |
| 1710 } | |
| 1711 pixDestroy(&pixt); | |
| 1712 | |
| 1713 pixt = pixRemoveBorder(pixdb, bordsize); | |
| 1714 pixDestroy(&pixsb); | |
| 1715 pixDestroy(&pixdb); | |
| 1716 | |
| 1717 if (!pixd) { | |
| 1718 pixd = pixt; | |
| 1719 } else { | |
| 1720 pixCopy(pixd, pixt); | |
| 1721 pixDestroy(&pixt); | |
| 1722 } | |
| 1723 | |
| 1724 selDestroy(&selh1); | |
| 1725 selDestroy(&selh2); | |
| 1726 selDestroy(&selv1); | |
| 1727 selDestroy(&selv2); | |
| 1728 return pixd; | |
| 1729 } | |
| 1730 | |
| 1731 | |
| 1732 /*-----------------------------------------------------------------* | |
| 1733 * Functions associated with boundary conditions * | |
| 1734 *-----------------------------------------------------------------*/ | |
| 1735 /*! | |
| 1736 * \brief resetMorphBoundaryCondition() | |
| 1737 * | |
| 1738 * \param[in] bc SYMMETRIC_MORPH_BC, ASYMMETRIC_MORPH_BC | |
| 1739 * \return void | |
| 1740 */ | |
| 1741 void | |
| 1742 resetMorphBoundaryCondition(l_int32 bc) | |
| 1743 { | |
| 1744 if (bc != SYMMETRIC_MORPH_BC && bc != ASYMMETRIC_MORPH_BC) { | |
| 1745 L_WARNING("invalid bc; using asymmetric\n", __func__); | |
| 1746 bc = ASYMMETRIC_MORPH_BC; | |
| 1747 } | |
| 1748 MORPH_BC = bc; | |
| 1749 return; | |
| 1750 } | |
| 1751 | |
| 1752 | |
| 1753 /*! | |
| 1754 * \brief getMorphBorderPixelColor() | |
| 1755 * | |
| 1756 * \param[in] type L_MORPH_DILATE, L_MORPH_ERODE | |
| 1757 * \param[in] depth of pix | |
| 1758 * \return color of border pixels for this operation | |
| 1759 */ | |
| 1760 l_uint32 | |
| 1761 getMorphBorderPixelColor(l_int32 type, | |
| 1762 l_int32 depth) | |
| 1763 { | |
| 1764 if (type != L_MORPH_DILATE && type != L_MORPH_ERODE) | |
| 1765 return ERROR_INT("invalid type", __func__, 0); | |
| 1766 if (depth != 1 && depth != 2 && depth != 4 && depth != 8 && | |
| 1767 depth != 16 && depth != 32) | |
| 1768 return ERROR_INT("invalid depth", __func__, 0); | |
| 1769 | |
| 1770 if (MORPH_BC == ASYMMETRIC_MORPH_BC || type == L_MORPH_DILATE) | |
| 1771 return 0; | |
| 1772 | |
| 1773 /* Symmetric & erosion */ | |
| 1774 if (depth < 32) | |
| 1775 return ((1 << depth) - 1); | |
| 1776 else /* depth == 32 */ | |
| 1777 return 0xffffff00; | |
| 1778 } | |
| 1779 | |
| 1780 | |
| 1781 /*-----------------------------------------------------------------* | |
| 1782 * Static helpers for arg processing * | |
| 1783 *-----------------------------------------------------------------*/ | |
| 1784 /*! | |
| 1785 * \brief processMorphArgs1() | |
| 1786 * | |
| 1787 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 1788 * or different from pixs | |
| 1789 * \param[in] pixs 1 bpp | |
| 1790 * \param[in] sel | |
| 1791 * \param[out] ppixt copy or clone of %pixs | |
| 1792 * \return pixd, or NULL on error. | |
| 1793 * | |
| 1794 * <pre> | |
| 1795 * Notes: | |
| 1796 * (1) This is used for generic erosion, dilation and HMT. | |
| 1797 * </pre> | |
| 1798 */ | |
| 1799 static PIX * | |
| 1800 processMorphArgs1(PIX *pixd, | |
| 1801 PIX *pixs, | |
| 1802 SEL *sel, | |
| 1803 PIX **ppixt) | |
| 1804 { | |
| 1805 l_int32 sx, sy; | |
| 1806 | |
| 1807 if (!ppixt) | |
| 1808 return (PIX *)ERROR_PTR("&pixt not defined", __func__, pixd); | |
| 1809 *ppixt = NULL; | |
| 1810 if (!pixs) | |
| 1811 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 1812 if (!sel) | |
| 1813 return (PIX *)ERROR_PTR("sel not defined", __func__, pixd); | |
| 1814 if (pixGetDepth(pixs) != 1) | |
| 1815 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 1816 | |
| 1817 selGetParameters(sel, &sx, &sy, NULL, NULL); | |
| 1818 if (sx == 0 || sy == 0) | |
| 1819 return (PIX *)ERROR_PTR("sel of size 0", __func__, pixd); | |
| 1820 | |
| 1821 /* We require pixd to exist and to be the same size as pixs. | |
| 1822 * Further, pixt must be a copy (or clone) of pixs. */ | |
| 1823 if (!pixd) { | |
| 1824 if ((pixd = pixCreateTemplate(pixs)) == NULL) | |
| 1825 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 1826 *ppixt = pixClone(pixs); | |
| 1827 } else { | |
| 1828 pixResizeImageData(pixd, pixs); | |
| 1829 if (pixd == pixs) { /* in-place; must make a copy of pixs */ | |
| 1830 if ((*ppixt = pixCopy(NULL, pixs)) == NULL) | |
| 1831 return (PIX *)ERROR_PTR("pixt not made", __func__, pixd); | |
| 1832 } else { | |
| 1833 *ppixt = pixClone(pixs); | |
| 1834 } | |
| 1835 } | |
| 1836 return pixd; | |
| 1837 } | |
| 1838 | |
| 1839 | |
| 1840 /*! | |
| 1841 * \brief processMorphArgs2() | |
| 1842 * | |
| 1843 * This is used for generic openings and closings. | |
| 1844 */ | |
| 1845 static PIX * | |
| 1846 processMorphArgs2(PIX *pixd, | |
| 1847 PIX *pixs, | |
| 1848 SEL *sel) | |
| 1849 { | |
| 1850 l_int32 sx, sy; | |
| 1851 | |
| 1852 if (!pixs) | |
| 1853 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 1854 if (!sel) | |
| 1855 return (PIX *)ERROR_PTR("sel not defined", __func__, pixd); | |
| 1856 if (pixGetDepth(pixs) != 1) | |
| 1857 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 1858 | |
| 1859 selGetParameters(sel, &sx, &sy, NULL, NULL); | |
| 1860 if (sx == 0 || sy == 0) | |
| 1861 return (PIX *)ERROR_PTR("sel of size 0", __func__, pixd); | |
| 1862 | |
| 1863 if (!pixd) | |
| 1864 return pixCreateTemplate(pixs); | |
| 1865 pixResizeImageData(pixd, pixs); | |
| 1866 return pixd; | |
| 1867 } |
