src/morphseq.c
PIX *
pixMorphSequence(PIX         *pixs,
                 const char  *sequence,
                 l_int32      dispsep)
{
char    *rawop, *op;
l_int32  nops, i, j, nred, fact, w, h, x, y, border;
l_int32  level[4];
PIX     *pixt1, *pixt2;
SARRAY  *sa;

    PROCNAME("pixMorphSequence");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (!sequence)
        return (PIX *)ERROR_PTR("sequence not defined", procName, NULL);

        /* Split sequence into individual operations */
    sa = sarrayCreate(0);
    sarraySplitString(sa, sequence, "+");
    nops = sarrayGetCount(sa);

    if (!morphSequenceVerify(sa)) {
        sarrayDestroy(&sa);
        return (PIX *)ERROR_PTR("sequence not valid", procName, NULL);
    }

        /* Parse and operate */
    border = 0;
    pixt1 = pixCopy(NULL, pixs);
    pixt2 = NULL;
    x = y = 0;
    for (i = 0; i < nops; i++) {
        rawop = sarrayGetString(sa, i, 0);
        op = stringRemoveChars(rawop, " \n\t");
        switch (op[0])
        {
        case 'd':
        case 'D':
            sscanf(&op[1], "%d.%d", &w, &h);
            pixt2 = pixDilateBrick(NULL, pixt1, w, h);
            pixDestroy(&pixt1);
            pixt1 = pixClone(pixt2);
            pixDestroy(&pixt2);
            if (dispsep > 0) {
                pixDisplay(pixt1, x, y);
                x += dispsep;
            }
            break;
        case 'e':
        case 'E':
            sscanf(&op[1], "%d.%d", &w, &h);
            pixt2 = pixErodeBrick(NULL, pixt1, w, h);
            pixDestroy(&pixt1);
            pixt1 = pixClone(pixt2);
            pixDestroy(&pixt2);
            if (dispsep > 0) {
                pixDisplay(pixt1, x, y);
                x += dispsep;
            }
            break;
        case 'o':
        case 'O':
            sscanf(&op[1], "%d.%d", &w, &h);
            pixOpenBrick(pixt1, pixt1, w, h);
            if (dispsep > 0) {
                pixDisplay(pixt1, x, y);
                x += dispsep;
            }
            break;
        case 'c':
        case 'C':
            sscanf(&op[1], "%d.%d", &w, &h);
            pixCloseSafeBrick(pixt1, pixt1, w, h);
            if (dispsep > 0) {
                pixDisplay(pixt1, x, y);
                x += dispsep;
            }
            break;
        case 'r':
        case 'R':
            nred = strlen(op) - 1;
            for (j = 0; j < nred; j++)
                level[j] = op[j + 1] - '0';
            for (j = nred; j < 4; j++)
                level[j] = 0;
            pixt2 = pixReduceRankBinaryCascade(pixt1, level[0], level[1],
                                               level[2], level[3]);
            pixDestroy(&pixt1);
            pixt1 = pixClone(pixt2);
            pixDestroy(&pixt2);
            if (dispsep > 0) {
                pixDisplay(pixt1, x, y);
                x += dispsep;
            }
            break;
        case 'x':
        case 'X':
            sscanf(&op[1], "%d", &fact);
            pixt2 = pixExpandReplicate(pixt1, fact);
            pixDestroy(&pixt1);
            pixt1 = pixClone(pixt2);
            pixDestroy(&pixt2);
            if (dispsep > 0) {
                pixDisplay(pixt1, x, y);
                x += dispsep;
            }
            break;
        case 'b':
        case 'B':
            sscanf(&op[1], "%d", &border);
            pixt2 = pixAddBorder(pixt1, border, 0);
            pixDestroy(&pixt1);
            pixt1 = pixClone(pixt2);
            pixDestroy(&pixt2);
            if (dispsep > 0) {
                pixDisplay(pixt1, x, y);
                x += dispsep;
            }
            break;
        default:
            /* All invalid ops are caught in the first pass */
            break;
        }
        FREE(op);
    }
    if (border > 0) {
        pixt2 = pixRemoveBorder(pixt1, border);
        pixDestroy(&pixt1);
        pixt1 = pixClone(pixt2);
        pixDestroy(&pixt2);
    }

    sarrayDestroy(&sa);
    return pixt1;
}
/*!
 *  pixMorphSequence()
 *
 *      Input:  pixs
 *              sequence (string specifying sequence)
 *              dispsep (horizontal separation in pixels between
 *                       successive displays; use zero to suppress display)
 *      Return: pixd, or null on error
 *
 *  Notes:
 *      (1) This does rasterop morphology on binary images.
 *      (2) This runs a pipeline of operations; no branching is allowed.
 *      (3) This only uses brick Sels, which are created on the fly.
 *          In the future this will be generalized to extract Sels from
 *          a Sela by name.
 *      (4) A new image is always produced; the input image is not changed.
 *      (5) This contains an interpreter, allowing sequences to be
 *          generated and run.
 *      (6) The format of the sequence string is defined below.
 *      (7) In addition to morphological operations, rank order reduction
 *          and replicated expansion allow operations to take place
 *          downscaled by a power of 2.
 *      (8) Intermediate results can optionally be displayed.
 *      (9) Thanks to Dar-Shyang Lee, who had the idea for this and
 *          built the first implementation.
 *      (10) The sequence string is formatted as follows:
 *            - An arbitrary number of operations,  each separated
 *              by a '+' character.  White space is ignored.
 *            - Each operation begins with a case-independent character
 *              specifying the operation:
 *                 d or D  (dilation)
 *                 e or E  (erosion)
 *                 o or O  (opening)
 *                 c or C  (closing)
 *                 r or R  (rank binary reduction)
 *                 x or X  (replicative binary expansion)
 *                 b or B  (add a border of 0 pixels of this size)
 *            - The args to the morphological operations are bricks of hits,
 *              and are formatted as a.b, where a and b are horizontal and
 *              vertical dimensions, rsp.
 *            - The args to the reduction are a sequence of up to 4 integers,
 *              each from 1 to 4.
 *            - The arg to the expansion is a power of two, in the set
 *              {2, 4, 8, 16}.
 *      (11) An example valid sequence is:
 *               "b32 + o1.3 + C3.1 + r23 + e2.2 + D3.2 + X4"
 *           In this example, the following operation sequence is carried out:
 *             * b32: Add a 32 pixel border around the input image
 *             * o1.3: Opening with vert sel of length 3 (e.g., 1 x 3)
 *             * C3.1: Closing with horiz sel of length 3  (e.g., 3 x 1)
 *             * r23: Two successive 2x2 reductions with rank 2 in the first
 *                    and rank 3 in the second.  The result is a 4x reduced pix.
 *             * e2.2: Erosion with a 2x2 sel (origin will be at x,y: 0,0)
 *             * d3.2: Dilation with a 3x2 sel (origin will be at x,y: 1,0)
 *             * X4: 4x replicative expansion, back to original resolution
 *      (12) The safe closing is used.  However, if you implement a
 *           closing as separable dilations followed by separable erosions,
 *           it will not be safe.  For that situation, you need to add
 *           a sufficiently large border as the first operation in
 *           the sequence.  This will be removed automatically at the
 *           end.  There are two cautions: 
 *              - When computing what is sufficient, remember that if
 *                reductions are carried out, the border is also reduced.
 *              - The border is removed at the end, so if a border is
 *                added at the beginning, the result must be at the
 *                same resolution as the input!
 */
