示例#1
0
static void
GetImageMask(PIX         *pixs,
             l_int32      res,
             BOXA       **pboxa,
             const char  *debugfile)
{
PIX   *pix1, *pix2, *pix3, *pix4;
PIXA  *pixa;

    pixSetResolution(pixs, 200, 200);
    pix1 = pixConvertTo1(pixs, 100);
    pix2 = pixGenerateHalftoneMask(pix1, NULL, NULL, NULL);
    pix3 = pixMorphSequence(pix2, "c20.1 + c1.20", 0);
    *pboxa = pixConnComp(pix3, NULL, 8);
    if (debugfile) {
        pixa = pixaCreate(0);
        pixaAddPix(pixa, pixs, L_COPY);
        pixaAddPix(pixa, pix1, L_INSERT);
        pixaAddPix(pixa, pix2, L_INSERT);
        pixaAddPix(pixa, pix3, L_INSERT);
        pix4 = pixaDisplayTiledInRows(pixa, 32, 1800, 0.25, 0, 25, 2);
        pixWrite(debugfile, pix4, IFF_JFIF_JPEG);
        pixDisplay(pix4, 100, 100);
        pixDestroy(&pix4);
        pixaDestroy(&pixa);
    } else {
        pixDestroy(&pix1);
        pixDestroy(&pix2);
        pixDestroy(&pix3);
    }

    return;
}
示例#2
0
PIX *
MakeReplacementMask(PIX  *pixs)
{
PIX  *pix1, *pix2, *pix3, *pix4;

    pix1 = pixMaskOverColorPixels(pixs, 95, 3);
    pix2 = pixMorphSequence(pix1, "o15.15", 0);
    pixSeedfillBinary(pix2, pix2, pix1, 8);
    pix3 = pixMorphSequence(pix2, "c15.15 + d61.31", 0);
    pix4 = pixRemoveBorderConnComps(pix3, 8);
    pixXor(pix4, pix4, pix3);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pix3);
    return pix4;
}
示例#3
0
/*!
 * \brief   pixModifyStrokeWidth()
 *
 * \param[in]   pixa  of 1 bpp pix
 * \param[in]   width  measured average stroke width
 * \param[in]   targetw  desired stroke width
 * \return  pix  with modified stroke width, or NULL on error
 */
PIX *
pixModifyStrokeWidth(PIX       *pixs,
                     l_float32  width,
                     l_float32  targetw)
{
char     buf[16];
l_int32  diff, size;

    PROCNAME("pixModifyStrokeWidth");

    if (!pixs || (pixGetDepth(pixs) != 1))
        return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
    if (targetw < 1)
        return (PIX *)ERROR_PTR("target width < 1", procName, NULL);

    diff = lept_roundftoi(targetw - width);
    if (diff == 0) return pixCopy(NULL, pixs);

    size = L_ABS(diff) + 1;
    if (diff < 0)  /* erode */
        snprintf(buf, sizeof(buf), "e%d.%d", size, size);
    else  /* diff > 0; dilate */
        snprintf(buf, sizeof(buf), "d%d.%d", size, size);
    return pixMorphSequence(pixs, buf, 0);
}
示例#4
0
int main(int    argc,
         char **argv)
{
char        *dirin, *dirout, *infile, *outfile, *tail;
l_int32      i, nfiles, border, x, y, w, h, xb, yb, wb, hb;
BOX         *box1, *box2;
BOXA        *boxa1, *boxa2;
PIX         *pixs, *pixt1, *pixd;
SARRAY      *safiles;
static char  mainName[] = "croptext";

    if (argc != 4)
        return ERROR_INT("Syntax: croptext dirin border dirout", mainName, 1);
    dirin = argv[1];
    border = atoi(argv[2]);
    dirout = argv[3];

    setLeptDebugOK(1);
    safiles = getSortedPathnamesInDirectory(dirin, NULL, 0, 0);
    nfiles = sarrayGetCount(safiles);

    for (i = 0; i < nfiles; i++) {
        infile = sarrayGetString(safiles, i, L_NOCOPY);
        splitPathAtDirectory(infile, NULL, &tail);
        outfile = genPathname(dirout, tail);
        pixs = pixRead(infile);
        pixt1 = pixMorphSequence(pixs, "r11 + c10.40 + o5.5 + x4", 0);
        boxa1 = pixConnComp(pixt1, NULL, 8);
        if (boxaGetCount(boxa1) == 0) {
            fprintf(stderr, "Warning: no components on page %s\n", tail);
            continue;
        }
        boxa2 = boxaSort(boxa1, L_SORT_BY_AREA, L_SORT_DECREASING, NULL);
        box1 = boxaGetBox(boxa2, 0, L_CLONE);
        boxGetGeometry(box1, &x, &y, &w, &h);
        xb = L_MAX(0, x - border);
        yb = L_MAX(0, y - border);
        wb = w + 2 * border;
        hb = h + 2 * border;
        box2 = boxCreate(xb, yb, wb, hb);
        pixd = pixClipRectangle(pixs, box2, NULL);
        pixWrite(outfile, pixd, IFF_TIFF_G4);

        pixDestroy(&pixs);
        pixDestroy(&pixt1);
        pixDestroy(&pixd);
        boxaDestroy(&boxa1);
        boxaDestroy(&boxa2);
    }

    return 0;
}
示例#5
0
/*
 *  pixDebugFlipDetect()
 *
 *      Input:  filename (for output debug file)
 *              pixs (input to pix*Detect)
 *              pixhm (hit-miss result from ascenders or descenders)
 *              enable (1 to enable this function; 0 to disable)
 *      Return: void
 */
static void
pixDebugFlipDetect(const char *filename,
                   PIX        *pixs,
                   PIX        *pixhm,
                   l_int32     enable)
{
PIX  *pixt, *pixthm;

   if (!enable) return;

        /* Display with red dot at counted locations */
    pixt = pixConvert1To4Cmap(pixs);
    pixthm = pixMorphSequence(pixhm, "d5.5", 0);
    pixSetMaskedCmap(pixt, pixthm, 0, 0, 255, 0, 0);

    pixWrite(filename, pixt, IFF_PNG);
    pixDestroy(&pixthm);
    pixDestroy(&pixt);
    return;
}
示例#6
0
/*!
 *  pixGenTextblockMask()
 *
 *      Input:  pixs (1 bpp, textline mask, assumed to be 150 to 200 ppi)
 *              pixvws (vertical white space mask)
 *              debug (flag: 1 for debug output)
 *      Return: pixd (textblock mask), or null on error
 *
 *  Notes:
 *      (1) Both the input masks (textline and vertical white space) and
 *          the returned textblock mask are at the same resolution.
 *      (2) The result is somewhat noisy, in that small "blocks" of
 *          text may be included.  These can be removed by post-processing,
 *          using, e.g.,
 *             pixSelectBySize(pix, 60, 60, 4, L_SELECT_IF_EITHER,
 *                             L_SELECT_IF_GTE, NULL);
 */
PIX *
pixGenTextblockMask(PIX     *pixs,
                    PIX     *pixvws,
                    l_int32  debug)
{
PIX  *pixt1, *pixt2, *pixt3, *pixd;

    PROCNAME("pixGenTextblockMask");

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

        /* Join pixels vertically to make a textblock mask */
    pixt1 = pixMorphSequence(pixs, "c1.10 + o4.1", 0);
    pixDisplayWriteFormat(pixt1, debug, IFF_PNG);

        /* Solidify the textblock mask and remove noise:
         *   (1) For each cc, 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. */
    pixt2 = pixMorphSequenceByComponent(pixt1, "c30.30 + d3.3", 8, 0, 0, NULL);
    pixCloseSafeBrick(pixt2, pixt2, 10, 1);
    pixDisplayWriteFormat(pixt2, debug, IFF_PNG);
    pixt3 = pixSubtract(NULL, pixt2, pixvws);
    pixDisplayWriteFormat(pixt3, debug, IFF_PNG);
    pixd = pixSelectBySize(pixt3, 25, 5, 8, L_SELECT_IF_BOTH,
                            L_SELECT_IF_GTE, NULL);
    pixDisplayWriteFormat(pixd, debug, IFF_PNG);

    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixDestroy(&pixt3);
    return pixd;
}
示例#7
0
/*!
 * \brief   pixSetStrokeWidth()
 *
 * \param[in]   pixs  1 bpp pix
 * \param[in]   width  set stroke width to this value, in [1 ... 100].
 * \param[in]   thinfirst  1 to thin all pix to a skeleton first; 0 to skip
 * \param[in]   connectivity  4 or 8, to be used if %thinfirst == 1
 * \return  pixd  with stroke width set to %width, or NULL on error
 *
 * <pre>
 * Notes:
 *      (1) See notes in pixaSetStrokeWidth().
 *      (2) A white border of sufficient width to avoid boundary
 *          artifacts in the thickening step is added before thinning.
 *      (3) %connectivity == 8 usually gives a slightly smoother result.
 * </pre>
 */
PIX *
pixSetStrokeWidth(PIX     *pixs,
                  l_int32  width,
                  l_int32  thinfirst,
                  l_int32  connectivity)
{
char     buf[16];
l_int32  border;
PIX     *pix1, *pix2, *pixd;

    PROCNAME("pixSetStrokeWidth");

    if (!pixs || (pixGetDepth(pixs) != 1))
        return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
    if (width < 1 || width > 100)
        return (PIX *)ERROR_PTR("width not in [1 ... 100]", procName, NULL);
    if (connectivity != 4 && connectivity != 8)
        return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);

    if (!thinfirst && width == 1)  /* nothing to do */
        return pixCopy(NULL, pixs);

        /* Add a white border */
    border = width / 2;
    pix1 = pixAddBorder(pixs, border, 0);

        /* Thin to a skeleton */
    if (thinfirst)
        pix2 = pixThinConnected(pix1, L_THIN_FG, connectivity, 0);
    else
        pix2 = pixClone(pix1);
    pixDestroy(&pix1);

        /* Dilate */
    snprintf(buf, sizeof(buf), "D%d.%d", width, width);
    pixd = pixMorphSequence(pix2, buf, 0);
    pixCopyText(pixd, pixs);
    pixDestroy(&pix2);
    return pixd;
}
示例#8
0
int main(int argc,
         char **argv) {
    char buffer1[256];
    char buffer2[256];
    l_int32 i, same, same2, factor1, factor2, diff, success;
    PIX *pixs, *pixsd, *pixt1, *pixt2, *pixt3;
    static char mainName[] = "binmorph2_reg";

    if (argc != 1)
        return ERROR_INT(" Syntax:  binmorph2_reg", mainName, 1);

    pixs = pixRead("rabi.png");
    pixsd = pixMorphCompSequence(pixs, "d5.5", 0);
    success = TRUE;
    for (i = 1; i < MAX_SEL_SIZE; i++) {

        /* Check if the size is exactly decomposable */
        selectComposableSizes(i, &factor1, &factor2);
        diff = factor1 * factor2 - i;
        fprintf(stderr, "%d: (%d, %d): %d\n", i, factor1, factor2, diff);

        /* Carry out operations on identical sized Sels: dilation */
        sprintf(buffer1, "d%d.%d", i + diff, i + diff);
        sprintf(buffer2, "d%d.%d", i, i);
        pixt1 = pixMorphSequence(pixsd, buffer1, 0);
        pixt2 = pixMorphCompSequence(pixsd, buffer2, 0);
        pixEqual(pixt1, pixt2, &same);
        if (i < 64) {
            pixt3 = pixMorphCompSequenceDwa(pixsd, buffer2, 0);
            pixEqual(pixt1, pixt3, &same2);
        } else {
            pixt3 = NULL;
            same2 = TRUE;
        }
        if (same && same2)
            writeResult(buffer1, 1);
        else {
            writeResult(buffer1, 0);
            success = FALSE;
        }
        pixDestroy(&pixt1);
        pixDestroy(&pixt2);
        pixDestroy(&pixt3);

        /* ... erosion */
        sprintf(buffer1, "e%d.%d", i + diff, i + diff);
        sprintf(buffer2, "e%d.%d", i, i);
        pixt1 = pixMorphSequence(pixsd, buffer1, 0);
        pixt2 = pixMorphCompSequence(pixsd, buffer2, 0);
        pixEqual(pixt1, pixt2, &same);
        if (i < 64) {
            pixt3 = pixMorphCompSequenceDwa(pixsd, buffer2, 0);
            pixEqual(pixt1, pixt3, &same2);
        } else {
            pixt3 = NULL;
            same2 = TRUE;
        }
        if (same && same2)
            writeResult(buffer1, 1);
        else {
            writeResult(buffer1, 0);
            success = FALSE;
        }
        pixDestroy(&pixt1);
        pixDestroy(&pixt2);
        pixDestroy(&pixt3);

        /* ... opening */
        sprintf(buffer1, "o%d.%d", i + diff, i + diff);
        sprintf(buffer2, "o%d.%d", i, i);
        pixt1 = pixMorphSequence(pixsd, buffer1, 0);
        pixt2 = pixMorphCompSequence(pixsd, buffer2, 0);
        pixEqual(pixt1, pixt2, &same);
        if (i < 64) {
            pixt3 = pixMorphCompSequenceDwa(pixsd, buffer2, 0);
            pixEqual(pixt1, pixt3, &same2);
        } else {
            pixt3 = NULL;
            same2 = TRUE;
        }
        if (same && same2)
            writeResult(buffer1, 1);
        else {
            writeResult(buffer1, 0);
            success = FALSE;
        }
        pixDestroy(&pixt1);
        pixDestroy(&pixt2);
        pixDestroy(&pixt3);

        /* ... closing */
        sprintf(buffer1, "c%d.%d", i + diff, i + diff);
        sprintf(buffer2, "c%d.%d", i, i);
        pixt1 = pixMorphSequence(pixsd, buffer1, 0);
        pixt2 = pixMorphCompSequence(pixsd, buffer2, 0);
        pixEqual(pixt1, pixt2, &same);
        if (i < 64) {
            pixt3 = pixMorphCompSequenceDwa(pixsd, buffer2, 0);
            pixEqual(pixt1, pixt3, &same2);
        } else {
            pixt3 = NULL;
            same2 = TRUE;
        }
        if (same && same2)
            writeResult(buffer1, 1);
        else {
            writeResult(buffer1, 0);
            success = FALSE;
        }
        pixDestroy(&pixt1);
        pixDestroy(&pixt2);
        pixDestroy(&pixt3);

    }
    pixDestroy(&pixs);
    pixDestroy(&pixsd);

    if (success)
        fprintf(stderr, "\n---------- Success: no errors ----------\n");
    else
        fprintf(stderr, "\n---------- Failure: error(s) found -----------\n");
    return 0;
}
示例#9
0
/*!
 *  pixUpDownDetectGeneral()
 *
 *      Input:  pixs (1 bpp, deskewed, English text, 150 - 300 ppi)
 *              &conf (<return> confidence that text is rightside-up)
 *              mincount (min number of up + down; use 0 for default)
 *              npixels (number of pixels removed from each side of word box)
 *              debug (1 for debug output; 0 otherwise)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) See pixOrientDetect() for other details.
 *      (2) @conf is the normalized difference between the number of
 *          detected up and down ascenders, assuming that the text
 *          is either rightside-up or upside-down and not rotated
 *          at a 90 degree angle.
 *      (3) The typical mode of operation is @npixels == 0.
 *          If @npixels > 0, this removes HMT matches at the
 *          beginning and ending of "words."  This is useful for
 *          pages that may have mostly digits, because if npixels == 0,
 *          leading "1" and "3" digits can register as having
 *          ascenders or descenders, and "7" digits can match descenders.
 *          Consequently, a page image of only digits may register
 *          as being upside-down.
 *      (4) We want to count the number of instances found using the HMT.
 *          An expensive way to do this would be to count the
 *          number of connected components.  A cheap way is to do a rank
 *          reduction cascade that reduces each component to a single
 *          pixel, and results (after two or three 2x reductions)
 *          in one pixel for each of the original components.
 *          After the reduction, you have a much smaller pix over
 *          which to count pixels.  We do only 2 reductions, because
 *          this function is designed to work for input pix between
 *          150 and 300 ppi, and an 8x reduction on a 150 ppi image
 *          is going too far -- components will get merged.
 */
