Beispiel #1
0
int main(int argc,
         char **argv) {
    char buf[32];
    char *filein, *fileout, *fontdir, *textstr;
    l_int32 n, i, maxdepth, ntext, border, lossless, display, showtext;
    l_float32 scalefact;
    L_BMF *bmf;
    PIX *pix1, *pix2, *pix3, *pix4, *pixd;
    PIXA *pixa, *pixad;
    static char mainName[] = "displaypixa";

    if (argc != 3 && argc != 4 && argc != 7 && argc != 8) {
        fprintf(stderr, "Syntax error in displaypixa:\n"
                "   displaypixa filein fileout [showtext]\n"
                "   displaypixa filein scalefact border"
                " lossless disp fileout [showtext]\n");
        return 1;
    }

    filein = argv[1];
    if ((pixa = pixaRead(filein)) == NULL)
        return ERROR_INT("pixa not made", mainName, 1);
    pixaCountText(pixa, &ntext);

    if (argc == 3 || argc == 4)
        fileout = argv[2];
    if (argc == 4)
        showtext = atoi(argv[3]);

    /* Simple specification; no output text */
    if (argc == 3 ||
        (argc == 4 && (ntext == 0 || showtext == 0))) {  /* no text output */
        pixaVerifyDepth(pixa, &maxdepth);
        pixd = pixaDisplayTiledInRows(pixa, maxdepth, 1400, 1.0, 0, 10, 0);
        pixDisplay(pixd, 100, 100);
        if (pixGetDepth(pixd) == 1)
            pixWrite(fileout, pixd, IFF_PNG);
        else
            pixWrite(fileout, pixd, IFF_JFIF_JPEG);
        pixDestroy(&pixd);
        pixaDestroy(&pixa);
        return 0;
    }

    /* Simple specification with output text */
    if (argc == 4) {  /* showtext == 1 && ntext > 0 */
        n = pixaGetCount(pixa);
        bmf = bmfCreate(NULL, 6);
        pixad = pixaCreate(n);
        for (i = 0; i < n; i++) {
            pix1 = pixaGetPix(pixa, i, L_CLONE);
            pix2 = pixConvertTo32(pix1);
            pix3 = pixAddBorderGeneral(pix2, 10, 10, 5, 5, 0xffffff00);
            textstr = pixGetText(pix1);
            if (textstr && strlen(textstr) > 0) {
                snprintf(buf, sizeof(buf), "%s", textstr);
                pix4 = pixAddSingleTextblock(pix3, bmf, buf, 0xff000000,
                                             L_ADD_BELOW, NULL);
            } else {
                pix4 = pixClone(pix3);
            }
            pixaAddPix(pixad, pix4, L_INSERT);
            pixDestroy(&pix1);
            pixDestroy(&pix2);
            pixDestroy(&pix3);
        }
        bmfDestroy(&bmf);
        pixaVerifyDepth(pixad, &maxdepth);
        pixd = pixaDisplayTiledInRows(pixad, maxdepth, 1400, 1.0, 0, 10, 0);
        pixDisplay(pixd, 100, 100);
        if (pixGetDepth(pixd) == 1)
            pixWrite(fileout, pixd, IFF_PNG);
        else
            pixWrite(fileout, pixd, IFF_JFIF_JPEG);
        pixDestroy(&pixd);
        pixaDestroy(&pixa);
        pixaDestroy(&pixad);
        return 0;
    }

    /* Full specification */
    scalefact = atof(argv[2]);
    border = atoi(argv[3]);
    lossless = atoi(argv[4]);
    display = atoi(argv[5]);
    fileout = argv[6];
    showtext = (argc == 8) ? atoi(argv[7]) : 0;
    if (showtext && ntext == 0)
        L_INFO("No text found in any of the pix\n", mainName);
    bmf = (showtext && ntext > 0) ? bmfCreate(NULL, 6) : NULL;
    n = pixaGetCount(pixa);
    pixad = pixaCreate(n);
    for (i = 0; i < n; i++) {
        pix1 = pixaGetPix(pixa, i, L_CLONE);
        pix2 = pixConvertTo32(pix1);
        pix3 = pixAddBorderGeneral(pix2, 10, 10, 5, 5, 0xffffff00);
        textstr = pixGetText(pix1);
        if (bmf && textstr && strlen(textstr) > 0) {
            snprintf(buf, sizeof(buf), "%s", textstr);
            pix4 = pixAddSingleTextblock(pix3, bmf, buf, 0xff000000,
                                         L_ADD_BELOW, NULL);
        } else {
            pix4 = pixClone(pix3);
        }
        pixaAddPix(pixad, pix4, L_INSERT);
        pixDestroy(&pix1);
        pixDestroy(&pix2);
        pixDestroy(&pix3);
    }
    bmfDestroy(&bmf);

    pixaVerifyDepth(pixad, &maxdepth);
    pixd = pixaDisplayTiledInRows(pixad, maxdepth, 1400, scalefact,
                                  0, 10, border);
    if (display) pixDisplay(pixd, 20, 20);
    if (pixGetDepth(pixd) == 1 || lossless)
        pixWrite(fileout, pixd, IFF_PNG);
    else
        pixWrite(fileout, pixd, IFF_JFIF_JPEG);

    pixDestroy(&pixd);
    pixaDestroy(&pixa);
    pixaDestroy(&pixad);
    return 0;
}
Beispiel #2
0
int main(int    argc,
         char **argv)
{
char        *filein, *fileout;
l_int32      ret;
l_float32    deg2rad;
l_float32    angle, conf, score;
PIX         *pix, *pixs, *pixd;
static char  mainName[] = "skewtest";

    if (argc != 3)
        return ERROR_INT(" Syntax:  skewtest filein fileout", mainName, 1);
    filein = argv[1];
    fileout = argv[2];

    setLeptDebugOK(1);
    pixd = NULL;
    deg2rad = 3.1415926535 / 180.;

    if ((pixs = pixRead(filein)) == NULL)
        return ERROR_INT("pixs not made", mainName, 1);

        /* Find the skew angle various ways */
    pix = pixConvertTo1(pixs, 130);
    pixWrite("/tmp/binarized.tif", pix, IFF_TIFF_G4);
    pixFindSkew(pix, &angle, &conf);
    fprintf(stderr, "pixFindSkew():\n"
                    "  conf = %5.3f, angle = %7.3f degrees\n", conf, angle);

    pixFindSkewSweepAndSearchScorePivot(pix, &angle, &conf, &score,
                                        SWEEP_REDUCTION2, SEARCH_REDUCTION,
                                        0.0, SWEEP_RANGE2, SWEEP_DELTA2,
                                        SEARCH_MIN_DELTA,
                                        L_SHEAR_ABOUT_CORNER);
    fprintf(stderr, "pixFind...Pivot(about corner):\n"
                    "  conf = %5.3f, angle = %7.3f degrees, score = %f\n",
            conf, angle, score);

    pixFindSkewSweepAndSearchScorePivot(pix, &angle, &conf, &score,
                                        SWEEP_REDUCTION2, SEARCH_REDUCTION,
                                        0.0, SWEEP_RANGE2, SWEEP_DELTA2,
                                        SEARCH_MIN_DELTA,
                                        L_SHEAR_ABOUT_CENTER);
    fprintf(stderr, "pixFind...Pivot(about center):\n"
                    "  conf = %5.3f, angle = %7.3f degrees, score = %f\n",
            conf, angle, score);

        /* Use top-level */
    pixd = pixDeskew(pixs, 0);
    pixWriteImpliedFormat(fileout, pixd, 0, 0);


#if 0
        /* Do it piecemeal; fails if outside the range */
    if (pixGetDepth(pixs) == 1) {
        pixd = pixDeskew(pix, DESKEW_REDUCTION);
        pixWrite(fileout, pixd, IFF_PNG);
    }
    else {
        ret = pixFindSkewSweepAndSearch(pix, &angle, &conf, SWEEP_REDUCTION2,
                                        SEARCH_REDUCTION, SWEEP_RANGE2,
                                        SWEEP_DELTA2, SEARCH_MIN_DELTA);
        if (ret)
            L_WARNING("skew angle not valid\n", mainName);
        else {
            fprintf(stderr, "conf = %5.3f, angle = %7.3f degrees\n",
                    conf, angle);
            if (conf > 2.5)
                pixd = pixRotate(pixs, angle * deg2rad, L_ROTATE_AREA_MAP,
                                 L_BRING_IN_WHITE, 0, 0);
            else
                pixd = pixClone(pixs);
            pixWrite(fileout, pixd, IFF_PNG);
            pixDestroy(&pixd);
        }
    }
#endif

    pixDestroy(&pixs);
    pixDestroy(&pix);
    pixDestroy(&pixd);
    return 0;
}
Beispiel #3
0
/*!
 *  pixRunlengthTransform()
 *
 *      Input:   pixs (1 bpp)
 *               color (0 for white runs, 1 for black runs)
 *               direction (L_HORIZONTAL_RUNS, L_VERTICAL_RUNS)
 *               depth (8 or 16 bpp)
 *      Return:  pixd (8 or 16 bpp), or null on error
 *
 *  Notes:
 *      (1) The dest Pix is 8 or 16 bpp, with the pixel values
 *          equal to the runlength in which it is a member.
 *          The length is clipped to the max pixel value if necessary.
 *      (2) The color determines if we're labelling white or black runs.
 *      (3) A pixel that is not a member of the chosen color gets
 *          value 0; it belongs to a run of length 0 of the
 *          chosen color.
 *      (4) To convert for maximum dynamic range, either linear or
 *          log, use pixMaxDynamicRange().
 */
PIX *
pixRunlengthTransform(PIX *pixs,
                      l_int32 color,
                      l_int32 direction,
                      l_int32 depth) {
    l_int32 i, j, w, h, wpld, bufsize, maxsize, n;
    l_int32 *start, *end, *buffer;
    l_uint32 *datad, *lined;
    PIX *pixt, *pixd;

    PROCNAME("pixRunlengthTransform");

    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 (depth != 8 && depth != 16)
        return (PIX *) ERROR_PTR("depth must be 8 or 16 bpp", procName, NULL);

    pixGetDimensions(pixs, &w, &h, NULL);
    if (direction == L_HORIZONTAL_RUNS)
        maxsize = 1 + w / 2;
    else if (direction == L_VERTICAL_RUNS)
        maxsize = 1 + h / 2;
    else
        return (PIX *) ERROR_PTR("invalid direction", procName, NULL);
    bufsize = L_MAX(w, h);

    if ((pixd = pixCreate(w, h, depth)) == NULL)
        return (PIX *) ERROR_PTR("pixd not made", procName, NULL);
    datad = pixGetData(pixd);
    wpld = pixGetWpl(pixd);

    if ((start = (l_int32 *) CALLOC(maxsize, sizeof(l_int32))) == NULL)
        return (PIX *) ERROR_PTR("start not made", procName, NULL);
    if ((end = (l_int32 *) CALLOC(maxsize, sizeof(l_int32))) == NULL)
        return (PIX *) ERROR_PTR("end not made", procName, NULL);
    if ((buffer = (l_int32 *) CALLOC(bufsize, sizeof(l_int32))) == NULL)
        return (PIX *) ERROR_PTR("buffer not made", procName, NULL);

    /* Use fg runs for evaluation */
    if (color == 0)
        pixt = pixInvert(NULL, pixs);
    else
        pixt = pixClone(pixs);

    if (direction == L_HORIZONTAL_RUNS) {
        for (i = 0; i < h; i++) {
            pixFindHorizontalRuns(pixt, i, start, end, &n);
            runlengthMembershipOnLine(buffer, w, depth, start, end, n);
            lined = datad + i * wpld;
            if (depth == 8) {
                for (j = 0; j < w; j++)
                    SET_DATA_BYTE(lined, j, buffer[j]);
            } else {  /* depth == 16 */
                for (j = 0; j < w; j++)
                    SET_DATA_TWO_BYTES(lined, j, buffer[j]);
            }
        }
    } else {  /* L_VERTICAL_RUNS */
        for (j = 0; j < w; j++) {
            pixFindVerticalRuns(pixt, j, start, end, &n);
            runlengthMembershipOnLine(buffer, h, depth, start, end, n);
            if (depth == 8) {
                for (i = 0; i < h; i++) {
                    lined = datad + i * wpld;
                    SET_DATA_BYTE(lined, j, buffer[i]);
                }
            } else {  /* depth == 16 */
                for (i = 0; i < h; i++) {
                    lined = datad + i * wpld;
                    SET_DATA_TWO_BYTES(lined, j, buffer[i]);
                }
            }
        }
    }

    pixDestroy(&pixt);
    FREE(start);
    FREE(end);
    FREE(buffer);
    return pixd;
}
Beispiel #4
0
/*!
 *  pixGenerateSelRandom()
 *
 *      Input:  pix (1 bpp, typically small, to be used as a pattern)
 *              hitfract (fraction of allowable fg pixels that are hits)
 *              missfract (fraction of allowable bg pixels that are misses)
 *              distance (min distance from boundary pixel; use 0 for default)
 *              toppix (number of extra pixels of bg added above)
 *              botpix (number of extra pixels of bg added below)
 *              leftpix (number of extra pixels of bg added to left)
 *              rightpix (number of extra pixels of bg added to right)
 *              &pixe (<optional return> input pix expanded by extra pixels)
 *      Return: sel (hit-miss for input pattern), or null on error
 *
 *  Notes:
 *    (1) Either of hitfract and missfract can be zero.  If both are zero,
 *        the sel would be empty, and NULL is returned.
 *    (2) No elements are selected that are less than 'distance' pixels away
 *        from a boundary pixel of the same color.  This makes the
 *        match much more robust to edge noise.  Valid inputs of
 *        'distance' are 0, 1, 2, 3 and 4.  If distance is either 0 or
 *        greater than 4, we reset it to the default value.
 *    (3) The 4 numbers for adding rectangles of pixels outside the fg
 *        can be use if the pattern is expected to be surrounded by bg
 *        (white) pixels.  On the other hand, if the pattern may be near
 *        other fg (black) components on some sides, use 0 for those sides.
 *    (4) The input pix, as extended by the extra pixels on selected sides,
 *        can optionally be returned.  For debugging, call
 *        pixDisplayHitMissSel() to visualize the hit-miss sel superimposed
 *        on the generating bitmap.
 */
