Пример #1
0
/*!
 * \brief   pixConnComp()
 *
 * \param[in]    pixs 1 bpp
 * \param[out]   ppixa   [optional] pixa of each c.c.
 * \param[in]    connectivity 4 or 8
 * \return  boxa, or NULL on error
 *
 * <pre>
 * Notes:
 *      (1) This is the top-level call for getting bounding boxes or
 *          a pixa of the components, and it can be used instead
 *          of either pixConnCompBB() or pixConnCompPixa(), rsp.
 * </pre>
 */
BOXA *
pixConnComp(PIX     *pixs,
            PIXA   **ppixa,
            l_int32  connectivity)
{

    PROCNAME("pixConnComp");

    if (ppixa) *ppixa = NULL;
    if (!pixs)
        return (BOXA *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 1)
        return (BOXA *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
    if (connectivity != 4 && connectivity != 8)
        return (BOXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);

    if (!ppixa)
        return pixConnCompBB(pixs, connectivity);
    else
        return pixConnCompPixa(pixs, ppixa, connectivity);
}
Пример #2
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);
}
Пример #3
0
int main_find_pattern(int    argc,
	char **argv)
{
	char        *filein, *fileout, *patternfile;
	l_int32      w, h, i, n;
	BOX         *box, *boxe;
	BOXA        *boxa1, *boxa2;
	PIX         *pixs, *pixp, *pixpe;
	PIX         *pixd, *pixt1, *pixt2, *pixhmt;
	SEL         *sel_2h, *sel;
	static char  mainName[] = "findpattern1";

	filein = "feyn.tif";
	patternfile = "char.tif";
	fileout = "result.findpattern1";

	if ((pixs = pixRead(filein)) == NULL)
		printf("pixs not made\n");
	if ((pixp = pixRead(patternfile)) == NULL)
		printf("pixp not made\n");
	w = pixGetWidth(pixp);
	h = pixGetHeight(pixp);

	/* generate the hit-miss Sel with runs */
	sel = pixGenerateSelWithRuns(pixp, NumHorLines, NumVertLines, 0,
		MinRunlength, 7, 7, 0, 0, &pixpe);

	/* display the Sel two ways */
	selWriteStream(stderr, sel);
	pixt1 = pixDisplayHitMissSel(pixpe, sel, 9, HitColor, MissColor);
	pixDisplay(pixt1, 200, 200);
	pixWrite("junkpixt", pixt1, IFF_PNG);

	/* use the Sel to find all instances in the page */
	startTimer();
	pixhmt = pixHMT(NULL, pixs, sel);
	fprintf(stderr, "Time to find patterns = %7.3f\n", stopTimer());

	/* small erosion to remove noise; typically not necessary if
	* there are enough elements in the Sel */
	sel_2h = selCreateBrick(1, 2, 0, 0, SEL_HIT);
	pixt2 = pixErode(NULL, pixhmt, sel_2h);

	/* display the result visually by placing the Sel at each
	* location found */
	pixd = pixDilate(NULL, pixt2, sel);
	pixWrite(fileout, pixd, IFF_TIFF_G4);

	/* display outut with an outline around each located pattern */
	boxa1 = pixConnCompBB(pixt2, 8);
	n = boxaGetCount(boxa1);
	boxa2 = boxaCreate(n);
	for (i = 0; i < n; i++) {
		box = boxaGetBox(boxa1, i, L_COPY);
		boxe = boxCreate(box->x - w / 2, box->y - h / 2, w + 4, h + 4);
		boxaAddBox(boxa2, boxe, L_INSERT);
		pixRenderBox(pixs, boxe, 4, L_FLIP_PIXELS);
		boxDestroy(&box);
	}
	pixWrite("junkoutline", pixs, IFF_TIFF_G4);
	//boxaWriteStream(stderr, boxa2); //TODO ???

	pixDestroy(&pixs);
	pixDestroy(&pixp);
	pixDestroy(&pixpe);
	pixDestroy(&pixt1);
	pixDestroy(&pixt2);
	pixDestroy(&pixhmt);
	pixDestroy(&pixd);
	selDestroy(&sel);
	selDestroy(&sel_2h);
	boxaDestroy(&boxa1);
	boxaDestroy(&boxa2);

	printf("\n---\nEND\n");
	getchar();
	return 0;
}
Пример #4
0
l_int32 main(int    argc,
             char **argv)
{
l_int32       bx, by, bw, bh;
l_uint32      pixval;
BOX          *box1, *box2;
BOXA         *boxa;
PIX          *pixs, *pixm, *pixd;
PIX          *pix0, *pix1, *pix2, *pix3, *pix4, *pix5, *pix6;
L_REGPARAMS  *rp;

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

        /* Find a mask for repainting pixels */
    pixs = pixRead("amoris.2.150.jpg");
    pix1 = MakeReplacementMask(pixs);
    boxa = pixConnCompBB(pix1, 8);
    box1 = boxaGetBox(boxa, 0, L_COPY);
    boxaDestroy(&boxa);

    /*--------------------------------------------------------*
     *                Show the individual steps               *
     *--------------------------------------------------------*/
        /* Locate a good tile to use */
    pixFindRepCloseTile(pixs, box1, L_VERT, 20, 30, 7, &box2, 1);
    pix0 = pixCopy(NULL, pix1);
    pixRenderBox(pix0, box2, 2, L_SET_PIXELS);

        /* Make a patch using this tile */
    boxGetGeometry(box1, &bx, &by, &bw, &bh);
    pix2 = pixClipRectangle(pixs, box2, NULL);
    regTestWritePixAndCheck(rp, pix2, IFF_PNG);  /* 0 */
    pixDisplayWithTitle(pix2, 400, 100, NULL, rp->display);
    pix3 = pixMirroredTiling(pix2, bw, bh);
    regTestWritePixAndCheck(rp, pix3, IFF_PNG);  /* 1 */
    pixDisplayWithTitle(pix3, 1000, 0, NULL, rp->display);

        /* Paint the patch through the mask */
    pixd = pixCopy(NULL, pixs);
    pixm = pixClipRectangle(pix1, box1, NULL);
    pixCombineMaskedGeneral(pixd, pix3, pixm, bx, by);
    regTestWritePixAndCheck(rp, pixd, IFF_PNG);  /* 2 */
    pixDisplayWithTitle(pixd, 0, 0, NULL, rp->display);
    boxDestroy(&box2);
    pixDestroy(&pixm);
    pixDestroy(&pixd);
    pixDestroy(&pix2);

        /* Blend two patches and then overlay.  Use the previous
         * tile found vertically and a new one found horizontally. */
    pixFindRepCloseTile(pixs, box1, L_HORIZ, 20, 30, 7, &box2, 1);
    pixRenderBox(pix0, box2, 2, L_SET_PIXELS);
    regTestWritePixAndCheck(rp, pix0, IFF_TIFF_G4);  /* 3 */
    pixDisplayWithTitle(pix0, 100, 100, NULL, rp->display);
    pix2 = pixClipRectangle(pixs, box2, NULL);
    pix4 = pixMirroredTiling(pix2, bw, bh);
    regTestWritePixAndCheck(rp, pix4, IFF_PNG);  /* 4 */
    pixDisplayWithTitle(pix4, 1100, 0, NULL, rp->display);
    pix5 = pixBlend(pix3, pix4, 0, 0, 0.5);
    regTestWritePixAndCheck(rp, pix5, IFF_PNG);  /* 5 */
    pixDisplayWithTitle(pix5, 1200, 0, NULL, rp->display);
    pix6 = pixClipRectangle(pix1, box1, NULL);
    pixd = pixCopy(NULL, pixs);
    pixCombineMaskedGeneral(pixd, pix5, pix6, bx, by);
    regTestWritePixAndCheck(rp, pixd, IFF_PNG);  /* 6 */
    pixDisplayWithTitle(pixd, 700, 200, NULL, rp->display);
    boxDestroy(&box2);
    pixDestroy(&pixd);
    pixDestroy(&pix0);
    pixDestroy(&pix2);
    pixDestroy(&pix3);
    pixDestroy(&pix4);
    pixDestroy(&pix5);
    pixDestroy(&pix6);

    /*--------------------------------------------------------*
     *          Show painting from a color near region        *
     *--------------------------------------------------------*/
    pix2 = pixCopy(NULL, pixs);
    pixGetColorNearMaskBoundary(pix2, pix1, box1, 20, &pixval, 0);
    pix3 = pixClipRectangle(pix1, box1, NULL);
    boxGetGeometry(box1, &bx, &by, NULL, NULL);
    pixSetMaskedGeneral(pix2, pix3, pixval, bx, by);
    regTestWritePixAndCheck(rp, pix2, IFF_PNG);  /* 7 */
    pixDisplayWithTitle(pix2, 0, 0, NULL, rp->display);
    boxDestroy(&box1);
    pixDestroy(&pix2);
    pixDestroy(&pix3);

    /*--------------------------------------------------------*
     *             Use the higher-level function              *
     *--------------------------------------------------------*/
        /* Use various tile selections and tile blending with one component */
    pix2 = pixCopy(NULL, pixs);
    pix3 = pixCopy(NULL, pixs);
    pix4 = pixCopy(NULL, pixs);
    pixPaintSelfThroughMask(pix2, pix1, 0, 0, L_HORIZ, 30, 50, 5, 10);
    pixPaintSelfThroughMask(pix3, pix1, 0, 0, L_VERT, 30, 50, 5, 0);
    pixPaintSelfThroughMask(pixs, pix1, 0, 0, L_BOTH_DIRECTIONS, 30, 50, 5, 20);
    regTestWritePixAndCheck(rp, pix2, IFF_PNG);  /* 8 */
    regTestWritePixAndCheck(rp, pix3, IFF_PNG);  /* 9 */
    regTestWritePixAndCheck(rp, pixs, IFF_PNG);  /* 10 */
    pixDisplayWithTitle(pix2, 300, 0, NULL, rp->display);
    pixDisplayWithTitle(pix3, 500, 0, NULL, rp->display);
    pixDisplayWithTitle(pixs, 700, 0, NULL, rp->display);

        /* Test with two components; */
    pix5 = pixFlipLR(NULL, pix1);
    pixOr(pix5, pix5, pix1);
    pixPaintSelfThroughMask(pix4, pix5, 0, 0, L_BOTH_DIRECTIONS, 50, 100, 5, 9);
    regTestWritePixAndCheck(rp, pix4, IFF_PNG);  /* 11 */
    pixDisplayWithTitle(pix4, 900, 0, NULL, rp->display);
    pixDestroy(&pixs);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pix3);
    pixDestroy(&pix4);
    pixDestroy(&pix5);

    return regTestCleanup(rp);
}
Пример #5
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;
}