l_int32
pixUpDownDetectGeneral(PIX        *pixs,
                       l_float32  *pconf,
                       l_int32     mincount,
                       l_int32     npixels,
                       l_int32     debug)
{
l_int32    countup, countdown, nmax;
l_float32  nup, ndown;
PIX       *pixt0, *pixt1, *pixt2, *pixt3, *pixm;
SEL       *sel1, *sel2, *sel3, *sel4;

    PROCNAME("pixUpDownDetectGeneral");

    if (!pconf)
        return ERROR_INT("&conf not defined", procName, 1);
    *pconf = 0.0;
    if (!pixs)
        return ERROR_INT("pixs not defined", procName, 1);
    if (mincount == 0)
        mincount = DEFAULT_MIN_UP_DOWN_COUNT;
    if (npixels < 0)
        npixels = 0;

    sel1 = selCreateFromString(textsel1, 5, 6, NULL);
    sel2 = selCreateFromString(textsel2, 5, 6, NULL);
    sel3 = selCreateFromString(textsel3, 5, 6, NULL);
    sel4 = selCreateFromString(textsel4, 5, 6, NULL);

        /* One of many reasonable pre-filtering sequences: (1, 8) and (30, 1).
         * This closes holes in x-height characters and joins them at
         * the x-height.  There is more noise in the descender detection
         * from this, but it works fairly well. */
    pixt0 = pixMorphCompSequence(pixs, "c1.8 + c30.1", 0);

        /* Optionally, make a mask of the word bounding boxes, shortening
         * each of them by a fixed amount at each end. */
    pixm = NULL;
    if (npixels > 0) {
        l_int32  i, nbox, x, y, w, h;
        BOX   *box;
        BOXA  *boxa;
        pixt1 = pixMorphSequence(pixt0, "o10.1", 0);
        boxa = pixConnComp(pixt1, NULL, 8);
        pixm = pixCreateTemplate(pixt1);
        pixDestroy(&pixt1);
        nbox = boxaGetCount(boxa);
        for (i = 0; i < nbox; i++) {
            box = boxaGetBox(boxa, i, L_CLONE);
            boxGetGeometry(box, &x, &y, &w, &h);
            if (w > 2 * npixels)
                pixRasterop(pixm, x + npixels, y - 6, w - 2 * npixels, h + 13,
                            PIX_SET, NULL, 0, 0);
            boxDestroy(&box);
        }
        boxaDestroy(&boxa);
    }

        /* Find the ascenders and optionally filter with pixm.
         * For an explanation of the procedure used for counting the result
         * of the HMT, see comments at the beginning of this function. */
    pixt1 = pixHMT(NULL, pixt0, sel1);
    pixt2 = pixHMT(NULL, pixt0, sel2);
    pixOr(pixt1, pixt1, pixt2);
    if (pixm)
        pixAnd(pixt1, pixt1, pixm);
    pixt3 = pixReduceRankBinaryCascade(pixt1, 1, 1, 0, 0);
    pixCountPixels(pixt3, &countup, NULL);
    pixDebugFlipDetect("junkpixup", pixs, pixt1, debug);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixDestroy(&pixt3);

        /* Find the ascenders and optionally filter with pixm. */
    pixt1 = pixHMT(NULL, pixt0, sel3);
    pixt2 = pixHMT(NULL, pixt0, sel4);
    pixOr(pixt1, pixt1, pixt2);
    if (pixm)
        pixAnd(pixt1, pixt1, pixm);
    pixt3 = pixReduceRankBinaryCascade(pixt1, 1, 1, 0, 0);
    pixCountPixels(pixt3, &countdown, NULL);
    pixDebugFlipDetect("junkpixdown", pixs, pixt1, debug);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixDestroy(&pixt3);

        /* Evaluate statistically, generating a confidence that is
         * related to the probability with a gaussian distribution. */
    nup = (l_float32)(countup);
    ndown = (l_float32)(countdown);
    nmax = L_MAX(countup, countdown);
    if (nmax > mincount)
        *pconf = 2. * ((nup - ndown) / sqrt(nup + ndown));

    if (debug) {
        if (pixm) pixWrite("junkpixm1", pixm, IFF_PNG);
        fprintf(stderr, "nup = %7.3f, ndown = %7.3f, conf = %7.3f\n",
                nup, ndown, *pconf);
        if (*pconf > DEFAULT_MIN_UP_DOWN_CONF)
            fprintf(stderr, "Text is rightside-up\n");
        if (*pconf < -DEFAULT_MIN_UP_DOWN_CONF)
            fprintf(stderr, "Text is upside-down\n");
    }

    pixDestroy(&pixt0);
    pixDestroy(&pixm);
    selDestroy(&sel1);
    selDestroy(&sel2);
    selDestroy(&sel3);
    selDestroy(&sel4);
    return 0;
}
示例#10
0
int main(int    argc,
         char **argv)
{
l_int32       i, w, h, nbox, npta, fgcount, bgcount, count;
BOXA         *boxa;
PIX          *pixs, *pixfg, *pixbg, *pixc, *pixb, *pixd;
PIX          *pix1, *pix2, *pix3, *pix4;
PIXA         *pixa;
PTA          *pta;
PTAA         *ptaafg, *ptaabg;
L_REGPARAMS  *rp;

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

    pixs = pixRead("feyn-fract.tif");
    boxa = pixConnComp(pixs, NULL, 8);
    nbox = boxaGetCount(boxa);
    regTestCompareValues(rp, nbox, 464, 0);  /* 0 */

        /* Get fg and bg boundary pixels */
    pixfg = pixMorphSequence(pixs, "e3.3", 0);
    pixXor(pixfg, pixfg, pixs);
    pixCountPixels(pixfg, &fgcount, NULL);
    regTestCompareValues(rp, fgcount, 58764, 0);  /* 1 */

    pixbg = pixMorphSequence(pixs, "d3.3", 0);
    pixXor(pixbg, pixbg, pixs);
    pixCountPixels(pixbg, &bgcount, NULL);
    regTestCompareValues(rp, bgcount, 60335, 0);  /* 2 */

        /* Get ptaa of fg pixels */
    ptaafg = ptaaGetBoundaryPixels(pixs, L_BOUNDARY_FG, 8, NULL, NULL);
    npta = ptaaGetCount(ptaafg);
    regTestCompareValues(rp, npta, nbox, 0);  /* 3 */
    count = 0;
    for (i = 0; i < npta; i++) {
        pta = ptaaGetPta(ptaafg, i, L_CLONE);
        count += ptaGetCount(pta);
        ptaDestroy(&pta);
    }
    regTestCompareValues(rp, fgcount, count, 0);  /* 4 */

        /* Get ptaa of bg pixels.  Note that the number of bg pts
         * is, in general, larger than the number of bg boundary pixels,
         * because bg boundary pixels are shared by two c.c. that
         * are 1 pixel apart. */
    ptaabg = ptaaGetBoundaryPixels(pixs, L_BOUNDARY_BG, 8, NULL, NULL);
    npta = ptaaGetCount(ptaabg);
    regTestCompareValues(rp, npta, nbox, 0);  /* 5 */
    count = 0;
    for (i = 0; i < npta; i++) {
        pta = ptaaGetPta(ptaabg, i, L_CLONE);
        count += ptaGetCount(pta);
        ptaDestroy(&pta);
    }
    regTestCompareValues(rp, count, 60602, 0);  /* 6 */

        /* Render the fg boundary pixels on top of pixs. */
    pixa = pixaCreate(4);
    pixc = pixRenderRandomCmapPtaa(pixs, ptaafg, 0, 0, 0);
    regTestWritePixAndCheck(rp, pixc, IFF_PNG);  /* 7 */
    pixSaveTiledOutline(pixc, pixa, 1.0, 1, 30, 2, 32);
    pixDestroy(&pixc);

        /* Render the bg boundary pixels on top of pixs. */
    pixc = pixRenderRandomCmapPtaa(pixs, ptaabg, 0, 0, 0);
    regTestWritePixAndCheck(rp, pixc, IFF_PNG);  /* 8 */
    pixSaveTiledOutline(pixc, pixa, 1.0, 0, 30, 2, 32);
    pixDestroy(&pixc);

    pixClearAll(pixs);

        /* Render the fg boundary pixels alone. */
    pixc = pixRenderRandomCmapPtaa(pixs, ptaafg, 0, 0, 0);
    regTestWritePixAndCheck(rp, pixc, IFF_PNG);  /* 9 */
    pixSaveTiledOutline(pixc, pixa, 1.0, 1, 30, 2, 32);

        /* Verify that the fg pixels are the same set as we
         * originally started with. */
    pixb = pixConvertTo1(pixc, 255);
    regTestComparePix(rp, pixb, pixfg);  /* 10 */
    pixDestroy(&pixc);
    pixDestroy(&pixb);

        /* Render the bg boundary pixels alone. */
    pixc = pixRenderRandomCmapPtaa(pixs, ptaabg, 0, 0, 0);
    regTestWritePixAndCheck(rp, pixc, IFF_PNG);  /* 11 */
    pixSaveTiledOutline(pixc, pixa, 1.0, 0, 30, 2, 32);

        /* Verify that the bg pixels are the same set as we
         * originally started with. */
    pixb = pixConvertTo1(pixc, 255);
    regTestComparePix(rp, pixb, pixbg);  /* 12 */
    pixDestroy(&pixc);
    pixDestroy(&pixb);

    pixd = pixaDisplay(pixa, 0, 0);
    pixDisplayWithTitle(pixd, 0, 0, NULL, rp->display);
    ptaaDestroy(&ptaafg);
    ptaaDestroy(&ptaabg);
    pixDestroy(&pixs);
    pixDestroy(&pixfg);
    pixDestroy(&pixbg);
    pixDestroy(&pixd);
    pixaDestroy(&pixa);
    boxaDestroy(&boxa);

        /* Test rotation */
    pix1 = pixRead("feyn-word.tif");
    pix2 = pixAddBorderGeneral(pix1, 200, 200, 200, 200, 0);
    pixa = pixaCreate(0);
    pix3 = PtaDisplayRotate(pix2, 0, 0);
    pixaAddPix(pixa, pix3, L_INSERT);
    pix3 = PtaDisplayRotate(pix2, 500, 100);
    pixaAddPix(pixa, pix3, L_INSERT);
    pix3 = PtaDisplayRotate(pix2, 100, 410);
    pixaAddPix(pixa, pix3, L_INSERT);
    pix3 = PtaDisplayRotate(pix2, 500, 410);
    pixaAddPix(pixa, pix3, L_INSERT);
    pix4 = pixaDisplayTiledInRows(pixa, 32, 1500, 1.0, 0, 30, 2);
    regTestWritePixAndCheck(rp, pix4, IFF_PNG);  /* 13 */
    pixDisplayWithTitle(pix4, 800, 0, NULL, rp->display);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pix4);
    pixaDestroy(&pixa);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    pixDestroy(&pixs);
    selDestroy(&sel);
    exit(0);
}
示例#12
0
/*!
 *  pixGetTextlineCenters()
 *
 *      Input:  pixs (1 bpp)
 *              debugflag (1 for debug output)
 *      Return: ptaa (of center values of textlines)
 *
 *  Notes:
 *      (1) This in general does not have a point for each value
 *          of x, because there will be gaps between words.
 *          It doesn't matter because we will fit a quadratic to the
 *          points that we do have.
 */