SEL *
pixGenerateSelRandom(PIX       *pixs,
                     l_float32  hitfract,
                     l_float32  missfract,
                     l_int32    distance,
                     l_int32    toppix,
                     l_int32    botpix,
                     l_int32    leftpix,
                     l_int32    rightpix,
                     PIX      **ppixe)
{
l_int32    ws, hs, w, h, x, y, i, j, thresh;
l_uint32   val;
PIX       *pixt1, *pixt2, *pixfg, *pixbg;
SEL       *seld, *sel;

    PROCNAME("pixGenerateSelRandom");

    if (ppixe) *ppixe = NULL;
    if (!pixs)
        return (SEL *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 1)
        return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
    if (hitfract <= 0.0 && missfract <= 0.0)
        return (SEL *)ERROR_PTR("no hits or misses", procName, NULL);
    if (hitfract > 1.0 || missfract > 1.0)
        return (SEL *)ERROR_PTR("fraction can't be > 1.0", procName, NULL);

    if (distance <= 0)
        distance = DEFAULT_DISTANCE_TO_BOUNDARY;
    if (distance > MAX_DISTANCE_TO_BOUNDARY) {
        L_WARNING("distance too large; setting to max value", procName);
        distance = MAX_DISTANCE_TO_BOUNDARY;
    }

        /* Locate the foreground */
    pixClipToForeground(pixs, &pixt1, NULL);
    if (!pixt1)
        return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL);
    ws = pixGetWidth(pixt1);
    hs = pixGetHeight(pixt1);
    w = ws;
    h = hs;

        /* Crop out a region including the foreground, and add pixels
         * on sides depending on the side flags */
    if (toppix || botpix || leftpix || rightpix) {
        x = y = 0;
        if (toppix) {
            h += toppix;
            y = toppix;
        }
        if (botpix)
            h += botpix;
        if (leftpix) {
            w += leftpix;
            x = leftpix;
        }
        if (rightpix)
            w += rightpix;
        pixt2 = pixCreate(w, h, 1);
        pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0);
    }
    else
        pixt2 = pixClone(pixt1);
    if (ppixe)
        *ppixe = pixClone(pixt2);
    pixDestroy(&pixt1);

        /* Identify fg and bg pixels that are at least 'distance' pixels
         * away from the boundary pixels in their set */
    seld = selCreateBrick(2 * distance + 1, 2 * distance + 1,
                          distance, distance, SEL_HIT);
    pixfg = pixErode(NULL, pixt2, seld);
    pixbg = pixDilate(NULL, pixt2, seld);
    pixInvert(pixbg, pixbg);
    selDestroy(&seld);
    pixDestroy(&pixt2);

        /* Generate the sel from a random selection of these points */
    sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE);
    if (hitfract > 0.0) {
        thresh = (l_int32)(hitfract * (l_float64)RAND_MAX);
        for (i = 0; i < h; i++) {
            for (j = 0; j < w; j++) {
                pixGetPixel(pixfg, j, i, &val);
                if (val) {
                    if (rand() < thresh)
                        selSetElement(sel, i, j, SEL_HIT);
                }
            }
        }
    }
    if (missfract > 0.0) {
        thresh = (l_int32)(missfract * (l_float64)RAND_MAX);
        for (i = 0; i < h; i++) {
            for (j = 0; j < w; j++) {
                pixGetPixel(pixbg, j, i, &val);
                if (val) {
                    if (rand() < thresh)
                        selSetElement(sel, i, j, SEL_MISS);
                }
            }
        }
    }

    pixDestroy(&pixfg);
    pixDestroy(&pixbg);
    return sel;
}
Beispiel #5
0
/*!
 *  pixOtsuAdaptiveThreshold()
 *
 *      Input:  pixs (8 bpp)
 *              sx, sy (desired tile dimensions; actual size may vary)
 *              smoothx, smoothy (half-width of convolution kernel applied to
 *                                threshold array: use 0 for no smoothing)
 *              scorefract (fraction of the max Otsu score; typ. 0.1;
 *                          use 0.0 for standard Otsu)
 *              &pixth (<optional return> array of threshold values
 *                      found for each tile)
 *              &pixd (<optional return> thresholded input pixs, based on
 *                     the threshold array)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) The Otsu method finds a single global threshold for an image.
 *          This function allows a locally adapted threshold to be
 *          found for each tile into which the image is broken up.
 *      (2) The array of threshold values, one for each tile, constitutes
 *          a highly downscaled image.  This array is optionally
 *          smoothed using a convolution.  The full width and height of the
 *          convolution kernel are (2 * @smoothx + 1) and (2 * @smoothy + 1).
 *      (3) The minimum tile dimension allowed is 16.  If such small
 *          tiles are used, it is recommended to use smoothing, because
 *          without smoothing, each small tile determines the splitting
 *          threshold independently.  A tile that is entirely in the
 *          image bg will then hallucinate fg, resulting in a very noisy
 *          binarization.  The smoothing should be large enough that no
 *          tile is only influenced by one type (fg or bg) of pixels,
 *          because it will force a split of its pixels.
 *      (4) To get a single global threshold for the entire image, use
 *          input values of @sx and @sy that are larger than the image.
 *          For this situation, the smoothing parameters are ignored.
 *      (5) The threshold values partition the image pixels into two classes:
 *          one whose values are less than the threshold and another
 *          whose values are greater than or equal to the threshold.
 *          This is the same use of 'threshold' as in pixThresholdToBinary().
 *      (6) The scorefract is the fraction of the maximum Otsu score, which
 *          is used to determine the range over which the histogram minimum
 *          is searched.  See numaSplitDistribution() for details on the
 *          underlying method of choosing a threshold.
 *      (7) This uses enables a modified version of the Otsu criterion for
 *          splitting the distribution of pixels in each tile into a
 *          fg and bg part.  The modification consists of searching for
 *          a minimum in the histogram over a range of pixel values where
 *          the Otsu score is within a defined fraction, @scorefract,
 *          of the max score.  To get the original Otsu algorithm, set
 *          @scorefract == 0.
 */
l_int32
pixOtsuAdaptiveThreshold(PIX       *pixs,
                         l_int32    sx,
                         l_int32    sy,
                         l_int32    smoothx,
                         l_int32    smoothy,
                         l_float32  scorefract,
                         PIX      **ppixth,
                         PIX      **ppixd)
{
l_int32     w, h, nx, ny, i, j, thresh;
l_uint32    val;
PIX        *pixt, *pixb, *pixthresh, *pixth, *pixd;
PIXTILING  *pt;

    PROCNAME("pixOtsuAdaptiveThreshold");

    if (!ppixth && !ppixd){
        return ERROR_INT("neither &pixth nor &pixd defined", procName, 1);
	    LOGE("neither &pixth nor &pixd defined");
	}
    if (ppixth) *ppixth = NULL;
    if (ppixd) *ppixd = NULL;
    if (!pixs || pixGetDepth(pixs) != 8){
        return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
		LOGE("pixs not defined or not 8 bpp");
	}
    if (sx < 16 || sy < 16){
        return ERROR_INT("sx and sy must be >= 16", procName, 1);
		LOGE("sx and sy must be >= 16");
	}
        /* Compute the threshold array for the tiles */
    pixGetDimensions(pixs, &w, &h, NULL);
    nx = L_MAX(1, w / sx);
    ny = L_MAX(1, h / sy);

    smoothx = L_MIN(smoothx, (nx - 1) / 2);
    smoothy = L_MIN(smoothy, (ny - 1) / 2);

    pt = pixTilingCreate(pixs, nx, ny, 0, 0, 0, 0);
    pixthresh = pixCreate(nx, ny, 8);
    for (i = 0; i < ny; i++) {
        for (j = 0; j < nx; j++) {
            pixt = pixTilingGetTile(pt, i, j);
            pixSplitDistributionFgBg(pixt, scorefract, 1, &thresh,
                                     NULL, NULL, 0);
            pixSetPixel(pixthresh, j, i, thresh);  /* see note (4) */
            pixDestroy(&pixt);
        }
    }

        /* Optionally smooth the threshold array */
    if (smoothx > 0 || smoothy > 0)
        pixth = pixBlockconv(pixthresh, smoothx, smoothy);
    else
        pixth = pixClone(pixthresh);
    pixDestroy(&pixthresh);

        /* Optionally apply the threshold array to binarize pixs */
    if (ppixd) {
        pixd = pixCreate(w, h, 1);
        for (i = 0; i < ny; i++) {
            for (j = 0; j < nx; j++) {
                pixt = pixTilingGetTile(pt, i, j);
                pixGetPixel(pixth, j, i, &val);
                pixb = pixThresholdToBinary(pixt, val);
                pixTilingPaintTile(pixd, i, j, pixb, pt);
                pixDestroy(&pixt);
                pixDestroy(&pixb);
            }
        }
        *ppixd = pixd;
    }

    if (ppixth)
        *ppixth = pixth;
    else
        pixDestroy(&pixth);

    pixTilingDestroy(&pt);

    return 0;
}
/*!
 * \brief   pixSaveTiledOutline()
 *
 * \param[in]    pixs 1, 2, 4, 8, 32 bpp
 * \param[in]    pixa the pix are accumulated here
 * \param[in]    scalefactor 0.0 to disable; otherwise this is a scale factor
 * \param[in]    newrow 0 if placed on the same row as previous; 1 otherwise
 * \param[in]    space horizontal and vertical spacing, in pixels
 * \param[in]    linewidth width of added outline for image; 0 for no outline
 * \param[in]    dp depth of pixa; 8 or 32 bpp; only used on first call
 * \return  0 if OK, 1 on error.
 *
 * <pre>
 * Notes:
 *      (1) Before calling this function for the first time, use
 *          pixaCreate() to make the %pixa that will accumulate the pix.
 *          This is passed in each time pixSaveTiled() is called.
 *      (2) %scalefactor scales the input image.  After scaling and
 *          possible depth conversion, the image is saved in the input
 *          pixa, along with a box that specifies the location to
 *          place it when tiled later.  Disable saving the pix by
 *          setting %scalefactor == 0.0.
 *      (3) %newrow and %space specify the location of the new pix
 *          with respect to the last one(s) that were entered.
 *      (4) %dp specifies the depth at which all pix are saved.  It can
 *          be only 8 or 32 bpp.  Any colormap is removed.  This is only
 *          used at the first invocation.
 *      (5) This function uses two variables from call to call.
 *          If they were static, the function would not be .so or thread
 *          safe, and furthermore, there would be interference with two or
 *          more pixa accumulating images at a time.  Consequently,
 *          we use the first pix in the pixa to store and obtain both
 *          the depth and the current position of the bottom (one pixel
 *          below the lowest image raster line when laid out using
 *          the boxa).  The bottom variable is stored in the input format
 *          field, which is the only field available for storing an int.
 * </pre>
 */
l_int32
pixSaveTiledOutline(PIX       *pixs,
                    PIXA      *pixa,
                    l_float32  scalefactor,
                    l_int32    newrow,
                    l_int32    space,
                    l_int32    linewidth,
                    l_int32    dp)
{
l_int32  n, top, left, bx, by, bw, w, h, depth, bottom;
BOX     *box;
PIX     *pix1, *pix2, *pix3, *pix4;

    PROCNAME("pixSaveTiledOutline");

    if (scalefactor == 0.0) return 0;

    if (!pixs)
        return ERROR_INT("pixs not defined", procName, 1);
    if (!pixa)
        return ERROR_INT("pixa not defined", procName, 1);

    n = pixaGetCount(pixa);
    if (n == 0) {
        bottom = 0;
        if (dp != 8 && dp != 32) {
            L_WARNING("dp not 8 or 32 bpp; using 32\n", procName);
            depth = 32;
        } else {
            depth = dp;
        }
    } else {  /* extract the depth and bottom params from the first pix */
        pix1 = pixaGetPix(pixa, 0, L_CLONE);
        depth = pixGetDepth(pix1);
        bottom = pixGetInputFormat(pix1);  /* not typical usage! */
        pixDestroy(&pix1);
    }

        /* Remove colormap if it exists; otherwise a copy.  This
         * guarantees that pix4 is not a clone of pixs. */
    pix1 = pixRemoveColormapGeneral(pixs, REMOVE_CMAP_BASED_ON_SRC, L_COPY);

        /* Scale and convert to output depth */
    if (scalefactor == 1.0) {
        pix2 = pixClone(pix1);
    } else if (scalefactor > 1.0) {
        pix2 = pixScale(pix1, scalefactor, scalefactor);
    } else if (scalefactor < 1.0) {
        if (pixGetDepth(pix1) == 1)
            pix2 = pixScaleToGray(pix1, scalefactor);
        else
            pix2 = pixScale(pix1, scalefactor, scalefactor);
    }
    pixDestroy(&pix1);
    if (depth == 8)
        pix3 = pixConvertTo8(pix2, 0);
    else
        pix3 = pixConvertTo32(pix2);
    pixDestroy(&pix2);

        /* Add black outline */
    if (linewidth > 0)
        pix4 = pixAddBorder(pix3, linewidth, 0);
    else
        pix4 = pixClone(pix3);
    pixDestroy(&pix3);

        /* Find position of current pix (UL corner plus size) */
    if (n == 0) {
        top = 0;
        left = 0;
    } else if (newrow == 1) {
        top = bottom + space;
        left = 0;
    } else if (n > 0) {
        pixaGetBoxGeometry(pixa, n - 1, &bx, &by, &bw, NULL);
        top = by;
        left = bx + bw + space;
    }

    pixGetDimensions(pix4, &w, &h, NULL);
    bottom = L_MAX(bottom, top + h);
    box = boxCreate(left, top, w, h);
    pixaAddPix(pixa, pix4, L_INSERT);
    pixaAddBox(pixa, box, L_INSERT);

        /* Save the new bottom value */
    pix1 = pixaGetPix(pixa, 0, L_CLONE);
    pixSetInputFormat(pix1, bottom);  /* not typical usage! */
    pixDestroy(&pix1);
    return 0;
}
/*!
 *  dewarpShowResults()
 *
 *      Input:  dewa
 *              sarray (of indexed input images)
 *              boxa (crop boxes for input images; can be null)
 *              firstpage, lastpage
 *              fontdir (for text bitmap fonts)
 *              pdfout (filename)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) This generates a pdf of image pairs (before, after) for
 *          the designated set of input pages.
 *      (2) If the boxa exists, its elements are aligned with numbers
 *          in the filenames in @sa.  It is used to crop the input images.
 *          It is assumed that the dewa was generated from the cropped
 *          images.  No undercropping is applied before rendering.
 */
l_int32
dewarpShowResults(L_DEWARPA   *dewa,
                  SARRAY      *sa,
                  BOXA        *boxa,
                  l_int32      firstpage,
                  l_int32      lastpage,
                  const char  *fontdir,
                  const char  *pdfout)
{
char       bufstr[256];
char      *outpath;
l_int32    i, modelpage;
L_BMF     *bmf;
BOX       *box;
L_DEWARP  *dew;
PIX       *pixs, *pixc, *pixd, *pixt1, *pixt2;
PIXA      *pixa;

    PROCNAME("dewarpShowResults");

    if (!dewa)
        return ERROR_INT("dewa not defined", procName, 1);
    if (!sa)
        return ERROR_INT("sa not defined", procName, 1);
    if (!pdfout)
        return ERROR_INT("pdfout not defined", procName, 1);
    if (firstpage > lastpage)
        return ERROR_INT("invalid first/last page numbers", procName, 1);

    lept_rmdir("dewarp_pdfout");
    lept_mkdir("dewarp_pdfout");
    if ((bmf = bmfCreate(fontdir, 6)) == NULL)
        L_ERROR("bmf not made; page info not displayed", procName);

    fprintf(stderr, "Dewarping and generating s/by/s view\n");
    for (i = firstpage; i <= lastpage; i++) {
        if (i && (i % 10 == 0)) fprintf(stderr, ".. %d ", i);
        pixs = pixReadIndexed(sa, i);
        if (boxa) {
            box = boxaGetBox(boxa, i, L_CLONE);
            pixc = pixClipRectangle(pixs, box, NULL);
            boxDestroy(&box);
        }
        else
            pixc = pixClone(pixs);
        dew = dewarpaGetDewarp(dewa, i);
        pixd = NULL;
        if (dew) {
            dewarpaApplyDisparity(dewa, dew->pageno, pixc,
                                        GRAYIN_VALUE, 0, 0, &pixd, NULL);
            dewarpMinimize(dew);
        }
        pixa = pixaCreate(2);
        pixaAddPix(pixa, pixc, L_INSERT);
        if (pixd)
            pixaAddPix(pixa, pixd, L_INSERT);
        pixt1 = pixaDisplayTiledAndScaled(pixa, 32, 500, 2, 0, 35, 2);
        if (dew) {
            modelpage = (dew->hasref) ? dew->refpage : dew->pageno;
            snprintf(bufstr, sizeof(bufstr), "Page %d; using %d\n",
                     i, modelpage);
        }
        else
            snprintf(bufstr, sizeof(bufstr), "Page %d; no dewarp\n", i);
        pixt2 = pixAddSingleTextblock(pixt1, bmf, bufstr, 0x0000ff00,
                                      L_ADD_BELOW, 0);
        snprintf(bufstr, sizeof(bufstr), "/tmp/dewarp_pdfout/%05d", i);
        pixWrite(bufstr, pixt2, IFF_JFIF_JPEG);
        pixaDestroy(&pixa);
        pixDestroy(&pixs);
        pixDestroy(&pixt1);
        pixDestroy(&pixt2);
    }
    fprintf(stderr, "\n");

    fprintf(stderr, "Generating pdf of result\n");
    convertFilesToPdf("/tmp/dewarp_pdfout", NULL, 100, 1.0, L_JPEG_ENCODE,
                      0, "Dewarp sequence", pdfout);
    outpath = genPathname(pdfout, NULL);
    fprintf(stderr, "Output written to: %s\n", outpath);
    FREE(outpath);
    bmfDestroy(&bmf);
    return 0;
}
Beispiel #8
0
/*!
 *  pixaDisplayTiledAndScaled()
 *
 *      Input:  pixa
 *              outdepth (output depth: 1, 8 or 32 bpp)
 *              tilewidth (each pix is scaled to this width)
 *              ncols (number of tiles in each row)
 *              background (0 for white, 1 for black; this is the color
 *                 of the spacing between the images)
 *              spacing  (between images, and on outside)
 *              border (width of additional black border on each image;
 *                      use 0 for no border)
 *      Return: pix of tiled images, or null on error
 *
 *  Notes:
 *      (1) This can be used to tile a number of renderings of
 *          an image that are at different scales and depths.
 *      (2) Each image, after scaling and optionally adding the
 *          black border, has width 'tilewidth'.  Thus, the border does
 *          not affect the spacing between the image tiles.  The
 *          maximum allowed border width is tilewidth / 5.
 */
