Exemple #1
0
/*!
 *  selaAddDwaLinear()
 *
 *      Input:  sela (<optional>)
 *      Return: sela with additional sels, or null on error
 *
 *  Notes:
 *      (1) Adds all linear (horizontal, vertical) sels from
 *          2 to 63 pixels in length, which are the sizes over
 *          which dwa code can be generated.
 */
SELA *
selaAddDwaLinear(SELA  *sela)
{
char     name[L_BUF_SIZE];
l_int32  i;
SEL     *sel;

    PROCNAME("selaAddDwaLinear");

    if (!sela) {
        if ((sela = selaCreate(0)) == NULL)
            return (SELA *)ERROR_PTR("sela not made", procName, NULL);
    }

    for (i = 2; i < 64; i++) {
        sel = selCreateBrick(1, i, 0, i / 2, 1);
        snprintf(name, L_BUF_SIZE, "sel_%dh", i);
        selaAddSel(sela, sel, name, 0);
    }
    for (i = 2; i < 64; i++) {
        sel = selCreateBrick(i, 1, i / 2, 0, 1);
        snprintf(name, L_BUF_SIZE, "sel_%dv", i);
        selaAddSel(sela, sel, name, 0);
    }
    return sela;
}
Exemple #2
0
main(int    argc,
     char **argv)
{
l_int32      i, j, w, h, same, width, height, cx, cy;
l_uint32     val;
PIX         *pixs, *pixse, *pixd1, *pixd2;
SEL         *sel;
static char  mainName[] = "rasterop_reg";

    if (argc != 1)
	return ERROR_INT(" Syntax:  rasterop_reg", mainName, 1);
    pixs = pixRead("feyn.tif");

    for (width = 1; width <= 25; width += 3) {
	for (height = 1; height <= 25; height += 4) {

	    cx = width / 2;
	    cy = height / 2;

		/* Dilate using an actual sel */
	    sel = selCreateBrick(height, width, cy, cx, SEL_HIT);
	    pixd1 = pixDilate(NULL, pixs, sel);

		/* Dilate using a pix as a sel */
	    pixse = pixCreate(width, height, 1);
	    pixSetAll(pixse);
	    pixd2 = pixCopy(NULL, pixs);
	    w = pixGetWidth(pixs);
	    h = pixGetHeight(pixs);
	    for (i = 0; i < h; i++) {
		for (j = 0; j < w; j++) {
		    pixGetPixel(pixs, j, i, &val);
		    if (val)
			pixRasterop(pixd2, j - cx, i - cy, width, height,
				    PIX_SRC | PIX_DST, pixse, 0, 0);
		}
	    }

	    pixEqual(pixd1, pixd2, &same);
	    if (same == 1)
		fprintf(stderr, "Correct for (%d,%d)\n", width, height);
	    else {
		fprintf(stderr, "Error: results are different!\n");
		fprintf(stderr, "SE: width = %d, height = %d\n", width, height);
		pixWrite("/tmp/junkout1", pixd1, IFF_PNG);
		pixWrite("/tmp/junkout2", pixd2, IFF_PNG);
                return 1;
	    }

	    pixDestroy(&pixse);
	    pixDestroy(&pixd1);
	    pixDestroy(&pixd2);
	    selDestroy(&sel);
	}
    }
    pixDestroy(&pixs);
    return 0;
}
Exemple #3
0
/*!
 *  selaAddBasic()
 *
 *      Input:  sela (<optional>)
 *      Return: sela with additional sels, or null on error
 *
 *  Notes:
 *      (1) Adds the following sels:
 *            - all linear (horiz, vert) brick sels that are
 *              necessary for decomposable sels up to size 63
 *            - square brick sels up to size 10
 *            - 4 diagonal sels
 */
SELA *
selaAddBasic(SELA  *sela)
{
char     name[L_BUF_SIZE];
l_int32  i, size;
SEL     *sel;

    PROCNAME("selaAddBasic");

    if (!sela) {
        if ((sela = selaCreate(0)) == NULL)
            return (SELA *)ERROR_PTR("sela not made", procName, NULL);
    }

    /*--------------------------------------------------------------*
     *             Linear horizontal and vertical sels              *
     *--------------------------------------------------------------*/
    for (i = 0; i < num_linear; i++) {
        size = basic_linear[i];
        sel = selCreateBrick(1, size, 0, size / 2, 1);
        snprintf(name, L_BUF_SIZE, "sel_%dh", size);
        selaAddSel(sela, sel, name, 0);
    }
    for (i = 0; i < num_linear; i++) {
        size = basic_linear[i];
        sel = selCreateBrick(size, 1, size / 2, 0, 1);
        snprintf(name, L_BUF_SIZE, "sel_%dv", size);
        selaAddSel(sela, sel, name, 0);
    }

    /*-----------------------------------------------------------*
     *                      2-d Bricks                           *
     *-----------------------------------------------------------*/
    for (i = 2; i <= 5; i++) {
        sel = selCreateBrick(i, i, i / 2, i / 2, 1);
        snprintf(name, L_BUF_SIZE, "sel_%d", i);
        selaAddSel(sela, sel, name, 0);
    }

    /*-----------------------------------------------------------*
     *                        Diagonals                          *
     *-----------------------------------------------------------*/
        /*  0c  1
            1   0  */
    sel = selCreateBrick(2, 2, 0, 0, 1);
    selSetElement(sel, 0, 0, 0);
    selSetElement(sel, 1, 1, 0);
    selaAddSel(sela, sel, "sel_2dp", 0);

        /*  1c  0
            0   1   */
    sel = selCreateBrick(2, 2, 0, 0, 1);
    selSetElement(sel, 0, 1, 0);
    selSetElement(sel, 1, 0, 0);
    selaAddSel(sela, sel, "sel_2dm", 0);

        /*  Diagonal, slope +, size 5 */
    sel = selCreate(5, 5, "sel_5dp");
    sel->cy = 2;
    sel->cx = 2;
    selSetElement(sel, 0, 4, 1);
    selSetElement(sel, 1, 3, 1);
    selSetElement(sel, 2, 2, 1);
    selSetElement(sel, 3, 1, 1);
    selSetElement(sel, 4, 0, 1);
    selaAddSel(sela, sel, "sel_5dp", 0);

        /*  Diagonal, slope -, size 5 */
    sel = selCreate(5, 5, "sel_5dm");
    sel->cy = 2;
    sel->cx = 2;
    selSetElement(sel, 0, 0, 1);
    selSetElement(sel, 1, 1, 1);
    selSetElement(sel, 2, 2, 1);
    selSetElement(sel, 3, 3, 1);
    selSetElement(sel, 4, 4, 1);
    selaAddSel(sela, sel, "sel_5dm", 0);

    return sela;
}
Exemple #4
0
/*!
 *  selaAddHitMiss()
 *
 *      Input:  sela  (<optional>)
 *      Return: sela with additional sels, or null on error
 */
