Leptonica 1.68
C Image Processing Library
|
00001 /*====================================================================* 00002 - Copyright (C) 2001 Leptonica. All rights reserved. 00003 - This software is distributed in the hope that it will be 00004 - useful, but with NO WARRANTY OF ANY KIND. 00005 - No author or distributor accepts responsibility to anyone for the 00006 - consequences of using this software, or for whether it serves any 00007 - particular purpose or works at all, unless he or she says so in 00008 - writing. Everyone is granted permission to copy, modify and 00009 - redistribute this source code, for commercial or non-commercial 00010 - purposes, with the following restrictions: (1) the origin of this 00011 - source code must not be misrepresented; (2) modified versions must 00012 - be plainly marked as such; and (3) this notice may not be removed 00013 - or altered from any source or modified source distribution. 00014 *====================================================================*/ 00015 00016 /* 00017 * morphdwa.c 00018 * 00019 * Binary morphological (dwa) ops with brick Sels 00020 * PIX *pixDilateBrickDwa() 00021 * PIX *pixErodeBrickDwa() 00022 * PIX *pixOpenBrickDwa() 00023 * PIX *pixCloseBrickDwa() 00024 * 00025 * Binary composite morphological (dwa) ops with brick Sels 00026 * PIX *pixDilateCompBrickDwa() 00027 * PIX *pixErodeCompBrickDwa() 00028 * PIX *pixOpenCompBrickDwa() 00029 * PIX *pixCloseCompBrickDwa() 00030 * 00031 * Binary extended composite morphological (dwa) ops with brick Sels 00032 * PIX *pixDilateCompBrickExtendDwa() 00033 * PIX *pixErodeCompBrickExtendDwa() 00034 * PIX *pixOpenCompBrickExtendDwa() 00035 * PIX *pixCloseCompBrickExtendDwa() 00036 * l_int32 getExtendedCompositeParameters() 00037 * 00038 * These are higher-level interfaces for dwa morphology with brick Sels. 00039 * Because many morphological operations are performed using 00040 * separable brick Sels, it is useful to have a simple interface 00041 * for this. 00042 * 00043 * We have included all 58 of the brick Sels that are generated 00044 * by selaAddBasic(). These are sufficient for all the decomposable 00045 * bricks up to size 63, which is the limit for dwa Sels with 00046 * origins at the center of the Sel. 00047 * 00048 * All three sets can be used as the basic interface for general 00049 * brick operations. Here are the internal calling sequences: 00050 * 00051 * (1) If you try to apply a non-decomposable operation, such as 00052 * pixErodeBrickDwa(), with a Sel size that doesn't exist, 00053 * this calls a decomposable operation, pixErodeCompBrickDwa(), 00054 * instead. This can differ in linear Sel size by up to 00055 * 2 pixels from the request. 00056 * 00057 * (2) If either Sel brick dimension is greater than 63, the extended 00058 * composite function is called. 00059 * 00060 * (3) The extended composite function calls the composite function 00061 * a number of times with size 63, and once with size < 63. 00062 * Because each operation with a size of 63 is done compositely 00063 * with 7 x 9 (exactly 63), the net result is correct in 00064 * length to within 2 pixels. 00065 * 00066 * For composite operations, both using a comb and extended (beyond 63), 00067 * horizontal and vertical operations are composed separately 00068 * and sequentially. 00069 * 00070 * We have also included use of all the 76 comb Sels that are generated 00071 * by selaAddDwaCombs(). The generated code is in dwacomb.2.c 00072 * and dwacomblow.2.c. These are used for the composite dwa 00073 * brick operations. 00074 * 00075 * The non-composite brick operations, such as pixDilateBrickDwa(), 00076 * will call the associated composite operation in situations where 00077 * the requisite brick Sel has not been compiled into fmorphgen*.1.c. 00078 * 00079 * If you want to use brick Sels that are not represented in the 00080 * basic set of 58, you must generate the dwa code to implement them. 00081 * You have three choices for how to use these: 00082 * 00083 * (1) Add both the new Sels and the dwa code to the library: 00084 * - For simplicity, add your new brick Sels to those defined 00085 * in selaAddBasic(). 00086 * - Recompile the library. 00087 * - Make prog/fmorphautogen. 00088 * - Run prog/fmorphautogen, to generate new versions of the 00089 * dwa code in fmorphgen.1.c and fmorphgenlow.1.c. 00090 * - Copy these two files to src. 00091 * - Recompile the library again. 00092 * - Use the new brick Sels in your program and compile it. 00093 * 00094 * (2) Make both the new Sels and dwa code outside the library, 00095 * and link it directly to an executable: 00096 * - Write a function to generate the new Sels in a Sela, and call 00097 * fmorphautogen(sela, <N>, filename) to generate the code. 00098 * - Compile your program that uses the newly generated function 00099 * pixMorphDwa_<N>(), and link to the two new C files. 00100 * 00101 * (3) Make the new Sels in the library and use the dwa code outside it: 00102 * - Add code in the library to generate your new brick Sels. 00103 * (It is suggested that you NOT add these Sels to the 00104 * selaAddBasic() function; write a new function that generates 00105 * a new Sela.) 00106 * - Recompile the library. 00107 * - Write a small program that generates the Sela and calls 00108 * fmorphautogen(sela, <N>, filename) to generate the code. 00109 * - Compile your program that uses the newly generated function 00110 * pixMorphDwa_<N>(), and link to the two new C files. 00111 * As an example of this approach, see prog/dwamorph*_reg.c: 00112 * - added selaAddDwaLinear() to sel2.c 00113 * - wrote dwamorph1_reg.c, to generate the dwa code. 00114 * - compiled and linked the generated code with the application, 00115 * dwamorph2_reg.c. (Note: because this was a regression test, 00116 * dwamorph1_reg also builds and runs the application program.) 00117 */ 00118 00119 #include <stdio.h> 00120 #include <stdlib.h> 00121 #include "allheaders.h" 00122 00123 #ifndef NO_CONSOLE_IO 00124 #define DEBUG_SEL_LOOKUP 0 00125 #endif /* ~NO_CONSOLE_IO */ 00126 00127 00128 /*-----------------------------------------------------------------* 00129 * Binary morphological (dwa) ops with brick Sels * 00130 *-----------------------------------------------------------------*/ 00131 /*! 00132 * pixDilateBrickDwa() 00133 * 00134 * Input: pixd (<optional>; this can be null, equal to pixs, 00135 * or different from pixs) 00136 * pixs (1 bpp) 00137 * hsize (width of brick Sel) 00138 * vsize (height of brick Sel) 00139 * Return: pixd 00140 * 00141 * Notes: 00142 * (1) These implement 2D brick Sels, using linear Sels generated 00143 * with selaAddBasic(). 00144 * (2) A brick Sel has hits for all elements. 00145 * (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) 00146 * (4) Do separably if both hsize and vsize are > 1. 00147 * (5) It is necessary that both horizontal and vertical Sels 00148 * of the input size are defined in the basic sela. 00149 * (6) There are three cases: 00150 * (a) pixd == null (result into new pixd) 00151 * (b) pixd == pixs (in-place; writes result back to pixs) 00152 * (c) pixd != pixs (puts result into existing pixd) 00153 * (7) For clarity, if the case is known, use these patterns: 00154 * (a) pixd = pixDilateBrickDwa(NULL, pixs, ...); 00155 * (b) pixDilateBrickDwa(pixs, pixs, ...); 00156 * (c) pixDilateBrickDwa(pixd, pixs, ...); 00157 * (8) The size of pixd is determined by pixs. 00158 * (9) If either linear Sel is not found, this calls 00159 * the appropriate decomposible function. 00160 */ 00161 PIX * 00162 pixDilateBrickDwa(PIX *pixd, 00163 PIX *pixs, 00164 l_int32 hsize, 00165 l_int32 vsize) 00166 { 00167 l_int32 found; 00168 char *selnameh, *selnamev; 00169 SELA *sela; 00170 PIX *pixt1, *pixt2, *pixt3; 00171 00172 PROCNAME("pixDilateBrickDwa"); 00173 00174 if (!pixs) 00175 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 00176 if (pixGetDepth(pixs) != 1) 00177 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 00178 if (hsize < 1 || vsize < 1) 00179 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 00180 00181 if (hsize == 1 && vsize == 1) 00182 return pixCopy(pixd, pixs); 00183 00184 sela = selaAddBasic(NULL); 00185 found = TRUE; 00186 selnameh = selnamev = NULL; 00187 if (hsize > 1) { 00188 selnameh = selaGetBrickName(sela, hsize, 1); 00189 if (!selnameh) found = FALSE; 00190 } 00191 if (vsize > 1) { 00192 selnamev = selaGetBrickName(sela, 1, vsize); 00193 if (!selnamev) found = FALSE; 00194 } 00195 selaDestroy(&sela); 00196 if (!found) { 00197 L_INFO("Calling the decomposable dwa function", procName); 00198 if (selnameh) FREE(selnameh); 00199 if (selnamev) FREE(selnamev); 00200 return pixDilateCompBrickDwa(pixd, pixs, hsize, vsize); 00201 } 00202 00203 if (vsize == 1) { 00204 pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnameh); 00205 FREE(selnameh); 00206 } 00207 else if (hsize == 1) { 00208 pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnamev); 00209 FREE(selnamev); 00210 } 00211 else { 00212 pixt1 = pixAddBorder(pixs, 32, 0); 00213 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh); 00214 pixFMorphopGen_1(pixt1, pixt3, L_MORPH_DILATE, selnamev); 00215 pixt2 = pixRemoveBorder(pixt1, 32); 00216 pixDestroy(&pixt1); 00217 pixDestroy(&pixt3); 00218 FREE(selnameh); 00219 FREE(selnamev); 00220 } 00221 00222 if (!pixd) 00223 return pixt2; 00224 00225 pixTransferAllData(pixd, &pixt2, 0, 0); 00226 return pixd; 00227 } 00228 00229 00230 /*! 00231 * pixErodeBrickDwa() 00232 * 00233 * Input: pixd (<optional>; this can be null, equal to pixs, 00234 * or different from pixs) 00235 * pixs (1 bpp) 00236 * hsize (width of brick Sel) 00237 * vsize (height of brick Sel) 00238 * Return: pixd 00239 * 00240 * Notes: 00241 * (1) These implement 2D brick Sels, using linear Sels generated 00242 * with selaAddBasic(). 00243 * (2) A brick Sel has hits for all elements. 00244 * (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) 00245 * (4) Do separably if both hsize and vsize are > 1. 00246 * (5) It is necessary that both horizontal and vertical Sels 00247 * of the input size are defined in the basic sela. 00248 * (6) Note that we must always set or clear the border pixels 00249 * before each operation, depending on the the b.c. 00250 * (symmetric or asymmetric). 00251 * (7) There are three cases: 00252 * (a) pixd == null (result into new pixd) 00253 * (b) pixd == pixs (in-place; writes result back to pixs) 00254 * (c) pixd != pixs (puts result into existing pixd) 00255 * (8) For clarity, if the case is known, use these patterns: 00256 * (a) pixd = pixErodeBrickDwa(NULL, pixs, ...); 00257 * (b) pixErodeBrickDwa(pixs, pixs, ...); 00258 * (c) pixErodeBrickDwa(pixd, pixs, ...); 00259 * (9) The size of the result is determined by pixs. 00260 * (10) If either linear Sel is not found, this calls 00261 * the appropriate decomposible function. 00262 */ 00263 PIX * 00264 pixErodeBrickDwa(PIX *pixd, 00265 PIX *pixs, 00266 l_int32 hsize, 00267 l_int32 vsize) 00268 { 00269 l_int32 found; 00270 char *selnameh, *selnamev; 00271 SELA *sela; 00272 PIX *pixt1, *pixt2, *pixt3; 00273 00274 PROCNAME("pixErodeBrickDwa"); 00275 00276 if (!pixs) 00277 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 00278 if (pixGetDepth(pixs) != 1) 00279 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 00280 if (hsize < 1 || vsize < 1) 00281 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 00282 00283 if (hsize == 1 && vsize == 1) 00284 return pixCopy(pixd, pixs); 00285 00286 sela = selaAddBasic(NULL); 00287 found = TRUE; 00288 selnameh = selnamev = NULL; 00289 if (hsize > 1) { 00290 selnameh = selaGetBrickName(sela, hsize, 1); 00291 if (!selnameh) found = FALSE; 00292 } 00293 if (vsize > 1) { 00294 selnamev = selaGetBrickName(sela, 1, vsize); 00295 if (!selnamev) found = FALSE; 00296 } 00297 selaDestroy(&sela); 00298 if (!found) { 00299 L_INFO("Calling the decomposable dwa function", procName); 00300 if (selnameh) FREE(selnameh); 00301 if (selnamev) FREE(selnamev); 00302 return pixErodeCompBrickDwa(pixd, pixs, hsize, vsize); 00303 } 00304 00305 if (vsize == 1) { 00306 pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnameh); 00307 FREE(selnameh); 00308 } 00309 else if (hsize == 1) { 00310 pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnamev); 00311 FREE(selnamev); 00312 } 00313 else { 00314 pixt1 = pixAddBorder(pixs, 32, 0); 00315 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh); 00316 pixFMorphopGen_1(pixt1, pixt3, L_MORPH_ERODE, selnamev); 00317 pixt2 = pixRemoveBorder(pixt1, 32); 00318 pixDestroy(&pixt1); 00319 pixDestroy(&pixt3); 00320 FREE(selnameh); 00321 FREE(selnamev); 00322 } 00323 00324 if (!pixd) 00325 return pixt2; 00326 00327 pixTransferAllData(pixd, &pixt2, 0, 0); 00328 return pixd; 00329 } 00330 00331 00332 /*! 00333 * pixOpenBrickDwa() 00334 * 00335 * Input: pixd (<optional>; this can be null, equal to pixs, 00336 * or different from pixs) 00337 * pixs (1 bpp) 00338 * hsize (width of brick Sel) 00339 * vsize (height of brick Sel) 00340 * Return: pixd 00341 * 00342 * Notes: 00343 * (1) These implement 2D brick Sels, using linear Sels generated 00344 * with selaAddBasic(). 00345 * (2) A brick Sel has hits for all elements. 00346 * (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) 00347 * (4) Do separably if both hsize and vsize are > 1. 00348 * (5) It is necessary that both horizontal and vertical Sels 00349 * of the input size are defined in the basic sela. 00350 * (6) Note that we must always set or clear the border pixels 00351 * before each operation, depending on the the b.c. 00352 * (symmetric or asymmetric). 00353 * (7) There are three cases: 00354 * (a) pixd == null (result into new pixd) 00355 * (b) pixd == pixs (in-place; writes result back to pixs) 00356 * (c) pixd != pixs (puts result into existing pixd) 00357 * (8) For clarity, if the case is known, use these patterns: 00358 * (a) pixd = pixOpenBrickDwa(NULL, pixs, ...); 00359 * (b) pixOpenBrickDwa(pixs, pixs, ...); 00360 * (c) pixOpenBrickDwa(pixd, pixs, ...); 00361 * (9) The size of the result is determined by pixs. 00362 * (10) If either linear Sel is not found, this calls 00363 * the appropriate decomposible function. 00364 */ 00365 PIX * 00366 pixOpenBrickDwa(PIX *pixd, 00367 PIX *pixs, 00368 l_int32 hsize, 00369 l_int32 vsize) 00370 { 00371 l_int32 found; 00372 char *selnameh, *selnamev; 00373 SELA *sela; 00374 PIX *pixt1, *pixt2, *pixt3; 00375 00376 PROCNAME("pixOpenBrickDwa"); 00377 00378 if (!pixs) 00379 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 00380 if (pixGetDepth(pixs) != 1) 00381 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 00382 if (hsize < 1 || vsize < 1) 00383 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 00384 00385 if (hsize == 1 && vsize == 1) 00386 return pixCopy(pixd, pixs); 00387 00388 sela = selaAddBasic(NULL); 00389 found = TRUE; 00390 selnameh = selnamev = NULL; 00391 if (hsize > 1) { 00392 selnameh = selaGetBrickName(sela, hsize, 1); 00393 if (!selnameh) found = FALSE; 00394 } 00395 if (vsize > 1) { 00396 selnamev = selaGetBrickName(sela, 1, vsize); 00397 if (!selnamev) found = FALSE; 00398 } 00399 selaDestroy(&sela); 00400 if (!found) { 00401 L_INFO("Calling the decomposable dwa function", procName); 00402 if (selnameh) FREE(selnameh); 00403 if (selnamev) FREE(selnamev); 00404 return pixOpenCompBrickDwa(pixd, pixs, hsize, vsize); 00405 } 00406 00407 pixt1 = pixAddBorder(pixs, 32, 0); 00408 if (vsize == 1) { /* horizontal only */ 00409 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnameh); 00410 FREE(selnameh); 00411 } 00412 else if (hsize == 1) { /* vertical only */ 00413 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnamev); 00414 FREE(selnamev); 00415 } 00416 else { /* do separable */ 00417 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh); 00418 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev); 00419 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh); 00420 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev); 00421 FREE(selnameh); 00422 FREE(selnamev); 00423 pixDestroy(&pixt3); 00424 } 00425 pixt3 = pixRemoveBorder(pixt2, 32); 00426 pixDestroy(&pixt1); 00427 pixDestroy(&pixt2); 00428 00429 if (!pixd) 00430 return pixt3; 00431 00432 pixTransferAllData(pixd, &pixt3, 0, 0); 00433 return pixd; 00434 } 00435 00436 00437 /*! 00438 * pixCloseBrickDwa() 00439 * 00440 * Input: pixd (<optional>; this can be null, equal to pixs, 00441 * or different from pixs) 00442 * pixs (1 bpp) 00443 * hsize (width of brick Sel) 00444 * vsize (height of brick Sel) 00445 * Return: pixd 00446 * 00447 * Notes: 00448 * (1) This is a 'safe' closing; we add an extra border of 32 OFF 00449 * pixels for the standard asymmetric b.c. 00450 * (2) These implement 2D brick Sels, using linear Sels generated 00451 * with selaAddBasic(). 00452 * (3) A brick Sel has hits for all elements. 00453 * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) 00454 * (5) Do separably if both hsize and vsize are > 1. 00455 * (6) It is necessary that both horizontal and vertical Sels 00456 * of the input size are defined in the basic sela. 00457 * (7) Note that we must always set or clear the border pixels 00458 * before each operation, depending on the the b.c. 00459 * (symmetric or asymmetric). 00460 * (8) There are three cases: 00461 * (a) pixd == null (result into new pixd) 00462 * (b) pixd == pixs (in-place; writes result back to pixs) 00463 * (c) pixd != pixs (puts result into existing pixd) 00464 * (9) For clarity, if the case is known, use these patterns: 00465 * (a) pixd = pixCloseBrickDwa(NULL, pixs, ...); 00466 * (b) pixCloseBrickDwa(pixs, pixs, ...); 00467 * (c) pixCloseBrickDwa(pixd, pixs, ...); 00468 * (10) The size of the result is determined by pixs. 00469 * (11) If either linear Sel is not found, this calls 00470 * the appropriate decomposible function. 00471 */ 00472 PIX * 00473 pixCloseBrickDwa(PIX *pixd, 00474 PIX *pixs, 00475 l_int32 hsize, 00476 l_int32 vsize) 00477 { 00478 l_int32 bordercolor, bordersize, found; 00479 char *selnameh, *selnamev; 00480 SELA *sela; 00481 PIX *pixt1, *pixt2, *pixt3; 00482 00483 PROCNAME("pixCloseBrickDwa"); 00484 00485 if (!pixs) 00486 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 00487 if (pixGetDepth(pixs) != 1) 00488 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 00489 if (hsize < 1 || vsize < 1) 00490 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 00491 00492 if (hsize == 1 && vsize == 1) 00493 return pixCopy(pixd, pixs); 00494 00495 sela = selaAddBasic(NULL); 00496 found = TRUE; 00497 selnameh = selnamev = NULL; 00498 if (hsize > 1) { 00499 selnameh = selaGetBrickName(sela, hsize, 1); 00500 if (!selnameh) found = FALSE; 00501 } 00502 if (vsize > 1) { 00503 selnamev = selaGetBrickName(sela, 1, vsize); 00504 if (!selnamev) found = FALSE; 00505 } 00506 selaDestroy(&sela); 00507 if (!found) { 00508 L_INFO("Calling the decomposable dwa function", procName); 00509 if (selnameh) FREE(selnameh); 00510 if (selnamev) FREE(selnamev); 00511 return pixCloseCompBrickDwa(pixd, pixs, hsize, vsize); 00512 } 00513 00514 /* For "safe closing" with ASYMMETRIC_MORPH_BC, we always need 00515 * an extra 32 OFF pixels around the image (in addition to 00516 * the 32 added pixels for all dwa operations), whereas with 00517 * SYMMETRIC_MORPH_BC this is not necessary. */ 00518 bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); 00519 if (bordercolor == 0) /* asymmetric b.c. */ 00520 bordersize = 64; 00521 else /* symmetric b.c. */ 00522 bordersize = 32; 00523 pixt1 = pixAddBorder(pixs, bordersize, 0); 00524 00525 if (vsize == 1) { /* horizontal only */ 00526 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnameh); 00527 FREE(selnameh); 00528 } 00529 else if (hsize == 1) { /* vertical only */ 00530 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev); 00531 FREE(selnamev); 00532 } 00533 else { /* do separable */ 00534 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh); 00535 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev); 00536 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh); 00537 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev); 00538 FREE(selnameh); 00539 FREE(selnamev); 00540 pixDestroy(&pixt3); 00541 } 00542 pixt3 = pixRemoveBorder(pixt2, bordersize); 00543 pixDestroy(&pixt1); 00544 pixDestroy(&pixt2); 00545 00546 if (!pixd) 00547 return pixt3; 00548 00549 pixTransferAllData(pixd, &pixt3, 0, 0); 00550 return pixd; 00551 } 00552 00553 00554 /*-----------------------------------------------------------------* 00555 * Binary composite morphological (dwa) ops with brick Sels * 00556 *-----------------------------------------------------------------*/ 00557 /*! 00558 * pixDilateCompBrickDwa() 00559 * 00560 * Input: pixd (<optional>; this can be null, equal to pixs, 00561 * or different from pixs) 00562 * pixs (1 bpp) 00563 * hsize (width of brick Sel) 00564 * vsize (height of brick Sel) 00565 * Return: pixd 00566 * 00567 * Notes: 00568 * (1) These implement a separable composite dilation with 2D brick Sels. 00569 * (2) For efficiency, it may decompose each linear morphological 00570 * operation into two (brick + comb). 00571 * (3) A brick Sel has hits for all elements. 00572 * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) 00573 * (5) Do separably if both hsize and vsize are > 1. 00574 * (6) It is necessary that both horizontal and vertical Sels 00575 * of the input size are defined in the basic sela. 00576 * (7) There are three cases: 00577 * (a) pixd == null (result into new pixd) 00578 * (b) pixd == pixs (in-place; writes result back to pixs) 00579 * (c) pixd != pixs (puts result into existing pixd) 00580 * (8) For clarity, if the case is known, use these patterns: 00581 * (a) pixd = pixDilateCompBrickDwa(NULL, pixs, ...); 00582 * (b) pixDilateCompBrickDwa(pixs, pixs, ...); 00583 * (c) pixDilateCompBrickDwa(pixd, pixs, ...); 00584 * (9) The size of pixd is determined by pixs. 00585 * (10) CAUTION: both hsize and vsize are being decomposed. 00586 * The decomposer chooses a product of sizes (call them 00587 * 'terms') for each that is close to the input size, 00588 * but not necessarily equal to it. It attempts to optimize: 00589 * (a) for consistency with the input values: the product 00590 * of terms is close to the input size 00591 * (b) for efficiency of the operation: the sum of the 00592 * terms is small; ideally about twice the square 00593 * root of the input size. 00594 * So, for example, if the input hsize = 37, which is 00595 * a prime number, the decomposer will break this into two 00596 * terms, 6 and 6, so that the net result is a dilation 00597 * with hsize = 36. 00598 */ 00599 PIX * 00600 pixDilateCompBrickDwa(PIX *pixd, 00601 PIX *pixs, 00602 l_int32 hsize, 00603 l_int32 vsize) 00604 { 00605 char *selnameh1, *selnameh2, *selnamev1, *selnamev2; 00606 l_int32 hsize1, hsize2, vsize1, vsize2; 00607 PIX *pixt1, *pixt2, *pixt3; 00608 00609 PROCNAME("pixDilateCompBrickDwa"); 00610 00611 if (!pixs) 00612 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 00613 if (pixGetDepth(pixs) != 1) 00614 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 00615 if (hsize < 1 || vsize < 1) 00616 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 00617 if (hsize > 63 || vsize > 63) 00618 return pixDilateCompBrickExtendDwa(pixd, pixs, hsize, vsize); 00619 00620 if (hsize == 1 && vsize == 1) 00621 return pixCopy(pixd, pixs); 00622 00623 hsize1 = hsize2 = vsize1 = vsize2 = 1; 00624 selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; 00625 if (hsize > 1) 00626 getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, 00627 &selnameh2, NULL, NULL); 00628 if (vsize > 1) 00629 getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, 00630 &selnamev1, &selnamev2); 00631 00632 #if DEBUG_SEL_LOOKUP 00633 fprintf(stderr, "nameh1=%s, nameh2=%s, namev1=%s, namev2=%s\n", 00634 selnameh1, selnameh2, selnamev1, selnamev2); 00635 fprintf(stderr, "hsize1=%d, hsize2=%d, vsize1=%d, vsize2=%d\n", 00636 hsize1, hsize2, vsize1, vsize2); 00637 #endif /* DEBUG_SEL_LOOKUP */ 00638 00639 pixt1 = pixAddBorder(pixs, 64, 0); 00640 if (vsize == 1) { 00641 if (hsize2 == 1) 00642 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); 00643 else { 00644 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); 00645 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); 00646 pixDestroy(&pixt3); 00647 } 00648 } 00649 else if (hsize == 1) { 00650 if (vsize2 == 1) 00651 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1); 00652 else { 00653 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1); 00654 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnamev2); 00655 pixDestroy(&pixt3); 00656 } 00657 } 00658 else { /* vsize and hsize both > 1 */ 00659 if (hsize2 == 1) 00660 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); 00661 else { 00662 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); 00663 pixt3 = pixFMorphopGen_2(NULL, pixt2, L_MORPH_DILATE, selnameh2); 00664 pixDestroy(&pixt2); 00665 } 00666 if (vsize2 == 1) 00667 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); 00668 else { 00669 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); 00670 pixFMorphopGen_2(pixt2, pixt2, L_MORPH_DILATE, selnamev2); 00671 } 00672 pixDestroy(&pixt3); 00673 } 00674 pixDestroy(&pixt1); 00675 pixt1 = pixRemoveBorder(pixt2, 64); 00676 pixDestroy(&pixt2); 00677 if (selnameh1) FREE(selnameh1); 00678 if (selnameh2) FREE(selnameh2); 00679 if (selnamev1) FREE(selnamev1); 00680 if (selnamev2) FREE(selnamev2); 00681 00682 if (!pixd) 00683 return pixt1; 00684 00685 pixTransferAllData(pixd, &pixt1, 0, 0); 00686 return pixd; 00687 } 00688 00689 00690 /*! 00691 * pixErodeCompBrickDwa() 00692 * 00693 * Input: pixd (<optional>; this can be null, equal to pixs, 00694 * or different from pixs) 00695 * pixs (1 bpp) 00696 * hsize (width of brick Sel) 00697 * vsize (height of brick Sel) 00698 * Return: pixd 00699 * 00700 * Notes: 00701 * (1) These implement a separable composite erosion with 2D brick Sels. 00702 * (2) For efficiency, it may decompose each linear morphological 00703 * operation into two (brick + comb). 00704 * (3) A brick Sel has hits for all elements. 00705 * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) 00706 * (5) Do separably if both hsize and vsize are > 1. 00707 * (6) It is necessary that both horizontal and vertical Sels 00708 * of the input size are defined in the basic sela. 00709 * (7) There are three cases: 00710 * (a) pixd == null (result into new pixd) 00711 * (b) pixd == pixs (in-place; writes result back to pixs) 00712 * (c) pixd != pixs (puts result into existing pixd) 00713 * (8) For clarity, if the case is known, use these patterns: 00714 * (a) pixd = pixErodeCompBrickDwa(NULL, pixs, ...); 00715 * (b) pixErodeCompBrickDwa(pixs, pixs, ...); 00716 * (c) pixErodeCompBrickDwa(pixd, pixs, ...); 00717 * (9) The size of pixd is determined by pixs. 00718 * (10) CAUTION: both hsize and vsize are being decomposed. 00719 * The decomposer chooses a product of sizes (call them 00720 * 'terms') for each that is close to the input size, 00721 * but not necessarily equal to it. It attempts to optimize: 00722 * (a) for consistency with the input values: the product 00723 * of terms is close to the input size 00724 * (b) for efficiency of the operation: the sum of the 00725 * terms is small; ideally about twice the square 00726 * root of the input size. 00727 * So, for example, if the input hsize = 37, which is 00728 * a prime number, the decomposer will break this into two 00729 * terms, 6 and 6, so that the net result is a dilation 00730 * with hsize = 36. 00731 */ 00732 PIX * 00733 pixErodeCompBrickDwa(PIX *pixd, 00734 PIX *pixs, 00735 l_int32 hsize, 00736 l_int32 vsize) 00737 { 00738 char *selnameh1, *selnameh2, *selnamev1, *selnamev2; 00739 l_int32 hsize1, hsize2, vsize1, vsize2, bordercolor; 00740 PIX *pixt1, *pixt2, *pixt3; 00741 00742 PROCNAME("pixErodeCompBrickDwa"); 00743 00744 if (!pixs) 00745 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 00746 if (pixGetDepth(pixs) != 1) 00747 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 00748 if (hsize < 1 || vsize < 1) 00749 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 00750 if (hsize > 63 || vsize > 63) 00751 return pixErodeCompBrickExtendDwa(pixd, pixs, hsize, vsize); 00752 00753 if (hsize == 1 && vsize == 1) 00754 return pixCopy(pixd, pixs); 00755 00756 hsize1 = hsize2 = vsize1 = vsize2 = 1; 00757 selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; 00758 if (hsize > 1) 00759 getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, 00760 &selnameh2, NULL, NULL); 00761 if (vsize > 1) 00762 getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, 00763 &selnamev1, &selnamev2); 00764 00765 /* For symmetric b.c., bordercolor == 1 for erosion */ 00766 bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); 00767 pixt1 = pixAddBorder(pixs, 64, bordercolor); 00768 00769 if (vsize == 1) { 00770 if (hsize2 == 1) 00771 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); 00772 else { 00773 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); 00774 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); 00775 pixDestroy(&pixt3); 00776 } 00777 } 00778 else if (hsize == 1) { 00779 if (vsize2 == 1) 00780 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); 00781 else { 00782 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); 00783 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnamev2); 00784 pixDestroy(&pixt3); 00785 } 00786 } 00787 else { /* vsize and hsize both > 1 */ 00788 if (hsize2 == 1) 00789 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); 00790 else { 00791 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); 00792 pixt3 = pixFMorphopGen_2(NULL, pixt2, L_MORPH_ERODE, selnameh2); 00793 pixDestroy(&pixt2); 00794 } 00795 if (vsize2 == 1) 00796 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); 00797 else { 00798 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); 00799 pixFMorphopGen_2(pixt2, pixt2, L_MORPH_ERODE, selnamev2); 00800 } 00801 pixDestroy(&pixt3); 00802 } 00803 pixDestroy(&pixt1); 00804 pixt1 = pixRemoveBorder(pixt2, 64); 00805 pixDestroy(&pixt2); 00806 if (selnameh1) FREE(selnameh1); 00807 if (selnameh2) FREE(selnameh2); 00808 if (selnamev1) FREE(selnamev1); 00809 if (selnamev2) FREE(selnamev2); 00810 00811 if (!pixd) 00812 return pixt1; 00813 00814 pixTransferAllData(pixd, &pixt1, 0, 0); 00815 return pixd; 00816 } 00817 00818 00819 /*! 00820 * pixOpenCompBrickDwa() 00821 * 00822 * Input: pixd (<optional>; this can be null, equal to pixs, 00823 * or different from pixs) 00824 * pixs (1 bpp) 00825 * hsize (width of brick Sel) 00826 * vsize (height of brick Sel) 00827 * Return: pixd 00828 * 00829 * Notes: 00830 * (1) These implement a separable composite opening with 2D brick Sels. 00831 * (2) For efficiency, it may decompose each linear morphological 00832 * operation into two (brick + comb). 00833 * (3) A brick Sel has hits for all elements. 00834 * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) 00835 * (5) Do separably if both hsize and vsize are > 1. 00836 * (6) It is necessary that both horizontal and vertical Sels 00837 * of the input size are defined in the basic sela. 00838 * (7) There are three cases: 00839 * (a) pixd == null (result into new pixd) 00840 * (b) pixd == pixs (in-place; writes result back to pixs) 00841 * (c) pixd != pixs (puts result into existing pixd) 00842 * (8) For clarity, if the case is known, use these patterns: 00843 * (a) pixd = pixOpenCompBrickDwa(NULL, pixs, ...); 00844 * (b) pixOpenCompBrickDwa(pixs, pixs, ...); 00845 * (c) pixOpenCompBrickDwa(pixd, pixs, ...); 00846 * (9) The size of pixd is determined by pixs. 00847 * (10) CAUTION: both hsize and vsize are being decomposed. 00848 * The decomposer chooses a product of sizes (call them 00849 * 'terms') for each that is close to the input size, 00850 * but not necessarily equal to it. It attempts to optimize: 00851 * (a) for consistency with the input values: the product 00852 * of terms is close to the input size 00853 * (b) for efficiency of the operation: the sum of the 00854 * terms is small; ideally about twice the square 00855 * root of the input size. 00856 * So, for example, if the input hsize = 37, which is 00857 * a prime number, the decomposer will break this into two 00858 * terms, 6 and 6, so that the net result is a dilation 00859 * with hsize = 36. 00860 */ 00861 PIX * 00862 pixOpenCompBrickDwa(PIX *pixd, 00863 PIX *pixs, 00864 l_int32 hsize, 00865 l_int32 vsize) 00866 { 00867 char *selnameh1, *selnameh2, *selnamev1, *selnamev2; 00868 l_int32 hsize1, hsize2, vsize1, vsize2, bordercolor; 00869 PIX *pixt1, *pixt2, *pixt3; 00870 00871 PROCNAME("pixOpenCompBrickDwa"); 00872 00873 if (!pixs) 00874 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 00875 if (pixGetDepth(pixs) != 1) 00876 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 00877 if (hsize < 1 || vsize < 1) 00878 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 00879 if (hsize > 63 || vsize > 63) 00880 return pixOpenCompBrickExtendDwa(pixd, pixs, hsize, vsize); 00881 00882 if (hsize == 1 && vsize == 1) 00883 return pixCopy(pixd, pixs); 00884 00885 hsize1 = hsize2 = vsize1 = vsize2 = 1; 00886 selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; 00887 if (hsize > 1) 00888 getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, 00889 &selnameh2, NULL, NULL); 00890 if (vsize > 1) 00891 getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, 00892 &selnamev1, &selnamev2); 00893 00894 /* For symmetric b.c., initialize erosion with bordercolor == 1 */ 00895 bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); 00896 pixt1 = pixAddBorder(pixs, 64, bordercolor); 00897 00898 if (vsize == 1) { 00899 if (hsize2 == 1) { 00900 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); 00901 if (bordercolor == 1) 00902 pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); 00903 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnameh1); 00904 } 00905 else { 00906 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); 00907 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); 00908 if (bordercolor == 1) 00909 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); 00910 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1); 00911 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnameh2); 00912 } 00913 } 00914 else if (hsize == 1) { 00915 if (vsize2 == 1) { 00916 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); 00917 if (bordercolor == 1) 00918 pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); 00919 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); 00920 } 00921 else { 00922 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); 00923 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnamev2); 00924 if (bordercolor == 1) 00925 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); 00926 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); 00927 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); 00928 } 00929 } 00930 else { /* vsize and hsize both > 1 */ 00931 if (hsize2 == 1 && vsize2 == 1) { 00932 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); 00933 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); 00934 if (bordercolor == 1) 00935 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); 00936 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1); 00937 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev1); 00938 } 00939 else if (vsize2 == 1) { 00940 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); 00941 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); 00942 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); 00943 if (bordercolor == 1) 00944 pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); 00945 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnameh1); 00946 pixFMorphopGen_2(pixt3, pixt2, L_MORPH_DILATE, selnameh2); 00947 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev1); 00948 } 00949 else if (hsize2 == 1) { 00950 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); 00951 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); 00952 pixFMorphopGen_2(pixt3, pixt2, L_MORPH_ERODE, selnamev2); 00953 if (bordercolor == 1) 00954 pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); 00955 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnameh1); 00956 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); 00957 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); 00958 } 00959 else { /* both directions are combed */ 00960 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); 00961 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); 00962 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); 00963 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); 00964 if (bordercolor == 1) 00965 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); 00966 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1); 00967 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnameh2); 00968 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); 00969 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); 00970 } 00971 } 00972 pixDestroy(&pixt3); 00973 00974 pixDestroy(&pixt1); 00975 pixt1 = pixRemoveBorder(pixt2, 64); 00976 pixDestroy(&pixt2); 00977 if (selnameh1) FREE(selnameh1); 00978 if (selnameh2) FREE(selnameh2); 00979 if (selnamev1) FREE(selnamev1); 00980 if (selnamev2) FREE(selnamev2); 00981 00982 if (!pixd) 00983 return pixt1; 00984 00985 pixTransferAllData(pixd, &pixt1, 0, 0); 00986 return pixd; 00987 } 00988 00989 00990 /*! 00991 * pixCloseCompBrickDwa() 00992 * 00993 * Input: pixd (<optional>; this can be null, equal to pixs, 00994 * or different from pixs) 00995 * pixs (1 bpp) 00996 * hsize (width of brick Sel) 00997 * vsize (height of brick Sel) 00998 * Return: pixd 00999 * 01000 * Notes: 01001 * (1) This implements a separable composite safe closing with 2D 01002 * brick Sels. 01003 * (2) For efficiency, it may decompose each linear morphological 01004 * operation into two (brick + comb). 01005 * (3) A brick Sel has hits for all elements. 01006 * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) 01007 * (5) Do separably if both hsize and vsize are > 1. 01008 * (6) It is necessary that both horizontal and vertical Sels 01009 * of the input size are defined in the basic sela. 01010 * (7) There are three cases: 01011 * (a) pixd == null (result into new pixd) 01012 * (b) pixd == pixs (in-place; writes result back to pixs) 01013 * (c) pixd != pixs (puts result into existing pixd) 01014 * (8) For clarity, if the case is known, use these patterns: 01015 * (a) pixd = pixCloseCompBrickDwa(NULL, pixs, ...); 01016 * (b) pixCloseCompBrickDwa(pixs, pixs, ...); 01017 * (c) pixCloseCompBrickDwa(pixd, pixs, ...); 01018 * (9) The size of pixd is determined by pixs. 01019 * (10) CAUTION: both hsize and vsize are being decomposed. 01020 * The decomposer chooses a product of sizes (call them 01021 * 'terms') for each that is close to the input size, 01022 * but not necessarily equal to it. It attempts to optimize: 01023 * (a) for consistency with the input values: the product 01024 * of terms is close to the input size 01025 * (b) for efficiency of the operation: the sum of the 01026 * terms is small; ideally about twice the square 01027 * root of the input size. 01028 * So, for example, if the input hsize = 37, which is 01029 * a prime number, the decomposer will break this into two 01030 * terms, 6 and 6, so that the net result is a dilation 01031 * with hsize = 36. 01032 */ 01033 PIX * 01034 pixCloseCompBrickDwa(PIX *pixd, 01035 PIX *pixs, 01036 l_int32 hsize, 01037 l_int32 vsize) 01038 { 01039 char *selnameh1, *selnameh2, *selnamev1, *selnamev2; 01040 l_int32 hsize1, hsize2, vsize1, vsize2, setborder; 01041 PIX *pixt1, *pixt2, *pixt3; 01042 01043 PROCNAME("pixCloseCompBrickDwa"); 01044 01045 if (!pixs) 01046 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 01047 if (pixGetDepth(pixs) != 1) 01048 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 01049 if (hsize < 1 || vsize < 1) 01050 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 01051 if (hsize > 63 || vsize > 63) 01052 return pixCloseCompBrickExtendDwa(pixd, pixs, hsize, vsize); 01053 01054 if (hsize == 1 && vsize == 1) 01055 return pixCopy(pixd, pixs); 01056 01057 hsize1 = hsize2 = vsize1 = vsize2 = 1; 01058 selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; 01059 if (hsize > 1) 01060 getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, 01061 &selnameh2, NULL, NULL); 01062 if (vsize > 1) 01063 getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, 01064 &selnamev1, &selnamev2); 01065 01066 pixt3 = NULL; 01067 /* For symmetric b.c., PIX_SET border for erosions */ 01068 setborder = getMorphBorderPixelColor(L_MORPH_ERODE, 1); 01069 pixt1 = pixAddBorder(pixs, 64, 0); 01070 01071 if (vsize == 1) { 01072 if (hsize2 == 1) 01073 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnameh1); 01074 else { 01075 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); 01076 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); 01077 if (setborder == 1) 01078 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); 01079 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1); 01080 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnameh2); 01081 } 01082 } 01083 else if (hsize == 1) { 01084 if (vsize2 == 1) 01085 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev1); 01086 else { 01087 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1); 01088 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnamev2); 01089 if (setborder == 1) 01090 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); 01091 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); 01092 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); 01093 } 01094 } 01095 else { /* vsize and hsize both > 1 */ 01096 if (hsize2 == 1 && vsize2 == 1) { 01097 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); 01098 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); 01099 if (setborder == 1) 01100 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); 01101 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1); 01102 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev1); 01103 } 01104 else if (vsize2 == 1) { 01105 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); 01106 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); 01107 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); 01108 if (setborder == 1) 01109 pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_SET); 01110 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnameh1); 01111 pixFMorphopGen_2(pixt3, pixt2, L_MORPH_ERODE, selnameh2); 01112 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev1); 01113 } 01114 else if (hsize2 == 1) { 01115 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); 01116 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); 01117 pixFMorphopGen_2(pixt3, pixt2, L_MORPH_DILATE, selnamev2); 01118 if (setborder == 1) 01119 pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_SET); 01120 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnameh1); 01121 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); 01122 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); 01123 } 01124 else { /* both directions are combed */ 01125 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); 01126 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); 01127 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); 01128 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); 01129 if (setborder == 1) 01130 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); 01131 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1); 01132 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnameh2); 01133 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); 01134 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); 01135 } 01136 } 01137 pixDestroy(&pixt3); 01138 01139 pixDestroy(&pixt1); 01140 pixt1 = pixRemoveBorder(pixt2, 64); 01141 pixDestroy(&pixt2); 01142 if (selnameh1) FREE(selnameh1); 01143 if (selnameh2) FREE(selnameh2); 01144 if (selnamev1) FREE(selnamev1); 01145 if (selnamev2) FREE(selnamev2); 01146 01147 if (!pixd) 01148 return pixt1; 01149 01150 pixTransferAllData(pixd, &pixt1, 0, 0); 01151 return pixd; 01152 } 01153 01154 01155 /*--------------------------------------------------------------------------* 01156 * Binary expanded composite morphological (dwa) ops with brick Sels * 01157 *--------------------------------------------------------------------------*/ 01158 /*! 01159 * pixDilateCompBrickExtendDwa() 01160 * 01161 * Input: pixd (<optional>; this can be null, equal to pixs, 01162 * or different from pixs) 01163 * pixs (1 bpp) 01164 * hsize (width of brick Sel) 01165 * vsize (height of brick Sel) 01166 * Return: pixd 01167 * 01168 * Notes: 01169 * (1) Ankur Jain suggested and implemented extending the composite 01170 * DWA operations beyond the 63 pixel limit. This is a 01171 * simplified and approximate implementation of the extension. 01172 * This allows arbitrary Dwa morph operations using brick Sels, 01173 * by decomposing the horizontal and vertical dilations into 01174 * a sequence of 63-element dilations plus a dilation of size 01175 * between 3 and 62. 01176 * (2) The 63-element dilations are exact, whereas the extra dilation 01177 * is approximate, because the underlying decomposition is 01178 * in pixDilateCompBrickDwa(). See there for further details. 01179 * (3) There are three cases: 01180 * (a) pixd == null (result into new pixd) 01181 * (b) pixd == pixs (in-place; writes result back to pixs) 01182 * (c) pixd != pixs (puts result into existing pixd) 01183 * (4) There is no need to call this directly: pixDilateCompBrickDwa() 01184 * calls this function if either brick dimension exceeds 63. 01185 */ 01186 PIX * 01187 pixDilateCompBrickExtendDwa(PIX *pixd, 01188 PIX *pixs, 01189 l_int32 hsize, 01190 l_int32 vsize) 01191 { 01192 l_int32 i, nops, nh, extrah, nv, extrav; 01193 PIX *pixt1, *pixt2, *pixt3; 01194 01195 PROCNAME("pixDilateCompBrickExtendDwa"); 01196 01197 if (!pixs) 01198 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 01199 if (pixGetDepth(pixs) != 1) 01200 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 01201 if (hsize < 1 || vsize < 1) 01202 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 01203 01204 if (hsize < 64 && vsize < 64) 01205 return pixDilateCompBrickDwa(pixd, pixs, hsize, vsize); 01206 01207 if (hsize > 63) 01208 getExtendedCompositeParameters(hsize, &nh, &extrah, NULL); 01209 if (vsize > 63) 01210 getExtendedCompositeParameters(vsize, &nv, &extrav, NULL); 01211 01212 /* Horizontal dilation first: pixs --> pixt2. Do not alter pixs. */ 01213 pixt1 = pixCreateTemplateNoInit(pixs); /* temp image */ 01214 if (hsize == 1) 01215 pixt2 = pixClone(pixs); 01216 else if (hsize < 64) 01217 pixt2 = pixDilateCompBrickDwa(NULL, pixs, hsize, 1); 01218 else if (hsize == 64) /* approximate */ 01219 pixt2 = pixDilateCompBrickDwa(NULL, pixs, 63, 1); 01220 else { 01221 nops = (extrah < 3) ? nh : nh + 1; 01222 if (nops & 1) { /* odd */ 01223 if (extrah > 2) 01224 pixt2 = pixDilateCompBrickDwa(NULL, pixs, extrah, 1); 01225 else 01226 pixt2 = pixDilateCompBrickDwa(NULL, pixs, 63, 1); 01227 for (i = 0; i < nops / 2; i++) { 01228 pixDilateCompBrickDwa(pixt1, pixt2, 63, 1); 01229 pixDilateCompBrickDwa(pixt2, pixt1, 63, 1); 01230 } 01231 } 01232 else { /* nops even */ 01233 if (extrah > 2) { 01234 pixDilateCompBrickDwa(pixt1, pixs, extrah, 1); 01235 pixt2 = pixDilateCompBrickDwa(NULL, pixt1, 63, 1); 01236 } 01237 else { /* they're all 63s */ 01238 pixDilateCompBrickDwa(pixt1, pixs, 63, 1); 01239 pixt2 = pixDilateCompBrickDwa(NULL, pixt1, 63, 1); 01240 } 01241 for (i = 0; i < nops / 2 - 1; i++) { 01242 pixDilateCompBrickDwa(pixt1, pixt2, 63, 1); 01243 pixDilateCompBrickDwa(pixt2, pixt1, 63, 1); 01244 } 01245 } 01246 } 01247 01248 /* Vertical dilation: pixt2 --> pixt3. */ 01249 if (vsize == 1) 01250 pixt3 = pixClone(pixt2); 01251 else if (vsize < 64) 01252 pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, vsize); 01253 else if (vsize == 64) /* approximate */ 01254 pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, 63); 01255 else { 01256 nops = (extrav < 3) ? nv : nv + 1; 01257 if (nops & 1) { /* odd */ 01258 if (extrav > 2) 01259 pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, extrav); 01260 else 01261 pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, 63); 01262 for (i = 0; i < nops / 2; i++) { 01263 pixDilateCompBrickDwa(pixt1, pixt3, 1, 63); 01264 pixDilateCompBrickDwa(pixt3, pixt1, 1, 63); 01265 } 01266 } 01267 else { /* nops even */ 01268 if (extrav > 2) { 01269 pixDilateCompBrickDwa(pixt1, pixt2, 1, extrav); 01270 pixt3 = pixDilateCompBrickDwa(NULL, pixt1, 1, 63); 01271 } 01272 else { /* they're all 63s */ 01273 pixDilateCompBrickDwa(pixt1, pixt2, 1, 63); 01274 pixt3 = pixDilateCompBrickDwa(NULL, pixt1, 1, 63); 01275 } 01276 for (i = 0; i < nops / 2 - 1; i++) { 01277 pixDilateCompBrickDwa(pixt1, pixt3, 1, 63); 01278 pixDilateCompBrickDwa(pixt3, pixt1, 1, 63); 01279 } 01280 } 01281 } 01282 pixDestroy(&pixt1); 01283 pixDestroy(&pixt2); 01284 01285 if (!pixd) 01286 return pixt3; 01287 01288 pixTransferAllData(pixd, &pixt3, 0, 0); 01289 return pixd; 01290 } 01291 01292 01293 /*! 01294 * pixErodeCompBrickExtendDwa() 01295 * 01296 * Input: pixd (<optional>; this can be null, equal to pixs, 01297 * or different from pixs) 01298 * pixs (1 bpp) 01299 * hsize (width of brick Sel) 01300 * vsize (height of brick Sel) 01301 * Return: pixd 01302 * 01303 * Notes: 01304 * (1) See pixDilateCompBrickExtendDwa() for usage. 01305 * (2) There is no need to call this directly: pixErodeCompBrickDwa() 01306 * calls this function if either brick dimension exceeds 63. 01307 */ 01308 PIX * 01309 pixErodeCompBrickExtendDwa(PIX *pixd, 01310 PIX *pixs, 01311 l_int32 hsize, 01312 l_int32 vsize) 01313 { 01314 l_int32 i, nops, nh, extrah, nv, extrav; 01315 PIX *pixt1, *pixt2, *pixt3; 01316 01317 PROCNAME("pixErodeCompBrickExtendDwa"); 01318 01319 if (!pixs) 01320 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 01321 if (pixGetDepth(pixs) != 1) 01322 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 01323 if (hsize < 1 || vsize < 1) 01324 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 01325 01326 if (hsize < 64 && vsize < 64) 01327 return pixErodeCompBrickDwa(pixd, pixs, hsize, vsize); 01328 01329 if (hsize > 63) 01330 getExtendedCompositeParameters(hsize, &nh, &extrah, NULL); 01331 if (vsize > 63) 01332 getExtendedCompositeParameters(vsize, &nv, &extrav, NULL); 01333 01334 /* Horizontal erosion first: pixs --> pixt2. Do not alter pixs. */ 01335 pixt1 = pixCreateTemplateNoInit(pixs); /* temp image */ 01336 if (hsize == 1) 01337 pixt2 = pixClone(pixs); 01338 else if (hsize < 64) 01339 pixt2 = pixErodeCompBrickDwa(NULL, pixs, hsize, 1); 01340 else if (hsize == 64) /* approximate */ 01341 pixt2 = pixErodeCompBrickDwa(NULL, pixs, 63, 1); 01342 else { 01343 nops = (extrah < 3) ? nh : nh + 1; 01344 if (nops & 1) { /* odd */ 01345 if (extrah > 2) 01346 pixt2 = pixErodeCompBrickDwa(NULL, pixs, extrah, 1); 01347 else 01348 pixt2 = pixErodeCompBrickDwa(NULL, pixs, 63, 1); 01349 for (i = 0; i < nops / 2; i++) { 01350 pixErodeCompBrickDwa(pixt1, pixt2, 63, 1); 01351 pixErodeCompBrickDwa(pixt2, pixt1, 63, 1); 01352 } 01353 } 01354 else { /* nops even */ 01355 if (extrah > 2) { 01356 pixErodeCompBrickDwa(pixt1, pixs, extrah, 1); 01357 pixt2 = pixErodeCompBrickDwa(NULL, pixt1, 63, 1); 01358 } 01359 else { /* they're all 63s */ 01360 pixErodeCompBrickDwa(pixt1, pixs, 63, 1); 01361 pixt2 = pixErodeCompBrickDwa(NULL, pixt1, 63, 1); 01362 } 01363 for (i = 0; i < nops / 2 - 1; i++) { 01364 pixErodeCompBrickDwa(pixt1, pixt2, 63, 1); 01365 pixErodeCompBrickDwa(pixt2, pixt1, 63, 1); 01366 } 01367 } 01368 } 01369 01370 /* Vertical erosion: pixt2 --> pixt3. */ 01371 if (vsize == 1) 01372 pixt3 = pixClone(pixt2); 01373 else if (vsize < 64) 01374 pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, vsize); 01375 else if (vsize == 64) /* approximate */ 01376 pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, 63); 01377 else { 01378 nops = (extrav < 3) ? nv : nv + 1; 01379 if (nops & 1) { /* odd */ 01380 if (extrav > 2) 01381 pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, extrav); 01382 else 01383 pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, 63); 01384 for (i = 0; i < nops / 2; i++) { 01385 pixErodeCompBrickDwa(pixt1, pixt3, 1, 63); 01386 pixErodeCompBrickDwa(pixt3, pixt1, 1, 63); 01387 } 01388 } 01389 else { /* nops even */ 01390 if (extrav > 2) { 01391 pixErodeCompBrickDwa(pixt1, pixt2, 1, extrav); 01392 pixt3 = pixErodeCompBrickDwa(NULL, pixt1, 1, 63); 01393 } 01394 else { /* they're all 63s */ 01395 pixErodeCompBrickDwa(pixt1, pixt2, 1, 63); 01396 pixt3 = pixErodeCompBrickDwa(NULL, pixt1, 1, 63); 01397 } 01398 for (i = 0; i < nops / 2 - 1; i++) { 01399 pixErodeCompBrickDwa(pixt1, pixt3, 1, 63); 01400 pixErodeCompBrickDwa(pixt3, pixt1, 1, 63); 01401 } 01402 } 01403 } 01404 pixDestroy(&pixt1); 01405 pixDestroy(&pixt2); 01406 01407 if (!pixd) 01408 return pixt3; 01409 01410 pixTransferAllData(pixd, &pixt3, 0, 0); 01411 return pixd; 01412 } 01413 01414 01415 /*! 01416 * pixOpenCompBrickExtendDwa() 01417 * 01418 * Input: pixd (<optional>; this can be null, equal to pixs, 01419 * or different from pixs) 01420 * pixs (1 bpp) 01421 * hsize (width of brick Sel) 01422 * vsize (height of brick Sel) 01423 * Return: pixd 01424 * 01425 * (1) There are three cases: 01426 * (a) pixd == null (result into new pixd) 01427 * (b) pixd == pixs (in-place; writes result back to pixs) 01428 * (c) pixd != pixs (puts result into existing pixd) 01429 * (2) There is no need to call this directly: pixOpenCompBrickDwa() 01430 * calls this function if either brick dimension exceeds 63. 01431 */ 01432 PIX * 01433 pixOpenCompBrickExtendDwa(PIX *pixd, 01434 PIX *pixs, 01435 l_int32 hsize, 01436 l_int32 vsize) 01437 { 01438 PIX *pixt; 01439 01440 PROCNAME("pixOpenCompBrickExtendDwa"); 01441 01442 if (!pixs) 01443 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 01444 if (pixGetDepth(pixs) != 1) 01445 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 01446 if (hsize < 1 || vsize < 1) 01447 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 01448 01449 pixt = pixErodeCompBrickExtendDwa(NULL, pixs, hsize, vsize); 01450 pixd = pixDilateCompBrickExtendDwa(pixd, pixt, hsize, vsize); 01451 pixDestroy(&pixt); 01452 return pixd; 01453 } 01454 01455 01456 /*! 01457 * pixCloseCompBrickExtendDwa() 01458 * 01459 * Input: pixd (<optional>; this can be null, equal to pixs, 01460 * or different from pixs) 01461 * pixs (1 bpp) 01462 * hsize (width of brick Sel) 01463 * vsize (height of brick Sel) 01464 * Return: pixd 01465 * 01466 * (1) There are three cases: 01467 * (a) pixd == null (result into new pixd) 01468 * (b) pixd == pixs (in-place; writes result back to pixs) 01469 * (c) pixd != pixs (puts result into existing pixd) 01470 * (2) There is no need to call this directly: pixCloseCompBrickDwa() 01471 * calls this function if either brick dimension exceeds 63. 01472 */ 01473 PIX * 01474 pixCloseCompBrickExtendDwa(PIX *pixd, 01475 PIX *pixs, 01476 l_int32 hsize, 01477 l_int32 vsize) 01478 { 01479 l_int32 bordercolor, borderx, bordery; 01480 PIX *pixt1, *pixt2, *pixt3; 01481 01482 PROCNAME("pixCloseCompBrickExtendDwa"); 01483 01484 if (!pixs) 01485 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 01486 if (pixGetDepth(pixs) != 1) 01487 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 01488 if (hsize < 1 || vsize < 1) 01489 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 01490 01491 /* For "safe closing" with ASYMMETRIC_MORPH_BC, we always need 01492 * an extra 32 OFF pixels around the image (in addition to 01493 * the 32 added pixels for all dwa operations), whereas with 01494 * SYMMETRIC_MORPH_BC this is not necessary. */ 01495 bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); 01496 if (bordercolor == 0) { /* asymmetric b.c. */ 01497 borderx = 32 + (hsize / 64) * 32; 01498 bordery = 32 + (vsize / 64) * 32; 01499 } 01500 else /* symmetric b.c. */ 01501 borderx = bordery = 32; 01502 pixt1 = pixAddBorderGeneral(pixs, borderx, borderx, bordery, bordery, 0); 01503 01504 pixt2 = pixDilateCompBrickExtendDwa(NULL, pixt1, hsize, vsize); 01505 pixErodeCompBrickExtendDwa(pixt1, pixt2, hsize, vsize); 01506 01507 pixt3 = pixRemoveBorderGeneral(pixt1, borderx, borderx, bordery, bordery); 01508 pixDestroy(&pixt1); 01509 pixDestroy(&pixt2); 01510 01511 if (!pixd) 01512 return pixt3; 01513 01514 pixTransferAllData(pixd, &pixt3, 0, 0); 01515 return pixd; 01516 } 01517 01518 01519 /*! 01520 * getExtendedCompositeParameters() 01521 * 01522 * Input: size (of linear Sel) 01523 * &pn (<return> number of 63 wide convolutions) 01524 * &pextra (<return> size of extra Sel) 01525 * &actualsize (<optional return> actual size used in operation) 01526 * Return: 0 if OK, 1 on error 01527 * 01528 * Notes: 01529 * (1) The DWA implementation allows Sels to be used with hits 01530 * up to 31 pixels from the origin, either horizontally or 01531 * vertically. Larger Sels can be used if decomposed into 01532 * a set of operations with Sels not exceeding 63 pixels 01533 * in either width or height (and with the origin as close 01534 * to the center of the Sel as possible). 01535 * (2) This returns the decomposition of a linear Sel of length 01536 * @size into a set of @n Sels of length 63 plus an extra 01537 * Sel of length @extra. 01538 * (3) For notation, let w == @size, n == @n, and e == @extra. 01539 * We have 1 < e < 63. 01540 * 01541 * Then if w < 64, we have n = 0 and e = w. 01542 * The general formula for w > 63 is: 01543 * w = 63 + (n - 1) * 62 + (e - 1) 01544 * 01545 * Where did this come from? Each successive convolution with 01546 * a Sel of length L adds a total length (L - 1) to w. 01547 * This accounts for using 62 for each additional Sel of size 63, 01548 * and using (e - 1) for the additional Sel of size e. 01549 * 01550 * Solving for n and e for w > 63: 01551 * n = 1 + Int((w - 63) / 62) 01552 * e = w - 63 - (n - 1) * 62 + 1 01553 * 01554 * The extra part is decomposed into two factors f1 and f2, 01555 * and the actual size of the extra part is 01556 * e' = f1 * f2 01557 * Then the actual width is: 01558 * w' = 63 + (n - 1) * 62 + f1 * f2 - 1 01559 */ 01560 l_int32 01561 getExtendedCompositeParameters(l_int32 size, 01562 l_int32 *pn, 01563 l_int32 *pextra, 01564 l_int32 *pactualsize) 01565 { 01566 l_int32 n, extra, fact1, fact2; 01567 01568 PROCNAME("getExtendedCompositeParameters"); 01569 01570 if (!pn || !pextra) 01571 return ERROR_INT("&n and &extra not both defined", procName, 1); 01572 01573 if (size <= 63) { 01574 n = 0; 01575 extra = L_MIN(1, size); 01576 } 01577 else { /* size > 63 */ 01578 n = 1 + (l_int32)((size - 63) / 62); 01579 extra = size - 63 - (n - 1) * 62 + 1; 01580 } 01581 01582 if (pactualsize) { 01583 selectComposableSizes(extra, &fact1, &fact2); 01584 *pactualsize = 63 + (n - 1) * 62 + fact1 * fact2 - 1; 01585 } 01586 01587 *pn = n; 01588 *pextra = extra; 01589 return 0; 01590 } 01591 01592