pixMorphSequence()
Input:  pixs
sequence (string specifying sequence)
dispsep (horizontal separation in pixels between
successive displays; use zero to suppress display)
Return: pixd, or null on error

(1) This does rasterop morphology on binary images.
(2) This runs a pipeline of operations; no branching is allowed.
(3) This only uses brick Sels, which are created on the fly.
In the future this will be generalized to extract Sels from
a Sela by name.
(4) A new image is always produced; the input image is not changed.
(5) This contains an interpreter, allowing sequences to be
generated and run.
(6) The format of the sequence string is defined below.
(7) In addition to morphological operations, rank order reduction
and replicated expansion allow operations to take place
downscaled by a power of 2.
(8) Intermediate results can optionally be displayed.
(9) Thanks to Dar-Shyang Lee, who had the idea for this and
built the first implementation.
(10) The sequence string is formatted as follows:
- An arbitrary number of operations,  each separated
by a '+' character.  White space is ignored.
pixMorphSequence () Input : pixs sequence ( string specifying sequence ) dispsep ( horizontal separation in pixels between successive displays ; use zero to suppress display ) Return : pixd or null on error ( 1 ) This does rasterop morphology on binary images . ( 2 ) This runs a pipeline of operations ; no branching is allowed . ( 3 ) This only uses brick Sels which are created on the fly . In the future this will be generalized to extract Sels from a Sela by name . ( 4 ) A new image is always produced ; the input image is not changed . ( 5 ) This contains an interpreter allowing sequences to be generated and run . ( 6 ) The format of the sequence string is defined below . ( 7 ) In addition to morphological operations rank order reduction and replicated expansion allow operations to take place downscaled by a power of 2 . ( 8 ) Intermediate results can optionally be displayed . ( 9 ) Thanks to Dar - Shyang Lee who had the idea for this and built the first implementation . ( 10 ) The sequence string is formatted as follows : - An arbitrary number of operations each separated by a ' + ' character . White space is ignored .
PIX *
pixMorphSequence(PIX         *pixs,
                 const char  *sequence,
                 l_int32      dispsep)
{
char    *rawop, *op;
l_int32  nops, i, j, nred, fact, w, h, x, y, border;
l_int32  level[4];
PIX     *pixt1, *pixt2;
SARRAY  *sa;
    PROCNAME("pixMorphSequence");
    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (!sequence)
        return (PIX *)ERROR_PTR("sequence not defined", procName, NULL);
    sa = sarrayCreate(0);
    sarraySplitString(sa, sequence, "+");
    nops = sarrayGetCount(sa);
    if (!morphSequenceVerify(sa)) {
        sarrayDestroy(&sa);
        return (PIX *)ERROR_PTR("sequence not valid", procName, NULL);
    }
    border = 0;
    pixt1 = pixCopy(NULL, pixs);
    pixt2 = NULL;
    x = y = 0;
    for (i = 0; i < nops; i++) {
        rawop = sarrayGetString(sa, i, 0);
        op = stringRemoveChars(rawop, " \n\t");
        switch (op[0])
        {
        case 'd':
        case 'D':
            sscanf(&op[1], "%d.%d", &w, &h);
            pixt2 = pixDilateBrick(NULL, pixt1, w, h);
            pixDestroy(&pixt1);
            pixt1 = pixClone(pixt2);
            pixDestroy(&pixt2);
            if (dispsep > 0) {
                pixDisplay(pixt1, x, y);
                x += dispsep;
            }
            break;
        case 'e':
        case 'E':
            sscanf(&op[1], "%d.%d", &w, &h);
            pixt2 = pixErodeBrick(NULL, pixt1, w, h);
            pixDestroy(&pixt1);
            pixt1 = pixClone(pixt2);
            pixDestroy(&pixt2);
            if (dispsep > 0) {
                pixDisplay(pixt1, x, y);
                x += dispsep;
            }
            break;
        case 'o':
        case 'O':
            sscanf(&op[1], "%d.%d", &w, &h);
            pixOpenBrick(pixt1, pixt1, w, h);
            if (dispsep > 0) {
                pixDisplay(pixt1, x, y);
                x += dispsep;
            }
            break;
        case 'c':
        case 'C':
            sscanf(&op[1], "%d.%d", &w, &h);
            pixCloseSafeBrick(pixt1, pixt1, w, h);
            if (dispsep > 0) {
                pixDisplay(pixt1, x, y);
                x += dispsep;
            }
            break;
        case 'r':
        case 'R':
            nred = strlen(op) - 1;
            for (j = 0; j < nred; j++)
                level[j] = op[j + 1] - '0';
            for (j = nred; j < 4; j++)
                level[j] = 0;
            pixt2 = pixReduceRankBinaryCascade(pixt1, level[0], level[1],
                                               level[2], level[3]);
            pixDestroy(&pixt1);
            pixt1 = pixClone(pixt2);
            pixDestroy(&pixt2);
            if (dispsep > 0) {
                pixDisplay(pixt1, x, y);
                x += dispsep;
            }
            break;
        case 'x':
        case 'X':
            sscanf(&op[1], "%d", &fact);
            pixt2 = pixExpandReplicate(pixt1, fact);
            pixDestroy(&pixt1);
            pixt1 = pixClone(pixt2);
            pixDestroy(&pixt2);
            if (dispsep > 0) {
                pixDisplay(pixt1, x, y);
                x += dispsep;
            }
            break;
        case 'b':
        case 'B':
            sscanf(&op[1], "%d", &border);
            pixt2 = pixAddBorder(pixt1, border, 0);
            pixDestroy(&pixt1);
            pixt1 = pixClone(pixt2);
            pixDestroy(&pixt2);
            if (dispsep > 0) {
                pixDisplay(pixt1, x, y);
                x += dispsep;
            }
            break;
        default:
            break;
        }
        FREE(op);
    }
    if (border > 0) {
        pixt2 = pixRemoveBorder(pixt1, border);
        pixDestroy(&pixt1);
        pixt1 = pixClone(pixt2);
        pixDestroy(&pixt2);
    }
    sarrayDestroy(&sa);
    return pixt1;
}
PIX * pixMorphSequence ( PIX * pixs , const char * sequence , l_int32 dispsep ) { char * rawop , * op ; l_int32 nops , i , j , nred , fact , w , h , x , y , border ; l_int32 level [ 4 ] ; PIX * pixt1 , * pixt2 ; SARRAY * sa ; PROCNAME ( " " ) ; if ( ! pixs ) return ( PIX * ) ERROR_PTR ( " " , procName , NULL ) ; if ( ! sequence ) return ( PIX * ) ERROR_PTR ( " " , procName , NULL ) ; sa = sarrayCreate ( 0 ) ; sarraySplitString ( sa , sequence , " " ) ; nops = sarrayGetCount ( sa ) ; if ( ! morphSequenceVerify ( sa ) ) { sarrayDestroy ( & sa ) ; return ( PIX * ) ERROR_PTR ( " " , procName , NULL ) ; } border = 0 ; pixt1 = pixCopy ( NULL , pixs ) ; pixt2 = NULL ; x = y = 0 ; for ( i = 0 ; i < nops ; i ++ ) { rawop = sarrayGetString ( sa , i , 0 ) ; op = stringRemoveChars ( rawop , " \n \t " ) ; switch ( op [ 0 ] ) { case ' ' : case ' ' : sscanf ( & op [ 1 ] , " " , & w , & h ) ; pixt2 = pixDilateBrick ( NULL , pixt1 , w , h ) ; pixDestroy ( & pixt1 ) ; pixt1 = pixClone ( pixt2 ) ; pixDestroy ( & pixt2 ) ; if ( dispsep > 0 ) { pixDisplay ( pixt1 , x , y ) ; x += dispsep ; } break ; case ' ' : case ' ' : sscanf ( & op [ 1 ] , " " , & w , & h ) ; pixt2 = pixErodeBrick ( NULL , pixt1 , w , h ) ; pixDestroy ( & pixt1 ) ; pixt1 = pixClone ( pixt2 ) ; pixDestroy ( & pixt2 ) ; if ( dispsep > 0 ) { pixDisplay ( pixt1 , x , y ) ; x += dispsep ; } break ; case ' ' : case ' ' : sscanf ( & op [ 1 ] , " " , & w , & h ) ; pixOpenBrick ( pixt1 , pixt1 , w , h ) ; if ( dispsep > 0 ) { pixDisplay ( pixt1 , x , y ) ; x += dispsep ; } break ; case ' ' : case ' ' : sscanf ( & op [ 1 ] , " " , & w , & h ) ; pixCloseSafeBrick ( pixt1 , pixt1 , w , h ) ; if ( dispsep > 0 ) { pixDisplay ( pixt1 , x , y ) ; x += dispsep ; } break ; case ' ' : case ' ' : nred = strlen ( op ) - 1 ; for ( j = 0 ; j < nred ; j ++ ) level [ j ] = op [ j + 1 ] - ' ' ; for ( j = nred ; j < 4 ; j ++ ) level [ j ] = 0 ; pixt2 = pixReduceRankBinaryCascade ( pixt1 , level [ 0 ] , level [ 1 ] , level [ 2 ] , level [ 3 ] ) ; pixDestroy ( & pixt1 ) ; pixt1 = pixClone ( pixt2 ) ; pixDestroy ( & pixt2 ) ; if ( dispsep > 0 ) { pixDisplay ( pixt1 , x , y ) ; x += dispsep ; } break ; case ' ' : case ' ' : sscanf ( & op [ 1 ] , " " , & fact ) ; pixt2 = pixExpandReplicate ( pixt1 , fact ) ; pixDestroy ( & pixt1 ) ; pixt1 = pixClone ( pixt2 ) ; pixDestroy ( & pixt2 ) ; if ( dispsep > 0 ) { pixDisplay ( pixt1 , x , y ) ; x += dispsep ; } break ; case ' ' : case ' ' : sscanf ( & op [ 1 ] , " " , & border ) ; pixt2 = pixAddBorder ( pixt1 , border , 0 ) ; pixDestroy ( & pixt1 ) ; pixt1 = pixClone ( pixt2 ) ; pixDestroy ( & pixt2 ) ; if ( dispsep > 0 ) { pixDisplay ( pixt1 , x , y ) ; x += dispsep ; } break ; default : break ; } FREE ( op ) ; } if ( border > 0 ) { pixt2 = pixRemoveBorder ( pixt1 , border ) ; pixDestroy ( & pixt1 ) ; pixt1 = pixClone ( pixt2 ) ; pixDestroy ( & pixt2 ) ; } sarrayDestroy ( & sa ) ; return pixt1 ; }
pixMorphSequence()
      Input:  pixs
              sequence (string specifying sequence)
              dispsep (horizontal separation in pixels between
                       successive displays; use zero to suppress display)
      Return: pixd, or null on error
pixMorphSequence () Input : pixs sequence ( string specifying sequence ) dispsep ( horizontal separation in pixels between successive displays ; use zero to suppress display ) Return : pixd or null on error
/* Split sequence into individual operations */ /* Parse and operate */ /* All invalid ops are caught in the first pass */