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 /* 00018 * graymorph.c 00019 * 00020 * Top-level binary morphological operations (van Herk / Gil-Werman) 00021 * PIX *pixErodeGray() 00022 * PIX *pixDilateGray() 00023 * PIX *pixOpenGray() 00024 * PIX *pixCloseGray() 00025 * 00026 * Special operations for 1x3, 3x1 and 3x3 Sels (direct) 00027 * PIX *pixErodeGray3() 00028 * PIX *pixDilateGray3() 00029 * PIX *pixOpenGray3() 00030 * PIX *pixCloseGray3() 00031 * 00032 * 00033 * Method: Algorithm by van Herk and Gil and Werman, 1992 00034 * 00035 * Measured speed of the vH/G-W implementation is about 1 output 00036 * pixel per 120 PIII clock cycles, for a horizontal or vertical 00037 * erosion or dilation. The computation time doubles for opening 00038 * or closing, or for a square SE, as expected, and is independent 00039 * of the size of the SE. 00040 * 00041 * A faster implementation can be made directly for brick Sels 00042 * of maximum size 3. We unroll the computation for sets of 8 bytes. 00043 * It needs to be called explicitly; the general functions do not 00044 * default for the size 3 brick Sels. 00045 */ 00046 00047 #include <stdio.h> 00048 #include <stdlib.h> 00049 #include "allheaders.h" 00050 00051 static PIX *pixErodeGray3h(PIX *pixs); 00052 static PIX *pixErodeGray3v(PIX *pixs); 00053 static PIX *pixDilateGray3h(PIX *pixs); 00054 static PIX *pixDilateGray3v(PIX *pixs); 00055 00056 00057 /*-----------------------------------------------------------------* 00058 * Top-level gray morphological operations * 00059 *-----------------------------------------------------------------*/ 00060 /*! 00061 * pixErodeGray() 00062 * 00063 * Input: pixs 00064 * hsize (of Sel; must be odd; origin implicitly in center) 00065 * vsize (ditto) 00066 * Return: pixd 00067 * 00068 * Notes: 00069 * (1) Sel is a brick with all elements being hits 00070 * (2) If hsize = vsize = 1, just returns a copy. 00071 */ 00072 PIX * 00073 pixErodeGray(PIX *pixs, 00074 l_int32 hsize, 00075 l_int32 vsize) 00076 { 00077 l_uint8 *buffer, *minarray; 00078 l_int32 w, h, wplb, wplt; 00079 l_int32 leftpix, rightpix, toppix, bottompix, maxsize; 00080 l_uint32 *datab, *datat; 00081 PIX *pixb, *pixt, *pixd; 00082 00083 PROCNAME("pixErodeGray"); 00084 00085 if (!pixs) 00086 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00087 if (pixGetDepth(pixs) != 8) 00088 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 00089 if (hsize < 1 || vsize < 1) 00090 return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); 00091 if ((hsize & 1) == 0 ) { 00092 L_WARNING("horiz sel size must be odd; increasing by 1", procName); 00093 hsize++; 00094 } 00095 if ((vsize & 1) == 0 ) { 00096 L_WARNING("vert sel size must be odd; increasing by 1", procName); 00097 vsize++; 00098 } 00099 00100 if (hsize == 1 && vsize == 1) 00101 return pixCopy(NULL, pixs); 00102 00103 if (vsize == 1) { /* horizontal sel */ 00104 leftpix = (hsize + 1) / 2; 00105 rightpix = (3 * hsize + 1) / 2; 00106 toppix = 0; 00107 bottompix = 0; 00108 } 00109 else if (hsize == 1) { /* vertical sel */ 00110 leftpix = 0; 00111 rightpix = 0; 00112 toppix = (vsize + 1) / 2; 00113 bottompix = (3 * vsize + 1) / 2; 00114 } 00115 else { 00116 leftpix = (hsize + 1) / 2; 00117 rightpix = (3 * hsize + 1) / 2; 00118 toppix = (vsize + 1) / 2; 00119 bottompix = (3 * vsize + 1) / 2; 00120 } 00121 00122 if ((pixb = pixAddBorderGeneral(pixs, 00123 leftpix, rightpix, toppix, bottompix, 255)) == NULL) 00124 return (PIX *)ERROR_PTR("pixb not made", procName, NULL); 00125 if ((pixt = pixCreateTemplate(pixb)) == NULL) 00126 return (PIX *)ERROR_PTR("pixt not made", procName, NULL); 00127 00128 pixGetDimensions(pixt, &w, &h, NULL); 00129 datab = pixGetData(pixb); 00130 datat = pixGetData(pixt); 00131 wplb = pixGetWpl(pixb); 00132 wplt = pixGetWpl(pixt); 00133 00134 if ((buffer = (l_uint8 *)CALLOC(L_MAX(w, h), sizeof(l_uint8))) == NULL) 00135 return (PIX *)ERROR_PTR("buffer not made", procName, NULL); 00136 maxsize = L_MAX(hsize, vsize); 00137 if ((minarray = (l_uint8 *)CALLOC(2 * maxsize, sizeof(l_uint8))) == NULL) 00138 return (PIX *)ERROR_PTR("minarray not made", procName, NULL); 00139 00140 if (vsize == 1) 00141 erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, 00142 buffer, minarray); 00143 else if (hsize == 1) 00144 erodeGrayLow(datat, w, h, wplt, datab, wplb, vsize, L_VERT, 00145 buffer, minarray); 00146 else { 00147 erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, 00148 buffer, minarray); 00149 pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, 00150 PIX_SET); 00151 erodeGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, 00152 buffer, minarray); 00153 pixDestroy(&pixt); 00154 pixt = pixClone(pixb); 00155 } 00156 00157 if ((pixd = pixRemoveBorderGeneral(pixt, 00158 leftpix, rightpix, toppix, bottompix)) == NULL) 00159 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00160 00161 FREE(buffer); 00162 FREE(minarray); 00163 pixDestroy(&pixb); 00164 pixDestroy(&pixt); 00165 return pixd; 00166 } 00167 00168 00169 /*! 00170 * pixDilateGray() 00171 * 00172 * Input: pixs 00173 * hsize (of Sel; must be odd; origin implicitly in center) 00174 * vsize (ditto) 00175 * Return: pixd 00176 * 00177 * Notes: 00178 * (1) Sel is a brick with all elements being hits 00179 * (2) If hsize = vsize = 1, just returns a copy. 00180 */ 00181 PIX * 00182 pixDilateGray(PIX *pixs, 00183 l_int32 hsize, 00184 l_int32 vsize) 00185 { 00186 l_uint8 *buffer, *maxarray; 00187 l_int32 w, h, wplb, wplt; 00188 l_int32 leftpix, rightpix, toppix, bottompix, maxsize; 00189 l_uint32 *datab, *datat; 00190 PIX *pixb, *pixt, *pixd; 00191 00192 PROCNAME("pixDilateGray"); 00193 00194 if (!pixs) 00195 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00196 if (pixGetDepth(pixs) != 8) 00197 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 00198 if (hsize < 1 || vsize < 1) 00199 return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); 00200 if ((hsize & 1) == 0 ) { 00201 L_WARNING("horiz sel size must be odd; increasing by 1", procName); 00202 hsize++; 00203 } 00204 if ((vsize & 1) == 0 ) { 00205 L_WARNING("vert sel size must be odd; increasing by 1", procName); 00206 vsize++; 00207 } 00208 00209 if (hsize == 1 && vsize == 1) 00210 return pixCopy(NULL, pixs); 00211 00212 if (vsize == 1) { /* horizontal sel */ 00213 leftpix = (hsize + 1) / 2; 00214 rightpix = (3 * hsize + 1) / 2; 00215 toppix = 0; 00216 bottompix = 0; 00217 } 00218 else if (hsize == 1) { /* vertical sel */ 00219 leftpix = 0; 00220 rightpix = 0; 00221 toppix = (vsize + 1) / 2; 00222 bottompix = (3 * vsize + 1) / 2; 00223 } 00224 else { 00225 leftpix = (hsize + 1) / 2; 00226 rightpix = (3 * hsize + 1) / 2; 00227 toppix = (vsize + 1) / 2; 00228 bottompix = (3 * vsize + 1) / 2; 00229 } 00230 00231 if ((pixb = pixAddBorderGeneral(pixs, 00232 leftpix, rightpix, toppix, bottompix, 0)) == NULL) 00233 return (PIX *)ERROR_PTR("pixb not made", procName, NULL); 00234 if ((pixt = pixCreateTemplate(pixb)) == NULL) 00235 return (PIX *)ERROR_PTR("pixt not made", procName, NULL); 00236 00237 pixGetDimensions(pixt, &w, &h, NULL); 00238 datab = pixGetData(pixb); 00239 datat = pixGetData(pixt); 00240 wplb = pixGetWpl(pixb); 00241 wplt = pixGetWpl(pixt); 00242 00243 if ((buffer = (l_uint8 *)CALLOC(L_MAX(w, h), sizeof(l_uint8))) == NULL) 00244 return (PIX *)ERROR_PTR("buffer not made", procName, NULL); 00245 maxsize = L_MAX(hsize, vsize); 00246 if ((maxarray = (l_uint8 *)CALLOC(2 * maxsize, sizeof(l_uint8))) == NULL) 00247 return (PIX *)ERROR_PTR("buffer not made", procName, NULL); 00248 00249 if (vsize == 1) 00250 dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, 00251 buffer, maxarray); 00252 else if (hsize == 1) 00253 dilateGrayLow(datat, w, h, wplt, datab, wplb, vsize, L_VERT, 00254 buffer, maxarray); 00255 else { 00256 dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, 00257 buffer, maxarray); 00258 pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, 00259 PIX_CLR); 00260 dilateGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, 00261 buffer, maxarray); 00262 pixDestroy(&pixt); 00263 pixt = pixClone(pixb); 00264 } 00265 00266 if ((pixd = pixRemoveBorderGeneral(pixt, 00267 leftpix, rightpix, toppix, bottompix)) == NULL) 00268 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00269 00270 FREE(buffer); 00271 FREE(maxarray); 00272 pixDestroy(&pixb); 00273 pixDestroy(&pixt); 00274 return pixd; 00275 } 00276 00277 00278 /*! 00279 * pixOpenGray() 00280 * 00281 * Input: pixs 00282 * hsize (of Sel; must be odd; origin implicitly in center) 00283 * vsize (ditto) 00284 * Return: pixd 00285 * 00286 * Notes: 00287 * (1) Sel is a brick with all elements being hits 00288 * (2) If hsize = vsize = 1, just returns a copy. 00289 */ 00290 PIX * 00291 pixOpenGray(PIX *pixs, 00292 l_int32 hsize, 00293 l_int32 vsize) 00294 { 00295 l_uint8 *buffer; 00296 l_uint8 *array; /* used to find either min or max in interval */ 00297 l_int32 w, h, wplb, wplt; 00298 l_int32 leftpix, rightpix, toppix, bottompix, maxsize; 00299 l_uint32 *datab, *datat; 00300 PIX *pixb, *pixt, *pixd; 00301 00302 PROCNAME("pixOpenGray"); 00303 00304 if (!pixs) 00305 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00306 if (pixGetDepth(pixs) != 8) 00307 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 00308 if (hsize < 1 || vsize < 1) 00309 return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); 00310 if ((hsize & 1) == 0 ) { 00311 L_WARNING("horiz sel size must be odd; increasing by 1", procName); 00312 hsize++; 00313 } 00314 if ((vsize & 1) == 0 ) { 00315 L_WARNING("vert sel size must be odd; increasing by 1", procName); 00316 vsize++; 00317 } 00318 00319 if (hsize == 1 && vsize == 1) 00320 return pixCopy(NULL, pixs); 00321 00322 if (vsize == 1) { /* horizontal sel */ 00323 leftpix = (hsize + 1) / 2; 00324 rightpix = (3 * hsize + 1) / 2; 00325 toppix = 0; 00326 bottompix = 0; 00327 } 00328 else if (hsize == 1) { /* vertical sel */ 00329 leftpix = 0; 00330 rightpix = 0; 00331 toppix = (vsize + 1) / 2; 00332 bottompix = (3 * vsize + 1) / 2; 00333 } 00334 else { 00335 leftpix = (hsize + 1) / 2; 00336 rightpix = (3 * hsize + 1) / 2; 00337 toppix = (vsize + 1) / 2; 00338 bottompix = (3 * vsize + 1) / 2; 00339 } 00340 00341 if ((pixb = pixAddBorderGeneral(pixs, 00342 leftpix, rightpix, toppix, bottompix, 255)) == NULL) 00343 return (PIX *)ERROR_PTR("pixb not made", procName, NULL); 00344 if ((pixt = pixCreateTemplate(pixb)) == NULL) 00345 return (PIX *)ERROR_PTR("pixt not made", procName, NULL); 00346 00347 pixGetDimensions(pixt, &w, &h, NULL); 00348 datab = pixGetData(pixb); 00349 datat = pixGetData(pixt); 00350 wplb = pixGetWpl(pixb); 00351 wplt = pixGetWpl(pixt); 00352 00353 if ((buffer = (l_uint8 *)CALLOC(L_MAX(w, h), sizeof(l_uint8))) == NULL) 00354 return (PIX *)ERROR_PTR("buffer not made", procName, NULL); 00355 maxsize = L_MAX(hsize, vsize); 00356 if ((array = (l_uint8 *)CALLOC(2 * maxsize, sizeof(l_uint8))) == NULL) 00357 return (PIX *)ERROR_PTR("array not made", procName, NULL); 00358 00359 if (vsize == 1) { 00360 erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, 00361 buffer, array); 00362 pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, 00363 PIX_CLR); 00364 dilateGrayLow(datab, w, h, wplb, datat, wplt, hsize, L_HORIZ, 00365 buffer, array); 00366 } 00367 else if (hsize == 1) { 00368 erodeGrayLow(datat, w, h, wplt, datab, wplb, vsize, L_VERT, 00369 buffer, array); 00370 pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, 00371 PIX_CLR); 00372 dilateGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, 00373 buffer, array); 00374 } 00375 else { 00376 erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, 00377 buffer, array); 00378 pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, 00379 PIX_SET); 00380 erodeGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, 00381 buffer, array); 00382 pixSetOrClearBorder(pixb, leftpix, rightpix, toppix, bottompix, 00383 PIX_CLR); 00384 dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, 00385 buffer, array); 00386 pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, 00387 PIX_CLR); 00388 dilateGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, 00389 buffer, array); 00390 } 00391 00392 if ((pixd = pixRemoveBorderGeneral(pixb, 00393 leftpix, rightpix, toppix, bottompix)) == NULL) 00394 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00395 00396 FREE(buffer); 00397 FREE(array); 00398 pixDestroy(&pixb); 00399 pixDestroy(&pixt); 00400 return pixd; 00401 } 00402 00403 00404 /*! 00405 * pixCloseGray() 00406 * 00407 * Input: pixs 00408 * hsize (of Sel; must be odd; origin implicitly in center) 00409 * vsize (ditto) 00410 * Return: pixd 00411 * 00412 * Notes: 00413 * (1) Sel is a brick with all elements being hits 00414 * (2) If hsize = vsize = 1, just returns a copy. 00415 */ 00416 PIX * 00417 pixCloseGray(PIX *pixs, 00418 l_int32 hsize, 00419 l_int32 vsize) 00420 { 00421 l_uint8 *buffer; 00422 l_uint8 *array; /* used to find either min or max in interval */ 00423 l_int32 w, h, wplb, wplt; 00424 l_int32 leftpix, rightpix, toppix, bottompix, maxsize; 00425 l_uint32 *datab, *datat; 00426 PIX *pixb, *pixt, *pixd; 00427 00428 PROCNAME("pixCloseGray"); 00429 00430 if (!pixs) 00431 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00432 if (pixGetDepth(pixs) != 8) 00433 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 00434 if (hsize < 1 || vsize < 1) 00435 return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); 00436 if ((hsize & 1) == 0 ) { 00437 L_WARNING("horiz sel size must be odd; increasing by 1", procName); 00438 hsize++; 00439 } 00440 if ((vsize & 1) == 0 ) { 00441 L_WARNING("vert sel size must be odd; increasing by 1", procName); 00442 vsize++; 00443 } 00444 00445 if (hsize == 1 && vsize == 1) 00446 return pixCopy(NULL, pixs); 00447 00448 if (vsize == 1) { /* horizontal sel */ 00449 leftpix = (hsize + 1) / 2; 00450 rightpix = (3 * hsize + 1) / 2; 00451 toppix = 0; 00452 bottompix = 0; 00453 } 00454 else if (hsize == 1) { /* vertical sel */ 00455 leftpix = 0; 00456 rightpix = 0; 00457 toppix = (vsize + 1) / 2; 00458 bottompix = (3 * vsize + 1) / 2; 00459 } 00460 else { 00461 leftpix = (hsize + 1) / 2; 00462 rightpix = (3 * hsize + 1) / 2; 00463 toppix = (vsize + 1) / 2; 00464 bottompix = (3 * vsize + 1) / 2; 00465 } 00466 00467 if ((pixb = pixAddBorderGeneral(pixs, 00468 leftpix, rightpix, toppix, bottompix, 0)) == NULL) 00469 return (PIX *)ERROR_PTR("pixb not made", procName, NULL); 00470 if ((pixt = pixCreateTemplate(pixb)) == NULL) 00471 return (PIX *)ERROR_PTR("pixt not made", procName, NULL); 00472 00473 pixGetDimensions(pixt, &w, &h, NULL); 00474 datab = pixGetData(pixb); 00475 datat = pixGetData(pixt); 00476 wplb = pixGetWpl(pixb); 00477 wplt = pixGetWpl(pixt); 00478 00479 if ((buffer = (l_uint8 *)CALLOC(L_MAX(w, h), sizeof(l_uint8))) == NULL) 00480 return (PIX *)ERROR_PTR("buffer not made", procName, NULL); 00481 maxsize = L_MAX(hsize, vsize); 00482 if ((array = (l_uint8 *)CALLOC(2 * maxsize, sizeof(l_uint8))) == NULL) 00483 return (PIX *)ERROR_PTR("array not made", procName, NULL); 00484 00485 if (vsize == 1) { 00486 dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, 00487 buffer, array); 00488 pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, 00489 PIX_SET); 00490 erodeGrayLow(datab, w, h, wplb, datat, wplt, hsize, L_HORIZ, 00491 buffer, array); 00492 } 00493 else if (hsize == 1) { 00494 dilateGrayLow(datat, w, h, wplt, datab, wplb, vsize, L_VERT, 00495 buffer, array); 00496 pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, 00497 PIX_SET); 00498 erodeGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, 00499 buffer, array); 00500 } 00501 else { 00502 dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, 00503 buffer, array); 00504 pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, 00505 PIX_CLR); 00506 dilateGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, 00507 buffer, array); 00508 pixSetOrClearBorder(pixb, leftpix, rightpix, toppix, bottompix, 00509 PIX_SET); 00510 erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, 00511 buffer, array); 00512 pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, 00513 PIX_SET); 00514 erodeGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, 00515 buffer, array); 00516 } 00517 00518 if ((pixd = pixRemoveBorderGeneral(pixb, 00519 leftpix, rightpix, toppix, bottompix)) == NULL) 00520 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00521 00522 FREE(buffer); 00523 FREE(array); 00524 pixDestroy(&pixb); 00525 pixDestroy(&pixt); 00526 return pixd; 00527 } 00528 00529 00530 /*-----------------------------------------------------------------* 00531 * Special operations for 1x3, 3x1 and 3x3 Sels * 00532 *-----------------------------------------------------------------*/ 00533 /*! 00534 * pixErodeGray3() 00535 * 00536 * Input: pixs (8 bpp, not cmapped) 00537 * hsize (1 or 3) 00538 * vsize (1 or 3) 00539 * Return: pixd, or null on error 00540 * 00541 * Notes: 00542 * (1) Special case for 1x3, 3x1 or 3x3 brick sel (all hits) 00543 * (2) If hsize = vsize = 1, just returns a copy. 00544 * (3) It would be nice not to add a border, but it is required 00545 * if we want the same results as from the general case. 00546 * We add 4 bytes on the left to speed up the copying, and 00547 * 8 bytes at the right and bottom to allow unrolling of 00548 * the computation of 8 pixels. 00549 */ 00550 PIX * 00551 pixErodeGray3(PIX *pixs, 00552 l_int32 hsize, 00553 l_int32 vsize) 00554 { 00555 PIX *pixt, *pixb, *pixbd, *pixd; 00556 00557 PROCNAME("pixErodeGray3"); 00558 00559 if (!pixs) 00560 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00561 if (pixGetDepth(pixs) != 8) 00562 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 00563 if (pixGetColormap(pixs)) 00564 return (PIX *)ERROR_PTR("pix has colormap", procName, NULL); 00565 if ((hsize != 1 && hsize != 3) || 00566 (vsize != 1 && vsize != 3)) 00567 return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", procName, NULL); 00568 00569 if (hsize == 1 && vsize == 1) 00570 return pixCopy(NULL, pixs); 00571 00572 pixb = pixAddBorderGeneral(pixs, 4, 8, 2, 8, 255); 00573 00574 if (vsize == 1) 00575 pixbd = pixErodeGray3h(pixb); 00576 else if (hsize == 1) 00577 pixbd = pixErodeGray3v(pixb); 00578 else { /* vize == hsize == 3 */ 00579 pixt = pixErodeGray3h(pixb); 00580 pixbd = pixErodeGray3v(pixt); 00581 pixDestroy(&pixt); 00582 } 00583 00584 pixd = pixRemoveBorderGeneral(pixbd, 4, 8, 2, 8); 00585 pixDestroy(&pixb); 00586 pixDestroy(&pixbd); 00587 return pixd; 00588 } 00589 00590 00591 /*! 00592 * pixErodeGray3h() 00593 * 00594 * Input: pixs (8 bpp, not cmapped) 00595 * Return: pixd, or null on error 00596 * 00597 * Notes: 00598 * (1) Special case for horizontal 3x1 brick Sel; 00599 * also used as the first step for the 3x3 brick Sel. 00600 */ 00601 static PIX * 00602 pixErodeGray3h(PIX *pixs) 00603 { 00604 l_uint32 *datas, *datad, *lines, *lined; 00605 l_int32 w, h, wpl, i, j; 00606 l_int32 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9, minval; 00607 PIX *pixd; 00608 00609 PROCNAME("pixErodeGray3h"); 00610 00611 if (!pixs) 00612 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00613 if (pixGetDepth(pixs) != 8) 00614 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 00615 00616 pixd = pixCreateTemplateNoInit(pixs); 00617 pixSetBorderVal(pixd, 4, 8, 2, 8, 0); /* only to silence valgrind */ 00618 pixGetDimensions(pixs, &w, &h, NULL); 00619 datas = pixGetData(pixs); 00620 datad = pixGetData(pixd); 00621 wpl = pixGetWpl(pixs); 00622 for (i = 0; i < h; i++) { 00623 lines = datas + i * wpl; 00624 lined = datad + i * wpl; 00625 for (j = 1; j < w - 8; j += 8) { 00626 val0 = GET_DATA_BYTE(lines, j - 1); 00627 val1 = GET_DATA_BYTE(lines, j); 00628 val2 = GET_DATA_BYTE(lines, j + 1); 00629 val3 = GET_DATA_BYTE(lines, j + 2); 00630 val4 = GET_DATA_BYTE(lines, j + 3); 00631 val5 = GET_DATA_BYTE(lines, j + 4); 00632 val6 = GET_DATA_BYTE(lines, j + 5); 00633 val7 = GET_DATA_BYTE(lines, j + 6); 00634 val8 = GET_DATA_BYTE(lines, j + 7); 00635 val9 = GET_DATA_BYTE(lines, j + 8); 00636 minval = L_MIN(val1, val2); 00637 SET_DATA_BYTE(lined, j, L_MIN(val0, minval)); 00638 SET_DATA_BYTE(lined, j + 1, L_MIN(minval, val3)); 00639 minval = L_MIN(val3, val4); 00640 SET_DATA_BYTE(lined, j + 2, L_MIN(val2, minval)); 00641 SET_DATA_BYTE(lined, j + 3, L_MIN(minval, val5)); 00642 minval = L_MIN(val5, val6); 00643 SET_DATA_BYTE(lined, j + 4, L_MIN(val4, minval)); 00644 SET_DATA_BYTE(lined, j + 5, L_MIN(minval, val7)); 00645 minval = L_MIN(val7, val8); 00646 SET_DATA_BYTE(lined, j + 6, L_MIN(val6, minval)); 00647 SET_DATA_BYTE(lined, j + 7, L_MIN(minval, val9)); 00648 } 00649 } 00650 return pixd; 00651 } 00652 00653 00654 /*! 00655 * pixErodeGray3v() 00656 * 00657 * Input: pixs (8 bpp, not cmapped) 00658 * Return: pixd, or null on error 00659 * 00660 * Notes: 00661 * (1) Special case for vertical 1x3 brick Sel; 00662 * also used as the second step for the 3x3 brick Sel. 00663 * (2) Surprisingly, this is faster than setting up the 00664 * lineptrs array and accessing into it; e.g., 00665 * val4 = GET_DATA_BYTE(lines8[i + 3], j); 00666 */ 00667 static PIX * 00668 pixErodeGray3v(PIX *pixs) 00669 { 00670 l_uint32 *datas, *datad, *linesi, *linedi; 00671 l_int32 w, h, wpl, i, j; 00672 l_int32 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9, minval; 00673 PIX *pixd; 00674 00675 PROCNAME("pixErodeGray3v"); 00676 00677 if (!pixs) 00678 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00679 if (pixGetDepth(pixs) != 8) 00680 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 00681 00682 pixd = pixCreateTemplateNoInit(pixs); 00683 pixGetDimensions(pixs, &w, &h, NULL); 00684 datas = pixGetData(pixs); 00685 datad = pixGetData(pixd); 00686 wpl = pixGetWpl(pixs); 00687 for (j = 0; j < w; j++) { 00688 for (i = 1; i < h - 8; i += 8) { 00689 linesi = datas + i * wpl; 00690 linedi = datad + i * wpl; 00691 val0 = GET_DATA_BYTE(linesi - wpl, j); 00692 val1 = GET_DATA_BYTE(linesi, j); 00693 val2 = GET_DATA_BYTE(linesi + wpl, j); 00694 val3 = GET_DATA_BYTE(linesi + 2 * wpl, j); 00695 val4 = GET_DATA_BYTE(linesi + 3 * wpl, j); 00696 val5 = GET_DATA_BYTE(linesi + 4 * wpl, j); 00697 val6 = GET_DATA_BYTE(linesi + 5 * wpl, j); 00698 val7 = GET_DATA_BYTE(linesi + 6 * wpl, j); 00699 val8 = GET_DATA_BYTE(linesi + 7 * wpl, j); 00700 val9 = GET_DATA_BYTE(linesi + 8 * wpl, j); 00701 minval = L_MIN(val1, val2); 00702 SET_DATA_BYTE(linedi, j, L_MIN(val0, minval)); 00703 SET_DATA_BYTE(linedi + wpl, j, L_MIN(minval, val3)); 00704 minval = L_MIN(val3, val4); 00705 SET_DATA_BYTE(linedi + 2 * wpl, j, L_MIN(val2, minval)); 00706 SET_DATA_BYTE(linedi + 3 * wpl, j, L_MIN(minval, val5)); 00707 minval = L_MIN(val5, val6); 00708 SET_DATA_BYTE(linedi + 4 * wpl, j, L_MIN(val4, minval)); 00709 SET_DATA_BYTE(linedi + 5 * wpl, j, L_MIN(minval, val7)); 00710 minval = L_MIN(val7, val8); 00711 SET_DATA_BYTE(linedi + 6 * wpl, j, L_MIN(val6, minval)); 00712 SET_DATA_BYTE(linedi + 7 * wpl, j, L_MIN(minval, val9)); 00713 } 00714 } 00715 return pixd; 00716 } 00717 00718 00719 /*! 00720 * pixDilateGray3() 00721 * 00722 * Input: pixs (8 bpp, not cmapped) 00723 * hsize (1 or 3) 00724 * vsize (1 or 3) 00725 * Return: pixd, or null on error 00726 * 00727 * Notes: 00728 * (1) Special case for 1x3, 3x1 or 3x3 brick sel (all hits) 00729 * (2) If hsize = vsize = 1, just returns a copy. 00730 */ 00731 PIX * 00732 pixDilateGray3(PIX *pixs, 00733 l_int32 hsize, 00734 l_int32 vsize) 00735 { 00736 PIX *pixt, *pixb, *pixbd, *pixd; 00737 00738 PROCNAME("pixDilateGray3"); 00739 00740 if (!pixs) 00741 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00742 if (pixGetDepth(pixs) != 8) 00743 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 00744 if (pixGetColormap(pixs)) 00745 return (PIX *)ERROR_PTR("pix has colormap", procName, NULL); 00746 if ((hsize != 1 && hsize != 3) || 00747 (vsize != 1 && vsize != 3)) 00748 return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", procName, NULL); 00749 00750 if (hsize == 1 && vsize == 1) 00751 return pixCopy(NULL, pixs); 00752 00753 pixb = pixAddBorderGeneral(pixs, 4, 8, 2, 8, 0); 00754 00755 if (vsize == 1) 00756 pixbd = pixDilateGray3h(pixb); 00757 else if (hsize == 1) 00758 pixbd = pixDilateGray3v(pixb); 00759 else { /* vize == hsize == 3 */ 00760 pixt = pixDilateGray3h(pixb); 00761 pixbd = pixDilateGray3v(pixt); 00762 pixDestroy(&pixt); 00763 } 00764 00765 pixd = pixRemoveBorderGeneral(pixbd, 4, 8, 2, 8); 00766 pixDestroy(&pixb); 00767 pixDestroy(&pixbd); 00768 return pixd; 00769 } 00770 00771 00772 /*! 00773 * pixDilateGray3h() 00774 * 00775 * Input: pixs (8 bpp, not cmapped) 00776 * Return: pixd, or null on error 00777 * 00778 * Notes: 00779 * (1) Special case for horizontal 3x1 brick Sel; 00780 * also used as the first step for the 3x3 brick Sel. 00781 */ 00782 static PIX * 00783 pixDilateGray3h(PIX *pixs) 00784 { 00785 l_uint32 *datas, *datad, *lines, *lined; 00786 l_int32 w, h, wpl, i, j; 00787 l_int32 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9, maxval; 00788 PIX *pixd; 00789 00790 PROCNAME("pixDilateGray3h"); 00791 00792 if (!pixs) 00793 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00794 if (pixGetDepth(pixs) != 8) 00795 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 00796 00797 pixd = pixCreateTemplateNoInit(pixs); 00798 pixSetBorderVal(pixd, 4, 8, 2, 8, 0); /* only to silence valgrind */ 00799 pixGetDimensions(pixs, &w, &h, NULL); 00800 datas = pixGetData(pixs); 00801 datad = pixGetData(pixd); 00802 wpl = pixGetWpl(pixs); 00803 for (i = 0; i < h; i++) { 00804 lines = datas + i * wpl; 00805 lined = datad + i * wpl; 00806 for (j = 1; j < w - 8; j += 8) { 00807 val0 = GET_DATA_BYTE(lines, j - 1); 00808 val1 = GET_DATA_BYTE(lines, j); 00809 val2 = GET_DATA_BYTE(lines, j + 1); 00810 val3 = GET_DATA_BYTE(lines, j + 2); 00811 val4 = GET_DATA_BYTE(lines, j + 3); 00812 val5 = GET_DATA_BYTE(lines, j + 4); 00813 val6 = GET_DATA_BYTE(lines, j + 5); 00814 val7 = GET_DATA_BYTE(lines, j + 6); 00815 val8 = GET_DATA_BYTE(lines, j + 7); 00816 val9 = GET_DATA_BYTE(lines, j + 8); 00817 maxval = L_MAX(val1, val2); 00818 SET_DATA_BYTE(lined, j, L_MAX(val0, maxval)); 00819 SET_DATA_BYTE(lined, j + 1, L_MAX(maxval, val3)); 00820 maxval = L_MAX(val3, val4); 00821 SET_DATA_BYTE(lined, j + 2, L_MAX(val2, maxval)); 00822 SET_DATA_BYTE(lined, j + 3, L_MAX(maxval, val5)); 00823 maxval = L_MAX(val5, val6); 00824 SET_DATA_BYTE(lined, j + 4, L_MAX(val4, maxval)); 00825 SET_DATA_BYTE(lined, j + 5, L_MAX(maxval, val7)); 00826 maxval = L_MAX(val7, val8); 00827 SET_DATA_BYTE(lined, j + 6, L_MAX(val6, maxval)); 00828 SET_DATA_BYTE(lined, j + 7, L_MAX(maxval, val9)); 00829 } 00830 } 00831 return pixd; 00832 } 00833 00834 00835 /*! 00836 * pixDilateGray3v() 00837 * 00838 * Input: pixs (8 bpp, not cmapped) 00839 * Return: pixd, or null on error 00840 * 00841 * Notes: 00842 * (1) Special case for vertical 1x3 brick Sel; 00843 * also used as the second step for the 3x3 brick Sel. 00844 */ 00845 static PIX * 00846 pixDilateGray3v(PIX *pixs) 00847 { 00848 l_uint32 *datas, *datad, *linesi, *linedi; 00849 l_int32 w, h, wpl, i, j; 00850 l_int32 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9, maxval; 00851 PIX *pixd; 00852 00853 PROCNAME("pixDilateGray3v"); 00854 00855 if (!pixs) 00856 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00857 if (pixGetDepth(pixs) != 8) 00858 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 00859 00860 pixd = pixCreateTemplateNoInit(pixs); 00861 pixGetDimensions(pixs, &w, &h, NULL); 00862 datas = pixGetData(pixs); 00863 datad = pixGetData(pixd); 00864 wpl = pixGetWpl(pixs); 00865 for (j = 0; j < w; j++) { 00866 for (i = 1; i < h - 8; i += 8) { 00867 linesi = datas + i * wpl; 00868 linedi = datad + i * wpl; 00869 val0 = GET_DATA_BYTE(linesi - wpl, j); 00870 val1 = GET_DATA_BYTE(linesi, j); 00871 val2 = GET_DATA_BYTE(linesi + wpl, j); 00872 val3 = GET_DATA_BYTE(linesi + 2 * wpl, j); 00873 val4 = GET_DATA_BYTE(linesi + 3 * wpl, j); 00874 val5 = GET_DATA_BYTE(linesi + 4 * wpl, j); 00875 val6 = GET_DATA_BYTE(linesi + 5 * wpl, j); 00876 val7 = GET_DATA_BYTE(linesi + 6 * wpl, j); 00877 val8 = GET_DATA_BYTE(linesi + 7 * wpl, j); 00878 val9 = GET_DATA_BYTE(linesi + 8 * wpl, j); 00879 maxval = L_MAX(val1, val2); 00880 SET_DATA_BYTE(linedi, j, L_MAX(val0, maxval)); 00881 SET_DATA_BYTE(linedi + wpl, j, L_MAX(maxval, val3)); 00882 maxval = L_MAX(val3, val4); 00883 SET_DATA_BYTE(linedi + 2 * wpl, j, L_MAX(val2, maxval)); 00884 SET_DATA_BYTE(linedi + 3 * wpl, j, L_MAX(maxval, val5)); 00885 maxval = L_MAX(val5, val6); 00886 SET_DATA_BYTE(linedi + 4 * wpl, j, L_MAX(val4, maxval)); 00887 SET_DATA_BYTE(linedi + 5 * wpl, j, L_MAX(maxval, val7)); 00888 maxval = L_MAX(val7, val8); 00889 SET_DATA_BYTE(linedi + 6 * wpl, j, L_MAX(val6, maxval)); 00890 SET_DATA_BYTE(linedi + 7 * wpl, j, L_MAX(maxval, val9)); 00891 } 00892 } 00893 return pixd; 00894 } 00895 00896 00897 /*! 00898 * pixOpenGray3() 00899 * 00900 * Input: pixs (8 bpp, not cmapped) 00901 * hsize (1 or 3) 00902 * vsize (1 or 3) 00903 * Return: pixd, or null on error 00904 * 00905 * Notes: 00906 * (1) Special case for 1x3, 3x1 or 3x3 brick sel (all hits) 00907 * (2) If hsize = vsize = 1, just returns a copy. 00908 * (3) It would be nice not to add a border, but it is required 00909 * to get the same results as for the general case. 00910 */ 00911 PIX * 00912 pixOpenGray3(PIX *pixs, 00913 l_int32 hsize, 00914 l_int32 vsize) 00915 { 00916 PIX *pixt, *pixb, *pixbd, *pixd; 00917 00918 PROCNAME("pixOpenGray3"); 00919 00920 if (!pixs) 00921 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00922 if (pixGetDepth(pixs) != 8) 00923 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 00924 if (pixGetColormap(pixs)) 00925 return (PIX *)ERROR_PTR("pix has colormap", procName, NULL); 00926 if ((hsize != 1 && hsize != 3) || 00927 (vsize != 1 && vsize != 3)) 00928 return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", procName, NULL); 00929 00930 if (hsize == 1 && vsize == 1) 00931 return pixCopy(NULL, pixs); 00932 00933 pixb = pixAddBorderGeneral(pixs, 4, 8, 2, 8, 255); /* set to max */ 00934 00935 if (vsize == 1) { 00936 pixt = pixErodeGray3h(pixb); 00937 pixSetBorderVal(pixt, 4, 8, 2, 8, 0); /* set to min */ 00938 pixbd = pixDilateGray3h(pixt); 00939 pixDestroy(&pixt); 00940 } 00941 else if (hsize == 1) { 00942 pixt = pixErodeGray3v(pixb); 00943 pixSetBorderVal(pixt, 4, 8, 2, 8, 0); 00944 pixbd = pixDilateGray3v(pixt); 00945 pixDestroy(&pixt); 00946 } 00947 else { /* vize == hsize == 3 */ 00948 pixt = pixErodeGray3h(pixb); 00949 pixbd = pixErodeGray3v(pixt); 00950 pixDestroy(&pixt); 00951 pixSetBorderVal(pixbd, 4, 8, 2, 8, 0); 00952 pixt = pixDilateGray3h(pixbd); 00953 pixDestroy(&pixbd); 00954 pixbd = pixDilateGray3v(pixt); 00955 pixDestroy(&pixt); 00956 } 00957 00958 pixd = pixRemoveBorderGeneral(pixbd, 4, 8, 2, 8); 00959 pixDestroy(&pixb); 00960 pixDestroy(&pixbd); 00961 return pixd; 00962 } 00963 00964 00965 /*! 00966 * pixCloseGray3() 00967 * 00968 * Input: pixs (8 bpp, not cmapped) 00969 * hsize (1 or 3) 00970 * vsize (1 or 3) 00971 * Return: pixd, or null on error 00972 * 00973 * Notes: 00974 * (1) Special case for 1x3, 3x1 or 3x3 brick sel (all hits) 00975 * (2) If hsize = vsize = 1, just returns a copy. 00976 */ 00977 PIX * 00978 pixCloseGray3(PIX *pixs, 00979 l_int32 hsize, 00980 l_int32 vsize) 00981 { 00982 PIX *pixt, *pixb, *pixbd, *pixd; 00983 00984 PROCNAME("pixCloseGray3"); 00985 00986 if (!pixs) 00987 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00988 if (pixGetDepth(pixs) != 8) 00989 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 00990 if (pixGetColormap(pixs)) 00991 return (PIX *)ERROR_PTR("pix has colormap", procName, NULL); 00992 if ((hsize != 1 && hsize != 3) || 00993 (vsize != 1 && vsize != 3)) 00994 return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", procName, NULL); 00995 00996 if (hsize == 1 && vsize == 1) 00997 return pixCopy(NULL, pixs); 00998 00999 pixb = pixAddBorderGeneral(pixs, 4, 8, 2, 8, 0); /* set to min */ 01000 01001 if (vsize == 1) { 01002 pixt = pixDilateGray3h(pixb); 01003 pixSetBorderVal(pixt, 4, 8, 2, 8, 255); /* set to max */ 01004 pixbd = pixErodeGray3h(pixt); 01005 pixDestroy(&pixt); 01006 } 01007 else if (hsize == 1) { 01008 pixt = pixDilateGray3v(pixb); 01009 pixSetBorderVal(pixt, 4, 8, 2, 8, 255); 01010 pixbd = pixErodeGray3v(pixt); 01011 pixDestroy(&pixt); 01012 } 01013 else { /* vize == hsize == 3 */ 01014 pixt = pixDilateGray3h(pixb); 01015 pixbd = pixDilateGray3v(pixt); 01016 pixDestroy(&pixt); 01017 pixSetBorderVal(pixbd, 4, 8, 2, 8, 255); 01018 pixt = pixErodeGray3h(pixbd); 01019 pixDestroy(&pixbd); 01020 pixbd = pixErodeGray3v(pixt); 01021 pixDestroy(&pixt); 01022 } 01023 01024 pixd = pixRemoveBorderGeneral(pixbd, 4, 8, 2, 8); 01025 pixDestroy(&pixb); 01026 pixDestroy(&pixbd); 01027 return pixd; 01028 } 01029 01030