PTAA *
pixGetTextlineCenters(PIX     *pixs,
                      l_int32  debugflag)
{
l_int32   i, w, h, bx, by, nsegs;
BOXA     *boxa;
PIX      *pix, *pixt1, *pixt2, *pixt3;
PIXA     *pixa1, *pixa2;
PTA      *pta;
PTAA     *ptaa;

    PROCNAME("pixGetTextlineCenters");

    if (!pixs || pixGetDepth(pixs) != 1)
        return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
    pixGetDimensions(pixs, &w, &h, NULL);

        /* Filter to solidify the text lines within the x-height region,
         * and to remove most of the ascenders and descenders. */
    pixt1 = pixMorphSequence(pixs, "c15.1 + o15.1 + c30.1", 0);
    pixDisplayWithTitle(pixt1, 0, 800, "pix1", debugflag);

        /* Get the 8-connected components ... */
    boxa = pixConnComp(pixt1, &pixa1, 8);
    pixDestroy(&pixt1);
    boxaDestroy(&boxa);
    if (pixaGetCount(pixa1) == 0) {
        pixaDestroy(&pixa1);
        return NULL;
    }

        /* ... and remove the short and thin c.c */
    pixa2 = pixaSelectBySize(pixa1, 100, 4, L_SELECT_IF_BOTH,
                                   L_SELECT_IF_GT, 0);
    if ((nsegs = pixaGetCount(pixa2)) == 0) {
        pixaDestroy(&pixa2);
        return NULL;
    }
    if (debugflag) {
        pixt2 = pixaDisplay(pixa2, w, h);
        pixDisplayWithTitle(pixt2, 800, 800, "pix2", 1);
        pixDestroy(&pixt2);
    }

        /* For each c.c., get the weighted center of each vertical column.
         * The result is a set of points going approximately through
         * the center of the x-height part of the text line.  */
    ptaa = ptaaCreate(nsegs);
    for (i = 0; i < nsegs; i++) {
        pixaGetBoxGeometry(pixa2, i, &bx, &by, NULL, NULL);
        pix = pixaGetPix(pixa2, i, L_CLONE);
        pta = pixGetMeanVerticals(pix, bx, by);
        ptaaAddPta(ptaa, pta, L_INSERT);
        pixDestroy(&pix);
    }
    if (debugflag) {
        pixt3 = pixCreateTemplate(pixt2);
        pix = pixDisplayPtaa(pixt3, ptaa);
        pixDisplayWithTitle(pix, 0, 1400, "pix3", 1);
        pixDestroy(&pix);
        pixDestroy(&pixt3);
    }

    pixaDestroy(&pixa1);
    pixaDestroy(&pixa2);
    return ptaa;
}
示例#13
0
main(int    argc,
     char **argv)
{
char        *filein, *fileout;
l_int32      thresh;
PIX         *pixs, *pixg, *pixb;
PIX         *pixmask4, *pixseed4, *pixsf4, *pixd4, *pixd;
static char  mainName[] = "pagesegtest2";

    if (argc != 4)
	exit(ERROR_INT(" Syntax:  pagesegtest2 filein thresh fileout",
                       mainName, 1));

    filein = argv[1];
    thresh = atoi(argv[2]);
    fileout = argv[3];

        /* Get a 1 bpp version of the page */
    if ((pixs = pixRead(filein)) == NULL)
	exit(ERROR_INT("pixs not made", mainName, 1));
    if (pixGetDepth(pixs) == 32)
        pixg = pixConvertRGBToGrayFast(pixs);
    else
        pixg = pixClone(pixs);
    if (pixGetDepth(pixg) == 8)
        pixb = pixThresholdToBinary(pixg, thresh);
    else
        pixb = pixClone(pixg);

        /* Make seed and mask, and fill seed into mask */
    pixseed4 = pixMorphSequence(pixb, seed_sequence, 0);
    pixmask4 = pixMorphSequence(pixb, mask_sequence, 0);
    pixsf4 = pixSeedfillBinary(NULL, pixseed4, pixmask4, 8);
    pixd4 = pixMorphSequence(pixsf4, dilation_sequence, 0);

        /* Mask at full resolution */
    pixd = pixExpandBinaryPower2(pixd4, 4);
    pixWrite(fileout, pixd, IFF_TIFF_G4);

        /* Extract non-image parts (e.g., text) at full resolution */
    pixSubtract(pixb, pixb, pixd);

    pixDisplayWithTitle(pixseed4, 400, 100, "halftone seed", DFLAG);
    pixDisplayWithTitle(pixmask4, 100, 100, "halftone seed mask", DFLAG);
    pixDisplayWithTitle(pixd4, 700, 100, "halftone mask", DFLAG);
    pixDisplayWithTitle(pixb, 1000, 100, "non-halftone", DFLAG);

#if 1
    pixWrite("junkseed", pixseed4, IFF_TIFF_G4);
    pixWrite("junkmask", pixmask4, IFF_TIFF_G4);
    pixWrite("junkfill", pixd4, IFF_TIFF_G4);
    pixWrite("junktext", pixb, IFF_TIFF_G4);
#endif

    pixDestroy(&pixs);
    pixDestroy(&pixg);
    pixDestroy(&pixb);
    pixDestroy(&pixseed4);
    pixDestroy(&pixmask4);
    pixDestroy(&pixsf4);
    pixDestroy(&pixd4);
    pixDestroy(&pixd);
    exit(0);
}
示例#14
0
int main(int    argc,
         char **argv)
{
char         buffer[512];
char        *tempfile1, *tempfile2;
l_uint8     *data;
l_int32      i, j, w, h, seq, ret, same;
size_t       nbytes;
const char  *title;
BOX         *box;
BOXA        *boxa1, *boxa2;
L_BYTEA     *ba;
L_PDF_DATA  *lpd;
PIX         *pix1, *pix2, *pix3, *pix4, *pix5, *pix6;
PIX         *pixs, *pixt, *pixg, *pixgc, *pixc;
static char  mainName[] = "pdfiotest";

    if (argc != 1)
        return ERROR_INT("syntax: pdfiotest", mainName, 1);
    l_pdfSetDateAndVersion(0);

    lept_mkdir("lept/pdf");

#if 1
    /* ---------------  Single image tests  ------------------- */
    fprintf(stderr, "\n*** Writing single images as pdf files\n");

    convertToPdf("weasel2.4c.png", L_FLATE_ENCODE, 0, "/tmp/lept/pdf/file01.pdf",
                 0, 0, 72, "weasel2.4c.png", NULL, 0);
    convertToPdf("test24.jpg", L_JPEG_ENCODE, 0, "/tmp/lept/pdf/file02.pdf",
                 0, 0, 72, "test24.jpg", NULL, 0);
    convertToPdf("feyn.tif", L_G4_ENCODE, 0, "/tmp/lept/pdf/file03.pdf",
                 0, 0, 300, "feyn.tif", NULL, 0);

    pixs = pixRead("feyn.tif");
    pixConvertToPdf(pixs, L_G4_ENCODE, 0, "/tmp/lept/pdf/file04.pdf", 0, 0, 300,
                    "feyn.tif", NULL, 0);
    pixDestroy(&pixs);

    pixs = pixRead("test24.jpg");
    pixConvertToPdf(pixs, L_JPEG_ENCODE, 5, "/tmp/lept/pdf/file05.pdf",
                    0, 0, 72, "test24.jpg", NULL, 0);
    pixDestroy(&pixs);

    pixs = pixRead("feyn.tif");
    pixt = pixScaleToGray2(pixs);
    pixWrite("/tmp/lept/pdf/feyn8.png", pixt, IFF_PNG);
    convertToPdf("/tmp/lept/pdf/feyn8.png", L_JPEG_ENCODE, 0,
                 "/tmp/lept/pdf/file06.pdf", 0, 0, 150, "feyn8.png", NULL, 0);
    pixDestroy(&pixs);
    pixDestroy(&pixt);

    convertToPdf("weasel4.16g.png", L_FLATE_ENCODE, 0,
                 "/tmp/lept/pdf/file07.pdf", 0, 0, 30,
                 "weasel4.16g.png", NULL, 0);

    pixs = pixRead("test24.jpg");
    pixg = pixConvertTo8(pixs, 0);
    box = boxCreate(100, 100, 100, 100);
    pixc = pixClipRectangle(pixs, box, NULL);
    pixgc = pixClipRectangle(pixg, box, NULL);
    pixWrite("/tmp/lept/pdf/pix32.jpg", pixc, IFF_JFIF_JPEG);
    pixWrite("/tmp/lept/pdf/pix8.jpg", pixgc, IFF_JFIF_JPEG);
    convertToPdf("/tmp/lept/pdf/pix32.jpg", L_FLATE_ENCODE, 0,
                 "/tmp/lept/pdf/file08.pdf", 0, 0, 72, "pix32.jpg", NULL, 0);
    convertToPdf("/tmp/lept/pdf/pix8.jpg", L_FLATE_ENCODE, 0,
                 "/tmp/lept/pdf/file09.pdf", 0, 0, 72, "pix8.jpg", NULL, 0);
    pixDestroy(&pixs);
    pixDestroy(&pixg);
    pixDestroy(&pixc);
    pixDestroy(&pixgc);
    boxDestroy(&box);
#endif


#if 1
    /* ---------------  Multiple image tests  ------------------- */
    fprintf(stderr, "\n*** Writing multiple images as single page pdf files\n");

    pix1 = pixRead("feyn-fract.tif");
    pix2 = pixRead("weasel8.240c.png");

/*    l_pdfSetDateAndVersion(0); */
        /* First, write the 1 bpp image through the mask onto the weasels */
    for (i = 0; i < 5; i++) {
        for (j = 0; j < 10; j++) {
            seq = (i == 0 && j == 0) ? L_FIRST_IMAGE : L_NEXT_IMAGE;
            title = (i == 0 && j == 0) ? "feyn-fract.tif" : NULL;
            pixConvertToPdf(pix2, L_FLATE_ENCODE, 0, NULL, 100 * j,
                            100 * i, 70, title, &lpd, seq);
        }
    }
    pixConvertToPdf(pix1, L_G4_ENCODE, 0, "/tmp/lept/pdf/file10.pdf", 0, 0, 80,
                    NULL, &lpd, L_LAST_IMAGE);

        /* Now, write the 1 bpp image over the weasels */
    l_pdfSetG4ImageMask(0);
    for (i = 0; i < 5; i++) {
        for (j = 0; j < 10; j++) {
            seq = (i == 0 && j == 0) ? L_FIRST_IMAGE : L_NEXT_IMAGE;
            title = (i == 0 && j == 0) ? "feyn-fract.tif" : NULL;
            pixConvertToPdf(pix2, L_FLATE_ENCODE, 0, NULL, 100 * j,
                            100 * i, 70, title, &lpd, seq);
        }
    }
    pixConvertToPdf(pix1, L_G4_ENCODE, 0, "/tmp/lept/pdf/file11.pdf", 0, 0, 80,
                    NULL, &lpd, L_LAST_IMAGE);
    l_pdfSetG4ImageMask(1);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
#endif

#if 1
    /* -------- pdf convert segmented with no image regions -------- */
    fprintf(stderr, "\n*** Writing segmented images without image regions\n");

    pix1 = pixRead("rabi.png");
    pix2 = pixScaleToGray2(pix1);
    pixWrite("/tmp/lept/pdf/rabi8.jpg", pix2, IFF_JFIF_JPEG);
    pix3 = pixThresholdTo4bpp(pix2, 16, 1);
    pixWrite("/tmp/lept/pdf/rabi4.png", pix3, IFF_PNG);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pix3);

        /* 1 bpp input */
    convertToPdfSegmented("rabi.png", 300, L_G4_ENCODE, 128, NULL, 0, 0,
                          NULL, "/tmp/lept/pdf/file12.pdf");
    convertToPdfSegmented("rabi.png", 300, L_JPEG_ENCODE, 128, NULL, 0, 0,
                          NULL, "/tmp/lept/pdf/file13.pdf");
    convertToPdfSegmented("rabi.png", 300, L_FLATE_ENCODE, 128, NULL, 0, 0,
                          NULL, "/tmp/lept/pdf/file14.pdf");

        /* 8 bpp input, no cmap */
    convertToPdfSegmented("/tmp/lept/pdf/rabi8.jpg", 150, L_G4_ENCODE, 128,
                          NULL, 0, 0, NULL, "/tmp/lept/pdf/file15.pdf");
    convertToPdfSegmented("/tmp/lept/pdf/rabi8.jpg", 150, L_JPEG_ENCODE, 128,
                          NULL, 0, 0, NULL, "/tmp/lept/pdf/file16.pdf");
    convertToPdfSegmented("/tmp/lept/pdf/rabi8.jpg", 150, L_FLATE_ENCODE, 128,
                          NULL, 0, 0, NULL, "/tmp/lept/pdf/file17.pdf");

        /* 4 bpp input, cmap */
    convertToPdfSegmented("/tmp/lept/pdf/rabi4.png", 150, L_G4_ENCODE, 128,
                          NULL, 0, 0, NULL, "/tmp/lept/pdf/file18.pdf");
    convertToPdfSegmented("/tmp/lept/pdf/rabi4.png", 150, L_JPEG_ENCODE, 128,
                          NULL, 0, 0, NULL, "/tmp/lept/pdf/file19.pdf");
    convertToPdfSegmented("/tmp/lept/pdf/rabi4.png", 150, L_FLATE_ENCODE, 128,
                          NULL, 0, 0, NULL, "/tmp/lept/pdf/file20.pdf");

#endif

#if 1
    /* ---------- pdf convert segmented with image regions ---------- */
    fprintf(stderr, "\n*** Writing segmented images with image regions\n");

        /* Get the image region(s) for rabi.png.  There are two
         * small bogus regions at the top, but we'll keep them for
         * the demonstration. */
    pix1 = pixRead("rabi.png");
    pixSetResolution(pix1, 300, 300);
    pixGetDimensions(pix1, &w, &h, NULL);
    pix2 = pixGenerateHalftoneMask(pix1, NULL, NULL, NULL);
    pix3 = pixMorphSequence(pix2, "c20.1 + c1.20", 0);
    boxa1 = pixConnComp(pix3, NULL, 8);
    boxa2 = boxaTransform(boxa1, 0, 0, 0.5, 0.5);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pix3);

        /* 1 bpp input */
    convertToPdfSegmented("rabi.png", 300, L_G4_ENCODE, 128, boxa1,
                          0, 0.25, NULL, "/tmp/lept/pdf/file21.pdf");
    convertToPdfSegmented("rabi.png", 300, L_JPEG_ENCODE, 128, boxa1,
                          0, 0.25, NULL, "/tmp/lept/pdf/file22.pdf");
    convertToPdfSegmented("rabi.png", 300, L_FLATE_ENCODE, 128, boxa1,
                          0, 0.25, NULL, "/tmp/lept/pdf/file23.pdf");

        /* 8 bpp input, no cmap */
    convertToPdfSegmented("/tmp/lept/pdf/rabi8.jpg", 150, L_G4_ENCODE, 128,
                          boxa2, 0, 0.5, NULL, "/tmp/lept/pdf/file24.pdf");
    convertToPdfSegmented("/tmp/lept/pdf/rabi8.jpg", 150, L_JPEG_ENCODE, 128,
                          boxa2, 0, 0.5, NULL, "/tmp/lept/pdf/file25.pdf");
    convertToPdfSegmented("/tmp/lept/pdf/rabi8.jpg", 150, L_FLATE_ENCODE, 128,
                          boxa2, 0, 0.5, NULL, "/tmp/lept/pdf/file26.pdf");

        /* 4 bpp input, cmap */
    convertToPdfSegmented("/tmp/lept/pdf/rabi4.png", 150, L_G4_ENCODE, 128,
                          boxa2, 0, 0.5, NULL, "/tmp/lept/pdf/file27.pdf");
    convertToPdfSegmented("/tmp/lept/pdf/rabi4.png", 150, L_JPEG_ENCODE, 128,
                          boxa2, 0, 0.5, NULL, "/tmp/lept/pdf/file28.pdf");
    convertToPdfSegmented("/tmp/lept/pdf/rabi4.png", 150, L_FLATE_ENCODE, 128,
                          boxa2, 0, 0.5, NULL, "/tmp/lept/pdf/file29.pdf");

        /* 4 bpp input, cmap, data output */
    data = NULL;
    convertToPdfDataSegmented("/tmp/lept/pdf/rabi4.png", 150, L_G4_ENCODE,
                              128, boxa2, 0, 0.5, NULL, &data, &nbytes);
    l_binaryWrite("/tmp/lept/pdf/file30.pdf", "w", data, nbytes);
    lept_free(data);
    convertToPdfDataSegmented("/tmp/lept/pdf/rabi4.png", 150, L_JPEG_ENCODE,
                              128, boxa2, 0, 0.5, NULL, &data, &nbytes);
    l_binaryWrite("/tmp/lept/pdf/file31.pdf", "w", data, nbytes);
    lept_free(data);
    convertToPdfDataSegmented("/tmp/lept/pdf/rabi4.png", 150, L_FLATE_ENCODE,
                              128, boxa2, 0, 0.5, NULL, &data, &nbytes);
    l_binaryWrite("/tmp/lept/pdf/file32.pdf", "w", data, nbytes);
    lept_free(data);

    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);