SELA *
selaAddHitMiss(SELA  *sela)
{
SEL  *sel;

    PROCNAME("selaAddHitMiss");

    if (!sela) {
        if ((sela = selaCreate(0)) == NULL)
            return (SELA *)ERROR_PTR("sela not made", procName, NULL);
    }

#if 0   /*  use just for testing */
    sel = selCreateBrick(3, 3, 1, 1, 2);
    selaAddSel(sela, sel, "sel_bad", 0);
#endif


    /*--------------------------------------------------------------*
     *                   Isolated foreground pixel                  *
     *--------------------------------------------------------------*/
    sel = selCreateBrick(3, 3, 1, 1, SEL_MISS);
    selSetElement(sel, 1, 1, SEL_HIT);
    selaAddSel(sela, sel, "sel_3hm", 0);

    /*--------------------------------------------------------------*
     *                Horizontal and vertical edges                 *
     *--------------------------------------------------------------*/
    sel = selCreateBrick(2, 3, 0, 1, SEL_HIT);
    selSetElement(sel, 1, 0, SEL_MISS);
    selSetElement(sel, 1, 1, SEL_MISS);
    selSetElement(sel, 1, 2, SEL_MISS);
    selaAddSel(sela, sel, "sel_3de", 0);

    sel = selCreateBrick(2, 3, 1, 1, SEL_HIT);
    selSetElement(sel, 0, 0, SEL_MISS);
    selSetElement(sel, 0, 1, SEL_MISS);
    selSetElement(sel, 0, 2, SEL_MISS);
    selaAddSel(sela, sel, "sel_3ue", 0);

    sel = selCreateBrick(3, 2, 1, 0, SEL_HIT);
    selSetElement(sel, 0, 1, SEL_MISS);
    selSetElement(sel, 1, 1, SEL_MISS);
    selSetElement(sel, 2, 1, SEL_MISS);
    selaAddSel(sela, sel, "sel_3re", 0);

    sel = selCreateBrick(3, 2, 1, 1, SEL_HIT);
    selSetElement(sel, 0, 0, SEL_MISS);
    selSetElement(sel, 1, 0, SEL_MISS);
    selSetElement(sel, 2, 0, SEL_MISS);
    selaAddSel(sela, sel, "sel_3le", 0);

    /*--------------------------------------------------------------*
     *                        Slanted edge                          *
     *--------------------------------------------------------------*/
    sel = selCreateBrick(13, 6, 6, 2, SEL_DONT_CARE);
    selSetElement(sel, 0, 3, SEL_MISS);
    selSetElement(sel, 0, 5, SEL_HIT);
    selSetElement(sel, 4, 2, SEL_MISS);
    selSetElement(sel, 4, 4, SEL_HIT);
    selSetElement(sel, 8, 1, SEL_MISS);
    selSetElement(sel, 8, 3, SEL_HIT);
    selSetElement(sel, 12, 0, SEL_MISS);
    selSetElement(sel, 12, 2, SEL_HIT);
    selaAddSel(sela, sel, "sel_sl1", 0);

    /*--------------------------------------------------------------*
     *                           Corners                            *
     *  This allows for up to 3 missing edge pixels at the corner   *
     *--------------------------------------------------------------*/
    sel = selCreateBrick(4, 4, 1, 1, SEL_MISS);
    selSetElement(sel, 1, 1, SEL_DONT_CARE);
    selSetElement(sel, 1, 2, SEL_DONT_CARE);
    selSetElement(sel, 2, 1, SEL_DONT_CARE);
    selSetElement(sel, 1, 3, SEL_HIT);
    selSetElement(sel, 2, 2, SEL_HIT);
    selSetElement(sel, 2, 3, SEL_HIT);
    selSetElement(sel, 3, 1, SEL_HIT);
    selSetElement(sel, 3, 2, SEL_HIT);
    selSetElement(sel, 3, 3, SEL_HIT);
    selaAddSel(sela, sel, "sel_ulc", 0);

    sel = selCreateBrick(4, 4, 1, 2, SEL_MISS);
    selSetElement(sel, 1, 1, SEL_DONT_CARE);
    selSetElement(sel, 1, 2, SEL_DONT_CARE);
    selSetElement(sel, 2, 2, SEL_DONT_CARE);
    selSetElement(sel, 1, 0, SEL_HIT);
    selSetElement(sel, 2, 0, SEL_HIT);
    selSetElement(sel, 2, 1, SEL_HIT);
    selSetElement(sel, 3, 0, SEL_HIT);
    selSetElement(sel, 3, 1, SEL_HIT);
    selSetElement(sel, 3, 2, SEL_HIT);
    selaAddSel(sela, sel, "sel_urc", 0);

    sel = selCreateBrick(4, 4, 2, 1, SEL_MISS);
    selSetElement(sel, 1, 1, SEL_DONT_CARE);
    selSetElement(sel, 2, 1, SEL_DONT_CARE);
    selSetElement(sel, 2, 2, SEL_DONT_CARE);
    selSetElement(sel, 0, 1, SEL_HIT);
    selSetElement(sel, 0, 2, SEL_HIT);
    selSetElement(sel, 0, 3, SEL_HIT);
    selSetElement(sel, 1, 2, SEL_HIT);
    selSetElement(sel, 1, 3, SEL_HIT);
    selSetElement(sel, 2, 3, SEL_HIT);
    selaAddSel(sela, sel, "sel_llc", 0);

    sel = selCreateBrick(4, 4, 2, 2, SEL_MISS);
    selSetElement(sel, 1, 2, SEL_DONT_CARE);
    selSetElement(sel, 2, 1, SEL_DONT_CARE);
    selSetElement(sel, 2, 2, SEL_DONT_CARE);
    selSetElement(sel, 0, 0, SEL_HIT);
    selSetElement(sel, 0, 1, SEL_HIT);
    selSetElement(sel, 0, 2, SEL_HIT);
    selSetElement(sel, 1, 0, SEL_HIT);
    selSetElement(sel, 1, 1, SEL_HIT);
    selSetElement(sel, 2, 0, SEL_HIT);
    selaAddSel(sela, sel, "sel_lrc", 0);

    return sela;
}
Exemple #5
0
/*!
 *  pixGenerateSelBoundary()
 *
 *      Input:  pix (1 bpp, typically small, to be used as a pattern)
 *              hitdist (min distance from fg boundary pixel)
 *              missdist (min distance from bg boundary pixel)
 *              hitskip (number of boundary pixels skipped between hits)
 *              missskip (number of boundary pixels skipped between misses)
 *              topflag (flag for extra pixels of bg added above)
 *              botflag (flag for extra pixels of bg added below)
 *              leftflag (flag for extra pixels of bg added to left)
 *              rightflag (flag for extra pixels of bg added to right)
 *              &pixe (<optional return> input pix expanded by extra pixels)
 *      Return: sel (hit-miss for input pattern), or null on error
 *
 *  Notes:
 *    (1) All fg elements selected are exactly hitdist pixels away from
 *        the nearest fg boundary pixel, and ditto for bg elements.
 *        Valid inputs of hitdist and missdist are 0, 1, 2, 3 and 4.
 *        For example, a hitdist of 0 puts the hits at the fg boundary.
 *        Usually, the distances should be > 0 avoid the effect of
 *        noise at the boundary.
 *    (2) Set hitskip < 0 if no hits are to be used.  Ditto for missskip.
 *        If both hitskip and missskip are < 0, the sel would be empty,
 *        and NULL is returned.
 *    (3) The 4 flags determine whether the sel is increased on that side
 *        to allow bg misses to be placed all along that boundary.
 *        The increase in sel size on that side is the minimum necessary
 *        to allow the misses to be placed at mindist.  For text characters,
 *        the topflag and botflag are typically set to 1, and the leftflag
 *        and rightflag to 0.
 *    (4) The input pix, as extended by the extra pixels on selected sides,
 *        can optionally be returned.  For debugging, call
 *        pixDisplayHitMissSel() to visualize the hit-miss sel superimposed
 *        on the generating bitmap.
 *    (5) This is probably the best of the three sel generators, in the
 *        sense that you have the most flexibility with the smallest number
 *        of hits and misses.
 */