PIX *
pixaDisplayTiledAndScaled(PIXA    *pixa,
                          l_int32  outdepth,
                          l_int32  tilewidth,
                          l_int32  ncols,
                          l_int32  background,
                          l_int32  spacing,
                          l_int32  border)
{
l_int32    x, y, w, h, wd, hd, d;
l_int32    i, n, nrows, maxht, ninrow, irow, bordval;
l_int32   *rowht;
l_float32  scalefact;
PIX       *pix, *pixn, *pixt, *pixb, *pixd;
PIXA      *pixan;

    PROCNAME("pixaDisplayTiledAndScaled");

    if (!pixa)
        return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
    if (outdepth != 1 && outdepth != 8 && outdepth != 32)
        return (PIX *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL);
    if (border < 0 || border > tilewidth / 5)
        border = 0;
    
    if ((n = pixaGetCount(pixa)) == 0)
        return (PIX *)ERROR_PTR("no components", procName, NULL);

        /* Normalize scale and depth for each pix; optionally add border */
    pixan = pixaCreate(n);
    bordval = (outdepth == 1) ? 1 : 0;
    for (i = 0; i < n; i++) {
        if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL)
            continue;

        pixGetDimensions(pix, &w, &h, &d);
        scalefact = (l_float32)(tilewidth - 2 * border) / (l_float32)w;
        if (d == 1 && outdepth > 1 && scalefact < 1.0)
            pixt = pixScaleToGray(pix, scalefact);
        else
            pixt = pixScale(pix, scalefact, scalefact);

        if (outdepth == 1)
            pixn = pixConvertTo1(pixt, 128);
        else if (outdepth == 8)
            pixn = pixConvertTo8(pixt, FALSE);
        else  /* outdepth == 32 */
            pixn = pixConvertTo32(pixt);
        pixDestroy(&pixt);

        if (border)
            pixb = pixAddBorder(pixn, border, bordval);
        else
            pixb = pixClone(pixn);

        pixaAddPix(pixan, pixb, L_INSERT);
        pixDestroy(&pix);
        pixDestroy(&pixn);
    }
    if ((n = pixaGetCount(pixan)) == 0) { /* should not have changed! */
        pixaDestroy(&pixan);
        return (PIX *)ERROR_PTR("no components", procName, NULL);
    }

        /* Determine the size of each row and of pixd */
    wd = tilewidth * ncols + spacing * (ncols + 1);
    nrows = (n + ncols - 1) / ncols;
    if ((rowht = (l_int32 *)CALLOC(nrows, sizeof(l_int32))) == NULL)
        return (PIX *)ERROR_PTR("rowht array not made", procName, NULL);
    maxht = 0;
    ninrow = 0;
    irow = 0;
    for (i = 0; i < n; i++) {
        pix = pixaGetPix(pixan, i, L_CLONE);
        ninrow++;
        pixGetDimensions(pix, &w, &h, NULL);
        maxht = L_MAX(h, maxht);
        if (ninrow == ncols) {
            rowht[irow] = maxht;
            maxht = ninrow = 0;  /* reset */
            irow++;
        }
        pixDestroy(&pix);
    }
    if (ninrow > 0) {   /* last fencepost */
        rowht[irow] = maxht;
        irow++;  /* total number of rows */
    }
    nrows = irow;
    hd = spacing * (nrows + 1);
    for (i = 0; i < nrows; i++)
        hd += rowht[i];

    pixd = pixCreate(wd, hd, outdepth);
    if ((background == 1 && outdepth == 1) ||
        (background == 0 && outdepth != 1))
        pixSetAll(pixd);

        /* Now blit images to pixd */
    x = y = spacing;
    irow = 0;
    for (i = 0; i < n; i++) {
        pix = pixaGetPix(pixan, i, L_CLONE);
        pixGetDimensions(pix, &w, &h, NULL);
        if (i && ((i % ncols) == 0)) {  /* start new row */
            x = spacing;
            y += spacing + rowht[irow];
            irow++;
        }
        pixRasterop(pixd, x, y, w, h, PIX_SRC, pix, 0, 0);
        x += tilewidth + spacing;
        pixDestroy(&pix);
    }

    pixaDestroy(&pixan);
    FREE(rowht);
    return pixd;
}
Beispiel #9
0
/*!
 *  ioFormatTest()
 *
 *      Input:  filename (input file)
 *      Return: 0 if OK; 1 on error or if the test fails
 *
 *  Notes:
 *      (1) This writes and reads a set of output files losslessly
 *          in different formats to /tmp/format/, and tests that the
 *          result before and after is unchanged.
 *      (2) This should work properly on input images of any depth,
 *          with and without colormaps.
 *      (3) All supported formats are tested for bmp, png, tiff and
 *          non-ascii pnm.  Ascii pnm also works (but who'd ever want
 *          to use it?)   We allow 2 bpp bmp, although it's not
 *          supported elsewhere.  And we don't support reading
 *          16 bpp png, although this can be turned on in pngio.c.
 *      (4) This silently skips png or tiff testing if HAVE_LIBPNG
 *          or HAVE_LIBTIFF are 0, respectively.
 */
l_int32
ioFormatTest(const char  *filename)
{
l_int32   d, equal, problems;
PIX      *pixs, *pixc, *pix1, *pix2;
PIXCMAP  *cmap;

    PROCNAME("ioFormatTest");

    if (!filename)
        return ERROR_INT("filename not defined", procName, 1);

    if ((pixs = pixRead(filename)) == NULL)
        return ERROR_INT("pixs not made", procName, 1);

    lept_mkdir("lept");

        /* Note that the reader automatically removes colormaps
         * from 1 bpp BMP images, but not from 8 bpp BMP images.
         * Therefore, if our 8 bpp image initially doesn't have a
         * colormap, we are going to need to remove it from any
         * pix read from a BMP file. */
    pixc = pixClone(pixs);  /* laziness */

        /* This does not test the alpha layer pixels, because most
         * formats don't support it.  Remove any alpha.  */
    if (pixGetSpp(pixc) == 4)
        pixSetSpp(pixc, 3);
    cmap = pixGetColormap(pixc);  /* colormap; can be NULL */
    d = pixGetDepth(pixc);

    problems = FALSE;

        /* ----------------------- BMP -------------------------- */

        /* BMP works for 1, 2, 4, 8 and 32 bpp images.
         * It always writes colormaps for 1 and 8 bpp, so we must
         * remove it after readback if the input image doesn't have
         * a colormap.  Although we can write/read 2 bpp BMP, nobody
         * else can read them! */
    if (d == 1 || d == 8) {
        L_INFO("write/read bmp\n", procName);
        pixWrite(FILE_BMP, pixc, IFF_BMP);
        pix1 = pixRead(FILE_BMP);
        if (!cmap)
            pix2 = pixRemoveColormap(pix1, REMOVE_CMAP_BASED_ON_SRC);
        else
            pix2 = pixClone(pix1);
        pixEqual(pixc, pix2, &equal);
        if (!equal) {
            L_INFO("   **** bad bmp image: d = %d ****\n", procName, d);
            problems = TRUE;
        }
        pixDestroy(&pix1);
        pixDestroy(&pix2);
    }

    if (d == 2 || d == 4 || d == 32) {
        L_INFO("write/read bmp\n", procName);
        pixWrite(FILE_BMP, pixc, IFF_BMP);
        pix1 = pixRead(FILE_BMP);
        pixEqual(pixc, pix1, &equal);
        if (!equal) {
            L_INFO("   **** bad bmp image: d = %d ****\n", procName, d);
            problems = TRUE;
        }
        pixDestroy(&pix1);
    }

        /* ----------------------- PNG -------------------------- */
#if HAVE_LIBPNG
        /* PNG works for all depths, but here, because we strip
         * 16 --> 8 bpp on reading, we don't test png for 16 bpp. */
    if (d != 16) {
        L_INFO("write/read png\n", procName);
        pixWrite(FILE_PNG, pixc, IFF_PNG);
        pix1 = pixRead(FILE_PNG);
        pixEqual(pixc, pix1, &equal);
        if (!equal) {
            L_INFO("   **** bad png image: d = %d ****\n", procName, d);
            problems = TRUE;
        }
        pixDestroy(&pix1);
    }
#endif  /* HAVE_LIBPNG */

        /* ----------------------- TIFF -------------------------- */
#if HAVE_LIBTIFF
        /* TIFF works for 1, 2, 4, 8, 16 and 32 bpp images.
         * Because 8 bpp tiff always writes 256 entry colormaps, the
         * colormap sizes may be different for 8 bpp images with
         * colormap; we are testing if the image content is the same.
         * Likewise, the 2 and 4 bpp tiff images with colormaps
         * have colormap sizes 4 and 16, rsp.  This test should
         * work properly on the content, regardless of the number
         * of color entries in pixc. */

        /* tiff uncompressed works for all pixel depths */
    L_INFO("write/read uncompressed tiff\n", procName);
    pixWrite(FILE_TIFF, pixc, IFF_TIFF);
    pix1 = pixRead(FILE_TIFF);
    pixEqual(pixc, pix1, &equal);
    if (!equal) {
        L_INFO("   **** bad tiff uncompressed image: d = %d ****\n",
               procName, d);
        problems = TRUE;
    }
    pixDestroy(&pix1);

        /* tiff lzw works for all pixel depths */
    L_INFO("write/read lzw compressed tiff\n", procName);
    pixWrite(FILE_LZW, pixc, IFF_TIFF_LZW);
    pix1 = pixRead(FILE_LZW);
    pixEqual(pixc, pix1, &equal);
    if (!equal) {
        L_INFO("   **** bad tiff lzw compressed image: d = %d ****\n",
               procName, d);
        problems = TRUE;
    }
    pixDestroy(&pix1);

        /* tiff adobe deflate (zip) works for all pixel depths */
    L_INFO("write/read zip compressed tiff\n", procName);
    pixWrite(FILE_ZIP, pixc, IFF_TIFF_ZIP);
    pix1 = pixRead(FILE_ZIP);
    pixEqual(pixc, pix1, &equal);
    if (!equal) {
        L_INFO("   **** bad tiff zip compressed image: d = %d ****\n",
               procName, d);
        problems = TRUE;
    }
    pixDestroy(&pix1);

        /* tiff g4, g3, rle and packbits work for 1 bpp */
    if (d == 1) {
        L_INFO("write/read g4 compressed tiff\n", procName);
        pixWrite(FILE_G4, pixc, IFF_TIFF_G4);
        pix1 = pixRead(FILE_G4);
        pixEqual(pixc, pix1, &equal);
        if (!equal) {
            L_INFO("   **** bad tiff g4 image ****\n", procName);
            problems = TRUE;
        }
        pixDestroy(&pix1);

        L_INFO("write/read g3 compressed tiff\n", procName);
        pixWrite(FILE_G3, pixc, IFF_TIFF_G3);
        pix1 = pixRead(FILE_G3);
        pixEqual(pixc, pix1, &equal);
        if (!equal) {
            L_INFO("   **** bad tiff g3 image ****\n", procName);
            problems = TRUE;
        }
        pixDestroy(&pix1);

        L_INFO("write/read rle compressed tiff\n", procName);
        pixWrite(FILE_RLE, pixc, IFF_TIFF_RLE);
        pix1 = pixRead(FILE_RLE);
        pixEqual(pixc, pix1, &equal);
        if (!equal) {
            L_INFO("   **** bad tiff rle image: d = %d ****\n", procName, d);
            problems = TRUE;
        }
        pixDestroy(&pix1);

        L_INFO("write/read packbits compressed tiff\n", procName);
        pixWrite(FILE_PB, pixc, IFF_TIFF_PACKBITS);
        pix1 = pixRead(FILE_PB);
        pixEqual(pixc, pix1, &equal);
        if (!equal) {
            L_INFO("   **** bad tiff packbits image: d = %d ****\n",
                   procName, d);
            problems = TRUE;
        }
        pixDestroy(&pix1);
    }
#endif  /* HAVE_LIBTIFF */

        /* ----------------------- PNM -------------------------- */

        /* pnm works for 1, 2, 4, 8, 16 and 32 bpp.
         * pnm doesn't have colormaps, so when we write colormapped
         * pix out as pnm, the colormap is removed.  Thus for the test,
         * we must remove the colormap from pixc before testing.  */
    L_INFO("write/read pnm\n", procName);
    pixWrite(FILE_PNM, pixc, IFF_PNM);
    pix1 = pixRead(FILE_PNM);
    if (cmap)
        pix2 = pixRemoveColormap(pixc, REMOVE_CMAP_BASED_ON_SRC);
    else
        pix2 = pixClone(pixc);
    pixEqual(pix1, pix2, &equal);
    if (!equal) {
        L_INFO("   **** bad pnm image: d = %d ****\n", procName, d);
        problems = TRUE;
    }
    pixDestroy(&pix1);
    pixDestroy(&pix2);

    if (problems == FALSE)
        L_INFO("All formats read and written OK!\n", procName);

    pixDestroy(&pixc);
    pixDestroy(&pixs);
    return problems;
}
Beispiel #10
0
/*!
 * \brief   jbWordsInTextlines()
 *
 * \param[in]    dirin directory of input pages
 * \param[in]    reduction 1 for full res; 2 for half-res
 * \param[in]    maxwidth of word mask components, to be kept
 * \param[in]    maxheight of word mask components, to be kept
 * \param[in]    thresh on correlation; 0.80 is reasonable
 * \param[in]    weight for handling thick text; 0.6 is reasonable
 * \param[out]   pnatl numa with textline index for each component
 * \param[in]    firstpage 0-based
 * \param[in]    npages use 0 for all pages in dirin
 * \return  classer for the set of pages
 *
 * <pre>
 * Notes:
 *      (1) This is a high-level function.  See prog/jbwords for example
 *          of usage.
 *      (2) Typically, use input of 75 - 150 ppi for finding words.
 * </pre>
 */
JBCLASSER *
jbWordsInTextlines(const char  *dirin,
                   l_int32      reduction,
                   l_int32      maxwidth,
                   l_int32      maxheight,
                   l_float32    thresh,
                   l_float32    weight,
                   NUMA       **pnatl,
                   l_int32      firstpage,
                   l_int32      npages)
{
char       *fname;
l_int32     nfiles, i, w, h;
BOXA       *boxa;
JBCLASSER  *classer;
NUMA       *nai, *natl;
PIX        *pix1, *pix2;
PIXA       *pixa;
SARRAY     *safiles;

    PROCNAME("jbWordsInTextlines");

    if (!pnatl)
        return (JBCLASSER *)ERROR_PTR("&natl not defined", procName, NULL);
    *pnatl = NULL;
    if (!dirin)
        return (JBCLASSER *)ERROR_PTR("dirin not defined", procName, NULL);
    if (reduction != 1 && reduction != 2)
        return (JBCLASSER *)ERROR_PTR("reduction not in {1,2}", procName, NULL);

    safiles = getSortedPathnamesInDirectory(dirin, NULL, firstpage, npages);
    nfiles = sarrayGetCount(safiles);

        /* Classify components */
    classer = jbCorrelationInit(JB_WORDS, maxwidth, maxheight, thresh, weight);
    classer->safiles = sarrayCopy(safiles);
    natl = numaCreate(0);
    *pnatl = natl;
    for (i = 0; i < nfiles; i++) {
        fname = sarrayGetString(safiles, i, L_NOCOPY);
        if ((pix1 = pixRead(fname)) == NULL) {
            L_WARNING("image file %d not read\n", procName, i);
            continue;
        }
        if (reduction == 1)
            pix2 = pixClone(pix1);
        else  /* reduction == 2 */
            pix2 = pixReduceRankBinaryCascade(pix1, 1, 0, 0, 0);
        pixGetWordsInTextlines(pix2, JB_WORDS_MIN_WIDTH,
                               JB_WORDS_MIN_HEIGHT, maxwidth, maxheight,
                               &boxa, &pixa, &nai);
        pixGetDimensions(pix2, &w, &h, NULL);
        classer->w = w;
        classer->h = h;
        jbAddPageComponents(classer, pix2, boxa, pixa);
        numaJoin(natl, nai, 0, -1);
        pixDestroy(&pix1);
        pixDestroy(&pix2);
        numaDestroy(&nai);
        boxaDestroy(&boxa);
        pixaDestroy(&pixa);
    }

    sarrayDestroy(&safiles);
    return classer;
}
Beispiel #11
0
/*!
 *  pixaDisplayTiledInRows()
 *
 *      Input:  pixa
 *              outdepth (output depth: 1, 8 or 32 bpp)
 *              maxwidth (of output image)
 *              scalefactor (applied to every pix; use 1.0 for no scaling)
 *              background (0 for white, 1 for black; this is the color
 *                 of the spacing between the images)
 *              spacing  (between images, and on outside)
 *              border (width of black border added to each image;
 *                      use 0 for no border)
 *      Return: pixd (of tiled images), or null on error
 *
 *  Notes:
 *      (1) This saves a pixa to a single image file of width not to
 *          exceed maxwidth, with background color either white or black,
 *          and with each row tiled such that the top of each pix is
 *          aligned and separated by 'spacing' from the next one.
 *          A black border can be added to each pix.
 *      (2) All pix are converted to outdepth; existing colormaps are removed.
 *      (3) This does a reasonably spacewise-efficient job of laying
 *          out the individual pix images into a tiled composite.
 */