#endif


#if 1
    /* -------- pdf convert segmented from color image -------- */
    fprintf(stderr, "\n*** Writing color segmented images\n");

    pix1 = pixRead("candelabrum.011.jpg");
    pix2 = pixScale(pix1, 3.0, 3.0);
    pixWrite("/tmp/lept/pdf/candelabrum3.jpg", pix2, IFF_JFIF_JPEG);
    GetImageMask(pix2, 200, &boxa1, "/tmp/lept/pdf/seg1.jpg");
    convertToPdfSegmented("/tmp/lept/pdf/candelabrum3.jpg", 200, L_G4_ENCODE,
                          100, boxa1, 0, 0.25, NULL,
                          "/tmp/lept/pdf/file33.pdf");
    convertToPdfSegmented("/tmp/lept/pdf/candelabrum3.jpg", 200, L_JPEG_ENCODE,
                          100, boxa1, 0, 0.25, NULL,
                          "/tmp/lept/pdf/file34.pdf");
    convertToPdfSegmented("/tmp/lept/pdf/candelabrum3.jpg", 200, L_FLATE_ENCODE,
                          100, boxa1, 0, 0.25, NULL,
                          "/tmp/lept/pdf/file35.pdf");

    pixDestroy(&pix1);
    pixDestroy(&pix2);
    boxaDestroy(&boxa1);

    pix1 = pixRead("lion-page.00016.jpg");
    pix2 = pixScale(pix1, 3.0, 3.0);
    pixWrite("/tmp/lept/pdf/lion16.jpg", pix2, IFF_JFIF_JPEG);
    pix3 = pixRead("lion-mask.00016.tif");
    boxa1 = pixConnComp(pix3, NULL, 8);
    boxa2 = boxaTransform(boxa1, 0, 0, 3.0, 3.0);
    convertToPdfSegmented("/tmp/lept/pdf/lion16.jpg", 200, L_G4_ENCODE,
                          190, boxa2, 0, 0.5, NULL, "/tmp/lept/pdf/file36.pdf");
    convertToPdfSegmented("/tmp/lept/pdf/lion16.jpg", 200, L_JPEG_ENCODE,
                          190, boxa2, 0, 0.5, NULL, "/tmp/lept/pdf/file37.pdf");
    convertToPdfSegmented("/tmp/lept/pdf/lion16.jpg", 200, L_FLATE_ENCODE,
                          190, boxa2, 0, 0.5, NULL, "/tmp/lept/pdf/file38.pdf");

        /* Quantize the non-image part and flate encode.
         * This is useful because it results in a smaller file than
         * when you flate-encode the un-quantized non-image regions. */
    pix4 = pixScale(pix3, 3.0, 3.0);  /* higher res mask, for combining */
    pix5 = QuantizeNonImageRegion(pix2, pix4, 12);
    pixWrite("/tmp/lept/pdf/lion16-quant.png", pix5, IFF_PNG);
    convertToPdfSegmented("/tmp/lept/pdf/lion16-quant.png", 200, L_FLATE_ENCODE,
                          190, boxa2, 0, 0.5, NULL, "/tmp/lept/pdf/file39.pdf");

    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pix3);
    pixDestroy(&pix4);
    pixDestroy(&pix5);
    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);
#endif

#if 1
    /* ------------------ Test multipage pdf generation ----------------- */
    fprintf(stderr, "\n*** Writing multipage pdfs from single page pdfs\n");

        /* Generate a multi-page pdf from all these files */
    startTimer();
    concatenatePdf("/tmp/lept/pdf", "file", "/tmp/lept/pdf/cat_lept.pdf");
    fprintf(stderr,
            "All files have been concatenated: /tmp/lept/pdf/cat_lept.pdf\n"
                    "Concatenation time: %7.3f\n", stopTimer());
#endif

#if 1
    /* ----------- Test corruption recovery by concatenation ------------ */
        /* Put two good pdf files in a directory */
    lept_rmdir("lept/good");
    lept_mkdir("lept/good");
    lept_cp("testfile1.pdf", "lept/good", NULL, NULL);
    lept_cp("testfile2.pdf", "lept/good", NULL, NULL);
    concatenatePdf("/tmp/lept/good", "file", "/tmp/lept/pdf/good.pdf");

        /* Make a bad version with the pdf id removed, so that it is not
         * recognized as a pdf */
    lept_rmdir("lept/bad");
    lept_mkdir("lept/bad");
    ba = l_byteaInitFromFile("testfile2.pdf");
    data = l_byteaGetData(ba, &nbytes);
    l_binaryWrite("/tmp/lept/bad/testfile0.notpdf.pdf", "w",
                  data + 10, nbytes - 10);

        /* Make a version with a corrupted trailer */
    if (data)
        data[2297] = '2';  /* munge trailer object 6: change 458 --> 428 */
    l_binaryWrite("/tmp/lept/bad/testfile2.bad.pdf", "w", data, nbytes);
    l_byteaDestroy(&ba);

        /* Copy testfile1.pdf to the /tmp/lept/bad directory.  Then
         * run concat on the bad files.  The "not pdf" file should be
         * ignored, and the corrupted pdf file should be properly parsed,
         * so the resulting concatenated pdf files should be identical.  */
    fprintf(stderr, "\nWe attempt to build from the bad directory\n");
    lept_cp("testfile1.pdf", "lept/bad", NULL, NULL);
    concatenatePdf("/tmp/lept/bad", "file", "/tmp/lept/pdf/bad.pdf");
    filesAreIdentical("/tmp/lept/pdf/good.pdf", "/tmp/lept/pdf/bad.pdf", &same);
    if (same)
        fprintf(stderr, "Fixed: files are the same\n"
                        "Attempt succeeded\n");
    else
        fprintf(stderr, "Busted: files are different\n");
#endif

#if 0
    fprintf(stderr, "\n*** pdftk writes multipage pdfs from images\n");
    tempfile1 = genPathname("/tmp/lept/pdf", "file*.pdf");
    tempfile2 = genPathname("/tmp/lept/pdf", "cat_pdftk.pdf");
    snprintf(buffer, sizeof(buffer), "pdftk %s output %s",
             tempfile1, tempfile2);
    ret = system(buffer);  /* pdftk */
    lept_free(tempfile1);
    lept_free(tempfile2);
#endif

#if 1
    /* -- Test simple interface for generating multi-page pdf from images -- */
    fprintf(stderr, "\n*** Writing multipage pdfs from images\n");

        /* Put four image files in a directory.  They will be encoded thus:
         *     file1.png:  flate (8 bpp, only 10 colors)
         *     file2.jpg:  dct (8 bpp, 256 colors because of the jpeg encoding)
         *     file3.tif:  g4 (1 bpp)
         *     file4.jpg:  dct (32 bpp)    */
    lept_mkdir("lept/image");
    pix1 = pixRead("feyn.tif");
    pix2 = pixRead("rabi.png");
    pix3 = pixScaleToGray3(pix1);
    pix4 = pixScaleToGray3(pix2);
    pix5 = pixScale(pix1, 0.33, 0.33);
    pix6 = pixRead("test24.jpg");
    pixWrite("/tmp/lept/image/file1.png", pix3, IFF_PNG);  /* 10 colors */
    pixWrite("/tmp/lept/image/file2.jpg", pix4, IFF_JFIF_JPEG); /* 256 colors */
    pixWrite("/tmp/lept/image/file3.tif", pix5, IFF_TIFF_G4);
    pixWrite("/tmp/lept/image/file4.jpg", pix6, IFF_JFIF_JPEG);

    startTimer();
    convertFilesToPdf("/tmp/lept/image", "file", 100, 0.8, 0, 75, "4 file test",
                      "/tmp/lept/pdf/fourimages.pdf");
    fprintf(stderr, "4-page pdf generated: /tmp/lept/pdf/fourimages.pdf\n"
                    "Time: %7.3f\n", stopTimer());
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pix3);
    pixDestroy(&pix4);
    pixDestroy(&pix5);
    pixDestroy(&pix6);
#endif

    return 0;
}
示例#15
0
main(int    argc,
     char **argv)
{
char        *infile;
PIX         *pixs, *pixg, *pixc, *pixd;
static char  mainName[] = "morphseq_reg";

    if (argc != 1)
	return ERROR_INT(" Syntax:  morphseq_reg", mainName, 1);

    pixs = pixRead("feyn.tif");

        /* 1 bpp */
    pixd = pixMorphSequence(pixs, SEQUENCE1, -1);
    pixDestroy(&pixd);
    pixd = pixMorphSequence(pixs, SEQUENCE1, DISPLAY_SEPARATION);
    pixWrite("/tmp/morphseq1.png", pixd, IFF_PNG);
    pixDestroy(&pixd);

    pixd = pixMorphCompSequence(pixs, SEQUENCE2, -2);
    pixDestroy(&pixd);
    pixd = pixMorphCompSequence(pixs, SEQUENCE2, DISPLAY_SEPARATION);
    pixWrite("/tmp/morphseq2.png", pixd, IFF_PNG);
    pixDestroy(&pixd);

    pixd = pixMorphSequenceDwa(pixs, SEQUENCE2, -3);
    pixDestroy(&pixd);
    pixd = pixMorphSequenceDwa(pixs, SEQUENCE2, DISPLAY_SEPARATION);
    pixWrite("/tmp/morphseq3.png", pixd, IFF_PNG);
    pixDestroy(&pixd);

    pixd = pixMorphCompSequenceDwa(pixs, SEQUENCE2, -4);
    pixDestroy(&pixd);
    pixd = pixMorphCompSequenceDwa(pixs, SEQUENCE2, DISPLAY_SEPARATION);
    pixWrite("/tmp/morphseq4.png", pixd, IFF_PNG);
    pixDestroy(&pixd);

        /* 8 bpp */
    pixg = pixScaleToGray(pixs, 0.25);
    pixd = pixGrayMorphSequence(pixg, SEQUENCE3, -5, 150);
    pixDestroy(&pixd);
    pixd = pixGrayMorphSequence(pixg, SEQUENCE3, DISPLAY_SEPARATION, 150);
    pixWrite("/tmp/morphseq5.png", pixd, IFF_PNG);
    pixDestroy(&pixd);

    pixd = pixGrayMorphSequence(pixg, SEQUENCE4, -6, 300);
    pixWrite("/tmp/morphseq6.png", pixd, IFF_PNG);
    pixDestroy(&pixd);

        /* 32 bpp */
    pixc = pixRead("wyom.jpg");
    pixd = pixColorMorphSequence(pixc, SEQUENCE5, -7, 150);
    pixDestroy(&pixd);
    pixd = pixColorMorphSequence(pixc, SEQUENCE5, DISPLAY_SEPARATION, 450);
    pixWrite("/tmp/morphseq7.png", pixd, IFF_PNG);
    pixDestroy(&pixc);
    pixDestroy(&pixd);

        /* Syntax error handling */
    fprintf(stderr, " ------------ Error messages follow ------------------\n");
    pixd = pixMorphSequence(pixs, BAD_SEQUENCE, 50);  /* fails; returns null */
    pixd = pixGrayMorphSequence(pixg, BAD_SEQUENCE, 50, 0);  /* this fails */

    pixDestroy(&pixg);
    pixDestroy(&pixs);
    return 0;
}
示例#16
0
/*!
 * \brief   pixFindBaselines()
 *
 * \param[in]    pixs     1 bpp, 300 ppi
 * \param[out]   ppta     [optional] pairs of pts corresponding to
 *                        approx. ends of each text line
 * \param[in]    pixadb   for debug output; use NULL to skip
 * \return  na of baseline y values, or NULL on error
 *
 * <pre>
 * Notes:
 *      (1) Input binary image must have text lines already aligned
 *          horizontally.  This can be done by either rotating the
 *          image with pixDeskew(), or, if a projective transform
 *          is required, by doing pixDeskewLocal() first.
 *      (2) Input null for &pta if you don't want this returned.
 *          The pta will come in pairs of points (left and right end
 *          of each baseline).
 *      (3) Caution: this will not work properly on text with multiple
 *          columns, where the lines are not aligned between columns.
 *          If there are multiple columns, they should be extracted
 *          separately before finding the baselines.
 *      (4) This function constructs different types of output
 *          for baselines; namely, a set of raster line values and
 *          a set of end points of each baseline.
 *      (5) This function was designed to handle short and long text lines
 *          without using dangerous thresholds on the peak heights.  It does
 *          this by combining the differential signal with a morphological
 *          analysis of the locations of the text lines.  One can also
 *          combine this data to normalize the peak heights, by weighting
 *          the differential signal in the region of each baseline
 *          by the inverse of the width of the text line found there.
 * </pre>
 */