SEL *
pixGenerateSelBoundary(PIX     *pixs,
                       l_int32  hitdist,
                       l_int32  missdist,
                       l_int32  hitskip,
                       l_int32  missskip,
                       l_int32  topflag,
                       l_int32  botflag,
                       l_int32  leftflag,
                       l_int32  rightflag,
                       PIX      **ppixe)
{
l_int32  ws, hs, w, h, x, y, ix, iy, i, npt;
PIX     *pixt1, *pixt2, *pixt3, *pixfg, *pixbg;
SEL     *selh, *selm, *sel_3, *sel;
PTA     *ptah, *ptam;

    PROCNAME("pixGenerateSelBoundary");

    if (ppixe) *ppixe = NULL;
    if (!pixs)
        return (SEL *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 1)
        return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
    if (hitdist < 0 || hitdist > 4 || missdist < 0 || missdist > 4)
        return (SEL *)ERROR_PTR("dist not in {0 .. 4}", procName, NULL);
    if (hitskip < 0 && missskip < 0)
        return (SEL *)ERROR_PTR("no hits or misses", procName, NULL);

        /* Locate the foreground */
    pixClipToForeground(pixs, &pixt1, NULL);
    if (!pixt1)
        return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL);
    ws = pixGetWidth(pixt1);
    hs = pixGetHeight(pixt1);
    w = ws;
    h = hs;

        /* Crop out a region including the foreground, and add pixels
         * on sides depending on the side flags */
    if (topflag || botflag || leftflag || rightflag) {
        x = y = 0;
        if (topflag) {
            h += missdist + 1;
            y = missdist + 1;
        }
        if (botflag)
            h += missdist + 1;
        if (leftflag) {
            w += missdist + 1;
            x = missdist + 1;
        }
        if (rightflag)
            w += missdist + 1;
        pixt2 = pixCreate(w, h, 1);
        pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0);
    }
    else {
        pixt2 = pixClone(pixt1);
    }
    if (ppixe)
        *ppixe = pixClone(pixt2);
    pixDestroy(&pixt1);

        /* Identify fg and bg pixels that are exactly hitdist and
         * missdist (rsp) away from the boundary pixels in their set.
         * Then get a subsampled set of these points. */
    sel_3 = selCreateBrick(3, 3, 1, 1, SEL_HIT);
    if (hitskip >= 0) {
        selh = selCreateBrick(2 * hitdist + 1, 2 * hitdist + 1,
                              hitdist, hitdist, SEL_HIT);
        pixt3 = pixErode(NULL, pixt2, selh);
        pixfg = pixErode(NULL, pixt3, sel_3);
        pixXor(pixfg, pixfg, pixt3);
        ptah = pixSubsampleBoundaryPixels(pixfg, hitskip);
        pixDestroy(&pixt3);
        pixDestroy(&pixfg);
        selDestroy(&selh);
    }
    if (missskip >= 0) {
        selm = selCreateBrick(2 * missdist + 1, 2 * missdist + 1,
                              missdist, missdist, SEL_HIT);
        pixt3 = pixDilate(NULL, pixt2, selm);
        pixbg = pixDilate(NULL, pixt3, sel_3);
        pixXor(pixbg, pixbg, pixt3);
        ptam = pixSubsampleBoundaryPixels(pixbg, missskip);
        pixDestroy(&pixt3);
        pixDestroy(&pixbg);
        selDestroy(&selm);
    }
    selDestroy(&sel_3);
    pixDestroy(&pixt2);

        /* Generate the hit-miss sel from these point */
    sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE);
    if (hitskip >= 0) {
        npt = ptaGetCount(ptah);
        for (i = 0; i < npt; i++) {
            ptaGetIPt(ptah, i, &ix, &iy);
            selSetElement(sel, iy, ix, SEL_HIT);
        }
    }
    if (missskip >= 0) {
        npt = ptaGetCount(ptam);
        for (i = 0; i < npt; i++) {
            ptaGetIPt(ptam, i, &ix, &iy);
            selSetElement(sel, iy, ix, SEL_MISS);
        }
    }

    ptaDestroy(&ptah);
    ptaDestroy(&ptam);
    return sel;
}
Exemple #6
0
/*!
 *  pixGenerateSelRandom()
 *
 *      Input:  pix (1 bpp, typically small, to be used as a pattern)
 *              hitfract (fraction of allowable fg pixels that are hits)
 *              missfract (fraction of allowable bg pixels that are misses)
 *              distance (min distance from boundary pixel; use 0 for default)
 *              toppix (number of extra pixels of bg added above)
 *              botpix (number of extra pixels of bg added below)
 *              leftpix (number of extra pixels of bg added to left)
 *              rightpix (number of extra pixels of bg added to right)
 *              &pixe (<optional return> input pix expanded by extra pixels)
 *      Return: sel (hit-miss for input pattern), or null on error
 *
 *  Notes:
 *    (1) Either of hitfract and missfract can be zero.  If both are zero,
 *        the sel would be empty, and NULL is returned.
 *    (2) No elements are selected that are less than 'distance' pixels away
 *        from a boundary pixel of the same color.  This makes the
 *        match much more robust to edge noise.  Valid inputs of
 *        'distance' are 0, 1, 2, 3 and 4.  If distance is either 0 or
 *        greater than 4, we reset it to the default value.
 *    (3) The 4 numbers for adding rectangles of pixels outside the fg
 *        can be use if the pattern is expected to be surrounded by bg
 *        (white) pixels.  On the other hand, if the pattern may be near
 *        other fg (black) components on some sides, use 0 for those sides.
 *    (4) The input pix, as extended by the extra pixels on selected sides,
 *        can optionally be returned.  For debugging, call
 *        pixDisplayHitMissSel() to visualize the hit-miss sel superimposed
 *        on the generating bitmap.
 */
SEL *
pixGenerateSelRandom(PIX       *pixs,
                     l_float32  hitfract,
                     l_float32  missfract,
                     l_int32    distance,
                     l_int32    toppix,
                     l_int32    botpix,
                     l_int32    leftpix,
                     l_int32    rightpix,
                     PIX      **ppixe)
{
l_int32    ws, hs, w, h, x, y, i, j, thresh;
l_uint32   val;
PIX       *pixt1, *pixt2, *pixfg, *pixbg;
SEL       *seld, *sel;

    PROCNAME("pixGenerateSelRandom");

    if (ppixe) *ppixe = NULL;
    if (!pixs)
        return (SEL *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 1)
        return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
    if (hitfract <= 0.0 && missfract <= 0.0)
        return (SEL *)ERROR_PTR("no hits or misses", procName, NULL);
    if (hitfract > 1.0 || missfract > 1.0)
        return (SEL *)ERROR_PTR("fraction can't be > 1.0", procName, NULL);

    if (distance <= 0)
        distance = DEFAULT_DISTANCE_TO_BOUNDARY;
    if (distance > MAX_DISTANCE_TO_BOUNDARY) {
        L_WARNING("distance too large; setting to max value", procName);
        distance = MAX_DISTANCE_TO_BOUNDARY;
    }

        /* Locate the foreground */
    pixClipToForeground(pixs, &pixt1, NULL);
    if (!pixt1)
        return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL);
    ws = pixGetWidth(pixt1);
    hs = pixGetHeight(pixt1);
    w = ws;
    h = hs;

        /* Crop out a region including the foreground, and add pixels
         * on sides depending on the side flags */
    if (toppix || botpix || leftpix || rightpix) {
        x = y = 0;
        if (toppix) {
            h += toppix;
            y = toppix;
        }
        if (botpix)
            h += botpix;
        if (leftpix) {
            w += leftpix;
            x = leftpix;
        }
        if (rightpix)
            w += rightpix;
        pixt2 = pixCreate(w, h, 1);
        pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0);
    }
    else
        pixt2 = pixClone(pixt1);
    if (ppixe)
        *ppixe = pixClone(pixt2);
    pixDestroy(&pixt1);

        /* Identify fg and bg pixels that are at least 'distance' pixels
         * away from the boundary pixels in their set */
    seld = selCreateBrick(2 * distance + 1, 2 * distance + 1,
                          distance, distance, SEL_HIT);
    pixfg = pixErode(NULL, pixt2, seld);
    pixbg = pixDilate(NULL, pixt2, seld);
    pixInvert(pixbg, pixbg);
    selDestroy(&seld);
    pixDestroy(&pixt2);

        /* Generate the sel from a random selection of these points */
    sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE);
    if (hitfract > 0.0) {
        thresh = (l_int32)(hitfract * (l_float64)RAND_MAX);
        for (i = 0; i < h; i++) {
            for (j = 0; j < w; j++) {
                pixGetPixel(pixfg, j, i, &val);
                if (val) {
                    if (rand() < thresh)
                        selSetElement(sel, i, j, SEL_HIT);
                }
            }
        }
    }
    if (missfract > 0.0) {
        thresh = (l_int32)(missfract * (l_float64)RAND_MAX);
        for (i = 0; i < h; i++) {
            for (j = 0; j < w; j++) {
                pixGetPixel(pixbg, j, i, &val);
                if (val) {
                    if (rand() < thresh)
                        selSetElement(sel, i, j, SEL_MISS);
                }
            }
        }
    }

    pixDestroy(&pixfg);
    pixDestroy(&pixbg);
    return sel;
}
Exemple #7
0
/*!
 *  pixGenerateSelWithRuns()
 *
 *      Input:  pix (1 bpp, typically small, to be used as a pattern)
 *              nhlines (number of hor lines along which elements are found)
 *              nvlines (number of vert lines along which elements are found)
 *              distance (min distance from boundary pixel; use 0 for default)
 *              minlength (min runlength to set hit or miss; use 0 for default)
 *              toppix (number of extra pixels of bg added above)
 *              botpix (number of extra pixels of bg added below)
 *              leftpix (number of extra pixels of bg added to left)
 *              rightpix (number of extra pixels of bg added to right)
 *              &pixe (<optional return> input pix expanded by extra pixels)
 *      Return: sel (hit-miss for input pattern), or null on error
 *
 *  Notes:
 *    (1) The horizontal and vertical lines along which elements are
 *        selected are roughly equally spaced.  The actual locations of
 *        the hits and misses are the centers of respective run-lengths.
 *    (2) No elements are selected that are less than 'distance' pixels away
 *        from a boundary pixel of the same color.  This makes the
 *        match much more robust to edge noise.  Valid inputs of
 *        'distance' are 0, 1, 2, 3 and 4.  If distance is either 0 or
 *        greater than 4, we reset it to the default value.
 *    (3) The 4 numbers for adding rectangles of pixels outside the fg
 *        can be use if the pattern is expected to be surrounded by bg
 *        (white) pixels.  On the other hand, if the pattern may be near
 *        other fg (black) components on some sides, use 0 for those sides.
 *    (4) The pixels added to a side allow you to have miss elements there.
 *        There is a constraint between distance, minlength, and
 *        the added pixels for this to work.  We illustrate using the
 *        default values.  If you add 5 pixels to the top, and use a
 *        distance of 1, then you end up with a vertical run of at least
 *        4 bg pixels along the top edge of the image.  If you use a
 *        minimum runlength of 3, each vertical line will always find
 *        a miss near the center of its run.  However, if you use a
 *        minimum runlength of 5, you will not get a miss on every vertical
 *        line.  As another example, if you have 7 added pixels and a
 *        distance of 2, you can use a runlength up to 5 to guarantee
 *        that the miss element is recorded.  We give a warning if the
 *        contraint does not guarantee a miss element outside the
 *        image proper.
 *    (5) The input pix, as extended by the extra pixels on selected sides,
 *        can optionally be returned.  For debugging, call
 *        pixDisplayHitMissSel() to visualize the hit-miss sel superimposed
 *        on the generating bitmap.
 */