PIX *
pixaDisplayTiledInRows(PIXA      *pixa,
                       l_int32    outdepth,
                       l_int32    maxwidth,
                       l_float32  scalefactor,
                       l_int32    background,
                       l_int32    spacing,
                       l_int32    border)
{
l_int32  h;  /* cumulative height over all the rows */
l_int32  w;  /* cumulative height in the current row */
l_int32  bordval, wtry, wt, ht;
l_int32  irow;  /* index of current pix in current row */
l_int32  wmaxrow;  /* width of the largest row */
l_int32  maxh;  /* max height in row */
l_int32  i, j, index, n, x, y, nrows, ninrow;
NUMA    *nainrow;  /* number of pix in the row */
NUMA    *namaxh;  /* height of max pix in the row */
PIX     *pix, *pixn, *pixt, *pixd;
PIXA    *pixan;

    PROCNAME("pixaDisplayTiledInRows");

    if (!pixa)
        return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
    if (outdepth != 1 && outdepth != 8 && outdepth != 32)
        return (PIX *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL);
    if (border < 0)
        border = 0;
    if (scalefactor <= 0.0) scalefactor = 1.0;
    
    if ((n = pixaGetCount(pixa)) == 0)
        return (PIX *)ERROR_PTR("no components", procName, NULL);

        /* Normalize depths, scale, remove colormaps; optionally add border */
    pixan = pixaCreate(n);
    bordval = (outdepth == 1) ? 1 : 0;
    for (i = 0; i < n; i++) {
        if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL)
            continue;

        if (outdepth == 1)
            pixn = pixConvertTo1(pix, 128);
        else if (outdepth == 8)
            pixn = pixConvertTo8(pix, FALSE);
        else  /* outdepth == 32 */
            pixn = pixConvertTo32(pix);
        pixDestroy(&pix);

        if (scalefactor != 1.0)
            pixt = pixScale(pixn, scalefactor, scalefactor);
        else
            pixt = pixClone(pixn);
        if (border)
            pixd = pixAddBorder(pixt, border, bordval);
        else
            pixd = pixClone(pixt);
        pixDestroy(&pixn);
        pixDestroy(&pixt);

        pixaAddPix(pixan, pixd, L_INSERT);
    }
    if (pixaGetCount(pixan) != n) {
        n = pixaGetCount(pixan);
        L_WARNING_INT("only got %d components", procName, n);
        if (n == 0) {
            pixaDestroy(&pixan);
            return (PIX *)ERROR_PTR("no components", procName, NULL);
        }
    }

        /* Compute parameters for layout */
    nainrow = numaCreate(0);
    namaxh = numaCreate(0);
    wmaxrow = 0;
    w = h = spacing;
    maxh = 0;  /* max height in row */
    for (i = 0, irow = 0; i < n; i++, irow++) {
        pixaGetPixDimensions(pixan, i, &wt, &ht, NULL);
        wtry = w + wt + spacing;
        if (wtry > maxwidth) {  /* end the current row and start next one */
            numaAddNumber(nainrow, irow); 
            numaAddNumber(namaxh, maxh); 
            wmaxrow = L_MAX(wmaxrow, w);
            h += maxh + spacing;
            irow = 0;
            w = wt + 2 * spacing;
            maxh = ht;
        } else {
            w = wtry;
            maxh = L_MAX(maxh, ht);
        }
    }

        /* Enter the parameters for the last row */
    numaAddNumber(nainrow, irow); 
    numaAddNumber(namaxh, maxh); 
    wmaxrow = L_MAX(wmaxrow, w);
    h += maxh + spacing;
            
    if ((pixd = pixCreate(wmaxrow, h, outdepth)) == NULL) {
        numaDestroy(&nainrow);
        numaDestroy(&namaxh);
        pixaDestroy(&pixan);
	return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    }

        /* Reset the background color if necessary */
    if ((background == 1 && outdepth == 1) ||
        (background == 0 && outdepth != 1))
        pixSetAll(pixd);

        /* Blit the images to the dest */
    nrows = numaGetCount(nainrow);
    y = spacing;
    for (i = 0, index = 0; i < nrows; i++) {  /* over rows */
        numaGetIValue(nainrow, i, &ninrow);
        numaGetIValue(namaxh, i, &maxh);
        x = spacing;
        for (j = 0; j < ninrow; j++, index++) {   /* over pix in row */
            pix = pixaGetPix(pixan, index, L_CLONE);
            pixGetDimensions(pix, &wt, &ht, NULL);
            pixRasterop(pixd, x, y, wt, ht, PIX_SRC, pix, 0, 0);
            pixDestroy(&pix);
            x += wt + spacing;
        }
        y += maxh + spacing;
    }

    numaDestroy(&nainrow);
    numaDestroy(&namaxh);
    pixaDestroy(&pixan);
    return pixd;
}
Beispiel #12
0
void
RotateTest(PIX          *pixs,
           l_int32       reduction,
           L_REGPARAMS  *rp)
{
l_int32   w, h, d, outformat;
PIX      *pixt1, *pixt2, *pixt3, *pixd;
PIXA     *pixa;

    pixGetDimensions(pixs, &w, &h, &d);
    outformat = (d == 8 || d == 32) ? IFF_JFIF_JPEG : IFF_PNG;

    pixa = pixaCreate(0);
    pixt1 = pixRotate(pixs, ANGLE1, L_ROTATE_SHEAR, L_BRING_IN_WHITE, w, h);
    pixSaveTiled(pixt1, pixa, reduction, 1, 20, 32);
    pixt2 = pixRotate(pixs, ANGLE1, L_ROTATE_SHEAR, L_BRING_IN_BLACK, w, h);
    pixSaveTiled(pixt2, pixa, reduction, 0, 20, 0);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixt1 = pixRotate(pixs, ANGLE1, L_ROTATE_SHEAR, L_BRING_IN_WHITE, 0, 0);
    pixSaveTiled(pixt1, pixa, reduction, 1, 20, 0);
    pixt2 = pixRotate(pixs, ANGLE1, L_ROTATE_SHEAR, L_BRING_IN_BLACK, 0, 0);
    pixSaveTiled(pixt2, pixa, reduction, 0, 20, 0);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixt1 = pixRotate(pixs, ANGLE2, L_ROTATE_SHEAR, L_BRING_IN_WHITE, w, h);
    pixSaveTiled(pixt1, pixa, reduction, 1, 20, 0);
    pixt2 = pixRotate(pixs, ANGLE2, L_ROTATE_SHEAR, L_BRING_IN_BLACK, w, h);
    pixSaveTiled(pixt2, pixa, reduction, 0, 20, 0);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixt1 = pixRotate(pixs, ANGLE2, L_ROTATE_SHEAR, L_BRING_IN_WHITE, 0, 0);
    pixSaveTiled(pixt1, pixa, reduction, 1, 20, 0);
    pixt2 = pixRotate(pixs, ANGLE2, L_ROTATE_SHEAR, L_BRING_IN_BLACK, 0, 0);
    pixSaveTiled(pixt2, pixa, reduction, 0, 20, 0);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixd = pixaDisplay(pixa, 0, 0);
    regTestWritePixAndCheck(rp, pixd, outformat);
    pixDisplayWithTitle(pixd, 100, 100, NULL, rp->display);
    pixDestroy(&pixd);
    pixaDestroy(&pixa);

    pixa = pixaCreate(0);
    pixt1 = pixRotate(pixs, ANGLE2, L_ROTATE_SAMPLING, L_BRING_IN_WHITE, w, h);
    pixSaveTiled(pixt1, pixa, reduction, 1, 20, 32);
    pixt2 = pixRotate(pixs, ANGLE2, L_ROTATE_SAMPLING, L_BRING_IN_BLACK, w, h);
    pixSaveTiled(pixt2, pixa, reduction, 0, 20, 0);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    pixt1 = pixRotate(pixs, ANGLE2, L_ROTATE_SAMPLING, L_BRING_IN_WHITE, 0, 0);
    pixSaveTiled(pixt1, pixa, reduction, 1, 20, 0);
    pixt2 = pixRotate(pixs, ANGLE2, L_ROTATE_SAMPLING, L_BRING_IN_BLACK, 0, 0);
    pixSaveTiled(pixt2, pixa, reduction, 0, 20, 0);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);

    if (pixGetDepth(pixs) == 1)
        pixt1 = pixScaleToGray2(pixs);
    else
        pixt1 = pixClone(pixs);
    pixt2 = pixRotate(pixt1, ANGLE2, L_ROTATE_AREA_MAP, L_BRING_IN_WHITE, w, h);
    pixSaveTiled(pixt2, pixa, reduction, 1, 20, 0);
    pixt3 = pixRotate(pixt1, ANGLE2, L_ROTATE_AREA_MAP, L_BRING_IN_BLACK, w, h);
    pixSaveTiled(pixt3, pixa, reduction, 0, 20, 0);
    pixDestroy(&pixt2);
    pixDestroy(&pixt3);
    pixt2 = pixRotate(pixt1, ANGLE2, L_ROTATE_AREA_MAP, L_BRING_IN_WHITE, 0, 0);
    pixSaveTiled(pixt2, pixa, reduction, 1, 20, 0);
    pixt3 = pixRotate(pixt1, ANGLE2, L_ROTATE_AREA_MAP, L_BRING_IN_BLACK, 0, 0);
    pixSaveTiled(pixt3, pixa, reduction, 0, 20, 0);
    pixDestroy(&pixt2);
    pixDestroy(&pixt3);
    pixDestroy(&pixt1);
    pixd = pixaDisplay(pixa, 0, 0);
    regTestWritePixAndCheck(rp, pixd, outformat);
    pixDisplayWithTitle(pixd, 100, 100, NULL, rp->display);
    pixDestroy(&pixd);
    pixaDestroy(&pixa);

    return;
}
Beispiel #13
0
/*
 *  pixWriteSegmentedPageToPS()
 *
 *      Input:  pixs (all depths; colormap ok)
 *              pixm (<optional> 1 bpp segmentation mask over image region)
 *              textscale (scale of text output relative to pixs)
 *              imagescale (scale of image output relative to pixs)
 *              threshold (threshold for binarization; typ. 190)
 *              pageno (page number in set; use 1 for new output file)
 *              fileout (output ps file)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) This generates the PS string for a mixed text/image page,
 *          and adds it to an existing file if @pageno > 1.
 *          The PS output is determined by fitting the result to
 *          a letter-size (8.5 x 11 inch) page.
 *      (2) The two images (pixs and pixm) are at the same resolution
 *          (typically 300 ppi).  They are used to generate two compressed
 *          images, pixb and pixc, that are put directly into the output
 *          PS file.
 *      (3) pixb is the text component.  In the PostScript world, we think of
 *          it as a mask through which we paint black.  It is produced by
 *          scaling pixs by @textscale, and thresholding to 1 bpp.
 *      (4) pixc is the image component, which is that part of pixs under
 *          the mask pixm.  It is scaled from pixs by @imagescale.
 *      (5) Typical values are textscale = 2.0 and imagescale = 0.5.
 *      (6) If pixm == NULL, the page has only text.  If it is all black,
 *          the page is all image and has no text.
 *      (7) This can be used to write a multi-page PS file, by using
 *          sequential page numbers with the same output file.  It can
 *          also be used to write separate PS files for each page,
 *          by using different output files with @pageno = 0 or 1.
 */
l_int32
pixWriteSegmentedPageToPS(PIX         *pixs,
                          PIX         *pixm,
                          l_float32    textscale,
                          l_float32    imagescale,
                          l_int32      threshold,
                          l_int32      pageno,
                          const char  *fileout)
{
l_int32    alltext, notext, d, ret;
l_uint32   val;
l_float32  scaleratio;
PIX       *pixmi, *pixmis, *pixt, *pixg, *pixsc, *pixb, *pixc;

    PROCNAME("pixWriteSegmentedPageToPS");

    if (!pixs)
        return ERROR_INT("pixs not defined", procName, 1);
    if (!fileout)
        return ERROR_INT("fileout not defined", procName, 1);
    if (imagescale <= 0.0 || textscale <= 0.0)
        return ERROR_INT("relative scales must be > 0.0", procName, 1);

        /* Analyze the page.  Determine the ratio by which the
         * binary text mask is scaled relative to the image part.
         * If there is no image region (alltext == TRUE), the
         * text mask will be rendered directly to fit the page,
         * and scaleratio = 1.0.  */
    alltext = TRUE;
    notext = FALSE;
    scaleratio = 1.0;
    if (pixm) {
        pixZero(pixm, &alltext);  /* pixm empty: all text */
        if (alltext)
            pixm = NULL;  /* treat it as not existing here */
        else {
            pixmi = pixInvert(NULL, pixm);
            pixZero(pixmi, &notext);  /* pixm full; no text */
            pixDestroy(&pixmi);
            scaleratio = textscale / imagescale;
        }
    }

    if (pixGetDepth(pixs) == 1) {  /* render tiff g4 */
        pixb = pixClone(pixs);
        pixc = NULL;
    }
    else {
        pixt = pixConvertTo8Or32(pixs, 0, 0);  /* this can be a clone of pixs */

            /* Get the binary text mask.  Note that pixg cannot be a
             * clone of pixs, because it may be altered by pixSetMasked(). */
        pixb = NULL;
        if (notext == FALSE) {
            d = pixGetDepth(pixt);
            if (d == 8)
                pixg = pixCopy(NULL, pixt);
            else  /* d == 32 */
                pixg = pixConvertRGBToLuminance(pixt);
            if (pixm)  /* clear out the image parts */
                pixSetMasked(pixg, pixm, 255);
            if (textscale == 1.0)
                pixsc = pixClone(pixg);
            else if (textscale >= 0.7)
                pixsc = pixScaleGrayLI(pixg, textscale, textscale);
            else
                pixsc = pixScaleAreaMap(pixg, textscale, textscale);
            pixb = pixThresholdToBinary(pixsc, threshold);
            pixDestroy(&pixg);
            pixDestroy(&pixsc);
        }

            /* Get the scaled image region */
        pixc = NULL;
        if (pixm) {
            if (imagescale == 1.0)
                pixsc = pixClone(pixt);  /* can possibly be a clone of pixs */
            else
                pixsc = pixScale(pixt, imagescale, imagescale);

                /* If pixm is not full, clear the pixels in pixsc
                 * corresponding to bg in pixm, where there can be text
                 * that is written through the mask pixb.  Note that
                 * we could skip this and use pixsc directly in
                 * pixWriteMixedToPS(); however, clearing these
                 * non-image regions to a white background will reduce
                 * the size of pixc (relative to pixsc), and hence
                 * reduce the size of the PS file that is generated.
                 * Use a copy so that we don't accidentally alter pixs.  */
            if (notext == FALSE) {
                pixmis = pixScale(pixm, imagescale, imagescale);
                pixmi = pixInvert(NULL, pixmis);
                val = (d == 8) ? 0xff : 0xffffff00;
                pixc = pixCopy(NULL, pixsc);
                pixSetMasked(pixc, pixmi, val);  /* clear non-image part */
                pixDestroy(&pixmis);
                pixDestroy(&pixmi);
            }
            else
                pixc = pixClone(pixsc);
            pixDestroy(&pixsc);
        }
        pixDestroy(&pixt);
    }

    ret = pixWriteMixedToPS(pixb, pixc, scaleratio, pageno, fileout);
    pixDestroy(&pixb);
    pixDestroy(&pixc);
    return ret;
}
Beispiel #14
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);
}
/*!
 * \brief   pixGetWordsInTextlines()
 *
 * \param[in]    pixs 1 bpp, typ. 300 ppi
 * \param[in]    reduction 1 for input res; 2 for 2x reduction of input res
 * \param[in]    minwidth, minheight of saved components; smaller are discarded
 * \param[in]    maxwidth, maxheight of saved components; larger are discarded
 * \param[out]   pboxad word boxes sorted in textline line order
 * \param[out]   ppixad word images sorted in textline line order
 * \param[out]   pnai index of textline for each word
 * \return  0 if OK, 1 on error
 *
 * <pre>
 * Notes:
 *      (1) The input should be at a resolution of about 300 ppi.
 *          The word masks and word images can be computed at either
 *          150 ppi or 300 ppi.  For the former, set reduction = 2.
 *      (2) The four size constraints on saved components are all
 *          scaled by %reduction.
 *      (3) The result are word images (and their b.b.), extracted in
 *          textline order, at either full res or 2x reduction,
 *          and with a numa giving the textline index for each word.
 *      (4) The pixa and boxa interfaces should make this type of
 *          application simple to put together.  The steps are:
 *           ~ optionally reduce by 2x
 *           ~ generate first estimate of word masks
 *           ~ get b.b. of these, and remove the small and big ones
 *           ~ extract pixa of the word images, using the b.b.
 *           ~ sort actual word images in textline order (2d)
 *           ~ flatten them to a pixa (1d), saving the textline index
 *             for each pix
 *      (5) In an actual application, it may be desirable to pre-filter
 *          the input image to remove large components, to extract
 *          single columns of text, and to deskew them.  For example,
 *          to remove both large components and small noisy components
 *          that can interfere with the statistics used to estimate
 *          parameters for segmenting by words, but still retain text lines,
 *          the following image preprocessing can be done:
 *                Pix *pixt = pixMorphSequence(pixs, "c40.1", 0);
 *                Pix *pixf = pixSelectBySize(pixt, 0, 60, 8,
 *                                     L_SELECT_HEIGHT, L_SELECT_IF_LT, NULL);
 *                pixAnd(pixf, pixf, pixs);  // the filtered image
 *          The closing turns text lines into long blobs, but does not
 *          significantly increase their height.  But if there are many
 *          small connected components in a dense texture, this is likely
 *          to generate tall components that will be eliminated in pixf.
 * </pre>
 */