NUMA *
pixFindBaselines(PIX   *pixs,
                 PTA  **ppta,
                 PIXA  *pixadb)
{
l_int32    h, i, j, nbox, val1, val2, ndiff, bx, by, bw, bh;
l_int32    imaxloc, peakthresh, zerothresh, inpeak;
l_int32    mintosearch, max, maxloc, nloc, locval;
l_int32   *array;
l_float32  maxval;
BOXA      *boxa1, *boxa2, *boxa3;
GPLOT     *gplot;
NUMA      *nasum, *nadiff, *naloc, *naval;
PIX       *pix1, *pix2;
PTA       *pta;

    PROCNAME("pixFindBaselines");

    if (ppta) *ppta = NULL;
    if (!pixs || pixGetDepth(pixs) != 1)
        return (NUMA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);

        /* Close up the text characters, removing noise */
    pix1 = pixMorphSequence(pixs, "c25.1 + e15.1", 0);

        /* Estimate the resolution */
    if (pixadb) pixaAddPix(pixadb, pixScale(pix1, 0.25, 0.25), L_INSERT);

        /* Save the difference of adjacent row sums.
         * The high positive-going peaks are the baselines */
    if ((nasum = pixCountPixelsByRow(pix1, NULL)) == NULL) {
        pixDestroy(&pix1);
        return (NUMA *)ERROR_PTR("nasum not made", procName, NULL);
    }
    h = pixGetHeight(pixs);
    nadiff = numaCreate(h);
    numaGetIValue(nasum, 0, &val2);
    for (i = 0; i < h - 1; i++) {
        val1 = val2;
        numaGetIValue(nasum, i + 1, &val2);
        numaAddNumber(nadiff, val1 - val2);
    }
    numaDestroy(&nasum);

    if (pixadb) {  /* show the difference signal */
        lept_mkdir("lept/baseline");
        gplotSimple1(nadiff, GPLOT_PNG, "/tmp/lept/baseline/diff", "Diff Sig");
        pix2 = pixRead("/tmp/lept/baseline/diff.png");
        pixaAddPix(pixadb, pix2, L_INSERT);
    }

        /* Use the zeroes of the profile to locate each baseline. */
    array = numaGetIArray(nadiff);
    ndiff = numaGetCount(nadiff);
    numaGetMax(nadiff, &maxval, &imaxloc);
    numaDestroy(&nadiff);

        /* Use this to begin locating a new peak: */
    peakthresh = (l_int32)maxval / PEAK_THRESHOLD_RATIO;
        /* Use this to begin a region between peaks: */
    zerothresh = (l_int32)maxval / ZERO_THRESHOLD_RATIO;

    naloc = numaCreate(0);
    naval = numaCreate(0);
    inpeak = FALSE;
    for (i = 0; i < ndiff; i++) {
        if (inpeak == FALSE) {
            if (array[i] > peakthresh) {  /* transition to in-peak */
                inpeak = TRUE;
                mintosearch = i + MIN_DIST_IN_PEAK; /* accept no zeros
                                               * between i and mintosearch */
                max = array[i];
                maxloc = i;
            }
        } else {  /* inpeak == TRUE; look for max */
            if (array[i] > max) {
                max = array[i];
                maxloc = i;
                mintosearch = i + MIN_DIST_IN_PEAK;
            } else if (i > mintosearch && array[i] <= zerothresh) {  /* leave */
                inpeak = FALSE;
                numaAddNumber(naval, max);
                numaAddNumber(naloc, maxloc);
            }
        }
    }
    LEPT_FREE(array);

        /* If array[ndiff-1] is max, eg. no descenders, baseline at bottom */
    if (inpeak) {
        numaAddNumber(naval, max);
        numaAddNumber(naloc, maxloc);
    }

    if (pixadb) {  /* show the raster locations for the peaks */
        gplot = gplotCreate("/tmp/lept/baseline/loc", GPLOT_PNG, "Peak locs",
                            "rasterline", "height");
        gplotAddPlot(gplot, naloc, naval, GPLOT_POINTS, "locs");
        gplotMakeOutput(gplot);
        gplotDestroy(&gplot);
        pix2 = pixRead("/tmp/lept/baseline/loc.png");
        pixaAddPix(pixadb, pix2, L_INSERT);
    }
    numaDestroy(&naval);

        /* Generate an approximate profile of text line width.
         * First, filter the boxes of text, where there may be
         * more than one box for a given textline. */
    pix2 = pixMorphSequence(pix1, "r11 + c20.1 + o30.1 +c1.3", 0);
    if (pixadb) pixaAddPix(pixadb, pix2, L_COPY);
    boxa1 = pixConnComp(pix2, NULL, 4);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    if (boxaGetCount(boxa1) == 0) {
        numaDestroy(&naloc);
        boxaDestroy(&boxa1);
        L_INFO("no compnents after filtering\n", procName);
        return NULL;
    }
    boxa2 = boxaTransform(boxa1, 0, 0, 4., 4.);
    boxa3 = boxaSort(boxa2, L_SORT_BY_Y, L_SORT_INCREASING, NULL);
    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);

        /* Optionally, find the baseline segments */
    pta = NULL;
    if (ppta) {
        pta = ptaCreate(0);
        *ppta = pta;
    }
    if (pta) {
      nloc = numaGetCount(naloc);
      nbox = boxaGetCount(boxa3);
      for (i = 0; i < nbox; i++) {
          boxaGetBoxGeometry(boxa3, i, &bx, &by, &bw, &bh);
          for (j = 0; j < nloc; j++) {
              numaGetIValue(naloc, j, &locval);
              if (L_ABS(locval - (by + bh)) > 25)
                  continue;
              ptaAddPt(pta, bx, locval);
              ptaAddPt(pta, bx + bw, locval);
              break;
          }
      }
    }
    boxaDestroy(&boxa3);

    if (pixadb && pta) {  /* display baselines */
        l_int32  npts, x1, y1, x2, y2;
        pix1 = pixConvertTo32(pixs);
        npts = ptaGetCount(pta);
        for (i = 0; i < npts; i += 2) {
            ptaGetIPt(pta, i, &x1, &y1);
            ptaGetIPt(pta, i + 1, &x2, &y2);
            pixRenderLineArb(pix1, x1, y1, x2, y2, 2, 255, 0, 0);
        }
        pixWrite("/tmp/lept/baseline/baselines.png", pix1, IFF_PNG);
        pixaAddPix(pixadb, pixScale(pix1, 0.25, 0.25), L_INSERT);
        pixDestroy(&pix1);
    }

    return naloc;
}
示例#17
0
/*!
 *  pixMaskedThreshOnBackgroundNorm()
 *
 *      Input:  pixs (8 bpp grayscale; not colormapped)
 *              pixim (<optional> 1 bpp 'image' mask; can be null)
 *              sx, sy (tile size in pixels)
 *              thresh (threshold for determining foreground)
 *              mincount (min threshold on counts in a tile)
 *              smoothx (half-width of block convolution kernel width)
 *              smoothy (half-width of block convolution kernel height)
 *              scorefract (fraction of the max Otsu score; typ. ~ 0.1)
 *              &thresh (<optional return> threshold value that was
 *                       used on the normalized image)
 *      Return: pixd (1 bpp thresholded image), or null on error
 *
 *  Notes:
 *      (1) This begins with a standard background normalization.
 *          Additionally, there is a flexible background norm, that
 *          will adapt to a rapidly varying background, and this
 *          puts white pixels in the background near regions with
 *          significant foreground.  The white pixels are turned into
 *          a 1 bpp selection mask by binarization followed by dilation.
 *          Otsu thresholding is performed on the input image to get an
 *          estimate of the threshold in the non-mask regions.
 *          The background normalized image is thresholded with two
 *          different values, and the result is combined using
 *          the selection mask.
 *      (2) Note that the numbers 255 (for bgval target) and 190 (for
 *          thresholding on pixn) are tied together, and explicitly
 *          defined in this function.
 *      (3) See pixBackgroundNorm() for meaning and typical values
 *          of input parameters.  For a start, you can try:
 *            sx, sy = 10, 15
 *            thresh = 100
 *            mincount = 50
 *            smoothx, smoothy = 2
 */
PIX *
pixMaskedThreshOnBackgroundNorm(PIX       *pixs,
                                PIX       *pixim,
                                l_int32    sx,
                                l_int32    sy,
                                l_int32    thresh,
                                l_int32    mincount,
                                l_int32    smoothx,
                                l_int32    smoothy,
                                l_float32  scorefract,
                                l_int32   *pthresh)
{
l_int32   w, h;
l_uint32  val;
PIX      *pixn, *pixm, *pixd, *pixt1, *pixt2, *pixt3, *pixt4;

    PROCNAME("pixMaskedThreshOnBackgroundNorm");

    if (pthresh) *pthresh = 0;
    if (!pixs || pixGetDepth(pixs) != 8)
        return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
    if (pixGetColormap(pixs))
        return (PIX *)ERROR_PTR("pixs is colormapped", procName, NULL);
    if (sx < 4 || sy < 4)
        return (PIX *)ERROR_PTR("sx and sy must be >= 4", procName, NULL);
    if (mincount > sx * sy) {
        L_WARNING("mincount too large for tile size\n", procName);
        mincount = (sx * sy) / 3;
    }

        /* Standard background normalization */
    pixn = pixBackgroundNorm(pixs, pixim, NULL, sx, sy, thresh,
                             mincount, 255, smoothx, smoothy);
    if (!pixn)
        return (PIX *)ERROR_PTR("pixn not made", procName, NULL);

        /* Special background normalization for adaptation to quickly
         * varying background.  Threshold on the very light parts,
         * which tend to be near significant edges, and dilate to
         * form a mask over regions that are typically text.  The
         * dilation size is chosen to cover the text completely,
         * except for very thick fonts. */
    pixt1 = pixBackgroundNormFlex(pixs, 7, 7, 1, 1, 20);
    pixt2 = pixThresholdToBinary(pixt1, 240);
    pixInvert(pixt2, pixt2);
    pixm = pixMorphSequence(pixt2, "d21.21", 0);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);

        /* Use Otsu to get a global threshold estimate for the image,
         * which is stored as a single pixel in pixt3. */
    pixGetDimensions(pixs, &w, &h, NULL);
    pixOtsuAdaptiveThreshold(pixs, w, h, 0, 0, scorefract, &pixt3, NULL);
    if (pixt3 && pthresh) {
        pixGetPixel(pixt3, 0, 0, &val);
        *pthresh = val;
    }
    pixDestroy(&pixt3);

        /* Threshold the background normalized images differentially,
         * using a high value correlated with the background normalization
         * for the part of the image under the mask (i.e., near the
         * darker, thicker foreground), and a value that depends on the Otsu
         * threshold for the rest of the image.  This gives a solid
         * (high) thresholding for the foreground parts of the image,
         * while allowing the background and light foreground to be
         * reasonably well cleaned using a threshold adapted to the
         * input image. */
    pixd = pixThresholdToBinary(pixn, val + 30);  /* for bg and light fg */
    pixt4 = pixThresholdToBinary(pixn, 190);  /* for heavier fg */
    pixCombineMasked(pixd, pixt4, pixm);
    pixDestroy(&pixt4);
    pixDestroy(&pixm);
    pixDestroy(&pixn);

    if (!pixd)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    else
        return pixd;
}
示例#18
0
/*!
 * \brief   pixItalicWords()
 *
 * \param[in]    pixs       1 bpp
 * \param[in]    boxaw      [optional] word bounding boxes; can be NULL
 * \param[in]    pixw       [optional] word box mask; can be NULL
 * \param[out]   pboxa      boxa of italic words
 * \param[in]    debugflag  1 for debug output; 0 otherwise
 * \return  0 if OK, 1 on error
 *
 * <pre>
 * Notes:
 *      (1) You can input the bounding boxes for the words in one of
 *          two forms: as bounding boxes (%boxaw) or as a word mask with
 *          the word bounding boxes filled (%pixw).  For example,
 *          to compute %pixw, you can use pixWordMaskByDilation().
 *      (2) Alternatively, you can set both of these inputs to NULL,
 *          in which case the word mask is generated here.  This is
 *          done by dilating and closing the input image to connect
 *          letters within a word, while leaving the words separated.
 *          The parameters are chosen under the assumption that the
 *          input is 10 to 12 pt text, scanned at about 300 ppi.
 *      (3) sel_ital1 and sel_ital2 detect the right edges that are
 *          nearly vertical, at approximately the angle of italic
 *          strokes.  We use the right edge to avoid getting seeds
 *          from lower-case 'y'.  The typical italic slant has a smaller
 *          angle with the vertical than the 'W', so in most cases we
 *          will not trigger on the slanted lines in the 'W'.
 *      (4) Note that sel_ital2 is shorter than sel_ital1.  It is
 *          more appropriate for a typical font scanned at 200 ppi.
 * </pre>
 */