SEL *
pixGenerateSelWithRuns(PIX     *pixs,
                       l_int32  nhlines,
                       l_int32  nvlines,
                       l_int32  distance,
                       l_int32  minlength,
                       l_int32  toppix,
                       l_int32  botpix,
                       l_int32  leftpix,
                       l_int32  rightpix,
                       PIX    **ppixe)
{
l_int32    ws, hs, w, h, x, y, xval, yval, i, j, nh, nm;
l_float32  delh, delw;
NUMA      *nah, *nam;
PIX       *pixt1, *pixt2, *pixfg, *pixbg;
PTA       *ptah, *ptam;
SEL       *seld, *sel;

    PROCNAME("pixGenerateSelWithRuns");

    if (ppixe) *ppixe = NULL;
    if (!pixs)
        return (SEL *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 1)
        return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
    if (nhlines < 1 && nvlines < 1)
        return (SEL *)ERROR_PTR("nvlines and nhlines both < 1", procName, NULL);

    if (distance <= 0)
        distance = DEFAULT_DISTANCE_TO_BOUNDARY;
    if (minlength <= 0)
        minlength = DEFAULT_MIN_RUNLENGTH;
    if (distance > MAX_DISTANCE_TO_BOUNDARY) {
        L_WARNING("distance too large; setting to max value", procName);
        distance = MAX_DISTANCE_TO_BOUNDARY;
    }

        /* Locate the foreground */
    pixClipToForeground(pixs, &pixt1, NULL);
    if (!pixt1)
        return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL);
    ws = pixGetWidth(pixt1);
    hs = pixGetHeight(pixt1);
    w = ws;
    h = hs;

        /* Crop out a region including the foreground, and add pixels
         * on sides depending on the side flags */
    if (toppix || botpix || leftpix || rightpix) {
        x = y = 0;
        if (toppix) {
            h += toppix;
            y = toppix;
            if (toppix < distance + minlength)
                L_WARNING("no miss elements in added top pixels", procName);
        }
        if (botpix) {
            h += botpix;
            if (botpix < distance + minlength)
                L_WARNING("no miss elements in added bot pixels", procName);
        }
        if (leftpix) {
            w += leftpix;
            x = leftpix;
            if (leftpix < distance + minlength)
                L_WARNING("no miss elements in added left pixels", procName);
        }
        if (rightpix) {
            w += rightpix;
            if (rightpix < distance + minlength)
                L_WARNING("no miss elements in added right pixels", procName);
        }
        pixt2 = pixCreate(w, h, 1);
        pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0);
    }
    else
        pixt2 = pixClone(pixt1);
    if (ppixe)
        *ppixe = pixClone(pixt2);
    pixDestroy(&pixt1);

        /* Identify fg and bg pixels that are at least 'distance' pixels
         * away from the boundary pixels in their set */
    seld = selCreateBrick(2 * distance + 1, 2 * distance + 1,
                          distance, distance, SEL_HIT);
    pixfg = pixErode(NULL, pixt2, seld);
    pixbg = pixDilate(NULL, pixt2, seld);
    pixInvert(pixbg, pixbg);
    selDestroy(&seld);
    pixDestroy(&pixt2);

        /* Accumulate hit and miss points */
    ptah = ptaCreate(0);
    ptam = ptaCreate(0);
    if (nhlines >= 1) {
        delh = (l_float32)h / (l_float32)(nhlines + 1);
        for (i = 0, y = 0; i < nhlines; i++) {
            y += (l_int32)(delh + 0.5);
            nah = pixGetRunCentersOnLine(pixfg, -1, y, minlength);
            nam = pixGetRunCentersOnLine(pixbg, -1, y, minlength);
            nh = numaGetCount(nah);
            nm = numaGetCount(nam);
            for (j = 0; j < nh; j++) {
                numaGetIValue(nah, j, &xval);
                ptaAddPt(ptah, xval, y);
            }
            for (j = 0; j < nm; j++) {
                numaGetIValue(nam, j, &xval);
                ptaAddPt(ptam, xval, y);
            }
            numaDestroy(&nah);
            numaDestroy(&nam);
        }
    }
    if (nvlines >= 1) {
        delw = (l_float32)w / (l_float32)(nvlines + 1);
        for (i = 0, x = 0; i < nvlines; i++) {
            x += (l_int32)(delw + 0.5);
            nah = pixGetRunCentersOnLine(pixfg, x, -1, minlength);
            nam = pixGetRunCentersOnLine(pixbg, x, -1, minlength);
            nh = numaGetCount(nah);
            nm = numaGetCount(nam);
            for (j = 0; j < nh; j++) {
                numaGetIValue(nah, j, &yval);
                ptaAddPt(ptah, x, yval);
            }
            for (j = 0; j < nm; j++) {
                numaGetIValue(nam, j, &yval);
                ptaAddPt(ptam, x, yval);
            }
            numaDestroy(&nah);
            numaDestroy(&nam);
        }
    }

        /* Make the Sel with those points */
    sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE);
    nh = ptaGetCount(ptah);
    for (i = 0; i < nh; i++) {
        ptaGetIPt(ptah, i, &x, &y);
        selSetElement(sel, y, x, SEL_HIT);
    }
    nm = ptaGetCount(ptam);
    for (i = 0; i < nm; i++) {
        ptaGetIPt(ptam, i, &x, &y);
        selSetElement(sel, y, x, SEL_MISS);
    }

    pixDestroy(&pixfg);
    pixDestroy(&pixbg);
    ptaDestroy(&ptah);
    ptaDestroy(&ptam);
    return sel;
}
Exemple #8
0
int main_find_pattern(int    argc,
	char **argv)
{
	char        *filein, *fileout, *patternfile;
	l_int32      w, h, i, n;
	BOX         *box, *boxe;
	BOXA        *boxa1, *boxa2;
	PIX         *pixs, *pixp, *pixpe;
	PIX         *pixd, *pixt1, *pixt2, *pixhmt;
	SEL         *sel_2h, *sel;
	static char  mainName[] = "findpattern1";

	filein = "feyn.tif";
	patternfile = "char.tif";
	fileout = "result.findpattern1";

	if ((pixs = pixRead(filein)) == NULL)
		printf("pixs not made\n");
	if ((pixp = pixRead(patternfile)) == NULL)
		printf("pixp not made\n");
	w = pixGetWidth(pixp);
	h = pixGetHeight(pixp);

	/* generate the hit-miss Sel with runs */
	sel = pixGenerateSelWithRuns(pixp, NumHorLines, NumVertLines, 0,
		MinRunlength, 7, 7, 0, 0, &pixpe);

	/* display the Sel two ways */
	selWriteStream(stderr, sel);
	pixt1 = pixDisplayHitMissSel(pixpe, sel, 9, HitColor, MissColor);
	pixDisplay(pixt1, 200, 200);
	pixWrite("junkpixt", pixt1, IFF_PNG);

	/* use the Sel to find all instances in the page */
	startTimer();
	pixhmt = pixHMT(NULL, pixs, sel);
	fprintf(stderr, "Time to find patterns = %7.3f\n", stopTimer());

	/* small erosion to remove noise; typically not necessary if
	* there are enough elements in the Sel */
	sel_2h = selCreateBrick(1, 2, 0, 0, SEL_HIT);
	pixt2 = pixErode(NULL, pixhmt, sel_2h);

	/* display the result visually by placing the Sel at each
	* location found */
	pixd = pixDilate(NULL, pixt2, sel);
	pixWrite(fileout, pixd, IFF_TIFF_G4);

	/* display outut with an outline around each located pattern */
	boxa1 = pixConnCompBB(pixt2, 8);
	n = boxaGetCount(boxa1);
	boxa2 = boxaCreate(n);
	for (i = 0; i < n; i++) {
		box = boxaGetBox(boxa1, i, L_COPY);
		boxe = boxCreate(box->x - w / 2, box->y - h / 2, w + 4, h + 4);
		boxaAddBox(boxa2, boxe, L_INSERT);
		pixRenderBox(pixs, boxe, 4, L_FLIP_PIXELS);
		boxDestroy(&box);
	}
	pixWrite("junkoutline", pixs, IFF_TIFF_G4);
	//boxaWriteStream(stderr, boxa2); //TODO ???

	pixDestroy(&pixs);
	pixDestroy(&pixp);
	pixDestroy(&pixpe);
	pixDestroy(&pixt1);
	pixDestroy(&pixt2);
	pixDestroy(&pixhmt);
	pixDestroy(&pixd);
	selDestroy(&sel);
	selDestroy(&sel_2h);
	boxaDestroy(&boxa1);
	boxaDestroy(&boxa2);

	printf("\n---\nEND\n");
	getchar();
	return 0;
}
main(int    argc,
     char **argv)
{
char        *selnameh, *selnamev;
l_int32      ok, same, w, h, i, bordercolor, extraborder;
l_int32      width[3] = {21, 1, 21};
l_int32      height[3] = {1, 7, 7};
PIX         *pixs, *pixref;
PIX         *pixt0, *pixt1, *pixt2, *pixt3, *pixt4;
SEL         *sel;
SELA        *sela;
static char  mainName[] = "binmorph3_reg";

    if (argc != 1)
	exit(ERROR_INT(" Syntax: binmorph3_reg", mainName, 1));

    if ((pixs = pixRead("feyn.tif")) == NULL)
	exit(ERROR_INT("pix not made", mainName, 1));

#if TEST_SYMMETRIC
    resetMorphBoundaryCondition(SYMMETRIC_MORPH_BC);
#endif  /* TEST_SYMMETRIC */

    for (i = 0; i < 3; i++) {
        w = width[i];
        h = height[i];
        sel = selCreateBrick(h, w, h / 2, w / 2, SEL_HIT);
	selnameh = NULL;
	selnamev = NULL;


	    /* Get the selnames for horiz and vert */
        sela = selaAddBasic(NULL);
        if (w > 1) {
            if ((selnameh = selaGetBrickName(sela, w, 1)) == NULL) {
                selaDestroy(&sela);
                return ERROR_INT("dwa hor sel not defined", mainName, 1);
            }
        }
        if (h > 1) {
            if ((selnamev = selaGetBrickName(sela, 1, h)) == NULL) {
                selaDestroy(&sela);
                return ERROR_INT("dwa vert sel not defined", mainName, 1);
            }
        }
	fprintf(stderr, "w = %d, h = %d, selh = %s, selv = %s\n",
                w, h, selnameh, selnamev);
        ok = TRUE;
        selaDestroy(&sela);

            /* ----------------- Dilation ----------------- */
        fprintf(stderr, "Testing dilation\n");
        pixref = pixDilate(NULL, pixs, sel);
        pixt1 = pixDilateBrickDwa(NULL, pixs, w, h);
        pixEqual(pixref, pixt1, &same);
        if (!same) {
            fprintf(stderr, "pixref != pixt1 !\n"); ok = FALSE;
	}
	pixDestroy(&pixt1);

	if (w > 1)
            pixt1 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnameh);
	else
            pixt1 = pixClone(pixs);
	if (h > 1)
            pixt2 = pixMorphDwa_1(NULL, pixt1, L_MORPH_DILATE, selnamev);
	else
            pixt2 = pixClone(pixt1);
        pixEqual(pixref, pixt2, &same);
        if (!same) {
            fprintf(stderr, "pixref != pixt2 !\n"); ok = FALSE;
	}
	pixDestroy(&pixt1);
	pixDestroy(&pixt2);

	pixt1 = pixAddBorder(pixs, 32, 0);
	if (w > 1)
            pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh);
	else
            pixt2 = pixClone(pixt1);
        if (h > 1)
            pixt3 = pixFMorphopGen_1(NULL, pixt2, L_MORPH_DILATE, selnamev);
	else
            pixt3 = pixClone(pixt2);
	pixt4 = pixRemoveBorder(pixt3, 32);
        pixEqual(pixref, pixt4, &same);
        if (!same) {
            fprintf(stderr, "pixref != pixt4 !\n"); ok = FALSE;
	}
	pixDestroy(&pixref);
	pixDestroy(&pixt1);
	pixDestroy(&pixt2);
	pixDestroy(&pixt3);
	pixDestroy(&pixt4);

            /* ----------------- Erosion ----------------- */
        fprintf(stderr, "Testing erosion\n");
        pixref = pixErode(NULL, pixs, sel);
        pixt1 = pixErodeBrickDwa(NULL, pixs, w, h);
        pixEqual(pixref, pixt1, &same);
        if (!same) {
            fprintf(stderr, "pixref != pixt1 !\n"); ok = FALSE;
	}
	pixDestroy(&pixt1);

	if (w > 1)
            pixt1 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnameh);
	else
            pixt1 = pixClone(pixs);
	if (h > 1)
            pixt2 = pixMorphDwa_1(NULL, pixt1, L_MORPH_ERODE, selnamev);
	else
            pixt2 = pixClone(pixt1);
        pixEqual(pixref, pixt2, &same);
        if (!same) {
            fprintf(stderr, "pixref != pixt2 !\n"); ok = FALSE;
	}
	pixDestroy(&pixt1);
	pixDestroy(&pixt2);

	pixt1 = pixAddBorder(pixs, 32, 0);
	if (w > 1)
            pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh);
	else
            pixt2 = pixClone(pixt1);
        if (h > 1)
            pixt3 = pixFMorphopGen_1(NULL, pixt2, L_MORPH_ERODE, selnamev);
	else
            pixt3 = pixClone(pixt2);
	pixt4 = pixRemoveBorder(pixt3, 32);
        pixEqual(pixref, pixt4, &same);
        if (!same) {
            fprintf(stderr, "pixref != pixt4 !\n"); ok = FALSE;
        }
	pixDestroy(&pixref);
	pixDestroy(&pixt1);
	pixDestroy(&pixt2);
	pixDestroy(&pixt3);
	pixDestroy(&pixt4);

            /* ----------------- Opening ----------------- */
        fprintf(stderr, "Testing opening\n");
        pixref = pixOpen(NULL, pixs, sel);
        pixt1 = pixOpenBrickDwa(NULL, pixs, w, h);
        pixEqual(pixref, pixt1, &same);
        if (!same) {
            fprintf(stderr, "pixref != pixt1 !\n"); ok = FALSE;
        }
        pixDestroy(&pixt1);

        if (h == 1)
            pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_OPEN, selnameh);
        else if (w == 1)
            pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_OPEN, selnamev);
        else {
            pixt1 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnameh);
            pixt2 = pixMorphDwa_1(NULL, pixt1, L_MORPH_ERODE, selnamev);
            pixMorphDwa_1(pixt1, pixt2, L_MORPH_DILATE, selnameh);
            pixMorphDwa_1(pixt2, pixt1, L_MORPH_DILATE, selnamev);
            pixDestroy(&pixt1);
        }
        pixEqual(pixref, pixt2, &same);
        if (!same) {
            fprintf(stderr, "pixref != pixt2 !\n"); ok = FALSE;
        }
        pixDestroy(&pixt2);

        pixt1 = pixAddBorder(pixs, 32, 0);
        if (h == 1)
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnameh);
        else if (w == 1)
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnamev);
        else {
            pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh);
            pixt3 = pixFMorphopGen_1(NULL, pixt2, L_MORPH_ERODE, selnamev);
            pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnameh);
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev);
	    pixDestroy(&pixt2);
        }
        pixt4 = pixRemoveBorder(pixt3, 32);
        pixEqual(pixref, pixt4, &same);
        if (!same) {
            fprintf(stderr, "pixref != pixt4 !\n"); ok = FALSE;
	}
	pixDestroy(&pixref);
        pixDestroy(&pixt1);
        pixDestroy(&pixt3);
	pixDestroy(&pixt4);

            /* ----------------- Closing ----------------- */
        fprintf(stderr, "Testing closing\n");
        pixref = pixClose(NULL, pixs, sel);

	    /* Note: L_MORPH_CLOSE for h==1 or w==1 gives safe closing,
	     * so we can't use it here. */
        if (h == 1) {
            pixt1 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnameh);
            pixt2 = pixMorphDwa_1(NULL, pixt1, L_MORPH_ERODE, selnameh);
	}
        else if (w == 1) {
            pixt1 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnamev);
            pixt2 = pixMorphDwa_1(NULL, pixt1, L_MORPH_ERODE, selnamev);
	}
        else {
            pixt1 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnameh);
            pixt2 = pixMorphDwa_1(NULL, pixt1, L_MORPH_DILATE, selnamev);
            pixMorphDwa_1(pixt1, pixt2, L_MORPH_ERODE, selnameh);
            pixMorphDwa_1(pixt2, pixt1, L_MORPH_ERODE, selnamev);
        }
        pixDestroy(&pixt1);
        pixEqual(pixref, pixt2, &same);
        if (!same) {
            fprintf(stderr, "pixref != pixt2 !\n"); ok = FALSE;
        }
        pixDestroy(&pixt2);

	    /* Note: by adding only 32 pixels of border, we get
	     * the normal closing operation, even when calling
	     * with L_MORPH_CLOSE, because it requires 32 pixels
	     * of border to be safe. */
        pixt1 = pixAddBorder(pixs, 32, 0);
        if (h == 1)
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnameh);
        else if (w == 1)
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev);
        else {
            pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh);
            pixt3 = pixFMorphopGen_1(NULL, pixt2, L_MORPH_DILATE, selnamev);
            pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnameh);
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev);
	    pixDestroy(&pixt2);
        }
        pixt4 = pixRemoveBorder(pixt3, 32);
        pixEqual(pixref, pixt4, &same);
        if (!same) {
            fprintf(stderr, "pixref != pixt4 !\n"); ok = FALSE;
	}
	pixDestroy(&pixref);
        pixDestroy(&pixt1);
        pixDestroy(&pixt3);
	pixDestroy(&pixt4);

            /* ------------- Safe Closing ----------------- */
        fprintf(stderr, "Testing safe closing\n");
        pixref = pixCloseSafe(NULL, pixs, sel);
        pixt0 = pixCloseSafeBrick(NULL, pixs, w, h);
        pixEqual(pixref, pixt0, &same);
        if (!same) {
            fprintf(stderr, "pixref != pixt0 !\n"); ok = FALSE;
        }
        pixDestroy(&pixt0);

        pixt1 = pixCloseBrickDwa(NULL, pixs, w, h);
        pixEqual(pixref, pixt1, &same);
        if (!same) {
            fprintf(stderr, "pixref != pixt1 !\n"); ok = FALSE;
        }
        pixDestroy(&pixt1);

        bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1);
        if (bordercolor == 0)   /* asymmetric b.c. */
            extraborder = 32;
        else   /* symmetric b.c. */
            extraborder = 0;

	    /* Note: for safe closing we need 64 border pixels.
	     * However, when we implement a separable Sel
	     * with pixMorphDwa_*(), we must do dilation and
	     * erosion explicitly, and these functions only
	     * add/remove a 32-pixel border.  Thus, for that
	     * case we must add an additional 32-pixel border
	     * before doing the operations.  That is the reason
	     * why the implementation in morphdwa.c adds the
	     * 64 bit border and then uses the lower-level
	     * pixFMorphopGen_*() functions. */
        if (h == 1)
            pixt3 = pixMorphDwa_1(NULL, pixs, L_MORPH_CLOSE, selnameh);
        else if (w == 1)
            pixt3 = pixMorphDwa_1(NULL, pixs, L_MORPH_CLOSE, selnamev);
        else {
            pixt0 = pixAddBorder(pixs, extraborder, 0);
            pixt1 = pixMorphDwa_1(NULL, pixt0, L_MORPH_DILATE, selnameh);
            pixt2 = pixMorphDwa_1(NULL, pixt1, L_MORPH_DILATE, selnamev);
            pixMorphDwa_1(pixt1, pixt2, L_MORPH_ERODE, selnameh);
            pixMorphDwa_1(pixt2, pixt1, L_MORPH_ERODE, selnamev);
	    pixt3 = pixRemoveBorder(pixt2, extraborder);
            pixDestroy(&pixt0);
            pixDestroy(&pixt1);
            pixDestroy(&pixt2);
        }
        pixEqual(pixref, pixt3, &same);
        if (!same) {
            fprintf(stderr, "pixref != pixt3 !\n"); ok = FALSE;
        }
        pixDestroy(&pixt3);

        pixt1 = pixAddBorder(pixs, 32 + extraborder, 0);
        if (h == 1)
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnameh);
        else if (w == 1)
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev);
        else {
            pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh);
            pixt3 = pixFMorphopGen_1(NULL, pixt2, L_MORPH_DILATE, selnamev);
            pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnameh);
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev);
	    pixDestroy(&pixt2);
        }
        pixt4 = pixRemoveBorder(pixt3, 32 + extraborder);
        pixEqual(pixref, pixt4, &same);
        if (!same) {
            fprintf(stderr, "pixref != pixt4 !\n"); ok = FALSE;
	}
	pixDestroy(&pixref);
        pixDestroy(&pixt1);
        pixDestroy(&pixt3);
	pixDestroy(&pixt4);

        if (ok)
            fprintf(stderr, "All morph tests OK!\n");
	selDestroy(&sel);
	lept_free(selnameh);
	lept_free(selnamev);

    }

    pixDestroy(&pixs);
    return 0;
}
Exemple #10
0
int main(int    argc,
         char **argv)
{
l_int32       i, j, w, h, same, width, height, cx, cy;
l_uint32      val;
BOX          *box;
PIX          *pix0, *pixs, *pixse, *pixd1, *pixd2;
SEL          *sel;
L_REGPARAMS  *rp;

    if (regTestSetup(argc, argv, &rp))
        return 1;

    pix0 = pixRead("feyn-fract.tif");
    box = boxCreate(293, 37, pixGetWidth(pix0) - 691, pixGetHeight(pix0) -145);
    pixs = pixClipRectangle(pix0, box, NULL);
    boxDestroy(&box);
    if (rp->display) pixDisplay(pixs, 100, 100);

        /* Test 63 different sizes */
    for (width = 1; width <= 25; width += 3) {   /* 9 values */
	for (height = 1; height <= 25; height += 4) {  /* 7 values */

	    cx = width / 2;
	    cy = height / 2;

		/* Dilate using an actual sel */
	    sel = selCreateBrick(height, width, cy, cx, SEL_HIT);
	    pixd1 = pixDilate(NULL, pixs, sel);

		/* Dilate using a pix as a sel */
	    pixse = pixCreate(width, height, 1);
	    pixSetAll(pixse);
	    pixd2 = pixCopy(NULL, pixs);
	    w = pixGetWidth(pixs);
	    h = pixGetHeight(pixs);
	    for (i = 0; i < h; i++) {
		for (j = 0; j < w; j++) {
		    pixGetPixel(pixs, j, i, &val);
		    if (val)
			pixRasterop(pixd2, j - cx, i - cy, width, height,
				    PIX_SRC | PIX_DST, pixse, 0, 0);
		}
	    }

	    pixEqual(pixd1, pixd2, &same);
            regTestCompareValues(rp, 1, same, 0.0);  /* 0 - 62 */
	    if (same == 0) {
		fprintf(stderr,
                        "Results differ for SE (width,height) = (%d,%d)\n",
                        width, height);
            }

	    pixDestroy(&pixse);
	    pixDestroy(&pixd1);
	    pixDestroy(&pixd2);
	    selDestroy(&sel);
	}
    }
    pixDestroy(&pix0);
    pixDestroy(&pixs);
    return regTestCleanup(rp);
}
main(int    argc,
     char **argv)
{
l_int32      i, ok, same;
char         sequence[512];
PIX         *pixs, *pixref;
PIX         *pixt1, *pixt2, *pixt3, *pixt4, *pixt5, *pixt6;
PIX         *pixt7, *pixt8, *pixt9, *pixt10, *pixt11;
PIX         *pixt12, *pixt13, *pixt14;
SEL         *sel;
static char  mainName[] = "binmorph1_reg";

    if (argc != 1)
	exit(ERROR_INT(" Syntax: binmorph1_reg", mainName, 1));

    if ((pixs = pixRead("feyn.tif")) == NULL)
	exit(ERROR_INT("pix not made", mainName, 1));

#if TEST_SYMMETRIC
        /* This works properly if there is an added border */
    resetMorphBoundaryCondition(SYMMETRIC_MORPH_BC);
#if 1
    pixt1 = pixAddBorder(pixs, 32, 0);
    pixTransferAllData(pixs, &pixt1, 0, 0);
#endif
#endif  /* TEST_SYMMETRIC */

        /* This is our test sel */
    sel = selCreateBrick(HEIGHT, WIDTH, HEIGHT / 2, WIDTH / 2, SEL_HIT);

        /* Dilation */
    fprintf(stderr, "Testing dilation\n");
    ok = TRUE;
    pixref = pixDilate(NULL, pixs, sel);   /* new one */
    pixt1 = pixCreateTemplate(pixs);
    pixDilate(pixt1, pixs, sel);           /* existing one */
    pixEqual(pixref, pixt1, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt1 !\n"); ok = FALSE;
    }
    pixt2 = pixCopy(NULL, pixs);
    pixDilate(pixt2, pixt2, sel);          /* in-place */
    pixEqual(pixref, pixt2, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt2 !\n"); ok = FALSE;
    }
    sprintf(sequence, "d%d.%d", WIDTH, HEIGHT);
    pixt3 = pixMorphSequence(pixs, sequence, 0);    /* sequence, atomic */
    pixEqual(pixref, pixt3, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt3 !\n"); ok = FALSE;
    }
    sprintf(sequence, "d%d.1 + d1.%d", WIDTH, HEIGHT);
    pixt4 = pixMorphSequence(pixs, sequence, 0);    /* sequence, separable */
    pixEqual(pixref, pixt4, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt4 !\n"); ok = FALSE;
    }
    pixt5 = pixDilateBrick(NULL, pixs, WIDTH, HEIGHT);  /* new one */
    pixEqual(pixref, pixt5, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt5 !\n"); ok = FALSE;
    }
    pixt6 = pixCreateTemplate(pixs);
    pixDilateBrick(pixt6, pixs, WIDTH, HEIGHT);  /* existing one */
    pixEqual(pixref, pixt6, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt6 !\n"); ok = FALSE;
    }
    pixt7 = pixCopy(NULL, pixs);
    pixDilateBrick(pixt7, pixt7, WIDTH, HEIGHT);  /* in-place */
    pixEqual(pixref, pixt7, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt7 !\n"); ok = FALSE;
    }
    pixt8 = pixDilateBrickDwa(NULL, pixs, WIDTH, HEIGHT);  /* new one */
    pixEqual(pixref, pixt8, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt8 !\n"); ok = FALSE;
    }
    pixt9 = pixCreateTemplate(pixs);
    pixDilateBrickDwa(pixt9, pixs, WIDTH, HEIGHT);  /* existing one */
    pixEqual(pixref, pixt9, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt9 !\n"); ok = FALSE;
    }
    pixt10 = pixCopy(NULL, pixs);
    pixDilateBrickDwa(pixt10, pixt10, WIDTH, HEIGHT);  /* in-place */
    pixEqual(pixref, pixt10, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt10 !\n"); ok = FALSE;
    }
    pixt11 = pixCreateTemplate(pixs);
    pixDilateCompBrickDwa(pixt11, pixs, WIDTH, HEIGHT);  /* existing one */
    pixEqual(pixref, pixt11, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt11 !\n"); ok = FALSE;
    }
    sprintf(sequence, "d%d.%d", WIDTH, HEIGHT);
    pixt12 = pixMorphCompSequence(pixs, sequence, 0);    /* comp sequence */
    pixEqual(pixref, pixt12, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt12!\n"); ok = FALSE;
    }
    pixt13 = pixMorphSequenceDwa(pixs, sequence, 0);    /* dwa sequence */
    pixEqual(pixref, pixt13, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt13!\n"); ok = FALSE;
    }
    pixDestroy(&pixref);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixDestroy(&pixt3);
    pixDestroy(&pixt4);
    pixDestroy(&pixt5);
    pixDestroy(&pixt6);
    pixDestroy(&pixt7);
    pixDestroy(&pixt8);
    pixDestroy(&pixt9);
    pixDestroy(&pixt10);
    pixDestroy(&pixt11);
    pixDestroy(&pixt12);
    pixDestroy(&pixt13);

        /* Erosion */
    fprintf(stderr, "Testing erosion\n");
    pixref = pixErode(NULL, pixs, sel);   /* new one */
    pixt1 = pixCreateTemplate(pixs);
    pixErode(pixt1, pixs, sel);           /* existing one */
    pixEqual(pixref, pixt1, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt1 !\n"); ok = FALSE;
    }
    pixt2 = pixCopy(NULL, pixs);
    pixErode(pixt2, pixt2, sel);          /* in-place */
    pixEqual(pixref, pixt2, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt2 !\n"); ok = FALSE;
    }
    sprintf(sequence, "e%d.%d", WIDTH, HEIGHT);
    pixt3 = pixMorphSequence(pixs, sequence, 0);    /* sequence, atomic */
    pixEqual(pixref, pixt3, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt3 !\n"); ok = FALSE;
    }
    sprintf(sequence, "e%d.1 + e1.%d", WIDTH, HEIGHT);
    pixt4 = pixMorphSequence(pixs, sequence, 0);    /* sequence, separable */
    pixEqual(pixref, pixt4, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt4 !\n"); ok = FALSE;
    }
    pixt5 = pixErodeBrick(NULL, pixs, WIDTH, HEIGHT);  /* new one */
    pixEqual(pixref, pixt5, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt5 !\n"); ok = FALSE;
    }
    pixt6 = pixCreateTemplate(pixs);
    pixErodeBrick(pixt6, pixs, WIDTH, HEIGHT);  /* existing one */
    pixEqual(pixref, pixt6, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt6 !\n"); ok = FALSE;
    }
    pixt7 = pixCopy(NULL, pixs);
    pixErodeBrick(pixt7, pixt7, WIDTH, HEIGHT);  /* in-place */
    pixEqual(pixref, pixt7, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt7 !\n"); ok = FALSE;
    }
    pixt8 = pixErodeBrickDwa(NULL, pixs, WIDTH, HEIGHT);  /* new one */
    pixEqual(pixref, pixt8, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt8 !\n"); ok = FALSE;
    }
    pixt9 = pixCreateTemplate(pixs);
    pixErodeBrickDwa(pixt9, pixs, WIDTH, HEIGHT);  /* existing one */
    pixEqual(pixref, pixt9, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt9 !\n"); ok = FALSE;
    }
    pixt10 = pixCopy(NULL, pixs);
    pixErodeBrickDwa(pixt10, pixt10, WIDTH, HEIGHT);  /* in-place */
    pixEqual(pixref, pixt10, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt10 !\n"); ok = FALSE;
    }
    pixt11 = pixCreateTemplate(pixs);
    pixErodeCompBrickDwa(pixt11, pixs, WIDTH, HEIGHT);  /* existing one */
    pixEqual(pixref, pixt11, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt11 !\n"); ok = FALSE;
    }
    
    sprintf(sequence, "e%d.%d", WIDTH, HEIGHT);
    pixt12 = pixMorphCompSequence(pixs, sequence, 0);    /* comp sequence */
    pixEqual(pixref, pixt12, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt12!\n"); ok = FALSE;
    }
    pixt13 = pixMorphSequenceDwa(pixs, sequence, 0);    /* dwa sequence */
    pixEqual(pixref, pixt13, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt13!\n"); ok = FALSE;
    }
    pixDestroy(&pixref);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixDestroy(&pixt3);
    pixDestroy(&pixt4);
    pixDestroy(&pixt5);
    pixDestroy(&pixt6);
    pixDestroy(&pixt7);
    pixDestroy(&pixt8);
    pixDestroy(&pixt9);
    pixDestroy(&pixt10);
    pixDestroy(&pixt11);
    pixDestroy(&pixt12);
    pixDestroy(&pixt13);

        /* Opening */
    fprintf(stderr, "Testing opening\n");
    pixref = pixOpen(NULL, pixs, sel);   /* new one */
    pixt1 = pixCreateTemplate(pixs);
    pixOpen(pixt1, pixs, sel);           /* existing one */
    pixEqual(pixref, pixt1, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt1 !\n"); ok = FALSE;
    }
    pixt2 = pixCopy(NULL, pixs);
    pixOpen(pixt2, pixt2, sel);          /* in-place */
    pixEqual(pixref, pixt2, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt2 !\n"); ok = FALSE;
    }
    sprintf(sequence, "o%d.%d", WIDTH, HEIGHT);
    pixt3 = pixMorphSequence(pixs, sequence, 0);    /* sequence, atomic */
    pixEqual(pixref, pixt3, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt3 !\n"); ok = FALSE;
    }
    sprintf(sequence, "e%d.%d + d%d.%d", WIDTH, HEIGHT, WIDTH, HEIGHT);
    pixt4 = pixMorphSequence(pixs, sequence, 0);    /* sequence, separable */
    pixEqual(pixref, pixt4, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt4 !\n"); ok = FALSE;
    }
    sprintf(sequence, "e%d.1 + e1.%d + d%d.1 + d1.%d", WIDTH, HEIGHT,
            WIDTH, HEIGHT);
    pixt5 = pixMorphSequence(pixs, sequence, 0);    /* sequence, separable^2 */
    pixEqual(pixref, pixt5, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt5 !\n"); ok = FALSE;
    }
    pixt6 = pixOpenBrick(NULL, pixs, WIDTH, HEIGHT);  /* new one */
    pixEqual(pixref, pixt6, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt6 !\n"); ok = FALSE;
    }
    pixt7 = pixCreateTemplate(pixs);
    pixOpenBrick(pixt7, pixs, WIDTH, HEIGHT);  /* existing one */
    pixEqual(pixref, pixt7, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt7 !\n"); ok = FALSE;
    }
    pixt8 = pixCopy(NULL, pixs);  /* in-place */
    pixOpenBrick(pixt8, pixt8, WIDTH, HEIGHT);  /* existing one */
    pixEqual(pixref, pixt8, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt8 !\n"); ok = FALSE;
    }
    pixt9 = pixOpenBrickDwa(NULL, pixs, WIDTH, HEIGHT);  /* new one */
    pixEqual(pixref, pixt9, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt9 !\n"); ok = FALSE;
    }
    pixt10 = pixCreateTemplate(pixs);
    pixOpenBrickDwa(pixt10, pixs, WIDTH, HEIGHT);  /* existing one */
    pixEqual(pixref, pixt10, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt10 !\n"); ok = FALSE;
    }
    pixt11 = pixCopy(NULL, pixs);
    pixOpenBrickDwa(pixt11, pixt11, WIDTH, HEIGHT);  /* in-place */
    pixEqual(pixref, pixt11, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt11 !\n"); ok = FALSE;
    }
    sprintf(sequence, "o%d.%d", WIDTH, HEIGHT);
    pixt12 = pixMorphCompSequence(pixs, sequence, 0);    /* comp sequence */
    pixEqual(pixref, pixt12, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt12!\n"); ok = FALSE;
    }

#if 0
    pixWrite("/tmp/junkref.png", pixref, IFF_PNG);
    pixWrite("/tmp/junk12.png", pixt12, IFF_PNG);
    pixt13 = pixXor(NULL, pixref, pixt12);
    pixWrite("/tmp/junk12a.png", pixt13, IFF_PNG);
    pixDestroy(&pixt13);
#endif

    pixt13 = pixMorphSequenceDwa(pixs, sequence, 0);    /* dwa sequence */
    pixEqual(pixref, pixt13, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt13!\n"); ok = FALSE;
    }
    pixt14 = pixCreateTemplate(pixs);
    pixOpenCompBrickDwa(pixt14, pixs, WIDTH, HEIGHT);  /* existing one */
    pixEqual(pixref, pixt14, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt14 !\n"); ok = FALSE;
    }

    pixDestroy(&pixref);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixDestroy(&pixt3);
    pixDestroy(&pixt4);
    pixDestroy(&pixt5);
    pixDestroy(&pixt6);
    pixDestroy(&pixt7);
    pixDestroy(&pixt8);
    pixDestroy(&pixt9);
    pixDestroy(&pixt10);
    pixDestroy(&pixt11);
    pixDestroy(&pixt12);
    pixDestroy(&pixt13);
    pixDestroy(&pixt14);

        /* Closing */
    fprintf(stderr, "Testing closing\n");
    pixref = pixClose(NULL, pixs, sel);   /* new one */
    pixt1 = pixCreateTemplate(pixs);
    pixClose(pixt1, pixs, sel);           /* existing one */
    pixEqual(pixref, pixt1, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt1 !\n"); ok = FALSE;
    }
    pixt2 = pixCopy(NULL, pixs);
    pixClose(pixt2, pixt2, sel);          /* in-place */
    pixEqual(pixref, pixt2, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt2 !\n"); ok = FALSE;
    }
    sprintf(sequence, "d%d.%d + e%d.%d", WIDTH, HEIGHT, WIDTH, HEIGHT);
    pixt3 = pixMorphSequence(pixs, sequence, 0);    /* sequence, separable */
    pixEqual(pixref, pixt3, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt3 !\n"); ok = FALSE;
    }
    sprintf(sequence, "d%d.1 + d1.%d + e%d.1 + e1.%d", WIDTH, HEIGHT,
            WIDTH, HEIGHT);
    pixt4 = pixMorphSequence(pixs, sequence, 0);    /* sequence, separable^2 */
    pixEqual(pixref, pixt4, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt4 !\n"); ok = FALSE;
    }
    pixt5 = pixCloseBrick(NULL, pixs, WIDTH, HEIGHT);  /* new one */
    pixEqual(pixref, pixt5, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt5 !\n"); ok = FALSE;
    }
    pixt6 = pixCreateTemplate(pixs);
    pixCloseBrick(pixt6, pixs, WIDTH, HEIGHT);  /* existing one */
    pixEqual(pixref, pixt6, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt6 !\n"); ok = FALSE;
    }
    pixt7 = pixCopy(NULL, pixs);  /* in-place */
    pixCloseBrick(pixt7, pixt7, WIDTH, HEIGHT);  /* existing one */
    pixEqual(pixref, pixt7, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt7 !\n"); ok = FALSE;
    }
    pixDestroy(&pixref);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixDestroy(&pixt3);
    pixDestroy(&pixt4);
    pixDestroy(&pixt5);
    pixDestroy(&pixt6);
    pixDestroy(&pixt7);

        /* Safe closing (using pix, not pixs) */
    fprintf(stderr, "Testing safe closing\n");
    pixref = pixCloseSafe(NULL, pixs, sel);   /* new one */
    pixt1 = pixCreateTemplate(pixs);
    pixCloseSafe(pixt1, pixs, sel);           /* existing one */
    pixEqual(pixref, pixt1, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt1 !\n"); ok = FALSE;
    }
    pixt2 = pixCopy(NULL, pixs);
    pixCloseSafe(pixt2, pixt2, sel);          /* in-place */
    pixEqual(pixref, pixt2, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt2 !\n"); ok = FALSE;
    }
    sprintf(sequence, "c%d.%d", WIDTH, HEIGHT);
    pixt3 = pixMorphSequence(pixs, sequence, 0);    /* sequence, atomic */
    pixEqual(pixref, pixt3, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt3 !\n"); ok = FALSE;
    }
    sprintf(sequence, "b32 + d%d.%d + e%d.%d", WIDTH, HEIGHT, WIDTH, HEIGHT);
    pixt4 = pixMorphSequence(pixs, sequence, 0);    /* sequence, separable */
    pixEqual(pixref, pixt4, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt4 !\n"); ok = FALSE;
    }
    sprintf(sequence, "b32 + d%d.1 + d1.%d + e%d.1 + e1.%d", WIDTH, HEIGHT,
            WIDTH, HEIGHT);
    pixt5 = pixMorphSequence(pixs, sequence, 0);    /* sequence, separable^2 */
    pixEqual(pixref, pixt5, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt5 !\n"); ok = FALSE;
    }
    pixt6 = pixCloseSafeBrick(NULL, pixs, WIDTH, HEIGHT);  /* new one */
    pixEqual(pixref, pixt6, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt6 !\n"); ok = FALSE;
    }
    pixt7 = pixCreateTemplate(pixs);
    pixCloseSafeBrick(pixt7, pixs, WIDTH, HEIGHT);  /* existing one */
    pixEqual(pixref, pixt7, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt7 !\n"); ok = FALSE;
    }
    pixt8 = pixCopy(NULL, pixs);  /* in-place */
    pixCloseSafeBrick(pixt8, pixt8, WIDTH, HEIGHT);  /* existing one */
    pixEqual(pixref, pixt8, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt8 !\n"); ok = FALSE;
    }
    pixt9 = pixCloseBrickDwa(NULL, pixs, WIDTH, HEIGHT);  /* new one */
    pixEqual(pixref, pixt9, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt9 !\n"); ok = FALSE;
    }
    pixt10 = pixCreateTemplate(pixs);
    pixCloseBrickDwa(pixt10, pixs, WIDTH, HEIGHT);  /* existing one */
    pixEqual(pixref, pixt10, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt10 !\n"); ok = FALSE;
    }
    pixt11 = pixCopy(NULL, pixs);
    pixCloseBrickDwa(pixt11, pixt11, WIDTH, HEIGHT);  /* in-place */
    pixEqual(pixref, pixt11, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt11 !\n"); ok = FALSE;
    }
    sprintf(sequence, "c%d.%d", WIDTH, HEIGHT);
    pixt12 = pixMorphCompSequence(pixs, sequence, 0);    /* comp sequence */
    pixEqual(pixref, pixt12, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt12!\n"); ok = FALSE;
    }
    pixt13 = pixMorphSequenceDwa(pixs, sequence, 0);    /* dwa sequence */
    pixEqual(pixref, pixt13, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt13!\n"); ok = FALSE;
    }
    pixt14 = pixCreateTemplate(pixs);
    pixCloseCompBrickDwa(pixt14, pixs, WIDTH, HEIGHT);  /* existing one */
    pixEqual(pixref, pixt14, &same);
    if (!same) {
        fprintf(stderr, "pixref != pixt14 !\n"); ok = FALSE;
    }

#if 0
    pixWrite("/tmp/junkref.png", pixref, IFF_PNG);
    pixWrite("/tmp/junk12.png", pixt12, IFF_PNG);
    pixt13 = pixXor(NULL, pixref, pixt12);
    pixWrite("/tmp/junk12a.png", pixt13, IFF_PNG);
    pixDestroy(&pixt13);
#endif

    pixDestroy(&pixref);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixDestroy(&pixt3);
    pixDestroy(&pixt4);
    pixDestroy(&pixt5);
    pixDestroy(&pixt6);
    pixDestroy(&pixt7);
    pixDestroy(&pixt8);
    pixDestroy(&pixt9);
    pixDestroy(&pixt10);
    pixDestroy(&pixt11);
    pixDestroy(&pixt12);
    pixDestroy(&pixt13);
    pixDestroy(&pixt14);

    if (ok)
        fprintf(stderr, "All morph tests OK!\n");

    pixDestroy(&pixs);
    selDestroy(&sel);
    exit(0);
}