l_int32
pixGetWordsInTextlines(PIX     *pixs,
                       l_int32  reduction,
                       l_int32  minwidth,
                       l_int32  minheight,
                       l_int32  maxwidth,
                       l_int32  maxheight,
                       BOXA   **pboxad,
                       PIXA   **ppixad,
                       NUMA   **pnai)
{
l_int32  maxdil;
BOXA    *boxa1, *boxad;
BOXAA   *baa;
NUMA    *nai;
NUMAA   *naa;
PIXA    *pixa1, *pixad;
PIX     *pix1;
PIXAA   *paa;

    PROCNAME("pixGetWordsInTextlines");

    if (!pboxad || !ppixad || !pnai)
        return ERROR_INT("&boxad, &pixad, &nai not all defined", procName, 1);
    *pboxad = NULL;
    *ppixad = NULL;
    *pnai = NULL;
    if (!pixs)
        return ERROR_INT("pixs not defined", procName, 1);
    if (reduction != 1 && reduction != 2)
        return ERROR_INT("reduction not in {1,2}", procName, 1);

    if (reduction == 1) {
        pix1 = pixClone(pixs);
        maxdil = 18;
    } else {  /* reduction == 2 */
        pix1 = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0);
        maxdil = 9;
    }

        /* Get the bounding boxes of the words from the word mask. */
    pixWordBoxesByDilation(pix1, maxdil, minwidth, minheight,
                           maxwidth, maxheight, &boxa1, NULL);

        /* Generate a pixa of the word images */
    pixa1 = pixaCreateFromBoxa(pix1, boxa1, NULL);  /* mask over each word */

        /* Sort the bounding boxes of these words by line.  We use the
         * index mapping to allow identical sorting of the pixa. */
    baa = boxaSort2d(boxa1, &naa, -1, -1, 4);
    paa = pixaSort2dByIndex(pixa1, naa, L_CLONE);

        /* Flatten the word paa */
    pixad = pixaaFlattenToPixa(paa, &nai, L_CLONE);
    boxad = pixaGetBoxa(pixad, L_COPY);

    *pnai = nai;
    *pboxad = boxad;
    *ppixad = pixad;

    pixDestroy(&pix1);
    pixaDestroy(&pixa1);
    boxaDestroy(&boxa1);
    boxaaDestroy(&baa);
    pixaaDestroy(&paa);
    numaaDestroy(&naa);
    return 0;
}
Beispiel #16
0
l_int32 main(int    argc,
             char **argv)
{
char         buf[256], dirname[256];
char        *dirin, *pattern, *subdirout, *fname, *tail, *basename;
l_int32      thresh, i, n;
l_float32    scalefactor;
PIX         *pix1, *pix2, *pix3, *pix4;
SARRAY      *sa;
static char  mainName[] = "binarizefiles.c";

    if (argc != 6) {
        fprintf(stderr,
            "Syntax: binarizefiles dirin pattern thresh scalefact dirout\n"
            "      dirin: input directory for image files\n"
            "      pattern: use 'allfiles' to convert all files\n"
            "               in the directory\n"
            "      thresh: 0 for adaptive; > 0 for global thresh (e.g., 128)\n"
            "      scalefactor: in (0.0 ... 4.0]; use 1.0 to prevent scaling\n"
            "      subdirout: subdirectory of /tmp for output files\n");
        return 1;
    }

    dirin = argv[1];
    pattern = argv[2];
    thresh = atoi(argv[3]);
    scalefactor = atof(argv[4]);
    subdirout = argv[5];
    if (!strcmp(pattern, "allfiles"))
              pattern = NULL;
    if (scalefactor <= 0.0 || scalefactor > 4.0) {
        L_WARNING("invalid scalefactor: setting to 1.0\n", mainName);
        scalefactor = 1.0;
    }

        /* Get the input filenames */
    sa = getSortedPathnamesInDirectory(dirin, pattern, 0, 0);
    sarrayWriteStream(stderr, sa);
    n = sarrayGetCount(sa);

        /* Write the output files */
    makeTempDirname(dirname, 256, subdirout);
    fprintf(stderr, "dirname: %s\n", dirname);
    lept_mkdir(subdirout);
    for (i = 0; i < n; i++) {
        fname = sarrayGetString(sa, i, L_NOCOPY);
        if ((pix1 = pixRead(fname)) == NULL) {
            L_ERROR("file %s not read as image", mainName, fname);
            continue;
        }
        splitPathAtDirectory(fname, NULL, &tail);
        splitPathAtExtension(tail, &basename, NULL);
        snprintf(buf, sizeof(buf), "%s/%s.tif", dirname, basename);
        FREE(tail);
        FREE(basename);
        fprintf(stderr, "fileout: %s\n", buf);
        if (scalefactor != 1.0)
            pix2 = pixScale(pix1, scalefactor, scalefactor);
        else
            pix2 = pixClone(pix1);
        if (thresh == 0) {
            pix4 = pixConvertTo8(pix2, 0);
            pix3 = pixAdaptThresholdToBinary(pix4, NULL, 1.0);
            pixDestroy(&pix4);
        } else {
            pix3 = pixConvertTo1(pix2, thresh);
        }
        pixWrite(buf, pix3, IFF_TIFF_G4);
        pixDestroy(&pix1);
        pixDestroy(&pix2);
        pixDestroy(&pix3);
    }
    sarrayDestroy(&sa);
    return 0;
}
/*!
 * \brief   pixDisplayWriteFormat()
 *
 * \param[in]    pix 1, 2, 4, 8, 16, 32 bpp
 * \param[in]    reduction -1 to erase and reset; 0 to disable;
 *                         otherwise this is a reduction factor
 * \param[in]    format IFF_DEFAULT or IFF_PNG
 * \return  0 if OK; 1 on error
 *
 * <pre>
 * Notes:
 *      (1) This writes files with pathnames "/tmp/lept/display/file.*"
 *          if reduction \> 0.  These can be collected into a pdf using
 *          pixDisplayMultiple();
 *      (2) Before writing a set of files, call
 *              pixDisplayWrite(NULL, -1);
 *          This erases any previously written files in that directory.
 *      (3) If reduction \> 1 and depth == 1, this does a scale-to-gray
 *          reduction.
 *      (4) This function uses a static internal variable to number
 *          output files written by a single process.  Behavior
 *          with a shared library may be unpredictable.
 *      (5) Output file format is as follows:
 *            format == IFF_DEFAULT:
 *                png if d \< 8 or d == 16 or if the output pix
 *                has a colormap.   Otherwise, output is jpg.
 *            format == IFF_PNG:
 *                png (lossless) on all images.
 *      (6) For 16 bpp, the choice of full dynamic range with log scale
 *          is the best for displaying these images.  Alternative outputs are
 *             pix8 = pixMaxDynamicRange(pixt, L_LINEAR_SCALE);
 *             pix8 = pixConvert16To8(pixt, 0);  // low order byte
 *             pix8 = pixConvert16To8(pixt, 1);  // high order byte
 * </pre>
 */
l_int32
pixDisplayWriteFormat(PIX     *pixs,
                      l_int32  reduction,
                      l_int32  format)
{
char            buf[L_BUF_SIZE];
char           *fname;
l_float32       scale;
PIX            *pix1, *pix2;
static l_int32  index = 0;  /* caution: not .so or thread safe */

    PROCNAME("pixDisplayWriteFormat");

    if (reduction == 0) return 0;

    if (reduction < 0) {  /* initialize */
        lept_rmdir("lept/display");
        index = 0;
        return 0;
    }

    if (!pixs)
        return ERROR_INT("pixs not defined", procName, 1);
    if (format != IFF_DEFAULT && format != IFF_PNG) {
        L_INFO("invalid format; using default\n", procName);
        format = IFF_DEFAULT;
    }

    if (index == 0)
        lept_mkdir("lept/display");
    index++;

    if (reduction == 1) {
        pix1 = pixClone(pixs);
    } else {
        scale = 1. / (l_float32)reduction;
        if (pixGetDepth(pixs) == 1)
            pix1 = pixScaleToGray(pixs, scale);
        else
            pix1 = pixScale(pixs, scale, scale);
    }

    if (pixGetDepth(pix1) == 16) {
        pix2 = pixMaxDynamicRange(pix1, L_LOG_SCALE);
        snprintf(buf, L_BUF_SIZE, "file.%03d.png", index);
        fname = genPathname("/tmp/lept/display", buf);
        pixWrite(fname, pix2, IFF_PNG);
        pixDestroy(&pix2);
    } else if (pixGetDepth(pix1) < 8 || pixGetColormap(pix1) ||
               format == IFF_PNG) {
        snprintf(buf, L_BUF_SIZE, "file.%03d.png", index);
        fname = genPathname("/tmp/lept/display", buf);
        pixWrite(fname, pix1, IFF_PNG);
    } else {
        snprintf(buf, L_BUF_SIZE, "file.%03d.jpg", index);
        fname = genPathname("/tmp/lept/display", buf);
        pixWrite(fname, pix1, format);
    }
    LEPT_FREE(fname);
    pixDestroy(&pix1);

    return 0;
}
// On setting the input image, a clone of it is owned by this class.
void ShiroRekhaSplitter::set_orig_pix(Pix* pix) {
  if (orig_pix_) {
    pixDestroy(&orig_pix_);
  }
  orig_pix_ = pixClone(pix);
}
/*!
 * \brief   pixDisplayWithTitle()
 *
 * \param[in]    pix 1, 2, 4, 8, 16, 32 bpp
 * \param[in]    x, y  location of display frame
 * \param[in]    title [optional] on frame; can be NULL;
 * \param[in]    dispflag 1 to write, else disabled
 * \return  0 if OK; 1 on error
 *
 * <pre>
 * Notes:
 *      (1) See notes for pixDisplay().
 *      (2) This displays the image if dispflag == 1.
 * </pre>
 */
l_int32
pixDisplayWithTitle(PIX         *pixs,
                    l_int32      x,
                    l_int32      y,
                    const char  *title,
                    l_int32      dispflag)
{
char           *tempname;
char            buffer[L_BUF_SIZE];
static l_int32  index = 0;  /* caution: not .so or thread safe */
l_int32         w, h, d, spp, maxheight, opaque, threeviews, ignore;
l_float32       ratw, rath, ratmin;
PIX            *pix0, *pix1, *pix2;
PIXCMAP        *cmap;
#ifndef _WIN32
l_int32         wt, ht;
#else
char           *pathname;
char            fullpath[_MAX_PATH];
#endif  /* _WIN32 */

    PROCNAME("pixDisplayWithTitle");

    if (dispflag != 1) return 0;
    if (!pixs)
        return ERROR_INT("pixs not defined", procName, 1);
    if (var_DISPLAY_PROG != L_DISPLAY_WITH_XZGV &&
        var_DISPLAY_PROG != L_DISPLAY_WITH_XLI &&
        var_DISPLAY_PROG != L_DISPLAY_WITH_XV &&
        var_DISPLAY_PROG != L_DISPLAY_WITH_IV &&
        var_DISPLAY_PROG != L_DISPLAY_WITH_OPEN) {
        return ERROR_INT("no program chosen for display", procName, 1);
    }

        /* Display with three views if either spp = 4 or if colormapped
         * and the alpha component is not fully opaque */
    opaque = TRUE;
    if ((cmap = pixGetColormap(pixs)) != NULL)
        pixcmapIsOpaque(cmap, &opaque);
    spp = pixGetSpp(pixs);
    threeviews = (spp == 4 || !opaque) ? TRUE : FALSE;

        /* If colormapped and not opaque, remove the colormap to RGBA */
    if (!opaque)
        pix0 = pixRemoveColormap(pixs, REMOVE_CMAP_WITH_ALPHA);
    else
        pix0 = pixClone(pixs);

        /* Scale if necessary; this will also remove a colormap */
    pixGetDimensions(pix0, &w, &h, &d);
    maxheight = (threeviews) ? MAX_DISPLAY_HEIGHT / 3 : MAX_DISPLAY_HEIGHT;
    if (w <= MAX_DISPLAY_WIDTH && h <= maxheight) {
        if (d == 16)  /* take MSB */
            pix1 = pixConvert16To8(pix0, 1);
        else
            pix1 = pixClone(pix0);
    } else {
        ratw = (l_float32)MAX_DISPLAY_WIDTH / (l_float32)w;
        rath = (l_float32)maxheight / (l_float32)h;
        ratmin = L_MIN(ratw, rath);
        if (ratmin < 0.125 && d == 1)
            pix1 = pixScaleToGray8(pix0);
        else if (ratmin < 0.25 && d == 1)
            pix1 = pixScaleToGray4(pix0);
        else if (ratmin < 0.33 && d == 1)
            pix1 = pixScaleToGray3(pix0);
        else if (ratmin < 0.5 && d == 1)
            pix1 = pixScaleToGray2(pix0);
        else
            pix1 = pixScale(pix0, ratmin, ratmin);
    }
    pixDestroy(&pix0);
    if (!pix1)
        return ERROR_INT("pix1 not made", procName, 1);

        /* Generate the three views if required */
    if (threeviews)
        pix2 = pixDisplayLayersRGBA(pix1, 0xffffff00, 0);
    else
        pix2 = pixClone(pix1);

    if (index == 0) {
        lept_rmdir("lept/disp");
        lept_mkdir("lept/disp");
    }

    index++;
    if (pixGetDepth(pix2) < 8 ||
        (w < MAX_SIZE_FOR_PNG && h < MAX_SIZE_FOR_PNG)) {
        snprintf(buffer, L_BUF_SIZE, "/tmp/lept/disp/write.%03d.png", index);
        pixWrite(buffer, pix2, IFF_PNG);
    } else {
        snprintf(buffer, L_BUF_SIZE, "/tmp/lept/disp/write.%03d.jpg", index);
        pixWrite(buffer, pix2, IFF_JFIF_JPEG);
    }
    tempname = genPathname(buffer, NULL);

#ifndef _WIN32

        /* Unix */
    if (var_DISPLAY_PROG == L_DISPLAY_WITH_XZGV) {
            /* no way to display title */
        pixGetDimensions(pix2, &wt, &ht, NULL);
        snprintf(buffer, L_BUF_SIZE,
                 "xzgv --geometry %dx%d+%d+%d %s &", wt + 10, ht + 10,
                 x, y, tempname);
    } else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XLI) {
        if (title) {
            snprintf(buffer, L_BUF_SIZE,
               "xli -dispgamma 1.0 -quiet -geometry +%d+%d -title \"%s\" %s &",
               x, y, title, tempname);
        } else {
            snprintf(buffer, L_BUF_SIZE,
               "xli -dispgamma 1.0 -quiet -geometry +%d+%d %s &",
               x, y, tempname);
        }
    } else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XV) {
        if (title) {
            snprintf(buffer, L_BUF_SIZE,
                     "xv -quit -geometry +%d+%d -name \"%s\" %s &",
                     x, y, title, tempname);
        } else {
            snprintf(buffer, L_BUF_SIZE,
                     "xv -quit -geometry +%d+%d %s &", x, y, tempname);
        }
    } else if (var_DISPLAY_PROG == L_DISPLAY_WITH_OPEN) {
        snprintf(buffer, L_BUF_SIZE, "open %s &", tempname);
    }
    ignore = system(buffer);