l_int32
pixItalicWords(PIX     *pixs,
               BOXA    *boxaw,
               PIX     *pixw,
               BOXA   **pboxa,
               l_int32  debugflag)
{
char     opstring[32];
l_int32  size;
BOXA    *boxa;
PIX     *pixsd, *pixm, *pixd;
SEL     *sel_ital1, *sel_ital2, *sel_ital3;

    PROCNAME("pixItalicWords");

    if (!pixs)
        return ERROR_INT("pixs not defined", procName, 1);
    if (!pboxa)
        return ERROR_INT("&boxa not defined", procName, 1);
    if (boxaw && pixw)
        return ERROR_INT("both boxaw and pixw are defined", procName, 1);

    sel_ital1 = selCreateFromString(str_ital1, 13, 6, NULL);
    sel_ital2 = selCreateFromString(str_ital2, 10, 6, NULL);
    sel_ital3 = selCreateFromString(str_ital3, 4, 2, NULL);

        /* Make the italic seed: extract with HMT; remove noise.
         * The noise removal close/open is important to exclude
         * situations where a small slanted line accidentally
         * matches sel_ital1. */
    pixsd = pixHMT(NULL, pixs, sel_ital1);
    pixClose(pixsd, pixsd, sel_ital3);
    pixOpen(pixsd, pixsd, sel_ital3);

        /* Make the word mask.  Use input boxes or mask if given. */
    size = 0;  /* init */
    if (boxaw) {
        pixm = pixCreateTemplate(pixs);
        pixMaskBoxa(pixm, pixm, boxaw, L_SET_PIXELS);
    } else if (pixw) {
        pixm = pixClone(pixw);
    } else {
        pixWordMaskByDilation(pixs, NULL, &size, NULL);
        L_INFO("dilation size = %d\n", procName, size);
        snprintf(opstring, sizeof(opstring), "d1.5 + c%d.1", size);
        pixm = pixMorphSequence(pixs, opstring, 0);
    }

        /* Binary reconstruction to fill in those word mask
         * components for which there is at least one seed pixel. */
    pixd = pixSeedfillBinary(NULL, pixsd, pixm, 8);
    boxa = pixConnComp(pixd, NULL, 8);
    *pboxa = boxa;

    if (debugflag) {
            /* Save results at at 2x reduction */
        lept_mkdir("lept/ital");
        l_int32  res, upper;
        BOXA  *boxat;
        GPLOT *gplot;
        NUMA  *na;
        PIXA  *pad;
        PIX   *pix1, *pix2, *pix3;
        pad = pixaCreate(0);
        boxat = pixConnComp(pixm, NULL, 8);
        boxaWrite("/tmp/lept/ital/ital.ba", boxat);
        pixSaveTiledOutline(pixs, pad, 0.5, 1, 20, 2, 32);  /* orig */
        pixSaveTiledOutline(pixsd, pad, 0.5, 1, 20, 2, 0);  /* seed */
        pix1 = pixConvertTo32(pixm);
        pixRenderBoxaArb(pix1, boxat, 3, 255, 0, 0);
        pixSaveTiledOutline(pix1, pad, 0.5, 1, 20, 2, 0);  /* mask + outline */
        pixDestroy(&pix1);
        pixSaveTiledOutline(pixd, pad, 0.5, 1, 20, 2, 0);  /* ital mask */
        pix1 = pixConvertTo32(pixs);
        pixRenderBoxaArb(pix1, boxa, 3, 255, 0, 0);
        pixSaveTiledOutline(pix1, pad, 0.5, 1, 20, 2, 0);  /* orig + outline */
        pixDestroy(&pix1);
        pix1 = pixCreateTemplate(pixs);
        pix2 = pixSetBlackOrWhiteBoxa(pix1, boxa, L_SET_BLACK);
        pixCopy(pix1, pixs);
        pix3 = pixDilateBrick(NULL, pixs, 3, 3);
        pixCombineMasked(pix1, pix3, pix2);
        pixSaveTiledOutline(pix1, pad, 0.5, 1, 20, 2, 0);  /* ital bolded */
        pixDestroy(&pix1);
        pixDestroy(&pix2);
        pixDestroy(&pix3);
        pix2 = pixaDisplay(pad, 0, 0);
        pixWrite("/tmp/lept/ital/ital.png", pix2, IFF_PNG);
        pixDestroy(&pix2);

            /* Assuming the image represents 6 inches of actual page width,
             * the pixs resolution is approximately
             *    (width of pixs in pixels) / 6
             * and the images have been saved at half this resolution.   */
        res = pixGetWidth(pixs) / 12;
        L_INFO("resolution = %d\n", procName, res);
        l_pdfSetDateAndVersion(0);
        pixaConvertToPdf(pad, res, 1.0, L_FLATE_ENCODE, 75, "Italic Finder",
                         "/tmp/lept/ital/ital.pdf");
        l_pdfSetDateAndVersion(1);
        pixaDestroy(&pad);
        boxaDestroy(&boxat);

            /* Plot histogram of horizontal white run sizes.  A small
             * initial vertical dilation removes most runs that are neither
             * inter-character nor inter-word.  The larger first peak is
             * from inter-character runs, and the smaller second peak is
             * from inter-word runs. */
        pix1 = pixDilateBrick(NULL, pixs, 1, 15);
        upper = L_MAX(30, 3 * size);
        na = pixRunHistogramMorph(pix1, L_RUN_OFF, L_HORIZ, upper);
        pixDestroy(&pix1);
        gplot = gplotCreate("/tmp/lept/ital/runhisto", GPLOT_PNG,
                "Histogram of horizontal runs of white pixels, vs length",
                "run length", "number of runs");
        gplotAddPlot(gplot, NULL, na, GPLOT_LINES, "plot1");
        gplotMakeOutput(gplot);
        gplotDestroy(&gplot);
        numaDestroy(&na);
    }

    selDestroy(&sel_ital1);
    selDestroy(&sel_ital2);
    selDestroy(&sel_ital3);
    pixDestroy(&pixsd);
    pixDestroy(&pixm);
    pixDestroy(&pixd);
    return 0;
}
示例#19
0
main(int    argc,
     char **argv)
{
l_int32      h;
l_float32    scalefactor;
BOX         *box;
BOXA        *boxa1, *boxa2;
BOXAA       *baa;
PIX         *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7, *pix8, *pix9;
L_REGPARAMS  *rp;

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

    lept_rmdir("segtest");
    lept_mkdir("segtest");
    baa = boxaaCreate(5);

        /* Image region input.  */
    pix1 = pixRead("wet-day.jpg");
    pix2 = pixScaleToSize(pix1, WIDTH, 0);
    pixWrite("/tmp/segtest/0.jpg", pix2, IFF_JFIF_JPEG);
    regTestCheckFile(rp, "/tmp/segtest/0.jpg");   /* 0 */
    box = boxCreate(105, 161, 620, 872);   /* image region */
    boxa1 = boxaCreate(1);
    boxaAddBox(boxa1, box, L_INSERT);
    boxaaAddBoxa(baa, boxa1, L_INSERT);
    pixDestroy(&pix1);
    pixDestroy(&pix2);

        /* Compute image region at w = 2 * WIDTH */
    pix1 = pixRead("candelabrum-11.jpg");
    pix2 = pixScaleToSize(pix1, WIDTH, 0);
    pix3 = pixConvertTo1(pix2, 100);
    pix4 = pixExpandBinaryPower2(pix3, 2);  /* w = 2 * WIDTH */
    pix5 = pixGenHalftoneMask(pix4, NULL, NULL, 1);
    pix6 = pixMorphSequence(pix5, "c20.1 + c1.20", 0);
    pix7 = pixMaskConnComp(pix6, 8, &boxa1);
    pix8 = pixReduceBinary2(pix7, NULL);  /* back to w = WIDTH */
    pix9 = pixBackgroundNormSimple(pix2, pix8, NULL);
    pixWrite("/tmp/segtest/1.jpg", pix9, IFF_JFIF_JPEG);
    regTestCheckFile(rp, "/tmp/segtest/1.jpg");   /* 1 */
    boxa2 = boxaTransform(boxa1, 0, 0, 0.5, 0.5);  /* back to w = WIDTH */
    boxaaAddBoxa(baa, boxa2, L_INSERT);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pix3);
    pixDestroy(&pix4);
    pixDestroy(&pix5);
    pixDestroy(&pix6);
    pixDestroy(&pix7);
    pixDestroy(&pix8);
    pixDestroy(&pix9);
    boxaDestroy(&boxa1);

        /* Use mask to find image region */
    pix1 = pixRead("lion-page.00016.jpg");
    pix2 = pixScaleToSize(pix1, WIDTH, 0);
    pixWrite("/tmp/segtest/2.jpg", pix2, IFF_JFIF_JPEG);
    regTestCheckFile(rp, "/tmp/segtest/2.jpg");   /* 2 */
    pix3 = pixRead("lion-mask.00016.tif");
    pix4 = pixScaleToSize(pix3, WIDTH, 0);
    boxa1 = pixConnComp(pix4, NULL, 8);
    boxaaAddBoxa(baa, boxa1, L_INSERT);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pix3);
    pixDestroy(&pix4);

        /* Compute image region at full res */
    pix1 = pixRead("rabi.png");
    scalefactor = (l_float32)WIDTH / (l_float32)pixGetWidth(pix1);
    pix2 = pixScaleToGray(pix1, scalefactor);
    pixWrite("/tmp/segtest/3.jpg", pix2, IFF_JFIF_JPEG);
    regTestCheckFile(rp, "/tmp/segtest/3.jpg");   /* 3 */
    pix3 = pixGenHalftoneMask(pix1, NULL, NULL, 0);
    pix4 = pixMorphSequence(pix3, "c20.1 + c1.20", 0);
    boxa1 = pixConnComp(pix4, NULL, 8);
    boxa2 = boxaTransform(boxa1, 0, 0, scalefactor, scalefactor);
    boxaaAddBoxa(baa, boxa2, L_INSERT);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pix3);
    pixDestroy(&pix4);
    boxaDestroy(&boxa1);

        /* Page with no image regions */
    pix1 = pixRead("lucasta-47.jpg");
    pix2 = pixScaleToSize(pix1, WIDTH, 0);
    boxa1 = boxaCreate(1);
    pixWrite("/tmp/segtest/4.jpg", pix2, IFF_JFIF_JPEG);
    regTestCheckFile(rp, "/tmp/segtest/4.jpg");   /* 4 */
    boxaaAddBoxa(baa, boxa1, L_INSERT);
    pixDestroy(&pix1);
    pixDestroy(&pix2);

        /* Page that is all image */
    pix1 = pixRead("map1.jpg");
    pix2 = pixScaleToSize(pix1, WIDTH, 0);
    pixWrite("/tmp/segtest/5.jpg", pix2, IFF_JFIF_JPEG);
    regTestCheckFile(rp, "/tmp/segtest/5.jpg");   /* 5 */
    h = pixGetHeight(pix2);
    box = boxCreate(0, 0, WIDTH, h);
    boxa1 = boxaCreate(1);
    boxaAddBox(boxa1, box, L_INSERT);
    boxaaAddBoxa(baa, boxa1, L_INSERT);
    pixDestroy(&pix1);
    pixDestroy(&pix2);

        /* Save the boxaa file */
    boxaaWrite("/tmp/segtest/seg.baa", baa);
    regTestCheckFile(rp, "/tmp/segtest/seg.baa");   /* 6 */

        /* Do the conversion */
    l_pdfSetDateAndVersion(FALSE);
    convertSegmentedFilesToPdf("/tmp/segtest", ".jpg", 100, L_G4_ENCODE,
                               140, baa, 75, 0.6, "Segmentation Test",
                               "/tmp/pdfseg.7.pdf");
    regTestCheckFile(rp, "/tmp/pdfseg.7.pdf");   /* 7 */

    boxaaDestroy(&baa);
    return regTestCleanup(rp);
}
main(int    argc,
     char **argv)
{
char        *str;
char         buffer1[256];
char         buffer2[256];
l_int32      i, same, same2, factor1, factor2, diff, success;
PIX         *pixs, *pixsd, *pixt1, *pixt2, *pixt3;
SEL         *sel1, *sel2;
static char  mainName[] = "binmorph2_reg";

#if 1
    pixs = pixRead("rabi.png");
    pixsd = pixMorphCompSequence(pixs, "d5.5", 0);
    success = TRUE;
    for (i = 1; i < MAX_SEL_SIZE; i++) {
      
            /* Check if the size is exactly decomposable */
        selectComposableSizes(i, &factor1, &factor2);
        diff = factor1 * factor2 - i;
        fprintf(stderr, "%d: (%d, %d): %d\n", i, factor1, factor2, diff);

	    /* Carry out operations on identical sized Sels: dilation */
        sprintf(buffer1, "d%d.%d", i + diff, i + diff);
        sprintf(buffer2, "d%d.%d", i, i);
        pixt1 = pixMorphSequence(pixsd, buffer1, 0);
        pixt2 = pixMorphCompSequence(pixsd, buffer2, 0);
        pixEqual(pixt1, pixt2, &same);
	if (i < 64) {
            pixt3 = pixMorphCompSequenceDwa(pixsd, buffer2, 0);
            pixEqual(pixt1, pixt3, &same2);
	} else {
            pixt3 = NULL;
	    same2 = TRUE;
	}
        if (same && same2)
            writeResult(buffer1, 1);
        else {
            writeResult(buffer1, 0);
            success = FALSE;
        }
        pixDestroy(&pixt1);
        pixDestroy(&pixt2);
        pixDestroy(&pixt3);

	    /* ... erosion */
        sprintf(buffer1, "e%d.%d", i + diff, i + diff);
        sprintf(buffer2, "e%d.%d", i, i);
        pixt1 = pixMorphSequence(pixsd, buffer1, 0);
        pixt2 = pixMorphCompSequence(pixsd, buffer2, 0);
        pixEqual(pixt1, pixt2, &same);
	if (i < 64) {
            pixt3 = pixMorphCompSequenceDwa(pixsd, buffer2, 0);
            pixEqual(pixt1, pixt3, &same2);
	} else {
            pixt3 = NULL;
	    same2 = TRUE;
	}
        if (same && same2)
            writeResult(buffer1, 1);
        else {
            writeResult(buffer1, 0);
            success = FALSE;
        }
        pixDestroy(&pixt1);
        pixDestroy(&pixt2);
        pixDestroy(&pixt3);

	    /* ... opening */
        sprintf(buffer1, "o%d.%d", i + diff, i + diff);
        sprintf(buffer2, "o%d.%d", i, i);
        pixt1 = pixMorphSequence(pixsd, buffer1, 0);
        pixt2 = pixMorphCompSequence(pixsd, buffer2, 0);
        pixEqual(pixt1, pixt2, &same);
	if (i < 64) {
            pixt3 = pixMorphCompSequenceDwa(pixsd, buffer2, 0);
            pixEqual(pixt1, pixt3, &same2);
	} else {
            pixt3 = NULL;
	    same2 = TRUE;
	}
        if (same && same2)
            writeResult(buffer1, 1);
        else {
            writeResult(buffer1, 0);
            success = FALSE;
        }
        pixDestroy(&pixt1);
        pixDestroy(&pixt2);
        pixDestroy(&pixt3);

            /* ... closing */
        sprintf(buffer1, "c%d.%d", i + diff, i + diff);
        sprintf(buffer2, "c%d.%d", i, i);
        pixt1 = pixMorphSequence(pixsd, buffer1, 0);
        pixt2 = pixMorphCompSequence(pixsd, buffer2, 0);
        pixEqual(pixt1, pixt2, &same);
	if (i < 64) {
            pixt3 = pixMorphCompSequenceDwa(pixsd, buffer2, 0);
            pixEqual(pixt1, pixt3, &same2);
	} else {
            pixt3 = NULL;
	    same2 = TRUE;
	}
        if (same && same2)
            writeResult(buffer1, 1);
        else {
            writeResult(buffer1, 0);
            success = FALSE;
        }
        pixDestroy(&pixt1);
        pixDestroy(&pixt2);
        pixDestroy(&pixt3);

    }
    pixDestroy(&pixs);
    pixDestroy(&pixsd);

    if (success)
        fprintf(stderr, "\n---------- Success: no errors ----------\n");
    else
        fprintf(stderr, "\n---------- Failure: error(s) found -----------\n");
#endif


#if 0
    for (i = 1; i < 400; i++) {
        selectComposableSizes(i, &factor1, &factor2);
        diff = factor1 * factor2 - i;
        fprintf(stderr, "%d: (%d, %d): %d\n",
                  i, factor1, factor2, diff);
        selectComposableSels(i, L_HORIZ, &sel1, &sel2);
        selDestroy(&sel1);
        selDestroy(&sel2);
    }
#endif

#if 0
    selectComposableSels(68, L_HORIZ, &sel1, &sel2);  /* 17, 4 */
    str = selPrintToString(sel2);
    fprintf(stderr, str);
    selDestroy(&sel1);
    selDestroy(&sel2);
    FREE(str);
    selectComposableSels(70, L_HORIZ, &sel1, &sel2);  /* 10, 7 */
    str = selPrintToString(sel2);
    selDestroy(&sel1);
    selDestroy(&sel2);
    fprintf(stderr, str);
    FREE(str);
    selectComposableSels(85, L_HORIZ, &sel1, &sel2);  /* 17, 5 */
    str = selPrintToString(sel2);
    selDestroy(&sel1);
    selDestroy(&sel2);
    fprintf(stderr, str);
    FREE(str);
    selectComposableSels(96, L_HORIZ, &sel1, &sel2);  /* 12, 8 */
    str = selPrintToString(sel2);
    selDestroy(&sel1);
    selDestroy(&sel2);
    fprintf(stderr, str);
    FREE(str);

    { SELA *sela;
    sela = selaAddBasic(NULL);
    selaWrite("/tmp/junksela.sela", sela);
    selaDestroy(&sela);
    }
#endif

    return 0;
}
示例#21
0
/*!
 *  pixFindPageForeground()
 *
 *      Input:  pixs (full resolution (any type or depth)
 *              threshold (for binarization; typically about 128)
 *              mindist (min distance of text from border to allow
 *                       cleaning near border; at 2x reduction, this
 *                       should be larger than 50; typically about 70)
 *              erasedist (when conditions are satisfied, erase anything
 *                         within this distance of the edge;
 *                         typically 30 at 2x reduction)
 *              pagenum (use for debugging when called repeatedly; labels
 *                       debug images that are assembled into pdfdir)
 *              showmorph (set to a negative integer to show steps in
 *                         generating masks; this is typically used
 *                         for debugging region extraction)
 *              display (set to 1  to display mask and selected region
 *                       for debugging a single page)
 *              pdfdir (subdirectory of /tmp where images showing the
 *                      result are placed when called repeatedly; use
 *                      null if no output requested)
 *      Return: box (region including foreground, with some pixel noise
 *                   removed), or null if not found
 *
 *  Notes:
 *      (1) This doesn't simply crop to the fg.  It attempts to remove
 *          pixel noise and junk at the edge of the image before cropping.
 *          The input @threshold is used if pixs is not 1 bpp.
 *      (2) There are several debugging options, determined by the
 *          last 4 arguments.
 *      (3) If you want pdf output of results when called repeatedly,
 *          the pagenum arg labels the images written, which go into
 *          /tmp/<pdfdir>/<pagenum>.png.  In that case,
 *          you would clean out the /tmp directory before calling this
 *          function on each page:
 *              lept_rmdir(pdfdir);
 *              lept_mkdir(pdfdir);
 */
