Esempio n. 1
0
main(int    argc,
     char **argv)
{
PIX         *pixd;
SEL         *sel1, *sel2, *sel3, *sel4;
SELA        *sela;
static char  mainName[] = "livre_orient";

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

    sela = selaCreate(4);
    selaAddSel(sela, sel1, "textsel1", L_INSERT);
    selaAddSel(sela, sel2, "textsel2", L_INSERT);
    selaAddSel(sela, sel3, "textsel3", L_INSERT);
    selaAddSel(sela, sel4, "textsel4", L_INSERT);

    pixd = selaDisplayInPix(sela, 28, 3, 30, 4);
    pixWrite("/tmp/orient.png", pixd, IFF_PNG);
    pixDisplay(pixd, 100, 100);

    pixDestroy(&pixd);
    selaDestroy(&sela);
    return 0;
}
Esempio n. 2
0
/*!
 * \brief   sela8ccThin()
 *
 * \param[in]    sela [optional]
 * \return  sela with additional sels, or NULL on error
 *
 * <pre>
 * Notes:
 *      (1) Adds the 9 basic sels for 8-cc thinning.
 * </pre>
 */
SELA *
sela8ccThin(SELA  *sela)
{
SEL  *sel;

    if (!sela) sela = selaCreate(9);

    sel = selCreateFromString(sel_8_1, 3, 3, "sel_8_1");
    selaAddSel(sela, sel, NULL, 0);
    sel = selCreateFromString(sel_8_2, 3, 3, "sel_8_2");
    selaAddSel(sela, sel, NULL, 0);
    sel = selCreateFromString(sel_8_3, 3, 3, "sel_8_3");
    selaAddSel(sela, sel, NULL, 0);
    sel = selCreateFromString(sel_8_4, 3, 3, "sel_8_4");
    selaAddSel(sela, sel, NULL, 0);
    sel = selCreateFromString(sel_8_5, 3, 3, "sel_8_5");
    selaAddSel(sela, sel, NULL, 0);
    sel = selCreateFromString(sel_8_6, 3, 3, "sel_8_6");
    selaAddSel(sela, sel, NULL, 0);
    sel = selCreateFromString(sel_8_7, 3, 3, "sel_8_7");
    selaAddSel(sela, sel, NULL, 0);
    sel = selCreateFromString(sel_8_8, 3, 3, "sel_8_8");
    selaAddSel(sela, sel, NULL, 0);
    sel = selCreateFromString(sel_8_9, 3, 3, "sel_8_9");
    selaAddSel(sela, sel, NULL, 0);

    return sela;
}
Esempio n. 3
0
/*!
 * \brief   sela4and8ccThin()
 *
 * \param[in]    sela [optional]
 * \return  sela with additional sels, or NULL on error
 *
 * <pre>
 * Notes:
 *      (1) Adds the 2 basic sels for either 4-cc or 8-cc thinning.
 * </pre>
 */
