Ejemplo n.º 1
0
static PIX *
DisplayBoxa(BOXA  *boxa)
{
l_int32  w, h;
BOX     *box;
PIX     *pix1, *pix2, *pix3;
PIXA    *pixa;

    pixa = pixaCreate(2);
    boxaGetExtent(boxa, &w, &h, &box);
    pix1 = pixCreate(w, h, 1);
    pixMaskBoxa(pix1, pix1, boxa, L_SET_PIXELS);
    pixaAddPix(pixa, pix1, L_INSERT);
    pix2 = pixCreate(w, h, 32);
    pixSetAll(pix2);
    pixRenderBoxaArb(pix2, boxa, 2, 0, 255, 0);
    pixRenderBoxArb(pix2, box, 3, 255, 0, 0);
    pixaAddPix(pixa, pix2, L_INSERT);
    pix3 = pixaDisplayTiledInRows(pixa, 32, 1000, 1.0, 0, 30, 2);
    boxDestroy(&box);
    pixaDestroy(&pixa);
    return pix3;
}
Ejemplo n.º 2
0
/*!
 * Note: this method is generally inferior to pixHasColorRegions(); it
 *       is retained as a reference only
 *
 * \brief   pixFindColorRegionsLight()
 *
 * \param[in]    pixs        32 bpp rgb
 * \param[in]    pixm        [optional] 1 bpp mask image
 * \param[in]    factor      subsample factor; integer >= 1
 * \param[in]    darkthresh  threshold to eliminate dark pixels (e.g., text)
 *                           from consideration; typ. 70; -1 for default.
 * \param[in]    lightthresh threshold for minimum gray value at 95% rank
 *                           near white; typ. 220; -1 for default
 * \param[in]    mindiff     minimum difference from 95% rank value, used
 *                           to count darker pixels; typ. 50; -1 for default
 * \param[in]    colordiff   minimum difference in (max - min) component to
 *                           qualify as a color pixel; typ. 40; -1 for default
 * \param[out]   pcolorfract fraction of 'color' pixels found
 * \param[out]   pcolormask1 [optional] mask over background color, if any
 * \param[out]   pcolormask2 [optional] filtered mask over background color
 * \param[out]   pixadb      [optional] debug intermediate results
 * \return  0 if OK, 1 on error
 *
 * <pre>
 * Notes:
 *      (1) This function tries to determine if there is a significant
 *          color or darker region on a scanned page image where part
 *          of the image is very close to "white".  It will also allow
 *          extraction of small regions of lightly colored pixels.
 *          If the background is darker (and reddish), use instead
 *          pixHasColorRegions2().
 *      (2) If %pixm exists, only pixels under fg are considered. Typically,
 *          the inverse of %pixm would have fg pixels over a photograph.
 *      (3) There are four thresholds.
 *          * %darkthresh: ignore pixels darker than this (typ. fg text).
 *            We make a 1 bpp mask of these pixels, and then dilate it to
 *            remove all vestiges of fg from their vicinity.
 *          * %lightthresh: let val95 be the pixel value for which 95%
 *            of the non-masked pixels have a lower value (darker) of
 *            their min component.  Then if val95 is darker than
 *            %lightthresh, the image is not considered to have a
 *            light bg, and this returns 0.0 for %colorfract.
 *          * %mindiff: we are interested in the fraction of pixels that
 *            have two conditions.  The first is that their min component
 *            is at least %mindiff darker than val95.
 *          * %colordiff: the second condition is that the max-min diff
 *            of the pixel components exceeds %colordiff.
 *      (4) This returns in %pcolorfract the fraction of pixels that have
 *          both a min component that is at least %mindiff below that at the
 *          95% rank value (where 100% rank is the lightest value), and
 *          a max-min diff that is at least %colordiff.  Without the
 *          %colordiff constraint, gray pixels of intermediate value
 *          could get flagged by this function.
 *      (5) No masks are returned unless light color pixels are found.
 *          If colorfract > 0.0 and %pcolormask1 is defined, this returns
 *          a 1 bpp mask with fg pixels over the color background.
 *          This mask may have some holes in it.
 *      (6) If colorfract > 0.0 and %pcolormask2 is defined, this returns
 *          a filtered version of colormask1.  The two changes are
 *            (a) small holes have been filled
 *            (b) components near the border have been removed.
 *          The latter insures that dark pixels near the edge of the
 *          image are not included.
 *      (7) To generate a boxa of rectangular regions from the overlap
 *          of components in the filtered mask:
 *                boxa1 = pixConnCompBB(colormask2, 8);
 *                boxa2 = boxaCombineOverlaps(boxa1);
 *          This is done here in debug mode.
 * </pre>
 */