BOX *
pixFindPageForeground(PIX         *pixs,
                      l_int32      threshold,
                      l_int32      mindist,
                      l_int32      erasedist,
                      l_int32      pagenum,
                      l_int32      showmorph,
                      l_int32      display,
                      const char  *pdfdir)
{
char     buf[64];
l_int32  flag, nbox, intersects;
l_int32  w, h, bx, by, bw, bh, left, right, top, bottom;
PIX     *pixb, *pixb2, *pixseed, *pixsf, *pixm, *pix1, *pixg2;
BOX     *box, *boxfg, *boxin, *boxd;
BOXA    *ba1, *ba2;

    PROCNAME("pixFindPageForeground");

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

        /* Binarize, downscale by 0.5, remove the noise to generate a seed,
         * and do a seedfill back from the seed into those 8-connected
         * components of the binarized image for which there was at least
         * one seed pixel.  Also clear out any components that are within
         * 10 pixels of the edge at 2x reduction. */
    flag = (showmorph) ? -1 : 0;  /* if showmorph == -1, write intermediate
                                   * images to /tmp/seq_output_1.pdf */
    pixb = pixConvertTo1(pixs, threshold);
    pixb2 = pixScale(pixb, 0.5, 0.5);
    pixseed = pixMorphSequence(pixb2, "o1.2 + c9.9 + o3.5", flag);
    pixsf = pixSeedfillBinary(NULL, pixseed, pixb2, 8);
    pixSetOrClearBorder(pixsf, 10, 10, 10, 10, PIX_SET);
    pixm = pixRemoveBorderConnComps(pixsf, 8);
    if (display) pixDisplay(pixm, 100, 100);

        /* Now, where is the main block of text?  We want to remove noise near
         * the edge of the image, but to do that, we have to be convinced that
         * (1) there is noise and (2) it is far enough from the text block
         * and close enough to the edge.  For each edge, if the block
         * is more than mindist from that edge, then clean 'erasedist'
         * pixels from the edge. */
    pix1 = pixMorphSequence(pixm, "c50.50", flag - 1);
    ba1 = pixConnComp(pix1, NULL, 8);
    ba2 = boxaSort(ba1, L_SORT_BY_AREA, L_SORT_DECREASING, NULL);
    pixGetDimensions(pix1, &w, &h, NULL);
    nbox = boxaGetCount(ba2);
    if (nbox > 1) {
        box = boxaGetBox(ba2, 0, L_CLONE);
        boxGetGeometry(box, &bx, &by, &bw, &bh);
        left = (bx > mindist) ? erasedist : 0;
        right = (w - bx - bw > mindist) ? erasedist : 0;
        top = (by > mindist) ? erasedist : 0;
        bottom = (h - by - bh > mindist) ? erasedist : 0;
        pixSetOrClearBorder(pixm, left, right, top, bottom, PIX_CLR);
        boxDestroy(&box);
    }
    pixDestroy(&pix1);
    boxaDestroy(&ba1);
    boxaDestroy(&ba2);

        /* Locate the foreground region; don't bother cropping */
    pixClipToForeground(pixm, NULL, &boxfg);

        /* Sanity check the fg region.  Make sure it's not confined
         * to a thin boundary on the left and right sides of the image,
         * in which case it is likely to be noise. */
    if (boxfg) {
        boxin = boxCreate(0.1 * w, 0, 0.8 * w, h);
        boxIntersects(boxfg, boxin, &intersects);
        if (!intersects) {
            L_INFO("found only noise on page %d\n", procName, pagenum);
            boxDestroy(&boxfg);
        }
        boxDestroy(&boxin);
    }

    boxd = NULL;
    if (!boxfg) {
        L_INFO("no fg region found for page %d\n", procName, pagenum);
    } else {
        boxAdjustSides(boxfg, boxfg, -2, 2, -2, 2);  /* tiny expansion */
        boxd = boxTransform(boxfg, 0, 0, 2.0, 2.0);

            /* Write image showing box for this page.  This is to be
             * bundled up into a pdf of all the pages, which can be
             * generated by convertFilesToPdf()  */
        if (pdfdir) {
            pixg2 = pixConvert1To4Cmap(pixb);
            pixRenderBoxArb(pixg2, boxd, 3, 255, 0, 0);
            snprintf(buf, sizeof(buf), "/tmp/%s/%05d.png", pdfdir, pagenum);
            if (display) pixDisplay(pixg2, 700, 100);
            pixWrite(buf, pixg2, IFF_PNG);
            pixDestroy(&pixg2);
        }
    }

    pixDestroy(&pixb);
    pixDestroy(&pixb2);
    pixDestroy(&pixseed);
    pixDestroy(&pixsf);
    pixDestroy(&pixm);
    boxDestroy(&boxfg);
    return boxd;
}
示例#22
0
l_int32 main(int    argc,
             char **argv)
{
l_int32       irval, igval, ibval;
l_float32     rval, gval, bval, fract, fgfract;
L_BMF        *bmf;
BOX          *box;
BOXA         *boxa;
FPIX         *fpix;
PIX          *pixs, *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7;
PIX          *pix8, *pix9, *pix10, *pix11, *pix12, *pix13, *pix14, *pix15;
PIXA         *pixa;
L_REGPARAMS  *rp;

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

    pixa = pixaCreate(0);
    pixs = pixRead("breviar38.150.jpg");
/*    pixs = pixRead("breviar32.150.jpg"); */
    pixaAddPix(pixa, pixs, L_CLONE);
    regTestWritePixAndCheck(rp, pixs, IFF_JFIF_JPEG);  /* 0 */
    pixDisplayWithTitle(pixs, 0, 0, "Input image", rp->display);

        /* Extract the blue component, which is small in all the text
         * regions, including in the highlight color region */
    pix1 = pixGetRGBComponent(pixs, COLOR_BLUE);
    pixaAddPix(pixa, pix1, L_CLONE);
    regTestWritePixAndCheck(rp, pix1, IFF_JFIF_JPEG);  /* 1 */
    pixDisplayWithTitle(pix1, 200, 0, "Blue component", rp->display);

        /* Do a background normalization, with the background set to
         * approximately 200 */
    pix2 = pixBackgroundNormSimple(pix1, NULL, NULL);
    pixaAddPix(pixa, pix2, L_COPY);
    regTestWritePixAndCheck(rp, pix2, IFF_JFIF_JPEG);  /* 2 */
    pixDisplayWithTitle(pix2, 400, 0, "BG normalized to 200", rp->display);

        /* Do a linear transform on the gray pixels, with 50 going to
         * black and 160 going to white.  50 is sufficiently low to
         * make both the red and black print quite dark.  Quantize
         * to a few equally spaced gray levels.  This is the image
         * to which highlight color will be applied. */
    pixGammaTRC(pix2, pix2, 1.0, 50, 160);
    pix3 = pixThresholdOn8bpp(pix2, 7, 1);
    pixaAddPix(pixa, pix3, L_CLONE);
    regTestWritePixAndCheck(rp, pix3, IFF_JFIF_JPEG);  /* 3 */
    pixDisplayWithTitle(pix3, 600, 0, "Basic quantized with white bg",
                        rp->display);

        /* Identify the regions of red text.  First, make a mask
         * consisting of all pixels such that (R-B)/B is larger
         * than 2.0.  This will have all the red, plus a lot of
         * the dark pixels. */
    fpix = pixComponentFunction(pixs, 1.0, 0.0, -1.0, 0.0, 0.0, 1.0);
    pix4 = fpixThresholdToPix(fpix, 2.0);
    pixInvert(pix4, pix4);  /* red plus some dark text */
    pixaAddPix(pixa, pix4, L_CLONE);
    regTestWritePixAndCheck(rp, pix4, IFF_PNG);  /* 4 */
    pixDisplayWithTitle(pix4, 800, 0, "Red plus dark pixels", rp->display);

        /* Make a mask consisting of all the red and background pixels */
    pix5 = pixGetRGBComponent(pixs, COLOR_RED);
    pix6 = pixThresholdToBinary(pix5, 128);
    pixInvert(pix6, pix6);  /* red plus background (white) */

        /* Intersect the two masks to get a mask consisting of pixels
         * that are almost certainly red.  This is the seed. */
    pix7 = pixAnd(NULL, pix4, pix6);  /* red only (seed) */
    pixaAddPix(pixa, pix7, L_COPY);
    regTestWritePixAndCheck(rp, pix7, IFF_PNG);  /* 5 */
    pixDisplayWithTitle(pix7, 0, 600, "Seed for red color", rp->display);

        /* Make the clipping mask by thresholding the image with
         * the background cleaned to white. */
    pix8 =  pixThresholdToBinary(pix2, 230);  /* mask */
    pixaAddPix(pixa, pix8, L_CLONE);
    regTestWritePixAndCheck(rp, pix8, IFF_PNG);  /* 6 */
    pixDisplayWithTitle(pix8, 200, 600, "Clipping mask for red components",
                        rp->display);

        /* Fill into the mask from the seed */
    pixSeedfillBinary(pix7, pix7, pix8, 8);  /* filled: red plus touching */
    regTestWritePixAndCheck(rp, pix7, IFF_PNG);  /* 7 */
    pixDisplayWithTitle(pix7, 400, 600, "Red component mask filled",
                        rp->display);

        /* Remove long horizontal and vertical lines from the filled result */
    pix9 = pixMorphSequence(pix7, "o40.1", 0);
    pixSubtract(pix7, pix7, pix9);  /* remove long horizontal lines */
    pixDestroy(&pix9);
    pix9 = pixMorphSequence(pix7, "o1.40", 0);
    pixSubtract(pix7, pix7, pix9);  /* remove long vertical lines */

        /* Close the regions to be colored  */
    pix10 = pixMorphSequence(pix7, "c5.1", 0);
    pixaAddPix(pixa, pix10, L_CLONE);
    regTestWritePixAndCheck(rp, pix10, IFF_PNG);  /* 8 */
    pixDisplayWithTitle(pix10, 600, 600,
                        "Components defining regions allowing coloring",
                        rp->display);

        /* Sanity check on amount to be colored.  Only accept images
         * with less than 10% of all the pixels with highlight color */
    pixForegroundFraction(pix10, &fgfract);
    if (fgfract >= 0.10) {
        L_INFO("too much highlighting: fract = %6.3f; removing it\n",
               rp->testname, fgfract);
        pixClearAll(pix10);
        pixSetPixel(pix10, 0, 0, 1);
    }

        /* Get the bounding boxes of the regions to be colored */
    boxa = pixConnCompBB(pix10, 8);

        /* Get a color to paint that is representative of the
         * actual highlight color in the image.  Scale each
         * color component up from the average by an amount necessary
         * to saturate the red.  Then divide the green and
         * blue components by 2.0.  */
    pixGetAverageMaskedRGB(pixs, pix7, 0, 0, 1, L_MEAN_ABSVAL,
                           &rval, &gval, &bval);
    fract = 255.0 / rval;
    irval = lept_roundftoi(fract * rval);
    igval = lept_roundftoi(fract * gval / 2.0);
    ibval = lept_roundftoi(fract * bval / 2.0);
    fprintf(stderr, "(r,g,b) = (%d,%d,%d)\n", irval, igval, ibval);

        /* Color the quantized gray version in the selected regions */
    pix11 = pixColorGrayRegions(pix3, boxa, L_PAINT_DARK, 220, irval,
                                igval, ibval);
    pixaAddPix(pixa, pix11, L_CLONE);
    regTestWritePixAndCheck(rp, pix11, IFF_PNG);  /* 9 */
    pixDisplayWithTitle(pix11, 800, 600, "Final colored result", rp->display);
    pixaAddPix(pixa, pixs, L_CLONE);

        /* Test colorization on gray and cmapped gray */
    pix12 = pixColorGrayRegions(pix2, boxa, L_PAINT_DARK, 220, 0, 255, 0);
    pixaAddPix(pixa, pix12, L_CLONE);
    regTestWritePixAndCheck(rp, pix12, IFF_PNG);  /* 10 */
    pixDisplayWithTitle(pix12, 900, 600, "Colorizing boxa gray", rp->display);

    box = boxCreate(200, 200, 250, 350);
    pix13 = pixCopy(NULL, pix2);
    pixColorGray(pix13, box, L_PAINT_DARK, 220, 0, 0, 255);
    pixaAddPix(pixa, pix13, L_CLONE);
    regTestWritePixAndCheck(rp, pix13, IFF_PNG);  /* 11 */
    pixDisplayWithTitle(pix13, 1000, 600, "Colorizing box gray", rp->display);

    pix14 = pixThresholdTo4bpp(pix2, 6, 1);
    pix15 = pixColorGrayRegions(pix14, boxa, L_PAINT_DARK, 220, 0, 0, 255);
    pixaAddPix(pixa, pix15, L_CLONE);
    regTestWritePixAndCheck(rp, pix15, IFF_PNG);  /* 12 */
    pixDisplayWithTitle(pix15, 1100, 600, "Colorizing boxa cmap", rp->display);

    pixColorGrayCmap(pix14, box, L_PAINT_DARK, 0, 255, 255);
    pixaAddPix(pixa, pix14, L_CLONE);
    regTestWritePixAndCheck(rp, pix14, IFF_PNG);  /* 13 */
    pixDisplayWithTitle(pix14, 1200, 600, "Colorizing box cmap", rp->display);
    boxDestroy(&box);

        /* Generate a pdf of the intermediate results */
    lept_mkdir("lept");
    L_INFO("Writing to /tmp/lept/colorize.pdf\n", rp->testname);
    pixaConvertToPdf(pixa, 90, 1.0, 0, 0, "Colorizing highlighted text",
                     "/tmp/lept/colorize.pdf");


    pixaDestroy(&pixa);
    fpixDestroy(&fpix);
    boxDestroy(&box);
    boxaDestroy(&boxa);
    pixDestroy(&pixs);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pix3);
    pixDestroy(&pix4);
    pixDestroy(&pix5);
    pixDestroy(&pix6);
    pixDestroy(&pix7);
    pixDestroy(&pix8);
    pixDestroy(&pix9);
    pixDestroy(&pix10);
    pixDestroy(&pix11);
    pixDestroy(&pix12);
    pixDestroy(&pix13);
    pixDestroy(&pix14);
    pixDestroy(&pix15);

        /* Test the color detector */
    pixa = pixaCreate(7);
    bmf = bmfCreate("./fonts", 4);
    pix1 = TestForRedColor(rp, "brev06.75.jpg", 1, bmf);  /* 14 */
    pixaAddPix(pixa, pix1, L_INSERT);
    pix1 = TestForRedColor(rp, "brev10.75.jpg", 0, bmf);  /* 15 */
    pixaAddPix(pixa, pix1, L_INSERT);
    pix1 = TestForRedColor(rp, "brev14.75.jpg", 1, bmf);  /* 16 */
    pixaAddPix(pixa, pix1, L_INSERT);
    pix1 = TestForRedColor(rp, "brev20.75.jpg", 1, bmf);  /* 17 */
    pixaAddPix(pixa, pix1, L_INSERT);
    pix1 = TestForRedColor(rp, "brev36.75.jpg", 0, bmf);  /* 18 */
    pixaAddPix(pixa, pix1, L_INSERT);
    pix1 = TestForRedColor(rp, "brev53.75.jpg", 1, bmf);  /* 19 */
    pixaAddPix(pixa, pix1, L_INSERT);
    pix1 = TestForRedColor(rp, "brev56.75.jpg", 1, bmf);  /* 20 */
    pixaAddPix(pixa, pix1, L_INSERT);

        /* Generate a pdf of the color detector results */
    L_INFO("Writing to /tmp/lept/colordetect.pdf\n", rp->testname);
    pixaConvertToPdf(pixa, 45, 1.0, 0, 0, "Color detection",
                     "/tmp/lept/colordetect.pdf");
    pixaDestroy(&pixa);
    bmfDestroy(&bmf);

    return regTestCleanup(rp);
}
示例#23
0
/*!
 *  pixSplitIntoCharacters()
 *
 *      Input:  pixs (1 bpp, contains only deskewed text)
 *              minw (minimum component width for initial filtering; typ. 4)
 *              minh (minimum component height for initial filtering; typ. 4)
 *              &boxa (<optional return> character bounding boxes)
 *              &pixa (<optional return> character images)
 *              &pixdebug (<optional return> showing splittings)
 *
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) This is a simple function that attempts to find split points
 *          based on vertical pixel profiles.
 *      (2) It should be given an image that has an arbitrary number
 *          of text characters.
 *      (3) The returned pixa includes the boxes from which the
 *          (possibly split) components are extracted.
 */