#else  /* _WIN32 */

        /* Windows: L_DISPLAY_WITH_IV */
    pathname = genPathname(tempname, NULL);
    _fullpath(fullpath, pathname, sizeof(fullpath));
    if (title) {
        snprintf(buffer, L_BUF_SIZE,
                 "i_view32.exe \"%s\" /pos=(%d,%d) /title=\"%s\"",
                 fullpath, x, y, title);
    } else {
        snprintf(buffer, L_BUF_SIZE, "i_view32.exe \"%s\" /pos=(%d,%d)",
                 fullpath, x, y);
    }
    ignore = system(buffer);
    LEPT_FREE(pathname);

#endif  /* _WIN32 */

    pixDestroy(&pix1);
    pixDestroy(&pix2);
    LEPT_FREE(tempname);
    return 0;
}
// Top-level method to perform splitting based on current settings.
// Returns true if a split was actually performed.
// split_for_pageseg should be true if the splitting is being done prior to
// page segmentation. This mode uses the flag
// pageseg_devanagari_split_strategy to determine the splitting strategy.
bool ShiroRekhaSplitter::Split(bool split_for_pageseg) {
  SplitStrategy split_strategy = split_for_pageseg ? pageseg_split_strategy_ :
      ocr_split_strategy_;
  if (split_strategy == NO_SPLIT) {
    return false;  // Nothing to do.
  }
  ASSERT_HOST(split_strategy == MINIMAL_SPLIT ||
              split_strategy == MAXIMAL_SPLIT);
  ASSERT_HOST(orig_pix_);
  if (devanagari_split_debuglevel > 0) {
    tprintf("Splitting shiro-rekha ...\n");
    tprintf("Split strategy = %s\n",
            split_strategy == MINIMAL_SPLIT ? "Minimal" : "Maximal");
    tprintf("Initial pageseg available = %s\n",
            segmentation_block_list_ ? "yes" : "no");
  }
  // Create a copy of original image to store the splitting output.
  pixDestroy(&splitted_image_);
  splitted_image_ = pixCopy(NULL, orig_pix_);

  // Initialize debug image if required.
  if (devanagari_split_debugimage) {
    pixDestroy(&debug_image_);
    debug_image_ = pixConvertTo32(orig_pix_);
  }

  // Determine all connected components in the input image. A close operation
  // may be required prior to this, depending on the current settings.
  Pix* pix_for_ccs = pixClone(orig_pix_);
  if (perform_close_ && global_xheight_ != kUnspecifiedXheight &&
      !segmentation_block_list_) {
    if (devanagari_split_debuglevel > 0) {
      tprintf("Performing a global close operation..\n");
    }
    // A global measure is available for xheight, but no local information
    // exists.
    pixDestroy(&pix_for_ccs);
    pix_for_ccs = pixCopy(NULL, orig_pix_);
    PerformClose(pix_for_ccs, global_xheight_);
  }
  Pixa* ccs;
  Boxa* tmp_boxa = pixConnComp(pix_for_ccs, &ccs, 8);
  boxaDestroy(&tmp_boxa);
  pixDestroy(&pix_for_ccs);

  // Iterate over all connected components. Get their bounding boxes and clip
  // out the image regions corresponding to these boxes from the original image.
  // Conditionally run splitting on each of them.
  Boxa* regions_to_clear = boxaCreate(0);
  for (int i = 0; i < pixaGetCount(ccs); ++i) {
    Box* box = ccs->boxa->box[i];
    Pix* word_pix = pixClipRectangle(orig_pix_, box, NULL);
    ASSERT_HOST(word_pix);
    int xheight = GetXheightForCC(box);
    if (xheight == kUnspecifiedXheight && segmentation_block_list_ &&
        devanagari_split_debugimage) {
      pixRenderBoxArb(debug_image_, box, 1, 255, 0, 0);
    }
    // If some xheight measure is available, attempt to pre-eliminate small
    // blobs from the shiro-rekha process. This is primarily to save the CCs
    // corresponding to punctuation marks/small dots etc which are part of
    // larger graphemes.
    if (xheight == kUnspecifiedXheight ||
        (box->w > xheight / 3 && box->h > xheight / 2)) {
      SplitWordShiroRekha(split_strategy, word_pix, xheight,
                          box->x, box->y, regions_to_clear);
    } else if (devanagari_split_debuglevel > 0) {
      tprintf("CC dropped from splitting: %d,%d (%d, %d)\n",
              box->x, box->y, box->w, box->h);
    }
    pixDestroy(&word_pix);
  }
  // Actually clear the boxes now.
  for (int i = 0; i < boxaGetCount(regions_to_clear); ++i) {
    Box* box = boxaGetBox(regions_to_clear, i, L_CLONE);
    pixClearInRect(splitted_image_, box);
    boxDestroy(&box);
  }
  boxaDestroy(&regions_to_clear);
  pixaDestroy(&ccs);
  if (devanagari_split_debugimage) {
    DumpDebugImage(split_for_pageseg ? "pageseg_split_debug.png" :
                   "ocr_split_debug.png");
  }
  return true;
}
Beispiel #21
0
/*!
 *  pixGenerateSelWithRuns()
 *
 *      Input:  pix (1 bpp, typically small, to be used as a pattern)
 *              nhlines (number of hor lines along which elements are found)
 *              nvlines (number of vert lines along which elements are found)
 *              distance (min distance from boundary pixel; use 0 for default)
 *              minlength (min runlength to set hit or miss; use 0 for default)
 *              toppix (number of extra pixels of bg added above)
 *              botpix (number of extra pixels of bg added below)
 *              leftpix (number of extra pixels of bg added to left)
 *              rightpix (number of extra pixels of bg added to right)
 *              &pixe (<optional return> input pix expanded by extra pixels)
 *      Return: sel (hit-miss for input pattern), or null on error
 *
 *  Notes:
 *    (1) The horizontal and vertical lines along which elements are
 *        selected are roughly equally spaced.  The actual locations of
 *        the hits and misses are the centers of respective run-lengths.
 *    (2) No elements are selected that are less than 'distance' pixels away
 *        from a boundary pixel of the same color.  This makes the
 *        match much more robust to edge noise.  Valid inputs of
 *        'distance' are 0, 1, 2, 3 and 4.  If distance is either 0 or
 *        greater than 4, we reset it to the default value.
 *    (3) The 4 numbers for adding rectangles of pixels outside the fg
 *        can be use if the pattern is expected to be surrounded by bg
 *        (white) pixels.  On the other hand, if the pattern may be near
 *        other fg (black) components on some sides, use 0 for those sides.
 *    (4) The pixels added to a side allow you to have miss elements there.
 *        There is a constraint between distance, minlength, and
 *        the added pixels for this to work.  We illustrate using the
 *        default values.  If you add 5 pixels to the top, and use a
 *        distance of 1, then you end up with a vertical run of at least
 *        4 bg pixels along the top edge of the image.  If you use a
 *        minimum runlength of 3, each vertical line will always find
 *        a miss near the center of its run.  However, if you use a
 *        minimum runlength of 5, you will not get a miss on every vertical
 *        line.  As another example, if you have 7 added pixels and a
 *        distance of 2, you can use a runlength up to 5 to guarantee
 *        that the miss element is recorded.  We give a warning if the
 *        contraint does not guarantee a miss element outside the
 *        image proper.
 *    (5) The input pix, as extended by the extra pixels on selected sides,
 *        can optionally be returned.  For debugging, call
 *        pixDisplayHitMissSel() to visualize the hit-miss sel superimposed
 *        on the generating bitmap.
 */
SEL *
pixGenerateSelWithRuns(PIX     *pixs,
                       l_int32  nhlines,
                       l_int32  nvlines,
                       l_int32  distance,
                       l_int32  minlength,
                       l_int32  toppix,
                       l_int32  botpix,
                       l_int32  leftpix,
                       l_int32  rightpix,
                       PIX    **ppixe)
{
l_int32    ws, hs, w, h, x, y, xval, yval, i, j, nh, nm;
l_float32  delh, delw;
NUMA      *nah, *nam;
PIX       *pixt1, *pixt2, *pixfg, *pixbg;
PTA       *ptah, *ptam;
SEL       *seld, *sel;

    PROCNAME("pixGenerateSelWithRuns");

    if (ppixe) *ppixe = NULL;
    if (!pixs)
        return (SEL *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 1)
        return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
    if (nhlines < 1 && nvlines < 1)
        return (SEL *)ERROR_PTR("nvlines and nhlines both < 1", procName, NULL);

    if (distance <= 0)
        distance = DEFAULT_DISTANCE_TO_BOUNDARY;
    if (minlength <= 0)
        minlength = DEFAULT_MIN_RUNLENGTH;
    if (distance > MAX_DISTANCE_TO_BOUNDARY) {
        L_WARNING("distance too large; setting to max value", procName);
        distance = MAX_DISTANCE_TO_BOUNDARY;
    }

        /* Locate the foreground */
    pixClipToForeground(pixs, &pixt1, NULL);
    if (!pixt1)
        return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL);
    ws = pixGetWidth(pixt1);
    hs = pixGetHeight(pixt1);
    w = ws;
    h = hs;

        /* Crop out a region including the foreground, and add pixels
         * on sides depending on the side flags */
    if (toppix || botpix || leftpix || rightpix) {
        x = y = 0;
        if (toppix) {
            h += toppix;
            y = toppix;
            if (toppix < distance + minlength)
                L_WARNING("no miss elements in added top pixels", procName);
        }
        if (botpix) {
            h += botpix;
            if (botpix < distance + minlength)
                L_WARNING("no miss elements in added bot pixels", procName);
        }
        if (leftpix) {
            w += leftpix;
            x = leftpix;
            if (leftpix < distance + minlength)
                L_WARNING("no miss elements in added left pixels", procName);
        }
        if (rightpix) {
            w += rightpix;
            if (rightpix < distance + minlength)
                L_WARNING("no miss elements in added right pixels", procName);
        }
        pixt2 = pixCreate(w, h, 1);
        pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0);
    }
    else
        pixt2 = pixClone(pixt1);
    if (ppixe)
        *ppixe = pixClone(pixt2);
    pixDestroy(&pixt1);

        /* Identify fg and bg pixels that are at least 'distance' pixels
         * away from the boundary pixels in their set */
    seld = selCreateBrick(2 * distance + 1, 2 * distance + 1,
                          distance, distance, SEL_HIT);
    pixfg = pixErode(NULL, pixt2, seld);
    pixbg = pixDilate(NULL, pixt2, seld);
    pixInvert(pixbg, pixbg);
    selDestroy(&seld);
    pixDestroy(&pixt2);

        /* Accumulate hit and miss points */
    ptah = ptaCreate(0);
    ptam = ptaCreate(0);
    if (nhlines >= 1) {
        delh = (l_float32)h / (l_float32)(nhlines + 1);
        for (i = 0, y = 0; i < nhlines; i++) {
            y += (l_int32)(delh + 0.5);
            nah = pixGetRunCentersOnLine(pixfg, -1, y, minlength);
            nam = pixGetRunCentersOnLine(pixbg, -1, y, minlength);
            nh = numaGetCount(nah);
            nm = numaGetCount(nam);
            for (j = 0; j < nh; j++) {
                numaGetIValue(nah, j, &xval);
                ptaAddPt(ptah, xval, y);
            }
            for (j = 0; j < nm; j++) {
                numaGetIValue(nam, j, &xval);
                ptaAddPt(ptam, xval, y);
            }
            numaDestroy(&nah);
            numaDestroy(&nam);
        }
    }
    if (nvlines >= 1) {
        delw = (l_float32)w / (l_float32)(nvlines + 1);
        for (i = 0, x = 0; i < nvlines; i++) {
            x += (l_int32)(delw + 0.5);
            nah = pixGetRunCentersOnLine(pixfg, x, -1, minlength);
            nam = pixGetRunCentersOnLine(pixbg, x, -1, minlength);
            nh = numaGetCount(nah);
            nm = numaGetCount(nam);
            for (j = 0; j < nh; j++) {
                numaGetIValue(nah, j, &yval);
                ptaAddPt(ptah, x, yval);
            }
            for (j = 0; j < nm; j++) {
                numaGetIValue(nam, j, &yval);
                ptaAddPt(ptam, x, yval);
            }
            numaDestroy(&nah);
            numaDestroy(&nam);
        }
    }

        /* Make the Sel with those points */
    sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE);
    nh = ptaGetCount(ptah);
    for (i = 0; i < nh; i++) {
        ptaGetIPt(ptah, i, &x, &y);
        selSetElement(sel, y, x, SEL_HIT);
    }
    nm = ptaGetCount(ptam);
    for (i = 0; i < nm; i++) {
        ptaGetIPt(ptam, i, &x, &y);
        selSetElement(sel, y, x, SEL_MISS);
    }

    pixDestroy(&pixfg);
    pixDestroy(&pixbg);
    ptaDestroy(&ptah);
    ptaDestroy(&ptam);
    return sel;
}
Beispiel #22
0
/*!
 *  pixSaveTiledOutline()
 *
 *      Input:  pixs (1, 2, 4, 8, 32 bpp)
 *              pixa (the pix are accumulated here)
 *              reduction (0 to disable; otherwise this is a reduction factor)
 *              newrow (0 if placed on the same row as previous; 1 otherwise)
 *              space (horizontal and vertical spacing, in pixels)
 *              linewidth (width of added outline for image; 0 for no outline)
 *              dp (depth of pixa; 8 or 32 bpp; only used on first call)
 *      Return: 0 if OK, 1 on error.
 *
 *  Notes:
 *      (1) Before calling this function for the first time, use
 *          pixaCreate() to make the @pixa that will accumulate the pix.
 *          This is passed in each time pixSaveTiled() is called.
 *      (2) @reduction is the integer reduction factor for the input
 *          image.  After reduction and possible depth conversion,
 *          the image is saved in the input pixa, along with a box
 *          that specifies the location to place it when tiled later.
 *          Disable saving the pix by setting reduction == 0.
 *      (3) @newrow and @space specify the location of the new pix
 *          with respect to the last one(s) that were entered.
 *      (4) @dp specifies the depth at which all pix are saved.  It can
 *          be only 8 or 32 bpp.  Any colormap is removed.  This is only
 *          used at the first invocation.
 *      (5) This function uses two variables from call to call.
 *          If they were static, the function would not be .so or thread
 *          safe, and furthermore, there would be interference with two or
 *          more pixa accumulating images at a time.  Consequently,
 *          we use the first pix in the pixa to store and obtain both
 *          the depth and the current position of the bottom (one pixel
 *          below the lowest image raster line when laid out using
 *          the boxa).  The bottom variable is stored in the input format
 *          field, which is the only field available for storing an int.
 */
