示例#1
0
// Get a set of bounding boxes of possible vertical lines in the image.
// The input resolution overrides any resolution set in src_pix.
// The output line_pix contains just all the detected lines.
Boxa* LineFinder::GetVLineBoxes(int resolution, Pix* src_pix, Pix** line_pix) {
#ifdef HAVE_LIBLEPT
  // Remove any parts of 1 inch/kThinLineFraction wide or more, by opening
  // away the thin lines and subtracting what's left.
  // This is very generous and will leave in even quite wide lines.
  Pix* pixt1 = pixOpenBrick(NULL, src_pix, resolution / kThinLineFraction, 1);
  pixSubtract(pixt1, src_pix, pixt1);
  // Spread sideways to allow for some skew.
  Pix* pixt2 = pixDilateBrick(NULL, pixt1, 3, 1);
  // Now keep only tall stuff of height at least 1 inch/kMinLineLengthFraction.
  pixOpenBrick(pixt1, pixt2, 1, resolution / kMinLineLengthFraction);
  pixDestroy(&pixt2);
  // Put a single pixel crack in every line at an arbitrary spacing,
  // so they break up and the bounding boxes can be used to get the
  // direction accurately enough without needing outlines.
  int wpl = pixGetWpl(pixt1);
  int height = pixGetHeight(pixt1);
  l_uint32* data = pixGetData(pixt1);
  for (int y = kCrackSpacing; y < height; y += kCrackSpacing) {
    memset(data + wpl * y, 0, wpl * sizeof(*data));
  }
  if (textord_tabfind_show_vlines)
    pixWrite("vlines.png", pixt1, IFF_PNG);
  Boxa* boxa = pixConnComp(pixt1, NULL, 8);
  *line_pix = pixt1;
  return boxa;
#else
  return NULL;
#endif
}
示例#2
0
/*!
 *  pixGenHalftoneMask()
 *
 *      Input:  pixs (1 bpp, assumed to be 150 to 200 ppi)
 *              &pixtext (<optional return> text part of pixs)
 *              &htfound (<optional return> 1 if the mask is not empty)
 *              debug (flag: 1 for debug output)
 *      Return: pixd (halftone mask), or null on error
 */
PIX *
pixGenHalftoneMask(PIX      *pixs,
                   PIX     **ppixtext,
                   l_int32  *phtfound,
                   l_int32   debug)
{
l_int32  empty;
PIX     *pixt1, *pixt2, *pixhs, *pixhm, *pixd;

    PROCNAME("pixGenHalftoneMask");

    if (ppixtext) *ppixtext = NULL;
    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 1)
        return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);

        /* Compute seed for halftone parts at 8x reduction */
    pixt1 = pixReduceRankBinaryCascade(pixs, 4, 4, 3, 0);
    pixt2 = pixOpenBrick(NULL, pixt1, 5, 5);
    pixhs = pixExpandReplicate(pixt2, 8);  /* back to 2x reduction */
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixDisplayWriteFormat(pixhs, debug, IFF_PNG);

        /* Compute mask for connected regions */
    pixhm = pixCloseSafeBrick(NULL, pixs, 4, 4);
    pixDisplayWriteFormat(pixhm, debug, IFF_PNG);

        /* Fill seed into mask to get halftone mask */
    pixd = pixSeedfillBinary(NULL, pixhs, pixhm, 4);

#if 0
        /* Moderate opening to remove thin lines, etc. */
    pixOpenBrick(pixd, pixd, 10, 10);
    pixDisplayWrite(pixd, debug);