l_int32
pixSplitIntoCharacters(PIX     *pixs,
                       l_int32  minw,
                       l_int32  minh,
                       BOXA   **pboxa,
                       PIXA   **ppixa,
                       PIX    **ppixdebug)
{
l_int32  ncomp, i, xoff, yoff;
BOXA   *boxa1, *boxa2, *boxat1, *boxat2, *boxad;
BOXAA  *baa;
PIX    *pix, *pix1, *pix2, *pixdb;
PIXA   *pixa1, *pixadb;

    PROCNAME("pixSplitIntoCharacters");

    if (pboxa) *pboxa = NULL;
    if (ppixa) *ppixa = NULL;
    if (ppixdebug) *ppixdebug = NULL;
    if (!pixs || pixGetDepth(pixs) != 1)
        return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);

        /* Remove the small stuff */
    pix1 = pixSelectBySize(pixs, minw, minh, 8, L_SELECT_IF_BOTH,
                           L_SELECT_IF_GT, NULL);

        /* Small vertical close for consolidation */
    pix2 = pixMorphSequence(pix1, "c1.10", 0);
    pixDestroy(&pix1);

        /* Get the 8-connected components */
    boxa1 = pixConnComp(pix2, &pixa1, 8);
    pixDestroy(&pix2);
    boxaDestroy(&boxa1);

        /* Split the components if obvious */
    ncomp = pixaGetCount(pixa1);
    boxa2 = boxaCreate(ncomp);
    pixadb = (ppixdebug) ? pixaCreate(ncomp) : NULL;
    for (i = 0; i < ncomp; i++) {
        pix = pixaGetPix(pixa1, i, L_CLONE);
        if (ppixdebug) {
            boxat1 = pixSplitComponentWithProfile(pix, 10, 7, &pixdb);
            if (pixdb)
                pixaAddPix(pixadb, pixdb, L_INSERT);
        } else {
            boxat1 = pixSplitComponentWithProfile(pix, 10, 7, NULL);
        }
        pixaGetBoxGeometry(pixa1, i, &xoff, &yoff, NULL, NULL);
        boxat2 = boxaTransform(boxat1, xoff, yoff, 1.0, 1.0);
        boxaJoin(boxa2, boxat2, 0, -1);
        pixDestroy(&pix);
        boxaDestroy(&boxat1);
        boxaDestroy(&boxat2);
    }
    pixaDestroy(&pixa1);

        /* Generate the debug image */
    if (ppixdebug) {
        if (pixaGetCount(pixadb) > 0) {
            *ppixdebug = pixaDisplayTiledInRows(pixadb, 32, 1500,
                                                1.0, 0, 20, 1);
        }
        pixaDestroy(&pixadb);
    }

        /* Do a 2D sort on the bounding boxes, and flatten the result to 1D */
    baa = boxaSort2d(boxa2, NULL, 0, 0, 5);
    boxad = boxaaFlattenToBoxa(baa, NULL, L_CLONE);
    boxaaDestroy(&baa);
    boxaDestroy(&boxa2);

        /* Optionally extract the pieces from the input image */
    if (ppixa)
        *ppixa = pixClipRectangles(pixs, boxad);
    if (pboxa)
        *pboxa = boxad;
    else
        boxaDestroy(&boxad);
    return 0;
}
示例#24
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;
}
示例#25
0
/*!
 *  pixaGenerateFont()
 *
 *      Input:  dir (directory holding image of character set)
 *              size (4, 6, 8, ... , 20, in pts at 300 ppi)
 *              &bl1 (<return> baseline of row 1)
 *              &bl2 (<return> baseline of row 2)
 *              &bl3 (<return> baseline of row 3)
 *      Return: pixa of font bitmaps for 95 characters, or null on error
 *
 *  These font generation functions use 9 sets, each with bitmaps
 *  of 94 ascii characters, all in Palatino-Roman font.
 *  Each input bitmap has 3 rows of characters.  The range of
 *  ascii values in each row is as follows:
 *    row 0:  32-57   (32 is a space)
 *    row 1:  58-91   (92, '\', is not represented in this font)
 *    row 2:  93-126 
 *  We LR flip the '/' char to generate a bitmap for the missing
 *  '\' character, so that we have representations of all 95
 *  printable chars.
 *
 *  Computation of the bitmaps and baselines for a single
 *  font takes from 40 to 200 msec on a 2 GHz processor,
 *  depending on the size.  Use pixaGetFont() to read the
 *  generated character set directly from files that were
 *  produced in prog/genfonts.c using this function.
 */
PIXA *
pixaGenerateFont(const char  *dir,
                 l_int32      size,
                 l_int32     *pbl0,
                 l_int32     *pbl1,
                 l_int32     *pbl2)
{
char     *pathname;
l_int32   fileno;
l_int32   i, j, nrows, nrowchars, nchars, h, yval;
l_int32   width, height;
l_int32   baseline[3];
l_int32  *tab;
BOX      *box, *box1, *box2;
BOXA     *boxar, *boxac, *boxacs;
PIX      *pixs, *pixt1, *pixt2, *pixt3;
PIX      *pixr, *pixrc, *pixc;
PIXA     *pixa;

    PROCNAME("pixaGenerateFont");

    if (!pbl0 || !pbl1 || !pbl2)
        return (PIXA *)ERROR_PTR("&bl not all defined", procName, NULL);
    *pbl0 = *pbl1 = *pbl2 = 0;

    fileno = (size / 2) - 2;
    if (fileno < 0 || fileno > NFONTS)
        return (PIXA *)ERROR_PTR("font size invalid", procName, NULL);
    tab = makePixelSumTab8();
    pathname = genPathname(dir, inputfonts[fileno]);
    if ((pixs = pixRead(pathname)) == NULL)
        return (PIXA *)ERROR_PTR("pixs not all defined", procName, NULL);
    FREE(pathname);

    pixa = pixaCreate(95);
    pixt1 = pixMorphSequence(pixs, "c1.35 + c101.1", 0);
    boxar = pixConnComp(pixt1, NULL, 8);  /* one box for each row */
    pixDestroy(&pixt1);
    nrows = boxaGetCount(boxar);
#if  DEBUG_FONT_GEN
    fprintf(stderr, "For font %s, number of rows is %d\n",
            inputfonts[fileno], nrows);
#endif  /* DEBUG_FONT_GEN */
    if (nrows != 3) {
        L_INFO_INT2("nrows = %d; skipping font %d", procName, nrows, fileno);
        return (PIXA *)ERROR_PTR("3 rows not generated", procName, NULL);
    }
    for (i = 0; i < nrows; i++) {
        box = boxaGetBox(boxar, i, L_CLONE);
        pixr = pixClipRectangle(pixs, box, NULL);  /* row of chars */
        pixGetTextBaseline(pixr, tab, &yval);
        baseline[i] = yval;

#if DEBUG_BASELINE
      { PIX *pixbl;
        fprintf(stderr, "row %d, yval = %d, h = %d\n",
                i, yval, pixGetHeight(pixr));
        pixbl = pixCopy(NULL, pixr);
        pixRenderLine(pixbl, 0, yval, pixGetWidth(pixbl), yval, 1,
                      L_FLIP_PIXELS);
        if (i == 0 )
            pixWrite("junktl0", pixbl, IFF_PNG);
        else if (i == 1)
            pixWrite("junktl1", pixbl, IFF_PNG);
        else
            pixWrite("junktl2", pixbl, IFF_PNG);
        pixDestroy(&pixbl);
      }
#endif  /* DEBUG_BASELINE */

        boxDestroy(&box);
        pixrc = pixCloseSafeBrick(NULL, pixr, 1, 35);
        boxac = pixConnComp(pixrc, NULL, 8);
        boxacs = boxaSort(boxac, L_SORT_BY_X, L_SORT_INCREASING, NULL);
        if (i == 0) {  /* consolidate the two components of '"' */
            box1 = boxaGetBox(boxacs, 1, L_CLONE);
            box2 = boxaGetBox(boxacs, 2, L_CLONE);
            box1->w = box2->x + box2->w - box1->x;  /* increase width */
            boxDestroy(&box1);
            boxDestroy(&box2);
            boxaRemoveBox(boxacs, 2);
        }
        h = pixGetHeight(pixr);
        nrowchars = boxaGetCount(boxacs);
        for (j = 0; j < nrowchars; j++) {
            box = boxaGetBox(boxacs, j, L_COPY);
            if (box->w <= 2 && box->h == 1) {  /* skip 1x1, 2x1 components */
                boxDestroy(&box);
                continue;
            }
            box->y = 0;
            box->h = h - 1;
            pixc = pixClipRectangle(pixr, box, NULL);
            boxDestroy(&box);
            if (i == 0 && j == 0)  /* add a pix for the space; change later */
                pixaAddPix(pixa, pixc, L_COPY);
            if (i == 2 && j == 0)  /* add a pix for the '\'; change later */
                pixaAddPix(pixa, pixc, L_COPY);
            pixaAddPix(pixa, pixc, L_INSERT);
        }
        pixDestroy(&pixr);
        pixDestroy(&pixrc);
        boxaDestroy(&boxac);
        boxaDestroy(&boxacs);
    }

    nchars = pixaGetCount(pixa);
    if (nchars != 95)
        return (PIXA *)ERROR_PTR("95 chars not generated", procName, NULL);

    *pbl0 = baseline[0];
    *pbl1 = baseline[1];
    *pbl2 = baseline[2];
        
        /* Fix the space character up; it should have no ON pixels,
         * and be about twice as wide as the '!' character.    */
    pixt2 = pixaGetPix(pixa, 0, L_CLONE);
    width = 2 * pixGetWidth(pixt2);
    height = pixGetHeight(pixt2);
    pixDestroy(&pixt2);
    pixt2 = pixCreate(width, height, 1);
    pixaReplacePix(pixa, 0, pixt2, NULL);

        /* Fix up the '\' character; use a LR flip of the '/' char */
    pixt2 = pixaGetPix(pixa, 15, L_CLONE);
    pixt3 = pixFlipLR(NULL, pixt2);
    pixDestroy(&pixt2);
    pixaReplacePix(pixa, 60, pixt3, NULL);
    
#if DEBUG_CHARS
  { PIX *pixd;
    pixd = pixaDisplayTiled(pixa, 1500, 0, 10);
    pixDisplay(pixd, 100 * i, 200);
    pixDestroy(&pixd);
  }
#endif  /* DEBUG_CHARS */

    pixDestroy(&pixs);
    boxaDestroy(&boxar);
    FREE(tab);

    return pixa;
}