Exemple #1
0
/*!
 *  boxOverlapFraction()
 *
 *      Input:  box1, box2 (two boxes)
 *              &fract (<return> the fraction of box2 overlapped by box1)
 *      Return: 0 if OK, 1 on error.
 *
 *  Notes:
 *      (1) The result depends on the order of the input boxes,
 *          because the overlap is taken as a fraction of box2.
 */
l_int32
boxOverlapFraction(BOX        *box1,
                   BOX        *box2,
                   l_float32  *pfract)
{
l_int32  w2, h2, w, h;
BOX     *boxo;

    PROCNAME("boxOverlapFraction");

    if (!pfract)
        return ERROR_INT("&fract not defined", procName, 1);
    *pfract = 0.0;
    if (!box1)
        return ERROR_INT("box1 not defined", procName, 1);
    if (!box2)
        return ERROR_INT("box2 not defined", procName, 1);

    if ((boxo = boxOverlapRegion(box1, box2)) == NULL)  /* no overlap */
        return 0;

    boxGetGeometry(box2, NULL, NULL, &w2, &h2);
    boxGetGeometry(boxo, NULL, NULL, &w, &h);
    *pfract = (l_float32)(w * h) / (l_float32)(w2 * h2);
    boxDestroy(&boxo);
    return 0;
}
Exemple #2
0
void Java_com_example_ocr_Pixa_nativeMergeAndReplacePix(JNIEnv *env, jclass clazz,
                                                                         jint nativePixa,
                                                                         jint indexA, jint indexB) {
  PIXA *pixa = (PIXA *) nativePixa;

  l_int32 op;
  l_int32 x, y, w, h;
  l_int32 dx, dy, dw, dh;
  PIX *pixs, *pixd;
  BOX *boxA, *boxB, *boxd;

  boxA = pixaGetBox(pixa, indexA, L_CLONE);
  boxB = pixaGetBox(pixa, indexB, L_CLONE);
  boxd = boxBoundingRegion(boxA, boxB);

  boxGetGeometry(boxd, &x, &y, &w, &h);
  pixd = pixCreate(w, h, 1);

  op = PIX_SRC | PIX_DST;

  pixs = pixaGetPix(pixa, indexA, L_CLONE);
  boxGetGeometry(boxA, &dx, &dy, &dw, &dh);
  pixRasterop(pixd, dx - x, dy - y, dw, dh, op, pixs, 0, 0);
  pixDestroy(&pixs);
  boxDestroy(&boxA);

  pixs = pixaGetPix(pixa, indexB, L_CLONE);
  boxGetGeometry(boxB, &dx, &dy, &dw, &dh);
  pixRasterop(pixd, dx - x, dy - y, dw, dh, op, pixs, 0, 0);
  pixDestroy(&pixs);
  boxDestroy(&boxB);

  pixaReplacePix(pixa, indexA, pixd, boxd);

}
Exemple #3
0
/*!
 *  boxaGetCoverage()
 *
 *      Input:  boxa
 *              wc, hc (dimensions of overall clipping rectangle with UL
 *                      corner at (0, 0) that is covered by the boxes.
 *              exactflag (1 for guaranteeing an exact result; 0 for getting
 *                         an exact result only if the boxes do not overlap)
 *              &fract (<return> sum of box area as fraction of w * h)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) The boxes in boxa are clipped to the input rectangle.
 *      (2) * When @exactflag == 1, we generate a 1 bpp pix of size
 *            wc x hc, paint all the boxes black, and count the fg pixels.
 *            This can take 1 msec on a large page with many boxes.
 *          * When @exactflag == 0, we clip each box to the wc x hc region
 *            and sum the resulting areas.  This is faster.
 *          * The results are the same when none of the boxes overlap
 *            within the wc x hc region.
 */
l_int32
boxaGetCoverage(BOXA       *boxa,
                l_int32     wc,
                l_int32     hc,
                l_int32     exactflag,
                l_float32  *pfract)
{
l_int32  i, n, x, y, w, h, sum;
BOX     *box, *boxc;
PIX     *pixt;

    PROCNAME("boxaGetCoverage");

    if (!pfract)
        return ERROR_INT("&fract not defined", procName, 1);
    *pfract = 0.0;
    if (!boxa)
        return ERROR_INT("boxa not defined", procName, 1);

    n = boxaGetCount(boxa);
    if (n == 0)
        return ERROR_INT("no boxes in boxa", procName, 1);

    if (exactflag == 0) {  /* quick and dirty */
        sum = 0;
        for (i = 0; i < n; i++) {
            box = boxaGetBox(boxa, i, L_CLONE);
            if ((boxc = boxClipToRectangle(box, wc, hc)) != NULL) {
                boxGetGeometry(boxc, NULL, NULL, &w, &h);
                sum += w * h;
                boxDestroy(&boxc);
            }
            boxDestroy(&box);
        }
    }
    else {  /* slower and exact */
        pixt = pixCreate(wc, hc, 1);
        for (i = 0; i < n; i++) {
            box = boxaGetBox(boxa, i, L_CLONE);
            boxGetGeometry(box, &x, &y, &w, &h);
            pixRasterop(pixt, x, y, w, h, PIX_SET, NULL, 0, 0);
            boxDestroy(&box);
        }
        pixCountPixels(pixt, &sum, NULL);
        pixDestroy(&pixt);
    }

    *pfract = (l_float32)sum / (l_float32)(wc * hc);
    return 0;
}
Exemple #4
0
/*!
 *  boxRotateOrth()
 *
 *      Input:  box
 *              w, h (of image in which the box is embedded)
 *              rotation (0 = noop, 1 = 90 deg, 2 = 180 deg, 3 = 270 deg;
 *                        all rotations are clockwise)
 *      Return: boxd, or null on error
 *
 *  Notes:
 *      (1) Rotate the image with the embedded box by the specified amount.
 *      (2) After rotation, the rotated box is always measured with
 *          respect to the UL corner of the image.
 */
BOX *
boxRotateOrth(BOX     *box,
              l_int32  w,
              l_int32  h,
              l_int32  rotation)
{
l_int32  bx, by, bw, bh, xdist, ydist;

    PROCNAME("boxRotateOrth");

    if (!box)
        return (BOX *)ERROR_PTR("box not defined", procName, NULL);
    if (rotation == 0)
        return boxCopy(box);
    if (rotation < 1 || rotation > 3)
        return (BOX *)ERROR_PTR("rotation not in {0,1,2,3}", procName, NULL);

    boxGetGeometry(box, &bx, &by, &bw, &bh);
    if (bw <= 0 || bh <= 0)  /* invalid */
        return boxCreate(0, 0, 0, 0);
    ydist = h - by - bh;  /* below box */
    xdist = w - bx - bw;  /* to right of box */
    if (rotation == 1)   /* 90 deg cw */
        return boxCreate(ydist, bx, bh, bw);
    else if (rotation == 2)  /* 180 deg cw */
        return boxCreate(xdist, ydist, bw, bh);
    else  /*  rotation == 3, 270 deg cw */
        return boxCreate(by, xdist, bh, bw);
}
/*!
 *  boxaaWriteStream()
 *
 *      Input: stream
 *             boxaa
 *      Return: 0 if OK, 1 on error
 */
l_int32
boxaaWriteStream(FILE   *fp,
                 BOXAA  *baa)
{
l_int32  n, i, x, y, w, h;
BOX     *box;
BOXA    *boxa;

    PROCNAME("boxaaWriteStream");

    if (!fp)
        return ERROR_INT("stream not defined", procName, 1);
    if (!baa)
        return ERROR_INT("baa not defined", procName, 1);

    n = boxaaGetCount(baa);
    fprintf(fp, "\nBoxaa Version %d\n", BOXAA_VERSION_NUMBER);
    fprintf(fp, "Number of boxa = %d\n", n);

    for (i = 0; i < n; i++) {
        if ((boxa = boxaaGetBoxa(baa, i, L_CLONE)) == NULL)
            return ERROR_INT("boxa not found", procName, 1);
        boxaGetExtent(boxa, NULL, NULL, &box);
        boxGetGeometry(box, &x, &y, &w, &h);
        fprintf(fp, "\nBoxa[%d] extent: x = %d, y = %d, w = %d, h = %d",
                i, x, y, w, h);
        boxaWriteStream(fp, boxa);
        boxDestroy(&box);
        boxaDestroy(&boxa);
    }
    return 0;
}
Exemple #6
0
/*!
 * \brief   boxConvertToPta()
 *
 * \param[in]    box
 * \param[in]    ncorners   2 or 4 for the representation of the box
 * \return  pta with %ncorners points, or NULL on error
 *
 * <pre>
 * Notes:
 *      (1) If ncorners == 2, we select the UL and LR corners.
 *          Otherwise we save all 4 corners in this order: UL, UR, LL, LR.
 * </pre>
 */