SELA *
sela4and8ccThin(SELA  *sela)
{
SEL  *sel;

    if (!sela) sela = selaCreate(2);

    sel = selCreateFromString(sel_48_1, 3, 3, "sel_48_1");
    selaAddSel(sela, sel, NULL, 0);
    sel = selCreateFromString(sel_48_2, 3, 3, "sel_48_2");
    selaAddSel(sela, sel, NULL, 0);

    return sela;
}
Esempio n. 4
0
main(int    argc,
     char **argv)
{
PIX          *pix;
SEL          *sel;
SELA         *sela1, *sela2;
L_REGPARAMS  *rp;

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

        /* selaRead() / selaWrite()  */
    sela1 = selaAddBasic(NULL);
    selaWrite("/tmp/sel.0.sela", sela1);
    regTestCheckFile(rp, "/tmp/sel.0.sela");  /* 0 */
    sela2 = selaRead("/tmp/sel.0.sela");
    selaWrite("/tmp/sel.1.sela", sela2);
    regTestCheckFile(rp, "/tmp/sel.1.sela");  /* 1 */
    regTestCompareFiles(rp, 0, 1);  /* 2 */
    selaDestroy(&sela1);
    selaDestroy(&sela2);
    
	/* Create from file and display result */
    sela1 = selaCreateFromFile("flipsels.txt");
    pix = selaDisplayInPix(sela1, 31, 3, 15, 4);
    regTestWritePixAndCheck(rp, pix, IFF_PNG);  /* 3 */
    pixDisplayWithTitle(pix, 100, 100, NULL, rp->display);
    selaWrite("/tmp/sel.3.sela", sela1);
    regTestCheckFile(rp, "/tmp/sel.3.sela");  /* 4 */
    pixDestroy(&pix);
    selaDestroy(&sela1);

        /* Create the same set of Sels from compiled strings and compare */
    sela2 = selaCreate(4);
    sel = selCreateFromString(textsel1, 5, 6, "textsel1");
    selaAddSel(sela2, sel, NULL, 0);
    sel = selCreateFromString(textsel2, 5, 6, "textsel2");
    selaAddSel(sela2, sel, NULL, 0);
    sel = selCreateFromString(textsel3, 5, 6, "textsel3");
    selaAddSel(sela2, sel, NULL, 0);
    sel = selCreateFromString(textsel4, 5, 6, "textsel4");
    selaAddSel(sela2, sel, NULL, 0);
    selaWrite("/tmp/sel.4.sela", sela2);
    regTestCheckFile(rp, "/tmp/sel.4.sela");  /* 5 */
    regTestCompareFiles(rp, 4, 5);  /* 6 */
    selaDestroy(&sela2);

    return regTestCleanup(rp);
}
Esempio n. 5
0
int main(int    argc,
         char **argv)
{
SEL         *sel1, *sel2, *sel3, *sel4;
SELA        *sela;
PIX         *pix, *pixd;
PIXA        *pixa;
static char  mainName[] = "flipselgen";

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

    sela = selaCreate(0);
    sel1 = selCreateFromString(textsel1, 5, 6, "flipsel1");
    sel2 = selCreateFromString(textsel2, 5, 6, "flipsel2");
    sel3 = selCreateFromString(textsel3, 5, 6, "flipsel3");
    sel4 = selCreateFromString(textsel4, 5, 6, "flipsel4");
    selaAddSel(sela, sel1, NULL, 0);
    selaAddSel(sela, sel2, NULL, 0);
    selaAddSel(sela, sel3, NULL, 0);
    selaAddSel(sela, sel4, NULL, 0);

    pixa = pixaCreate(4);
    pix = selDisplayInPix(sel1, 23, 2);
    pixDisplayWithTitle(pix, 100, 100, "sel1", DFLAG);
    pixaAddPix(pixa, pix, L_INSERT);
    pix = selDisplayInPix(sel2, 23, 2);
    pixDisplayWithTitle(pix, 275, 100, "sel2", DFLAG);
    pixaAddPix(pixa, pix, L_INSERT);
    pix = selDisplayInPix(sel3, 23, 2);
    pixDisplayWithTitle(pix, 450, 100, "sel3", DFLAG);
    pixaAddPix(pixa, pix, L_INSERT);
    pix = selDisplayInPix(sel4, 23, 2);
    pixDisplayWithTitle(pix, 625, 100, "sel4", DFLAG);
    pixaAddPix(pixa, pix, L_INSERT);

    pixd = pixaDisplayTiled(pixa, 800, 0, 15);
    pixDisplayWithTitle(pixd, 100, 300, "allsels", DFLAG);
    pixDestroy(&pixd);
    pixaDestroy(&pixa);

    if (fhmtautogen(sela, INDEX, NULL))
        return ERROR_INT(" Generation failed", mainName, 1);

    selaDestroy(&sela);
    return 0;
}
Esempio n. 6
0
/*!
 *  pixThin()
 *
 *      Input:  pixs (1 bpp)
 *              type (L_THIN_FG, L_THIN_BG)
 *              connectivity (4 or 8)
 *              maxiters (max number of iters allowed; use 0 to iterate
 *                        until completion)
 *      Return: pixd, or null on error
 *
 *  Notes:
 *      (1) See "Connectivity-preserving morphological image transformations,"
 *          Dan S. Bloomberg, in SPIE Visual Communications and Image
 *          Processing, Conference 1606, pp. 320-334, November 1991,
 *          Boston, MA.   A web version is available at
 *              http://www.leptonica.com/papers/conn.pdf
 *      (2) We implement here two of the best iterative
 *          morphological thinning algorithms, for 4 c.c and 8 c.c.
 *          Each iteration uses a mixture of parallel operations
 *          (using several different 3x3 Sels) and serial operations.
 *          Specifically, each thinning iteration consists of
 *          four sequential thinnings from each of four directions.
 *          Each of these thinnings is a parallel composite
 *          operation, where the union of a set of HMTs are set
 *          subtracted from the input.  For 4-cc thinning, we
 *          use 3 HMTs in parallel, and for 8-cc thinning we use 4 HMTs.
 *      (3) A "good" thinning algorithm is one that generates a skeleton
 *          that is near the medial axis and has neither pruned
 *          real branches nor left extra dendritic branches.
 *      (4) To thin the foreground, which is the usual situation,
 *          use type == L_THIN_FG.  Thickening the foreground is equivalent
 *          to thinning the background (type == L_THIN_BG), where the
 *          opposite connectivity gets preserved.  For example, to thicken
 *          the fg using 4-connectivity, we thin the bg using Sels that
 *          preserve 8-connectivity.
 */
