Example #1
0
main(int    argc,
     char **argv)
{
char        *filein, *fileout;
l_int32      w, h, d, w2, h2, i, ncols;
l_float32    angle, conf;
BOX         *box;
BOXA        *boxa, *boxas, *boxad, *boxa2;
NUMA        *numa;
PIX         *pixs, *pixt, *pixb, *pixb2, *pixd;
PIX         *pixtlm, *pixvws;
PIX         *pixt1, *pixt2, *pixt3, *pixt4, *pixt5, *pixt6;
PIXA        *pixam, *pixac, *pixad, *pixat;
PIXAA       *pixaa, *pixaa2;
PTA         *pta;
SEL         *selsplit;
static char  mainName[] = "textlinemask";

    if (argc != 3)
        exit(ERROR_INT(" Syntax:  textlinemask filein fileout", mainName, 1));

    filein = argv[1];
    fileout = argv[2];

    pixDisplayWrite(NULL, -1);  /* init debug output */

    if ((pixs = pixRead(filein)) == NULL)
        return ERROR_INT("pixs not made", mainName, 1);
    pixGetDimensions(pixs, &w, &h, &d);

        /* Binarize input */
    if (d == 8)
        pixt = pixThresholdToBinary(pixs, 128);
    else if (d == 1)
        pixt = pixClone(pixs);
    else {
        fprintf(stderr, "depth is %d\n", d);
        exit(1);
    }

        /* Deskew */
    pixb = pixFindSkewAndDeskew(pixt, 1, &angle, &conf);
    pixDestroy(&pixt);
    fprintf(stderr, "Skew angle: %7.2f degrees; %6.2f conf\n", angle, conf);
    pixDisplayWrite(pixb, DEBUG_OUTPUT);

#if 1
        /* Use full image morphology to find columns, at 2x reduction.
         * This only works for very simple layouts where each column
         * of text extends the full height of the input image. 
         * pixam has a pix component over each column.  */
    pixb2 = pixReduceRankBinary2(pixb, 2, NULL);
    pixt1 = pixMorphCompSequence(pixb2, "c5.500", 0);
    boxa = pixConnComp(pixt1, &pixam, 8);
    ncols = boxaGetCount(boxa);
    fprintf(stderr, "Num columns: %d\n", ncols);
    pixDisplayWrite(pixt1, DEBUG_OUTPUT);

        /* Use selective region-based morphology to get the textline mask. */
    pixad = pixaMorphSequenceByRegion(pixb2, pixam, "c100.3", 0, 0);
    pixGetDimensions(pixb2, &w2, &h2, NULL);
    if (DEBUG_OUTPUT) {
        pixt2 = pixaDisplay(pixad, w2, h2);
        pixDisplayWrite(pixt2, DEBUG_OUTPUT);
        pixDestroy(&pixt2);
    }

        /* Some of the lines may be touching, so use a HMT to split the
         * lines in each column, and use a pixaa to save the results. */
    selsplit = selCreateFromString(seltext, 17, 7, "selsplit");
    pixaa = pixaaCreate(ncols);
    for (i = 0; i < ncols; i++) {
        pixt3 = pixaGetPix(pixad, i, L_CLONE);
        box = pixaGetBox(pixad, i, L_COPY);
        pixt4 = pixHMT(NULL, pixt3, selsplit);
        pixXor(pixt4, pixt4, pixt3);
        boxa2 = pixConnComp(pixt4, &pixac, 8);
        pixaaAddPixa(pixaa, pixac, L_INSERT);
        pixaaAddBox(pixaa, box, L_INSERT);
        if (DEBUG_OUTPUT) {
            pixt5 = pixaDisplayRandomCmap(pixac, 0, 0);
            pixDisplayWrite(pixt5, DEBUG_OUTPUT);
            fprintf(stderr, "Num textlines in col %d: %d\n", i,
                    boxaGetCount(boxa2));
            pixDestroy(&pixt5);
        }
        pixDestroy(&pixt3);
        pixDestroy(&pixt4);
        boxaDestroy(&boxa2);
    }

        /* Visual output */
    if (DEBUG_OUTPUT) {
        pixDisplayMultiple("/tmp/junk_write_display*");
        pixat = pixaReadFiles("/tmp", "junk_write_display");
        pixt5 = selDisplayInPix(selsplit, 31, 2);
        pixaAddPix(pixat, pixt5, L_INSERT);
        pixt6 = pixaDisplayTiledAndScaled(pixat, 32, 400, 3, 0, 35, 3);
        pixWrite(fileout, pixt6, IFF_PNG);
        pixaDestroy(&pixat);
        pixDestroy(&pixt6);
    }

        /* Test pixaa I/O */
    pixaaWrite("/tmp/junkpixaa", pixaa);
    pixaa2 = pixaaRead("/tmp/junkpixaa");
    pixaaWrite("/tmp/junkpixaa2", pixaa2);

        /* Test pixaa display */
    pixd = pixaaDisplay(pixaa, w2, h2);
    pixWrite("/tmp/junkdisplay", pixd, IFF_PNG);
    pixDestroy(&pixd);

        /* Cleanup */
    pixDestroy(&pixb2);
    pixDestroy(&pixt1);
    pixaDestroy(&pixam);
    pixaDestroy(&pixad);
    pixaaDestroy(&pixaa);
    pixaaDestroy(&pixaa2);
    boxaDestroy(&boxa);
    selDestroy(&selsplit);
#endif

#if 0
        /*  Use the baseline finder; not really what is needed */
    numa = pixFindBaselines(pixb, &pta, 1);
#endif

#if 0
        /* Use the textline mask function; parameters are not quite right */
    pixb2 = pixReduceRankBinary2(pixb, 2, NULL);
    pixtlm = pixGenTextlineMask(pixb2, &pixvws, NULL, 1);
    pixDisplay(pixtlm, 0, 100);
    pixDisplay(pixvws, 500, 100);
    pixDestroy(&pixb2);
    pixDestroy(&pixtlm);
    pixDestroy(&pixvws);
#endif

#if 0
        /* Use the Breuel whitespace partition method; slow and we would
         * still need to work to extract the fg regions. */
    pixb2 = pixReduceRankBinary2(pixb, 2, NULL);
    boxas = pixConnComp(pixb2, NULL, 8);
    boxad = boxaGetWhiteblocks(boxas, NULL, L_SORT_BY_HEIGHT,
                              3, 0.1, 200, 0.2, 0);
    pixd = pixDrawBoxa(pixb2, boxad, 7, 0xe0708000);
    pixDisplay(pixd, 100, 500);
    pixDestroy(&pixb2);
    pixDestroy(&pixd);
    boxaDestroy(&boxas);
    boxaDestroy(&boxad);
#endif


#if 0
        /* Use morphology to find columns and then selective
         * region-based morphology to get the textline mask.
         * This is for display; we really want to get a pixa of the
         * specific textline masks.   */
    startTimer();
    pixb2 = pixReduceRankBinary2(pixb, 2, NULL);
    pixt1 = pixMorphCompSequence(pixb2, "c5.500", 0);  /* column mask */
    pixt2 = pixMorphSequenceByRegion(pixb2, pixt1, "c100.3", 8, 0, 0, &boxa);
    fprintf(stderr, "time = %7.3f sec\n", stopTimer());
    pixDisplay(pixt1, 100, 500);
    pixDisplay(pixt2, 800, 500);
    pixDestroy(&pixb2);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    boxaDestroy(&boxa);
#endif

    pixDestroy(&pixs);
    pixDestroy(&pixb);

    exit(0);
}
Example #2
0
/*!
 *  pixGetRegionsBinary()
 *
 *      Input:  pixs (1 bpp, assumed to be 300 to 400 ppi)
 *              &pixhm (<optional return> halftone mask)
 *              &pixtm (<optional return> textline mask)
 *              &pixtb (<optional return> textblock mask)
 *              debug (flag: set to 1 for debug output)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) It is best to deskew the image before segmenting.
 *      (2) The debug flag enables a number of outputs.  These
 *          are included to show how to generate and save/display
 *          these results.
 */
