Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/morphdwa.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 morphdwa.c | |
| 29 * <pre> | |
| 30 * | |
| 31 * Binary morphological (dwa) ops with brick Sels | |
| 32 * PIX *pixDilateBrickDwa() | |
| 33 * PIX *pixErodeBrickDwa() | |
| 34 * PIX *pixOpenBrickDwa() | |
| 35 * PIX *pixCloseBrickDwa() | |
| 36 * | |
| 37 * Binary composite morphological (dwa) ops with brick Sels | |
| 38 * PIX *pixDilateCompBrickDwa() | |
| 39 * PIX *pixErodeCompBrickDwa() | |
| 40 * PIX *pixOpenCompBrickDwa() | |
| 41 * PIX *pixCloseCompBrickDwa() | |
| 42 * | |
| 43 * Binary extended composite morphological (dwa) ops with brick Sels | |
| 44 * PIX *pixDilateCompBrickExtendDwa() | |
| 45 * PIX *pixErodeCompBrickExtendDwa() | |
| 46 * PIX *pixOpenCompBrickExtendDwa() | |
| 47 * PIX *pixCloseCompBrickExtendDwa() | |
| 48 * l_int32 getExtendedCompositeParameters() | |
| 49 * | |
| 50 * These are higher-level interfaces for dwa morphology with brick Sels. | |
| 51 * Because many morphological operations are performed using | |
| 52 * separable brick Sels, it is useful to have a simple interface | |
| 53 * for this. | |
| 54 * | |
| 55 * We have included all 58 of the brick Sels that are generated | |
| 56 * by selaAddBasic(). These are sufficient for all the decomposable | |
| 57 * bricks up to size 63, which is the limit for dwa Sels with | |
| 58 * origins at the center of the Sel. | |
| 59 * | |
| 60 * All three sets can be used as the basic interface for general | |
| 61 * brick operations. Here are the internal calling sequences: | |
| 62 * | |
| 63 * (1) If you try to apply a non-decomposable operation, such as | |
| 64 * pixErodeBrickDwa(), with a Sel size that doesn't exist, | |
| 65 * this calls a decomposable operation, pixErodeCompBrickDwa(), | |
| 66 * instead. This can differ in linear Sel size by up to | |
| 67 * 2 pixels from the request. | |
| 68 * | |
| 69 * (2) If either Sel brick dimension is greater than 63, the extended | |
| 70 * composite function is called. | |
| 71 * | |
| 72 * (3) The extended composite function calls the composite function | |
| 73 * a number of times with size 63, and once with size < 63. | |
| 74 * Because each operation with a size of 63 is done compositely | |
| 75 * with 7 x 9 (exactly 63), the net result is correct in | |
| 76 * length to within 2 pixels. | |
| 77 * | |
| 78 * For composite operations, both using a comb and extended (beyond 63), | |
| 79 * horizontal and vertical operations are composed separately | |
| 80 * and sequentially. | |
| 81 * | |
| 82 * We have also included use of all the 76 comb Sels that are generated | |
| 83 * by selaAddDwaCombs(). The generated code is in dwacomb.2.c | |
| 84 * and dwacomblow.2.c. These are used for the composite dwa | |
| 85 * brick operations. | |
| 86 * | |
| 87 * The non-composite brick operations, such as pixDilateBrickDwa(), | |
| 88 * will call the associated composite operation in situations where | |
| 89 * the requisite brick Sel has not been compiled into fmorphgen*.1.c. | |
| 90 * | |
| 91 * If you want to use brick Sels that are not represented in the | |
| 92 * basic set of 58, you must generate the dwa code to implement them. | |
| 93 * You have three choices for how to use these: | |
| 94 * | |
| 95 * (1) Add both the new Sels and the dwa code to the library: | |
| 96 * ~ For simplicity, add your new brick Sels to those defined | |
| 97 * in selaAddBasic(). | |
| 98 * ~ Recompile the library. | |
| 99 * ~ Make prog/fmorphautogen. | |
| 100 * ~ Run prog/fmorphautogen, to generate new versions of the | |
| 101 * dwa code in fmorphgen.1.c and fmorphgenlow.1.c. | |
| 102 * ~ Copy these two files to src. | |
| 103 * ~ Recompile the library again. | |
| 104 * ~ Use the new brick Sels in your program and compile it. | |
| 105 * | |
| 106 * (2) Make both the new Sels and dwa code outside the library, | |
| 107 * and link it directly to an executable: | |
| 108 * ~ Write a function to generate the new Sels in a Sela, and call | |
| 109 * fmorphautogen(sela, <N>, filename) to generate the code. | |
| 110 * ~ Compile your program that uses the newly generated function | |
| 111 * pixMorphDwa_<N>(), and link to the two new C files. | |
| 112 * | |
| 113 * (3) Make the new Sels in the library and use the dwa code outside it: | |
| 114 * ~ Add code in the library to generate your new brick Sels. | |
| 115 * (It is suggested that you NOT add these Sels to the | |
| 116 * selaAddBasic() function; write a new function that generates | |
| 117 * a new Sela.) | |
| 118 * ~ Recompile the library. | |
| 119 * ~ Write a small program that generates the Sela and calls | |
| 120 * fmorphautogen(sela, <N>, filename) to generate the code. | |
| 121 * ~ Compile your program that uses the newly generated function | |
| 122 * pixMorphDwa_<N>(), and link to the two new C files. | |
| 123 * As an example of this approach, see prog/dwamorph*_reg.c: | |
| 124 * ~ added selaAddDwaLinear() to sel2.c | |
| 125 * ~ wrote dwamorph1_reg.c, to generate the dwa code. | |
| 126 * ~ compiled and linked the generated code with the application, | |
| 127 * dwamorph2_reg.c. (Note: because this was a regression test, | |
| 128 * dwamorph1_reg also builds and runs the application program.) | |
| 129 * </pre> | |
| 130 */ | |
| 131 | |
| 132 #ifdef HAVE_CONFIG_H | |
| 133 #include <config_auto.h> | |
| 134 #endif /* HAVE_CONFIG_H */ | |
| 135 | |
| 136 #include "allheaders.h" | |
| 137 | |
| 138 #ifndef NO_CONSOLE_IO | |
| 139 #define DEBUG_SEL_LOOKUP 0 | |
| 140 #endif /* ~NO_CONSOLE_IO */ | |
| 141 | |
| 142 /*-----------------------------------------------------------------* | |
| 143 * Binary morphological (dwa) ops with brick Sels * | |
| 144 *-----------------------------------------------------------------*/ | |
| 145 /*! | |
| 146 * \brief pixDilateBrickDwa() | |
| 147 * | |
| 148 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 149 * or different from pixs | |
| 150 * \param[in] pixs 1 bpp | |
| 151 * \param[in] hsize width of brick Sel | |
| 152 * \param[in] vsize height of brick Sel | |
| 153 * \return pixd | |
| 154 * | |
| 155 * <pre> | |
| 156 * Notes: | |
| 157 * (1) These implement 2D brick Sels, using linear Sels generated | |
| 158 * with selaAddBasic(). | |
| 159 * (2) A brick Sel has hits for all elements. | |
| 160 * (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) | |
| 161 * (4) Do separably if both hsize and vsize are > 1. | |
| 162 * (5) It is necessary that both horizontal and vertical Sels | |
| 163 * of the input size are defined in the basic sela. | |
| 164 * (6) There are three cases: | |
| 165 * (a) pixd == null (result into new pixd) | |
| 166 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 167 * (c) pixd != pixs (puts result into existing pixd) | |
| 168 * (7) For clarity, if the case is known, use these patterns: | |
| 169 * (a) pixd = pixDilateBrickDwa(NULL, pixs, ...); | |
| 170 * (b) pixDilateBrickDwa(pixs, pixs, ...); | |
| 171 * (c) pixDilateBrickDwa(pixd, pixs, ...); | |
| 172 * (8) The size of pixd is determined by pixs. | |
| 173 * (9) If either linear Sel is not found, this calls | |
| 174 * the appropriate decomposible function. | |
| 175 * </pre> | |
| 176 */ | |
| 177 PIX * | |
| 178 pixDilateBrickDwa(PIX *pixd, | |
| 179 PIX *pixs, | |
| 180 l_int32 hsize, | |
| 181 l_int32 vsize) | |
| 182 { | |
| 183 l_int32 found; | |
| 184 char *selnameh, *selnamev; | |
| 185 SELA *sela; | |
| 186 PIX *pixt1, *pixt2, *pixt3; | |
| 187 | |
| 188 if (!pixs) | |
| 189 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 190 if (pixGetDepth(pixs) != 1) | |
| 191 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 192 if (hsize < 1 || vsize < 1) | |
| 193 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 194 | |
| 195 if (hsize == 1 && vsize == 1) | |
| 196 return pixCopy(pixd, pixs); | |
| 197 | |
| 198 sela = selaAddBasic(NULL); | |
| 199 found = TRUE; | |
| 200 selnameh = selnamev = NULL; | |
| 201 if (hsize > 1) { | |
| 202 selnameh = selaGetBrickName(sela, hsize, 1); | |
| 203 if (!selnameh) found = FALSE; | |
| 204 } | |
| 205 if (vsize > 1) { | |
| 206 selnamev = selaGetBrickName(sela, 1, vsize); | |
| 207 if (!selnamev) found = FALSE; | |
| 208 } | |
| 209 selaDestroy(&sela); | |
| 210 if (!found) { | |
| 211 L_INFO("Calling the decomposable dwa function\n", __func__); | |
| 212 if (selnameh) LEPT_FREE(selnameh); | |
| 213 if (selnamev) LEPT_FREE(selnamev); | |
| 214 return pixDilateCompBrickDwa(pixd, pixs, hsize, vsize); | |
| 215 } | |
| 216 | |
| 217 if (vsize == 1) { | |
| 218 pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnameh); | |
| 219 LEPT_FREE(selnameh); | |
| 220 } else if (hsize == 1) { | |
| 221 pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnamev); | |
| 222 LEPT_FREE(selnamev); | |
| 223 } else { | |
| 224 pixt1 = pixAddBorder(pixs, 32, 0); | |
| 225 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh); | |
| 226 pixFMorphopGen_1(pixt1, pixt3, L_MORPH_DILATE, selnamev); | |
| 227 pixt2 = pixRemoveBorder(pixt1, 32); | |
| 228 pixDestroy(&pixt1); | |
| 229 pixDestroy(&pixt3); | |
| 230 LEPT_FREE(selnameh); | |
| 231 LEPT_FREE(selnamev); | |
| 232 } | |
| 233 | |
| 234 if (!pixd) | |
| 235 return pixt2; | |
| 236 | |
| 237 pixTransferAllData(pixd, &pixt2, 0, 0); | |
| 238 return pixd; | |
| 239 } | |
| 240 | |
| 241 | |
| 242 /*! | |
| 243 * \brief pixErodeBrickDwa() | |
| 244 * | |
| 245 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 246 * or different from pixs | |
| 247 * \param[in] pixs 1 bpp | |
| 248 * \param[in] hsize width of brick Sel | |
| 249 * \param[in] vsize height of brick Sel | |
| 250 * \return pixd | |
| 251 * | |
| 252 * <pre> | |
| 253 * Notes: | |
| 254 * (1) These implement 2D brick Sels, using linear Sels generated | |
| 255 * with selaAddBasic(). | |
| 256 * (2) A brick Sel has hits for all elements. | |
| 257 * (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) | |
| 258 * (4) Do separably if both hsize and vsize are > 1. | |
| 259 * (5) It is necessary that both horizontal and vertical Sels | |
| 260 * of the input size are defined in the basic sela. | |
| 261 * (6) Note that we must always set or clear the border pixels | |
| 262 * before each operation, depending on the the b.c. | |
| 263 * (symmetric or asymmetric). | |
| 264 * (7) There are three cases: | |
| 265 * (a) pixd == null (result into new pixd) | |
| 266 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 267 * (c) pixd != pixs (puts result into existing pixd) | |
| 268 * (8) For clarity, if the case is known, use these patterns: | |
| 269 * (a) pixd = pixErodeBrickDwa(NULL, pixs, ...); | |
| 270 * (b) pixErodeBrickDwa(pixs, pixs, ...); | |
| 271 * (c) pixErodeBrickDwa(pixd, pixs, ...); | |
| 272 * (9) The size of the result is determined by pixs. | |
| 273 * (10) If either linear Sel is not found, this calls | |
| 274 * the appropriate decomposible function. | |
| 275 * </pre> | |
| 276 */ | |
| 277 PIX * | |
| 278 pixErodeBrickDwa(PIX *pixd, | |
| 279 PIX *pixs, | |
| 280 l_int32 hsize, | |
| 281 l_int32 vsize) | |
| 282 { | |
| 283 l_int32 found; | |
| 284 char *selnameh, *selnamev; | |
| 285 SELA *sela; | |
| 286 PIX *pixt1, *pixt2, *pixt3; | |
| 287 | |
| 288 if (!pixs) | |
| 289 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 290 if (pixGetDepth(pixs) != 1) | |
| 291 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 292 if (hsize < 1 || vsize < 1) | |
| 293 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 294 | |
| 295 if (hsize == 1 && vsize == 1) | |
| 296 return pixCopy(pixd, pixs); | |
| 297 | |
| 298 sela = selaAddBasic(NULL); | |
| 299 found = TRUE; | |
| 300 selnameh = selnamev = NULL; | |
| 301 if (hsize > 1) { | |
| 302 selnameh = selaGetBrickName(sela, hsize, 1); | |
| 303 if (!selnameh) found = FALSE; | |
| 304 } | |
| 305 if (vsize > 1) { | |
| 306 selnamev = selaGetBrickName(sela, 1, vsize); | |
| 307 if (!selnamev) found = FALSE; | |
| 308 } | |
| 309 selaDestroy(&sela); | |
| 310 if (!found) { | |
| 311 L_INFO("Calling the decomposable dwa function\n", __func__); | |
| 312 if (selnameh) LEPT_FREE(selnameh); | |
| 313 if (selnamev) LEPT_FREE(selnamev); | |
| 314 return pixErodeCompBrickDwa(pixd, pixs, hsize, vsize); | |
| 315 } | |
| 316 | |
| 317 if (vsize == 1) { | |
| 318 pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnameh); | |
| 319 LEPT_FREE(selnameh); | |
| 320 } else if (hsize == 1) { | |
| 321 pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnamev); | |
| 322 LEPT_FREE(selnamev); | |
| 323 } else { | |
| 324 pixt1 = pixAddBorder(pixs, 32, 0); | |
| 325 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh); | |
| 326 pixFMorphopGen_1(pixt1, pixt3, L_MORPH_ERODE, selnamev); | |
| 327 pixt2 = pixRemoveBorder(pixt1, 32); | |
| 328 pixDestroy(&pixt1); | |
| 329 pixDestroy(&pixt3); | |
| 330 LEPT_FREE(selnameh); | |
| 331 LEPT_FREE(selnamev); | |
| 332 } | |
| 333 | |
| 334 if (!pixd) | |
| 335 return pixt2; | |
| 336 | |
| 337 pixTransferAllData(pixd, &pixt2, 0, 0); | |
| 338 return pixd; | |
| 339 } | |
| 340 | |
| 341 | |
| 342 /*! | |
| 343 * \brief pixOpenBrickDwa() | |
| 344 * | |
| 345 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 346 * or different from pixs | |
| 347 * \param[in] pixs 1 bpp | |
| 348 * \param[in] hsize width of brick Sel | |
| 349 * \param[in] vsize height of brick Sel | |
| 350 * \return pixd | |
| 351 * | |
| 352 * <pre> | |
| 353 * Notes: | |
| 354 * (1) These implement 2D brick Sels, using linear Sels generated | |
| 355 * with selaAddBasic(). | |
| 356 * (2) A brick Sel has hits for all elements. | |
| 357 * (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) | |
| 358 * (4) Do separably if both hsize and vsize are > 1. | |
| 359 * (5) It is necessary that both horizontal and vertical Sels | |
| 360 * of the input size are defined in the basic sela. | |
| 361 * (6) Note that we must always set or clear the border pixels | |
| 362 * before each operation, depending on the the b.c. | |
| 363 * (symmetric or asymmetric). | |
| 364 * (7) There are three cases: | |
| 365 * (a) pixd == null (result into new pixd) | |
| 366 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 367 * (c) pixd != pixs (puts result into existing pixd) | |
| 368 * (8) For clarity, if the case is known, use these patterns: | |
| 369 * (a) pixd = pixOpenBrickDwa(NULL, pixs, ...); | |
| 370 * (b) pixOpenBrickDwa(pixs, pixs, ...); | |
| 371 * (c) pixOpenBrickDwa(pixd, pixs, ...); | |
| 372 * (9) The size of the result is determined by pixs. | |
| 373 * (10) If either linear Sel is not found, this calls | |
| 374 * the appropriate decomposible function. | |
| 375 * </pre> | |
| 376 */ | |
| 377 PIX * | |
| 378 pixOpenBrickDwa(PIX *pixd, | |
| 379 PIX *pixs, | |
| 380 l_int32 hsize, | |
| 381 l_int32 vsize) | |
| 382 { | |
| 383 l_int32 found; | |
| 384 char *selnameh, *selnamev; | |
| 385 SELA *sela; | |
| 386 PIX *pixt1, *pixt2, *pixt3; | |
| 387 | |
| 388 if (!pixs) | |
| 389 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 390 if (pixGetDepth(pixs) != 1) | |
| 391 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 392 if (hsize < 1 || vsize < 1) | |
| 393 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 394 | |
| 395 if (hsize == 1 && vsize == 1) | |
| 396 return pixCopy(pixd, pixs); | |
| 397 | |
| 398 sela = selaAddBasic(NULL); | |
| 399 found = TRUE; | |
| 400 selnameh = selnamev = NULL; | |
| 401 if (hsize > 1) { | |
| 402 selnameh = selaGetBrickName(sela, hsize, 1); | |
| 403 if (!selnameh) found = FALSE; | |
| 404 } | |
| 405 if (vsize > 1) { | |
| 406 selnamev = selaGetBrickName(sela, 1, vsize); | |
| 407 if (!selnamev) found = FALSE; | |
| 408 } | |
| 409 selaDestroy(&sela); | |
| 410 if (!found) { | |
| 411 L_INFO("Calling the decomposable dwa function\n", __func__); | |
| 412 if (selnameh) LEPT_FREE(selnameh); | |
| 413 if (selnamev) LEPT_FREE(selnamev); | |
| 414 return pixOpenCompBrickDwa(pixd, pixs, hsize, vsize); | |
| 415 } | |
| 416 | |
| 417 pixt1 = pixAddBorder(pixs, 32, 0); | |
| 418 if (vsize == 1) { /* horizontal only */ | |
| 419 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnameh); | |
| 420 LEPT_FREE(selnameh); | |
| 421 } else if (hsize == 1) { /* vertical only */ | |
| 422 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnamev); | |
| 423 LEPT_FREE(selnamev); | |
| 424 } else { /* do separable */ | |
| 425 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh); | |
| 426 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev); | |
| 427 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh); | |
| 428 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev); | |
| 429 LEPT_FREE(selnameh); | |
| 430 LEPT_FREE(selnamev); | |
| 431 pixDestroy(&pixt3); | |
| 432 } | |
| 433 pixt3 = pixRemoveBorder(pixt2, 32); | |
| 434 pixDestroy(&pixt1); | |
| 435 pixDestroy(&pixt2); | |
| 436 | |
| 437 if (!pixd) | |
| 438 return pixt3; | |
| 439 | |
| 440 pixTransferAllData(pixd, &pixt3, 0, 0); | |
| 441 return pixd; | |
| 442 } | |
| 443 | |
| 444 | |
| 445 /*! | |
| 446 * \brief pixCloseBrickDwa() | |
| 447 * | |
| 448 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 449 * or different from pixs | |
| 450 * \param[in] pixs 1 bpp | |
| 451 * \param[in] hsize width of brick Sel | |
| 452 * \param[in] vsize height of brick Sel | |
| 453 * \return pixd | |
| 454 * | |
| 455 * <pre> | |
| 456 * Notes: | |
| 457 * (1) This is a 'safe' closing; we add an extra border of 32 OFF | |
| 458 * pixels for the standard asymmetric b.c. | |
| 459 * (2) These implement 2D brick Sels, using linear Sels generated | |
| 460 * with selaAddBasic(). | |
| 461 * (3) A brick Sel has hits for all elements. | |
| 462 * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) | |
| 463 * (5) Do separably if both hsize and vsize are > 1. | |
| 464 * (6) It is necessary that both horizontal and vertical Sels | |
| 465 * of the input size are defined in the basic sela. | |
| 466 * (7) Note that we must always set or clear the border pixels | |
| 467 * before each operation, depending on the the b.c. | |
| 468 * (symmetric or asymmetric). | |
| 469 * (8) There are three cases: | |
| 470 * (a) pixd == null (result into new pixd) | |
| 471 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 472 * (c) pixd != pixs (puts result into existing pixd) | |
| 473 * (9) For clarity, if the case is known, use these patterns: | |
| 474 * (a) pixd = pixCloseBrickDwa(NULL, pixs, ...); | |
| 475 * (b) pixCloseBrickDwa(pixs, pixs, ...); | |
| 476 * (c) pixCloseBrickDwa(pixd, pixs, ...); | |
| 477 * (10) The size of the result is determined by pixs. | |
| 478 * (11) If either linear Sel is not found, this calls | |
| 479 * the appropriate decomposible function. | |
| 480 * </pre> | |
| 481 */ | |
| 482 PIX * | |
| 483 pixCloseBrickDwa(PIX *pixd, | |
| 484 PIX *pixs, | |
| 485 l_int32 hsize, | |
| 486 l_int32 vsize) | |
| 487 { | |
| 488 l_int32 bordercolor, bordersize, found; | |
| 489 char *selnameh, *selnamev; | |
| 490 SELA *sela; | |
| 491 PIX *pixt1, *pixt2, *pixt3; | |
| 492 | |
| 493 if (!pixs) | |
| 494 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 495 if (pixGetDepth(pixs) != 1) | |
| 496 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 497 if (hsize < 1 || vsize < 1) | |
| 498 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 499 | |
| 500 if (hsize == 1 && vsize == 1) | |
| 501 return pixCopy(pixd, pixs); | |
| 502 | |
| 503 sela = selaAddBasic(NULL); | |
| 504 found = TRUE; | |
| 505 selnameh = selnamev = NULL; | |
| 506 if (hsize > 1) { | |
| 507 selnameh = selaGetBrickName(sela, hsize, 1); | |
| 508 if (!selnameh) found = FALSE; | |
| 509 } | |
| 510 if (vsize > 1) { | |
| 511 selnamev = selaGetBrickName(sela, 1, vsize); | |
| 512 if (!selnamev) found = FALSE; | |
| 513 } | |
| 514 selaDestroy(&sela); | |
| 515 if (!found) { | |
| 516 L_INFO("Calling the decomposable dwa function\n", __func__); | |
| 517 if (selnameh) LEPT_FREE(selnameh); | |
| 518 if (selnamev) LEPT_FREE(selnamev); | |
| 519 return pixCloseCompBrickDwa(pixd, pixs, hsize, vsize); | |
| 520 } | |
| 521 | |
| 522 /* For "safe closing" with ASYMMETRIC_MORPH_BC, we always need | |
| 523 * an extra 32 OFF pixels around the image (in addition to | |
| 524 * the 32 added pixels for all dwa operations), whereas with | |
| 525 * SYMMETRIC_MORPH_BC this is not necessary. */ | |
| 526 bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); | |
| 527 if (bordercolor == 0) /* asymmetric b.c. */ | |
| 528 bordersize = 64; | |
| 529 else /* symmetric b.c. */ | |
| 530 bordersize = 32; | |
| 531 pixt1 = pixAddBorder(pixs, bordersize, 0); | |
| 532 | |
| 533 if (vsize == 1) { /* horizontal only */ | |
| 534 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnameh); | |
| 535 LEPT_FREE(selnameh); | |
| 536 } else if (hsize == 1) { /* vertical only */ | |
| 537 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev); | |
| 538 LEPT_FREE(selnamev); | |
| 539 } else { /* do separable */ | |
| 540 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh); | |
| 541 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev); | |
| 542 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh); | |
| 543 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev); | |
| 544 LEPT_FREE(selnameh); | |
| 545 LEPT_FREE(selnamev); | |
| 546 pixDestroy(&pixt3); | |
| 547 } | |
| 548 pixt3 = pixRemoveBorder(pixt2, bordersize); | |
| 549 pixDestroy(&pixt1); | |
| 550 pixDestroy(&pixt2); | |
| 551 | |
| 552 if (!pixd) | |
| 553 return pixt3; | |
| 554 | |
| 555 pixTransferAllData(pixd, &pixt3, 0, 0); | |
| 556 return pixd; | |
| 557 } | |
| 558 | |
| 559 | |
| 560 /*-----------------------------------------------------------------* | |
| 561 * Binary composite morphological (dwa) ops with brick Sels * | |
| 562 *-----------------------------------------------------------------*/ | |
| 563 /*! | |
| 564 * \brief pixDilateCompBrickDwa() | |
| 565 * | |
| 566 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 567 * or different from pixs | |
| 568 * \param[in] pixs 1 bpp | |
| 569 * \param[in] hsize width of brick Sel | |
| 570 * \param[in] vsize height of brick Sel | |
| 571 * \return pixd | |
| 572 * | |
| 573 * <pre> | |
| 574 * Notes: | |
| 575 * (1) These implement a separable composite dilation with 2D brick Sels. | |
| 576 * (2) For efficiency, it may decompose each linear morphological | |
| 577 * operation into two (brick + comb). | |
| 578 * (3) A brick Sel has hits for all elements. | |
| 579 * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) | |
| 580 * (5) Do separably if both hsize and vsize are > 1. | |
| 581 * (6) It is necessary that both horizontal and vertical Sels | |
| 582 * of the input size are defined in the basic sela. | |
| 583 * (7) There are three cases: | |
| 584 * (a) pixd == null (result into new pixd) | |
| 585 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 586 * (c) pixd != pixs (puts result into existing pixd) | |
| 587 * (8) For clarity, if the case is known, use these patterns: | |
| 588 * (a) pixd = pixDilateCompBrickDwa(NULL, pixs, ...); | |
| 589 * (b) pixDilateCompBrickDwa(pixs, pixs, ...); | |
| 590 * (c) pixDilateCompBrickDwa(pixd, pixs, ...); | |
| 591 * (9) The size of pixd is determined by pixs. | |
| 592 * (10) CAUTION: both hsize and vsize are being decomposed. | |
| 593 * The decomposer chooses a product of sizes (call them | |
| 594 * 'terms') for each that is close to the input size, | |
| 595 * but not necessarily equal to it. It attempts to optimize: | |
| 596 * (a) for consistency with the input values: the product | |
| 597 * of terms is close to the input size | |
| 598 * (b) for efficiency of the operation: the sum of the | |
| 599 * terms is small; ideally about twice the square | |
| 600 * root of the input size. | |
| 601 * So, for example, if the input hsize = 37, which is | |
| 602 * a prime number, the decomposer will break this into two | |
| 603 * terms, 6 and 6, so that the net result is a dilation | |
| 604 * with hsize = 36. | |
| 605 * </pre> | |
| 606 */ | |
| 607 PIX * | |
| 608 pixDilateCompBrickDwa(PIX *pixd, | |
| 609 PIX *pixs, | |
| 610 l_int32 hsize, | |
| 611 l_int32 vsize) | |
| 612 { | |
| 613 char *selnameh1, *selnameh2, *selnamev1, *selnamev2; | |
| 614 l_int32 hsize1, hsize2, vsize1, vsize2; | |
| 615 PIX *pixt1, *pixt2, *pixt3; | |
| 616 | |
| 617 if (!pixs) | |
| 618 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 619 if (pixGetDepth(pixs) != 1) | |
| 620 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 621 if (hsize < 1 || vsize < 1) | |
| 622 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 623 if (hsize > 63 || vsize > 63) | |
| 624 return pixDilateCompBrickExtendDwa(pixd, pixs, hsize, vsize); | |
| 625 | |
| 626 if (hsize == 1 && vsize == 1) | |
| 627 return pixCopy(pixd, pixs); | |
| 628 | |
| 629 hsize1 = hsize2 = vsize1 = vsize2 = 1; | |
| 630 selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; | |
| 631 if (hsize > 1) | |
| 632 getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, | |
| 633 &selnameh2, NULL, NULL); | |
| 634 if (vsize > 1) | |
| 635 getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, | |
| 636 &selnamev1, &selnamev2); | |
| 637 | |
| 638 #if DEBUG_SEL_LOOKUP | |
| 639 lept_stderr("nameh1=%s, nameh2=%s, namev1=%s, namev2=%s\n", | |
| 640 selnameh1, selnameh2, selnamev1, selnamev2); | |
| 641 lept_stderr("hsize1=%d, hsize2=%d, vsize1=%d, vsize2=%d\n", | |
| 642 hsize1, hsize2, vsize1, vsize2); | |
| 643 #endif /* DEBUG_SEL_LOOKUP */ | |
| 644 | |
| 645 pixt1 = pixAddBorder(pixs, 64, 0); | |
| 646 if (vsize == 1) { | |
| 647 if (hsize2 == 1) { | |
| 648 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); | |
| 649 } else { | |
| 650 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); | |
| 651 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); | |
| 652 pixDestroy(&pixt3); | |
| 653 } | |
| 654 } else if (hsize == 1) { | |
| 655 if (vsize2 == 1) { | |
| 656 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1); | |
| 657 } else { | |
| 658 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1); | |
| 659 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnamev2); | |
| 660 pixDestroy(&pixt3); | |
| 661 } | |
| 662 } else { /* vsize and hsize both > 1 */ | |
| 663 if (hsize2 == 1) { | |
| 664 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); | |
| 665 } else { | |
| 666 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); | |
| 667 pixt3 = pixFMorphopGen_2(NULL, pixt2, L_MORPH_DILATE, selnameh2); | |
| 668 pixDestroy(&pixt2); | |
| 669 } | |
| 670 if (vsize2 == 1) { | |
| 671 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); | |
| 672 } else { | |
| 673 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); | |
| 674 pixFMorphopGen_2(pixt2, pixt2, L_MORPH_DILATE, selnamev2); | |
| 675 } | |
| 676 pixDestroy(&pixt3); | |
| 677 } | |
| 678 pixDestroy(&pixt1); | |
| 679 pixt1 = pixRemoveBorder(pixt2, 64); | |
| 680 pixDestroy(&pixt2); | |
| 681 if (selnameh1) LEPT_FREE(selnameh1); | |
| 682 if (selnameh2) LEPT_FREE(selnameh2); | |
| 683 if (selnamev1) LEPT_FREE(selnamev1); | |
| 684 if (selnamev2) LEPT_FREE(selnamev2); | |
| 685 | |
| 686 if (!pixd) | |
| 687 return pixt1; | |
| 688 | |
| 689 pixTransferAllData(pixd, &pixt1, 0, 0); | |
| 690 return pixd; | |
| 691 } | |
| 692 | |
| 693 | |
| 694 /*! | |
| 695 * \brief pixErodeCompBrickDwa() | |
| 696 * | |
| 697 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 698 * or different from pixs | |
| 699 * \param[in] pixs 1 bpp | |
| 700 * \param[in] hsize width of brick Sel | |
| 701 * \param[in] vsize height of brick Sel | |
| 702 * \return pixd | |
| 703 * | |
| 704 * <pre> | |
| 705 * Notes: | |
| 706 * (1) These implement a separable composite erosion with 2D brick Sels. | |
| 707 * (2) For efficiency, it may decompose each linear morphological | |
| 708 * operation into two (brick + comb). | |
| 709 * (3) A brick Sel has hits for all elements. | |
| 710 * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) | |
| 711 * (5) Do separably if both hsize and vsize are > 1. | |
| 712 * (6) It is necessary that both horizontal and vertical Sels | |
| 713 * of the input size are defined in the basic sela. | |
| 714 * (7) There are three cases: | |
| 715 * (a) pixd == null (result into new pixd) | |
| 716 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 717 * (c) pixd != pixs (puts result into existing pixd) | |
| 718 * (8) For clarity, if the case is known, use these patterns: | |
| 719 * (a) pixd = pixErodeCompBrickDwa(NULL, pixs, ...); | |
| 720 * (b) pixErodeCompBrickDwa(pixs, pixs, ...); | |
| 721 * (c) pixErodeCompBrickDwa(pixd, pixs, ...); | |
| 722 * (9) The size of pixd is determined by pixs. | |
| 723 * (10) CAUTION: both hsize and vsize are being decomposed. | |
| 724 * The decomposer chooses a product of sizes (call them | |
| 725 * 'terms') for each that is close to the input size, | |
| 726 * but not necessarily equal to it. It attempts to optimize: | |
| 727 * (a) for consistency with the input values: the product | |
| 728 * of terms is close to the input size | |
| 729 * (b) for efficiency of the operation: the sum of the | |
| 730 * terms is small; ideally about twice the square | |
| 731 * root of the input size. | |
| 732 * So, for example, if the input hsize = 37, which is | |
| 733 * a prime number, the decomposer will break this into two | |
| 734 * terms, 6 and 6, so that the net result is a dilation | |
| 735 * with hsize = 36. | |
| 736 * </pre> | |
| 737 */ | |
| 738 PIX * | |
| 739 pixErodeCompBrickDwa(PIX *pixd, | |
| 740 PIX *pixs, | |
| 741 l_int32 hsize, | |
| 742 l_int32 vsize) | |
| 743 { | |
| 744 char *selnameh1, *selnameh2, *selnamev1, *selnamev2; | |
| 745 l_int32 hsize1, hsize2, vsize1, vsize2, bordercolor; | |
| 746 PIX *pixt1, *pixt2, *pixt3; | |
| 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 if (hsize > 63 || vsize > 63) | |
| 755 return pixErodeCompBrickExtendDwa(pixd, pixs, hsize, vsize); | |
| 756 | |
| 757 if (hsize == 1 && vsize == 1) | |
| 758 return pixCopy(pixd, pixs); | |
| 759 | |
| 760 hsize1 = hsize2 = vsize1 = vsize2 = 1; | |
| 761 selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; | |
| 762 if (hsize > 1) | |
| 763 getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, | |
| 764 &selnameh2, NULL, NULL); | |
| 765 if (vsize > 1) | |
| 766 getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, | |
| 767 &selnamev1, &selnamev2); | |
| 768 | |
| 769 /* For symmetric b.c., bordercolor == 1 for erosion */ | |
| 770 bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); | |
| 771 pixt1 = pixAddBorder(pixs, 64, bordercolor); | |
| 772 | |
| 773 if (vsize == 1) { | |
| 774 if (hsize2 == 1) { | |
| 775 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); | |
| 776 } else { | |
| 777 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); | |
| 778 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); | |
| 779 pixDestroy(&pixt3); | |
| 780 } | |
| 781 } else if (hsize == 1) { | |
| 782 if (vsize2 == 1) { | |
| 783 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); | |
| 784 } else { | |
| 785 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); | |
| 786 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnamev2); | |
| 787 pixDestroy(&pixt3); | |
| 788 } | |
| 789 } else { /* vsize and hsize both > 1 */ | |
| 790 if (hsize2 == 1) { | |
| 791 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); | |
| 792 } else { | |
| 793 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); | |
| 794 pixt3 = pixFMorphopGen_2(NULL, pixt2, L_MORPH_ERODE, selnameh2); | |
| 795 pixDestroy(&pixt2); | |
| 796 } | |
| 797 if (vsize2 == 1) { | |
| 798 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); | |
| 799 } else { | |
| 800 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); | |
| 801 pixFMorphopGen_2(pixt2, pixt2, L_MORPH_ERODE, selnamev2); | |
| 802 } | |
| 803 pixDestroy(&pixt3); | |
| 804 } | |
| 805 pixDestroy(&pixt1); | |
| 806 pixt1 = pixRemoveBorder(pixt2, 64); | |
| 807 pixDestroy(&pixt2); | |
| 808 if (selnameh1) LEPT_FREE(selnameh1); | |
| 809 if (selnameh2) LEPT_FREE(selnameh2); | |
| 810 if (selnamev1) LEPT_FREE(selnamev1); | |
| 811 if (selnamev2) LEPT_FREE(selnamev2); | |
| 812 | |
| 813 if (!pixd) | |
| 814 return pixt1; | |
| 815 | |
| 816 pixTransferAllData(pixd, &pixt1, 0, 0); | |
| 817 return pixd; | |
| 818 } | |
| 819 | |
| 820 | |
| 821 /*! | |
| 822 * \brief pixOpenCompBrickDwa() | |
| 823 * | |
| 824 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 825 * or different from pixs | |
| 826 * \param[in] pixs 1 bpp | |
| 827 * \param[in] hsize width of brick Sel | |
| 828 * \param[in] vsize height of brick Sel | |
| 829 * \return pixd | |
| 830 * | |
| 831 * <pre> | |
| 832 * Notes: | |
| 833 * (1) These implement a separable composite opening with 2D brick Sels. | |
| 834 * (2) For efficiency, it may decompose each linear morphological | |
| 835 * operation into two (brick + comb). | |
| 836 * (3) A brick Sel has hits for all elements. | |
| 837 * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) | |
| 838 * (5) Do separably if both hsize and vsize are > 1. | |
| 839 * (6) It is necessary that both horizontal and vertical Sels | |
| 840 * of the input size are defined in the basic sela. | |
| 841 * (7) There are three cases: | |
| 842 * (a) pixd == null (result into new pixd) | |
| 843 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 844 * (c) pixd != pixs (puts result into existing pixd) | |
| 845 * (8) For clarity, if the case is known, use these patterns: | |
| 846 * (a) pixd = pixOpenCompBrickDwa(NULL, pixs, ...); | |
| 847 * (b) pixOpenCompBrickDwa(pixs, pixs, ...); | |
| 848 * (c) pixOpenCompBrickDwa(pixd, pixs, ...); | |
| 849 * (9) The size of pixd is determined by pixs. | |
| 850 * (10) CAUTION: both hsize and vsize are being decomposed. | |
| 851 * The decomposer chooses a product of sizes (call them | |
| 852 * 'terms') for each that is close to the input size, | |
| 853 * but not necessarily equal to it. It attempts to optimize: | |
| 854 * (a) for consistency with the input values: the product | |
| 855 * of terms is close to the input size | |
| 856 * (b) for efficiency of the operation: the sum of the | |
| 857 * terms is small; ideally about twice the square | |
| 858 * root of the input size. | |
| 859 * So, for example, if the input hsize = 37, which is | |
| 860 * a prime number, the decomposer will break this into two | |
| 861 * terms, 6 and 6, so that the net result is a dilation | |
| 862 * with hsize = 36. | |
| 863 * </pre> | |
| 864 */ | |
| 865 PIX * | |
| 866 pixOpenCompBrickDwa(PIX *pixd, | |
| 867 PIX *pixs, | |
| 868 l_int32 hsize, | |
| 869 l_int32 vsize) | |
| 870 { | |
| 871 char *selnameh1, *selnameh2, *selnamev1, *selnamev2; | |
| 872 l_int32 hsize1, hsize2, vsize1, vsize2, bordercolor; | |
| 873 PIX *pixt1, *pixt2, *pixt3; | |
| 874 | |
| 875 if (!pixs) | |
| 876 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 877 if (pixGetDepth(pixs) != 1) | |
| 878 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 879 if (hsize < 1 || vsize < 1) | |
| 880 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 881 if (hsize > 63 || vsize > 63) | |
| 882 return pixOpenCompBrickExtendDwa(pixd, pixs, hsize, vsize); | |
| 883 | |
| 884 if (hsize == 1 && vsize == 1) | |
| 885 return pixCopy(pixd, pixs); | |
| 886 | |
| 887 hsize1 = hsize2 = vsize1 = vsize2 = 1; | |
| 888 selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; | |
| 889 if (hsize > 1) | |
| 890 getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, | |
| 891 &selnameh2, NULL, NULL); | |
| 892 if (vsize > 1) | |
| 893 getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, | |
| 894 &selnamev1, &selnamev2); | |
| 895 | |
| 896 /* For symmetric b.c., initialize erosion with bordercolor == 1 */ | |
| 897 bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); | |
| 898 pixt1 = pixAddBorder(pixs, 64, bordercolor); | |
| 899 | |
| 900 if (vsize == 1) { | |
| 901 if (hsize2 == 1) { | |
| 902 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); | |
| 903 if (bordercolor == 1) | |
| 904 pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); | |
| 905 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnameh1); | |
| 906 } else { | |
| 907 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); | |
| 908 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); | |
| 909 if (bordercolor == 1) | |
| 910 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); | |
| 911 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1); | |
| 912 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnameh2); | |
| 913 } | |
| 914 } else if (hsize == 1) { | |
| 915 if (vsize2 == 1) { | |
| 916 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); | |
| 917 if (bordercolor == 1) | |
| 918 pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); | |
| 919 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); | |
| 920 } else { | |
| 921 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); | |
| 922 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnamev2); | |
| 923 if (bordercolor == 1) | |
| 924 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); | |
| 925 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); | |
| 926 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); | |
| 927 } | |
| 928 } else { /* vsize and hsize both > 1 */ | |
| 929 if (hsize2 == 1 && vsize2 == 1) { | |
| 930 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); | |
| 931 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); | |
| 932 if (bordercolor == 1) | |
| 933 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); | |
| 934 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1); | |
| 935 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev1); | |
| 936 } else if (vsize2 == 1) { | |
| 937 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); | |
| 938 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); | |
| 939 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); | |
| 940 if (bordercolor == 1) | |
| 941 pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); | |
| 942 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnameh1); | |
| 943 pixFMorphopGen_2(pixt3, pixt2, L_MORPH_DILATE, selnameh2); | |
| 944 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev1); | |
| 945 } else if (hsize2 == 1) { | |
| 946 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); | |
| 947 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); | |
| 948 pixFMorphopGen_2(pixt3, pixt2, L_MORPH_ERODE, selnamev2); | |
| 949 if (bordercolor == 1) | |
| 950 pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); | |
| 951 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnameh1); | |
| 952 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); | |
| 953 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); | |
| 954 } else { /* both directions are combed */ | |
| 955 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); | |
| 956 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); | |
| 957 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); | |
| 958 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); | |
| 959 if (bordercolor == 1) | |
| 960 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); | |
| 961 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1); | |
| 962 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnameh2); | |
| 963 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); | |
| 964 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); | |
| 965 } | |
| 966 } | |
| 967 pixDestroy(&pixt3); | |
| 968 | |
| 969 pixDestroy(&pixt1); | |
| 970 pixt1 = pixRemoveBorder(pixt2, 64); | |
| 971 pixDestroy(&pixt2); | |
| 972 if (selnameh1) LEPT_FREE(selnameh1); | |
| 973 if (selnameh2) LEPT_FREE(selnameh2); | |
| 974 if (selnamev1) LEPT_FREE(selnamev1); | |
| 975 if (selnamev2) LEPT_FREE(selnamev2); | |
| 976 | |
| 977 if (!pixd) | |
| 978 return pixt1; | |
| 979 | |
| 980 pixTransferAllData(pixd, &pixt1, 0, 0); | |
| 981 return pixd; | |
| 982 } | |
| 983 | |
| 984 | |
| 985 /*! | |
| 986 * \brief pixCloseCompBrickDwa() | |
| 987 * | |
| 988 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 989 * or different from pixs | |
| 990 * \param[in] pixs 1 bpp | |
| 991 * \param[in] hsize width of brick Sel | |
| 992 * \param[in] vsize height of brick Sel | |
| 993 * \return pixd | |
| 994 * | |
| 995 * <pre> | |
| 996 * Notes: | |
| 997 * (1) This implements a separable composite safe closing with 2D | |
| 998 * brick Sels. | |
| 999 * (2) For efficiency, it may decompose each linear morphological | |
| 1000 * operation into two (brick + comb). | |
| 1001 * (3) A brick Sel has hits for all elements. | |
| 1002 * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) | |
| 1003 * (5) Do separably if both hsize and vsize are > 1. | |
| 1004 * (6) It is necessary that both horizontal and vertical Sels | |
| 1005 * of the input size are defined in the basic sela. | |
| 1006 * (7) There are three cases: | |
| 1007 * (a) pixd == null (result into new pixd) | |
| 1008 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 1009 * (c) pixd != pixs (puts result into existing pixd) | |
| 1010 * (8) For clarity, if the case is known, use these patterns: | |
| 1011 * (a) pixd = pixCloseCompBrickDwa(NULL, pixs, ...); | |
| 1012 * (b) pixCloseCompBrickDwa(pixs, pixs, ...); | |
| 1013 * (c) pixCloseCompBrickDwa(pixd, pixs, ...); | |
| 1014 * (9) The size of pixd is determined by pixs. | |
| 1015 * (10) CAUTION: both hsize and vsize are being decomposed. | |
| 1016 * The decomposer chooses a product of sizes (call them | |
| 1017 * 'terms') for each that is close to the input size, | |
| 1018 * but not necessarily equal to it. It attempts to optimize: | |
| 1019 * (a) for consistency with the input values: the product | |
| 1020 * of terms is close to the input size | |
| 1021 * (b) for efficiency of the operation: the sum of the | |
| 1022 * terms is small; ideally about twice the square | |
| 1023 * root of the input size. | |
| 1024 * So, for example, if the input hsize = 37, which is | |
| 1025 * a prime number, the decomposer will break this into two | |
| 1026 * terms, 6 and 6, so that the net result is a dilation | |
| 1027 * with hsize = 36. | |
| 1028 * </pre> | |
| 1029 */ | |
| 1030 PIX * | |
| 1031 pixCloseCompBrickDwa(PIX *pixd, | |
| 1032 PIX *pixs, | |
| 1033 l_int32 hsize, | |
| 1034 l_int32 vsize) | |
| 1035 { | |
| 1036 char *selnameh1, *selnameh2, *selnamev1, *selnamev2; | |
| 1037 l_int32 hsize1, hsize2, vsize1, vsize2, setborder; | |
| 1038 PIX *pixt1, *pixt2, *pixt3; | |
| 1039 | |
| 1040 if (!pixs) | |
| 1041 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 1042 if (pixGetDepth(pixs) != 1) | |
| 1043 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 1044 if (hsize < 1 || vsize < 1) | |
| 1045 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 1046 if (hsize > 63 || vsize > 63) | |
| 1047 return pixCloseCompBrickExtendDwa(pixd, pixs, hsize, vsize); | |
| 1048 | |
| 1049 if (hsize == 1 && vsize == 1) | |
| 1050 return pixCopy(pixd, pixs); | |
| 1051 | |
| 1052 hsize1 = hsize2 = vsize1 = vsize2 = 1; | |
| 1053 selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; | |
| 1054 if (hsize > 1) | |
| 1055 getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, | |
| 1056 &selnameh2, NULL, NULL); | |
| 1057 if (vsize > 1) | |
| 1058 getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, | |
| 1059 &selnamev1, &selnamev2); | |
| 1060 | |
| 1061 pixt3 = NULL; | |
| 1062 /* For symmetric b.c., PIX_SET border for erosions */ | |
| 1063 setborder = getMorphBorderPixelColor(L_MORPH_ERODE, 1); | |
| 1064 pixt1 = pixAddBorder(pixs, 64, 0); | |
| 1065 | |
| 1066 if (vsize == 1) { | |
| 1067 if (hsize2 == 1) { | |
| 1068 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnameh1); | |
| 1069 } else { | |
| 1070 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); | |
| 1071 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); | |
| 1072 if (setborder == 1) | |
| 1073 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); | |
| 1074 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1); | |
| 1075 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnameh2); | |
| 1076 } | |
| 1077 } else if (hsize == 1) { | |
| 1078 if (vsize2 == 1) { | |
| 1079 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev1); | |
| 1080 } else { | |
| 1081 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1); | |
| 1082 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnamev2); | |
| 1083 if (setborder == 1) | |
| 1084 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); | |
| 1085 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); | |
| 1086 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); | |
| 1087 } | |
| 1088 } else { /* vsize and hsize both > 1 */ | |
| 1089 if (hsize2 == 1 && vsize2 == 1) { | |
| 1090 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); | |
| 1091 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); | |
| 1092 if (setborder == 1) | |
| 1093 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); | |
| 1094 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1); | |
| 1095 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev1); | |
| 1096 } else if (vsize2 == 1) { | |
| 1097 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); | |
| 1098 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); | |
| 1099 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); | |
| 1100 if (setborder == 1) | |
| 1101 pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_SET); | |
| 1102 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnameh1); | |
| 1103 pixFMorphopGen_2(pixt3, pixt2, L_MORPH_ERODE, selnameh2); | |
| 1104 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev1); | |
| 1105 } else if (hsize2 == 1) { | |
| 1106 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); | |
| 1107 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); | |
| 1108 pixFMorphopGen_2(pixt3, pixt2, L_MORPH_DILATE, selnamev2); | |
| 1109 if (setborder == 1) | |
| 1110 pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_SET); | |
| 1111 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnameh1); | |
| 1112 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); | |
| 1113 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); | |
| 1114 } else { /* both directions are combed */ | |
| 1115 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); | |
| 1116 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); | |
| 1117 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); | |
| 1118 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); | |
| 1119 if (setborder == 1) | |
| 1120 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); | |
| 1121 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1); | |
| 1122 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnameh2); | |
| 1123 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); | |
| 1124 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); | |
| 1125 } | |
| 1126 } | |
| 1127 pixDestroy(&pixt3); | |
| 1128 | |
| 1129 pixDestroy(&pixt1); | |
| 1130 pixt1 = pixRemoveBorder(pixt2, 64); | |
| 1131 pixDestroy(&pixt2); | |
| 1132 if (selnameh1) LEPT_FREE(selnameh1); | |
| 1133 if (selnameh2) LEPT_FREE(selnameh2); | |
| 1134 if (selnamev1) LEPT_FREE(selnamev1); | |
| 1135 if (selnamev2) LEPT_FREE(selnamev2); | |
| 1136 | |
| 1137 if (!pixd) | |
| 1138 return pixt1; | |
| 1139 | |
| 1140 pixTransferAllData(pixd, &pixt1, 0, 0); | |
| 1141 return pixd; | |
| 1142 } | |
| 1143 | |
| 1144 | |
| 1145 /*--------------------------------------------------------------------------* | |
| 1146 * Binary expanded composite morphological (dwa) ops with brick Sels * | |
| 1147 *--------------------------------------------------------------------------*/ | |
| 1148 /*! | |
| 1149 * \brief pixDilateCompBrickExtendDwa() | |
| 1150 * | |
| 1151 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 1152 * or different from pixs | |
| 1153 * \param[in] pixs 1 bpp | |
| 1154 * \param[in] hsize width of brick Sel | |
| 1155 * \param[in] vsize height of brick Sel | |
| 1156 * \return pixd | |
| 1157 * | |
| 1158 * <pre> | |
| 1159 * Notes: | |
| 1160 * (1) Ankur Jain suggested and implemented extending the composite | |
| 1161 * DWA operations beyond the 63 pixel limit. This is a | |
| 1162 * simplified and approximate implementation of the extension. | |
| 1163 * This allows arbitrary Dwa morph operations using brick Sels, | |
| 1164 * by decomposing the horizontal and vertical dilations into | |
| 1165 * a sequence of 63-element dilations plus a dilation of size | |
| 1166 * between 3 and 62. | |
| 1167 * (2) The 63-element dilations are exact, whereas the extra dilation | |
| 1168 * is approximate, because the underlying decomposition is | |
| 1169 * in pixDilateCompBrickDwa(). See there for further details. | |
| 1170 * (3) There are three cases: | |
| 1171 * (a) pixd == null (result into new pixd) | |
| 1172 * (b) pixd == pixs (in-place; writes result back to pixs) | |
| 1173 * (c) pixd != pixs (puts result into existing pixd) | |
| 1174 * (4) There is no need to call this directly: pixDilateCompBrickDwa() | |
| 1175 * calls this function if either brick dimension exceeds 63. | |
| 1176 * </pre> | |
| 1177 */ | |
| 1178 PIX * | |
| 1179 pixDilateCompBrickExtendDwa(PIX *pixd, | |
| 1180 PIX *pixs, | |
| 1181 l_int32 hsize, | |
| 1182 l_int32 vsize) | |
| 1183 { | |
| 1184 l_int32 i, nops, nh, extrah, nv, extrav; | |
| 1185 PIX *pixt1, *pixt2, *pixt3; | |
| 1186 | |
| 1187 if (!pixs) | |
| 1188 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 1189 if (pixGetDepth(pixs) != 1) | |
| 1190 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 1191 if (hsize < 1 || vsize < 1) | |
| 1192 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 1193 | |
| 1194 if (hsize < 64 && vsize < 64) | |
| 1195 return pixDilateCompBrickDwa(pixd, pixs, hsize, vsize); | |
| 1196 | |
| 1197 if (hsize > 63) | |
| 1198 getExtendedCompositeParameters(hsize, &nh, &extrah, NULL); | |
| 1199 if (vsize > 63) | |
| 1200 getExtendedCompositeParameters(vsize, &nv, &extrav, NULL); | |
| 1201 | |
| 1202 /* Horizontal dilation first: pixs --> pixt2. Do not alter pixs. */ | |
| 1203 pixt1 = pixCreateTemplate(pixs); /* temp image */ | |
| 1204 if (hsize == 1) { | |
| 1205 pixt2 = pixClone(pixs); | |
| 1206 } else if (hsize < 64) { | |
| 1207 pixt2 = pixDilateCompBrickDwa(NULL, pixs, hsize, 1); | |
| 1208 } else if (hsize == 64) { /* approximate */ | |
| 1209 pixt2 = pixDilateCompBrickDwa(NULL, pixs, 63, 1); | |
| 1210 } else { | |
| 1211 nops = (extrah < 3) ? nh : nh + 1; | |
| 1212 if (nops & 1) { /* odd */ | |
| 1213 if (extrah > 2) | |
| 1214 pixt2 = pixDilateCompBrickDwa(NULL, pixs, extrah, 1); | |
| 1215 else | |
| 1216 pixt2 = pixDilateCompBrickDwa(NULL, pixs, 63, 1); | |
| 1217 for (i = 0; i < nops / 2; i++) { | |
| 1218 pixDilateCompBrickDwa(pixt1, pixt2, 63, 1); | |
| 1219 pixDilateCompBrickDwa(pixt2, pixt1, 63, 1); | |
| 1220 } | |
| 1221 } else { /* nops even */ | |
| 1222 if (extrah > 2) { | |
| 1223 pixDilateCompBrickDwa(pixt1, pixs, extrah, 1); | |
| 1224 pixt2 = pixDilateCompBrickDwa(NULL, pixt1, 63, 1); | |
| 1225 } else { /* they're all 63s */ | |
| 1226 pixDilateCompBrickDwa(pixt1, pixs, 63, 1); | |
| 1227 pixt2 = pixDilateCompBrickDwa(NULL, pixt1, 63, 1); | |
| 1228 } | |
| 1229 for (i = 0; i < nops / 2 - 1; i++) { | |
| 1230 pixDilateCompBrickDwa(pixt1, pixt2, 63, 1); | |
| 1231 pixDilateCompBrickDwa(pixt2, pixt1, 63, 1); | |
| 1232 } | |
| 1233 } | |
| 1234 } | |
| 1235 | |
| 1236 /* Vertical dilation: pixt2 --> pixt3. */ | |
| 1237 if (vsize == 1) { | |
| 1238 pixt3 = pixClone(pixt2); | |
| 1239 } else if (vsize < 64) { | |
| 1240 pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, vsize); | |
| 1241 } else if (vsize == 64) { /* approximate */ | |
| 1242 pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, 63); | |
| 1243 } else { | |
| 1244 nops = (extrav < 3) ? nv : nv + 1; | |
| 1245 if (nops & 1) { /* odd */ | |
| 1246 if (extrav > 2) | |
| 1247 pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, extrav); | |
| 1248 else | |
| 1249 pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, 63); | |
| 1250 for (i = 0; i < nops / 2; i++) { | |
| 1251 pixDilateCompBrickDwa(pixt1, pixt3, 1, 63); | |
| 1252 pixDilateCompBrickDwa(pixt3, pixt1, 1, 63); | |
| 1253 } | |
| 1254 } else { /* nops even */ | |
| 1255 if (extrav > 2) { | |
| 1256 pixDilateCompBrickDwa(pixt1, pixt2, 1, extrav); | |
| 1257 pixt3 = pixDilateCompBrickDwa(NULL, pixt1, 1, 63); | |
| 1258 } else { /* they're all 63s */ | |
| 1259 pixDilateCompBrickDwa(pixt1, pixt2, 1, 63); | |
| 1260 pixt3 = pixDilateCompBrickDwa(NULL, pixt1, 1, 63); | |
| 1261 } | |
| 1262 for (i = 0; i < nops / 2 - 1; i++) { | |
| 1263 pixDilateCompBrickDwa(pixt1, pixt3, 1, 63); | |
| 1264 pixDilateCompBrickDwa(pixt3, pixt1, 1, 63); | |
| 1265 } | |
| 1266 } | |
| 1267 } | |
| 1268 pixDestroy(&pixt1); | |
| 1269 pixDestroy(&pixt2); | |
| 1270 | |
| 1271 if (!pixd) | |
| 1272 return pixt3; | |
| 1273 | |
| 1274 pixTransferAllData(pixd, &pixt3, 0, 0); | |
| 1275 return pixd; | |
| 1276 } | |
| 1277 | |
| 1278 | |
| 1279 /*! | |
| 1280 * \brief pixErodeCompBrickExtendDwa() | |
| 1281 * | |
| 1282 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 1283 * or different from pixs | |
| 1284 * \param[in] pixs 1 bpp | |
| 1285 * \param[in] hsize width of brick Sel | |
| 1286 * \param[in] vsize height of brick Sel | |
| 1287 * \return pixd | |
| 1288 * | |
| 1289 * <pre> | |
| 1290 * Notes: | |
| 1291 * (1) See pixDilateCompBrickExtendDwa() for usage. | |
| 1292 * (2) There is no need to call this directly: pixErodeCompBrickDwa() | |
| 1293 * calls this function if either brick dimension exceeds 63. | |
| 1294 * </pre> | |
| 1295 */ | |
| 1296 PIX * | |
| 1297 pixErodeCompBrickExtendDwa(PIX *pixd, | |
| 1298 PIX *pixs, | |
| 1299 l_int32 hsize, | |
| 1300 l_int32 vsize) | |
| 1301 { | |
| 1302 l_int32 i, nops, nh, extrah, nv, extrav; | |
| 1303 PIX *pixt1, *pixt2, *pixt3; | |
| 1304 | |
| 1305 if (!pixs) | |
| 1306 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 1307 if (pixGetDepth(pixs) != 1) | |
| 1308 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 1309 if (hsize < 1 || vsize < 1) | |
| 1310 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 1311 | |
| 1312 if (hsize < 64 && vsize < 64) | |
| 1313 return pixErodeCompBrickDwa(pixd, pixs, hsize, vsize); | |
| 1314 | |
| 1315 if (hsize > 63) | |
| 1316 getExtendedCompositeParameters(hsize, &nh, &extrah, NULL); | |
| 1317 if (vsize > 63) | |
| 1318 getExtendedCompositeParameters(vsize, &nv, &extrav, NULL); | |
| 1319 | |
| 1320 /* Horizontal erosion first: pixs --> pixt2. Do not alter pixs. */ | |
| 1321 pixt1 = pixCreateTemplate(pixs); /* temp image */ | |
| 1322 if (hsize == 1) { | |
| 1323 pixt2 = pixClone(pixs); | |
| 1324 } else if (hsize < 64) { | |
| 1325 pixt2 = pixErodeCompBrickDwa(NULL, pixs, hsize, 1); | |
| 1326 } else if (hsize == 64) { /* approximate */ | |
| 1327 pixt2 = pixErodeCompBrickDwa(NULL, pixs, 63, 1); | |
| 1328 } else { | |
| 1329 nops = (extrah < 3) ? nh : nh + 1; | |
| 1330 if (nops & 1) { /* odd */ | |
| 1331 if (extrah > 2) | |
| 1332 pixt2 = pixErodeCompBrickDwa(NULL, pixs, extrah, 1); | |
| 1333 else | |
| 1334 pixt2 = pixErodeCompBrickDwa(NULL, pixs, 63, 1); | |
| 1335 for (i = 0; i < nops / 2; i++) { | |
| 1336 pixErodeCompBrickDwa(pixt1, pixt2, 63, 1); | |
| 1337 pixErodeCompBrickDwa(pixt2, pixt1, 63, 1); | |
| 1338 } | |
| 1339 } else { /* nops even */ | |
| 1340 if (extrah > 2) { | |
| 1341 pixErodeCompBrickDwa(pixt1, pixs, extrah, 1); | |
| 1342 pixt2 = pixErodeCompBrickDwa(NULL, pixt1, 63, 1); | |
| 1343 } else { /* they're all 63s */ | |
| 1344 pixErodeCompBrickDwa(pixt1, pixs, 63, 1); | |
| 1345 pixt2 = pixErodeCompBrickDwa(NULL, pixt1, 63, 1); | |
| 1346 } | |
| 1347 for (i = 0; i < nops / 2 - 1; i++) { | |
| 1348 pixErodeCompBrickDwa(pixt1, pixt2, 63, 1); | |
| 1349 pixErodeCompBrickDwa(pixt2, pixt1, 63, 1); | |
| 1350 } | |
| 1351 } | |
| 1352 } | |
| 1353 | |
| 1354 /* Vertical erosion: pixt2 --> pixt3. */ | |
| 1355 if (vsize == 1) { | |
| 1356 pixt3 = pixClone(pixt2); | |
| 1357 } else if (vsize < 64) { | |
| 1358 pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, vsize); | |
| 1359 } else if (vsize == 64) { /* approximate */ | |
| 1360 pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, 63); | |
| 1361 } else { | |
| 1362 nops = (extrav < 3) ? nv : nv + 1; | |
| 1363 if (nops & 1) { /* odd */ | |
| 1364 if (extrav > 2) | |
| 1365 pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, extrav); | |
| 1366 else | |
| 1367 pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, 63); | |
| 1368 for (i = 0; i < nops / 2; i++) { | |
| 1369 pixErodeCompBrickDwa(pixt1, pixt3, 1, 63); | |
| 1370 pixErodeCompBrickDwa(pixt3, pixt1, 1, 63); | |
| 1371 } | |
| 1372 } else { /* nops even */ | |
| 1373 if (extrav > 2) { | |
| 1374 pixErodeCompBrickDwa(pixt1, pixt2, 1, extrav); | |
| 1375 pixt3 = pixErodeCompBrickDwa(NULL, pixt1, 1, 63); | |
| 1376 } else { /* they're all 63s */ | |
| 1377 pixErodeCompBrickDwa(pixt1, pixt2, 1, 63); | |
| 1378 pixt3 = pixErodeCompBrickDwa(NULL, pixt1, 1, 63); | |
| 1379 } | |
| 1380 for (i = 0; i < nops / 2 - 1; i++) { | |
| 1381 pixErodeCompBrickDwa(pixt1, pixt3, 1, 63); | |
| 1382 pixErodeCompBrickDwa(pixt3, pixt1, 1, 63); | |
| 1383 } | |
| 1384 } | |
| 1385 } | |
| 1386 pixDestroy(&pixt1); | |
| 1387 pixDestroy(&pixt2); | |
| 1388 | |
| 1389 if (!pixd) | |
| 1390 return pixt3; | |
| 1391 | |
| 1392 pixTransferAllData(pixd, &pixt3, 0, 0); | |
| 1393 return pixd; | |
| 1394 } | |
| 1395 | |
| 1396 | |
| 1397 /*! | |
| 1398 * \brief pixOpenCompBrickExtendDwa() | |
| 1399 * | |
| 1400 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 1401 * or different from pixs | |
| 1402 * \param[in] pixs 1 bpp | |
| 1403 * \param[in] hsize width of brick Sel | |
| 1404 * \param[in] vsize height of brick Sel | |
| 1405 * \return pixd | |
| 1406 * | |
| 1407 * <pre> | |
| 1408 * Notes: | |
| 1409 * 1) There are three cases: | |
| 1410 * a) pixd == null (result into new pixd | |
| 1411 * b) pixd == pixs (in-place; writes result back to pixs | |
| 1412 * c) pixd != pixs (puts result into existing pixd | |
| 1413 * 2) There is no need to call this directly: pixOpenCompBrickDwa( | |
| 1414 * calls this function if either brick dimension exceeds 63. | |
| 1415 * </pre> | |
| 1416 */ | |
| 1417 PIX * | |
| 1418 pixOpenCompBrickExtendDwa(PIX *pixd, | |
| 1419 PIX *pixs, | |
| 1420 l_int32 hsize, | |
| 1421 l_int32 vsize) | |
| 1422 { | |
| 1423 PIX *pixt; | |
| 1424 | |
| 1425 if (!pixs) | |
| 1426 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 1427 if (pixGetDepth(pixs) != 1) | |
| 1428 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 1429 if (hsize < 1 || vsize < 1) | |
| 1430 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 1431 | |
| 1432 pixt = pixErodeCompBrickExtendDwa(NULL, pixs, hsize, vsize); | |
| 1433 pixd = pixDilateCompBrickExtendDwa(pixd, pixt, hsize, vsize); | |
| 1434 pixDestroy(&pixt); | |
| 1435 return pixd; | |
| 1436 } | |
| 1437 | |
| 1438 | |
| 1439 /*! | |
| 1440 * \brief pixCloseCompBrickExtendDwa() | |
| 1441 * | |
| 1442 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 1443 * or different from pixs | |
| 1444 * \param[in] pixs 1 bpp | |
| 1445 * \param[in] hsize width of brick Sel | |
| 1446 * \param[in] vsize height of brick Sel | |
| 1447 * \return pixd | |
| 1448 * | |
| 1449 * <pre> | |
| 1450 * Notes: | |
| 1451 * 1) There are three cases: | |
| 1452 * a) pixd == null (result into new pixd | |
| 1453 * b) pixd == pixs (in-place; writes result back to pixs | |
| 1454 * c) pixd != pixs (puts result into existing pixd | |
| 1455 * 2) There is no need to call this directly: pixCloseCompBrickDwa( | |
| 1456 * calls this function if either brick dimension exceeds 63. | |
| 1457 * </pre> | |
| 1458 */ | |
| 1459 PIX * | |
| 1460 pixCloseCompBrickExtendDwa(PIX *pixd, | |
| 1461 PIX *pixs, | |
| 1462 l_int32 hsize, | |
| 1463 l_int32 vsize) | |
| 1464 { | |
| 1465 l_int32 bordercolor, borderx, bordery; | |
| 1466 PIX *pixt1, *pixt2, *pixt3; | |
| 1467 | |
| 1468 if (!pixs) | |
| 1469 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 1470 if (pixGetDepth(pixs) != 1) | |
| 1471 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 1472 if (hsize < 1 || vsize < 1) | |
| 1473 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd); | |
| 1474 | |
| 1475 /* For "safe closing" with ASYMMETRIC_MORPH_BC, we always need | |
| 1476 * an extra 32 OFF pixels around the image (in addition to | |
| 1477 * the 32 added pixels for all dwa operations), whereas with | |
| 1478 * SYMMETRIC_MORPH_BC this is not necessary. */ | |
| 1479 bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); | |
| 1480 if (bordercolor == 0) { /* asymmetric b.c. */ | |
| 1481 borderx = 32 + (hsize / 64) * 32; | |
| 1482 bordery = 32 + (vsize / 64) * 32; | |
| 1483 } else { /* symmetric b.c. */ | |
| 1484 borderx = bordery = 32; | |
| 1485 } | |
| 1486 pixt1 = pixAddBorderGeneral(pixs, borderx, borderx, bordery, bordery, 0); | |
| 1487 | |
| 1488 pixt2 = pixDilateCompBrickExtendDwa(NULL, pixt1, hsize, vsize); | |
| 1489 pixErodeCompBrickExtendDwa(pixt1, pixt2, hsize, vsize); | |
| 1490 | |
| 1491 pixt3 = pixRemoveBorderGeneral(pixt1, borderx, borderx, bordery, bordery); | |
| 1492 pixDestroy(&pixt1); | |
| 1493 pixDestroy(&pixt2); | |
| 1494 | |
| 1495 if (!pixd) | |
| 1496 return pixt3; | |
| 1497 | |
| 1498 pixTransferAllData(pixd, &pixt3, 0, 0); | |
| 1499 return pixd; | |
| 1500 } | |
| 1501 | |
| 1502 | |
| 1503 /*! | |
| 1504 * \brief getExtendedCompositeParameters() | |
| 1505 * | |
| 1506 * \param[in] size of linear Sel | |
| 1507 * \param[out] pn number of 63 wide convolutions | |
| 1508 * \param[out] pextra size of extra Sel | |
| 1509 * \param[out] pactualsize [optional] actual size used in operation | |
| 1510 * \return 0 if OK, 1 on error | |
| 1511 * | |
| 1512 * <pre> | |
| 1513 * Notes: | |
| 1514 * (1) The DWA implementation allows Sels to be used with hits | |
| 1515 * up to 31 pixels from the origin, either horizontally or | |
| 1516 * vertically. Larger Sels can be used if decomposed into | |
| 1517 * a set of operations with Sels not exceeding 63 pixels | |
| 1518 * in either width or height (and with the origin as close | |
| 1519 * to the center of the Sel as possible). | |
| 1520 * (2) This returns the decomposition of a linear Sel of length | |
| 1521 * %size into a set of %n Sels of length 63 plus an extra | |
| 1522 * Sel of length %extra. | |
| 1523 * (3) For notation, let w == %size, n == %n, and e == %extra. | |
| 1524 * We have 1 < e < 63. | |
| 1525 * | |
| 1526 * Then if w < 64, we have n = 0 and e = w. | |
| 1527 * The general formula for w > 63 is: | |
| 1528 * w = 63 + (n - 1) * 62 + (e - 1) | |
| 1529 * | |
| 1530 * Where did this come from? Each successive convolution with | |
| 1531 * a Sel of length L adds a total length (L - 1) to w. | |
| 1532 * This accounts for using 62 for each additional Sel of size 63, | |
| 1533 * and using (e - 1) for the additional Sel of size e. | |
| 1534 * | |
| 1535 * Solving for n and e for w > 63: | |
| 1536 * n = 1 + Int((w - 63) / 62) | |
| 1537 * e = w - 63 - (n - 1) * 62 + 1 | |
| 1538 * | |
| 1539 * The extra part is decomposed into two factors f1 and f2, | |
| 1540 * and the actual size of the extra part is | |
| 1541 * e' = f1 * f2 | |
| 1542 * Then the actual width is: | |
| 1543 * w' = 63 + (n - 1) * 62 + f1 * f2 - 1 | |
| 1544 * </pre> | |
| 1545 */ | |
| 1546 l_ok | |
| 1547 getExtendedCompositeParameters(l_int32 size, | |
| 1548 l_int32 *pn, | |
| 1549 l_int32 *pextra, | |
| 1550 l_int32 *pactualsize) | |
| 1551 { | |
| 1552 l_int32 n, extra, fact1, fact2; | |
| 1553 | |
| 1554 if (!pn || !pextra) | |
| 1555 return ERROR_INT("&n and &extra not both defined", __func__, 1); | |
| 1556 | |
| 1557 if (size <= 63) { | |
| 1558 n = 0; | |
| 1559 extra = L_MIN(1, size); | |
| 1560 } else { /* size > 63 */ | |
| 1561 n = 1 + (l_int32)((size - 63) / 62); | |
| 1562 extra = size - 63 - (n - 1) * 62 + 1; | |
| 1563 } | |
| 1564 | |
| 1565 if (pactualsize) { | |
| 1566 selectComposableSizes(extra, &fact1, &fact2); | |
| 1567 *pactualsize = 63 + (n - 1) * 62 + fact1 * fact2 - 1; | |
| 1568 } | |
| 1569 | |
| 1570 *pn = n; | |
| 1571 *pextra = extra; | |
| 1572 return 0; | |
| 1573 } |