l_int32
pixSaveTiledOutline(PIX     *pixs,
                    PIXA    *pixa,
                    l_int32  reduction,
                    l_int32  newrow,
                    l_int32  space,
                    l_int32  linewidth,
                    l_int32  dp)
{
l_int32         n, top, left, bx, by, bw, w, h, depth, bottom;
l_float32       scale;
BOX            *box;
PIX            *pix, *pixt1, *pixt2, *pixt3;

    PROCNAME("pixSaveTiledOutline");

    if (reduction == 0) return 0;

    if (!pixs)
        return ERROR_INT("pixs not defined", procName, 1);
    if (!pixa)
        return ERROR_INT("pixa not defined", procName, 1);

    n = pixaGetCount(pixa);
    if (n == 0) {
        bottom = 0;
        if (dp != 8 && dp != 32) {
            L_WARNING("dp not 8 or 32 bpp; using 32", procName);
            depth = 32;
        } else
            depth = dp;
    }
    else {  /* extract the depth and bottom params from the first pix */
        pix = pixaGetPix(pixa, 0, L_CLONE);
        depth = pixGetDepth(pix);
        bottom = pixGetInputFormat(pix);  /* not typical usage! */
        pixDestroy(&pix);
    }

        /* Scale and convert to output depth */
    if (reduction == 1)
        pixt1 = pixClone(pixs);
    else {
        scale = 1. / (l_float32)reduction;
        if (pixGetDepth(pixs) == 1)
            pixt1 = pixScaleToGray(pixs, scale);
        else
            pixt1 = pixScale(pixs, scale, scale);
    }
    if (depth == 8)
        pixt2 = pixConvertTo8(pixt1, 0);
    else
        pixt2 = pixConvertTo32(pixt1);
    pixDestroy(&pixt1);

        /* Add black outline */
    if (linewidth > 0)
        pixt3 = pixAddBorder(pixt2, linewidth, 0);
    else
        pixt3 = pixClone(pixt2);
    pixDestroy(&pixt2);

        /* Find position of current pix (UL corner plus size) */
    if (n == 0) {
        top = 0;
        left = 0;
    }
    else if (newrow == 1) {
        top = bottom + space;
        left = 0;
    }
    else if (n > 0) {
        pixaGetBoxGeometry(pixa, n - 1, &bx, &by, &bw, NULL);
        top = by;
        left = bx + bw + space;
    }

    pixGetDimensions(pixt3, &w, &h, NULL);
    bottom = L_MAX(bottom, top + h);
    box = boxCreate(left, top, w, h);
    pixaAddPix(pixa, pixt3, L_INSERT);
    pixaAddBox(pixa, box, L_INSERT);

        /* Save the new bottom value */
    pix = pixaGetPix(pixa, 0, L_CLONE);
    pixSetInputFormat(pix, bottom);  /* not typical usage! */
    pixDestroy(&pix);

    return 0;
}
Beispiel #23
0
/*!
 *  pixGenerateSelBoundary()
 *
 *      Input:  pix (1 bpp, typically small, to be used as a pattern)
 *              hitdist (min distance from fg boundary pixel)
 *              missdist (min distance from bg boundary pixel)
 *              hitskip (number of boundary pixels skipped between hits)
 *              missskip (number of boundary pixels skipped between misses)
 *              topflag (flag for extra pixels of bg added above)
 *              botflag (flag for extra pixels of bg added below)
 *              leftflag (flag for extra pixels of bg added to left)
 *              rightflag (flag for extra pixels of bg added to right)
 *              &pixe (<optional return> input pix expanded by extra pixels)
 *      Return: sel (hit-miss for input pattern), or null on error
 *
 *  Notes:
 *    (1) All fg elements selected are exactly hitdist pixels away from
 *        the nearest fg boundary pixel, and ditto for bg elements.
 *        Valid inputs of hitdist and missdist are 0, 1, 2, 3 and 4.
 *        For example, a hitdist of 0 puts the hits at the fg boundary.
 *        Usually, the distances should be > 0 avoid the effect of
 *        noise at the boundary.
 *    (2) Set hitskip < 0 if no hits are to be used.  Ditto for missskip.
 *        If both hitskip and missskip are < 0, the sel would be empty,
 *        and NULL is returned.
 *    (3) The 4 flags determine whether the sel is increased on that side
 *        to allow bg misses to be placed all along that boundary.
 *        The increase in sel size on that side is the minimum necessary
 *        to allow the misses to be placed at mindist.  For text characters,
 *        the topflag and botflag are typically set to 1, and the leftflag
 *        and rightflag to 0.
 *    (4) The input pix, as extended by the extra pixels on selected sides,
 *        can optionally be returned.  For debugging, call
 *        pixDisplayHitMissSel() to visualize the hit-miss sel superimposed
 *        on the generating bitmap.
 *    (5) This is probably the best of the three sel generators, in the
 *        sense that you have the most flexibility with the smallest number
 *        of hits and misses.
 */
SEL *
pixGenerateSelBoundary(PIX     *pixs,
                       l_int32  hitdist,
                       l_int32  missdist,
                       l_int32  hitskip,
                       l_int32  missskip,
                       l_int32  topflag,
                       l_int32  botflag,
                       l_int32  leftflag,
                       l_int32  rightflag,
                       PIX      **ppixe)
{
l_int32  ws, hs, w, h, x, y, ix, iy, i, npt;
PIX     *pixt1, *pixt2, *pixt3, *pixfg, *pixbg;
SEL     *selh, *selm, *sel_3, *sel;
PTA     *ptah, *ptam;

    PROCNAME("pixGenerateSelBoundary");

    if (ppixe) *ppixe = NULL;
    if (!pixs)
        return (SEL *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 1)
        return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
    if (hitdist < 0 || hitdist > 4 || missdist < 0 || missdist > 4)
        return (SEL *)ERROR_PTR("dist not in {0 .. 4}", procName, NULL);
    if (hitskip < 0 && missskip < 0)
        return (SEL *)ERROR_PTR("no hits or misses", procName, NULL);

        /* Locate the foreground */
    pixClipToForeground(pixs, &pixt1, NULL);
    if (!pixt1)
        return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL);
    ws = pixGetWidth(pixt1);
    hs = pixGetHeight(pixt1);
    w = ws;
    h = hs;

        /* Crop out a region including the foreground, and add pixels
         * on sides depending on the side flags */
    if (topflag || botflag || leftflag || rightflag) {
        x = y = 0;
        if (topflag) {
            h += missdist + 1;
            y = missdist + 1;
        }
        if (botflag)
            h += missdist + 1;
        if (leftflag) {
            w += missdist + 1;
            x = missdist + 1;
        }
        if (rightflag)
            w += missdist + 1;
        pixt2 = pixCreate(w, h, 1);
        pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0);
    }
    else {
        pixt2 = pixClone(pixt1);
    }
    if (ppixe)
        *ppixe = pixClone(pixt2);
    pixDestroy(&pixt1);

        /* Identify fg and bg pixels that are exactly hitdist and
         * missdist (rsp) away from the boundary pixels in their set.
         * Then get a subsampled set of these points. */
    sel_3 = selCreateBrick(3, 3, 1, 1, SEL_HIT);
    if (hitskip >= 0) {
        selh = selCreateBrick(2 * hitdist + 1, 2 * hitdist + 1,
                              hitdist, hitdist, SEL_HIT);
        pixt3 = pixErode(NULL, pixt2, selh);
        pixfg = pixErode(NULL, pixt3, sel_3);
        pixXor(pixfg, pixfg, pixt3);
        ptah = pixSubsampleBoundaryPixels(pixfg, hitskip);
        pixDestroy(&pixt3);
        pixDestroy(&pixfg);
        selDestroy(&selh);
    }
    if (missskip >= 0) {
        selm = selCreateBrick(2 * missdist + 1, 2 * missdist + 1,
                              missdist, missdist, SEL_HIT);
        pixt3 = pixDilate(NULL, pixt2, selm);
        pixbg = pixDilate(NULL, pixt3, sel_3);
        pixXor(pixbg, pixbg, pixt3);
        ptam = pixSubsampleBoundaryPixels(pixbg, missskip);
        pixDestroy(&pixt3);
        pixDestroy(&pixbg);
        selDestroy(&selm);
    }
    selDestroy(&sel_3);
    pixDestroy(&pixt2);

        /* Generate the hit-miss sel from these point */
    sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE);
    if (hitskip >= 0) {
        npt = ptaGetCount(ptah);
        for (i = 0; i < npt; i++) {
            ptaGetIPt(ptah, i, &ix, &iy);
            selSetElement(sel, iy, ix, SEL_HIT);
        }
    }
    if (missskip >= 0) {
        npt = ptaGetCount(ptam);
        for (i = 0; i < npt; i++) {
            ptaGetIPt(ptam, i, &ix, &iy);
            selSetElement(sel, iy, ix, SEL_MISS);
        }
    }

    ptaDestroy(&ptah);
    ptaDestroy(&ptam);
    return sel;
}
Beispiel #24
0
/*!
 *  pixDisplayWithTitle()
 *
 *      Input:  pix (1, 2, 4, 8, 16, 32 bpp)
 *              x, y  (location of display frame)
 *              title (<optional> on frame; can be NULL);
 *              dispflag (1 to write, else disabled)
 *      Return: 0 if OK; 1 on error
 *
 *  Notes:
 *      (1) See notes for pixDisplay().
 *      (2) This displays the image if dispflag == 1.
 */
l_int32
pixDisplayWithTitle(PIX         *pixs,
                    l_int32      x,
                    l_int32      y,
                    const char  *title,
                    l_int32      dispflag)
{
char           *tempname;
char            buffer[L_BUF_SIZE];
static l_int32  index = 0;  /* caution: not .so or thread safe */
l_int32         w, h, d, ignore;
l_float32       ratw, rath, ratmin;
PIX            *pixt;
#ifndef _WIN32
l_int32         wt, ht;
#else
char           *pathname;
char            fullpath[_MAX_PATH];
#endif  /* _WIN32 */

    PROCNAME("pixDisplayWithTitle");

    if (dispflag != 1) return 0;
    if (!pixs)
        return ERROR_INT("pixs not defined", procName, 1);
    if (var_DISPLAY_PROG != L_DISPLAY_WITH_XV &&
        var_DISPLAY_PROG != L_DISPLAY_WITH_XLI &&
        var_DISPLAY_PROG != L_DISPLAY_WITH_XZGV &&
        var_DISPLAY_PROG != L_DISPLAY_WITH_IV)
        return ERROR_INT("no program chosen for display", procName, 1);

    pixGetDimensions(pixs, &w, &h, &d);
    if (w <= MAX_DISPLAY_WIDTH && h <= MAX_DISPLAY_HEIGHT) {
        if (d == 16)  /* take MSB */
            pixt = pixConvert16To8(pixs, 1);
        else
            pixt = pixClone(pixs);
    }
    else {
        ratw = (l_float32)MAX_DISPLAY_WIDTH / (l_float32)w;
        rath = (l_float32)MAX_DISPLAY_HEIGHT / (l_float32)h;
        ratmin = L_MIN(ratw, rath);
        if (ratmin < 0.125 && d == 1)
            pixt = pixScaleToGray8(pixs);
        else if (ratmin < 0.25 && d == 1)
            pixt = pixScaleToGray4(pixs);
        else if (ratmin < 0.33 && d == 1)
            pixt = pixScaleToGray3(pixs);
        else if (ratmin < 0.5 && d == 1)
            pixt = pixScaleToGray2(pixs);
        else
            pixt = pixScale(pixs, ratmin, ratmin);
        if (!pixt)
            return ERROR_INT("pixt not made", procName, 1);
    }

    if (index == 0) {
        lept_rmdir("display");
        lept_mkdir("display");
    }

    index++;
    if (pixGetDepth(pixt) < 8 ||
        (w < MAX_SIZE_FOR_PNG && h < MAX_SIZE_FOR_PNG)) {
        snprintf(buffer, L_BUF_SIZE, "/tmp/display/write.%03d.png", index);
        pixWrite(buffer, pixt, IFF_PNG);
    }
    else {
        snprintf(buffer, L_BUF_SIZE, "/tmp/display/write.%03d.jpg", index);
        pixWrite(buffer, pixt, IFF_JFIF_JPEG);
    }
    tempname = stringNew(buffer);

#ifndef _WIN32

        /* Unix */
    if (var_DISPLAY_PROG == L_DISPLAY_WITH_XV) {
        if (title)
            snprintf(buffer, L_BUF_SIZE,
                     "xv -quit -geometry +%d+%d -name \"%s\" %s &",
                     x, y, title, tempname);
        else
            snprintf(buffer, L_BUF_SIZE,
                     "xv -quit -geometry +%d+%d %s &", x, y, tempname);
    }
    else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XLI) {
        if (title)
            snprintf(buffer, L_BUF_SIZE,
               "xli -dispgamma 1.0 -quiet -geometry +%d+%d -title \"%s\" %s &",
               x, y, title, tempname);
        else
            snprintf(buffer, L_BUF_SIZE,
               "xli -dispgamma 1.0 -quiet -geometry +%d+%d %s &",
               x, y, tempname);
    }
    else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XZGV) {
            /* no way to display title */
        pixGetDimensions(pixt, &wt, &ht, NULL);
        snprintf(buffer, L_BUF_SIZE,
                 "xzgv --geometry %dx%d+%d+%d %s &", wt + 10, ht + 10,
                 x, y, tempname);
    }
    ignore = system(buffer);

#else  /* _WIN32 */

        /* Windows: L_DISPLAY_WITH_IV */
    pathname = genPathname(tempname, NULL);
    _fullpath(fullpath, pathname, sizeof(fullpath));
    if (title)
        snprintf(buffer, L_BUF_SIZE,
                 "i_view32.exe \"%s\" /pos=(%d,%d) /title=\"%s\"",
                 fullpath, x, y, title);
    else
        snprintf(buffer, L_BUF_SIZE, "i_view32.exe \"%s\" /pos=(%d,%d)",
                 fullpath, x, y);
    ignore = system(buffer);
    FREE(pathname);

#endif  /* _WIN32 */

    pixDestroy(&pixt);
    FREE(tempname);
    return 0;
}
Beispiel #25
0
/*!
 *  pixSauvolaBinarize()
 *
 *      Input:  pixs (8 bpp grayscale; not colormapped)
 *              whsize (window half-width for measuring local statistics)
 *              factor (factor for reducing threshold due to variance; >= 0)
 *              addborder (1 to add border of width (@whsize + 1) on all sides)
 *              &pixm (<optional return> local mean values)
 *              &pixsd (<optional return> local standard deviation values)
 *              &pixth (<optional return> threshold values)
 *              &pixd (<optional return> thresholded image)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) The window width and height are 2 * @whsize + 1.  The minimum
 *          value for @whsize is 2; typically it is >= 7..
 *      (2) The local statistics, measured over the window, are the
 *          average and standard deviation.
 *      (3) The measurements of the mean and standard deviation are
 *          performed inside a border of (@whsize + 1) pixels.  If pixs does
 *          not have these added border pixels, use @addborder = 1 to add
 *          it here; otherwise use @addborder = 0.
 *      (4) The Sauvola threshold is determined from the formula:
 *            t = m * (1 - k * (1 - s / 128))
 *          where:
 *            t = local threshold
 *            m = local mean
 *            k = @factor (>= 0)   [ typ. 0.35 ]
 *            s = local standard deviation, which is maximized at
 *                127.5 when half the samples are 0 and half are 255.
 *      (5) The basic idea of Niblack and Sauvola binarization is that
 *          the local threshold should be less than the median value,
 *          and the larger the variance, the closer to the median
 *          it should be chosen.  Typical values for k are between
 *          0.2 and 0.5.
 */
l_int32
pixSauvolaBinarize(PIX       *pixs,
                   l_int32    whsize,
                   l_float32  factor,
                   l_int32    addborder,
                   PIX      **ppixm,
                   PIX      **ppixsd,
                   PIX      **ppixth,
                   PIX      **ppixd)
{
l_int32  w, h;
PIX     *pixg, *pixsc, *pixm, *pixms, *pixth, *pixd;

    PROCNAME("pixSauvolaBinarize");


    if (!ppixm && !ppixsd && !ppixth && !ppixd)
        return ERROR_INT("no outputs", procName, 1);
    if (ppixm) *ppixm = NULL;
    if (ppixsd) *ppixsd = NULL;
    if (ppixth) *ppixth = NULL;
    if (ppixd) *ppixd = NULL;
    if (!pixs || pixGetDepth(pixs) != 8)
        return ERROR_INT("pixs undefined or not 8 bpp", procName, 1);
    if (pixGetColormap(pixs))
        return ERROR_INT("pixs is cmapped", procName, 1);
    pixGetDimensions(pixs, &w, &h, NULL);
    if (whsize < 2)
        return ERROR_INT("whsize must be >= 2", procName, 1);
    if (w < 2 * whsize + 3 || h < 2 * whsize + 3)
        return ERROR_INT("whsize too large for image", procName, 1);
    if (factor < 0.0)
        return ERROR_INT("factor must be >= 0", procName, 1);

    if (addborder) {
        pixg = pixAddMirroredBorder(pixs, whsize + 1, whsize + 1,
                                    whsize + 1, whsize + 1);
        pixsc = pixClone(pixs);
    } else {
        pixg = pixClone(pixs);
        pixsc = pixRemoveBorder(pixs, whsize + 1);
    }
    if (!pixg || !pixsc)
        return ERROR_INT("pixg and pixsc not made", procName, 1);

        /* All these functions strip off the border pixels. */
    if (ppixm || ppixth || ppixd)
        pixm = pixWindowedMean(pixg, whsize, whsize, 1, 1);
    if (ppixsd || ppixth || ppixd)
        pixms = pixWindowedMeanSquare(pixg, whsize, whsize, 1);
    if (ppixth || ppixd)
        pixth = pixSauvolaGetThreshold(pixm, pixms, factor, ppixsd);
    if (ppixd)
        pixd = pixApplyLocalThreshold(pixsc, pixth, 1);

    if (ppixm)
        *ppixm = pixm;
    else
        pixDestroy(&pixm);
    pixDestroy(&pixms);
    if (ppixth)
        *ppixth = pixth;
    else
        pixDestroy(&pixth);
    if (ppixd)
        *ppixd = pixd;
    else
        pixDestroy(&pixd);
    pixDestroy(&pixg);
    pixDestroy(&pixsc);
    return 0;
}
Beispiel #26
0
/*!
 *  pixDisplayWriteFormat()
 *
 *      Input:  pix (1, 2, 4, 8, 16, 32 bpp)
 *              reduction (-1 to reset/erase; 0 to disable;
 *                         otherwise this is a reduction factor)
 *              format (IFF_PNG or IFF_JFIF_JPEG)
 *      Return: 0 if OK; 1 on error
 *
 *  Notes:
 *      (1) This writes files if reduction > 0.  These can be displayed using
 *            pixDisplayMultiple("/tmp/junk_write_display*");
 *      (2) All previously written files can be erased by calling with
 *          reduction < 0; the value of pixs is ignored.
 *      (3) If reduction > 1 and depth == 1, this does a scale-to-gray
 *          reduction.
 *      (4) This function uses a static internal variable to number
 *          output files written by a single process.  Behavior
 *          with a shared library may be unpredictable.
 *      (5) Output file format is as follows:
 *            format == IFF_JFIF_JPEG:
 *                png if d < 8 or d == 16 or if the output pix
 *                has a colormap.   Otherwise, output is jpg.
 *            format == IFF_PNG:
 *                png (lossless) on all images.
 *      (6) For 16 bpp, the choice of full dynamic range with log scale
 *          is the best for displaying these images.  Alternative outputs are
 *             pix8 = pixMaxDynamicRange(pixt, L_LINEAR_SCALE);
 *             pix8 = pixConvert16To8(pixt, 0);  // low order byte
 *             pix8 = pixConvert16To8(pixt, 1);  // high order byte
 */