l_int32
pixGetRegionsBinary(PIX     *pixs,
                    PIX    **ppixhm,
                    PIX    **ppixtm,
                    PIX    **ppixtb,
                    l_int32  debug)
{
char    *tempname;
l_int32  htfound, tlfound;
PIX     *pixr, *pixt1, *pixt2;
PIX     *pixtext;  /* text pixels only */
PIX     *pixhm2;   /* halftone mask; 2x reduction */
PIX     *pixhm;    /* halftone mask;  */
PIX     *pixtm2;   /* textline mask; 2x reduction */
PIX     *pixtm;    /* textline mask */
PIX     *pixvws;   /* vertical white space mask */
PIX     *pixtb2;   /* textblock mask; 2x reduction */
PIX     *pixtbf2;  /* textblock mask; 2x reduction; small comps filtered */
PIX     *pixtb;    /* textblock mask */

    PROCNAME("pixGetRegionsBinary");

    if (ppixhm) *ppixhm = NULL;
    if (ppixtm) *ppixtm = NULL;
    if (ppixtb) *ppixtb = NULL;
    if (!pixs)
        return ERROR_INT("pixs not defined", procName, 1);
    if (pixGetDepth(pixs) != 1)
        return ERROR_INT("pixs not 1 bpp", procName, 1);

        /* 2x reduce, to 150 -200 ppi */
    pixr = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0);
    pixDisplayWrite(pixr, debug);

        /* Get the halftone mask */
    pixhm2 = pixGenHalftoneMask(pixr, &pixtext, &htfound, debug);

        /* Get the textline mask from the text pixels */
    pixtm2 = pixGenTextlineMask(pixtext, &pixvws, &tlfound, debug);

        /* Get the textblock mask from the textline mask */
    pixtb2 = pixGenTextblockMask(pixtm2, pixvws, debug);
    pixDestroy(&pixr);
    pixDestroy(&pixtext);
    pixDestroy(&pixvws);

        /* Remove small components from the mask, where a small
         * component is defined as one with both width and height < 60 */
    pixtbf2 = pixSelectBySize(pixtb2, 60, 60, 4, L_SELECT_IF_EITHER,
                              L_SELECT_IF_GTE, NULL);
    pixDestroy(&pixtb2);
    pixDisplayWriteFormat(pixtbf2, debug, IFF_PNG);

        /* Expand all masks to full resolution, and do filling or
         * small dilations for better coverage. */
    pixhm = pixExpandReplicate(pixhm2, 2);
    pixt1 = pixSeedfillBinary(NULL, pixhm, pixs, 8);
    pixOr(pixhm, pixhm, pixt1);
    pixDestroy(&pixt1);
    pixDisplayWriteFormat(pixhm, debug, IFF_PNG);

    pixt1 = pixExpandReplicate(pixtm2, 2);
    pixtm = pixDilateBrick(NULL, pixt1, 3, 3);
    pixDestroy(&pixt1);
    pixDisplayWriteFormat(pixtm, debug, IFF_PNG);

    pixt1 = pixExpandReplicate(pixtbf2, 2);
    pixtb = pixDilateBrick(NULL, pixt1, 3, 3);
    pixDestroy(&pixt1);
    pixDisplayWriteFormat(pixtb, debug, IFF_PNG);

    pixDestroy(&pixhm2);
    pixDestroy(&pixtm2);
    pixDestroy(&pixtbf2);

        /* Debug: identify objects that are neither text nor halftone image */
    if (debug) {
        pixt1 = pixSubtract(NULL, pixs, pixtm);  /* remove text pixels */
        pixt2 = pixSubtract(NULL, pixt1, pixhm);  /* remove halftone pixels */
        pixDisplayWriteFormat(pixt2, 1, IFF_PNG);
        pixDestroy(&pixt1);
        pixDestroy(&pixt2);
    }

        /* Debug: display textline components with random colors */
    if (debug) {
        l_int32  w, h;
        BOXA    *boxa;
        PIXA    *pixa;
        boxa = pixConnComp(pixtm, &pixa, 8);
        pixGetDimensions(pixtm, &w, &h, NULL);
        pixt1 = pixaDisplayRandomCmap(pixa, w, h);
        pixcmapResetColor(pixGetColormap(pixt1), 0, 255, 255, 255);
        pixDisplay(pixt1, 100, 100);
        pixDisplayWriteFormat(pixt1, 1, IFF_PNG);
        pixaDestroy(&pixa);
        boxaDestroy(&boxa);
        pixDestroy(&pixt1);
    }

        /* Debug: identify the outlines of each textblock */
    if (debug) {
        PIXCMAP  *cmap;
        PTAA     *ptaa;
        ptaa = pixGetOuterBordersPtaa(pixtb);
        tempname = genTempFilename("/tmp", "tb_outlines.ptaa", 0, 0);
        ptaaWrite(tempname, ptaa, 1);
        FREE(tempname);
        pixt1 = pixRenderRandomCmapPtaa(pixtb, ptaa, 1, 16, 1);
        cmap = pixGetColormap(pixt1);
        pixcmapResetColor(cmap, 0, 130, 130, 130);
        pixDisplay(pixt1, 500, 100);
        pixDisplayWriteFormat(pixt1, 1, IFF_PNG);
        pixDestroy(&pixt1);
        ptaaDestroy(&ptaa);
    }

        /* Debug: get b.b. for all mask components */
    if (debug) {
        BOXA  *bahm, *batm, *batb;
        bahm = pixConnComp(pixhm, NULL, 4);
        batm = pixConnComp(pixtm, NULL, 4);
        batb = pixConnComp(pixtb, NULL, 4);
        tempname = genTempFilename("/tmp", "htmask.boxa", 0, 0);
        boxaWrite(tempname, bahm);
        FREE(tempname);
        tempname = genTempFilename("/tmp", "textmask.boxa", 0, 0);
        boxaWrite(tempname, batm);
        FREE(tempname);
        tempname = genTempFilename("/tmp", "textblock.boxa", 0, 0);
        boxaWrite(tempname, batb);
        FREE(tempname);
	boxaDestroy(&bahm);
	boxaDestroy(&batm);
	boxaDestroy(&batb);
    }

    if (ppixhm)
        *ppixhm = pixhm;
    else
        pixDestroy(&pixhm);
    if (ppixtm)
        *ppixtm = pixtm;
    else
        pixDestroy(&pixtm);
    if (ppixtb)
        *ppixtb = pixtb;
    else
        pixDestroy(&pixtb);

    return 0;
}