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 * morphseq.c 00018 * 00019 * Run a sequence of binary rasterop morphological operations 00020 * PIX *pixMorphSequence() 00021 * 00022 * Run a sequence of binary composite rasterop morphological operations 00023 * PIX *pixMorphCompSequence() 00024 * 00025 * Run a sequence of binary dwa morphological operations 00026 * PIX *pixMorphSequenceDwa() 00027 * 00028 * Run a sequence of binary composite dwa morphological operations 00029 * PIX *pixMorphCompSequenceDwa() 00030 * 00031 * Parser verifier for binary morphological operations 00032 * l_int32 morphSequenceVerify() 00033 * 00034 * Run a sequence of grayscale morphological operations 00035 * PIX *pixGrayMorphSequence() 00036 * 00037 * Run a sequence of color morphological operations 00038 * PIX *pixColorMorphSequence() 00039 */ 00040 00041 #include <stdio.h> 00042 #include <stdlib.h> 00043 #include <string.h> 00044 #include "allheaders.h" 00045 00046 00047 /*-------------------------------------------------------------------------* 00048 * Run a sequence of binary rasterop morphological operations * 00049 *-------------------------------------------------------------------------*/ 00050 /*! 00051 * pixMorphSequence() 00052 * 00053 * Input: pixs 00054 * sequence (string specifying sequence) 00055 * dispsep (horizontal separation in pixels between 00056 * successive displays; use zero to suppress display) 00057 * Return: pixd, or null on error 00058 * 00059 * Notes: 00060 * (1) This does rasterop morphology on binary images. 00061 * (2) This runs a pipeline of operations; no branching is allowed. 00062 * (3) This only uses brick Sels, which are created on the fly. 00063 * In the future this will be generalized to extract Sels from 00064 * a Sela by name. 00065 * (4) A new image is always produced; the input image is not changed. 00066 * (5) This contains an interpreter, allowing sequences to be 00067 * generated and run. 00068 * (6) The format of the sequence string is defined below. 00069 * (7) In addition to morphological operations, rank order reduction 00070 * and replicated expansion allow operations to take place 00071 * downscaled by a power of 2. 00072 * (8) Intermediate results can optionally be displayed. 00073 * (9) Thanks to Dar-Shyang Lee, who had the idea for this and 00074 * built the first implementation. 00075 * (10) The sequence string is formatted as follows: 00076 * - An arbitrary number of operations, each separated 00077 * by a '+' character. White space is ignored. 00078 * - Each operation begins with a case-independent character 00079 * specifying the operation: 00080 * d or D (dilation) 00081 * e or E (erosion) 00082 * o or O (opening) 00083 * c or C (closing) 00084 * r or R (rank binary reduction) 00085 * x or X (replicative binary expansion) 00086 * b or B (add a border of 0 pixels of this size) 00087 * - The args to the morphological operations are bricks of hits, 00088 * and are formatted as a.b, where a and b are horizontal and 00089 * vertical dimensions, rsp. 00090 * - The args to the reduction are a sequence of up to 4 integers, 00091 * each from 1 to 4. 00092 * - The arg to the expansion is a power of two, in the set 00093 * {2, 4, 8, 16}. 00094 * (11) An example valid sequence is: 00095 * "b32 + o1.3 + C3.1 + r23 + e2.2 + D3.2 + X4" 00096 * In this example, the following operation sequence is carried out: 00097 * * b32: Add a 32 pixel border around the input image 00098 * * o1.3: Opening with vert sel of length 3 (e.g., 1 x 3) 00099 * * C3.1: Closing with horiz sel of length 3 (e.g., 3 x 1) 00100 * * r23: Two successive 2x2 reductions with rank 2 in the first 00101 * and rank 3 in the second. The result is a 4x reduced pix. 00102 * * e2.2: Erosion with a 2x2 sel (origin will be at x,y: 0,0) 00103 * * d3.2: Dilation with a 3x2 sel (origin will be at x,y: 1,0) 00104 * * X4: 4x replicative expansion, back to original resolution 00105 * (12) The safe closing is used. However, if you implement a 00106 * closing as separable dilations followed by separable erosions, 00107 * it will not be safe. For that situation, you need to add 00108 * a sufficiently large border as the first operation in 00109 * the sequence. This will be removed automatically at the 00110 * end. There are two cautions: 00111 * - When computing what is sufficient, remember that if 00112 * reductions are carried out, the border is also reduced. 00113 * - The border is removed at the end, so if a border is 00114 * added at the beginning, the result must be at the 00115 * same resolution as the input! 00116 */ 00117 PIX * 00118 pixMorphSequence(PIX *pixs, 00119 const char *sequence, 00120 l_int32 dispsep) 00121 { 00122 char *rawop, *op; 00123 l_int32 nops, i, j, nred, fact, w, h, x, y, border; 00124 l_int32 level[4]; 00125 PIX *pixt1, *pixt2; 00126 SARRAY *sa; 00127 00128 PROCNAME("pixMorphSequence"); 00129 00130 if (!pixs) 00131 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00132 if (!sequence) 00133 return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); 00134 00135 /* Split sequence into individual operations */ 00136 sa = sarrayCreate(0); 00137 sarraySplitString(sa, sequence, "+"); 00138 nops = sarrayGetCount(sa); 00139 00140 if (!morphSequenceVerify(sa)) { 00141 sarrayDestroy(&sa); 00142 return (PIX *)ERROR_PTR("sequence not valid", procName, NULL); 00143 } 00144 00145 /* Parse and operate */ 00146 border = 0; 00147 pixt1 = pixCopy(NULL, pixs); 00148 pixt2 = NULL; 00149 x = y = 0; 00150 for (i = 0; i < nops; i++) { 00151 rawop = sarrayGetString(sa, i, 0); 00152 op = stringRemoveChars(rawop, " \n\t"); 00153 switch (op[0]) 00154 { 00155 case 'd': 00156 case 'D': 00157 sscanf(&op[1], "%d.%d", &w, &h); 00158 pixt2 = pixDilateBrick(NULL, pixt1, w, h); 00159 pixDestroy(&pixt1); 00160 pixt1 = pixClone(pixt2); 00161 pixDestroy(&pixt2); 00162 if (dispsep > 0) { 00163 pixDisplay(pixt1, x, y); 00164 x += dispsep; 00165 } 00166 break; 00167 case 'e': 00168 case 'E': 00169 sscanf(&op[1], "%d.%d", &w, &h); 00170 pixt2 = pixErodeBrick(NULL, pixt1, w, h); 00171 pixDestroy(&pixt1); 00172 pixt1 = pixClone(pixt2); 00173 pixDestroy(&pixt2); 00174 if (dispsep > 0) { 00175 pixDisplay(pixt1, x, y); 00176 x += dispsep; 00177 } 00178 break; 00179 case 'o': 00180 case 'O': 00181 sscanf(&op[1], "%d.%d", &w, &h); 00182 pixOpenBrick(pixt1, pixt1, w, h); 00183 if (dispsep > 0) { 00184 pixDisplay(pixt1, x, y); 00185 x += dispsep; 00186 } 00187 break; 00188 case 'c': 00189 case 'C': 00190 sscanf(&op[1], "%d.%d", &w, &h); 00191 pixCloseSafeBrick(pixt1, pixt1, w, h); 00192 if (dispsep > 0) { 00193 pixDisplay(pixt1, x, y); 00194 x += dispsep; 00195 } 00196 break; 00197 case 'r': 00198 case 'R': 00199 nred = strlen(op) - 1; 00200 for (j = 0; j < nred; j++) 00201 level[j] = op[j + 1] - '0'; 00202 for (j = nred; j < 4; j++) 00203 level[j] = 0; 00204 pixt2 = pixReduceRankBinaryCascade(pixt1, level[0], level[1], 00205 level[2], level[3]); 00206 pixDestroy(&pixt1); 00207 pixt1 = pixClone(pixt2); 00208 pixDestroy(&pixt2); 00209 if (dispsep > 0) { 00210 pixDisplay(pixt1, x, y); 00211 x += dispsep; 00212 } 00213 break; 00214 case 'x': 00215 case 'X': 00216 sscanf(&op[1], "%d", &fact); 00217 pixt2 = pixExpandReplicate(pixt1, fact); 00218 pixDestroy(&pixt1); 00219 pixt1 = pixClone(pixt2); 00220 pixDestroy(&pixt2); 00221 if (dispsep > 0) { 00222 pixDisplay(pixt1, x, y); 00223 x += dispsep; 00224 } 00225 break; 00226 case 'b': 00227 case 'B': 00228 sscanf(&op[1], "%d", &border); 00229 pixt2 = pixAddBorder(pixt1, border, 0); 00230 pixDestroy(&pixt1); 00231 pixt1 = pixClone(pixt2); 00232 pixDestroy(&pixt2); 00233 if (dispsep > 0) { 00234 pixDisplay(pixt1, x, y); 00235 x += dispsep; 00236 } 00237 break; 00238 default: 00239 /* All invalid ops are caught in the first pass */ 00240 break; 00241 } 00242 FREE(op); 00243 } 00244 if (border > 0) { 00245 pixt2 = pixRemoveBorder(pixt1, border); 00246 pixDestroy(&pixt1); 00247 pixt1 = pixClone(pixt2); 00248 pixDestroy(&pixt2); 00249 } 00250 00251 sarrayDestroy(&sa); 00252 return pixt1; 00253 } 00254 00255 00256 /*-------------------------------------------------------------------------* 00257 * Run a sequence of binary composite rasterop morphological operations * 00258 *-------------------------------------------------------------------------*/ 00259 /*! 00260 * pixMorphCompSequence() 00261 * 00262 * Input: pixs 00263 * sequence (string specifying sequence) 00264 * dispsep (horizontal separation in pixels between 00265 * successive displays; use zero to suppress display) 00266 * Return: pixd, or null on error 00267 * 00268 * Notes: 00269 * (1) This does rasterop morphology on binary images, using composite 00270 * operations for extra speed on large Sels. 00271 * (2) Safe closing is used atomically. However, if you implement a 00272 * closing as a sequence with a dilation followed by an 00273 * erosion, it will not be safe, and to ensure that you have 00274 * no boundary effects you must add a border in advance and 00275 * remove it at the end. 00276 * (3) For other usage details, see the notes for pixMorphSequence(). 00277 * (4) The sequence string is formatted as follows: 00278 * - An arbitrary number of operations, each separated 00279 * by a '+' character. White space is ignored. 00280 * - Each operation begins with a case-independent character 00281 * specifying the operation: 00282 * d or D (dilation) 00283 * e or E (erosion) 00284 * o or O (opening) 00285 * c or C (closing) 00286 * r or R (rank binary reduction) 00287 * x or X (replicative binary expansion) 00288 * b or B (add a border of 0 pixels of this size) 00289 * - The args to the morphological operations are bricks of hits, 00290 * and are formatted as a.b, where a and b are horizontal and 00291 * vertical dimensions, rsp. 00292 * - The args to the reduction are a sequence of up to 4 integers, 00293 * each from 1 to 4. 00294 * - The arg to the expansion is a power of two, in the set 00295 * {2, 4, 8, 16}. 00296 */ 00297 PIX * 00298 pixMorphCompSequence(PIX *pixs, 00299 const char *sequence, 00300 l_int32 dispsep) 00301 { 00302 char *rawop, *op; 00303 l_int32 nops, i, j, nred, fact, w, h, x, y, border; 00304 l_int32 level[4]; 00305 PIX *pixt1, *pixt2; 00306 SARRAY *sa; 00307 00308 PROCNAME("pixMorphCompSequence"); 00309 00310 if (!pixs) 00311 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00312 if (!sequence) 00313 return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); 00314 00315 /* Split sequence into individual operations */ 00316 sa = sarrayCreate(0); 00317 sarraySplitString(sa, sequence, "+"); 00318 nops = sarrayGetCount(sa); 00319 00320 if (!morphSequenceVerify(sa)) { 00321 sarrayDestroy(&sa); 00322 return (PIX *)ERROR_PTR("sequence not valid", procName, NULL); 00323 } 00324 00325 /* Parse and operate */ 00326 border = 0; 00327 pixt1 = pixCopy(NULL, pixs); 00328 pixt2 = NULL; 00329 x = y = 0; 00330 for (i = 0; i < nops; i++) { 00331 rawop = sarrayGetString(sa, i, 0); 00332 op = stringRemoveChars(rawop, " \n\t"); 00333 switch (op[0]) 00334 { 00335 case 'd': 00336 case 'D': 00337 sscanf(&op[1], "%d.%d", &w, &h); 00338 pixt2 = pixDilateCompBrick(NULL, pixt1, w, h); 00339 pixDestroy(&pixt1); 00340 pixt1 = pixClone(pixt2); 00341 pixDestroy(&pixt2); 00342 if (dispsep > 0) { 00343 pixDisplay(pixt1, x, y); 00344 x += dispsep; 00345 } 00346 break; 00347 case 'e': 00348 case 'E': 00349 sscanf(&op[1], "%d.%d", &w, &h); 00350 pixt2 = pixErodeCompBrick(NULL, pixt1, w, h); 00351 pixDestroy(&pixt1); 00352 pixt1 = pixClone(pixt2); 00353 pixDestroy(&pixt2); 00354 if (dispsep > 0) { 00355 pixDisplay(pixt1, x, y); 00356 x += dispsep; 00357 } 00358 break; 00359 case 'o': 00360 case 'O': 00361 sscanf(&op[1], "%d.%d", &w, &h); 00362 pixOpenCompBrick(pixt1, pixt1, w, h); 00363 if (dispsep > 0) { 00364 pixDisplay(pixt1, x, y); 00365 x += dispsep; 00366 } 00367 break; 00368 case 'c': 00369 case 'C': 00370 sscanf(&op[1], "%d.%d", &w, &h); 00371 pixCloseSafeCompBrick(pixt1, pixt1, w, h); 00372 if (dispsep > 0) { 00373 pixDisplay(pixt1, x, y); 00374 x += dispsep; 00375 } 00376 break; 00377 case 'r': 00378 case 'R': 00379 nred = strlen(op) - 1; 00380 for (j = 0; j < nred; j++) 00381 level[j] = op[j + 1] - '0'; 00382 for (j = nred; j < 4; j++) 00383 level[j] = 0; 00384 pixt2 = pixReduceRankBinaryCascade(pixt1, level[0], level[1], 00385 level[2], level[3]); 00386 pixDestroy(&pixt1); 00387 pixt1 = pixClone(pixt2); 00388 pixDestroy(&pixt2); 00389 if (dispsep > 0) { 00390 pixDisplay(pixt1, x, y); 00391 x += dispsep; 00392 } 00393 break; 00394 case 'x': 00395 case 'X': 00396 sscanf(&op[1], "%d", &fact); 00397 pixt2 = pixExpandReplicate(pixt1, fact); 00398 pixDestroy(&pixt1); 00399 pixt1 = pixClone(pixt2); 00400 pixDestroy(&pixt2); 00401 if (dispsep > 0) { 00402 pixDisplay(pixt1, x, y); 00403 x += dispsep; 00404 } 00405 break; 00406 case 'b': 00407 case 'B': 00408 sscanf(&op[1], "%d", &border); 00409 pixt2 = pixAddBorder(pixt1, border, 0); 00410 pixDestroy(&pixt1); 00411 pixt1 = pixClone(pixt2); 00412 pixDestroy(&pixt2); 00413 if (dispsep > 0) { 00414 pixDisplay(pixt1, x, y); 00415 x += dispsep; 00416 } 00417 break; 00418 default: 00419 /* All invalid ops are caught in the first pass */ 00420 break; 00421 } 00422 FREE(op); 00423 } 00424 if (border > 0) { 00425 pixt2 = pixRemoveBorder(pixt1, border); 00426 pixDestroy(&pixt1); 00427 pixt1 = pixClone(pixt2); 00428 pixDestroy(&pixt2); 00429 } 00430 00431 sarrayDestroy(&sa); 00432 return pixt1; 00433 } 00434 00435 00436 /*-------------------------------------------------------------------------* 00437 * Run a sequence of binary dwa morphological operations * 00438 *-------------------------------------------------------------------------*/ 00439 /*! 00440 * pixMorphSequenceDwa() 00441 * 00442 * Input: pixs 00443 * sequence (string specifying sequence) 00444 * dispsep (horizontal separation in pixels between 00445 * successive displays; use zero to suppress display) 00446 * Return: pixd, or null on error 00447 * 00448 * Notes: 00449 * (1) This does dwa morphology on binary images. 00450 * (2) This runs a pipeline of operations; no branching is allowed. 00451 * (3) This only uses brick Sels that have been pre-compiled with 00452 * dwa code. 00453 * (4) A new image is always produced; the input image is not changed. 00454 * (5) This contains an interpreter, allowing sequences to be 00455 * generated and run. 00456 * (6) See pixMorphSequence() for further information about usage. 00457 */ 00458 PIX * 00459 pixMorphSequenceDwa(PIX *pixs, 00460 const char *sequence, 00461 l_int32 dispsep) 00462 { 00463 char *rawop, *op; 00464 l_int32 nops, i, j, nred, fact, w, h, x, y, border; 00465 l_int32 level[4]; 00466 PIX *pixt1, *pixt2; 00467 SARRAY *sa; 00468 00469 PROCNAME("pixMorphSequenceDwa"); 00470 00471 if (!pixs) 00472 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00473 if (!sequence) 00474 return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); 00475 00476 /* Split sequence into individual operations */ 00477 sa = sarrayCreate(0); 00478 sarraySplitString(sa, sequence, "+"); 00479 nops = sarrayGetCount(sa); 00480 00481 if (!morphSequenceVerify(sa)) { 00482 sarrayDestroy(&sa); 00483 return (PIX *)ERROR_PTR("sequence not valid", procName, NULL); 00484 } 00485 00486 /* Parse and operate */ 00487 border = 0; 00488 pixt1 = pixCopy(NULL, pixs); 00489 pixt2 = NULL; 00490 x = y = 0; 00491 for (i = 0; i < nops; i++) { 00492 rawop = sarrayGetString(sa, i, 0); 00493 op = stringRemoveChars(rawop, " \n\t"); 00494 switch (op[0]) 00495 { 00496 case 'd': 00497 case 'D': 00498 sscanf(&op[1], "%d.%d", &w, &h); 00499 pixt2 = pixDilateBrickDwa(NULL, pixt1, w, h); 00500 pixDestroy(&pixt1); 00501 pixt1 = pixClone(pixt2); 00502 pixDestroy(&pixt2); 00503 if (dispsep > 0) { 00504 pixDisplay(pixt1, x, y); 00505 x += dispsep; 00506 } 00507 break; 00508 case 'e': 00509 case 'E': 00510 sscanf(&op[1], "%d.%d", &w, &h); 00511 pixt2 = pixErodeBrickDwa(NULL, pixt1, w, h); 00512 pixDestroy(&pixt1); 00513 pixt1 = pixClone(pixt2); 00514 pixDestroy(&pixt2); 00515 if (dispsep > 0) { 00516 pixDisplay(pixt1, x, y); 00517 x += dispsep; 00518 } 00519 break; 00520 case 'o': 00521 case 'O': 00522 sscanf(&op[1], "%d.%d", &w, &h); 00523 pixOpenBrickDwa(pixt1, pixt1, w, h); 00524 if (dispsep > 0) { 00525 pixDisplay(pixt1, x, y); 00526 x += dispsep; 00527 } 00528 break; 00529 case 'c': 00530 case 'C': 00531 sscanf(&op[1], "%d.%d", &w, &h); 00532 pixCloseBrickDwa(pixt1, pixt1, w, h); 00533 if (dispsep > 0) { 00534 pixDisplay(pixt1, x, y); 00535 x += dispsep; 00536 } 00537 break; 00538 case 'r': 00539 case 'R': 00540 nred = strlen(op) - 1; 00541 for (j = 0; j < nred; j++) 00542 level[j] = op[j + 1] - '0'; 00543 for (j = nred; j < 4; j++) 00544 level[j] = 0; 00545 pixt2 = pixReduceRankBinaryCascade(pixt1, level[0], level[1], 00546 level[2], level[3]); 00547 pixDestroy(&pixt1); 00548 pixt1 = pixClone(pixt2); 00549 pixDestroy(&pixt2); 00550 if (dispsep > 0) { 00551 pixDisplay(pixt1, x, y); 00552 x += dispsep; 00553 } 00554 break; 00555 case 'x': 00556 case 'X': 00557 sscanf(&op[1], "%d", &fact); 00558 pixt2 = pixExpandReplicate(pixt1, fact); 00559 pixDestroy(&pixt1); 00560 pixt1 = pixClone(pixt2); 00561 pixDestroy(&pixt2); 00562 if (dispsep > 0) { 00563 pixDisplay(pixt1, x, y); 00564 x += dispsep; 00565 } 00566 break; 00567 case 'b': 00568 case 'B': 00569 sscanf(&op[1], "%d", &border); 00570 pixt2 = pixAddBorder(pixt1, border, 0); 00571 pixDestroy(&pixt1); 00572 pixt1 = pixClone(pixt2); 00573 pixDestroy(&pixt2); 00574 if (dispsep > 0) { 00575 pixDisplay(pixt1, x, y); 00576 x += dispsep; 00577 } 00578 break; 00579 default: 00580 /* All invalid ops are caught in the first pass */ 00581 break; 00582 } 00583 FREE(op); 00584 } 00585 if (border > 0) { 00586 pixt2 = pixRemoveBorder(pixt1, border); 00587 pixDestroy(&pixt1); 00588 pixt1 = pixClone(pixt2); 00589 pixDestroy(&pixt2); 00590 } 00591 00592 sarrayDestroy(&sa); 00593 return pixt1; 00594 } 00595 00596 00597 /*-------------------------------------------------------------------------* 00598 * Run a sequence of binary composite dwa morphological operations * 00599 *-------------------------------------------------------------------------*/ 00600 /*! 00601 * pixMorphCompSequenceDwa() 00602 * 00603 * Input: pixs 00604 * sequence (string specifying sequence) 00605 * dispsep (horizontal separation in pixels between 00606 * successive displays; use zero to suppress display) 00607 * Return: pixd, or null on error 00608 * 00609 * Notes: 00610 * (1) This does dwa morphology on binary images, using brick Sels. 00611 * (2) This runs a pipeline of operations; no branching is allowed. 00612 * (3) It implements all brick Sels that have dimensions up to 63 00613 * on each side, using a composite (linear + comb) when useful. 00614 * (4) A new image is always produced; the input image is not changed. 00615 * (5) This contains an interpreter, allowing sequences to be 00616 * generated and run. 00617 * (6) See pixMorphSequence() for further information about usage. 00618 */ 00619 PIX * 00620 pixMorphCompSequenceDwa(PIX *pixs, 00621 const char *sequence, 00622 l_int32 dispsep) 00623 { 00624 char *rawop, *op; 00625 l_int32 nops, i, j, nred, fact, w, h, x, y, border; 00626 l_int32 level[4]; 00627 PIX *pixt1, *pixt2; 00628 SARRAY *sa; 00629 00630 PROCNAME("pixMorphCompSequenceDwa"); 00631 00632 if (!pixs) 00633 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00634 if (!sequence) 00635 return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); 00636 00637 /* Split sequence into individual operations */ 00638 sa = sarrayCreate(0); 00639 sarraySplitString(sa, sequence, "+"); 00640 nops = sarrayGetCount(sa); 00641 00642 if (!morphSequenceVerify(sa)) { 00643 sarrayDestroy(&sa); 00644 return (PIX *)ERROR_PTR("sequence not valid", procName, NULL); 00645 } 00646 00647 /* Parse and operate */ 00648 border = 0; 00649 pixt1 = pixCopy(NULL, pixs); 00650 pixt2 = NULL; 00651 x = y = 0; 00652 for (i = 0; i < nops; i++) { 00653 rawop = sarrayGetString(sa, i, 0); 00654 op = stringRemoveChars(rawop, " \n\t"); 00655 switch (op[0]) 00656 { 00657 case 'd': 00658 case 'D': 00659 sscanf(&op[1], "%d.%d", &w, &h); 00660 pixt2 = pixDilateCompBrickDwa(NULL, pixt1, w, h); 00661 pixDestroy(&pixt1); 00662 pixt1 = pixClone(pixt2); 00663 pixDestroy(&pixt2); 00664 if (dispsep > 0) { 00665 pixDisplay(pixt1, x, y); 00666 x += dispsep; 00667 } 00668 break; 00669 case 'e': 00670 case 'E': 00671 sscanf(&op[1], "%d.%d", &w, &h); 00672 pixt2 = pixErodeCompBrickDwa(NULL, pixt1, w, h); 00673 pixDestroy(&pixt1); 00674 pixt1 = pixClone(pixt2); 00675 pixDestroy(&pixt2); 00676 if (dispsep > 0) { 00677 pixDisplay(pixt1, x, y); 00678 x += dispsep; 00679 } 00680 break; 00681 case 'o': 00682 case 'O': 00683 sscanf(&op[1], "%d.%d", &w, &h); 00684 pixOpenCompBrickDwa(pixt1, pixt1, w, h); 00685 if (dispsep > 0) { 00686 pixDisplay(pixt1, x, y); 00687 x += dispsep; 00688 } 00689 break; 00690 case 'c': 00691 case 'C': 00692 sscanf(&op[1], "%d.%d", &w, &h); 00693 pixCloseCompBrickDwa(pixt1, pixt1, w, h); 00694 if (dispsep > 0) { 00695 pixDisplay(pixt1, x, y); 00696 x += dispsep; 00697 } 00698 break; 00699 case 'r': 00700 case 'R': 00701 nred = strlen(op) - 1; 00702 for (j = 0; j < nred; j++) 00703 level[j] = op[j + 1] - '0'; 00704 for (j = nred; j < 4; j++) 00705 level[j] = 0; 00706 pixt2 = pixReduceRankBinaryCascade(pixt1, level[0], level[1], 00707 level[2], level[3]); 00708 pixDestroy(&pixt1); 00709 pixt1 = pixClone(pixt2); 00710 pixDestroy(&pixt2); 00711 if (dispsep > 0) { 00712 pixDisplay(pixt1, x, y); 00713 x += dispsep; 00714 } 00715 break; 00716 case 'x': 00717 case 'X': 00718 sscanf(&op[1], "%d", &fact); 00719 pixt2 = pixExpandReplicate(pixt1, fact); 00720 pixDestroy(&pixt1); 00721 pixt1 = pixClone(pixt2); 00722 pixDestroy(&pixt2); 00723 if (dispsep > 0) { 00724 pixDisplay(pixt1, x, y); 00725 x += dispsep; 00726 } 00727 break; 00728 case 'b': 00729 case 'B': 00730 sscanf(&op[1], "%d", &border); 00731 pixt2 = pixAddBorder(pixt1, border, 0); 00732 pixDestroy(&pixt1); 00733 pixt1 = pixClone(pixt2); 00734 pixDestroy(&pixt2); 00735 if (dispsep > 0) { 00736 pixDisplay(pixt1, x, y); 00737 x += dispsep; 00738 } 00739 break; 00740 default: 00741 /* All invalid ops are caught in the first pass */ 00742 break; 00743 } 00744 FREE(op); 00745 } 00746 if (border > 0) { 00747 pixt2 = pixRemoveBorder(pixt1, border); 00748 pixDestroy(&pixt1); 00749 pixt1 = pixClone(pixt2); 00750 pixDestroy(&pixt2); 00751 } 00752 00753 sarrayDestroy(&sa); 00754 return pixt1; 00755 } 00756 00757 00758 /*-------------------------------------------------------------------------* 00759 * Parser verifier for binary morphological operations * 00760 *-------------------------------------------------------------------------*/ 00761 /*! 00762 * morphSequenceVerify() 00763 * 00764 * Input: sarray (of operation sequence) 00765 * Return: TRUE if valid; FALSE otherwise or on error 00766 * 00767 * Notes: 00768 * (1) This does verification of valid binary morphological 00769 * operation sequences. 00770 * (2) See pixMorphSequence() for notes on valid operations 00771 * in the sequence. 00772 */ 00773 l_int32 00774 morphSequenceVerify(SARRAY *sa) 00775 { 00776 char *rawop, *op; 00777 l_int32 nops, i, j, nred, fact, valid, w, h, netred, border; 00778 l_int32 level[4]; 00779 l_int32 intlogbase2[5] = {1, 2, 3, 0, 4}; /* of arg/4 */ 00780 00781 PROCNAME("morphSequenceVerify"); 00782 00783 if (!sa) 00784 return ERROR_INT("sa not defined", procName, FALSE); 00785 00786 nops = sarrayGetCount(sa); 00787 valid = TRUE; 00788 netred = 0; 00789 border = 0; 00790 for (i = 0; i < nops; i++) { 00791 rawop = sarrayGetString(sa, i, 0); 00792 op = stringRemoveChars(rawop, " \n\t"); 00793 switch (op[0]) 00794 { 00795 case 'd': 00796 case 'D': 00797 case 'e': 00798 case 'E': 00799 case 'o': 00800 case 'O': 00801 case 'c': 00802 case 'C': 00803 if (sscanf(&op[1], "%d.%d", &w, &h) != 2) { 00804 fprintf(stderr, "*** op: %s invalid\n", op); 00805 valid = FALSE; 00806 break; 00807 } 00808 if (w <= 0 || h <= 0) { 00809 fprintf(stderr, 00810 "*** op: %s; w = %d, h = %d; must both be > 0\n", 00811 op, w, h); 00812 valid = FALSE; 00813 break; 00814 } 00815 /* fprintf(stderr, "op = %s; w = %d, h = %d\n", op, w, h); */ 00816 break; 00817 case 'r': 00818 case 'R': 00819 nred = strlen(op) - 1; 00820 netred += nred; 00821 if (nred < 1 || nred > 4) { 00822 fprintf(stderr, 00823 "*** op = %s; num reduct = %d; must be in {1,2,3,4}\n", 00824 op, nred); 00825 valid = FALSE; 00826 break; 00827 } 00828 for (j = 0; j < nred; j++) { 00829 level[j] = op[j + 1] - '0'; 00830 if (level[j] < 1 || level[j] > 4) { 00831 fprintf(stderr, "*** op = %s; level[%d] = %d is invalid\n", 00832 op, j, level[j]); 00833 valid = FALSE; 00834 break; 00835 } 00836 } 00837 if (!valid) 00838 break; 00839 /* fprintf(stderr, "op = %s", op); */ 00840 for (j = 0; j < nred; j++) { 00841 level[j] = op[j + 1] - '0'; 00842 /* fprintf(stderr, ", level[%d] = %d", j, level[j]); */ 00843 } 00844 /* fprintf(stderr, "\n"); */ 00845 break; 00846 case 'x': 00847 case 'X': 00848 if (sscanf(&op[1], "%d", &fact) != 1) { 00849 fprintf(stderr, "*** op: %s; fact invalid\n", op); 00850 valid = FALSE; 00851 break; 00852 } 00853 if (fact != 2 && fact != 4 && fact != 8 && fact != 16) { 00854 fprintf(stderr, "*** op = %s; invalid fact = %d\n", op, fact); 00855 valid = FALSE; 00856 break; 00857 } 00858 netred -= intlogbase2[fact / 4]; 00859 /* fprintf(stderr, "op = %s; fact = %d\n", op, fact); */ 00860 break; 00861 case 'b': 00862 case 'B': 00863 if (sscanf(&op[1], "%d", &fact) != 1) { 00864 fprintf(stderr, "*** op: %s; fact invalid\n", op); 00865 valid = FALSE; 00866 break; 00867 } 00868 if (i > 0) { 00869 fprintf(stderr, "*** op = %s; must be first op\n", op); 00870 valid = FALSE; 00871 break; 00872 } 00873 if (fact < 1) { 00874 fprintf(stderr, "*** op = %s; invalid fact = %d\n", op, fact); 00875 valid = FALSE; 00876 break; 00877 } 00878 border = fact; 00879 /* fprintf(stderr, "op = %s; fact = %d\n", op, fact); */ 00880 break; 00881 default: 00882 fprintf(stderr, "*** nonexistent op = %s\n", op); 00883 valid = FALSE; 00884 } 00885 FREE(op); 00886 } 00887 00888 if (border != 0 && netred != 0) { 00889 fprintf(stderr, 00890 "*** op = %s; border added but net reduction not 0\n", op); 00891 valid = FALSE; 00892 } 00893 return valid; 00894 } 00895 00896 00897 /*-----------------------------------------------------------------* 00898 * Run a sequence of grayscale morphological operations * 00899 *-----------------------------------------------------------------*/ 00900 /*! 00901 * pixGrayMorphSequence() 00902 * 00903 * Input: pixs 00904 * sequence (string specifying sequence) 00905 * dispsep (horizontal separation in pixels between 00906 * successive displays; use zero to suppress display) 00907 * dispy (if dispsep != 0, this gives the y-value of the 00908 * UL corner for display; otherwise it is ignored) 00909 * Return: pixd, or null on error 00910 * 00911 * Notes: 00912 * (1) This works on 8 bpp grayscale images. 00913 * (2) This runs a pipeline of operations; no branching is allowed. 00914 * (3) This only uses brick SELs. 00915 * (4) A new image is always produced; the input image is not changed. 00916 * (5) This contains an interpreter, allowing sequences to be 00917 * generated and run. 00918 * (6) The format of the sequence string is defined below. 00919 * (7) In addition to morphological operations, the composite 00920 * morph/subtract tophat can be performed. 00921 * (8) Sel sizes (width, height) must each be odd numbers. 00922 * (9) Intermediate results can optionally be displayed 00923 * (10) The sequence string is formatted as follows: 00924 * - An arbitrary number of operations, each separated 00925 * by a '+' character. White space is ignored. 00926 * - Each operation begins with a case-independent character 00927 * specifying the operation: 00928 * d or D (dilation) 00929 * e or E (erosion) 00930 * o or O (opening) 00931 * c or C (closing) 00932 * t or T (tophat) 00933 * - The args to the morphological operations are bricks of hits, 00934 * and are formatted as a.b, where a and b are horizontal and 00935 * vertical dimensions, rsp. (each must be an odd number) 00936 * - The args to the tophat are w or W (for white tophat) 00937 * or b or B (for black tophat), followed by a.b as for 00938 * the dilation, erosion, opening and closing. 00939 * Example valid sequences are: 00940 * "c5.3 + o7.5" 00941 * "c9.9 + tw9.9" 00942 */ 00943 PIX * 00944 pixGrayMorphSequence(PIX *pixs, 00945 const char *sequence, 00946 l_int32 dispsep, 00947 l_int32 dispy) 00948 { 00949 char *rawop, *op; 00950 l_int32 nops, i, valid, w, h, x; 00951 PIX *pixt1, *pixt2; 00952 SARRAY *sa; 00953 00954 PROCNAME("pixGrayMorphSequence"); 00955 00956 if (!pixs) 00957 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00958 if (!sequence) 00959 return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); 00960 00961 /* Split sequence into individual operations */ 00962 sa = sarrayCreate(0); 00963 sarraySplitString(sa, sequence, "+"); 00964 nops = sarrayGetCount(sa); 00965 00966 /* Verify that the operation sequence is valid */ 00967 valid = TRUE; 00968 for (i = 0; i < nops; i++) { 00969 rawop = sarrayGetString(sa, i, 0); 00970 op = stringRemoveChars(rawop, " \n\t"); 00971 switch (op[0]) 00972 { 00973 case 'd': 00974 case 'D': 00975 case 'e': 00976 case 'E': 00977 case 'o': 00978 case 'O': 00979 case 'c': 00980 case 'C': 00981 if (sscanf(&op[1], "%d.%d", &w, &h) != 2) { 00982 fprintf(stderr, "*** op: %s invalid\n", op); 00983 valid = FALSE; 00984 break; 00985 } 00986 if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) { 00987 fprintf(stderr, 00988 "*** op: %s; w = %d, h = %d; must both be odd\n", 00989 op, w, h); 00990 valid = FALSE; 00991 break; 00992 } 00993 /* fprintf(stderr, "op = %s; w = %d, h = %d\n", op, w, h); */ 00994 break; 00995 case 't': 00996 case 'T': 00997 if (op[1] != 'w' && op[1] != 'W' && 00998 op[1] != 'b' && op[1] != 'B') { 00999 fprintf(stderr, 01000 "*** op = %s; arg %c must be 'w' or 'b'\n", op, op[1]); 01001 valid = FALSE; 01002 break; 01003 } 01004 sscanf(&op[2], "%d.%d", &w, &h); 01005 if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) { 01006 fprintf(stderr, 01007 "*** op: %s; w = %d, h = %d; must both be odd\n", 01008 op, w, h); 01009 valid = FALSE; 01010 break; 01011 } 01012 /* fprintf(stderr, "op = %s", op); */ 01013 break; 01014 default: 01015 fprintf(stderr, "*** nonexistent op = %s\n", op); 01016 valid = FALSE; 01017 } 01018 FREE(op); 01019 } 01020 if (!valid) { 01021 sarrayDestroy(&sa); 01022 return (PIX *)ERROR_PTR("sequence invalid", procName, NULL); 01023 } 01024 01025 /* Parse and operate */ 01026 pixt1 = pixCopy(NULL, pixs); 01027 pixt2 = NULL; 01028 x = 0; 01029 for (i = 0; i < nops; i++) { 01030 rawop = sarrayGetString(sa, i, 0); 01031 op = stringRemoveChars(rawop, " \n\t"); 01032 switch (op[0]) 01033 { 01034 case 'd': 01035 case 'D': 01036 sscanf(&op[1], "%d.%d", &w, &h); 01037 pixt2 = pixDilateGray(pixt1, w, h); 01038 pixDestroy(&pixt1); 01039 pixt1 = pixClone(pixt2); 01040 pixDestroy(&pixt2); 01041 if (dispsep > 0) { 01042 pixDisplay(pixt1, x, dispy); 01043 x += dispsep; 01044 } 01045 break; 01046 case 'e': 01047 case 'E': 01048 sscanf(&op[1], "%d.%d", &w, &h); 01049 pixt2 = pixErodeGray(pixt1, w, h); 01050 pixDestroy(&pixt1); 01051 pixt1 = pixClone(pixt2); 01052 pixDestroy(&pixt2); 01053 if (dispsep > 0) { 01054 pixDisplay(pixt1, x, dispy); 01055 x += dispsep; 01056 } 01057 break; 01058 case 'o': 01059 case 'O': 01060 sscanf(&op[1], "%d.%d", &w, &h); 01061 pixt2 = pixOpenGray(pixt1, w, h); 01062 pixDestroy(&pixt1); 01063 pixt1 = pixClone(pixt2); 01064 pixDestroy(&pixt2); 01065 if (dispsep > 0) { 01066 pixDisplay(pixt1, x, dispy); 01067 x += dispsep; 01068 } 01069 break; 01070 case 'c': 01071 case 'C': 01072 sscanf(&op[1], "%d.%d", &w, &h); 01073 pixt2 = pixCloseGray(pixt1, w, h); 01074 pixDestroy(&pixt1); 01075 pixt1 = pixClone(pixt2); 01076 pixDestroy(&pixt2); 01077 if (dispsep > 0) { 01078 pixDisplay(pixt1, x, dispy); 01079 x += dispsep; 01080 } 01081 break; 01082 case 't': 01083 case 'T': 01084 sscanf(&op[2], "%d.%d", &w, &h); 01085 if (op[1] == 'w' || op[1] == 'W') 01086 pixt2 = pixTophat(pixt1, w, h, L_TOPHAT_WHITE); 01087 else /* 'b' or 'B' */ 01088 pixt2 = pixTophat(pixt1, w, h, L_TOPHAT_BLACK); 01089 pixDestroy(&pixt1); 01090 pixt1 = pixClone(pixt2); 01091 pixDestroy(&pixt2); 01092 if (dispsep > 0) { 01093 pixDisplay(pixt1, x, dispy); 01094 x += dispsep; 01095 } 01096 break; 01097 default: 01098 /* All invalid ops are caught in the first pass */ 01099 break; 01100 } 01101 FREE(op); 01102 } 01103 01104 sarrayDestroy(&sa); 01105 return pixt1; 01106 } 01107 01108 01109 /*-----------------------------------------------------------------* 01110 * Run a sequence of color morphological operations * 01111 *-----------------------------------------------------------------*/ 01112 /*! 01113 * pixColorMorphSequence() 01114 * 01115 * Input: pixs 01116 * sequence (string specifying sequence) 01117 * dispsep (horizontal separation in pixels between 01118 * successive displays; use zero to suppress display) 01119 * dispy (if dispsep != 0, this gives the y-value of the 01120 * UL corner for display; otherwise it is ignored) 01121 * Return: pixd, or null on error 01122 * 01123 * Notes: 01124 * (1) This works on 32 bpp rgb images. 01125 * (2) Each component is processed separately. 01126 * (3) This runs a pipeline of operations; no branching is allowed. 01127 * (4) This only uses brick SELs. 01128 * (5) A new image is always produced; the input image is not changed. 01129 * (6) This contains an interpreter, allowing sequences to be 01130 * generated and run. 01131 * (7) Sel sizes (width, height) must each be odd numbers. 01132 * (8) The format of the sequence string is defined below. 01133 * (9) Intermediate results can optionally be displayed. 01134 * (10) The sequence string is formatted as follows: 01135 * - An arbitrary number of operations, each separated 01136 * by a '+' character. White space is ignored. 01137 * - Each operation begins with a case-independent character 01138 * specifying the operation: 01139 * d or D (dilation) 01140 * e or E (erosion) 01141 * o or O (opening) 01142 * c or C (closing) 01143 * - The args to the morphological operations are bricks of hits, 01144 * and are formatted as a.b, where a and b are horizontal and 01145 * vertical dimensions, rsp. (each must be an odd number) 01146 * Example valid sequences are: 01147 * "c5.3 + o7.5" 01148 * "D9.1" 01149 */ 01150 PIX * 01151 pixColorMorphSequence(PIX *pixs, 01152 const char *sequence, 01153 l_int32 dispsep, 01154 l_int32 dispy) 01155 { 01156 char *rawop, *op; 01157 l_int32 nops, i, valid, w, h, x; 01158 PIX *pixt1, *pixt2; 01159 SARRAY *sa; 01160 01161 PROCNAME("pixColorMorphSequence"); 01162 01163 if (!pixs) 01164 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01165 if (!sequence) 01166 return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); 01167 01168 /* Split sequence into individual operations */ 01169 sa = sarrayCreate(0); 01170 sarraySplitString(sa, sequence, "+"); 01171 nops = sarrayGetCount(sa); 01172 01173 /* Verify that the operation sequence is valid */ 01174 valid = TRUE; 01175 for (i = 0; i < nops; i++) { 01176 rawop = sarrayGetString(sa, i, 0); 01177 op = stringRemoveChars(rawop, " \n\t"); 01178 switch (op[0]) 01179 { 01180 case 'd': 01181 case 'D': 01182 case 'e': 01183 case 'E': 01184 case 'o': 01185 case 'O': 01186 case 'c': 01187 case 'C': 01188 if (sscanf(&op[1], "%d.%d", &w, &h) != 2) { 01189 fprintf(stderr, "*** op: %s invalid\n", op); 01190 valid = FALSE; 01191 break; 01192 } 01193 if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) { 01194 fprintf(stderr, 01195 "*** op: %s; w = %d, h = %d; must both be odd\n", 01196 op, w, h); 01197 valid = FALSE; 01198 break; 01199 } 01200 /* fprintf(stderr, "op = %s; w = %d, h = %d\n", op, w, h); */ 01201 break; 01202 default: 01203 fprintf(stderr, "*** nonexistent op = %s\n", op); 01204 valid = FALSE; 01205 } 01206 FREE(op); 01207 } 01208 if (!valid) { 01209 sarrayDestroy(&sa); 01210 return (PIX *)ERROR_PTR("sequence invalid", procName, NULL); 01211 } 01212 01213 /* Parse and operate */ 01214 pixt1 = pixCopy(NULL, pixs); 01215 pixt2 = NULL; 01216 x = 0; 01217 for (i = 0; i < nops; i++) { 01218 rawop = sarrayGetString(sa, i, 0); 01219 op = stringRemoveChars(rawop, " \n\t"); 01220 switch (op[0]) 01221 { 01222 case 'd': 01223 case 'D': 01224 sscanf(&op[1], "%d.%d", &w, &h); 01225 pixt2 = pixColorMorph(pixt1, L_MORPH_DILATE, w, h); 01226 pixDestroy(&pixt1); 01227 pixt1 = pixClone(pixt2); 01228 pixDestroy(&pixt2); 01229 if (dispsep > 0) { 01230 pixDisplay(pixt1, x, dispy); 01231 x += dispsep; 01232 } 01233 break; 01234 case 'e': 01235 case 'E': 01236 sscanf(&op[1], "%d.%d", &w, &h); 01237 pixt2 = pixColorMorph(pixt1, L_MORPH_ERODE, w, h); 01238 pixDestroy(&pixt1); 01239 pixt1 = pixClone(pixt2); 01240 pixDestroy(&pixt2); 01241 if (dispsep > 0) { 01242 pixDisplay(pixt1, x, dispy); 01243 x += dispsep; 01244 } 01245 break; 01246 case 'o': 01247 case 'O': 01248 sscanf(&op[1], "%d.%d", &w, &h); 01249 pixt2 = pixColorMorph(pixt1, L_MORPH_OPEN, w, h); 01250 pixDestroy(&pixt1); 01251 pixt1 = pixClone(pixt2); 01252 pixDestroy(&pixt2); 01253 if (dispsep > 0) { 01254 pixDisplay(pixt1, x, dispy); 01255 x += dispsep; 01256 } 01257 break; 01258 case 'c': 01259 case 'C': 01260 sscanf(&op[1], "%d.%d", &w, &h); 01261 pixt2 = pixColorMorph(pixt1, L_MORPH_CLOSE, w, h); 01262 pixDestroy(&pixt1); 01263 pixt1 = pixClone(pixt2); 01264 pixDestroy(&pixt2); 01265 if (dispsep > 0) { 01266 pixDisplay(pixt1, x, dispy); 01267 x += dispsep; 01268 } 01269 break; 01270 default: 01271 /* All invalid ops are caught in the first pass */ 01272 break; 01273 } 01274 FREE(op); 01275 } 01276 01277 sarrayDestroy(&sa); 01278 return pixt1; 01279 } 01280 01281