#endif

        /* Check if mask is empty */
    pixZero(pixd, &empty);
    if (phtfound) {
        *phtfound = 0;
        if (!empty)
            *phtfound = 1;
    }

        /* Optionally, get all pixels that are not under the halftone mask */
    if (ppixtext) {
        if (empty)
            *ppixtext = pixCopy(NULL, pixs);
        else
            *ppixtext = pixSubtract(NULL, pixs, pixd);
        pixDisplayWriteFormat(*ppixtext, debug, IFF_PNG);
    }

    pixDestroy(&pixhs);
    pixDestroy(&pixhm);
    return pixd;
}
示例#3
0
// Get a set of bounding boxes of possible horizontal lines in the image.
// The input resolution overrides any resolution set in src_pix.
// The output line_pix contains just all the detected lines.
// The output boxes undergo the transformation (x,y)->(height-y,x) so the
// lines can be found with a vertical line finder afterwards.
// This transformation allows a simple x/y flip to reverse it in tesseract
// coordinates and it is faster to flip the lines than rotate the image.
Boxa* LineFinder::GetHLineBoxes(int resolution, Pix* src_pix, Pix** line_pix) {
#ifdef HAVE_LIBLEPT
  // Remove any parts of 1 inch/kThinLineFraction high or more, by opening
  // away the thin lines and subtracting what's left.
  // This is very generous and will leave in even quite wide lines.
  Pix* pixt1 = pixOpenBrick(NULL, src_pix, 1, resolution / kThinLineFraction);
  pixSubtract(pixt1, src_pix, pixt1);
  // Spread vertically to allow for some skew.
  Pix* pixt2 = pixDilateBrick(NULL, pixt1, 1, 3);
  // Now keep only wide stuff of width at least 1 inch/kMinLineLengthFraction.
  pixOpenBrick(pixt1, pixt2, resolution / kMinLineLengthFraction, 1);
  pixDestroy(&pixt2);
  // Put a single pixel crack in every line at an arbitrary spacing,
  // so they break up and the bounding boxes can be used to get the
  // direction accurately enough without needing outlines.
  int wpl = pixGetWpl(pixt1);
  int width = pixGetWidth(pixt1);
  int height = pixGetHeight(pixt1);
  l_uint32* data = pixGetData(pixt1);
  for (int y = 0; y < height; ++y, data += wpl) {
    for (int x = kCrackSpacing; x < width; x += kCrackSpacing) {
      CLEAR_DATA_BIT(data, x);
    }
  }
  if (textord_tabfind_show_vlines)
    pixWrite("hlines.png", pixt1, IFF_PNG);
  Boxa* boxa = pixConnComp(pixt1, NULL, 8);
  *line_pix = pixt1;

  // Iterate the boxes to flip x and y.
  int nboxes = boxaGetCount(boxa);
  for (int i = 0; i < nboxes; ++i) {
    l_int32 x, y, box_width, box_height;
    boxaGetBoxGeometry(boxa, i, &x, &y, &box_width, &box_height);
    Box* box = boxCreate(height - (y + box_height),
                         width - (x + box_width), box_height, box_width);
    boxaReplaceBox(boxa, i, box);
  }
  return boxa;
#else
  return NULL;
#endif
}
示例#4
0
main(int    argc,
     char **argv)
{
l_int32      i;
PIX         *pixs, *pixt1, *pixt2, *pixt3, *pixd;
PIXA        *pixa;
static char  mainName[] = "livre_seedgen";

    pixs = pixRead("pageseg2.tif");

    startTimer();
    for (i = 0; i < 100; i++) {
        pixt1 = pixReduceRankBinaryCascade(pixs, 1, 4, 4, 3);
        pixDestroy(&pixt1);
    }
    fprintf(stderr, "Time: %8.4f sec\n", stopTimer() / 100.);

        /* 4 2x rank reductions (levels 1, 4, 4, 3), followed by 5x5 opening */
    pixDisplayWrite(NULL, -1);
    pixDisplayWriteFormat(pixs, 4, IFF_PNG);
    pixt1 = pixReduceRankBinaryCascade(pixs, 1, 4, 0, 0);
    pixDisplayWriteFormat(pixt1, 1, IFF_PNG);
    pixt2 = pixReduceRankBinaryCascade(pixt1, 4, 3, 0, 0);
    pixDisplayWriteFormat(pixt2, 1, IFF_PNG);
    pixOpenBrick(pixt2, pixt2, 5, 5);
    pixt3 = pixExpandBinaryReplicate(pixt2, 2);
    pixDisplayWriteFormat(pixt3, 1, IFF_PNG);

        /* Generate the output image */
    pixa = pixaReadFiles("/tmp", "junk_write_display");
    pixd = pixaDisplayTiledAndScaled(pixa, 8, 250, 4, 0, 25, 2);
    pixWrite("/tmp/seedgen.png", pixd, IFF_PNG);

    pixDestroy(&pixs);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixDestroy(&pixt3);
    pixDestroy(&pixd);
    pixaDestroy(&pixa);
    return 0;
}
示例#5
0
l_int32
DoComparisonDwa2(L_REGPARAMS  *rp,
                 PIX          *pixs,
                 PIX          *pix1,
                 PIX          *pix2,
                 PIX          *pix3,
                 PIX          *pix4,
                 PIX          *pix5,
                 PIX          *pix6,
                 l_int32       size)  /* exactly decomposable */
{
    fprintf(stderr, "..%d..", size);

    if (TIMING) startTimer();
    pixDilateCompBrickExtendDwa(pix1, pixs, size, 1);
    pixDilateCompBrickExtendDwa(pix3, pixs, 1, size);
    pixDilateCompBrickExtendDwa(pix5, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Dwa: %7.3f sec\n", stopTimer());
    if (TIMING) startTimer();
    pixDilateBrick(pix2, pixs, size, 1);
    pixDilateBrick(pix4, pixs, 1, size);
    pixDilateBrick(pix6, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Rop: %7.3f sec\n", stopTimer());
    PixCompareDwa(rp, size, "dilate", pix1, pix2, pix3, pix4, pix5, pix6);

    if (TIMING) startTimer();
    pixErodeCompBrickExtendDwa(pix1, pixs, size, 1);
    pixErodeCompBrickExtendDwa(pix3, pixs, 1, size);
    pixErodeCompBrickExtendDwa(pix5, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Dwa: %7.3f sec\n", stopTimer());
    if (TIMING) startTimer();
    pixErodeBrick(pix2, pixs, size, 1);
    pixErodeBrick(pix4, pixs, 1, size);
    pixErodeBrick(pix6, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Rop: %7.3f sec\n", stopTimer());
    PixCompareDwa(rp, size, "erode", pix1, pix2, pix3, pix4, pix5, pix6);

    if (TIMING) startTimer();
    pixOpenCompBrickExtendDwa(pix1, pixs, size, 1);
    pixOpenCompBrickExtendDwa(pix3, pixs, 1, size);
    pixOpenCompBrickExtendDwa(pix5, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Dwa: %7.3f sec\n", stopTimer());
    if (TIMING) startTimer();
    pixOpenBrick(pix2, pixs, size, 1);
    pixOpenBrick(pix4, pixs, 1, size);
    pixOpenBrick(pix6, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Rop: %7.3f sec\n", stopTimer());
    PixCompareDwa(rp, size, "open", pix1, pix2, pix3, pix4, pix5, pix6);

    if (TIMING) startTimer();
    pixCloseCompBrickExtendDwa(pix1, pixs, size, 1);
    pixCloseCompBrickExtendDwa(pix3, pixs, 1, size);
    pixCloseCompBrickExtendDwa(pix5, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Dwa: %7.3f sec\n", stopTimer());
    if (TIMING) startTimer();
    pixCloseSafeBrick(pix2, pixs, size, 1);
    pixCloseSafeBrick(pix4, pixs, 1, size);
    pixCloseSafeBrick(pix6, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Rop: %7.3f sec\n", stopTimer());
    PixCompareDwa(rp, size, "close", pix1, pix2, pix3, pix4, pix5, pix6);

    return 0;
}
示例#6
0
l_int32
DoComparisonDwa2(PIX     *pixs,
                 PIX     *pixt1,
                 PIX     *pixt2,
                 PIX     *pixt3,
                 PIX     *pixt4,
                 PIX     *pixt5,
                 PIX     *pixt6,
                 l_int32  size)  /* exactly decomposable */
{
    fprintf(stderr, "..%d..", size);

    if (TIMING) startTimer();
    pixDilateCompBrickExtendDwa(pixt1, pixs, size, 1);
    pixDilateCompBrickExtendDwa(pixt3, pixs, 1, size);
    pixDilateCompBrickExtendDwa(pixt5, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Dwa: %7.3f sec\n", stopTimer());
    if (TIMING) startTimer();
    pixDilateBrick(pixt2, pixs, size, 1);
    pixDilateBrick(pixt4, pixs, 1, size);
    pixDilateBrick(pixt6, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Rop: %7.3f sec\n", stopTimer());
    PixCompareDwa(size, "dilate", pixt1, pixt2, pixt3, pixt4, pixt5, pixt6);

    if (TIMING) startTimer();
    pixErodeCompBrickExtendDwa(pixt1, pixs, size, 1);
    pixErodeCompBrickExtendDwa(pixt3, pixs, 1, size);
    pixErodeCompBrickExtendDwa(pixt5, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Dwa: %7.3f sec\n", stopTimer());
    if (TIMING) startTimer();
    pixErodeBrick(pixt2, pixs, size, 1);
    pixErodeBrick(pixt4, pixs, 1, size);
    pixErodeBrick(pixt6, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Rop: %7.3f sec\n", stopTimer());
    PixCompareDwa(size, "erode", pixt1, pixt2, pixt3, pixt4, pixt5, pixt6);

    if (TIMING) startTimer();
    pixOpenCompBrickExtendDwa(pixt1, pixs, size, 1);
    pixOpenCompBrickExtendDwa(pixt3, pixs, 1, size);
    pixOpenCompBrickExtendDwa(pixt5, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Dwa: %7.3f sec\n", stopTimer());
    if (TIMING) startTimer();
    pixOpenBrick(pixt2, pixs, size, 1);
    pixOpenBrick(pixt4, pixs, 1, size);
    pixOpenBrick(pixt6, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Rop: %7.3f sec\n", stopTimer());
    PixCompareDwa(size, "open", pixt1, pixt2, pixt3, pixt4, pixt5, pixt6);

    if (TIMING) startTimer();
    pixCloseCompBrickExtendDwa(pixt1, pixs, size, 1);
    pixCloseCompBrickExtendDwa(pixt3, pixs, 1, size);
    pixCloseCompBrickExtendDwa(pixt5, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Dwa: %7.3f sec\n", stopTimer());
    if (TIMING) startTimer();
    pixCloseSafeBrick(pixt2, pixs, size, 1);
    pixCloseSafeBrick(pixt4, pixs, 1, size);
    pixCloseSafeBrick(pixt6, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Rop: %7.3f sec\n", stopTimer());
    PixCompareDwa(size, "close", pixt1, pixt2, pixt3, pixt4, pixt5, pixt6);

#if 0
    pixWrite("/tmp/junkpixt3.png", pixt3, IFF_PNG);
    pixWrite("/tmp/junkpixt4.png", pixt4, IFF_PNG);
    pixXor(pixt3, pixt3, pixt4);
    pixWrite("/tmp/junkxor.png", pixt3, IFF_PNG);
#endif

    return 0;
}
示例#7
0
int main(int    argc,
         char **argv)
{
char          bufname[256];
l_int32       i, w, h;
l_float32    *mat1, *mat2, *mat3, *mat1i, *mat2i, *mat3i, *matdinv;
l_float32     matd[9], matdi[9];
BOXA         *boxa, *boxa2;
PIX          *pix, *pixs, *pixb, *pixg, *pixc, *pixcs;
PIX          *pixd, *pix1, *pix2, *pix3;
PIXA         *pixa;
PTA          *ptas, *ptad;
L_REGPARAMS  *rp;

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

    pix = pixRead("feyn.tif");
    pixs = pixScale(pix, 0.22, 0.22);
    pixDestroy(&pix);

#if ALL
        /* Test invertability of sequential. */
    fprintf(stderr, "Test invertability of sequential\n");
    pixa = pixaCreate(0);
    for (i = 0; i < 3; i++) {
        pixb = pixAddBorder(pixs, ADDED_BORDER_PIXELS, 0);
        MakePtas(i, &ptas, &ptad);
        pix1 = pixAffineSequential(pixb, ptad, ptas, 0, 0);
        regTestWritePixAndCheck(rp, pix1, IFF_PNG);  /* 0,3,6 */
        pixaAddPix(pixa, pix1, L_INSERT);
        pix2 = pixAffineSequential(pix1, ptas, ptad, 0, 0);
        regTestWritePixAndCheck(rp, pix2, IFF_PNG);  /* 1,4,7 */
        pixaAddPix(pixa, pix2, L_INSERT);
        pixd = pixRemoveBorder(pix2, ADDED_BORDER_PIXELS);
        pixXor(pixd, pixd, pixs);
        regTestWritePixAndCheck(rp, pixd, IFF_PNG);  /* 2,5,8 */
        pixaAddPix(pixa, pixd, L_INSERT);
        pixDestroy(&pixb);
        ptaDestroy(&ptas);
        ptaDestroy(&ptad);
    }

    pix1 = pixaDisplayTiledInColumns(pixa, 3, 1.0, 20, 3);
    pix2 = pixScaleToGray(pix1, 0.2);
    regTestWritePixAndCheck(rp, pix2, IFF_PNG);  /* 9 */
    pixDisplayWithTitle(pix2, 0, 100, NULL, rp->display);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixaDestroy(&pixa);
#endif

#if ALL
        /* Test invertability of sampling */
    fprintf(stderr, "Test invertability of sampling\n");
    pixa = pixaCreate(0);
    for (i = 0; i < 3; i++) {
        pixb = pixAddBorder(pixs, ADDED_BORDER_PIXELS, 0);
        MakePtas(i, &ptas, &ptad);
        pix1 = pixAffineSampledPta(pixb, ptad, ptas, L_BRING_IN_WHITE);
        regTestWritePixAndCheck(rp, pix1, IFF_PNG);  /* 10,13,16 */
        pixaAddPix(pixa, pix1, L_INSERT);
        pix2 = pixAffineSampledPta(pix1, ptas, ptad, L_BRING_IN_WHITE);
        regTestWritePixAndCheck(rp, pix2, IFF_PNG);  /* 11,14,17 */
        pixaAddPix(pixa, pix2, L_INSERT);
        pixd = pixRemoveBorder(pix2, ADDED_BORDER_PIXELS);
        pixXor(pixd, pixd, pixs);
        regTestWritePixAndCheck(rp, pixd, IFF_PNG);  /* 12,15,18 */
        pixaAddPix(pixa, pixd, L_INSERT);
        pixDestroy(&pixb);
        ptaDestroy(&ptas);
        ptaDestroy(&ptad);
    }

    pix1 = pixaDisplayTiledInColumns(pixa, 3, 1.0, 20, 3);
    pix2 = pixScaleToGray(pix1, 0.2);
    regTestWritePixAndCheck(rp, pix2, IFF_PNG);  /* 19 */
    pixDisplayWithTitle(pix2, 200, 100, NULL, rp->display);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pixs);
    pixaDestroy(&pixa);
#endif

#if ALL
        /* Test invertability of interpolation on grayscale */
    fprintf(stderr, "Test invertability of grayscale interpolation\n");
    pix = pixRead("feyn.tif");
    pixg = pixScaleToGray3(pix);
    pixDestroy(&pix);
    pixa = pixaCreate(0);
    for (i = 0; i < 3; i++) {
        pixb = pixAddBorder(pixg, ADDED_BORDER_PIXELS / 3, 255);
        MakePtas(i, &ptas, &ptad);
        pix1 = pixAffinePta(pixb, ptad, ptas, L_BRING_IN_WHITE);
        regTestWritePixAndCheck(rp, pix1, IFF_JFIF_JPEG);  /* 20,23,26 */
        pixaAddPix(pixa, pix1, L_INSERT);
        pix2 = pixAffinePta(pix1, ptas, ptad, L_BRING_IN_WHITE);
        regTestWritePixAndCheck(rp, pix2, IFF_JFIF_JPEG);  /* 21,24,27 */
        pixaAddPix(pixa, pix2, L_INSERT);
        pixd = pixRemoveBorder(pix2, ADDED_BORDER_PIXELS / 3);
        pixXor(pixd, pixd, pixg);
        pixInvert(pixd, pixd);
        regTestWritePixAndCheck(rp, pixd, IFF_JFIF_JPEG);  /* 22,25,28 */
        pixaAddPix(pixa, pixd, L_INSERT);
        pixDestroy(&pixb);
        ptaDestroy(&ptas);
        ptaDestroy(&ptad);
    }

    pix1 = pixaDisplayTiledInColumns(pixa, 3, 1.0, 20, 3);
    pix2 = pixScale(pix1, 0.2, 0.2);
    regTestWritePixAndCheck(rp, pix2, IFF_JFIF_JPEG);  /* 29 */
    pixDisplayWithTitle(pix2, 400, 100, NULL, rp->display);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pixg);
    pixaDestroy(&pixa);
#endif

#if ALL
        /* Test invertability of interpolation on color */
    fprintf(stderr, "Test invertability of color interpolation\n");
    pixa = pixaCreate(0);
    pixc = pixRead("test24.jpg");
    pixcs = pixScale(pixc, 0.3, 0.3);
    for (i = 0; i < 3; i++) {
        pixb = pixAddBorder(pixcs, ADDED_BORDER_PIXELS / 4, 0xffffff00);
        MakePtas(i, &ptas, &ptad);
        pix1 = pixAffinePta(pixb, ptad, ptas, L_BRING_IN_WHITE);
        regTestWritePixAndCheck(rp, pix1, IFF_JFIF_JPEG);  /* 30,33,36 */
        pixaAddPix(pixa, pix1, L_INSERT);
        pix2 = pixAffinePta(pix1, ptas, ptad, L_BRING_IN_WHITE);
        regTestWritePixAndCheck(rp, pix2, IFF_JFIF_JPEG);  /* 31,34,37 */
        pixaAddPix(pixa, pix2, L_INSERT);
        pixd = pixRemoveBorder(pix2, ADDED_BORDER_PIXELS / 4);
        pixXor(pixd, pixd, pixcs);
        pixInvert(pixd, pixd);
        regTestWritePixAndCheck(rp, pixd, IFF_JFIF_JPEG);  /* 32,35,38 */
        pixaAddPix(pixa, pixd, L_INSERT);
        pixDestroy(&pixb);
        ptaDestroy(&ptas);
        ptaDestroy(&ptad);
    }

    pix1 = pixaDisplayTiledInColumns(pixa, 3, 1.0, 20, 3);
    pix2 = pixScale(pix1, 0.25, 0.25);
    regTestWritePixAndCheck(rp, pix2, IFF_JFIF_JPEG);  /* 39 */
    pixDisplayWithTitle(pix2, 600, 100, NULL, rp->display);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pixc);
    pixaDestroy(&pixa);
#endif

#if ALL
       /* Comparison between sequential and sampling */
    fprintf(stderr, "Compare sequential with sampling\n");
    pix = pixRead("feyn.tif");
    pixs = pixScale(pix, 0.22, 0.22);
    pixDestroy(&pix);

    MakePtas(3, &ptas, &ptad);
    pixa = pixaCreate(0);

        /* Use sequential transforms */
    pix1 = pixAffineSequential(pixs, ptas, ptad,
                     ADDED_BORDER_PIXELS, ADDED_BORDER_PIXELS);
    regTestWritePixAndCheck(rp, pix1, IFF_PNG);  /* 40 */
    pixaAddPix(pixa, pix1, L_INSERT);

        /* Use sampled transform */
    pix2 = pixAffineSampledPta(pixs, ptas, ptad, L_BRING_IN_WHITE);
    regTestWritePixAndCheck(rp, pix2, IFF_PNG);  /* 41 */
    pixaAddPix(pixa, pix2, L_COPY);

        /* Compare the results */
    pixXor(pix2, pix2, pix1);
    regTestWritePixAndCheck(rp, pix2, IFF_PNG);  /* 42 */
    pixaAddPix(pixa, pix2, L_INSERT);

    pix1 = pixaDisplayTiledInColumns(pixa, 3, 1.0, 20, 3);
    pix2 = pixScale(pix1, 0.5, 0.5);
    regTestWritePixAndCheck(rp, pix2, IFF_PNG);  /* 43 */
    pixDisplayWithTitle(pix2, 800, 100, NULL, rp->display);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pixs);
    pixaDestroy(&pixa);
    ptaDestroy(&ptas);
    ptaDestroy(&ptad);
#endif


#if ALL
       /* Test with large distortion */
    fprintf(stderr, "Test with large distortion\n");
    MakePtas(4, &ptas, &ptad);
    pixa = pixaCreate(0);
    pix = pixRead("feyn.tif");
    pixg = pixScaleToGray6(pix);
    pixDestroy(&pix);

    pix1 = pixAffineSequential(pixg, ptas, ptad, 0, 0);
    regTestWritePixAndCheck(rp, pix1, IFF_PNG);  /* 44 */
    pixaAddPix(pixa, pix1, L_COPY);

    pix2 = pixAffineSampledPta(pixg, ptas, ptad, L_BRING_IN_WHITE);
    regTestWritePixAndCheck(rp, pix2, IFF_PNG);  /* 45 */
    pixaAddPix(pixa, pix2, L_COPY);

    pix3 = pixAffinePta(pixg, ptas, ptad, L_BRING_IN_WHITE);
    regTestWritePixAndCheck(rp, pix3, IFF_PNG);  /* 46 */
    pixaAddPix(pixa, pix3, L_INSERT);

    pixXor(pix1, pix1, pix2);
    pixInvert(pix1, pix1);
    regTestWritePixAndCheck(rp, pix1, IFF_PNG);  /* 47 */
    pixaAddPix(pixa, pix1, L_INSERT);
    pixXor(pix2, pix2, pix3);
    pixInvert(pix2, pix2);
    regTestWritePixAndCheck(rp, pix2, IFF_PNG);  /* 48 */
    pixaAddPix(pixa, pix2, L_INSERT);

    pix1 = pixaDisplayTiledInColumns(pixa, 5, 1.0, 20, 3);
    pix2 = pixScale(pix1, 0.8, 0.8);
    regTestWritePixAndCheck(rp, pix2, IFF_PNG);  /* 49 */
    pixDisplayWithTitle(pix2, 1000, 100, NULL, rp->display);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pixg);
    pixaDestroy(&pixa);
    ptaDestroy(&ptas);
    ptaDestroy(&ptad);
#endif

#if ALL 
        /* Set up pix and boxa */
    fprintf(stderr, "Test affine transforms and inverses on pix and boxa\n");
    pixa = pixaCreate(0);
    pix = pixRead("lucasta.1.300.tif");
    pixTranslate(pix, pix, 70, 0, L_BRING_IN_WHITE);
    pix1 = pixCloseBrick(NULL, pix, 14, 5);
    pixOpenBrick(pix1, pix1, 1, 2);
    boxa = pixConnComp(pix1, NULL, 8);
    pixs = pixConvertTo32(pix);
    pixGetDimensions(pixs, &w, &h, NULL);
    pixc = pixCopy(NULL, pixs);
    RenderHashedBoxa(pixc, boxa, 113);
    regTestWritePixAndCheck(rp, pixc, IFF_PNG);  /* 50 */
    pixaAddPix(pixa, pixc, L_INSERT);
    pixDestroy(&pix);
    pixDestroy(&pix1);

        /* Set up an affine transform in matd, and apply it to boxa */
    mat1 = createMatrix2dTranslate(SHIFTX, SHIFTY);
    mat2 = createMatrix2dScale(SCALEX, SCALEY);
    mat3 = createMatrix2dRotate(w / 2, h / 2, ROTATION);
    l_productMat3(mat3, mat2, mat1, matd, 3);
    boxa2 = boxaAffineTransform(boxa, matd);

        /* Set up the inverse transform --> matdi */
    mat1i = createMatrix2dTranslate(-SHIFTX, -SHIFTY);
    mat2i = createMatrix2dScale(1.0/ SCALEX, 1.0 / SCALEY);
    mat3i = createMatrix2dRotate(w / 2, h / 2, -ROTATION);
    l_productMat3(mat1i, mat2i, mat3i, matdi, 3);

        /* Invert the original affine transform --> matdinv */
    affineInvertXform(matd, &matdinv);
    if (rp->display) { 
        fprintf(stderr, "  Affine transform, applied to boxa\n");
        for (i = 0; i < 9; i++) {
            if (i && (i % 3 == 0))  fprintf(stderr, "\n");
            fprintf(stderr, "   %7.3f ", matd[i]);
        }
        fprintf(stderr, "\n  Inverse transform, by composing inverse parts");
        for (i = 0; i < 9; i++) {
            if (i % 3 == 0)  fprintf(stderr, "\n");
            fprintf(stderr, "   %7.3f ", matdi[i]);
        }
        fprintf(stderr, "\n  Inverse transform, by inverting affine xform");
        for (i = 0; i < 6; i++) {
            if (i % 3 == 0)  fprintf(stderr, "\n");
            fprintf(stderr, "   %7.3f ", matdinv[i]);
        }
        fprintf(stderr, "\n");
    }

        /* Apply the inverted affine transform --> pixs */
    pixd = pixAffine(pixs, matdinv, L_BRING_IN_WHITE);
    RenderHashedBoxa(pixd, boxa2, 513);
    regTestWritePixAndCheck(rp, pixd, IFF_PNG);  /* 51 */
    pixaAddPix(pixa, pixd, L_INSERT);

    pix1 = pixaDisplayTiledInColumns(pixa, 2, 1.0, 30, 2);
    regTestWritePixAndCheck(rp, pix1, IFF_PNG);  /* 52 */
    pixDisplayWithTitle(pix1, 1200, 100, NULL, rp->display);
    pixDestroy(&pix1);
    pixaDestroy(&pixa);

    pixDestroy(&pixs);
    boxaDestroy(&boxa);
    boxaDestroy(&boxa2);
    lept_free(mat1);
    lept_free(mat2);
    lept_free(mat3);
    lept_free(mat1i);
    lept_free(mat2i);
    lept_free(mat3i);
    lept_free(matdinv);
#endif

    return regTestCleanup(rp);
}
示例#8
0
l_int32
DoPageSegmentation(PIX     *pixs,   /* should be at least 300 ppi */
                   l_int32  which)  /* 1, 2, 3, 4 */
{
char         buf[256];
l_int32      zero;
BOXA        *boxatm, *boxahm;
PIX         *pixr;   /* image reduced to 150 ppi */
PIX         *pixhs;  /* image of halftone seed, 150 ppi */
PIX         *pixm;   /* image of mask of components, 150 ppi */
PIX         *pixhm1; /* image of halftone mask, 150 ppi */
PIX         *pixhm2; /* image of halftone mask, 300 ppi */
PIX         *pixht;  /* image of halftone components, 150 ppi */
PIX         *pixnht; /* image without halftone components, 150 ppi */
PIX         *pixi;   /* inverted image, 150 ppi */
PIX         *pixvws; /* image of vertical whitespace, 150 ppi */
PIX         *pixm1;  /* image of closed textlines, 150 ppi */
PIX         *pixm2;  /* image of refined text line mask, 150 ppi */
PIX         *pixm3;  /* image of refined text line mask, 300 ppi */
PIX         *pixb1;  /* image of text block mask, 150 ppi */
PIX         *pixb2;  /* image of text block mask, 300 ppi */
PIX         *pixnon; /* image of non-text or halftone, 150 ppi */
PIX         *pix1, *pix2, *pix3, *pix4;
PIXA        *pixa;
PIXCMAP     *cmap;
PTAA        *ptaa;
l_int32      ht_flag = 0;
l_int32      ws_flag = 0;
l_int32      text_flag = 0;
l_int32      block_flag = 0;

    PROCNAME("DoPageSegmentation");

    if (which == 1)
        ht_flag = 1;
    else if (which == 2)
        ws_flag = 1;
    else if (which == 3)
        text_flag = 1;
    else if (which == 4)
        block_flag = 1;
    else
        return ERROR_INT("invalid parameter: not in [1...4]", procName, 1);

    pixa = pixaCreate(0);
    lept_mkdir("lept/livre");

        /* Reduce to 150 ppi */
    pix1 = pixScaleToGray2(pixs);
    if (ws_flag || ht_flag || block_flag) pixaAddPix(pixa, pix1, L_COPY);
    if (which == 1)
        pixWrite("/tmp/lept/livre/orig.gray.150.png", pix1, IFF_PNG);
    pixDestroy(&pix1);
    pixr = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0);

        /* Get seed for halftone parts */
    pix1 = pixReduceRankBinaryCascade(pixr, 4, 4, 3, 0);
    pix2 = pixOpenBrick(NULL, pix1, 5, 5);
    pixhs = pixExpandBinaryPower2(pix2, 8);
    if (ht_flag) pixaAddPix(pixa, pixhs, L_COPY);
    if (which == 1)
        pixWrite("/tmp/lept/livre/htseed.150.png", pixhs, IFF_PNG);
    pixDestroy(&pix1);
    pixDestroy(&pix2);

        /* Get mask for connected regions */
    pixm = pixCloseSafeBrick(NULL, pixr, 4, 4);
    if (ht_flag) pixaAddPix(pixa, pixm, L_COPY);
    if (which == 1)
        pixWrite("/tmp/lept/livre/ccmask.150.png", pixm, IFF_PNG);

        /* Fill seed into mask to get halftone mask */
    pixhm1 = pixSeedfillBinary(NULL, pixhs, pixm, 4);
    if (ht_flag) pixaAddPix(pixa, pixhm1, L_COPY);
    if (which == 1) pixWrite("/tmp/lept/livre/htmask.150.png", pixhm1, IFF_PNG);
    pixhm2 = pixExpandBinaryPower2(pixhm1, 2);

        /* Extract halftone stuff */
    pixht = pixAnd(NULL, pixhm1, pixr);
    if (which == 1) pixWrite("/tmp/lept/livre/ht.150.png", pixht, IFF_PNG);

        /* Extract non-halftone stuff */
    pixnht = pixXor(NULL, pixht, pixr);
    if (text_flag) pixaAddPix(pixa, pixnht, L_COPY);
    if (which == 1) pixWrite("/tmp/lept/livre/text.150.png", pixnht, IFF_PNG);
    pixZero(pixht, &zero);
    if (zero)
        fprintf(stderr, "No halftone parts found\n");
    else
        fprintf(stderr, "Halftone parts found\n");

        /* Get bit-inverted image */
    pixi = pixInvert(NULL, pixnht);
    if (ws_flag) pixaAddPix(pixa, pixi, L_COPY);
    if (which == 1) pixWrite("/tmp/lept/livre/invert.150.png", pixi, IFF_PNG);

        /* The whitespace mask will break textlines where there
         * is a large amount of white space below or above.
         * We can prevent this by identifying regions of the
         * inverted image that have large horizontal (bigger than
         * the separation between columns) and significant
         * vertical extent (bigger than the separation between
         * textlines), and subtracting this from the whitespace mask. */
    pix1 = pixMorphCompSequence(pixi, "o80.60", 0);
    pix2 = pixSubtract(NULL, pixi, pix1);
    if (ws_flag) pixaAddPix(pixa, pix2, L_COPY);
    pixDestroy(&pix1);

        /* Identify vertical whitespace by opening inverted image */
    pix3 = pixOpenBrick(NULL, pix2, 5, 1);  /* removes thin vertical lines */
    pixvws = pixOpenBrick(NULL, pix3, 1, 200);  /* gets long vertical lines */
    if (text_flag || ws_flag) pixaAddPix(pixa, pixvws, L_COPY);
    if (which == 1) pixWrite("/tmp/lept/livre/vertws.150.png", pixvws, IFF_PNG);
    pixDestroy(&pix2);
    pixDestroy(&pix3);

        /* Get proto (early processed) text line mask. */
        /* First close the characters and words in the textlines */
    pixm1 = pixCloseSafeBrick(NULL, pixnht, 30, 1);
    if (text_flag) pixaAddPix(pixa, pixm1, L_COPY);
    if (which == 1)
        pixWrite("/tmp/lept/livre/textmask1.150.png", pixm1, IFF_PNG);

        /* Next open back up the vertical whitespace corridors */
    pixm2 = pixSubtract(NULL, pixm1, pixvws);
    if (which == 1)
        pixWrite("/tmp/lept/livre/textmask2.150.png", pixm2, IFF_PNG);

        /* Do a small opening to remove noise */
    pixOpenBrick(pixm2, pixm2, 3, 3);
    if (text_flag) pixaAddPix(pixa, pixm2, L_COPY);
    if (which == 1)
         pixWrite("/tmp/lept/livre/textmask3.150.png", pixm2, IFF_PNG);
    pixm3 = pixExpandBinaryPower2(pixm2, 2);

        /* Join pixels vertically to make text block mask */
    pixb1 = pixMorphSequence(pixm2, "c1.10 + o4.1", 0);
    if (block_flag) pixaAddPix(pixa, pixb1, L_COPY);
    if (which == 1)
        pixWrite("/tmp/lept/livre/textblock1.150.png", pixb1, IFF_PNG);

        /* Solidify the textblock mask and remove noise:
         *  (1) For each c.c., close the blocks and dilate slightly
         *      to form a solid mask.
         *  (2) Small horizontal closing between components
         *  (3) Open the white space between columns, again
         *  (4) Remove small components */
    pix1 = pixMorphSequenceByComponent(pixb1, "c30.30 + d3.3", 8, 0, 0, NULL);
    pixCloseSafeBrick(pix1, pix1, 10, 1);
    if (block_flag) pixaAddPix(pixa, pix1, L_COPY);
    pix2 = pixSubtract(NULL, pix1, pixvws);
    pix3 = pixSelectBySize(pix2, 25, 5, 8, L_SELECT_IF_BOTH,
                            L_SELECT_IF_GTE, NULL);
    if (block_flag) pixaAddPix(pixa, pix3, L_COPY);
    if (which == 1)
        pixWrite("/tmp/lept/livre/textblock2.150.png", pix3, IFF_PNG);
    pixb2 = pixExpandBinaryPower2(pix3, 2);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pix3);

        /* Identify the outlines of each textblock */
    ptaa = pixGetOuterBordersPtaa(pixb2);
    pix1 = pixRenderRandomCmapPtaa(pixb2, ptaa, 1, 8, 1);
    cmap = pixGetColormap(pix1);
    pixcmapResetColor(cmap, 0, 130, 130, 130);  /* set interior to gray */
    if (which == 1)
        pixWrite("/tmp/lept/livre/textblock3.300.png", pix1, IFF_PNG);
    pixDisplayWithTitle(pix1, 480, 360, "textblock mask with outlines", DFLAG);
    ptaaDestroy(&ptaa);
    pixDestroy(&pix1);

        /* Fill line mask (as seed) into the original */
    pix1 = pixSeedfillBinary(NULL, pixm3, pixs, 8);
    pixOr(pixm3, pixm3, pix1);
    pixDestroy(&pix1);
    if (which == 1)
        pixWrite("/tmp/lept/livre/textmask.300.png", pixm3, IFF_PNG);
    pixDisplayWithTitle(pixm3, 480, 360, "textline mask 4", DFLAG);

        /* Fill halftone mask (as seed) into the original */
    pix1 = pixSeedfillBinary(NULL, pixhm2, pixs, 8);
    pixOr(pixhm2, pixhm2, pix1);
    pixDestroy(&pix1);
    if (which == 1)
        pixWrite("/tmp/lept/livre/htmask.300.png", pixhm2, IFF_PNG);
    pixDisplayWithTitle(pixhm2, 520, 390, "halftonemask 2", DFLAG);

        /* Find objects that are neither text nor halftones */
    pix1 = pixSubtract(NULL, pixs, pixm3);  /* remove text pixels */
    pixnon = pixSubtract(NULL, pix1, pixhm2);  /* remove halftone pixels */
    pixDestroy(&pix1);
    if (which == 1)
        pixWrite("/tmp/lept/livre/other.300.png", pixnon, IFF_PNG);
    pixDisplayWithTitle(pixnon, 540, 420, "other stuff", DFLAG);

        /* Write out b.b. for text line mask and halftone mask components */
    boxatm = pixConnComp(pixm3, NULL, 4);
    boxahm = pixConnComp(pixhm2, NULL, 8);
    if (which == 1) {
        boxaWrite("/tmp/lept/livre/textmask.boxa", boxatm);
        boxaWrite("/tmp/lept/livre/htmask.boxa", boxahm);
    }

    pix1 = pixaDisplayTiledAndScaled(pixa, 8, 250, 4, 0, 25, 2);
    pixDisplay(pix1, 0, 375 * (which - 1));
    snprintf(buf, sizeof(buf), "/tmp/lept/livre/segout.%d.png", which);
    pixWrite(buf, pix1, IFF_PNG);
    pixDestroy(&pix1);
    pixaDestroy(&pixa);

        /* clean up to test with valgrind */
    pixDestroy(&pixr);
    pixDestroy(&pixhs);
    pixDestroy(&pixm);
    pixDestroy(&pixhm1);
    pixDestroy(&pixhm2);
    pixDestroy(&pixht);
    pixDestroy(&pixi);
    pixDestroy(&pixnht);
    pixDestroy(&pixvws);
    pixDestroy(&pixm1);
    pixDestroy(&pixm2);
    pixDestroy(&pixm3);
    pixDestroy(&pixb1);
    pixDestroy(&pixb2);
    pixDestroy(&pixnon);
    boxaDestroy(&boxatm);
    boxaDestroy(&boxahm);
    return 0;
}
示例#9
0
int main(int    argc,
         char **argv)
{
PIX          *pixs, *pix1, *pix2, *pix3, *pix4;
L_REGPARAMS  *rp;

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

    pixs = pixRead("test1.png");


        /* pixInvert */
    pix1 = pixInvert(NULL, pixs);
    pix2 = pixCreateTemplate(pixs);  /* into pixd of same size */
    pixInvert(pix2, pixs);
    regTestWritePixAndCheck(rp, pix1, IFF_PNG);  /* 0 */
    regTestComparePix(rp, pix1, pix2);  /* 1 */

    pix3 = pixRead("marge.jpg");  /* into pixd of different size */
    pixInvert(pix3, pixs);
    regTestComparePix(rp, pix1, pix3);  /* 2 */
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pix3);

    pix1 = pixOpenBrick(NULL, pixs, 1, 9);
    pix2 = pixDilateBrick(NULL, pixs, 1, 9);

        /* pixOr */
    pix3 = pixCreateTemplate(pixs);
    pixOr(pix3, pixs, pix1);  /* existing */
    pix4 = pixOr(NULL, pixs, pix1);  /* new */
    regTestWritePixAndCheck(rp, pix3, IFF_PNG);  /* 3 */
    regTestComparePix(rp, pix3, pix4);  /* 4 */
    pixCopy(pix4, pix1);
    pixOr(pix4, pix4, pixs);  /* in-place */
    regTestComparePix(rp, pix3, pix4);  /* 5 */
    pixDestroy(&pix3);
    pixDestroy(&pix4);

    pix3 = pixCreateTemplate(pixs);
    pixOr(pix3, pixs, pix2);  /* existing */
    pix4 = pixOr(NULL, pixs, pix2);  /* new */
    regTestWritePixAndCheck(rp, pix3, IFF_PNG);  /* 6 */
    regTestComparePix(rp, pix3, pix4);  /* 7 */
    pixCopy(pix4, pix2);
    pixOr(pix4, pix4, pixs);  /* in-place */
    regTestComparePix(rp, pix3, pix4);  /* 8 */
    pixDestroy(&pix3);
    pixDestroy(&pix4);

        /* pixAnd */
    pix3 = pixCreateTemplate(pixs);
    pixAnd(pix3, pixs, pix1);  /* existing */
    pix4 = pixAnd(NULL, pixs, pix1);  /* new */
    regTestWritePixAndCheck(rp, pix3, IFF_PNG);  /* 9 */
    regTestComparePix(rp, pix3, pix4);  /* 10 */
    pixCopy(pix4, pix1);
    pixAnd(pix4, pix4, pixs);  /* in-place */
    regTestComparePix(rp, pix3, pix4);  /* 11 */
    pixDestroy(&pix3);
    pixDestroy(&pix4);

    pix3 = pixCreateTemplate(pixs);
    pixAnd(pix3, pixs, pix2);  /* existing */
    pix4 = pixAnd(NULL, pixs, pix2);  /* new */
    regTestWritePixAndCheck(rp, pix3, IFF_PNG);  /* 12 */
    regTestComparePix(rp, pix3, pix4);  /* 13 */
    pixCopy(pix4, pix2);
    pixAnd(pix4, pix4, pixs);  /* in-place */
    regTestComparePix(rp, pix3, pix4);  /* 14 */
    pixDestroy(&pix3);
    pixDestroy(&pix4);

        /* pixXor */
    pix3 = pixCreateTemplate(pixs);
    pixXor(pix3, pixs, pix1);  /* existing */
    pix4 = pixXor(NULL, pixs, pix1);  /* new */
    regTestWritePixAndCheck(rp, pix3, IFF_PNG);  /* 15 */
    regTestComparePix(rp, pix3, pix4);  /* 16 */
    pixCopy(pix4, pix1);
    pixXor(pix4, pix4, pixs);  /* in-place */
    regTestComparePix(rp, pix3, pix4);  /* 17 */
    pixDestroy(&pix3);
    pixDestroy(&pix4);

    pix3 = pixCreateTemplate(pixs);
    pixXor(pix3, pixs, pix2);  /* existing */
    pix4 = pixXor(NULL, pixs, pix2);  /* new */
    regTestWritePixAndCheck(rp, pix3, IFF_PNG);  /* 18 */
    regTestComparePix(rp, pix3, pix4);  /* 19 */
    pixCopy(pix4, pix2);
    pixXor(pix4, pix4, pixs);  /* in-place */
    regTestComparePix(rp, pix3, pix4);  /* 20 */
    pixDestroy(&pix3);
    pixDestroy(&pix4);

        /* pixSubtract */
    pix3 = pixCreateTemplate(pixs);
    pixSubtract(pix3, pixs, pix1);  /* existing */
    pix4 = pixSubtract(NULL, pixs, pix1);  /* new */
    regTestWritePixAndCheck(rp, pix3, IFF_PNG);  /* 21 */
    regTestComparePix(rp, pix3, pix4);  /* 22 */
    pixCopy(pix4, pix1);
    pixSubtract(pix4, pixs, pix4);  /* in-place */
    regTestComparePix(rp, pix3, pix4);  /* 23 */
    pixDestroy(&pix3);
    pixDestroy(&pix4);

    pix3 = pixCreateTemplate(pixs);
    pixSubtract(pix3, pixs, pix2);  /* existing */
    pix4 = pixSubtract(NULL, pixs, pix2);  /* new */
    regTestWritePixAndCheck(rp, pix3, IFF_PNG);  /* 24 */
    regTestComparePix(rp, pix3, pix4);  /* 25 */
    pixCopy(pix4, pix2);
    pixSubtract(pix4, pixs, pix4);  /* in-place */
    regTestComparePix(rp, pix3, pix4);  /* 26 */
    pixDestroy(&pix3);
    pixDestroy(&pix4);

    pix4 = pixRead("marge.jpg");
    pixSubtract(pix4, pixs, pixs);  /* subtract from itself; should be empty */
    pix3 = pixCreateTemplate(pixs);
    regTestComparePix(rp, pix3, pix4);  /* 27*/
    pixDestroy(&pix3);
    pixDestroy(&pix4);

    pixSubtract(pixs, pixs, pixs);  /* subtract from itself; should be empty */
    pix3 = pixCreateTemplate(pixs);
    regTestComparePix(rp, pix3, pixs);  /* 28*/
    pixDestroy(&pix3);

    pixDestroy(&pixs);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    return regTestCleanup(rp);
}
示例#10
0
/* morph composite with morph non-composite */
l_int32
DoComparisonDwa1(PIX *pixs,
                 PIX *pixt1,
                 PIX *pixt2,
                 PIX *pixt3,
                 PIX *pixt4,
                 PIX *pixt5,
                 PIX *pixt6,
                 l_int32 isize) {
    l_int32 fact1, fact2, size;

    selectComposableSizes(isize, &fact1, &fact2);
    size = fact1 * fact2;

    fprintf(stderr, "..%d..", size);

    if (TIMING) startTimer();
    pixDilateCompBrick(pixt1, pixs, size, 1);
    pixDilateCompBrick(pixt3, pixs, 1, size);
    pixDilateCompBrick(pixt5, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Dwa: %7.3f sec\n", stopTimer());
    if (TIMING) startTimer();
    pixDilateBrick(pixt2, pixs, size, 1);
    pixDilateBrick(pixt4, pixs, 1, size);
    pixDilateBrick(pixt6, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Rop: %7.3f sec\n", stopTimer());
    PixCompareDwa(size, "dilate", pixt1, pixt2, pixt3, pixt4, pixt5, pixt6);

    if (TIMING) startTimer();
    pixErodeCompBrick(pixt1, pixs, size, 1);
    pixErodeCompBrick(pixt3, pixs, 1, size);
    pixErodeCompBrick(pixt5, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Dwa: %7.3f sec\n", stopTimer());
    if (TIMING) startTimer();
    pixErodeBrick(pixt2, pixs, size, 1);
    pixErodeBrick(pixt4, pixs, 1, size);
    pixErodeBrick(pixt6, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Rop: %7.3f sec\n", stopTimer());
    PixCompareDwa(size, "erode", pixt1, pixt2, pixt3, pixt4, pixt5, pixt6);

    if (TIMING) startTimer();
    pixOpenCompBrick(pixt1, pixs, size, 1);
    pixOpenCompBrick(pixt3, pixs, 1, size);
    pixOpenCompBrick(pixt5, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Dwa: %7.3f sec\n", stopTimer());
    if (TIMING) startTimer();
    pixOpenBrick(pixt2, pixs, size, 1);
    pixOpenBrick(pixt4, pixs, 1, size);
    pixOpenBrick(pixt6, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Rop: %7.3f sec\n", stopTimer());
    PixCompareDwa(size, "open", pixt1, pixt2, pixt3, pixt4, pixt5, pixt6);

#if 1
    pixWrite("/tmp/junko1.png", pixt1, IFF_PNG);
    pixWrite("/tmp/junko2.png", pixt2, IFF_PNG);
    pixXor(pixt1, pixt1, pixt2);
    pixWrite("/tmp/junkoxor.png", pixt1, IFF_PNG);
#endif

#if 0
    pixDisplay(pixt1, 100, 100);
    pixDisplay(pixt2, 800, 100);
    pixWrite("/tmp/junkpixt1.png", pixt1, IFF_PNG);
    pixWrite("/tmp/junkpixt2.png", pixt2, IFF_PNG);
#endif

    if (TIMING) startTimer();
    pixCloseSafeCompBrick(pixt1, pixs, size, 1);
    pixCloseSafeCompBrick(pixt3, pixs, 1, size);
    pixCloseSafeCompBrick(pixt5, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Dwa: %7.3f sec\n", stopTimer());
    if (TIMING) startTimer();
    pixCloseSafeBrick(pixt2, pixs, size, 1);
    pixCloseSafeBrick(pixt4, pixs, 1, size);
    pixCloseSafeBrick(pixt6, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Rop: %7.3f sec\n", stopTimer());
    PixCompareDwa(size, "close", pixt1, pixt2, pixt3, pixt4, pixt5, pixt6);

#if 1
    pixWrite("/tmp/junkc1.png", pixt1, IFF_PNG);
    pixWrite("/tmp/junkc2.png", pixt2, IFF_PNG);
    pixXor(pixt1, pixt1, pixt2);
    pixWrite("/tmp/junkcxor.png", pixt1, IFF_PNG);
#endif

    return 0;
}
示例#11
0
main(int    argc,
     char **argv)
{
l_int32      i, n, ws, hs, w, h, rval, gval, bval, order;
l_float32   *mat1, *mat2, *mat3;
l_float32    matd[9];
BOX         *box, *boxt;
BOXA        *boxa, *boxat, *boxa1, *boxa2, *boxa3, *boxa4, *boxa5;
PIX         *pix, *pixs, *pixb, *pixc, *pixt, *pixt1, *pixt2, *pixt3;
PIXA        *pixa;
static char  mainName[] = "xformbox_reg";

    /* ----------------------------------------------------------- *
     *                Test hash rendering in 3 modes               *
     * ----------------------------------------------------------- */
    pixs = pixRead("feyn.tif");
    box = boxCreate(461, 429, 1393, 342);
    pixt1 = pixClipRectangle(pixs, box, NULL);
    boxa = pixConnComp(pixt1, NULL, 8);
    n = boxaGetCount(boxa);
    pixt2 = pixConvertTo8(pixt1, 1);
    pixt3 = pixConvertTo32(pixt1);
    for (i = 0; i < n; i++) {
        boxt = boxaGetBox(boxa, i, L_CLONE);
	rval = (1413 * i) % 256;
	gval = (4917 * i) % 256;
	bval = (7341 * i) % 256;
	pixRenderHashBox(pixt1, boxt, 8, 2, i % 4, 1, L_SET_PIXELS);
	pixRenderHashBoxArb(pixt2, boxt, 7, 2, i % 4, 1, rval, gval, bval);
	pixRenderHashBoxBlend(pixt3, boxt, 7, 2, i % 4, 1, rval, gval, bval,
                              0.5);
	boxDestroy(&boxt);
    }
    pixDisplay(pixt1, 0, 0);
    pixDisplay(pixt2, 0, 300);
    pixDisplay(pixt3, 0, 570);
    pixWrite("/tmp/junkpixt1.png", pixt1, IFF_PNG);
    pixWrite("/tmp/junkpixt2.png", pixt2, IFF_PNG);
    pixWrite("/tmp/junkpixt3.png", pixt3, IFF_PNG);

    boxaDestroy(&boxa);
    boxDestroy(&box);
    pixDestroy(&pixs);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixDestroy(&pixt3);


    /* ----------------------------------------------------------- *
     *    Test box transforms with either translation or scaling   *
     *    combined with rotation, using the simple 'ordered'       *
     *    function.  Show that the order of the operations does    *
     *    not matter; different hashing schemes end up in the      *
     *    identical boxes.                                         *
     * ----------------------------------------------------------- */
    pix = pixRead("feyn.tif");
    box = boxCreate(420, 360, 1500, 465);
    pixt = pixClipRectangle(pix, box, NULL);
    pixs = pixAddBorderGeneral(pixt, 0, 200, 0, 0, 0);
    boxDestroy(&box);
    pixDestroy(&pix);
    pixDestroy(&pixt);
    boxa = pixConnComp(pixs, NULL, 8);
    n = boxaGetCount(boxa);
    pixa = pixaCreate(0);

    pixt = pixConvertTo32(pixs);
    for (i = 0; i < 3; i++) {
        if (i == 0)
            order = L_TR_SC_RO;
        else if (i == 1)
            order = L_TR_RO_SC;
        else
            order = L_SC_TR_RO;
        boxat = boxaTransformOrdered(boxa, SHIFTX_2, SHIFTY_2, 1.0, 1.0,
                                     450, 250, ROTATION_2, order);
        RenderTransformedBoxa(pixt, boxat, i);
        boxaDestroy(&boxat);
    }
    pixSaveTiled(pixt, pixa, 1, 1, 30, 32);
    pixDestroy(&pixt);

    pixt = pixConvertTo32(pixs);
    for (i = 0; i < 3; i++) {
        if (i == 0)
            order = L_RO_TR_SC;
        else if (i == 1)
            order = L_RO_SC_TR;
        else
            order = L_SC_RO_TR;
        boxat = boxaTransformOrdered(boxa, SHIFTX_2, SHIFTY_2, 1.0, 1.0,
                                     450, 250, ROTATION_2, order);
        RenderTransformedBoxa(pixt, boxat, i + 4);
        boxaDestroy(&boxat);
    }
    pixSaveTiled(pixt, pixa, 1, 1, 30, 0);
    pixDestroy(&pixt);

    pixt = pixConvertTo32(pixs);
    for (i = 0; i < 3; i++) {
        if (i == 0)
            order = L_TR_SC_RO;
        else if (i == 1)
            order = L_SC_RO_TR;
        else
            order = L_SC_TR_RO;
        boxat = boxaTransformOrdered(boxa, 0, 0, SCALEX_2, SCALEY_2,
                                     450, 250, ROTATION_2, order);
        RenderTransformedBoxa(pixt, boxat, i + 8);
        boxaDestroy(&boxat);
    }
    pixSaveTiled(pixt, pixa, 1, 1, 30, 0);
    pixDestroy(&pixt);

    pixt = pixConvertTo32(pixs);
    for (i = 0; i < 3; i++) {
        if (i == 0)
            order = L_RO_TR_SC;
        else if (i == 1)
            order = L_RO_SC_TR;
        else
            order = L_TR_RO_SC;
        boxat = boxaTransformOrdered(boxa, 0, 0, SCALEX_2, SCALEY_2,
                                     450, 250, ROTATION_2, order);
        RenderTransformedBoxa(pixt, boxat, i + 16);
        boxaDestroy(&boxat);
    }
    pixSaveTiled(pixt, pixa, 1, 1, 30, 0);
    pixDestroy(&pixt);

    pixt = pixaDisplay(pixa, 0, 0);
    pixWrite("/tmp/junkxform1.png", pixt, IFF_PNG);
    pixDisplay(pixt, 1000, 0);
    pixDestroy(&pixt);
    pixDestroy(&pixs);
    boxaDestroy(&boxa);
    pixaDestroy(&pixa);


    /* ----------------------------------------------------------- *
     *    Do more testing of box and pta transforms.  Show that    *
     *    resulting boxes are identical by three methods.          *
     * ----------------------------------------------------------- */
        /* Set up pix and boxa */
    pixa = pixaCreate(0);
    pix = pixRead("lucasta.1.300.tif");
    pixTranslate(pix, pix, 70, 0, L_BRING_IN_WHITE);
    pixt = pixCloseBrick(NULL, pix, 14, 5);
    pixOpenBrick(pixt, pixt, 1, 2);
    boxa = pixConnComp(pixt, NULL, 8);
    pixs = pixConvertTo32(pix);
    pixc = pixCopy(NULL, pixs);
    RenderTransformedBoxa(pixc, boxa, 113);
    pixSaveTiled(pixc, pixa, 2, 1, 30, 32);
    pixDestroy(&pix);
    pixDestroy(&pixc);
    pixDestroy(&pixt);

        /* (a) Do successive discrete operations: shift, scale, rotate */
    pixt1 = pixTranslate(NULL, pixs, SHIFTX_3, SHIFTY_3, L_BRING_IN_WHITE);
    boxa1 = boxaTranslate(boxa, SHIFTX_3, SHIFTY_3);
    pixc = pixCopy(NULL, pixt1);
    RenderTransformedBoxa(pixc, boxa1, 213);
    pixSaveTiled(pixc, pixa, 2, 0, 30, 32);
    pixDestroy(&pixc);

    pixt2 = pixScale(pixt1, SCALEX_3, SCALEY_3);
    boxa2 = boxaScale(boxa1, SCALEX_3, SCALEY_3);
    pixc = pixCopy(NULL, pixt2);
    RenderTransformedBoxa(pixc, boxa2, 313);
    pixSaveTiled(pixc, pixa, 2, 1, 30, 32);
    pixDestroy(&pixc);

    pixGetDimensions(pixt2, &w, &h, NULL);
    pixt3 = pixRotateAM(pixt2, ROTATION_3, L_BRING_IN_WHITE);
    boxa3 = boxaRotate(boxa2, w / 2, h / 2, ROTATION_3);
    pixc = pixCopy(NULL, pixt3);
    RenderTransformedBoxa(pixc, boxa3, 413);
    pixSaveTiled(pixc, pixa, 2, 0, 30, 32);
    pixDestroy(&pixc);

        /* (b) Set up and use the composite transform */
    mat1 = createMatrix2dTranslate(SHIFTX_3, SHIFTY_3);
    mat2 = createMatrix2dScale(SCALEX_3, SCALEY_3);
    mat3 = createMatrix2dRotate(w / 2, h / 2, ROTATION_3);
    l_productMat3(mat3, mat2, mat1, matd, 3);
    boxa4 = boxaAffineTransform(boxa, matd);
    pixc = pixCopy(NULL, pixt3);
    RenderTransformedBoxa(pixc, boxa4, 513);
    pixSaveTiled(pixc, pixa, 2, 1, 30, 32);
    pixDestroy(&pixc);

        /* (c) Use the special 'ordered' function */
    pixGetDimensions(pixs, &ws, &hs, NULL);
    boxa5 = boxaTransformOrdered(boxa, SHIFTX_3, SHIFTY_3,
                                 SCALEX_3, SCALEY_3,
                                 ws / 2, hs / 2, ROTATION_3, L_TR_SC_RO);
    pixc = pixCopy(NULL, pixt3);
    RenderTransformedBoxa(pixc, boxa5, 613);
    pixSaveTiled(pixc, pixa, 2, 0, 30, 32);
    pixDestroy(&pixc);

    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixDestroy(&pixt3);
    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);
    boxaDestroy(&boxa3);
    boxaDestroy(&boxa4);
    boxaDestroy(&boxa5);
    lept_free(mat1);
    lept_free(mat2);
    lept_free(mat3);

    pixt = pixaDisplay(pixa, 0, 0);
    pixWrite("/tmp/junkxform2.png", pixt, IFF_PNG);
    pixDisplay(pixt, 1000, 300);
    pixDestroy(&pixt);
    pixDestroy(&pixs);
    boxaDestroy(&boxa);
    pixaDestroy(&pixa);
    return 0;
}
示例#12
0
/*!
 *  pixGenTextlineMask()
 *
 *      Input:  pixs (1 bpp, assumed to be 150 to 200 ppi)
 *              &pixvws (<return> vertical whitespace mask)
 *              &tlfound (<optional return> 1 if the mask is not empty)
 *              debug (flag: 1 for debug output)
 *      Return: pixd (textline mask), or null on error
 *
 *  Notes:
 *      (1) The input pixs should be deskewed.
 *      (2) pixs should have no halftone pixels.
 *      (3) Both the input image and the returned textline mask
 *          are at the same resolution.
 */
PIX *
pixGenTextlineMask(PIX      *pixs,
                   PIX     **ppixvws,
                   l_int32  *ptlfound,
                   l_int32   debug)
{
l_int32  empty;
PIX     *pixt1, *pixt2, *pixvws, *pixd;

    PROCNAME("pixGenTextlineMask");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (!ppixvws)
        return (PIX *)ERROR_PTR("&pixvws not defined", procName, NULL);
    if (pixGetDepth(pixs) != 1)
        return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);

        /* First we need a vertical whitespace mask.  Invert the image. */
    pixt1 = pixInvert(NULL, pixs);

        /* The whitespace mask will break textlines where there
         * is a large amount of white space below or above.
         * This can be prevented by identifying regions of the
         * inverted image that have large horizontal extent (bigger than
	 * the separation between columns) and significant
         * vertical extent (bigger than the separation between
	 * textlines), and subtracting this from the bg. */
    pixt2 = pixMorphCompSequence(pixt1, "o80.60", 0);
    pixSubtract(pixt1, pixt1, pixt2);
    pixDisplayWriteFormat(pixt1, debug, IFF_PNG);
    pixDestroy(&pixt2);

        /* Identify vertical whitespace by opening the remaining bg.
         * o5.1 removes thin vertical bg lines and o1.200 extracts
         * long vertical bg lines. */
    pixvws = pixMorphCompSequence(pixt1, "o5.1 + o1.200", 0);
    *ppixvws = pixvws;
    pixDisplayWriteFormat(pixvws, debug, IFF_PNG);
    pixDestroy(&pixt1);

        /* Three steps to getting text line mask:
         *   (1) close the characters and words in the textlines
         *   (2) open the vertical whitespace corridors back up
         *   (3) small opening to remove noise    */
    pixt1 = pixCloseSafeBrick(NULL, pixs, 30, 1);
    pixDisplayWrite(pixt1, debug);
    pixd = pixSubtract(NULL, pixt1, pixvws);
    pixOpenBrick(pixd, pixd, 3, 3);
    pixDisplayWriteFormat(pixd, debug, IFF_PNG);
    pixDestroy(&pixt1);

        /* Check if text line mask is empty */
    if (ptlfound) {
        *ptlfound = 0;
        pixZero(pixd, &empty);
        if (!empty)
            *ptlfound = 1;
    }

    return pixd;
}
/*!
 * \brief   pixMorphSequence()
 *
 * \param[in]    pixs
 * \param[in]    sequence string specifying sequence
 * \param[in]    dispsep controls debug display of each result in the sequence:
 *                       0: no output
 *                       > 0: gives horizontal separation in pixels between
 *                            successive displays
 *                       < 0: pdf output; abs(dispsep) is used for naming
 * \return  pixd, or NULL on error
 *
 * <pre>
 * 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!
 * </pre>
 */
PIX *
pixMorphSequence(PIX         *pixs,
                 const char  *sequence,
                 l_int32      dispsep)
{
char    *rawop, *op, *fname;
char     buf[256];
l_int32  nops, i, j, nred, fact, w, h, x, y, border, pdfout;
l_int32  level[4];
PIX     *pixt1, *pixt2;
PIXA    *pixa;
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);
    pdfout = (dispsep < 0) ? 1 : 0;

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

        /* Parse and operate */
    pixa = NULL;
    if (pdfout) {
        pixa = pixaCreate(0);
        pixaAddPix(pixa, pixs, L_CLONE);
        snprintf(buf, sizeof(buf), "/tmp/seq_output_%d.pdf", L_ABS(dispsep));
        fname = genPathname(buf, NULL);
    }
    border = 0;
    pixt1 = pixCopy(NULL, pixs);
    pixt2 = NULL;
    x = y = 0;
    for (i = 0; i < nops; i++) {
        rawop = sarrayGetString(sa, i, L_NOCOPY);
        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);
            pixSwapAndDestroy(&pixt1, &pixt2);
            break;
        case 'e':
        case 'E':
            sscanf(&op[1], "%d.%d", &w, &h);
            pixt2 = pixErodeBrick(NULL, pixt1, w, h);
            pixSwapAndDestroy(&pixt1, &pixt2);
            break;
        case 'o':
        case 'O':
            sscanf(&op[1], "%d.%d", &w, &h);
            pixOpenBrick(pixt1, pixt1, w, h);
            break;
        case 'c':
        case 'C':
            sscanf(&op[1], "%d.%d", &w, &h);
            pixCloseSafeBrick(pixt1, pixt1, w, h);
            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]);
            pixSwapAndDestroy(&pixt1, &pixt2);
            break;
        case 'x':
        case 'X':
            sscanf(&op[1], "%d", &fact);
            pixt2 = pixExpandReplicate(pixt1, fact);
            pixSwapAndDestroy(&pixt1, &pixt2);
            break;
        case 'b':
        case 'B':
            sscanf(&op[1], "%d", &border);
            pixt2 = pixAddBorder(pixt1, border, 0);
            pixSwapAndDestroy(&pixt1, &pixt2);
            break;
        default:
            /* All invalid ops are caught in the first pass */
            break;
        }
        LEPT_FREE(op);

            /* Debug output */
        if (dispsep > 0) {
            pixDisplay(pixt1, x, y);
            x += dispsep;
        }
        if (pdfout)
            pixaAddPix(pixa, pixt1, L_COPY);
    }
    if (border > 0) {
        pixt2 = pixRemoveBorder(pixt1, border);
        pixSwapAndDestroy(&pixt1, &pixt2);
    }

    if (pdfout) {
        pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
        LEPT_FREE(fname);
        pixaDestroy(&pixa);
    }

    sarrayDestroy(&sa);
    return pixt1;
}
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);
}
示例#15
0
/*!
 *  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!
 */
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;
}
示例#16
0
/* dwa composite with morph non-composite */
l_int32
DoComparisonDwa5(PIX *pixs,
                 PIX *pixt1,
                 PIX *pixt2,
                 PIX *pixt3,
                 PIX *pixt4,
                 PIX *pixt5,
                 PIX *pixt6,
                 l_int32 isize) {
    l_int32 fact1, fact2, size;

    selectComposableSizes(isize, &fact1, &fact2);
    size = fact1 * fact2;

    fprintf(stderr, "..%d..", size);

    if (TIMING) startTimer();
    pixDilateCompBrickDwa(pixt1, pixs, size, 1);
    pixDilateCompBrickDwa(pixt3, pixs, 1, size);
    pixDilateCompBrickDwa(pixt5, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Dwa: %7.3f sec\n", stopTimer());
    if (TIMING) startTimer();
    pixDilateBrick(pixt2, pixs, size, 1);
    pixDilateBrick(pixt4, pixs, 1, size);
    pixDilateBrick(pixt6, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Rop: %7.3f sec\n", stopTimer());
    PixCompareDwa(size, "dilate", pixt1, pixt2, pixt3, pixt4, pixt5, pixt6);

/*    pixDisplay(pixt1, 100, 100);  */
/*    pixDisplay(pixt2, 800, 100);  */

    if (TIMING) startTimer();
    pixErodeCompBrickDwa(pixt1, pixs, size, 1);
    pixErodeCompBrickDwa(pixt3, pixs, 1, size);
    pixErodeCompBrickDwa(pixt5, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Dwa: %7.3f sec\n", stopTimer());
    if (TIMING) startTimer();
    pixErodeBrick(pixt2, pixs, size, 1);
    pixErodeBrick(pixt4, pixs, 1, size);
    pixErodeBrick(pixt6, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Rop: %7.3f sec\n", stopTimer());
    PixCompareDwa(size, "erode", pixt1, pixt2, pixt3, pixt4, pixt5, pixt6);

    if (TIMING) startTimer();
    pixOpenCompBrickDwa(pixt1, pixs, size, 1);
    pixOpenCompBrickDwa(pixt3, pixs, 1, size);
    pixOpenCompBrickDwa(pixt5, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Dwa: %7.3f sec\n", stopTimer());
    if (TIMING) startTimer();
    pixOpenBrick(pixt2, pixs, size, 1);
    pixOpenBrick(pixt4, pixs, 1, size);
    pixOpenBrick(pixt6, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Rop: %7.3f sec\n", stopTimer());
    PixCompareDwa(size, "open", pixt1, pixt2, pixt3, pixt4, pixt5, pixt6);

    if (TIMING) startTimer();
    pixCloseCompBrickDwa(pixt1, pixs, size, 1);
    pixCloseCompBrickDwa(pixt3, pixs, 1, size);
    pixCloseCompBrickDwa(pixt5, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Dwa: %7.3f sec\n", stopTimer());
    if (TIMING) startTimer();
    pixCloseSafeBrick(pixt2, pixs, size, 1);
    pixCloseSafeBrick(pixt4, pixs, 1, size);
    pixCloseSafeBrick(pixt6, pixs, size, size);
    if (TIMING) fprintf(stderr, "Time Rop: %7.3f sec\n", stopTimer());
    PixCompareDwa(size, "close", pixt1, pixt2, pixt3, pixt4, pixt5, pixt6);

    return 0;
}
main(int    argc,
     char **argv)
{
char         bufname[256];
l_int32      i, j, w, h, d, x, y, wpls;
l_uint32    *datas, *lines;
l_float32   *vc;
l_float32   *mat1, *mat2, *mat3, *mat1i, *mat2i, *mat3i, *matdinv;
l_float32    matd[9], matdi[9];
BOXA        *boxa, *boxa2;
PIX         *pix, *pixs, *pixb, *pixg, *pixc, *pixcs;
PIX         *pixd, *pixt1, *pixt2, *pixt3;
PIXA        *pixa;
PTA         *ptas, *ptad;
static char  mainName[] = "affine_reg";

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

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

#if ALL
        /* Test invertability of sequential. */
    pixa = pixaCreate(0);
    for (i = 0; i < 3; i++) {
        pixb = pixAddBorder(pixs, ADDED_BORDER_PIXELS, 0);
        MakePtas(i, &ptas, &ptad);
        pixt1 = pixAffineSequential(pixb, ptad, ptas, 0, 0);
        pixSaveTiled(pixt1, pixa, 3, 1, 20, 8);
        pixt2 = pixAffineSequential(pixt1, ptas, ptad, 0, 0);
        pixSaveTiled(pixt2, pixa, 3, 0, 20, 0);
        pixd = pixRemoveBorder(pixt2, ADDED_BORDER_PIXELS);
        pixXor(pixd, pixd, pixs);
        pixSaveTiled(pixd, pixa, 3, 0, 20, 0);
        sprintf(bufname, "/tmp/junkseq%d.png", i);
        pixWrite(bufname, pixd, IFF_PNG);
        pixDestroy(&pixb);
        pixDestroy(&pixt1);
        pixDestroy(&pixt2);
        pixDestroy(&pixd);
        ptaDestroy(&ptas);
        ptaDestroy(&ptad);
    }

    pixt1 = pixaDisplay(pixa, 0, 0);
    pixWrite("/tmp/junkaffine1.png", pixt1, IFF_PNG);
    pixDisplay(pixt1, 100, 100);
    pixDestroy(&pixt1);
    pixaDestroy(&pixa);
#endif

#if ALL 
        /* Test invertability of sampling */
    pixa = pixaCreate(0);
    for (i = 0; i < 3; i++) {
        pixb = pixAddBorder(pixs, ADDED_BORDER_PIXELS, 0);
        MakePtas(i, &ptas, &ptad);
        pixt1 = pixAffineSampledPta(pixb, ptad, ptas, L_BRING_IN_WHITE);
        pixSaveTiled(pixt1, pixa, 3, 1, 20, 8);
        pixt2 = pixAffineSampledPta(pixt1, ptas, ptad, L_BRING_IN_WHITE);
        pixSaveTiled(pixt2, pixa, 3, 0, 20, 0);
        pixd = pixRemoveBorder(pixt2, ADDED_BORDER_PIXELS);
        pixXor(pixd, pixd, pixs);
        pixSaveTiled(pixd, pixa, 3, 0, 20, 0);
        if (i == 0) pixWrite("/tmp/junksamp.png", pixt1, IFF_PNG);
        pixDestroy(&pixb);
        pixDestroy(&pixt1);
        pixDestroy(&pixt2);
        pixDestroy(&pixd);
        ptaDestroy(&ptas);
        ptaDestroy(&ptad);
    }

    pixt1 = pixaDisplay(pixa, 0, 0);
    pixWrite("/tmp/junkaffine2.png", pixt1, IFF_PNG);
    pixDisplay(pixt1, 100, 300);
    pixDestroy(&pixt1);
    pixaDestroy(&pixa);
#endif

#if ALL
        /* Test invertability of interpolation on grayscale */
    pixa = pixaCreate(0);
    pixg = pixScaleToGray3(pixs);
    for (i = 0; i < 3; i++) {
        pixb = pixAddBorder(pixg, ADDED_BORDER_PIXELS / 3, 255);
        MakePtas(i, &ptas, &ptad);
        pixt1 = pixAffinePta(pixb, ptad, ptas, L_BRING_IN_WHITE);
        pixSaveTiled(pixt1, pixa, 1, 1, 20, 8);
        pixt2 = pixAffinePta(pixt1, ptas, ptad, L_BRING_IN_WHITE);
        pixSaveTiled(pixt2, pixa, 1, 0, 20, 0);
        pixd = pixRemoveBorder(pixt2, ADDED_BORDER_PIXELS / 3);
        pixXor(pixd, pixd, pixg);
        pixSaveTiled(pixd, pixa, 1, 0, 20, 0);
        if (i == 0) pixWrite("/tmp/junkinterp.png", pixt1, IFF_PNG);
        pixDestroy(&pixb);
        pixDestroy(&pixt1);
        pixDestroy(&pixt2);
        pixDestroy(&pixd);
        ptaDestroy(&ptas);
        ptaDestroy(&ptad);
    }

    pixt1 = pixaDisplay(pixa, 0, 0);
    pixWrite("/tmp/junkaffine3.png", pixt1, IFF_PNG);
    pixDisplay(pixt1, 100, 500);
    pixDestroy(&pixt1);
    pixaDestroy(&pixa);
    pixDestroy(&pixg);
#endif

#if ALL
        /* Test invertability of interpolation on color */
    pixa = pixaCreate(0);
    pixc = pixRead("test24.jpg");
    pixcs = pixScale(pixc, 0.3, 0.3);
    for (i = 0; i < 3; i++) {
        pixb = pixAddBorder(pixcs, ADDED_BORDER_PIXELS / 4, 0xffffff00);
        MakePtas(i, &ptas, &ptad);
        pixt1 = pixAffinePta(pixb, ptad, ptas, L_BRING_IN_WHITE);
        pixSaveTiled(pixt1, pixa, 1, 1, 20, 32);
        pixt2 = pixAffinePta(pixt1, ptas, ptad, L_BRING_IN_WHITE);
        pixSaveTiled(pixt2, pixa, 1, 0, 20, 0);
        pixd = pixRemoveBorder(pixt2, ADDED_BORDER_PIXELS / 4);
        pixXor(pixd, pixd, pixcs);
        pixSaveTiled(pixd, pixa, 1, 0, 20, 0);
        pixDestroy(&pixb);
        pixDestroy(&pixt1);
        pixDestroy(&pixt2);
        pixDestroy(&pixd);
        ptaDestroy(&ptas);
        ptaDestroy(&ptad);
    }

    pixt1 = pixaDisplay(pixa, 0, 0);
    pixWrite("/tmp/junkaffine4.png", pixt1, IFF_PNG);
    pixDisplay(pixt1, 100, 500);
    pixDestroy(&pixt1);
    pixaDestroy(&pixa);
    pixDestroy(&pixc);
    pixDestroy(&pixcs);
#endif

#if ALL 
       /* Comparison between sequential and sampling */
    MakePtas(3, &ptas, &ptad);
    pixa = pixaCreate(0);

	/* Use sequential transforms */
    pixt1 = pixAffineSequential(pixs, ptas, ptad,
                     ADDED_BORDER_PIXELS, ADDED_BORDER_PIXELS);
    pixSaveTiled(pixt1, pixa, 2, 0, 20, 8);

	/* Use sampled transform */
    pixt2 = pixAffineSampledPta(pixs, ptas, ptad, L_BRING_IN_WHITE);
    pixSaveTiled(pixt2, pixa, 2, 0, 20, 8);

        /* Compare the results */
    pixXor(pixt2, pixt2, pixt1);
    pixSaveTiled(pixt2, pixa, 2, 0, 20, 8);

    pixd = pixaDisplay(pixa, 0, 0);
    pixWrite("/tmp/junkaffine5.png", pixd, IFF_PNG);
    pixDisplay(pixd, 100, 700);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixDestroy(&pixd);
    pixaDestroy(&pixa);
    ptaDestroy(&ptas);
    ptaDestroy(&ptad);
#endif

#if ALL 
       /* Get timings and test with large distortion */
    MakePtas(4, &ptas, &ptad);
    pixa = pixaCreate(0);
    pixg = pixScaleToGray3(pixs);

    startTimer();
    pixt1 = pixAffineSequential(pixg, ptas, ptad, 0, 0);
    fprintf(stderr, " Time for pixAffineSequentialPta(): %6.2f sec\n",
            stopTimer());
    pixSaveTiled(pixt1, pixa, 1, 1, 20, 8);

    startTimer();
    pixt2 = pixAffineSampledPta(pixg, ptas, ptad, L_BRING_IN_WHITE);
    fprintf(stderr, " Time for pixAffineSampledPta(): %6.2f sec\n", stopTimer());
    pixSaveTiled(pixt2, pixa, 1, 0, 20, 8);

    startTimer();
    pixt3 = pixAffinePta(pixg, ptas, ptad, L_BRING_IN_WHITE);
    fprintf(stderr, " Time for pixAffinePta(): %6.2f sec\n", stopTimer());
    pixSaveTiled(pixt3, pixa, 1, 0, 20, 8);

    pixXor(pixt1, pixt1, pixt2);
    pixSaveTiled(pixt1, pixa, 1, 1, 20, 8);
    pixXor(pixt2, pixt2, pixt3);
    pixSaveTiled(pixt2, pixa, 1, 0, 20, 8);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixDestroy(&pixt3);

    pixd = pixaDisplay(pixa, 0, 0);
    pixWrite("/tmp/junkaffine6.png", pixd, IFF_PNG);
    pixDisplay(pixd, 100, 900);
    pixDestroy(&pixd);
    pixDestroy(&pixg);
    pixaDestroy(&pixa);
    ptaDestroy(&ptas);
    ptaDestroy(&ptad);
#endif

    pixDestroy(&pixs);

#if 1
        /* Set up pix and boxa */
    pixa = pixaCreate(0);
    pix = pixRead("lucasta.1.300.tif");
    pixTranslate(pix, pix, 70, 0, L_BRING_IN_WHITE);
    pixt1 = pixCloseBrick(NULL, pix, 14, 5);
    pixOpenBrick(pixt1, pixt1, 1, 2);
    boxa = pixConnComp(pixt1, NULL, 8);
    pixs = pixConvertTo32(pix);
    pixGetDimensions(pixs, &w, &h, NULL);
    pixc = pixCopy(NULL, pixs);
    RenderHashedBoxa(pixc, boxa, 113);
    pixSaveTiled(pixc, pixa, 2, 1, 30, 32);
    pixDestroy(&pix);
    pixDestroy(&pixc);
    pixDestroy(&pixt1);

        /* Set up an affine transform in matd, and apply it to boxa */
    mat1 = createMatrix2dTranslate(SHIFTX, SHIFTY);
    mat2 = createMatrix2dScale(SCALEX, SCALEY);
    mat3 = createMatrix2dRotate(w / 2, h / 2, ROTATION);
    l_productMat3(mat3, mat2, mat1, matd, 3);
    boxa2 = boxaAffineTransform(boxa, matd);

        /* Set up the inverse transform in matdi */
    mat1i = createMatrix2dTranslate(-SHIFTX, -SHIFTY);
    mat2i = createMatrix2dScale(1.0/ SCALEX, 1.0 / SCALEY);
    mat3i = createMatrix2dRotate(w / 2, h / 2, -ROTATION);
    l_productMat3(mat1i, mat2i, mat3i, matdi, 3);

        /* Invert the original affine transform in matdinv */
    affineInvertXform(matd, &matdinv);
    fprintf(stderr, "Affine transform, applied to boxa\n");
    for (i = 0; i < 9; i++) {
        if (i && (i % 3 == 0))  fprintf(stderr, "\n");
        fprintf(stderr, " %7.3f ", matd[i]);
    }
    fprintf(stderr, "\nInverse transform, made by composing inverse parts");
    for (i = 0; i < 9; i++) {
        if (i % 3 == 0)  fprintf(stderr, "\n");
        fprintf(stderr, " %7.3f ", matdi[i]);
    }
    fprintf(stderr, "\nInverse transform, made by inverting the affine xform");
    for (i = 0; i < 6; i++) {
        if (i % 3 == 0)  fprintf(stderr, "\n");
        fprintf(stderr, " %7.3f ", matdinv[i]);
    }
    fprintf(stderr, "\n");

        /* Apply the inverted affine transform pixs */
    pixd = pixAffine(pixs, matdinv, L_BRING_IN_WHITE);
    RenderHashedBoxa(pixd, boxa2, 513);
    pixSaveTiled(pixd, pixa, 2, 0, 30, 32);
    pixDestroy(&pixd);

    pixd = pixaDisplay(pixa, 0, 0);
    pixWrite("/tmp/junkaffine7.png", pixd, IFF_PNG);
    pixDisplay(pixd, 100, 900);
    pixDestroy(&pixd);
    pixDestroy(&pixs);
    pixaDestroy(&pixa);
    boxaDestroy(&boxa);
    boxaDestroy(&boxa2);
    FREE(mat1);
    FREE(mat2);
    FREE(mat3);
    FREE(mat1i);
    FREE(mat2i);
    FREE(mat3i);
#endif

    return 0;
}
示例#18
0
int
main(int argc, char **argv) {
  if (argc < 3)
    return usage(argv[0]);

  char highlight = 0;
  char ignore_scrollbars = 1;
  /* Default output filename; can be overridden by command line. */
  const char *output_filename = "highlight.png";

  int argi = 1;

  for (; argi < argc; ++argi) {
    if (strcmp("--highlight", argv[argi]) == 0) {
      highlight = 1;
    } else if (strcmp("--no-ignore-scrollbars", argv[argi]) == 0) {
      ignore_scrollbars = 0;
    } else if (strcmp("--output", argv[argi]) == 0) {
      if (argi + 1 >= argc) {
        fprintf(stderr, "missing argument to --output\n");
        return 1;
      }
      output_filename = argv[++argi];
    } else {
      break;
    }
  }

  if (argc - argi < 2)
    return usage(argv[0]);

  PIX *a = pixRead(argv[argi]);
  PIX *b = pixRead(argv[argi + 1]);

  if (!a) {
    fprintf(stderr, "Failed to open %s\n", argv[argi]);
    return 1;
  }

  if (!b) {
    fprintf(stderr, "Failed to open %s\n", argv[argi + 1]);
    return 1;
  }

  if (pixGetWidth(a) != pixGetWidth(b) ||
      pixGetHeight(a) != pixGetHeight(b)) {
    fprintf(stderr, "Inputs are difference sizes\n");
    return 1;
  }

  PIX *delta = pixAbsDifference(a, b);
  pixInvert(delta, delta);
  if (!highlight)
    pixDestroy(&a);
  pixDestroy(&b);

  PIX *deltagray = pixConvertRGBToGray(delta, 0, 0, 0);
  pixDestroy(&delta);

  PIX *deltabinary = pixThresholdToBinary(deltagray, 254);
  PIX *deltabinaryclipped;
  const int clipwidth = pixGetWidth(deltabinary) - 15;
  const int clipheight = pixGetHeight(deltabinary) - 15;

  if (ignore_scrollbars && clipwidth > 0 && clipheight > 0) {
    BOX *clip = boxCreate(0, 0, clipwidth, clipheight);

    deltabinaryclipped = pixClipRectangle(deltabinary, clip, NULL);
    boxDestroy(&clip);
    pixDestroy(&deltabinary);
  } else {
    deltabinaryclipped = deltabinary;
    deltabinary = NULL;
  }

  PIX *hopened = pixOpenBrick(NULL, deltabinaryclipped, 3, 1);
  PIX *vopened = pixOpenBrick(NULL, deltabinaryclipped, 1, 3);
  pixDestroy(&deltabinaryclipped);

  PIX *opened = pixOr(NULL, hopened, vopened);
  pixDestroy(&hopened);
  pixDestroy(&vopened);

  l_int32 count;
  pixCountPixels(opened, &count, NULL);
  fprintf(stderr, "%d\n", count);

  if (count && highlight) {
    PIX *d1 = pixDilateBrick(NULL, opened, 7, 7);
    PIX *d2 = pixDilateBrick(NULL, opened, 3, 3);
    pixInvert(d2, d2);
    pixAnd(d1, d1, d2);
    pixPaintThroughMask(a, d1, 0, 0, 0xff << 24);
    pixWrite(output_filename, a, IFF_PNG);
  }

  return count > 0;
}