PTA *
boxConvertToPta(BOX     *box,
                l_int32  ncorners)
{
l_int32  x, y, w, h;
PTA     *pta;

    PROCNAME("boxConvertToPta");

    if (!box)
        return (PTA *)ERROR_PTR("box not defined", procName, NULL);
    if (ncorners != 2 && ncorners != 4)
        return (PTA *)ERROR_PTR("ncorners not 2 or 4", procName, NULL);

    if ((pta = ptaCreate(ncorners)) == NULL)
        return (PTA *)ERROR_PTR("pta not made", procName, NULL);
    boxGetGeometry(box, &x, &y, &w, &h);
    ptaAddPt(pta, x, y);
    if (ncorners == 2) {
        ptaAddPt(pta, x + w - 1, y + h - 1);
    } else {
        ptaAddPt(pta, x + w - 1, y);
        ptaAddPt(pta, x, y + h - 1);
        ptaAddPt(pta, x + w - 1, y + h - 1);
    }

    return pta;
}
Exemple #7
0
/*!
 * \brief   boxaGetSizes()
 *
 * \param[in]    boxa
 * \param[out]   pnaw    [optional] widths of valid boxes
 * \param[out]   pnah    [optional] heights of valid boxes
 * \return  0 if OK, 1 on error
 */
l_ok
boxaGetSizes(BOXA   *boxa,
             NUMA  **pnaw,
             NUMA  **pnah)
{
l_int32  i, n, w, h;
BOX     *box;

    PROCNAME("boxaGetSizes");

    if (pnaw) *pnaw = NULL;
    if (pnah) *pnah = NULL;
    if (!pnaw && !pnah)
        return ERROR_INT("no output requested", procName, 1);
    if (!boxa)
        return ERROR_INT("boxa not defined", procName, 1);

    n = boxaGetValidCount(boxa);
    if (pnaw) *pnaw = numaCreate(n);
    if (pnah) *pnah = numaCreate(n);
    for (i = 0; i < n; i++) {
        box = boxaGetValidBox(boxa, i, L_COPY);
        if (box) {
            boxGetGeometry(box, NULL, NULL, &w, &h);
            if (pnaw) numaAddNumber(*pnaw, w);
            if (pnah) numaAddNumber(*pnah, h);
            boxDestroy(&box);
        }
    }

    return 0;
}
Exemple #8
0
/*!
 *  boxAdjustSides()
 *
 *      Input:  boxd  (<optional>; this can be null, equal to boxs,
 *                     or different from boxs)
 *              boxs  (starting box; to have sides adjusted)
 *              delleft, delright, deltop, delbot (changes in location of
 *                                                 each side)
 *      Return: boxd, or null on error or if the computed boxd has
 *              width or height <= 0.
 *
 *  Notes:
 *      (1) Set boxd == NULL to get new box; boxd == boxs for in-place;
 *          or otherwise to resize existing boxd.
 *      (2) For usage, suggest one of these:
 *               boxd = boxAdjustSides(NULL, boxs, ...);   // new
 *               boxAdjustSides(boxs, boxs, ...);          // in-place
 *               boxAdjustSides(boxd, boxs, ...);          // other
 *      (1) New box dimensions are cropped at left and top to x >= 0 and y >= 0.
 *      (2) For example, to expand in-place by 20 pixels on each side, use
 *             boxAdjustSides(box, box, -20, 20, -20, 20);
 */
BOX *
boxAdjustSides(BOX     *boxd,
               BOX     *boxs,
               l_int32  delleft,
               l_int32  delright,
               l_int32  deltop,
               l_int32  delbot)
{
l_int32  x, y, w, h, xl, xr, yt, yb, wnew, hnew;

    PROCNAME("boxAdjustSides");

    if (!boxs)
        return (BOX *)ERROR_PTR("boxs not defined", procName, NULL);

    boxGetGeometry(boxs, &x, &y, &w, &h);
    xl = L_MAX(0, x + delleft);
    yt = L_MAX(0, y + deltop);
    xr = x + w + delright;  /* one pixel beyond right edge */
    yb = y + h + delbot;    /* one pixel below bottom edge */
    wnew = xr - xl;
    hnew = yb - yt;
    
    if (wnew < 1 || hnew < 1)
        return (BOX *)ERROR_PTR("boxd has 0 area", procName, NULL);

    if (!boxd)
        return boxCreate(xl, yt, wnew, hnew);
    else {
        boxSetGeometry(boxd, xl, yt, wnew, hnew);
        return boxd;
    }
}
Exemple #9
0
/*!
 *  boxRelocateOneSide()
 *
 *      Input:  boxd (<optional>; this can be null, equal to boxs,
 *                    or different from boxs);
 *              boxs (starting box; to have one side relocated)
 *              loc (new location of the side that is changing)
 *              sideflag (L_FROM_LEFT, etc., indicating the side that moves)
 *      Return: boxd, or null on error or if the computed boxd has
 *              width or height <= 0.
 *
 *  Notes:
 *      (1) Set boxd == NULL to get new box; boxd == boxs for in-place;
 *          or otherwise to resize existing boxd.
 *      (2) For usage, suggest one of these:
 *               boxd = boxRelocateOneSide(NULL, boxs, ...);   // new
 *               boxRelocateOneSide(boxs, boxs, ...);          // in-place
 *               boxRelocateOneSide(boxd, boxs, ...);          // other
 */
BOX *
boxRelocateOneSide(BOX     *boxd,
                   BOX     *boxs,
                   l_int32  loc,
                   l_int32  sideflag)
{
l_int32  x, y, w, h;

    PROCNAME("boxRelocateOneSide");

    if (!boxs)
        return (BOX *)ERROR_PTR("boxs not defined", procName, NULL);
    if (!boxd)
        boxd = boxCopy(boxs);

    boxGetGeometry(boxs, &x, &y, &w, &h);
    if (sideflag == L_FROM_LEFT)
        boxSetGeometry(boxd, loc, -1, w + x - loc, -1);
    else if (sideflag == L_FROM_RIGHT)
        boxSetGeometry(boxd, -1, -1, loc - x + 1, -1);
    else if (sideflag == L_FROM_TOP)
        boxSetGeometry(boxd, -1, loc, -1, h + y - loc);
    else if (sideflag == L_FROM_BOTTOM)
        boxSetGeometry(boxd, -1, -1, -1, loc - y + 1);
    return boxd;
}
/*!
 *  boxaGetBoxGeometry()
 *
 *      Input:  boxa
 *              index  (to the index-th box)
 *              &x, &y, &w, &h (<optional return>; each can be null)
 *      Return: 0 if OK, 1 on error
 */
l_int32
boxaGetBoxGeometry(BOXA     *boxa,
                   l_int32   index,
                   l_int32  *px,
                   l_int32  *py,
                   l_int32  *pw,
                   l_int32  *ph)
{
BOX  *box;

    PROCNAME("boxaGetBoxGeometry");

    if (px) *px = 0;
    if (py) *py = 0;
    if (pw) *pw = 0;
    if (ph) *ph = 0;
    if (!boxa)
        return ERROR_INT("boxa not defined", procName, 1);
    if (index < 0 || index >= boxa->n)
        return ERROR_INT("index not valid", procName, 1);

    if ((box = boxaGetBox(boxa, index, L_CLONE)) == NULL)
        return ERROR_INT("box not found!", procName, 1);
    boxGetGeometry(box, px, py, pw, ph);
    boxDestroy(&box);
    return 0;
}
Exemple #11
0
/*!
 *  pixAddWithIndicator()
 *
 *      Input:  pixs (1 bpp pix from which components are added; in-place)
 *              pixa (of connected components, some of which will be put
 *                    into pixs)
 *              na (numa indicator: add components corresponding to 1s)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) This complements pixRemoveWithIndicator().   Here, the selected
 *          components are added to pixs.
 */
l_int32
pixAddWithIndicator(PIX   *pixs,
                    PIXA  *pixa,
                    NUMA  *na)
{
l_int32  i, n, ival, x, y, w, h;
BOX     *box;
PIX     *pix;

    PROCNAME("pixAddWithIndicator");

    if (!pixs)
        return ERROR_INT("pixs not defined", procName, 1);
    if (!pixa)
        return ERROR_INT("pixa not defined", procName, 1);
    if (!na)
        return ERROR_INT("na not defined", procName, 1);
    n = pixaGetCount(pixa);
    if (n != numaGetCount(na))
        return ERROR_INT("pixa and na sizes not equal", procName, 1);

    for (i = 0; i < n; i++) {
        numaGetIValue(na, i, &ival);
        if (ival == 1) {
            pix = pixaGetPix(pixa, i, L_CLONE);
            box = pixaGetBox(pixa, i, L_CLONE);
            boxGetGeometry(box, &x, &y, &w, &h);
            pixRasterop(pixs, x, y, w, h, PIX_SRC | PIX_DST, pix, 0, 0);
            boxDestroy(&box);
            pixDestroy(&pix);
        }
    }

    return 0;
}
Exemple #12
0
/*!
 *  pixMeanInRectangle()
 *
 *      Input:  pix (8 bpp)
 *              box (region to compute mean value)
 *              pixma (mean accumulator)
 *              &val (<return> mean value
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) This function is intended to be used for many rectangles
 *          on the same image.  It can find the mean within a
 *          rectangle in O(1), independent of the size of the rectangle.
 */
l_int32
pixMeanInRectangle(PIX        *pixs,
                   BOX        *box,
                   PIX        *pixma,
                   l_float32  *pval)
{
l_int32    w, h, bx, by, bw, bh;
l_uint32   val00, val01, val10, val11;
l_float32  norm;
BOX       *boxc;

    PROCNAME("pixMeanInRectangle");

    if (!pval)
        return ERROR_INT("&val not defined", procName, 1);
    *pval = 0.0;
    if (!pixs || pixGetDepth(pixs) != 8)
        return ERROR_INT("pixs not defined", procName, 1);
    if (!box)
        return ERROR_INT("box not defined", procName, 1);
    if (!pixma)
        return ERROR_INT("pixma not defined", procName, 1);

        /* Clip rectangle to image */
    pixGetDimensions(pixs, &w, &h, NULL);
    boxc = boxClipToRectangle(box, w, h);
    boxGetGeometry(boxc, &bx, &by, &bw, &bh);
    boxDestroy(&boxc);

    if (bw == 0 || bh == 0)
        return ERROR_INT("no pixels in box", procName, 1);

        /* Use up to 4 points in the accumulator */
    norm = 1.0 / (bw * bh);
    if (bx > 0 && by > 0) {
        pixGetPixel(pixma, bx + bw - 1, by + bh - 1, &val11);
        pixGetPixel(pixma, bx + bw - 1, by - 1, &val10);
        pixGetPixel(pixma, bx - 1, by + bh - 1, &val01);
        pixGetPixel(pixma, bx - 1, by - 1, &val00);
        *pval = norm * (val11 - val01 + val00 - val10);
    }
    else if (by > 0) {  /* bx == 0 */
        pixGetPixel(pixma, bw - 1, by + bh - 1, &val11);
        pixGetPixel(pixma, bw - 1, by - 1, &val10);
        *pval = norm * (val11 - val10);
    }
    else if (bx > 0) {  /* by == 0 */
        pixGetPixel(pixma, bx + bw - 1, bh - 1, &val11);
        pixGetPixel(pixma, bx - 1, bh - 1, &val01);
        *pval = norm * (val11 - val01);
    }
    else {  /* bx == 0 && by == 0 */
        pixGetPixel(pixma, bw - 1, bh - 1, &val11);
        *pval = norm * val11;
    }

    return 0;
}
/*!
 * \brief   boxaExtractSortedPattern()
 *
 * \param[in]    boxa typ. of word bounding boxes, in textline order
 * \param[in]    na   index of textline for each box in boxa
 * \return  naa NUMAA, where each numa represents one textline,
 *                   or NULL on error
 *
 * <pre>
 * Notes:
 *      (1) The input is expected to come from pixGetWordBoxesInTextlines().
 *      (2) Each numa in the output consists of an average y coordinate
 *          of the first box in the textline, followed by pairs of
 *          x coordinates representing the left and right edges of each
 *          of the boxes in the textline.
 * </pre>
 */
NUMAA *
boxaExtractSortedPattern(BOXA  *boxa,
                         NUMA  *na)
{
l_int32  index, nbox, row, prevrow, x, y, w, h;
BOX     *box;
NUMA    *nad;
NUMAA   *naa;

    PROCNAME("boxaExtractSortedPattern");

    if (!boxa)
        return (NUMAA *)ERROR_PTR("boxa not defined", procName, NULL);
    if (!na)
        return (NUMAA *)ERROR_PTR("na not defined", procName, NULL);

    naa = numaaCreate(0);
    nbox = boxaGetCount(boxa);
    if (nbox == 0)
        return naa;

    prevrow = -1;
    for (index = 0; index < nbox; index++) {
        box = boxaGetBox(boxa, index, L_CLONE);
        numaGetIValue(na, index, &row);
        if (row > prevrow) {
            if (index > 0)
                numaaAddNuma(naa, nad, L_INSERT);
            nad = numaCreate(0);
            prevrow = row;
            boxGetGeometry(box, NULL, &y, NULL, &h);
            numaAddNumber(nad, y + h / 2);
        }
        boxGetGeometry(box, &x, NULL, &w, NULL);
        numaAddNumber(nad, x);
        numaAddNumber(nad, x + w - 1);
        boxDestroy(&box);
    }
    numaaAddNuma(naa, nad, L_INSERT);

    return naa;
}
Exemple #14
0
int main(int    argc,
         char **argv)
{
char        *dirin, *dirout, *infile, *outfile, *tail;
l_int32      i, nfiles, border, x, y, w, h, xb, yb, wb, hb;
BOX         *box1, *box2;
BOXA        *boxa1, *boxa2;
PIX         *pixs, *pixt1, *pixd;
SARRAY      *safiles;
static char  mainName[] = "croptext";

    if (argc != 4)
        return ERROR_INT("Syntax: croptext dirin border dirout", mainName, 1);
    dirin = argv[1];
    border = atoi(argv[2]);
    dirout = argv[3];

    setLeptDebugOK(1);
    safiles = getSortedPathnamesInDirectory(dirin, NULL, 0, 0);
    nfiles = sarrayGetCount(safiles);

    for (i = 0; i < nfiles; i++) {
        infile = sarrayGetString(safiles, i, L_NOCOPY);
        splitPathAtDirectory(infile, NULL, &tail);
        outfile = genPathname(dirout, tail);
        pixs = pixRead(infile);
        pixt1 = pixMorphSequence(pixs, "r11 + c10.40 + o5.5 + x4", 0);
        boxa1 = pixConnComp(pixt1, NULL, 8);
        if (boxaGetCount(boxa1) == 0) {
            fprintf(stderr, "Warning: no components on page %s\n", tail);
            continue;
        }
        boxa2 = boxaSort(boxa1, L_SORT_BY_AREA, L_SORT_DECREASING, NULL);
        box1 = boxaGetBox(boxa2, 0, L_CLONE);
        boxGetGeometry(box1, &x, &y, &w, &h);
        xb = L_MAX(0, x - border);
        yb = L_MAX(0, y - border);
        wb = w + 2 * border;
        hb = h + 2 * border;
        box2 = boxCreate(xb, yb, wb, hb);
        pixd = pixClipRectangle(pixs, box2, NULL);
        pixWrite(outfile, pixd, IFF_TIFF_G4);

        pixDestroy(&pixs);
        pixDestroy(&pixt1);
        pixDestroy(&pixd);
        boxaDestroy(&boxa1);
        boxaDestroy(&boxa2);
    }

    return 0;
}
Exemple #15
0
/**********************************************************************
 * char_box_to_tbox
 *
 * Create a TBOX from a character bounding box. If nonzero, the
 * x_offset accounts for any additional padding of the word box that
 * should be taken into account.
 *
 **********************************************************************/
TBOX char_box_to_tbox(Box* char_box, TBOX word_box, int x_offset) {
  l_int32 left;
  l_int32 top;
  l_int32 width;
  l_int32 height;
  l_int32 right;
  l_int32 bottom;

  boxGetGeometry(char_box, &left, &top, &width, &height);
  left += word_box.left() - x_offset;
  right = left + width;
  top = word_box.bottom() + word_box.height() - top;
  bottom = top - height;
  return TBOX(left, bottom, right, top);
}
Exemple #16
0
/**
 * Callback for Tesseract's monitor to update progress.
 */
bool progressJavaCallback(void* progress_this, int progress, int left, int right,
		int top, int bottom) {
  native_data_t *nat = (native_data_t*)progress_this;
  if (nat->isStateValid() && nat->currentTextBox != NULL) {
    if (progress > nat->lastProgress || left != 0 || right != 0 || top != 0 || bottom != 0) {
      int x, y, width, height;
      boxGetGeometry(nat->currentTextBox, &x, &y, &width, &height);
      nat->cachedEnv->CallVoidMethod(*(nat->cachedObject), method_onProgressValues, progress,
              (jint) left, (jint) right, (jint) top, (jint) bottom,
              (jint) x, (jint) (x + width), (jint) (y + height), (jint) y);
      nat->lastProgress = progress;
    }
  }
  return true;
}
Exemple #17
0
/*!
 *  boxaaAlignBox()
 *
 *      Input:  boxaa
 *              box (to be aligned with the last of one of the boxa
 *                   in boxaa, if possible)
 *              delta (amount by which consecutive components can miss
 *                     in overlap and still be included in the array)
 *              &index (of boxa with best overlap, or if none match,
 *                      this is the index of the next boxa to be generated)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) This is not greedy; it finds the boxa whose last box has
 *          the biggest overlap with the input box.
 */
l_int32
boxaaAlignBox(BOXAA    *baa,
              BOX      *box,
              l_int32   delta,
              l_int32  *pindex)
{
l_int32  i, n, m, y, yt, h, ht, ovlp, maxovlp, maxindex;
BOXA    *boxa;

    PROCNAME("boxaaAlignBox");

    if (!baa)
        return ERROR_INT("baa not defined", procName, 1);
    if (!box)
        return ERROR_INT("box not defined", procName, 1);
    if (!pindex)
        return ERROR_INT("&index not defined", procName, 1);

    n = boxaaGetCount(baa);
    boxGetGeometry(box, NULL, &y, NULL, &h);
    maxovlp = -10000000;
    for (i = 0; i < n; i++) {
        boxa = boxaaGetBoxa(baa, i, L_CLONE);
        if ((m = boxaGetCount(boxa)) == 0) {
            L_WARNING("no boxes in boxa", procName);
            continue;
        }
        boxaGetBoxGeometry(boxa, m - 1, NULL, &yt, NULL, &ht);  /* last one */
        boxaDestroy(&boxa);

            /* Overlap < 0 means the components do not overlap vertically */
        if (yt >= y)
            ovlp = y + h - 1 - yt;
        else
            ovlp = yt + ht - 1 - y;
        if (ovlp > maxovlp) {
            maxovlp = ovlp;
            maxindex = i;
        }
    }

    if (maxovlp + delta >= 0)
        *pindex = maxindex;
    else
        *pindex = n;
    return 0;
}
Exemple #18
0
// Helper erases false-positive line segments from the input/output line_pix.
// 1. Since thick lines shouldn't really break up, we can eliminate some false
//    positives by marking segments that are at least kMinThickLineWidth
//    thickness, yet have a length less than min_thick_length.
// 2. Lines that don't have at least 2 intersections with other lines and have
//    a lot of neighbouring non-lines are probably not lines (perhaps arabic
//    or Hindi words, or underlines.)
// Bad line components are erased from line_pix.
// Returns the number of remaining connected components.
static int FilterFalsePositives(int resolution, Pix* nonline_pix,
                                Pix* intersection_pix, Pix* line_pix) {
  int min_thick_length = static_cast<int>(resolution * kThickLengthMultiple);
  Pixa* pixa = NULL;
  Boxa* boxa = pixConnComp(line_pix, &pixa, 8);
  // Iterate over the boxes to remove false positives.
  int nboxes = boxaGetCount(boxa);
  int remaining_boxes = nboxes;
  for (int i = 0; i < nboxes; ++i) {
    Box* box = boxaGetBox(boxa, i, L_CLONE);
    l_int32 x, y, box_width, box_height;
    boxGetGeometry(box, &x, &y, &box_width, &box_height);
    Pix* comp_pix = pixaGetPix(pixa, i, L_CLONE);
    int max_width = MaxStrokeWidth(comp_pix);
    pixDestroy(&comp_pix);
    bool bad_line = false;
    // If the length is too short to stand-alone as a line, and the box width
    // is thick enough, and the stroke width is thick enough it is bad.
    if (box_width >= kMinThickLineWidth && box_height >= kMinThickLineWidth &&
        box_width < min_thick_length && box_height < min_thick_length &&
        max_width > kMinThickLineWidth) {
      // Too thick for the length.
      bad_line = true;
    }
    if (!bad_line &&
        (intersection_pix == NULL ||
        NumTouchingIntersections(box, intersection_pix) < 2)) {
      // Test non-line density near the line.
      int nonline_count = CountPixelsAdjacentToLine(max_width, box,
                                                    nonline_pix);
      if (nonline_count > box_height * box_width * kMaxNonLineDensity)
        bad_line = true;
    }
    if (bad_line) {
      // Not a good line.
      pixClearInRect(line_pix, box);
      --remaining_boxes;
    }
    boxDestroy(&box);
  }
  pixaDestroy(&pixa);
  boxaDestroy(&boxa);
  return remaining_boxes;
}
// Given an input pix, and a box, the sides of the box are shrunk inwards until
// they bound any black pixels found within the original box.
// The function converts between tesseract coords and the pix coords assuming
// that this pix is full resolution equal in size to the original image.
// Returns an empty box if there are no black pixels in the source box.
static TBOX BoundsWithinBox(Pix* pix, const TBOX& box) {
  int im_height = pixGetHeight(pix);
  Box* input_box = boxCreate(box.left(), im_height - box.top(),
                             box.width(), box.height());
  Box* output_box = NULL;
  pixClipBoxToForeground(pix, input_box, NULL, &output_box);
  TBOX result_box;
  if (output_box != NULL) {
    l_int32 x, y, width, height;
    boxGetGeometry(output_box, &x, &y, &width, &height);
    result_box.set_left(x);
    result_box.set_right(x + width);
    result_box.set_top(im_height - y);
    result_box.set_bottom(result_box.top() - height);
    boxDestroy(&output_box);
  }
  boxDestroy(&input_box);
  return result_box;
}
Exemple #20
0
/*!
 *  boxaGetValidBox()
 *
 *      Input:  boxa
 *              index  (to the index-th box)
 *              accessflag  (L_COPY or L_CLONE)
 *      Return: box, or null if box is not valid or on error
 *
 *  Notes:
 *      (1) This returns NULL for an invalid box in a boxa.
 *          For a box to be valid, both the width and height must be > 0.
 *      (2) We allow invalid boxes, with w = 0 or h = 0, as placeholders
 *          in boxa for which the index of the box in the boxa is important.
 *          This is an atypical situation; usually you want to put only
 *          valid boxes in a boxa.
 */
BOX *
boxaGetValidBox(BOXA    *boxa,
                l_int32  index,
                l_int32  accessflag)
{
l_int32  w, h;
BOX     *box;

    PROCNAME("boxaGetValidBox");

    if (!boxa)
        return (BOX *)ERROR_PTR("boxa not defined", procName, NULL);

    if ((box = boxaGetBox(boxa, index, accessflag)) == NULL)
        return (BOX *)ERROR_PTR("box not returned", procName, NULL);
    boxGetGeometry(box, NULL, NULL, &w, &h);
    if (w <= 0 || h <= 0)  /* not valid, but not necessarily an error */
        boxDestroy(&box);
    return box;
}
Exemple #21
0
/*!
 *  boxContainsPt()
 *
 *      Input:  box
 *              x, y (a point)
 *              &contains (<return> 1 if box contains point; 0 otherwise)
 *      Return: 0 if OK, 1 on error.
 */
l_int32
boxContainsPt(BOX       *box,
              l_float32  x,
              l_float32  y,
              l_int32   *pcontains)
{
l_int32  bx, by, bw, bh;

    PROCNAME("boxContainsPt");

    if (!pcontains)
        return ERROR_INT("&contains not defined", procName, 1);
    *pcontains = 0;
    if (!box)
        return ERROR_INT("&box not defined", procName, 1);
    boxGetGeometry(box, &bx, &by, &bw, &bh);
    if (x >= bx && x < bx + bw && y >= by && y < by + bh)
        *pcontains = 1;
    return 0;
}
Exemple #22
0
/*!
 *  boxGetCenter()
 *
 *      Input:  box
 *              &cx, &cy (<return> location of center of box)
 *      Return  0 if OK, 1 on error
 */
l_int32
boxGetCenter(BOX        *box,
             l_float32  *pcx,
             l_float32  *pcy)
{
l_int32  x, y, w, h;

    PROCNAME("boxGetCenter");

    if (!pcx || !pcy)
        return ERROR_INT("&cx, &cy not both defined", procName, 1);
    *pcx = *pcy = 0;
    if (!box)
        return ERROR_INT("box not defined", procName, 1);
    boxGetGeometry(box, &x, &y, &w, &h);
    *pcx = (l_float32)(x + 0.5 * w);
    *pcy = (l_float32)(y + 0.5 * h);

    return 0;
}
Exemple #23
0
jboolean Java_com_googlecode_leptonica_android_Box_nativeGetGeometry(JNIEnv *env, jclass clazz, jint nativeBox,
                                                         jintArray dimensions) {
  LOGV(__FUNCTION__);

  BOX *box = (BOX *) nativeBox;
  jint *dimensionArray = env->GetIntArrayElements(dimensions, NULL);
  l_int32 x, y, w, h;

  if (boxGetGeometry(box, &x, &y, &w, &h)) {
    return JNI_FALSE;
  }

  dimensionArray[0] = x;
  dimensionArray[1] = y;
  dimensionArray[2] = w;
  dimensionArray[3] = h;

  env->ReleaseIntArrayElements(dimensions, dimensionArray, 0);

  return JNI_TRUE;
}
/*!
 *  pixaGetBoxGeometry()
 *
 *      Input:  pixa
 *              index  (to the index-th box)
 *              &x, &y, &w, &h (<optional return>; each can be null)
 *      Return: 0 if OK, 1 on error
 */
l_int32
pixaGetBoxGeometry(PIXA     *pixa,
                   l_int32   index,
                   l_int32  *px,
                   l_int32  *py,
                   l_int32  *pw,
                   l_int32  *ph)
{
BOX  *box;

    PROCNAME("pixaGetBoxGeometry");

    if (!pixa)
        return ERROR_INT("pixa not defined", procName, 1);
    if (index < 0 || index >= pixa->n)
        return ERROR_INT("index not valid", procName, 1);

    if ((box = pixaGetBox(pixa, index, L_CLONE)) == NULL)
        return ERROR_INT("box not found!", procName, 1);
    boxGetGeometry(box, px, py, pw, ph);
    boxDestroy(&box);
    return 0;
}
Exemple #25
0
// Returns the number of black pixels found in the box made by adding the line
// width to both sides of the line bounding box. (Increasing the smallest
// dimension of the bounding box.)
static int CountPixelsAdjacentToLine(int line_width, Box* line_box,
                                     Pix* nonline_pix) {
  l_int32 x, y, box_width, box_height;
  boxGetGeometry(line_box, &x, &y, &box_width, &box_height);
  if (box_width > box_height) {
    // horizontal line.
    int bottom = MIN(pixGetHeight(nonline_pix), y + box_height + line_width);
    y = MAX(0, y - line_width);
    box_height = bottom - y;
  } else {
    // Vertical line.
    int right = MIN(pixGetWidth(nonline_pix), x + box_width + line_width);
    x = MAX(0, x - line_width);
    box_width = right - x;
  }
  Box* box = boxCreate(x, y, box_width, box_height);
  Pix* rect_pix = pixClipRectangle(nonline_pix, box, NULL);
  boxDestroy(&box);
  l_int32 result;
  pixCountPixels(rect_pix, &result, NULL);
  pixDestroy(&rect_pix);
  return result;
}
Exemple #26
0
/*!
 *  pixVarianceInRectangle()
 *
 *      Input:  pix (8 bpp)
 *              box (region to compute variance and/or root variance)
 *              pix_ma (mean accumulator)
 *              dpix_msa (mean square accumulator)
 *              &var (<optional return> variance)
 *              &rvar (<optional return> root variance)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) This function is intended to be used for many rectangles
 *          on the same image.  It can find the variance and/or the
 *          square root of the variance within a rectangle in O(1),
 *          independent of the size of the rectangle.
 */
l_int32
pixVarianceInRectangle(PIX *pixs,
                       BOX *box,
                       PIX *pix_ma,
                       DPIX *dpix_msa,
                       l_float32 *pvar,
                       l_float32 *prvar) {
    l_int32 w, h, bx, by, bw, bh;
    l_uint32 val00, val01, val10, val11;
    l_float64 dval00, dval01, dval10, dval11, mval, msval, var, norm;
    BOX *boxc;

    PROCNAME("pixVarianceInRectangle");

    if (!pvar && !prvar)
        return ERROR_INT("neither &var nor &rvar defined", procName, 1);
    if (pvar) *pvar = 0.0;
    if (prvar) *prvar = 0.0;
    if (!pixs || pixGetDepth(pixs) != 8)
        return ERROR_INT("pixs not defined", procName, 1);
    if (!box)
        return ERROR_INT("box not defined", procName, 1);
    if (!pix_ma)
        return ERROR_INT("pix_ma not defined", procName, 1);
    if (!dpix_msa)
        return ERROR_INT("dpix_msa not defined", procName, 1);

    /* Clip rectangle to image */
    pixGetDimensions(pixs, &w, &h, NULL);
    boxc = boxClipToRectangle(box, w, h);
    boxGetGeometry(boxc, &bx, &by, &bw, &bh);
    boxDestroy(&boxc);

    if (bw == 0 || bh == 0)
        return ERROR_INT("no pixels in box", procName, 1);

    /* Use up to 4 points in the accumulators */
    norm = 1.0 / (bw * bh);
    if (bx > 0 && by > 0) {
        pixGetPixel(pix_ma, bx + bw - 1, by + bh - 1, &val11);
        pixGetPixel(pix_ma, bx + bw - 1, by - 1, &val10);
        pixGetPixel(pix_ma, bx - 1, by + bh - 1, &val01);
        pixGetPixel(pix_ma, bx - 1, by - 1, &val00);
        dpixGetPixel(dpix_msa, bx + bw - 1, by + bh - 1, &dval11);
        dpixGetPixel(dpix_msa, bx + bw - 1, by - 1, &dval10);
        dpixGetPixel(dpix_msa, bx - 1, by + bh - 1, &dval01);
        dpixGetPixel(dpix_msa, bx - 1, by - 1, &dval00);
        mval = norm * (val11 - val01 + val00 - val10);
        msval = norm * (dval11 - dval01 + dval00 - dval10);
        var = (msval - mval * mval);
        if (pvar) *pvar = (l_float32) var;
        if (prvar) *prvar = (l_float32)(sqrt(var));
    } else if (by > 0) {  /* bx == 0 */
        pixGetPixel(pix_ma, bw - 1, by + bh - 1, &val11);
        pixGetPixel(pix_ma, bw - 1, by - 1, &val10);
        dpixGetPixel(dpix_msa, bw - 1, by + bh - 1, &dval11);
        dpixGetPixel(dpix_msa, bw - 1, by - 1, &dval10);
        mval = norm * (val11 - val10);
        msval = norm * (dval11 - dval10);
        var = (msval - mval * mval);
        if (pvar) *pvar = (l_float32) var;
        if (prvar) *prvar = (l_float32)(sqrt(var));
    } else if (bx > 0) {  /* by == 0 */
        pixGetPixel(pix_ma, bx + bw - 1, bh - 1, &val11);
        pixGetPixel(pix_ma, bx - 1, bh - 1, &val01);
        dpixGetPixel(dpix_msa, bx + bw - 1, bh - 1, &dval11);
        dpixGetPixel(dpix_msa, bx - 1, bh - 1, &dval01);
        mval = norm * (val11 - val01);
        msval = norm * (dval11 - dval01);
        var = (msval - mval * mval);
        if (pvar) *pvar = (l_float32) var;
        if (prvar) *prvar = (l_float32)(sqrt(var));
    } else {  /* bx == 0 && by == 0 */
        pixGetPixel(pix_ma, bw - 1, bh - 1, &val11);
        dpixGetPixel(dpix_msa, bw - 1, bh - 1, &dval11);
        mval = norm * val11;
        msval = norm * dval11;
        var = (msval - mval * mval);
        if (pvar) *pvar = (l_float32) var;
        if (prvar) *prvar = (l_float32)(sqrt(var));
    }

    return 0;
}
/*!
 *  pixSetSelectCmap()
 *
 *      Input:  pixs (1, 2, 4 or 8 bpp, with colormap)
 *              box (<optional> region to set color; can be NULL)
 *              sindex (colormap index of pixels to be changed)
 *              rval, gval, bval (new color to paint)
 *      Return: 0 if OK, 1 on error
 *
 *  Note:
 *      (1) This is an in-place operation.
 *      (2) It sets all pixels in region that have the color specified
 *          by the colormap index 'sindex' to the new color.
 *      (3) sindex must be in the existing colormap; otherwise an
 *          error is returned.
 *      (4) If the new color exists in the colormap, it is used;
 *          otherwise, it is added to the colormap.  If it cannot be
 *          added because the colormap is full, an error is returned.
 *      (5) If box is NULL, applies function to the entire image; otherwise,
 *          clips the operation to the intersection of the box and pix.
 *      (6) An DC of use would be to set to a specific color all
 *          the light (background) pixels within a certain region of
 *          a 3-level 2 bpp image, while leaving light pixels outside
 *          this region unchanged.
 */
l_int32
pixSetSelectCmap(PIX     *pixs,
                 BOX     *box,
                 l_int32  sindex,
                 l_int32  rval,
                 l_int32  gval,
                 l_int32  bval)
{
l_int32    i, j, w, h, d, n, x1, y1, x2, y2, bw, bh, val, wpls;
l_int32    index;  /* of new color to be set */
l_uint32  *lines, *datas;
PIXCMAP   *cmap;

    PROCNAME("pixSetSelectCmap");

    if (!pixs)
        return ERROR_INT("pixs not defined", procName, 1);
    if ((cmap = pixGetColormap(pixs)) == NULL)
        return ERROR_INT("no colormap", procName, 1);
    d = pixGetDepth(pixs);
    if (d != 1 && d != 2 && d != 4 && d != 8)
        return ERROR_INT("depth not in {1,2,4,8}", procName, 1);

        /* Add new color if necessary; get index of this color in cmap */
    n = pixcmapGetCount(cmap);
    if (sindex >= n)
        return ERROR_INT("sindex too large; no cmap entry", procName, 1);
    if (pixcmapGetIndex(cmap, rval, gval, bval, &index)) { /* not found */
        if (pixcmapAddColor(cmap, rval, gval, bval))
            return ERROR_INT("error adding cmap entry", procName, 1);
        else
            index = n;  /* we've added one color */
    }

        /* Determine the region of substitution */
    pixGetDimensions(pixs, &w, &h, NULL);
    if (!box) {
        x1 = y1 = 0;
        x2 = w;
        y2 = h;
    } else {
        boxGetGeometry(box, &x1, &y1, &bw, &bh);
        x2 = x1 + bw - 1;
        y2 = y1 + bh - 1;
    }

        /* Replace pixel value sindex by index in the region */
    datas = pixGetData(pixs);
    wpls = pixGetWpl(pixs);
    for (i = y1; i <= y2; i++) {
        if (i < 0 || i >= h)  /* clip */
            continue;
        lines = datas + i * wpls;
        for (j = x1; j <= x2; j++) {
            if (j < 0 || j >= w)  /* clip */
                continue;
            switch (d) {
            case 1:
                val = GET_DATA_BIT(lines, j);
                if (val == sindex) {
                    if (index == 0)
                        CLEAR_DATA_BIT(lines, j);
                    else
                        SET_DATA_BIT(lines, j);
                }
                break;
            case 2:
                val = GET_DATA_DIBIT(lines, j);
                if (val == sindex)
                    SET_DATA_DIBIT(lines, j, index);
                break;
            case 4:
                val = GET_DATA_QBIT(lines, j);
                if (val == sindex)
                    SET_DATA_QBIT(lines, j, index);
                break;
            case 8:
                val = GET_DATA_BYTE(lines, j);
                if (val == sindex)
                    SET_DATA_BYTE(lines, j, index);
                break;
            default:
                return ERROR_INT("depth not in {1,2,4,8}", procName, 1);
            }
        }
    }

    return 0;
}
/*!
 *  pixColorGrayRegionsCmap()
 *
 *      Input:  pixs (8 bpp, with colormap)
 *              boxa (of regions in which to apply color)
 *              type (L_PAINT_LIGHT, L_PAINT_DARK)
 *              rval, gval, bval (target color)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) This is an in-place operation.
 *      (2) If type == L_PAINT_LIGHT, it colorizes non-black pixels,
 *          preserving antialiasing.
 *          If type == L_PAINT_DARK, it colorizes non-white pixels,
 *          preserving antialiasing.  See pixColorGrayCmap() for details.
 *      (3) This can also be called through pixColorGrayRegions().
 *      (4) This increases the colormap size by the number of
 *          different gray (non-black or non-white) colors in the
 *          selected regions of pixs.  If there is not enough room in
 *          the colormap for this expansion, it returns 1 (error),
 *          and the caller should check the return value.
 *      (5) Because two boxes in the boxa can overlap, pixels that
 *          are colorized in the first box must be excluded in the
 *          second because their value exceeds the size of the map.
 */
l_int32
pixColorGrayRegionsCmap(PIX     *pixs,
                        BOXA    *boxa,
                        l_int32  type,
                        l_int32  rval,
                        l_int32  gval,
                        l_int32  bval)
{
l_int32    i, j, k, w, h, n, nc, x1, y1, x2, y2, bw, bh, wpl;
l_int32    val, nval;
l_int32   *map;
l_uint32  *line, *data;
BOX       *box;
NUMA      *na;
PIXCMAP   *cmap;

    PROCNAME("pixColorGrayRegionsCmap");

    if (!pixs)
        return ERROR_INT("pixs not defined", procName, 1);
    if (!boxa)
        return ERROR_INT("boxa not defined", procName, 1);
    if ((cmap = pixGetColormap(pixs)) == NULL)
        return ERROR_INT("no colormap", procName, 1);
    if (pixGetDepth(pixs) != 8)
        return ERROR_INT("depth not 8 bpp", procName, 1);
    if (type != L_PAINT_DARK && type != L_PAINT_LIGHT)
        return ERROR_INT("invalid type", procName, 1);

    nc = pixcmapGetCount(cmap);
    if (addColorizedGrayToCmap(cmap, type, rval, gval, bval, &na))
        return ERROR_INT("no room; cmap full", procName, 1);
    map = numaGetIArray(na);
    numaDestroy(&na);
    if (!map)
        return ERROR_INT("map not made", procName, 1);

    pixGetDimensions(pixs, &w, &h, NULL);
    data = pixGetData(pixs);
    wpl = pixGetWpl(pixs);
    n = boxaGetCount(boxa);
    for (k = 0; k < n; k++) {
        box = boxaGetBox(boxa, k, L_CLONE);
        boxGetGeometry(box, &x1, &y1, &bw, &bh);
        x2 = x1 + bw - 1;
        y2 = y1 + bh - 1;

            /* Remap gray pixels in the region */
        for (i = y1; i <= y2; i++) {
            if (i < 0 || i >= h)  /* clip */
                continue;
            line = data + i * wpl;
            for (j = x1; j <= x2; j++) {
                if (j < 0 || j >= w)  /* clip */
                    continue;
                val = GET_DATA_BYTE(line, j);
                if (val >= nc) continue;  /* from overlapping b.b. */
                nval = map[val];
                if (nval != 256)
                    SET_DATA_BYTE(line, j, nval);
            }
        }
        boxDestroy(&box);
    }

    FREE(map);
    return 0;
}
int main(int    argc,
         char **argv)
{
    l_int32      i, j;
    l_int32      w, h, bw, bh, wpls, rval, gval, bval, same;
    l_uint32     pixel;
    l_uint32    *lines, *datas;
    l_float32    sum1, sum2, ave1, ave2, ave3, ave4, diff1, diff2;
    l_float32    var1, var2, var3;
    BOX         *box1, *box2;
    NUMA        *na, *na1, *na2, *na3, *na4;
    PIX         *pix, *pixs, *pix1, *pix2, *pix3, *pix4, *pix5, *pixg, *pixd;
    PIXA        *pixa;
    static char  mainName[] = "numa2_reg";

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

    lept_mkdir("lept/numa2");

    /* -------------------------------------------------------------------*
     *                         Numa-windowed stats                        *
     * -------------------------------------------------------------------*/
#if  DO_ALL
    na = numaRead("lyra.5.na");
    numaWindowedStats(na, 5, &na1, &na2, &na3, &na4);
    gplotSimple1(na, GPLOT_PNG, "/tmp/lept/numa2/lyra6", "Original");
    gplotSimple1(na1, GPLOT_PNG, "/tmp/lept/numa2/lyra7", "Mean");
    gplotSimple1(na2, GPLOT_PNG, "/tmp/lept/numa2/lyra8", "Mean Square");
    gplotSimple1(na3, GPLOT_PNG, "/tmp/lept/numa2/lyra9", "Variance");
    gplotSimple1(na4, GPLOT_PNG, "/tmp/lept/numa2/lyra10", "RMS Difference");
    pixa = pixaCreate(5);
    pix1 = pixRead("/tmp/lept/numa2/lyra6.png");
    pix2 = pixRead("/tmp/lept/numa2/lyra7.png");
    pix3 = pixRead("/tmp/lept/numa2/lyra8.png");
    pix4 = pixRead("/tmp/lept/numa2/lyra9.png");
    pix5 = pixRead("/tmp/lept/numa2/lyra10.png");
    pixaAddPix(pixa, pix1, L_INSERT);
    pixaAddPix(pixa, pix2, L_INSERT);
    pixaAddPix(pixa, pix3, L_INSERT);
    pixaAddPix(pixa, pix4, L_INSERT);
    pixaAddPix(pixa, pix5, L_INSERT);
    pixd = pixaDisplayTiledInRows(pixa, 32, 1500, 1.0, 0, 20, 2);
    pixDisplay(pixd, 100, 0);
    pixWrite("/tmp/lept/numa2/window.png", pixd, IFF_PNG);
    pixDestroy(&pixd);
    pixaDestroy(&pixa);
    numaDestroy(&na);
    numaDestroy(&na1);
    numaDestroy(&na2);
    numaDestroy(&na3);
    numaDestroy(&na4);
#endif

    /* -------------------------------------------------------------------*
     *                        Extraction on a line                        *
     * -------------------------------------------------------------------*/
#if  DO_ALL
    /* First, make a pretty image */
    w = h = 200;
    pixs = pixCreate(w, h, 32);
    wpls = pixGetWpl(pixs);
    datas = pixGetData(pixs);
    for (i = 0; i < 200; i++) {
        lines = datas + i * wpls;
        for (j = 0; j < 200; j++) {
            rval = (l_int32)((255. * j) / w + (255. * i) / h);
            gval = (l_int32)((255. * 2 * j) / w + (255. * 2 * i) / h) % 255;
            bval = (l_int32)((255. * 4 * j) / w + (255. * 4 * i) / h) % 255;
            composeRGBPixel(rval, gval, bval, &pixel);
            lines[j] = pixel;
        }
    }
    pixg = pixConvertTo8(pixs, 0);  /* and a grayscale version */
    pixWrite("/tmp/lept/numa_pixg.png", pixg, IFF_PNG);
    pixDisplay(pixg, 450, 100);

    na1 = pixExtractOnLine(pixg, 20, 20, 180, 20, 1);
    na2 = pixExtractOnLine(pixg, 40, 30, 40, 170, 1);
    na3 = pixExtractOnLine(pixg, 20, 170, 180, 30, 1);
    na4 = pixExtractOnLine(pixg, 20, 190, 180, 10, 1);
    gplotSimple1(na1, GPLOT_PNG, "/tmp/lept/numa2/ext1", "Horizontal");
    gplotSimple1(na2, GPLOT_PNG, "/tmp/lept/numa2/ext2", "Vertical");
    gplotSimple1(na3, GPLOT_PNG, "/tmp/lept/numa2/ext3",
                 "Slightly more horizontal than vertical");
    gplotSimple1(na4, GPLOT_PNG, "/tmp/lept/numa2/ext4",
                 "Slightly more vertical than horizontal");
    pixa = pixaCreate(4);
    pix1 = pixRead("/tmp/lept/numa2/ext1.png");
    pix2 = pixRead("/tmp/lept/numa2/ext2.png");
    pix3 = pixRead("/tmp/lept/numa2/ext3.png");
    pix4 = pixRead("/tmp/lept/numa2/ext4.png");
    pixaAddPix(pixa, pix1, L_INSERT);
    pixaAddPix(pixa, pix2, L_INSERT);
    pixaAddPix(pixa, pix3, L_INSERT);
    pixaAddPix(pixa, pix4, L_INSERT);
    pixd = pixaDisplayTiledInRows(pixa, 32, 1500, 1.0, 0, 20, 2);
    pixDisplay(pixd, 100, 450);
    pixWrite("/tmp/lept/numa2/extract.png", pixd, IFF_PNG);
    pixDestroy(&pixd);
    pixaDestroy(&pixa);
    pixDestroy(&pixg);
    numaDestroy(&na1);
    numaDestroy(&na2);
    numaDestroy(&na3);
    numaDestroy(&na4);
#endif

    /* -------------------------------------------------------------------*
     *                     Row and column pixel sums                      *
     * -------------------------------------------------------------------*/
#if  DO_ALL
    /* Sum by columns in two halves (left and right) */
    pixs = pixRead("test8.jpg");
    pixGetDimensions(pixs, &w, &h, NULL);
    box1 = boxCreate(0, 0, w / 2, h);
    box2 = boxCreate(w / 2, 0, w - 2 / 2, h);
    na1 = pixAverageByColumn(pixs, box1, L_BLACK_IS_MAX);
    na2 = pixAverageByColumn(pixs, box2, L_BLACK_IS_MAX);
    numaJoin(na1, na2, 0, -1);
    na3 = pixAverageByColumn(pixs, NULL, L_BLACK_IS_MAX);
    numaSimilar(na1, na3, 0.0, &same);
    if (same)
        fprintf(stderr, "Same for columns\n");
    else
        fprintf(stderr, "Error for columns\n");
    pix = pixConvertTo32(pixs);
    pixRenderPlotFromNumaGen(&pix, na3, L_HORIZONTAL_LINE, 3, h / 2, 80, 1,
                             0xff000000);
    pixRenderPlotFromNuma(&pix, na3, L_PLOT_AT_BOT, 3, 80, 0xff000000);
    boxDestroy(&box1);
    boxDestroy(&box2);
    numaDestroy(&na1);
    numaDestroy(&na2);
    numaDestroy(&na3);

    /* Sum by rows in two halves (top and bottom) */
    box1 = boxCreate(0, 0, w, h / 2);
    box2 = boxCreate(0, h / 2, w, h - h / 2);
    na1 = pixAverageByRow(pixs, box1, L_WHITE_IS_MAX);
    na2 = pixAverageByRow(pixs, box2, L_WHITE_IS_MAX);
    numaJoin(na1, na2, 0, -1);
    na3 = pixAverageByRow(pixs, NULL, L_WHITE_IS_MAX);
    numaSimilar(na1, na3, 0.0, &same);
    if (same)
        fprintf(stderr, "Same for rows\n");
    else
        fprintf(stderr, "Error for rows\n");
    pixRenderPlotFromNumaGen(&pix, na3, L_VERTICAL_LINE, 3, w / 2, 80, 1,
                             0x00ff0000);
    pixRenderPlotFromNuma(&pix, na3, L_PLOT_AT_RIGHT, 3, 80, 0x00ff0000);
    pixDisplay(pix, 500, 200);
    boxDestroy(&box1);
    boxDestroy(&box2);
    numaDestroy(&na1);
    numaDestroy(&na2);
    numaDestroy(&na3);
    pixDestroy(&pix);

    /* Average left by rows; right by columns; compare totals */
    box1 = boxCreate(0, 0, w / 2, h);
    box2 = boxCreate(w / 2, 0, w - 2 / 2, h);
    na1 = pixAverageByRow(pixs, box1, L_WHITE_IS_MAX);
    na2 = pixAverageByColumn(pixs, box2, L_WHITE_IS_MAX);
    numaGetSum(na1, &sum1);  /* sum of averages of left box */
    numaGetSum(na2, &sum2);  /* sum of averages of right box */
    ave1 = sum1 / h;
    ave2 = 2.0 * sum2 / w;
    ave3 = 0.5 * (ave1 + ave2);  /* average over both halves */
    fprintf(stderr, "ave1 = %8.4f\n", sum1 / h);
    fprintf(stderr, "ave2 = %8.4f\n", 2.0 * sum2 / w);
    pixAverageInRect(pixs, NULL, &ave4);  /* entire image */
    diff1 = ave4 - ave3;
    diff2 = w * h * ave4 - (0.5 * w * sum1 + h * sum2);
    if (diff1 < 0.001)
        fprintf(stderr, "Average diffs are correct\n");
    else
        fprintf(stderr, "Average diffs are wrong: diff1 = %7.5f\n", diff1);
    if (diff2 < 20)  /* float-to-integer roundoff */
        fprintf(stderr, "Pixel sums are correct\n");
    else
        fprintf(stderr, "Pixel sums are in error: diff = %7.0f\n", diff2);

    /* Variance left and right halves.  Variance doesn't average
     * in a simple way, unlike pixel sums. */
    pixVarianceInRect(pixs, box1, &var1);  /* entire image */
    pixVarianceInRect(pixs, box2, &var2);  /* entire image */
    pixVarianceInRect(pixs, NULL, &var3);  /* entire image */
    fprintf(stderr, "0.5 * (var1 + var2) = %7.3f, var3 = %7.3f\n",
            0.5 * (var1 + var2), var3);
    boxDestroy(&box1);
    boxDestroy(&box2);
    numaDestroy(&na1);
    numaDestroy(&na2);
#endif

    /* -------------------------------------------------------------------*
     *                     Row and column variances                       *
     * -------------------------------------------------------------------*/
#if  DO_ALL

    /* Display variance by rows and columns */
    box1 = boxCreate(415, 0, 130, 425);
    boxGetGeometry(box1, NULL, NULL, &bw, &bh);
    na1 = pixVarianceByRow(pixs, box1);
    na2 = pixVarianceByColumn(pixs, box1);
    pix = pixConvertTo32(pixs);
    pix1 = pixCopy(NULL, pix);
    pixRenderPlotFromNumaGen(&pix, na1, L_VERTICAL_LINE, 3, 415, 100, 1,
                             0xff000000);
    pixRenderPlotFromNumaGen(&pix, na2, L_HORIZONTAL_LINE, 3, bh / 2, 100, 1,
                             0x00ff0000);
    pixRenderPlotFromNuma(&pix1, na1, L_PLOT_AT_LEFT, 3, 60, 0x00ff0000);
    pixRenderPlotFromNuma(&pix1, na1, L_PLOT_AT_MID_VERT, 3, 60, 0x0000ff00);
    pixRenderPlotFromNuma(&pix1, na1, L_PLOT_AT_RIGHT, 3, 60, 0xff000000);
    pixRenderPlotFromNuma(&pix1, na2, L_PLOT_AT_TOP, 3, 60, 0x0000ff00);
    pixRenderPlotFromNuma(&pix1, na2, L_PLOT_AT_MID_HORIZ, 3, 60, 0xff000000);
    pixRenderPlotFromNuma(&pix1, na2, L_PLOT_AT_BOT, 3, 60, 0x00ff0000);
    pixDisplay(pix, 500, 900);
    pixDisplay(pix1, 500, 1000);
    boxDestroy(&box1);
    numaDestroy(&na1);
    numaDestroy(&na2);
    pixDestroy(&pix);
    pixDestroy(&pix1);
    pixDestroy(&pixs);

    /* Again on a different image */
    pix1 = pixRead("boxedpage.jpg");
    pix2 = pixConvertTo8(pix1, 0);
    pixGetDimensions(pix2, &w, &h, NULL);
    na1 = pixVarianceByRow(pix2, NULL);
    pix3 = pixConvertTo32(pix1);
    pixRenderPlotFromNumaGen(&pix3, na1, L_VERTICAL_LINE, 3, 0, 70, 1,
                             0xff000000);
    na2 = pixVarianceByColumn(pix2, NULL);
    pixRenderPlotFromNumaGen(&pix3, na2, L_HORIZONTAL_LINE, 3, bh - 1, 70, 1,
                             0x00ff0000);
    pixDisplay(pix3, 1000, 0);
    numaDestroy(&na1);
    numaDestroy(&na2);
    pixDestroy(&pix3);

    /* Again, with an erosion */
    pix3 = pixErodeGray(pix2, 3, 21);
    pixDisplay(pix3, 1400, 0);
    na1 = pixVarianceByRow(pix3, NULL);
    pix4 = pixConvertTo32(pix1);
    pixRenderPlotFromNumaGen(&pix4, na1, L_VERTICAL_LINE, 3, 30, 70, 1,
                             0xff000000);
    na2 = pixVarianceByColumn(pix3, NULL);
    pixRenderPlotFromNumaGen(&pix4, na2, L_HORIZONTAL_LINE, 3, bh - 1, 70, 1,
                             0x00ff0000);
    pixDisplay(pix4, 1000, 550);
    numaDestroy(&na1);
    numaDestroy(&na2);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pix3);
    pixDestroy(&pix4);
#endif

    /* -------------------------------------------------------------------*
     *                    Windowed variance along a line                  *
     * -------------------------------------------------------------------*/
#if  DO_ALL
    pix1 = pixRead("boxedpage.jpg");
    pix2 = pixConvertTo8(pix1, 0);
    pixGetDimensions(pix2, &w, &h, NULL);
    pix3 = pixCopy(NULL, pix1);

    /* Plot along horizontal line */
    pixWindowedVarianceOnLine(pix2, L_HORIZONTAL_LINE, h / 2 - 30, 0,
                              w, 5, &na1);
    pixRenderPlotFromNumaGen(&pix1, na1, L_HORIZONTAL_LINE, 3, h / 2 - 30,
                             80, 1, 0xff000000);
    pixRenderPlotFromNuma(&pix3, na1, L_PLOT_AT_TOP, 3, 60, 0x00ff0000);
    pixRenderPlotFromNuma(&pix3, na1, L_PLOT_AT_BOT, 3, 60, 0x0000ff00);

    /* Plot along vertical line */
    pixWindowedVarianceOnLine(pix2, L_VERTICAL_LINE, 0.78 * w, 0,
                              h, 5, &na2);
    pixRenderPlotFromNumaGen(&pix1, na2, L_VERTICAL_LINE, 3, 0.78 * w, 60,
                             1, 0x00ff0000);
    pixRenderPlotFromNuma(&pix3, na2, L_PLOT_AT_LEFT, 3, 60, 0xff000000);
    pixRenderPlotFromNuma(&pix3, na2, L_PLOT_AT_RIGHT, 3, 60, 0x00ff0000);
    pixDisplay(pix1, 1000, 1000);
    pixDisplay(pix3, 1500, 1000);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pix3);
    numaDestroy(&na1);
    numaDestroy(&na2);
#endif
    return 0;
}
l_int32 main(int    argc,
             char **argv)
{
l_int32       bx, by, bw, bh;
l_uint32      pixval;
BOX          *box1, *box2;
BOXA         *boxa;
PIX          *pixs, *pixm, *pixd;
PIX          *pix0, *pix1, *pix2, *pix3, *pix4, *pix5, *pix6;
L_REGPARAMS  *rp;

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

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

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

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

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

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

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

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

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

    return regTestCleanup(rp);
}