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 * morph.c 00018 * 00019 * Generic binary morphological ops implemented with rasterop 00020 * PIX *pixDilate() 00021 * PIX *pixErode() 00022 * PIX *pixHMT() 00023 * PIX *pixOpen() 00024 * PIX *pixClose() 00025 * PIX *pixCloseSafe() 00026 * PIX *pixOpenGeneralized() 00027 * PIX *pixCloseGeneralized() 00028 * 00029 * Binary morphological (raster) ops with brick Sels 00030 * PIX *pixDilateBrick() 00031 * PIX *pixErodeBrick() 00032 * PIX *pixOpenBrick() 00033 * PIX *pixCloseBrick() 00034 * PIX *pixCloseSafeBrick() 00035 * 00036 * Binary composed morphological (raster) ops with brick Sels 00037 * l_int32 selectComposableSels() 00038 * l_int32 selectComposableSizes() 00039 * PIX *pixDilateCompBrick() 00040 * PIX *pixErodeCompBrick() 00041 * PIX *pixOpenCompBrick() 00042 * PIX *pixCloseCompBrick() 00043 * PIX *pixCloseSafeCompBrick() 00044 * 00045 * Functions associated with boundary conditions 00046 * void resetMorphBoundaryCondition() 00047 * l_int32 getMorphBorderPixelColor() 00048 * 00049 * Static helpers for arg processing 00050 * static PIX *processMorphArgs1() 00051 * static PIX *processMorphArgs2() 00052 * 00053 * You are provided with many simple ways to do binary morphology. 00054 * In particular, if you are using brick Sels, there are six 00055 * convenient methods, all specially tailored for separable operations 00056 * on brick Sels. A "brick" Sel is a Sel that is a rectangle 00057 * of solid SEL_HITs with the origin at or near the center. 00058 * Note that a brick Sel can have one dimension of size 1. 00059 * This is very common. All the brick Sel operations are 00060 * separable, meaning the operation is done first in the horizontal 00061 * direction and then in the vertical direction. If one of the 00062 * dimensions is 1, this is a special case where the operation is 00063 * only performed in the other direction. 00064 * 00065 * These six brick Sel methods are enumerated as follows: 00066 * 00067 * (1) Brick Sels: pix*Brick(), where * = {Dilate, Erode, Open, Close}. 00068 * These are separable rasterop implementations. The Sels are 00069 * automatically generated, used, and destroyed at the end. 00070 * You can get the result as a new Pix, in-place back into the src Pix, 00071 * or written to another existing Pix. 00072 * 00073 * (2) Brick Sels: pix*CompBrick(), where * = {Dilate, Erode, Open, Close}. 00074 * These are separable, 2-way composite, rasterop implementations. 00075 * The Sels are automatically generated, used, and destroyed at the end. 00076 * You can get the result as a new Pix, in-place back into the src Pix, 00077 * or written to another existing Pix. For large Sels, these are 00078 * considerably faster than the corresponding pix*Brick() functions. 00079 * N.B.: The size of the Sels that are actually used are typically 00080 * close to, but not exactly equal to, the size input to the function. 00081 * 00082 * (3) Brick Sels: pix*BrickDwa(), where * = {Dilate, Erode, Open, Close}. 00083 * These are separable dwa (destination word accumulation) 00084 * implementations. They use auto-gen'd dwa code. You can get 00085 * the result as a new Pix, in-place back into the src Pix, 00086 * or written to another existing Pix. This is typically 00087 * about 3x faster than the analogous rasterop pix*Brick() 00088 * function, but it has the limitation that the Sel size must 00089 * be less than 63. This is pre-set to work on a number 00090 * of pre-generated Sels. If you want to use other Sels, the 00091 * code can be auto-gen'd for them; see the instructions in morphdwa.c. 00092 * 00093 * (4) Same as (1), but you run it through pixMorphSequence(), with 00094 * the sequence string either compiled in or generated using sprintf. 00095 * All intermediate images and Sels are created, used and destroyed. 00096 * You always get the result as a new Pix. For example, you can 00097 * specify a separable 11 x 17 brick opening as "o11.17", 00098 * or you can specify the horizontal and vertical operations 00099 * explicitly as "o11.1 + o1.11". See morphseq.c for details. 00100 * 00101 * (5) Same as (2), but you run it through pixMorphCompSequence(), with 00102 * the sequence string either compiled in or generated using sprintf. 00103 * All intermediate images and Sels are created, used and destroyed. 00104 * You always get the result as a new Pix. See morphseq.c for details. 00105 * 00106 * (6) Same as (3), but you run it through pixMorphSequenceDwa(), with 00107 * the sequence string either compiled in or generated using sprintf. 00108 * All intermediate images and Sels are created, used and destroyed. 00109 * You always get the result as a new Pix. See morphseq.c for details. 00110 * 00111 * If you are using Sels that are not bricks, you have two choices: 00112 * (a) simplest: use the basic rasterop implementations (pixDilate(), ...) 00113 * (b) fastest: generate the destination word accumumlation (dwa) 00114 * code for your Sels and compile it with the library. 00115 * 00116 * For an example, see flipdetect.c, which gives implementations 00117 * using hit-miss Sels with both the rasterop and dwa versions. 00118 * For the latter, the dwa code resides in fliphmtgen.c, and it 00119 * was generated by prog/flipselgen.c. Both the rasterop and dwa 00120 * implementations are tested by prog/fliptest.c. 00121 * 00122 * A global constant MORPH_BC is used to set the boundary conditions 00123 * for rasterop-based binary morphology. MORPH_BC, in morph.c, 00124 * is set by default to ASYMMETRIC_MORPH_BC for a non-symmetric 00125 * convention for boundary pixels in dilation and erosion: 00126 * All pixels outside the image are assumed to be OFF 00127 * for both dilation and erosion. 00128 * To use a symmetric definition, see comments in pixErode() 00129 * and reset MORPH_BC to SYMMETRIC_MORPH_BC, using 00130 * resetMorphBoundaryCondition(). 00131 * 00132 * Boundary artifacts are possible in closing when the non-symmetric 00133 * boundary conditions are used, because foreground pixels very close 00134 * to the edge can be removed. This can be avoided by using either 00135 * the symmetric boundary conditions or the function pixCloseSafe(), 00136 * which adds a border before the operation and removes it afterwards. 00137 * 00138 * The hit-miss transform (HMT) is the bit-and of 2 erosions: 00139 * (erosion of the src by the hits) & (erosion of the bit-inverted 00140 * src by the misses) 00141 * 00142 * The 'generalized opening' is an HMT followed by a dilation that uses 00143 * only the hits of the hit-miss Sel. 00144 * The 'generalized closing' is a dilation (again, with the hits 00145 * of a hit-miss Sel), followed by the HMT. 00146 * Both of these 'generalized' functions are idempotent. 00147 * 00148 * These functions are extensively tested in prog/binmorph1_reg.c, 00149 * prog/binmorph2_reg.c, and prog/binmorph3_reg.c. 00150 */ 00151 00152 #include <stdio.h> 00153 #include <math.h> 00154 #include "allheaders.h" 00155 00156 /* Global constant; initialized here; must be declared extern 00157 * in other files to access it directly. However, in most 00158 * cases that is not necessary, because it can be reset 00159 * using resetMorphBoundaryCondition(). */ 00160 LEPT_DLL l_int32 MORPH_BC = ASYMMETRIC_MORPH_BC; 00161 00162 /* We accept this cost in extra rasterops for decomposing exactly. */ 00163 static const l_int32 ACCEPTABLE_COST = 5; 00164 00165 /* Static helpers for arg processing */ 00166 static PIX * processMorphArgs1(PIX *pixd, PIX *pixs, SEL *sel, PIX **ppixt); 00167 static PIX * processMorphArgs2(PIX *pixd, PIX *pixs, SEL *sel); 00168 00169 00170 /*-----------------------------------------------------------------* 00171 * Generic binary morphological ops implemented with rasterop * 00172 *-----------------------------------------------------------------*/ 00173 /*! 00174 * pixDilate() 00175 * 00176 * Input: pixd (<optional>; this can be null, equal to pixs, 00177 * or different from pixs) 00178 * pixs (1 bpp) 00179 * sel 00180 * Return: pixd 00181 * 00182 * Notes: 00183 * (1) This dilates src using hits in Sel. 00184 * (2) There are three cases: 00185 * (a) pixd == null (result into new pixd) 00186 * (b) pixd == pixs (in-place; writes result back to pixs) 00187 * (c) pixd != pixs (puts result into existing pixd) 00188 * (3) For clarity, if the case is known, use these patterns: 00189 * (a) pixd = pixDilate(NULL, pixs, ...); 00190 * (b) pixDilate(pixs, pixs, ...); 00191 * (c) pixDilate(pixd, pixs, ...); 00192 * (4) The size of the result is determined by pixs. 00193 */ 00194 PIX * 00195 pixDilate(PIX *pixd, 00196 PIX *pixs, 00197 SEL *sel) 00198 { 00199 l_int32 i, j, w, h, sx, sy, cx, cy, seldata; 00200 PIX *pixt; 00201 00202 PROCNAME("pixDilate"); 00203 00204 if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL) 00205 return (PIX *)ERROR_PTR("processMorphArgs1 failed", procName, pixd); 00206 00207 pixGetDimensions(pixs, &w, &h, NULL); 00208 selGetParameters(sel, &sy, &sx, &cy, &cx); 00209 pixClearAll(pixd); 00210 for (i = 0; i < sy; i++) { 00211 for (j = 0; j < sx; j++) { 00212 seldata = sel->data[i][j]; 00213 if (seldata == 1) { /* src | dst */ 00214 pixRasterop(pixd, j - cx, i - cy, w, h, PIX_SRC | PIX_DST, 00215 pixt, 0, 0); 00216 } 00217 } 00218 } 00219 00220 pixDestroy(&pixt); 00221 return pixd; 00222 } 00223 00224 00225 /*! 00226 * pixErode() 00227 * 00228 * Input: pixd (<optional>; this can be null, equal to pixs, 00229 * or different from pixs) 00230 * pixs (1 bpp) 00231 * sel 00232 * Return: pixd 00233 * 00234 * Notes: 00235 * (1) This erodes src using hits in Sel. 00236 * (2) There are three cases: 00237 * (a) pixd == null (result into new pixd) 00238 * (b) pixd == pixs (in-place; writes result back to pixs) 00239 * (c) pixd != pixs (puts result into existing pixd) 00240 * (3) For clarity, if the case is known, use these patterns: 00241 * (a) pixd = pixErode(NULL, pixs, ...); 00242 * (b) pixErode(pixs, pixs, ...); 00243 * (c) pixErode(pixd, pixs, ...); 00244 * (4) The size of the result is determined by pixs. 00245 */ 00246 PIX * 00247 pixErode(PIX *pixd, 00248 PIX *pixs, 00249 SEL *sel) 00250 { 00251 l_int32 i, j, w, h, sx, sy, cx, cy, seldata; 00252 l_int32 xp, yp, xn, yn; 00253 PIX *pixt; 00254 00255 PROCNAME("pixErode"); 00256 00257 if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL) 00258 return (PIX *)ERROR_PTR("processMorphArgs1 failed", procName, pixd); 00259 00260 pixGetDimensions(pixs, &w, &h, NULL); 00261 selGetParameters(sel, &sy, &sx, &cy, &cx); 00262 pixSetAll(pixd); 00263 for (i = 0; i < sy; i++) { 00264 for (j = 0; j < sx; j++) { 00265 seldata = sel->data[i][j]; 00266 if (seldata == 1) { /* src & dst */ 00267 pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC & PIX_DST, 00268 pixt, 0, 0); 00269 } 00270 } 00271 } 00272 00273 /* Clear near edges. We do this for the asymmetric boundary 00274 * condition convention that implements erosion assuming all 00275 * pixels surrounding the image are OFF. If you use a 00276 * use a symmetric b.c. convention, where the erosion is 00277 * implemented assuming pixels surrounding the image 00278 * are ON, these operations are omitted. */ 00279 if (MORPH_BC == ASYMMETRIC_MORPH_BC) { 00280 selFindMaxTranslations(sel, &xp, &yp, &xn, &yn); 00281 if (xp > 0) 00282 pixRasterop(pixd, 0, 0, xp, h, PIX_CLR, NULL, 0, 0); 00283 if (xn > 0) 00284 pixRasterop(pixd, w - xn, 0, xn, h, PIX_CLR, NULL, 0, 0); 00285 if (yp > 0) 00286 pixRasterop(pixd, 0, 0, w, yp, PIX_CLR, NULL, 0, 0); 00287 if (yn > 0) 00288 pixRasterop(pixd, 0, h - yn, w, yn, PIX_CLR, NULL, 0, 0); 00289 } 00290 00291 pixDestroy(&pixt); 00292 return pixd; 00293 } 00294 00295 00296 /*! 00297 * pixHMT() 00298 * 00299 * Input: pixd (<optional>; this can be null, equal to pixs, 00300 * or different from pixs) 00301 * pixs (1 bpp) 00302 * sel 00303 * Return: pixd 00304 * 00305 * Notes: 00306 * (1) The hit-miss transform erodes the src, using both hits 00307 * and misses in the Sel. It ANDs the shifted src for hits 00308 * and ANDs the inverted shifted src for misses. 00309 * (2) There are three cases: 00310 * (a) pixd == null (result into new pixd) 00311 * (b) pixd == pixs (in-place; writes result back to pixs) 00312 * (c) pixd != pixs (puts result into existing pixd) 00313 * (3) For clarity, if the case is known, use these patterns: 00314 * (a) pixd = pixHMT(NULL, pixs, ...); 00315 * (b) pixHMT(pixs, pixs, ...); 00316 * (c) pixHMT(pixd, pixs, ...); 00317 * (4) The size of the result is determined by pixs. 00318 */ 00319 PIX * 00320 pixHMT(PIX *pixd, 00321 PIX *pixs, 00322 SEL *sel) 00323 { 00324 l_int32 i, j, w, h, sx, sy, cx, cy, firstrasterop, seldata; 00325 l_int32 xp, yp, xn, yn; 00326 PIX *pixt; 00327 00328 PROCNAME("pixHMT"); 00329 00330 if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL) 00331 return (PIX *)ERROR_PTR("processMorphArgs1 failed", procName, pixd); 00332 00333 pixGetDimensions(pixs, &w, &h, NULL); 00334 selGetParameters(sel, &sy, &sx, &cy, &cx); 00335 firstrasterop = TRUE; 00336 for (i = 0; i < sy; i++) { 00337 for (j = 0; j < sx; j++) { 00338 seldata = sel->data[i][j]; 00339 if (seldata == 1) { /* hit */ 00340 if (firstrasterop == TRUE) { /* src only */ 00341 pixClearAll(pixd); 00342 pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC, 00343 pixt, 0, 0); 00344 firstrasterop = FALSE; 00345 } 00346 else { /* src & dst */ 00347 pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC & PIX_DST, 00348 pixt, 0, 0); 00349 } 00350 } 00351 else if (seldata == 2) { /* miss */ 00352 if (firstrasterop == TRUE) { /* ~src only */ 00353 pixSetAll(pixd); 00354 pixRasterop(pixd, cx - j, cy - i, w, h, PIX_NOT(PIX_SRC), 00355 pixt, 0, 0); 00356 firstrasterop = FALSE; 00357 } 00358 else { /* ~src & dst */ 00359 pixRasterop(pixd, cx - j, cy - i, w, h, 00360 PIX_NOT(PIX_SRC) & PIX_DST, 00361 pixt, 0, 0); 00362 } 00363 } 00364 } 00365 } 00366 00367 /* Clear near edges */ 00368 selFindMaxTranslations(sel, &xp, &yp, &xn, &yn); 00369 if (xp > 0) 00370 pixRasterop(pixd, 0, 0, xp, h, PIX_CLR, NULL, 0, 0); 00371 if (xn > 0) 00372 pixRasterop(pixd, w - xn, 0, xn, h, PIX_CLR, NULL, 0, 0); 00373 if (yp > 0) 00374 pixRasterop(pixd, 0, 0, w, yp, PIX_CLR, NULL, 0, 0); 00375 if (yn > 0) 00376 pixRasterop(pixd, 0, h - yn, w, yn, PIX_CLR, NULL, 0, 0); 00377 00378 pixDestroy(&pixt); 00379 return pixd; 00380 } 00381 00382 00383 /*! 00384 * pixOpen() 00385 * 00386 * Input: pixd (<optional>; this can be null, equal to pixs, 00387 * or different from pixs) 00388 * pixs (1 bpp) 00389 * sel 00390 * Return: pixd 00391 * 00392 * Notes: 00393 * (1) Generic morphological opening, using hits in the Sel. 00394 * (2) There are three cases: 00395 * (a) pixd == null (result into new pixd) 00396 * (b) pixd == pixs (in-place; writes result back to pixs) 00397 * (c) pixd != pixs (puts result into existing pixd) 00398 * (3) For clarity, if the case is known, use these patterns: 00399 * (a) pixd = pixOpen(NULL, pixs, ...); 00400 * (b) pixOpen(pixs, pixs, ...); 00401 * (c) pixOpen(pixd, pixs, ...); 00402 * (4) The size of the result is determined by pixs. 00403 */ 00404 PIX * 00405 pixOpen(PIX *pixd, 00406 PIX *pixs, 00407 SEL *sel) 00408 { 00409 PIX *pixt; 00410 00411 PROCNAME("pixOpen"); 00412 00413 if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL) 00414 return (PIX *)ERROR_PTR("pixd not returned", procName, pixd); 00415 00416 if ((pixt = pixErode(NULL, pixs, sel)) == NULL) 00417 return (PIX *)ERROR_PTR("pixt not made", procName, pixd); 00418 pixDilate(pixd, pixt, sel); 00419 pixDestroy(&pixt); 00420 00421 return pixd; 00422 } 00423 00424 00425 /*! 00426 * pixClose() 00427 * 00428 * Input: pixd (<optional>; this can be null, equal to pixs, 00429 * or different from pixs) 00430 * pixs (1 bpp) 00431 * sel 00432 * Return: pixd 00433 * 00434 * Notes: 00435 * (1) Generic morphological closing, using hits in the Sel. 00436 * (2) This implementation is a strict dual of the opening if 00437 * symmetric boundary conditions are used (see notes at top 00438 * of this file). 00439 * (3) There are three cases: 00440 * (a) pixd == null (result into new pixd) 00441 * (b) pixd == pixs (in-place; writes result back to pixs) 00442 * (c) pixd != pixs (puts result into existing pixd) 00443 * (4) For clarity, if the case is known, use these patterns: 00444 * (a) pixd = pixClose(NULL, pixs, ...); 00445 * (b) pixClose(pixs, pixs, ...); 00446 * (c) pixClose(pixd, pixs, ...); 00447 * (5) The size of the result is determined by pixs. 00448 */ 00449 PIX * 00450 pixClose(PIX *pixd, 00451 PIX *pixs, 00452 SEL *sel) 00453 { 00454 PIX *pixt; 00455 00456 PROCNAME("pixClose"); 00457 00458 if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL) 00459 return (PIX *)ERROR_PTR("pixd not returned", procName, pixd); 00460 00461 if ((pixt = pixDilate(NULL, pixs, sel)) == NULL) 00462 return (PIX *)ERROR_PTR("pixt not made", procName, pixd); 00463 pixErode(pixd, pixt, sel); 00464 pixDestroy(&pixt); 00465 00466 return pixd; 00467 } 00468 00469 00470 /*! 00471 * pixCloseSafe() 00472 * 00473 * Input: pixd (<optional>; this can be null, equal to pixs, 00474 * or different from pixs) 00475 * pixs (1 bpp) 00476 * sel 00477 * Return: pixd 00478 * 00479 * Notes: 00480 * (1) Generic morphological closing, using hits in the Sel. 00481 * (2) If non-symmetric boundary conditions are used, this 00482 * function adds a border of OFF pixels that is of 00483 * sufficient size to avoid losing pixels from the dilation, 00484 * and it removes the border after the operation is finished. 00485 * It thus enforces a correct extensive result for closing. 00486 * (3) If symmetric b.c. are used, it is not necessary to add 00487 * and remove this border. 00488 * (4) There are three cases: 00489 * (a) pixd == null (result into new pixd) 00490 * (b) pixd == pixs (in-place; writes result back to pixs) 00491 * (c) pixd != pixs (puts result into existing pixd) 00492 * (5) For clarity, if the case is known, use these patterns: 00493 * (a) pixd = pixCloseSafe(NULL, pixs, ...); 00494 * (b) pixCloseSafe(pixs, pixs, ...); 00495 * (c) pixCloseSafe(pixd, pixs, ...); 00496 * (6) The size of the result is determined by pixs. 00497 */ 00498 PIX * 00499 pixCloseSafe(PIX *pixd, 00500 PIX *pixs, 00501 SEL *sel) 00502 { 00503 l_int32 xp, yp, xn, yn, xmax, xbord; 00504 PIX *pixt1, *pixt2; 00505 00506 PROCNAME("pixCloseSafe"); 00507 00508 if (!pixs) 00509 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 00510 if (!sel) 00511 return (PIX *)ERROR_PTR("sel not defined", procName, pixd); 00512 if (pixGetDepth(pixs) != 1) 00513 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 00514 00515 /* Symmetric b.c. handles correctly without added pixels */ 00516 if (MORPH_BC == SYMMETRIC_MORPH_BC) 00517 return pixClose(pixd, pixs, sel); 00518 00519 selFindMaxTranslations(sel, &xp, &yp, &xn, &yn); 00520 xmax = L_MAX(xp, xn); 00521 xbord = 32 * ((xmax + 31) / 32); /* full 32 bit words */ 00522 00523 if ((pixt1 = pixAddBorderGeneral(pixs, xbord, xbord, yp, yn, 0)) == NULL) 00524 return (PIX *)ERROR_PTR("pixt1 not made", procName, pixd); 00525 pixClose(pixt1, pixt1, sel); 00526 if ((pixt2 = pixRemoveBorderGeneral(pixt1, xbord, xbord, yp, yn)) == NULL) 00527 return (PIX *)ERROR_PTR("pixt2 not made", procName, pixd); 00528 pixDestroy(&pixt1); 00529 00530 if (!pixd) 00531 return pixt2; 00532 00533 pixCopy(pixd, pixt2); 00534 pixDestroy(&pixt2); 00535 return pixd; 00536 } 00537 00538 00539 /*! 00540 * pixOpenGeneralized() 00541 * 00542 * Input: pixd (<optional>; this can be null, equal to pixs, 00543 * or different from pixs) 00544 * pixs (1 bpp) 00545 * sel 00546 * Return: pixd 00547 * 00548 * Notes: 00549 * (1) Generalized morphological opening, using both hits and 00550 * misses in the Sel. 00551 * (2) This does a hit-miss transform, followed by a dilation 00552 * using the hits. 00553 * (3) There are three cases: 00554 * (a) pixd == null (result into new pixd) 00555 * (b) pixd == pixs (in-place; writes result back to pixs) 00556 * (c) pixd != pixs (puts result into existing pixd) 00557 * (4) For clarity, if the case is known, use these patterns: 00558 * (a) pixd = pixOpenGeneralized(NULL, pixs, ...); 00559 * (b) pixOpenGeneralized(pixs, pixs, ...); 00560 * (c) pixOpenGeneralized(pixd, pixs, ...); 00561 * (5) The size of the result is determined by pixs. 00562 */ 00563 PIX * 00564 pixOpenGeneralized(PIX *pixd, 00565 PIX *pixs, 00566 SEL *sel) 00567 { 00568 PIX *pixt; 00569 00570 PROCNAME("pixOpenGeneralized"); 00571 00572 if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL) 00573 return (PIX *)ERROR_PTR("pixd not returned", procName, pixd); 00574 00575 if ((pixt = pixHMT(NULL, pixs, sel)) == NULL) 00576 return (PIX *)ERROR_PTR("pixt not made", procName, pixd); 00577 pixDilate(pixd, pixt, sel); 00578 pixDestroy(&pixt); 00579 return pixd; 00580 } 00581 00582 00583 /*! 00584 * pixCloseGeneralized() 00585 * 00586 * Input: pixd (<optional>; this can be null, equal to pixs, 00587 * or different from pixs) 00588 * pixs (1 bpp) 00589 * sel 00590 * Return: pixd 00591 * 00592 * Notes: 00593 * (1) Generalized morphological closing, using both hits and 00594 * misses in the Sel. 00595 * (2) This does a dilation using the hits, followed by a 00596 * hit-miss transform. 00597 * (3) This operation is a dual of the generalized opening. 00598 * (4) There are three cases: 00599 * (a) pixd == null (result into new pixd) 00600 * (b) pixd == pixs (in-place; writes result back to pixs) 00601 * (c) pixd != pixs (puts result into existing pixd) 00602 * (5) For clarity, if the case is known, use these patterns: 00603 * (a) pixd = pixCloseGeneralized(NULL, pixs, ...); 00604 * (b) pixCloseGeneralized(pixs, pixs, ...); 00605 * (c) pixCloseGeneralized(pixd, pixs, ...); 00606 * (6) The size of the result is determined by pixs. 00607 */ 00608 PIX * 00609 pixCloseGeneralized(PIX *pixd, 00610 PIX *pixs, 00611 SEL *sel) 00612 { 00613 PIX *pixt; 00614 00615 PROCNAME("pixCloseGeneralized"); 00616 00617 if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL) 00618 return (PIX *)ERROR_PTR("pixd not returned", procName, pixd); 00619 00620 if ((pixt = pixDilate(NULL, pixs, sel)) == NULL) 00621 return (PIX *)ERROR_PTR("pixt not made", procName, pixd); 00622 pixHMT(pixd, pixt, sel); 00623 pixDestroy(&pixt); 00624 00625 return pixd; 00626 } 00627 00628 00629 /*-----------------------------------------------------------------* 00630 * Binary morphological (raster) ops with brick Sels * 00631 *-----------------------------------------------------------------*/ 00632 /*! 00633 * pixDilateBrick() 00634 * 00635 * Input: pixd (<optional>; this can be null, equal to pixs, 00636 * or different from pixs) 00637 * pixs (1 bpp) 00638 * hsize (width of brick Sel) 00639 * vsize (height of brick Sel) 00640 * Return: pixd 00641 * 00642 * Notes: 00643 * (1) Sel is a brick with all elements being hits 00644 * (2) The origin is at (x, y) = (hsize/2, vsize/2) 00645 * (3) Do separably if both hsize and vsize are > 1. 00646 * (4) There are three cases: 00647 * (a) pixd == null (result into new pixd) 00648 * (b) pixd == pixs (in-place; writes result back to pixs) 00649 * (c) pixd != pixs (puts result into existing pixd) 00650 * (5) For clarity, if the case is known, use these patterns: 00651 * (a) pixd = pixDilateBrick(NULL, pixs, ...); 00652 * (b) pixDilateBrick(pixs, pixs, ...); 00653 * (c) pixDilateBrick(pixd, pixs, ...); 00654 * (6) The size of the result is determined by pixs. 00655 */ 00656 PIX * 00657 pixDilateBrick(PIX *pixd, 00658 PIX *pixs, 00659 l_int32 hsize, 00660 l_int32 vsize) 00661 { 00662 PIX *pixt; 00663 SEL *sel, *selh, *selv; 00664 00665 PROCNAME("pixDilateBrick"); 00666 00667 if (!pixs) 00668 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 00669 if (pixGetDepth(pixs) != 1) 00670 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 00671 if (hsize < 1 || vsize < 1) 00672 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 00673 00674 if (hsize == 1 && vsize == 1) 00675 return pixCopy(pixd, pixs); 00676 if (hsize == 1 || vsize == 1) { /* no intermediate result */ 00677 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); 00678 pixd = pixDilate(pixd, pixs, sel); 00679 selDestroy(&sel); 00680 } 00681 else { 00682 selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT); 00683 selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT); 00684 pixt = pixDilate(NULL, pixs, selh); 00685 pixd = pixDilate(pixd, pixt, selv); 00686 pixDestroy(&pixt); 00687 selDestroy(&selh); 00688 selDestroy(&selv); 00689 } 00690 00691 return pixd; 00692 } 00693 00694 00695 /*! 00696 * pixErodeBrick() 00697 * 00698 * Input: pixd (<optional>; this can be null, equal to pixs, 00699 * or different from pixs) 00700 * pixs (1 bpp) 00701 * hsize (width of brick Sel) 00702 * vsize (height of brick Sel) 00703 * Return: pixd 00704 * 00705 * Notes: 00706 * (1) Sel is a brick with all elements being hits 00707 * (2) The origin is at (x, y) = (hsize/2, vsize/2) 00708 * (3) Do separably if both hsize and vsize are > 1. 00709 * (4) 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 * (5) For clarity, if the case is known, use these patterns: 00714 * (a) pixd = pixErodeBrick(NULL, pixs, ...); 00715 * (b) pixErodeBrick(pixs, pixs, ...); 00716 * (c) pixErodeBrick(pixd, pixs, ...); 00717 * (6) The size of the result is determined by pixs. 00718 */ 00719 PIX * 00720 pixErodeBrick(PIX *pixd, 00721 PIX *pixs, 00722 l_int32 hsize, 00723 l_int32 vsize) 00724 { 00725 PIX *pixt; 00726 SEL *sel, *selh, *selv; 00727 00728 PROCNAME("pixErodeBrick"); 00729 00730 if (!pixs) 00731 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 00732 if (pixGetDepth(pixs) != 1) 00733 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 00734 if (hsize < 1 || vsize < 1) 00735 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 00736 00737 if (hsize == 1 && vsize == 1) 00738 return pixCopy(pixd, pixs); 00739 if (hsize == 1 || vsize == 1) { /* no intermediate result */ 00740 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); 00741 pixd = pixErode(pixd, pixs, sel); 00742 selDestroy(&sel); 00743 } 00744 else { 00745 selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT); 00746 selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT); 00747 pixt = pixErode(NULL, pixs, selh); 00748 pixd = pixErode(pixd, pixt, selv); 00749 pixDestroy(&pixt); 00750 selDestroy(&selh); 00751 selDestroy(&selv); 00752 } 00753 00754 return pixd; 00755 } 00756 00757 00758 /*! 00759 * pixOpenBrick() 00760 * 00761 * Input: pixd (<optional>; this can be null, equal to pixs, 00762 * or different from pixs) 00763 * pixs (1 bpp) 00764 * hsize (width of brick Sel) 00765 * vsize (height of brick Sel) 00766 * Return: pixd, or null on error 00767 * 00768 * Notes: 00769 * (1) Sel is a brick with all elements being hits 00770 * (2) The origin is at (x, y) = (hsize/2, vsize/2) 00771 * (3) Do separably if both hsize and vsize are > 1. 00772 * (4) There are three cases: 00773 * (a) pixd == null (result into new pixd) 00774 * (b) pixd == pixs (in-place; writes result back to pixs) 00775 * (c) pixd != pixs (puts result into existing pixd) 00776 * (5) For clarity, if the case is known, use these patterns: 00777 * (a) pixd = pixOpenBrick(NULL, pixs, ...); 00778 * (b) pixOpenBrick(pixs, pixs, ...); 00779 * (c) pixOpenBrick(pixd, pixs, ...); 00780 * (6) The size of the result is determined by pixs. 00781 */ 00782 PIX * 00783 pixOpenBrick(PIX *pixd, 00784 PIX *pixs, 00785 l_int32 hsize, 00786 l_int32 vsize) 00787 { 00788 PIX *pixt; 00789 SEL *sel, *selh, *selv; 00790 00791 PROCNAME("pixOpenBrick"); 00792 00793 if (!pixs) 00794 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 00795 if (pixGetDepth(pixs) != 1) 00796 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 00797 if (hsize < 1 || vsize < 1) 00798 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 00799 00800 if (hsize == 1 && vsize == 1) 00801 return pixCopy(pixd, pixs); 00802 if (hsize == 1 || vsize == 1) { /* no intermediate result */ 00803 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); 00804 pixd = pixOpen(pixd, pixs, sel); 00805 selDestroy(&sel); 00806 } 00807 else { /* do separably */ 00808 selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT); 00809 selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT); 00810 pixt = pixErode(NULL, pixs, selh); 00811 pixd = pixErode(pixd, pixt, selv); 00812 pixDilate(pixt, pixd, selh); 00813 pixDilate(pixd, pixt, selv); 00814 pixDestroy(&pixt); 00815 selDestroy(&selh); 00816 selDestroy(&selv); 00817 } 00818 00819 return pixd; 00820 } 00821 00822 00823 /*! 00824 * pixCloseBrick() 00825 * 00826 * Input: pixd (<optional>; this can be null, equal to pixs, 00827 * or different from pixs) 00828 * pixs (1 bpp) 00829 * hsize (width of brick Sel) 00830 * vsize (height of brick Sel) 00831 * Return: pixd, or null on error 00832 * 00833 * Notes: 00834 * (1) Sel is a brick with all elements being hits 00835 * (2) The origin is at (x, y) = (hsize/2, vsize/2) 00836 * (3) Do separably if both hsize and vsize are > 1. 00837 * (4) There are three cases: 00838 * (a) pixd == null (result into new pixd) 00839 * (b) pixd == pixs (in-place; writes result back to pixs) 00840 * (c) pixd != pixs (puts result into existing pixd) 00841 * (5) For clarity, if the case is known, use these patterns: 00842 * (a) pixd = pixCloseBrick(NULL, pixs, ...); 00843 * (b) pixCloseBrick(pixs, pixs, ...); 00844 * (c) pixCloseBrick(pixd, pixs, ...); 00845 * (6) The size of the result is determined by pixs. 00846 */ 00847 PIX * 00848 pixCloseBrick(PIX *pixd, 00849 PIX *pixs, 00850 l_int32 hsize, 00851 l_int32 vsize) 00852 { 00853 PIX *pixt; 00854 SEL *sel, *selh, *selv; 00855 00856 PROCNAME("pixCloseBrick"); 00857 00858 if (!pixs) 00859 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 00860 if (pixGetDepth(pixs) != 1) 00861 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 00862 if (hsize < 1 || vsize < 1) 00863 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 00864 00865 if (hsize == 1 && vsize == 1) 00866 return pixCopy(pixd, pixs); 00867 if (hsize == 1 || vsize == 1) { /* no intermediate result */ 00868 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); 00869 pixd = pixClose(pixd, pixs, sel); 00870 selDestroy(&sel); 00871 } 00872 else { /* do separably */ 00873 selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT); 00874 selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT); 00875 pixt = pixDilate(NULL, pixs, selh); 00876 pixd = pixDilate(pixd, pixt, selv); 00877 pixErode(pixt, pixd, selh); 00878 pixErode(pixd, pixt, selv); 00879 pixDestroy(&pixt); 00880 selDestroy(&selh); 00881 selDestroy(&selv); 00882 } 00883 00884 return pixd; 00885 } 00886 00887 00888 /*! 00889 * pixCloseSafeBrick() 00890 * 00891 * Input: pixd (<optional>; this can be null, equal to pixs, 00892 * or different from pixs) 00893 * pixs (1 bpp) 00894 * hsize (width of brick Sel) 00895 * vsize (height of brick Sel) 00896 * Return: pixd, or null on error 00897 * 00898 * Notes: 00899 * (1) Sel is a brick with all elements being hits 00900 * (2) The origin is at (x, y) = (hsize/2, vsize/2) 00901 * (3) Do separably if both hsize and vsize are > 1. 00902 * (4) Safe closing adds a border of 0 pixels, of sufficient size so 00903 * that all pixels in input image are processed within 00904 * 32-bit words in the expanded image. As a result, there is 00905 * no special processing for pixels near the boundary, and there 00906 * are no boundary effects. The border is removed at the end. 00907 * (5) There are three cases: 00908 * (a) pixd == null (result into new pixd) 00909 * (b) pixd == pixs (in-place; writes result back to pixs) 00910 * (c) pixd != pixs (puts result into existing pixd) 00911 * (6) For clarity, if the case is known, use these patterns: 00912 * (a) pixd = pixCloseBrick(NULL, pixs, ...); 00913 * (b) pixCloseBrick(pixs, pixs, ...); 00914 * (c) pixCloseBrick(pixd, pixs, ...); 00915 * (7) The size of the result is determined by pixs. 00916 */ 00917 PIX * 00918 pixCloseSafeBrick(PIX *pixd, 00919 PIX *pixs, 00920 l_int32 hsize, 00921 l_int32 vsize) 00922 { 00923 l_int32 maxtrans, bordsize; 00924 PIX *pixsb, *pixt, *pixdb; 00925 SEL *sel, *selh, *selv; 00926 00927 PROCNAME("pixCloseSafeBrick"); 00928 00929 if (!pixs) 00930 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 00931 if (pixGetDepth(pixs) != 1) 00932 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 00933 if (hsize < 1 || vsize < 1) 00934 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 00935 00936 if (hsize == 1 && vsize == 1) 00937 return pixCopy(pixd, pixs); 00938 00939 /* Symmetric b.c. handles correctly without added pixels */ 00940 if (MORPH_BC == SYMMETRIC_MORPH_BC) 00941 return pixCloseBrick(pixd, pixs, hsize, vsize); 00942 00943 maxtrans = L_MAX(hsize / 2, vsize / 2); 00944 bordsize = 32 * ((maxtrans + 31) / 32); /* full 32 bit words */ 00945 pixsb = pixAddBorder(pixs, bordsize, 0); 00946 00947 if (hsize == 1 || vsize == 1) { /* no intermediate result */ 00948 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); 00949 pixdb = pixClose(NULL, pixsb, sel); 00950 selDestroy(&sel); 00951 } 00952 else { /* do separably */ 00953 selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT); 00954 selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT); 00955 pixt = pixDilate(NULL, pixsb, selh); 00956 pixdb = pixDilate(NULL, pixt, selv); 00957 pixErode(pixt, pixdb, selh); 00958 pixErode(pixdb, pixt, selv); 00959 pixDestroy(&pixt); 00960 selDestroy(&selh); 00961 selDestroy(&selv); 00962 } 00963 00964 pixt = pixRemoveBorder(pixdb, bordsize); 00965 pixDestroy(&pixsb); 00966 pixDestroy(&pixdb); 00967 00968 if (!pixd) 00969 pixd = pixt; 00970 else { 00971 pixCopy(pixd, pixt); 00972 pixDestroy(&pixt); 00973 } 00974 00975 return pixd; 00976 } 00977 00978 00979 /*-----------------------------------------------------------------* 00980 * Binary composed morphological (raster) ops with brick Sels * 00981 *-----------------------------------------------------------------*/ 00982 /* selectComposableSels() 00983 * 00984 * Input: size (of composed sel) 00985 * direction (L_HORIZ, L_VERT) 00986 * &sel1 (<optional return> contiguous sel; can be null) 00987 * &sel2 (<optional return> comb sel; can be null) 00988 * Return: 0 if OK, 1 on error 00989 * 00990 * Notes: 00991 * (1) When using composable Sels, where the original Sel is 00992 * decomposed into two, the best you can do in terms 00993 * of reducing the computation is by a factor: 00994 * 00995 * 2 * sqrt(size) / size 00996 * 00997 * In practice, you get quite close to this. E.g., 00998 * 00999 * Sel size | Optimum reduction factor 01000 * -------- ------------------------ 01001 * 36 | 1/3 01002 * 64 | 1/4 01003 * 144 | 1/6 01004 * 256 | 1/8 01005 */ 01006 l_int32 01007 selectComposableSels(l_int32 size, 01008 l_int32 direction, 01009 SEL **psel1, 01010 SEL **psel2) 01011 { 01012 l_int32 factor1, factor2; 01013 01014 PROCNAME("selectComposableSels"); 01015 01016 if (!psel1 && !psel2) 01017 return ERROR_INT("neither &sel1 nor &sel2 are defined", procName, 1); 01018 if (psel1) *psel1 = NULL; 01019 if (psel2) *psel2 = NULL; 01020 if (size < 1 || size > 250 * 250) 01021 return ERROR_INT("size < 1", procName, 1); 01022 if (direction != L_HORIZ && direction != L_VERT) 01023 return ERROR_INT("invalid direction", procName, 1); 01024 01025 if (selectComposableSizes(size, &factor1, &factor2)) 01026 return ERROR_INT("factors not found", procName, 1); 01027 01028 if (psel1) { 01029 if (direction == L_HORIZ) 01030 *psel1 = selCreateBrick(1, factor1, 0, factor1 / 2, SEL_HIT); 01031 else 01032 *psel1 = selCreateBrick(factor1, 1, factor1 / 2 , 0, SEL_HIT); 01033 } 01034 if (psel2) 01035 *psel2 = selCreateComb(factor1, factor2, direction); 01036 return 0; 01037 } 01038 01039 01040 /*! 01041 * selectComposableSizes() 01042 * 01043 * Input: size (of sel to be decomposed) 01044 * &factor1 (<return> larger factor) 01045 * &factor2 (<return> smaller factor) 01046 * Return: 0 if OK, 1 on error 01047 * 01048 * Notes: 01049 * (1) This works for Sel sizes up to 62500, which seems sufficient. 01050 * (2) The composable sel size is typically within +- 1 of 01051 * the requested size. Up to size = 300, the maximum difference 01052 * is +- 2. 01053 * (3) We choose an overall cost function where the penalty for 01054 * the size difference between input and actual is 4 times 01055 * the penalty for additional rasterops. 01056 * (4) Returned values: factor1 >= factor2 01057 * If size > 1, then factor1 > 1. 01058 */ 01059 l_int32 01060 selectComposableSizes(l_int32 size, 01061 l_int32 *pfactor1, 01062 l_int32 *pfactor2) 01063 { 01064 l_int32 i, midval, val1, val2m, val2p; 01065 l_int32 index, prodm, prodp; 01066 l_int32 mincost, totcost, rastcostm, rastcostp, diffm, diffp; 01067 l_int32 lowval[256]; 01068 l_int32 hival[256]; 01069 l_int32 rastcost[256]; /* excess in sum of sizes (extra rasterops) */ 01070 l_int32 diff[256]; /* diff between product (sel size) and input size */ 01071 01072 PROCNAME("selectComposableSizes"); 01073 01074 if (size < 1 || size > 250 * 250) 01075 return ERROR_INT("size < 1", procName, 1); 01076 if (!pfactor1 || !pfactor2) 01077 return ERROR_INT("&factor1 or &factor2 not defined", procName, 1); 01078 01079 midval = (l_int32)(sqrt((l_float64)size) + 0.001); 01080 if (midval * midval == size) { 01081 *pfactor1 = *pfactor2 = midval; 01082 return 0; 01083 } 01084 01085 /* Set up arrays. For each val1, optimize for lowest diff, 01086 * and save the rastcost, the diff, and the two factors. */ 01087 for (val1 = midval + 1, i = 0; val1 > 0; val1--, i++) { 01088 val2m = size / val1; 01089 val2p = val2m + 1; 01090 prodm = val1 * val2m; 01091 prodp = val1 * val2p; 01092 rastcostm = val1 + val2m - 2 * midval; 01093 rastcostp = val1 + val2p - 2 * midval; 01094 diffm = L_ABS(size - prodm); 01095 diffp = L_ABS(size - prodp); 01096 if (diffm <= diffp) { 01097 lowval[i] = L_MIN(val1, val2m); 01098 hival[i] = L_MAX(val1, val2m); 01099 rastcost[i] = rastcostm; 01100 diff[i] = diffm; 01101 } 01102 else { 01103 lowval[i] = L_MIN(val1, val2p); 01104 hival[i] = L_MAX(val1, val2p); 01105 rastcost[i] = rastcostp; 01106 diff[i] = diffp; 01107 } 01108 } 01109 01110 /* Choose the optimum factors; use cost ratio 4 on diff */ 01111 mincost = 10000; 01112 for (i = 0; i < midval + 1; i++) { 01113 if (diff[i] == 0 && rastcost[i] < ACCEPTABLE_COST) { 01114 *pfactor1 = hival[i]; 01115 *pfactor2 = lowval[i]; 01116 return 0; 01117 } 01118 totcost = 4 * diff[i] + rastcost[i]; 01119 if (totcost < mincost) { 01120 mincost = totcost; 01121 index = i; 01122 } 01123 } 01124 *pfactor1 = hival[index]; 01125 *pfactor2 = lowval[index]; 01126 01127 return 0; 01128 } 01129 01130 01131 /*! 01132 * pixDilateCompBrick() 01133 * 01134 * Input: pixd (<optional>; this can be null, equal to pixs, 01135 * or different from pixs) 01136 * pixs (1 bpp) 01137 * hsize (width of brick Sel) 01138 * vsize (height of brick Sel) 01139 * Return: pixd, or null on error 01140 * 01141 * Notes: 01142 * (1) Sel is a brick with all elements being hits 01143 * (2) The origin is at (x, y) = (hsize/2, vsize/2) 01144 * (3) Do compositely for each dimension > 1. 01145 * (4) Do separably if both hsize and vsize are > 1. 01146 * (5) There are three cases: 01147 * (a) pixd == null (result into new pixd) 01148 * (b) pixd == pixs (in-place; writes result back to pixs) 01149 * (c) pixd != pixs (puts result into existing pixd) 01150 * (6) For clarity, if the case is known, use these patterns: 01151 * (a) pixd = pixDilateCompBrick(NULL, pixs, ...); 01152 * (b) pixDilateCompBrick(pixs, pixs, ...); 01153 * (c) pixDilateCompBrick(pixd, pixs, ...); 01154 * (7) The dimensions of the resulting image are determined by pixs. 01155 * (8) CAUTION: both hsize and vsize are being decomposed. 01156 * The decomposer chooses a product of sizes (call them 01157 * 'terms') for each that is close to the input size, 01158 * but not necessarily equal to it. It attempts to optimize: 01159 * (a) for consistency with the input values: the product 01160 * of terms is close to the input size 01161 * (b) for efficiency of the operation: the sum of the 01162 * terms is small; ideally about twice the square 01163 * root of the input size. 01164 * So, for example, if the input hsize = 37, which is 01165 * a prime number, the decomposer will break this into two 01166 * terms, 6 and 6, so that the net result is a dilation 01167 * with hsize = 36. 01168 */ 01169 PIX * 01170 pixDilateCompBrick(PIX *pixd, 01171 PIX *pixs, 01172 l_int32 hsize, 01173 l_int32 vsize) 01174 { 01175 PIX *pixt1, *pixt2, *pixt3; 01176 SEL *selh1, *selh2, *selv1, *selv2; 01177 01178 PROCNAME("pixDilateCompBrick"); 01179 01180 if (!pixs) 01181 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 01182 if (pixGetDepth(pixs) != 1) 01183 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 01184 if (hsize < 1 || vsize < 1) 01185 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 01186 01187 pixt1 = pixAddBorder(pixs, 32, 0); 01188 01189 if (hsize == 1 && vsize == 1) 01190 return pixCopy(pixd, pixs); 01191 if (hsize > 1) 01192 selectComposableSels(hsize, L_HORIZ, &selh1, &selh2); 01193 if (vsize > 1) 01194 selectComposableSels(vsize, L_VERT, &selv1, &selv2); 01195 if (vsize == 1) { 01196 pixt2 = pixDilate(NULL, pixt1, selh1); 01197 pixt3 = pixDilate(NULL, pixt2, selh2); 01198 } 01199 else if (hsize == 1) { 01200 pixt2 = pixDilate(NULL, pixt1, selv1); 01201 pixt3 = pixDilate(NULL, pixt2, selv2); 01202 } 01203 else { 01204 pixt2 = pixDilate(NULL, pixt1, selh1); 01205 pixt3 = pixDilate(NULL, pixt2, selh2); 01206 pixDilate(pixt2, pixt3, selv1); 01207 pixDilate(pixt3, pixt2, selv2); 01208 } 01209 pixDestroy(&pixt1); 01210 pixDestroy(&pixt2); 01211 01212 if (hsize > 1) { 01213 selDestroy(&selh1); 01214 selDestroy(&selh2); 01215 } 01216 if (vsize > 1) { 01217 selDestroy(&selv1); 01218 selDestroy(&selv2); 01219 } 01220 01221 pixt1 = pixRemoveBorder(pixt3, 32); 01222 pixDestroy(&pixt3); 01223 if (!pixd) 01224 return pixt1; 01225 pixCopy(pixd, pixt1); 01226 pixDestroy(&pixt1); 01227 return pixd; 01228 } 01229 01230 01231 /*! 01232 * pixErodeCompBrick() 01233 * 01234 * Input: pixd (<optional>; this can be null, equal to pixs, 01235 * or different from pixs) 01236 * pixs (1 bpp) 01237 * hsize (width of brick Sel) 01238 * vsize (height of brick Sel) 01239 * Return: pixd, or null on error 01240 * 01241 * Notes: 01242 * (1) Sel is a brick with all elements being hits 01243 * (2) The origin is at (x, y) = (hsize/2, vsize/2) 01244 * (3) Do compositely for each dimension > 1. 01245 * (4) Do separably if both hsize and vsize are > 1. 01246 * (5) There are three cases: 01247 * (a) pixd == null (result into new pixd) 01248 * (b) pixd == pixs (in-place; writes result back to pixs) 01249 * (c) pixd != pixs (puts result into existing pixd) 01250 * (6) For clarity, if the case is known, use these patterns: 01251 * (a) pixd = pixErodeCompBrick(NULL, pixs, ...); 01252 * (b) pixErodeCompBrick(pixs, pixs, ...); 01253 * (c) pixErodeCompBrick(pixd, pixs, ...); 01254 * (7) The dimensions of the resulting image are determined by pixs. 01255 * (8) CAUTION: both hsize and vsize are being decomposed. 01256 * The decomposer chooses a product of sizes (call them 01257 * 'terms') for each that is close to the input size, 01258 * but not necessarily equal to it. It attempts to optimize: 01259 * (a) for consistency with the input values: the product 01260 * of terms is close to the input size 01261 * (b) for efficiency of the operation: the sum of the 01262 * terms is small; ideally about twice the square 01263 * root of the input size. 01264 * So, for example, if the input hsize = 37, which is 01265 * a prime number, the decomposer will break this into two 01266 * terms, 6 and 6, so that the net result is a dilation 01267 * with hsize = 36. 01268 */ 01269 PIX * 01270 pixErodeCompBrick(PIX *pixd, 01271 PIX *pixs, 01272 l_int32 hsize, 01273 l_int32 vsize) 01274 { 01275 PIX *pixt; 01276 SEL *selh1, *selh2, *selv1, *selv2; 01277 01278 PROCNAME("pixErodeCompBrick"); 01279 01280 if (!pixs) 01281 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 01282 if (pixGetDepth(pixs) != 1) 01283 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 01284 if (hsize < 1 || vsize < 1) 01285 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 01286 01287 if (hsize == 1 && vsize == 1) 01288 return pixCopy(pixd, pixs); 01289 if (hsize > 1) 01290 selectComposableSels(hsize, L_HORIZ, &selh1, &selh2); 01291 if (vsize > 1) 01292 selectComposableSels(vsize, L_VERT, &selv1, &selv2); 01293 if (vsize == 1) { 01294 pixt = pixErode(NULL, pixs, selh1); 01295 pixd = pixErode(pixd, pixt, selh2); 01296 } 01297 else if (hsize == 1) { 01298 pixt = pixErode(NULL, pixs, selv1); 01299 pixd = pixErode(pixd, pixt, selv2); 01300 } 01301 else { 01302 pixt = pixErode(NULL, pixs, selh1); 01303 pixd = pixErode(pixd, pixt, selh2); 01304 pixErode(pixt, pixd, selv1); 01305 pixErode(pixd, pixt, selv2); 01306 } 01307 pixDestroy(&pixt); 01308 01309 if (hsize > 1) { 01310 selDestroy(&selh1); 01311 selDestroy(&selh2); 01312 } 01313 if (vsize > 1) { 01314 selDestroy(&selv1); 01315 selDestroy(&selv2); 01316 } 01317 01318 return pixd; 01319 } 01320 01321 01322 /*! 01323 * pixOpenCompBrick() 01324 * 01325 * Input: pixd (<optional>; this can be null, equal to pixs, 01326 * or different from pixs) 01327 * pixs (1 bpp) 01328 * hsize (width of brick Sel) 01329 * vsize (height of brick Sel) 01330 * Return: pixd, or null on error 01331 * 01332 * Notes: 01333 * (1) Sel is a brick with all elements being hits 01334 * (2) The origin is at (x, y) = (hsize/2, vsize/2) 01335 * (3) Do compositely for each dimension > 1. 01336 * (4) Do separably if both hsize and vsize are > 1. 01337 * (5) There are three cases: 01338 * (a) pixd == null (result into new pixd) 01339 * (b) pixd == pixs (in-place; writes result back to pixs) 01340 * (c) pixd != pixs (puts result into existing pixd) 01341 * (6) For clarity, if the case is known, use these patterns: 01342 * (a) pixd = pixOpenCompBrick(NULL, pixs, ...); 01343 * (b) pixOpenCompBrick(pixs, pixs, ...); 01344 * (c) pixOpenCompBrick(pixd, pixs, ...); 01345 * (7) The dimensions of the resulting image are determined by pixs. 01346 * (8) CAUTION: both hsize and vsize are being decomposed. 01347 * The decomposer chooses a product of sizes (call them 01348 * 'terms') for each that is close to the input size, 01349 * but not necessarily equal to it. It attempts to optimize: 01350 * (a) for consistency with the input values: the product 01351 * of terms is close to the input size 01352 * (b) for efficiency of the operation: the sum of the 01353 * terms is small; ideally about twice the square 01354 * root of the input size. 01355 * So, for example, if the input hsize = 37, which is 01356 * a prime number, the decomposer will break this into two 01357 * terms, 6 and 6, so that the net result is a dilation 01358 * with hsize = 36. 01359 */ 01360 PIX * 01361 pixOpenCompBrick(PIX *pixd, 01362 PIX *pixs, 01363 l_int32 hsize, 01364 l_int32 vsize) 01365 { 01366 PIX *pixt; 01367 SEL *selh1, *selh2, *selv1, *selv2; 01368 01369 PROCNAME("pixOpenCompBrick"); 01370 01371 if (!pixs) 01372 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 01373 if (pixGetDepth(pixs) != 1) 01374 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 01375 if (hsize < 1 || vsize < 1) 01376 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 01377 01378 if (hsize == 1 && vsize == 1) 01379 return pixCopy(pixd, pixs); 01380 if (hsize > 1) 01381 selectComposableSels(hsize, L_HORIZ, &selh1, &selh2); 01382 if (vsize > 1) 01383 selectComposableSels(vsize, L_VERT, &selv1, &selv2); 01384 if (vsize == 1) { 01385 pixt = pixErode(NULL, pixs, selh1); 01386 pixd = pixErode(pixd, pixt, selh2); 01387 pixDilate(pixt, pixd, selh1); 01388 pixDilate(pixd, pixt, selh2); 01389 } 01390 else if (hsize == 1) { 01391 pixt = pixErode(NULL, pixs, selv1); 01392 pixd = pixErode(pixd, pixt, selv2); 01393 pixDilate(pixt, pixd, selv1); 01394 pixDilate(pixd, pixt, selv2); 01395 } 01396 else { /* do separably */ 01397 pixt = pixErode(NULL, pixs, selh1); 01398 pixd = pixErode(pixd, pixt, selh2); 01399 pixErode(pixt, pixd, selv1); 01400 pixErode(pixd, pixt, selv2); 01401 pixDilate(pixt, pixd, selh1); 01402 pixDilate(pixd, pixt, selh2); 01403 pixDilate(pixt, pixd, selv1); 01404 pixDilate(pixd, pixt, selv2); 01405 } 01406 pixDestroy(&pixt); 01407 01408 if (hsize > 1) { 01409 selDestroy(&selh1); 01410 selDestroy(&selh2); 01411 } 01412 if (vsize > 1) { 01413 selDestroy(&selv1); 01414 selDestroy(&selv2); 01415 } 01416 01417 return pixd; 01418 } 01419 01420 01421 /*! 01422 * pixCloseCompBrick() 01423 * 01424 * Input: pixd (<optional>; this can be null, equal to pixs, 01425 * or different from pixs) 01426 * pixs (1 bpp) 01427 * hsize (width of brick Sel) 01428 * vsize (height of brick Sel) 01429 * Return: pixd, or null on error 01430 * 01431 * Notes: 01432 * (1) Sel is a brick with all elements being hits 01433 * (2) The origin is at (x, y) = (hsize/2, vsize/2) 01434 * (3) Do compositely for each dimension > 1. 01435 * (4) Do separably if both hsize and vsize are > 1. 01436 * (5) There are three cases: 01437 * (a) pixd == null (result into new pixd) 01438 * (b) pixd == pixs (in-place; writes result back to pixs) 01439 * (c) pixd != pixs (puts result into existing pixd) 01440 * (6) For clarity, if the case is known, use these patterns: 01441 * (a) pixd = pixCloseCompBrick(NULL, pixs, ...); 01442 * (b) pixCloseCompBrick(pixs, pixs, ...); 01443 * (c) pixCloseCompBrick(pixd, pixs, ...); 01444 * (7) The dimensions of the resulting image are determined by pixs. 01445 * (8) CAUTION: both hsize and vsize are being decomposed. 01446 * The decomposer chooses a product of sizes (call them 01447 * 'terms') for each that is close to the input size, 01448 * but not necessarily equal to it. It attempts to optimize: 01449 * (a) for consistency with the input values: the product 01450 * of terms is close to the input size 01451 * (b) for efficiency of the operation: the sum of the 01452 * terms is small; ideally about twice the square 01453 * root of the input size. 01454 * So, for example, if the input hsize = 37, which is 01455 * a prime number, the decomposer will break this into two 01456 * terms, 6 and 6, so that the net result is a dilation 01457 * with hsize = 36. 01458 */ 01459 PIX * 01460 pixCloseCompBrick(PIX *pixd, 01461 PIX *pixs, 01462 l_int32 hsize, 01463 l_int32 vsize) 01464 { 01465 PIX *pixt; 01466 SEL *selh1, *selh2, *selv1, *selv2; 01467 01468 PROCNAME("pixCloseCompBrick"); 01469 01470 if (!pixs) 01471 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 01472 if (pixGetDepth(pixs) != 1) 01473 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 01474 if (hsize < 1 || vsize < 1) 01475 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 01476 01477 if (hsize == 1 && vsize == 1) 01478 return pixCopy(pixd, pixs); 01479 if (hsize > 1) 01480 selectComposableSels(hsize, L_HORIZ, &selh1, &selh2); 01481 if (vsize > 1) 01482 selectComposableSels(vsize, L_VERT, &selv1, &selv2); 01483 if (vsize == 1) { 01484 pixt = pixDilate(NULL, pixs, selh1); 01485 pixd = pixDilate(pixd, pixt, selh2); 01486 pixErode(pixt, pixd, selh1); 01487 pixErode(pixd, pixt, selh2); 01488 } 01489 else if (hsize == 1) { 01490 pixt = pixDilate(NULL, pixs, selv1); 01491 pixd = pixDilate(pixd, pixt, selv2); 01492 pixErode(pixt, pixd, selv1); 01493 pixErode(pixd, pixt, selv2); 01494 } 01495 else { /* do separably */ 01496 pixt = pixDilate(NULL, pixs, selh1); 01497 pixd = pixDilate(pixd, pixt, selh2); 01498 pixDilate(pixt, pixd, selv1); 01499 pixDilate(pixd, pixt, selv2); 01500 pixErode(pixt, pixd, selh1); 01501 pixErode(pixd, pixt, selh2); 01502 pixErode(pixt, pixd, selv1); 01503 pixErode(pixd, pixt, selv2); 01504 } 01505 pixDestroy(&pixt); 01506 01507 if (hsize > 1) { 01508 selDestroy(&selh1); 01509 selDestroy(&selh2); 01510 } 01511 if (vsize > 1) { 01512 selDestroy(&selv1); 01513 selDestroy(&selv2); 01514 } 01515 01516 return pixd; 01517 } 01518 01519 01520 /*! 01521 * pixCloseSafeCompBrick() 01522 * 01523 * Input: pixd (<optional>; this can be null, equal to pixs, 01524 * or different from pixs) 01525 * pixs (1 bpp) 01526 * hsize (width of brick Sel) 01527 * vsize (height of brick Sel) 01528 * Return: pixd, or null on error 01529 * 01530 * Notes: 01531 * (1) Sel is a brick with all elements being hits 01532 * (2) The origin is at (x, y) = (hsize/2, vsize/2) 01533 * (3) Do compositely for each dimension > 1. 01534 * (4) Do separably if both hsize and vsize are > 1. 01535 * (5) Safe closing adds a border of 0 pixels, of sufficient size so 01536 * that all pixels in input image are processed within 01537 * 32-bit words in the expanded image. As a result, there is 01538 * no special processing for pixels near the boundary, and there 01539 * are no boundary effects. The border is removed at the end. 01540 * (6) There are three cases: 01541 * (a) pixd == null (result into new pixd) 01542 * (b) pixd == pixs (in-place; writes result back to pixs) 01543 * (c) pixd != pixs (puts result into existing pixd) 01544 * (7) For clarity, if the case is known, use these patterns: 01545 * (a) pixd = pixCloseSafeCompBrick(NULL, pixs, ...); 01546 * (b) pixCloseSafeCompBrick(pixs, pixs, ...); 01547 * (c) pixCloseSafeCompBrick(pixd, pixs, ...); 01548 * (8) The dimensions of the resulting image are determined by pixs. 01549 * (9) CAUTION: both hsize and vsize are being decomposed. 01550 * The decomposer chooses a product of sizes (call them 01551 * 'terms') for each that is close to the input size, 01552 * but not necessarily equal to it. It attempts to optimize: 01553 * (a) for consistency with the input values: the product 01554 * of terms is close to the input size 01555 * (b) for efficiency of the operation: the sum of the 01556 * terms is small; ideally about twice the square 01557 * root of the input size. 01558 * So, for example, if the input hsize = 37, which is 01559 * a prime number, the decomposer will break this into two 01560 * terms, 6 and 6, so that the net result is a dilation 01561 * with hsize = 36. 01562 */ 01563 PIX * 01564 pixCloseSafeCompBrick(PIX *pixd, 01565 PIX *pixs, 01566 l_int32 hsize, 01567 l_int32 vsize) 01568 { 01569 l_int32 maxtrans, bordsize; 01570 PIX *pixsb, *pixt, *pixdb; 01571 SEL *selh1, *selh2, *selv1, *selv2; 01572 01573 PROCNAME("pixCloseSafeCompBrick"); 01574 01575 if (!pixs) 01576 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 01577 if (pixGetDepth(pixs) != 1) 01578 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 01579 if (hsize < 1 || vsize < 1) 01580 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); 01581 01582 if (hsize == 1 && vsize == 1) 01583 return pixCopy(pixd, pixs); 01584 01585 /* Symmetric b.c. handles correctly without added pixels */ 01586 if (MORPH_BC == SYMMETRIC_MORPH_BC) 01587 return pixCloseCompBrick(pixd, pixs, hsize, vsize); 01588 01589 maxtrans = L_MAX(hsize / 2, vsize / 2); 01590 bordsize = 32 * ((maxtrans + 31) / 32); /* full 32 bit words */ 01591 pixsb = pixAddBorder(pixs, bordsize, 0); 01592 01593 if (hsize > 1) 01594 selectComposableSels(hsize, L_HORIZ, &selh1, &selh2); 01595 if (vsize > 1) 01596 selectComposableSels(vsize, L_VERT, &selv1, &selv2); 01597 if (vsize == 1) { 01598 pixt = pixDilate(NULL, pixsb, selh1); 01599 pixdb = pixDilate(NULL, pixt, selh2); 01600 pixErode(pixt, pixdb, selh1); 01601 pixErode(pixdb, pixt, selh2); 01602 } 01603 else if (hsize == 1) { 01604 pixt = pixDilate(NULL, pixsb, selv1); 01605 pixdb = pixDilate(NULL, pixt, selv2); 01606 pixErode(pixt, pixdb, selv1); 01607 pixErode(pixdb, pixt, selv2); 01608 } 01609 else { /* do separably */ 01610 pixt = pixDilate(NULL, pixsb, selh1); 01611 pixdb = pixDilate(NULL, pixt, selh2); 01612 pixDilate(pixt, pixdb, selv1); 01613 pixDilate(pixdb, pixt, selv2); 01614 pixErode(pixt, pixdb, selh1); 01615 pixErode(pixdb, pixt, selh2); 01616 pixErode(pixt, pixdb, selv1); 01617 pixErode(pixdb, pixt, selv2); 01618 } 01619 pixDestroy(&pixt); 01620 01621 pixt = pixRemoveBorder(pixdb, bordsize); 01622 pixDestroy(&pixsb); 01623 pixDestroy(&pixdb); 01624 01625 if (!pixd) 01626 pixd = pixt; 01627 else { 01628 pixCopy(pixd, pixt); 01629 pixDestroy(&pixt); 01630 } 01631 01632 if (hsize > 1) { 01633 selDestroy(&selh1); 01634 selDestroy(&selh2); 01635 } 01636 if (vsize > 1) { 01637 selDestroy(&selv1); 01638 selDestroy(&selv2); 01639 } 01640 01641 return pixd; 01642 } 01643 01644 01645 /*-----------------------------------------------------------------* 01646 * Functions associated with boundary conditions * 01647 *-----------------------------------------------------------------*/ 01648 /*! 01649 * resetMorphBoundaryCondition() 01650 * 01651 * Input: bc (SYMMETRIC_MORPH_BC, ASYMMETRIC_MORPH_BC) 01652 * Return: void 01653 */ 01654 void 01655 resetMorphBoundaryCondition(l_int32 bc) 01656 { 01657 PROCNAME("resetMorphBoundaryCondition"); 01658 01659 if (bc != SYMMETRIC_MORPH_BC && bc != ASYMMETRIC_MORPH_BC) { 01660 L_WARNING("invalid bc; using asymmetric", procName); 01661 bc = ASYMMETRIC_MORPH_BC; 01662 } 01663 MORPH_BC = bc; 01664 return; 01665 } 01666 01667 01668 /*! 01669 * getMorphBorderPixelColor() 01670 * 01671 * Input: type (L_MORPH_DILATE, L_MORPH_ERODE) 01672 * depth (of pix) 01673 * Return: color of border pixels for this operation 01674 */ 01675 l_uint32 01676 getMorphBorderPixelColor(l_int32 type, 01677 l_int32 depth) 01678 { 01679 PROCNAME("getMorphBorderPixelColor"); 01680 01681 if (type != L_MORPH_DILATE && type != L_MORPH_ERODE) 01682 return ERROR_INT("invalid type", procName, 0); 01683 if (depth != 1 && depth != 2 && depth != 4 && depth != 8 && 01684 depth != 16 && depth != 32) 01685 return ERROR_INT("invalid depth", procName, 0); 01686 01687 if (MORPH_BC == ASYMMETRIC_MORPH_BC || type == L_MORPH_DILATE) 01688 return 0; 01689 01690 /* Symmetric & erosion */ 01691 if (depth < 32) 01692 return ((1 << depth) - 1); 01693 else /* depth == 32 */ 01694 return 0xffffff00; 01695 } 01696 01697 01698 /*-----------------------------------------------------------------* 01699 * Static helpers for arg processing * 01700 *-----------------------------------------------------------------*/ 01701 /*! 01702 * processMorphArgs1() 01703 * 01704 * Input: pixd (<optional>; this can be null, equal to pixs, 01705 * or different from pixs) 01706 * pixs (1 bpp) 01707 * sel 01708 * &pixt (<returned>) 01709 * Return: pixd, or null on error. 01710 * 01711 * Notes: 01712 * (1) This is used for generic erosion, dilation and HMT. 01713 */ 01714 static PIX * 01715 processMorphArgs1(PIX *pixd, 01716 PIX *pixs, 01717 SEL *sel, 01718 PIX **ppixt) 01719 { 01720 l_int32 sx, sy; 01721 01722 PROCNAME("processMorphArgs1"); 01723 01724 if (!ppixt) 01725 return (PIX *)ERROR_PTR("&pixt not defined", procName, pixd); 01726 *ppixt = NULL; 01727 if (!pixs) 01728 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 01729 if (!sel) 01730 return (PIX *)ERROR_PTR("sel not defined", procName, pixd); 01731 if (pixGetDepth(pixs) != 1) 01732 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 01733 01734 selGetParameters(sel, &sx, &sy, NULL, NULL); 01735 if (sx == 0 || sy == 0) 01736 return (PIX *)ERROR_PTR("sel of size 0", procName, pixd); 01737 01738 /* We require pixd to exist and to be the same size as pixs. 01739 * Further, pixt must be a copy (or clone) of pixs. */ 01740 if (!pixd) { 01741 if ((pixd = pixCreateTemplate(pixs)) == NULL) 01742 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 01743 *ppixt = pixClone(pixs); 01744 } 01745 else { 01746 pixResizeImageData(pixd, pixs); 01747 if (pixd == pixs) { /* in-place; must make a copy of pixs */ 01748 if ((*ppixt = pixCopy(NULL, pixs)) == NULL) 01749 return (PIX *)ERROR_PTR("pixt not made", procName, pixd); 01750 } 01751 else 01752 *ppixt = pixClone(pixs); 01753 } 01754 return pixd; 01755 } 01756 01757 01758 /*! 01759 * processMorphArgs2() 01760 * 01761 * This is used for generic openings and closings. 01762 */ 01763 static PIX * 01764 processMorphArgs2(PIX *pixd, 01765 PIX *pixs, 01766 SEL *sel) 01767 { 01768 l_int32 sx, sy; 01769 01770 PROCNAME("processMorphArgs2"); 01771 01772 if (!pixs) 01773 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 01774 if (!sel) 01775 return (PIX *)ERROR_PTR("sel not defined", procName, pixd); 01776 if (pixGetDepth(pixs) != 1) 01777 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); 01778 01779 selGetParameters(sel, &sx, &sy, NULL, NULL); 01780 if (sx == 0 || sy == 0) 01781 return (PIX *)ERROR_PTR("sel of size 0", procName, pixd); 01782 01783 if (!pixd) 01784 return pixCreateTemplate(pixs); 01785 pixResizeImageData(pixd, pixs); 01786 return pixd; 01787 }