l_int32
pixDisplayWriteFormat(PIX     *pixs,
                      l_int32  reduction,
                      l_int32  format)
{
char            buffer[L_BUF_SIZE];
l_int32         ignore;
l_float32       scale;
PIX            *pixt, *pix8;
static l_int32  index = 0;  /* caution: not .so or thread safe */

    PROCNAME("pixDisplayWriteFormat");

    if (reduction == 0) return 0;

    if (reduction < 0) {
        index = 0;  /* reset; this will cause erasure at next call to write */
        return 0;
    }

    if (format != IFF_JFIF_JPEG && format != IFF_PNG)
        return ERROR_INT("invalid format", procName, 1);
    if (!pixs)
        return ERROR_INT("pixs not defined", procName, 1);

    if (index == 0) {
        snprintf(buffer, L_BUF_SIZE,
           "rm -f /tmp/junk_write_display.*.png /tmp/junk_write_display.*.jpg");
        ignore = system(buffer);
    }
    index++;

    if (reduction == 1)
        pixt = pixClone(pixs);
    else {
        scale = 1. / (l_float32)reduction;
        if (pixGetDepth(pixs) == 1)
            pixt = pixScaleToGray(pixs, scale);
        else
            pixt = pixScale(pixs, scale, scale);
    }

    if (pixGetDepth(pixt) == 16) {
        pix8 = pixMaxDynamicRange(pixt, L_LOG_SCALE);
        snprintf(buffer, L_BUF_SIZE, "/tmp/junk_write_display.%03d.png", index);
        pixWrite(buffer, pix8, IFF_PNG);
        pixDestroy(&pix8);
    }
    else if (pixGetDepth(pixt) < 8 || pixGetColormap(pixt) ||
             format == IFF_PNG) {
        snprintf(buffer, L_BUF_SIZE, "/tmp/junk_write_display.%03d.png", index);
        pixWrite(buffer, pixt, IFF_PNG);
    }
    else {
        snprintf(buffer, L_BUF_SIZE, "/tmp/junk_write_display.%03d.jpg", index);
        pixWrite(buffer, pixt, format);
    }
    pixDestroy(&pixt);

    return 0;
}
Beispiel #27
0
int main(int    argc,
         char **argv)
{
char        *filein, *fileout, *fname;
char         buffer[512];
const char  *psfile = "/tmp/junk_split_image.ps";
l_int32      nx, ny, i, w, h, d, ws, hs, n, res, ignore;
l_float32    scale;
PIX         *pixs, *pixt, *pixr;
PIXA        *pixa;
static char  mainName[] = "splitimage2pdf";

    if (argc != 5)
        return ERROR_INT(" Syntax:  splitimage2pdf filein nx ny fileout",
                         mainName, 1);

    filein = argv[1];
    nx = atoi(argv[2]);
    ny = atoi(argv[3]);
    fileout = argv[4];

    lept_rm(NULL, "junk_split_image.ps");

    if ((pixs = pixRead(filein)) == NULL)
        return ERROR_INT("pixs not made", mainName, 1);
    d = pixGetDepth(pixs);
    if (d == 1 )
        lept_rm(NULL, "junk_split_image.tif");
    else if (d == 8 || d == 32)
        lept_rm(NULL, "junk_split_image.jpg");
    else
        return ERROR_INT("d not in {1,8,32} bpp", mainName, 1);

    pixGetDimensions(pixs, &ws, &hs, NULL);
    if (ny * ws > nx * hs)
        pixr = pixRotate90(pixs, 1);
    else
        pixr = pixClone(pixs);

    pixa = pixaSplitPix(pixr, nx, ny, 0, 0);
    n = pixaGetCount(pixa);
    res = 300;
    for (i = 0; i < n; i++) {
        pixt = pixaGetPix(pixa, i, L_CLONE);
        pixGetDimensions(pixt, &w, &h, NULL);
        scale = L_MIN(FILL_FACTOR * 2550 / w, FILL_FACTOR * 3300 / h);
        fname = NULL;
        if (d == 1) {
            fname = genPathname("/tmp", "junk_split_image.tif");
            pixWrite(fname, pixt, IFF_TIFF_G4);
            if (i == 0) {
                convertG4ToPS(fname, psfile, "w", 0, 0, 300,
                              scale, 1, FALSE, TRUE);
            } else {
                convertG4ToPS(fname, psfile, "a", 0, 0, 300,
                              scale, 1, FALSE, TRUE);
            }
        } else {
            fname = genPathname("/tmp", "junk_split_image.jpg");
            pixWrite(fname, pixt, IFF_JFIF_JPEG);
            if (i == 0) {
                convertJpegToPS(fname, psfile, "w", 0, 0, 300,
                                scale, 1, TRUE);
            } else {
                convertJpegToPS(fname, psfile, "a", 0, 0, 300,
                                scale, 1, TRUE);
            }
        }
        lept_free(fname);
        pixDestroy(&pixt);
    }

    snprintf(buffer, sizeof(buffer), "ps2pdf %s %s", psfile, fileout);
    ignore = system(buffer);  /* ps2pdf */

    pixaDestroy(&pixa);
    pixDestroy(&pixr);
    pixDestroy(&pixs);
    return 0;
}
Beispiel #28
0
/*!
 *  deskew()
 *
 *      Input:  pixs
 *              redsearch  (for binary search: reduction factor = 1, 2 or 4)
 *      Return: deskewed pix, or NULL on error
 */
PIX *
deskew(PIX     *pixs,
       l_int32  redsearch)
{
l_float32  angle, conf, deg2rad;
PIX       *pixg;  /* gray version */
PIX       *pixb; /* binary version */
PIX       *pixd;  /* destination image */

    PROCNAME("deskew");

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

    /* Calculate a skew angle.  We may need to make a binary version of the
     * image for this calculation.
     */
    if (pixGetDepth(pixs) != 1) {
	/* FIX ME:  We should probably pick a threshold value with more care.  */
	/* Create a grayscale image if we need one.  */
	if (pixGetDepth(pixs) >= 24) {
	    pixg = pixConvertRGBToGray(pixs, 0.0, 0.0, 0.0);
	} else {
	    pixg = pixs;
	}
	    
	pixb = pixThresholdToBinary(pixg, 127);
	if (pixg != pixs) {
	    pixDestroy(&pixg);
	}
	/* Assert:  We are done with any gray image.  */
    } else {
	pixb = pixs;
    }
    /* Assert: We have a valid binary image.  */
    if (redsearch != 1 && redsearch != 2 && redsearch != 4)
	return (PIX *)ERROR_PTR("redsearch not in {1,2,4}", procName, NULL);

    deg2rad = 3.1415926535 / 180.;
    if (pixFindSkewSweepAndSearch(pixb, &angle, &conf,
				  DEFAULT_SWEEP_REDUCTION, redsearch,
				  DEFAULT_SWEEP_RANGE, DEFAULT_SWEEP_DELTA,
				  DEFAULT_MINBS_DELTA)) {
	pixd = pixClone(pixs);
	goto finish;
    }
	
    if (L_ABS(angle) < MIN_DESKEW_ANGLE || conf < MIN_ALLOWED_CONFIDENCE) {
	pixd = pixClone(pixs);
	goto finish;
    }

    /* If the pixel depth of pixs is 1, we need to use a bit-depth
     * independent rotate instead of the more accurate area mapping rotate.
     */
    if (pixGetDepth(pixs) == 1) {
	if ((pixd = pixRotateShear(pixs, 0, 0, deg2rad * angle, 0xffffff00)) == NULL) {
	    pixd = pixClone(pixs);
	}
    } else {
#if defined(COLOR_ROTATE)
	if ((pixd = pixRotateAMColorFast(pixs, deg2rad * angle)) == NULL) {
	    pixd = pixClone(pixs);
	}
#else
	if ((pixd = pixRotateAM(pixs, deg2rad * angle, 0xffffff00)) == NULL) {
	    pixd = pixClone(pixs);
	}
#endif
    }

   finish:
    if (pixb != pixs) {
	pixDestroy(&pixb);
    }
    return pixd;
}
Beispiel #29
0
/*!
 *  pixStrokeWidthTransform()
 *
 *      Input:   pixs (1 bpp)
 *               color (0 for white runs, 1 for black runs)
 *               depth (of pixd: 8 or 16 bpp)
 *               nangles (2, 4, 6 or 8)
 *      Return:  pixd (8 or 16 bpp), or null on error
 *
 *  Notes:
 *      (1) The dest Pix is 8 or 16 bpp, with the pixel values
 *          equal to the stroke width in which it is a member.
 *          The values are clipped to the max pixel value if necessary.
 *      (2) The color determines if we're labelling white or black strokes.
 *      (3) A pixel that is not a member of the chosen color gets
 *          value 0; it belongs to a width of length 0 of the
 *          chosen color.
 *      (4) This chooses, for each dest pixel, the minimum of sets
 *          of runlengths through each pixel.  Here are the sets:
 *            nangles    increment          set
 *            -------    ---------    --------------------------------
 *               2          90       {0, 90}
 *               4          45       {0, 45, 90, 135}
 *               6          30       {0, 30, 60, 90, 120, 150}
 *               8          22.5     {0, 22.5, 45, 67.5, 90, 112.5, 135, 157.5}
 *      (5) Runtime scales linearly with (nangles - 2).
 */
PIX *
pixStrokeWidthTransform(PIX *pixs,
                        l_int32 color,
                        l_int32 depth,
                        l_int32 nangles) {
    l_float32 angle, pi;
    PIX *pixh, *pixv, *pixt, *pixg1, *pixg2, *pixg3, *pixg4;

    PROCNAME("pixStrokeWidthTransform");

    if (!pixs || pixGetDepth(pixs) != 1)
        return (PIX *) ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
    if (depth != 8 && depth != 16)
        return (PIX *) ERROR_PTR("depth must be 8 or 16 bpp", procName, NULL);
    if (nangles != 2 && nangles != 4 && nangles != 6 && nangles != 8)
        return (PIX *) ERROR_PTR("nangles not in {2,4,6,8}", procName, NULL);

    /* Use fg runs for evaluation */
    if (color == 0)
        pixt = pixInvert(NULL, pixs);
    else
        pixt = pixClone(pixs);

    /* Find min length at 0 and 90 degrees */
    pixh = pixRunlengthTransform(pixt, 1, L_HORIZONTAL_RUNS, depth);
    pixv = pixRunlengthTransform(pixt, 1, L_VERTICAL_RUNS, depth);
    pixg1 = pixMinOrMax(NULL, pixh, pixv, L_CHOOSE_MIN);
    pixDestroy(&pixh);
    pixDestroy(&pixv);

    pixg2 = pixg3 = pixg4 = NULL;
    pi = 3.1415926535;
    if (nangles == 4 || nangles == 8) {
        /* Find min length at +45 and -45 degrees */
        angle = pi / 4.0;
        pixg2 = pixFindMinRunsOrthogonal(pixt, angle, depth);
    }

    if (nangles == 6) {
        /* Find min length at +30 and -60 degrees */
        angle = pi / 6.0;
        pixg2 = pixFindMinRunsOrthogonal(pixt, angle, depth);

        /* Find min length at +60 and -30 degrees */
        angle = pi / 3.0;
        pixg3 = pixFindMinRunsOrthogonal(pixt, angle, depth);
    }

    if (nangles == 8) {
        /* Find min length at +22.5 and -67.5 degrees */
        angle = pi / 8.0;
        pixg3 = pixFindMinRunsOrthogonal(pixt, angle, depth);

        /* Find min length at +67.5 and -22.5 degrees */
        angle = 3.0 * pi / 8.0;
        pixg4 = pixFindMinRunsOrthogonal(pixt, angle, depth);
    }
    pixDestroy(&pixt);

    if (nangles > 2)
        pixMinOrMax(pixg1, pixg1, pixg2, L_CHOOSE_MIN);
    if (nangles > 4)
        pixMinOrMax(pixg1, pixg1, pixg3, L_CHOOSE_MIN);
    if (nangles > 6)
        pixMinOrMax(pixg1, pixg1, pixg4, L_CHOOSE_MIN);
    pixDestroy(&pixg2);
    pixDestroy(&pixg3);
    pixDestroy(&pixg4);
    return pixg1;
}
Beispiel #30
0
/*!
 *  pixQuadtreeVariance()
 *
 *      Input:  pixs (8 bpp, no colormap)
 *              nlevels (in quadtree)
 *             *pix_ma (input mean accumulator; can be null)
 *             *dpix_msa (input mean square accumulator; can be null)
 *             *pfpixa_v (<optional return> variance values in quadtree)
 *             *pfpixa_rv (<optional return> root variance values in quadtree)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) The returned fpixav and fpixarv have @nlevels of fpix,
 *          each containing at the respective levels the variance
 *          and root variance values.
 */
l_int32
pixQuadtreeVariance(PIX     *pixs,
                    l_int32  nlevels,
                    PIX     *pix_ma,
                    DPIX    *dpix_msa,
                    FPIXA  **pfpixa_v,
                    FPIXA  **pfpixa_rv)
{
l_int32    i, j, w, h, size, n;
l_float32  var, rvar;
BOX       *box;
BOXA      *boxa;
BOXAA     *baa;
FPIX      *fpixv, *fpixrv;
PIX       *pix_mac;  /* copy of mean accumulator */
DPIX      *dpix_msac;  /* msa clone */

    PROCNAME("pixQuadtreeVariance");

    if (!pfpixa_v && !pfpixa_rv)
        return ERROR_INT("neither &fpixav nor &fpixarv defined", procName, 1);
    if (pfpixa_v) *pfpixa_v = NULL;
    if (pfpixa_rv) *pfpixa_rv = NULL;
    if (!pixs || pixGetDepth(pixs) != 8)
        return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
    pixGetDimensions(pixs, &w, &h, NULL);
    if (nlevels > quadtreeMaxLevels(w, h))
        return ERROR_INT("nlevels too large for image", procName, 1);

    if (!pix_ma)
        pix_mac = pixBlockconvAccum(pixs);
    else
        pix_mac = pixClone(pix_ma);
    if (!pix_mac)
        return ERROR_INT("pix_mac not made", procName, 1);
    if (!dpix_msa)
        dpix_msac = pixMeanSquareAccum(pixs);
    else
        dpix_msac = dpixClone(dpix_msa);
    if (!dpix_msac)
        return ERROR_INT("dpix_msac not made", procName, 1);

    if ((baa = boxaaQuadtreeRegions(w, h, nlevels)) == NULL) {
        pixDestroy(&pix_mac);
        dpixDestroy(&dpix_msac);
        return ERROR_INT("baa not made", procName, 1);
    }

    if (pfpixa_v) *pfpixa_v = fpixaCreate(nlevels);
    if (pfpixa_rv) *pfpixa_rv = fpixaCreate(nlevels);
    for (i = 0; i < nlevels; i++) {
        boxa = boxaaGetBoxa(baa, i, L_CLONE);
        size = 1 << i;
        n = boxaGetCount(boxa);  /* n == size * size */
        if (pfpixa_v) fpixv = fpixCreate(size, size);
        if (pfpixa_rv) fpixrv = fpixCreate(size, size);
        for (j = 0; j < n; j++) {
            box = boxaGetBox(boxa, j, L_CLONE);
            pixVarianceInRectangle(pixs, box, pix_mac, dpix_msac, &var, &rvar);
            if (pfpixa_v) fpixSetPixel(fpixv, j % size, j / size, var);
            if (pfpixa_rv) fpixSetPixel(fpixrv, j % size, j / size, rvar);
            boxDestroy(&box);
        }
        if (pfpixa_v) fpixaAddFPix(*pfpixa_v, fpixv, L_INSERT);
        if (pfpixa_rv) fpixaAddFPix(*pfpixa_rv, fpixrv, L_INSERT);
        boxaDestroy(&boxa);
    }

    pixDestroy(&pix_mac);
    dpixDestroy(&dpix_msac);
    boxaaDestroy(&baa);
    return 0;
}