static l_int32
pixFindColorRegionsLight(PIX        *pixs,
                         PIX        *pixm,
                         l_int32     factor,
                         l_int32     darkthresh,
                         l_int32     lightthresh,
                         l_int32     mindiff,
                         l_int32     colordiff,
                         l_float32  *pcolorfract,
                         PIX       **pcolormask1,
                         PIX       **pcolormask2,
                         PIXA       *pixadb)
{
l_int32    lightbg, w, h, count;
l_float32  ratio, val95, rank;
BOXA      *boxa1, *boxa2;
NUMA      *nah;
PIX       *pix1, *pix2, *pix3, *pix4, *pix5, *pixm1, *pixm2, *pixm3;

    PROCNAME("pixFindColorRegionsLight");

    if (pcolormask1) *pcolormask1 = NULL;
    if (pcolormask2) *pcolormask2 = NULL;
    if (!pcolorfract)
        return ERROR_INT("&colorfract not defined", procName, 1);
    *pcolorfract = 0.0;
    if (!pixs || pixGetDepth(pixs) != 32)
        return ERROR_INT("pixs not defined or not 32 bpp", procName, 1);
    if (factor < 1) factor = 1;
    if (darkthresh < 0) darkthresh = 70;  /* defaults */
    if (lightthresh < 0) lightthresh = 220;
    if (mindiff < 0) mindiff = 50;
    if (colordiff < 0) colordiff = 40;

        /* Check if pixm covers most of the image.  If so, just return. */
    pixGetDimensions(pixs, &w, &h, NULL);
    if (pixm) {
        pixCountPixels(pixm, &count, NULL);
        ratio = (l_float32)count / ((l_float32)(w) * h);
        if (ratio > 0.7) {
            if (pixadb) L_INFO("pixm has big fg: %f5.2\n", procName, ratio);
            return 0;
        }
    }

        /* Make a mask pixm1 over the dark pixels in the image:
         * convert to gray using the average of the components;
         * threshold using %darkthresh; do a small dilation;
         * combine with pixm. */
    pix1 = pixConvertRGBToGray(pixs, 0.33, 0.34, 0.33);
    if (pixadb) pixaAddPix(pixadb, pixs, L_COPY);
    if (pixadb) pixaAddPix(pixadb, pix1, L_COPY);
    pixm1 = pixThresholdToBinary(pix1, darkthresh);
    pixDilateBrick(pixm1, pixm1, 7, 7);
    if (pixadb) pixaAddPix(pixadb, pixm1, L_COPY);
    if (pixm) {
        pixOr(pixm1, pixm1, pixm);
        if (pixadb) pixaAddPix(pixadb, pixm1, L_COPY);
    }
    pixDestroy(&pix1);

        /* Convert to gray using the minimum component value and
         * find the gray value at rank 0.95, that represents the light
         * pixels in the image.  If it is too dark, quit. */
    pix1 = pixConvertRGBToGrayMinMax(pixs, L_SELECT_MIN);
    pix2 = pixInvert(NULL, pixm1);  /* pixels that are not dark */
    pixGetRankValueMasked(pix1, pix2, 0, 0, factor, 0.95, &val95, &nah);
    pixDestroy(&pix2);
    if (pixadb) {
        L_INFO("val at 0.95 rank = %5.1f\n", procName, val95);
        gplotSimple1(nah, GPLOT_PNG, "/tmp/lept/histo1", "gray histo");
        pix3 = pixRead("/tmp/lept/histo1.png");
        pix4 = pixExpandReplicate(pix3, 2);
        pixaAddPix(pixadb, pix4, L_INSERT);
        pixDestroy(&pix3);
    }
    lightbg = (l_int32)val95 >= lightthresh;
    numaDestroy(&nah);
    if (!lightbg) {
        pixDestroy(&pix1);
        pixDestroy(&pixm1);
        return 0;
    }

        /* Make mask pixm2 over pixels that are darker than val95 - mindiff. */
    pixm2 = pixThresholdToBinary(pix1, val95 - mindiff);
    if (pixadb) pixaAddPix(pixadb, pixm2, L_COPY);
    pixDestroy(&pix1);

        /* Make a mask pixm3 over pixels that have some color saturation,
         * with a (max - min) component difference >= %colordiff,
         * and combine using AND with pixm2. */
    pix2 = pixConvertRGBToGrayMinMax(pixs, L_CHOOSE_MAXDIFF);
    pixm3 = pixThresholdToBinary(pix2, colordiff);
    pixDestroy(&pix2);
    pixInvert(pixm3, pixm3);  /* need pixels above threshold */
    if (pixadb) pixaAddPix(pixadb, pixm3, L_COPY);
    pixAnd(pixm2, pixm2, pixm3);
    if (pixadb) pixaAddPix(pixadb, pixm2, L_COPY);
    pixDestroy(&pixm3);

        /* Subtract the dark pixels represented by pixm1.
         * pixm2 now holds all the color pixels of interest  */
    pixSubtract(pixm2, pixm2, pixm1);
    pixDestroy(&pixm1);
    if (pixadb) pixaAddPix(pixadb, pixm2, L_COPY);

        /* But we're not quite finished.  Remove pixels from any component
         * that is touching the image border.  False color pixels can
         * sometimes be found there if the image is much darker near
         * the border, due to oxidation or reduced illumination. */
    pixm3 = pixRemoveBorderConnComps(pixm2, 8);
    pixDestroy(&pixm2);
    if (pixadb) pixaAddPix(pixadb, pixm3, L_COPY);

        /* Get the fraction of light color pixels */
    pixCountPixels(pixm3, &count, NULL);
    *pcolorfract = (l_float32)count / (w * h);
    if (pixadb) {
        if (count == 0)
            L_INFO("no light color pixels found\n", procName);
        else
            L_INFO("fraction of light color pixels = %5.3f\n", procName,
                   *pcolorfract);
    }

        /* Debug: extract the color pixels from pixs */
    if (pixadb && count > 0) {
            /* Use pixm3 to extract the color pixels */
        pix3 = pixCreateTemplate(pixs);
        pixSetAll(pix3);
        pixCombineMasked(pix3, pixs, pixm3);
        pixaAddPix(pixadb, pix3, L_INSERT);

            /* Use additional filtering to extract the color pixels */
        pix3 = pixCloseSafeBrick(NULL, pixm3, 15, 15);
        pixaAddPix(pixadb, pix3, L_INSERT);
        pix5 = pixCreateTemplate(pixs);
        pixSetAll(pix5);
        pixCombineMasked(pix5, pixs, pix3);
        pixaAddPix(pixadb, pix5, L_INSERT);

            /* Get the combined bounding boxes of the mask components
             * in pix3, and extract those pixels from pixs. */
        boxa1 = pixConnCompBB(pix3, 8);
        boxa2 = boxaCombineOverlaps(boxa1, NULL);
        pix4 = pixCreateTemplate(pix3);
        pixMaskBoxa(pix4, pix4, boxa2, L_SET_PIXELS);
        pixaAddPix(pixadb, pix4, L_INSERT);
        pix5 = pixCreateTemplate(pixs);
        pixSetAll(pix5);
        pixCombineMasked(pix5, pixs, pix4);
        pixaAddPix(pixadb, pix5, L_INSERT);
        boxaDestroy(&boxa1);
        boxaDestroy(&boxa2);
        pixaAddPix(pixadb, pixs, L_COPY);
    }

        /* Optional colormask returns */
    if (pcolormask2 && count > 0)
        *pcolormask2 = pixCloseSafeBrick(NULL, pixm3, 15, 15);
    if (pcolormask1 && count > 0)
        *pcolormask1 = pixm3;
    else
        pixDestroy(&pixm3);
    return 0;
}
Ejemplo n.º 3
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;
}