PIX *
pixThin(PIX     *pixs,
        l_int32  type,
        l_int32  connectivity,
        l_int32  maxiters)
{
PIX   *pixd;
SEL   *sel;
SELA  *sela;

    PROCNAME("pixThin");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 1)
        return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
    if (type != L_THIN_FG && type != L_THIN_BG)
        return (PIX *)ERROR_PTR("invalid fg/bg type", procName, NULL);
    if (connectivity != 4 && connectivity != 8)
        return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
    if (maxiters == 0) maxiters = 10000;

    sela = selaCreate(4);
    if (connectivity == 4) {
        sel = selCreateFromString(sel_4_1, 3, 3, "sel_4_1");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_4_2, 3, 3, "sel_4_2");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_4_3, 3, 3, "sel_4_3");
        selaAddSel(sela, sel, NULL, 0);
    } else {  /* connectivity == 8 */
        sel = selCreateFromString(sel_8_2, 3, 3, "sel_8_2");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_3, 3, 3, "sel_8_3");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_5, 3, 3, "sel_8_5");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_6, 3, 3, "sel_8_6");
        selaAddSel(sela, sel, NULL, 0);
    }

    pixd = pixThinGeneral(pixs, type, sela, maxiters);

    selaDestroy(&sela);
    return pixd;
}
Esempio n. 7
0
/*!
 *  pixThinExamples()
 *
 *      Input:  pixs (1 bpp)
 *              type (L_THIN_FG, L_THIN_BG)
 *              index (into specific examples; valid 1-9; see notes)
 *              maxiters (max number of iters allowed; use 0 to iterate
 *                        until completion)
 *              selfile (<optional> filename for output sel display)
 *      Return: pixd, or null on error
 *
 *  Notes:
 *      (1) See notes in pixThin().  The examples are taken from
 *          the paper referenced there.
 *      (2) Here we allow specific sets of HMTs to be used in
 *          parallel for thinning from each of four directions.
 *          One iteration consists of four such parallel thins.
 *      (3) The examples are indexed as follows:
 *          Thinning  (e.g., run to completion):
 *              index = 1     sel_4_1, sel_4_5, sel_4_6
 *              index = 2     sel_4_1, sel_4_7, sel_4_7_rot
 *              index = 3     sel_48_1, sel_48_1_rot, sel_48_2
 *              index = 4     sel_8_2, sel_8_3, sel_48_2
 *              index = 5     sel_8_1, sel_8_5, sel_8_6
 *              index = 6     sel_8_2, sel_8_3, sel_8_8, sel_8_9
 *              index = 7     sel_8_5, sel_8_6, sel_8_7, sel_8_7_rot
 *          Thickening:
 *              index = 8     sel_4_2, sel_4_3 (e.g,, do just a few iterations)
 *              index = 9     sel_8_4 (e.g., do just a few iterations)
 */
PIX *
pixThinExamples(PIX         *pixs,
                l_int32      type,
                l_int32      index,
                l_int32      maxiters,
                const char  *selfile)
{
PIX   *pixd, *pixt;
SEL   *sel;
SELA  *sela;

    PROCNAME("pixThinExamples");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 1)
        return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
    if (type != L_THIN_FG && type != L_THIN_BG)
        return (PIX *)ERROR_PTR("invalid fg/bg type", procName, NULL);
    if (index < 1 || index > 9)
        return (PIX *)ERROR_PTR("invalid index", procName, NULL);
    if (maxiters == 0) maxiters = 10000;

    switch(index)
    {
    case 1:
        sela = selaCreate(3);
        sel = selCreateFromString(sel_4_1, 3, 3, "sel_4_1");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_4_5, 3, 3, "sel_4_5");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_4_6, 3, 3, "sel_4_6");
        selaAddSel(sela, sel, NULL, 0);
        break;
    case 2:
        sela = selaCreate(3);
        sel = selCreateFromString(sel_4_1, 3, 3, "sel_4_1");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_4_7, 3, 3, "sel_4_7");
        selaAddSel(sela, sel, NULL, 0);
	sel = selRotateOrth(sel, 1);
        selaAddSel(sela, sel, "sel_4_7_rot", 0);
        break;
    case 3:
        sela = selaCreate(3);
        sel = selCreateFromString(sel_48_1, 3, 3, "sel_48_1");
        selaAddSel(sela, sel, NULL, 0);
	sel = selRotateOrth(sel, 1);
        selaAddSel(sela, sel, "sel_48_1_rot", 0);
        sel = selCreateFromString(sel_48_2, 3, 3, "sel_48_2");
        selaAddSel(sela, sel, NULL, 0);
        break;
    case 4:
        sela = selaCreate(3);
        sel = selCreateFromString(sel_8_2, 3, 3, "sel_8_2");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_3, 3, 3, "sel_8_3");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_48_2, 3, 3, "sel_48_2");
        selaAddSel(sela, sel, NULL, 0);
        break;
    case 5:
        sela = selaCreate(3);
        sel = selCreateFromString(sel_8_1, 3, 3, "sel_8_1");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_5, 3, 3, "sel_8_5");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_6, 3, 3, "sel_8_6");
        selaAddSel(sela, sel, NULL, 0);
        break;
    case 6:
        sela = selaCreate(4);
        sel = selCreateFromString(sel_8_2, 3, 3, "sel_8_2");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_3, 3, 3, "sel_8_3");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_8, 3, 3, "sel_8_8");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_9, 3, 3, "sel_8_9");
        selaAddSel(sela, sel, NULL, 0);
        break;
    case 7:
        sela = selaCreate(4);
        sel = selCreateFromString(sel_8_5, 3, 3, "sel_8_5");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_6, 3, 3, "sel_8_6");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_8_7, 3, 3, "sel_8_7");
        selaAddSel(sela, sel, NULL, 0);
        sel = selRotateOrth(sel, 1);
        selaAddSel(sela, sel, "sel_8_7_rot", 0);
        break;
    case 8:  /* thicken for this one; just a few iterations */
        sela = selaCreate(2);
        sel = selCreateFromString(sel_4_2, 3, 3, "sel_4_2");
        selaAddSel(sela, sel, NULL, 0);
        sel = selCreateFromString(sel_4_3, 3, 3, "sel_4_3");
        selaAddSel(sela, sel, NULL, 0);
        pixt = pixThinGeneral(pixs, type, sela, maxiters);
        pixd = pixRemoveBorderConnComps(pixt, 4);
        pixDestroy(&pixt);
        break;
    case 9:  /* thicken for this one; just a few iterations */
        sela = selaCreate(1);
        sel = selCreateFromString(sel_8_4, 3, 3, "sel_8_4");
        selaAddSel(sela, sel, NULL, 0);
        pixt = pixThinGeneral(pixs, type, sela, maxiters);
        pixd = pixRemoveBorderConnComps(pixt, 4);
        pixDestroy(&pixt);
        break;
    default:
        return (PIX *)ERROR_PTR("invalid index", procName, NULL);
    }

    if (index <= 7)
        pixd = pixThinGeneral(pixs, type, sela, maxiters);

        /* Optionally display the sels */
    if (selfile) {
        pixt = selaDisplayInPix(sela, 35, 3, 15, 4);
        pixWrite(selfile, pixt, IFF_PNG);
        pixDestroy(&pixt);
    }

    selaDestroy(&sela);
    return pixd;
}
Esempio n. 8
0
int main(int    argc,
         char **argv)
{
l_int32      w, h, d, w2, h2, i, ncols, ret;
l_float32    angle, conf;
BOX         *box;
BOXA        *boxa, *boxa2;
PIX         *pix, *pixs, *pixb, *pixb2, *pixd;
PIX         *pix1, *pix2, *pix3, *pix4, *pix5, *pix6;
PIXA        *pixam;  /* mask with a single component over each column */
PIXA        *pixac, *pixad, *pixat;
PIXAA       *pixaa, *pixaa2;
SEL         *selsplit;
static char  mainName[] = "arabic_lines";

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

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

        /* Binarize input */
    pixs = pixRead("arabic.png");
    pixGetDimensions(pixs, &w, &h, &d);
    pix = pixConvertTo1(pixs, 128);

        /* Deskew */
    pixb = pixFindSkewAndDeskew(pix, 1, &angle, &conf);
    pixDestroy(&pix);
    fprintf(stderr, "Skew angle: %7.2f degrees; %6.2f conf\n", angle, conf);
    pixDisplayWrite(pixb, 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.  */
    pixb2 = pixReduceRankBinary2(pixb, 2, NULL);
    pix1 = pixMorphCompSequence(pixb2, "c5.500", 0);
    boxa = pixConnComp(pix1, &pixam, 8);
    ncols = boxaGetCount(boxa);
    fprintf(stderr, "Num columns: %d\n", ncols);
    pixDisplayWrite(pix1, 1);

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

        /* 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++) {
        pix3 = pixaGetPix(pixad, i, L_CLONE);
        box = pixaGetBox(pixad, i, L_COPY);
        pix4 = pixHMT(NULL, pix3, selsplit);
        pixXor(pix4, pix4, pix3);
        boxa2 = pixConnComp(pix4, &pixac, 8);
        pixaaAddPixa(pixaa, pixac, L_INSERT);
        pixaaAddBox(pixaa, box, L_INSERT);
        pix5 = pixaDisplayRandomCmap(pixac, 0, 0);
        pixDisplayWrite(pix5, 1);
        fprintf(stderr, "Num textlines in col %d: %d\n", i,
                boxaGetCount(boxa2));
        pixDestroy(&pix5);
        pixDestroy(&pix3);
        pixDestroy(&pix4);
        boxaDestroy(&boxa2);
    }

        /* Visual output */
    ret = system("gthumb /tmp/display/file* &");
    pixat = pixaReadFiles("/tmp/display", "file");
    pix5 = selDisplayInPix(selsplit, 31, 2);
    pixaAddPix(pixat, pix5, L_INSERT);
    pix6 = pixaDisplayTiledAndScaled(pixat, 32, 400, 3, 0, 35, 3);
    pixWrite("/tmp/result.png", pix6, IFF_PNG);
    pixaDestroy(&pixat);
    pixDestroy(&pix6);

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

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

        /* Cleanup */
    pixDestroy(&pixb2);
    pixDestroy(&pix1);
    pixaDestroy(&pixam);
    pixaDestroy(&pixad);
    pixaaDestroy(&pixaa);
    pixaaDestroy(&pixaa2);
    boxaDestroy(&boxa);
    selDestroy(&selsplit);
    pixDestroy(&pixs);
    pixDestroy(&pixb);
    return 0;
}
Esempio n. 9
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);
}
Esempio n. 10
0
int main(int    argc,
         char **argv)
{
BOX         *box;
PIX         *pix, *pixs, *pixd, *pixt;
PIXA        *pixa;
SEL         *sel, *sel1, *sel2, *sel3;
SELA        *sela4, *sela8, *sela48;
static char  mainName[] = "ccthin1_reg";

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

        /* Generate and display all of the 4-cc sels */
    sela4 = selaCreate(9);
    sel = selCreateFromString(sel_4_1, 3, 3, "sel_4_1");
    selaAddSel(sela4, sel, NULL, 0);
    sel = selCreateFromString(sel_4_2, 3, 3, "sel_4_2");
    selaAddSel(sela4, sel, NULL, 0);
    sel = selCreateFromString(sel_4_3, 3, 3, "sel_4_3");
    selaAddSel(sela4, sel, NULL, 0);
    sel = selCreateFromString(sel_4_4, 3, 3, "sel_4_4");
    selaAddSel(sela4, sel, NULL, 0);
    sel = selCreateFromString(sel_4_5, 3, 3, "sel_4_5");
    selaAddSel(sela4, sel, NULL, 0);
    sel = selCreateFromString(sel_4_6, 3, 3, "sel_4_6");
    selaAddSel(sela4, sel, NULL, 0);
    sel = selCreateFromString(sel_4_7, 3, 3, "sel_4_7");
    selaAddSel(sela4, sel, NULL, 0);
    sel = selCreateFromString(sel_4_8, 3, 3, "sel_4_8");
    selaAddSel(sela4, sel, NULL, 0);
    sel = selCreateFromString(sel_4_9, 3, 3, "sel_4_9");
    selaAddSel(sela4, sel, NULL, 0);
    pixt = selaDisplayInPix(sela4, 35, 3, 15, 3);
    pixWrite("/tmp/junkallsel4.png", pixt, IFF_PNG);
    pixDestroy(&pixt);
    selaDestroy(&sela4);

        /* Generate and display all of the 8-cc sels */
    sela8 = selaCreate(9);
    sel = selCreateFromString(sel_8_1, 3, 3, "sel_8_1");
    selaAddSel(sela8, sel, NULL, 0);
    sel = selCreateFromString(sel_8_2, 3, 3, "sel_8_2");
    selaAddSel(sela8, sel, NULL, 0);
    sel = selCreateFromString(sel_8_3, 3, 3, "sel_8_3");
    selaAddSel(sela8, sel, NULL, 0);
    sel = selCreateFromString(sel_8_4, 3, 3, "sel_8_4");
    selaAddSel(sela8, sel, NULL, 0);
    sel = selCreateFromString(sel_8_5, 3, 3, "sel_8_5");
    selaAddSel(sela8, sel, NULL, 0);
    sel = selCreateFromString(sel_8_6, 3, 3, "sel_8_6");
    selaAddSel(sela8, sel, NULL, 0);
    sel = selCreateFromString(sel_8_7, 3, 3, "sel_8_7");
    selaAddSel(sela8, sel, NULL, 0);
    sel = selCreateFromString(sel_8_8, 3, 3, "sel_8_8");
    selaAddSel(sela8, sel, NULL, 0);
    sel = selCreateFromString(sel_8_9, 3, 3, "sel_8_9");
    selaAddSel(sela8, sel, NULL, 0);
    pixt = selaDisplayInPix(sela8, 35, 3, 15, 3);
    pixWrite("/tmp/junkallsel8.png", pixt, IFF_PNG);
    pixDestroy(&pixt);
    selaDestroy(&sela8);

        /* Generate and display all of the 4 and 8-cc preserving sels */
    sela48 = selaCreate(3);
    sel = selCreateFromString(sel_48_1, 3, 3, "sel_48_1");
    selaAddSel(sela48, sel, NULL, 0);
    sel = selCreateFromString(sel_48_2, 3, 3, "sel_48_2");
    selaAddSel(sela48, sel, NULL, 0);
    pixt = selaDisplayInPix(sela48, 35, 3, 15, 4);
    pixWrite("/tmp/junkallsel48.png", pixt, IFF_PNG);
    pixDestroy(&pixt);
    selaDestroy(&sela48);

        /* Generate and display three of the 4-cc sels and their rotations */
    sela4 = selaCreate(3);
    sel = selCreateFromString(sel_4_1, 3, 3, "sel_4_1");
    sel1 = selRotateOrth(sel, 1);
    sel2 = selRotateOrth(sel, 2);
    sel3 = selRotateOrth(sel, 3);
    selaAddSel(sela4, sel, NULL, 0);
    selaAddSel(sela4, sel1, "sel_4_1_90", 0);
    selaAddSel(sela4, sel2, "sel_4_1_180", 0);
    selaAddSel(sela4, sel3, "sel_4_1_270", 0);
    sel = selCreateFromString(sel_4_2, 3, 3, "sel_4_2");
    sel1 = selRotateOrth(sel, 1);
    sel2 = selRotateOrth(sel, 2);
    sel3 = selRotateOrth(sel, 3);
    selaAddSel(sela4, sel, NULL, 0);
    selaAddSel(sela4, sel1, "sel_4_2_90", 0);
    selaAddSel(sela4, sel2, "sel_4_2_180", 0);
    selaAddSel(sela4, sel3, "sel_4_2_270", 0);
    sel = selCreateFromString(sel_4_3, 3, 3, "sel_4_3");
    sel1 = selRotateOrth(sel, 1);
    sel2 = selRotateOrth(sel, 2);
    sel3 = selRotateOrth(sel, 3);
    selaAddSel(sela4, sel, NULL, 0);
    selaAddSel(sela4, sel1, "sel_4_3_90", 0);
    selaAddSel(sela4, sel2, "sel_4_3_180", 0);
    selaAddSel(sela4, sel3, "sel_4_3_270", 0);
    pixt = selaDisplayInPix(sela4, 35, 3, 15, 4);
    pixWrite("/tmp/junksel4.png", pixt, IFF_PNG);
    pixDestroy(&pixt);
    selaDestroy(&sela4);

        /* Generate and display four of the 8-cc sels and their rotations */
    sela8 = selaCreate(4);
    sel = selCreateFromString(sel_8_2, 3, 3, "sel_8_2");
    sel1 = selRotateOrth(sel, 1);
    sel2 = selRotateOrth(sel, 2);
    sel3 = selRotateOrth(sel, 3);
    selaAddSel(sela8, sel, NULL, 0);
    selaAddSel(sela8, sel1, "sel_8_2_90", 0);
    selaAddSel(sela8, sel2, "sel_8_2_180", 0);
    selaAddSel(sela8, sel3, "sel_8_2_270", 0);
    sel = selCreateFromString(sel_8_3, 3, 3, "sel_8_3");
    sel1 = selRotateOrth(sel, 1);
    sel2 = selRotateOrth(sel, 2);
    sel3 = selRotateOrth(sel, 3);
    selaAddSel(sela8, sel, NULL, 0);
    selaAddSel(sela8, sel1, "sel_8_3_90", 0);
    selaAddSel(sela8, sel2, "sel_8_3_180", 0);
    selaAddSel(sela8, sel3, "sel_8_3_270", 0);
    sel = selCreateFromString(sel_8_5, 3, 3, "sel_8_5");
    sel1 = selRotateOrth(sel, 1);
    sel2 = selRotateOrth(sel, 2);
    sel3 = selRotateOrth(sel, 3);
    selaAddSel(sela8, sel, NULL, 0);
    selaAddSel(sela8, sel1, "sel_8_5_90", 0);
    selaAddSel(sela8, sel2, "sel_8_5_180", 0);
    selaAddSel(sela8, sel3, "sel_8_5_270", 0);
    sel = selCreateFromString(sel_8_6, 3, 3, "sel_8_6");
    sel1 = selRotateOrth(sel, 1);
    sel2 = selRotateOrth(sel, 2);
    sel3 = selRotateOrth(sel, 3);
    selaAddSel(sela8, sel, NULL, 0);
    selaAddSel(sela8, sel1, "sel_8_6_90", 0);
    selaAddSel(sela8, sel2, "sel_8_6_180", 0);
    selaAddSel(sela8, sel3, "sel_8_6_270", 0);
    pixt = selaDisplayInPix(sela8, 35, 3, 15, 4);
    pixWrite("/tmp/junksel8.png", pixt, IFF_PNG);
    pixDestroy(&pixt);
    selaDestroy(&sela8);

        /* Test the best 4 and 8 cc thinning */
    pixDisplayWrite(NULL, 0);
    pix = pixRead("feyn.tif");
    box = boxCreate(683, 799, 970, 479);
    pixs = pixClipRectangle(pix, box, NULL);
    pixDisplayWrite(pixs, 1);

    pixt = pixThin(pixs, L_THIN_FG, 4, 0);
    pixDisplayWrite(pixt, 1);
    pixDestroy(&pixt);
    pixt = pixThin(pixs, L_THIN_BG, 4, 0);
    pixDisplayWrite(pixt, 1);
    pixDestroy(&pixt);

    pixt = pixThin(pixs, L_THIN_FG, 8, 0);
    pixDisplayWrite(pixt, 1);
    pixDestroy(&pixt);
    pixt = pixThin(pixs, L_THIN_BG, 8, 0);
    pixDisplayWrite(pixt, 1);
    pixDestroy(&pixt);

        /* Display tiled */
    pixa = pixaReadFiles("/tmp/display", "file");
    pixd = pixaDisplayTiledAndScaled(pixa, 8, 500, 1, 0, 25, 2);
    pixWrite("/tmp/junktiles.jpg", pixd, IFF_JFIF_JPEG);
    pixDestroy(&pixd);
    pixaDestroy(&pixa);
    pixDestroy(&pix);
    pixDestroy(&pixs);
    boxDestroy(&box);

    pixDisplayMultiple("/tmp/display/file*");
    return 0;
}
Esempio n. 11
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;
}
Esempio n. 12
0
/*!
 *  pixMirrorDetect()
 *
 *      Input:  pixs (1 bpp, deskewed, English text)
 *              &conf (<return> confidence that text is not LR mirror reversed)
 *              mincount (min number of left + right; use 0 for default)
 *              debug (1 for debug output; 0 otherwise)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) For this test, it is necessary that the text is horizontally
 *          oriented, with ascenders going up.
 *      (2) conf is the normalized difference between the number of
 *          right and left facing characters with ascenders.
 *          Left-facing are {d}; right-facing are {b, h, k}.
 *          At least that was the expectation.  In practice, we can
 *          really just say that it is the normalized difference in
 *          hits using two specific hit-miss filters, textsel1 and textsel2,
 *          after the image has been suitably pre-filtered so that
 *          these filters are effective.  See (4) for what's really happening.
 *      (3) A large positive conf value indicates normal text, whereas
 *          a large negative conf value means the page is mirror reversed.
 *      (4) The implementation is a bit tricky.  The general idea is
 *          to fill the x-height part of characters, but not the space
 *          between them, before doing the HMT.  This is done by
 *          finding pixels added using two different operations -- a
 *          horizontal close and a vertical dilation -- and adding
 *          the intersection of these sets to the original.  It turns
 *          out that the original intuition about the signal was largely
 *          in error: much of the signal for right-facing characters
 *          comes from the lower part of common x-height characters, like
 *          the e and c, that remain open after these operations.
 *          So it's important that the operations to close the x-height
 *          parts of the characters are purposely weakened sufficiently
 *          to allow these characters to remain open.  The wonders
 *          of morphology!
 */
l_int32
pixMirrorDetect(PIX        *pixs,
                l_float32  *pconf,
                l_int32     mincount,
                l_int32     debug)
{
l_int32    count1, count2, nmax;
l_float32  nleft, nright;
PIX       *pixt0, *pixt1, *pixt2, *pixt3;
SEL       *sel1, *sel2;

    PROCNAME("pixMirrorDetect");

    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_MIRROR_FLIP_COUNT;

    sel1 = selCreateFromString(textsel1, 5, 6, NULL);
    sel2 = selCreateFromString(textsel2, 5, 6, NULL);

        /* Fill x-height characters but not space between them, sort of. */
    pixt3 = pixMorphCompSequence(pixs, "d1.30", 0);
    pixXor(pixt3, pixt3, pixs);
    pixt0 = pixMorphCompSequence(pixs, "c15.1", 0);
    pixXor(pixt0, pixt0, pixs);
    pixAnd(pixt0, pixt0, pixt3);
    pixOr(pixt0, pixt0, pixs);
    pixDestroy(&pixt3);
/*    pixDisplayWrite(pixt0, 1); */

        /* Filter the right-facing characters. */
    pixt1 = pixHMT(NULL, pixt0, sel1);
    pixt3 = pixReduceRankBinaryCascade(pixt1, 1, 1, 0, 0);
    pixCountPixels(pixt3, &count1, NULL);
    pixDebugFlipDetect("junkpixright", pixs, pixt1, debug);
    pixDestroy(&pixt1);
    pixDestroy(&pixt3);

        /* Filter the left-facing characters. */
    pixt2 = pixHMT(NULL, pixt0, sel2);
    pixt3 = pixReduceRankBinaryCascade(pixt2, 1, 1, 0, 0);
    pixCountPixels(pixt3, &count2, NULL);
    pixDebugFlipDetect("junkpixleft", pixs, pixt2, debug);
    pixDestroy(&pixt2);
    pixDestroy(&pixt3);

    nright = (l_float32)count1;
    nleft = (l_float32)count2;
    nmax = L_MAX(count1, count2);
    pixDestroy(&pixt0);
    selDestroy(&sel1);
    selDestroy(&sel2);

    if (nmax > mincount)
        *pconf = 2. * ((nright - nleft) / sqrt(nright + nleft));

    if (debug) {
        fprintf(stderr, "nright = %f, nleft = %f\n", nright, nleft);
        if (*pconf > DEFAULT_MIN_MIRROR_FLIP_CONF)
            fprintf(stderr, "Text is not mirror reversed\n");
        if (*pconf < -DEFAULT_MIN_MIRROR_FLIP_CONF)
            fprintf(stderr, "Text is mirror reversed\n");
    }

    return 0;
}
Esempio n. 13
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;
}