Leptonica 1.68
C Image Processing Library

morphseq.c

Go to the documentation of this file.
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 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines