void
MakeWordBoxes2(PIX          *pixs,
               l_int32       reduction,
               L_REGPARAMS  *rp)
{
l_int32  default_minwidth = 10;
l_int32  default_minheight = 10;
l_int32  default_maxwidth = 400;
l_int32  default_maxheight = 70;
l_int32  minwidth, minheight, maxwidth, maxheight;
BOXA    *boxa1, *boxa2;
NUMA    *na;
PIX     *pixd1, *pixd2;
PIXA    *pixa;

    minwidth = default_minwidth / reduction;
    minheight = default_minheight / reduction;
    maxwidth = default_maxwidth / reduction;
    maxheight = default_maxheight / reduction;

        /* Get the word boxes */
    pixGetWordsInTextlines(pixs, reduction, minwidth, minheight,
                           maxwidth, maxheight, &boxa1, &pixa, &na);
    pixaDestroy(&pixa);
    numaDestroy(&na);
    if (reduction == 1)
        boxa2 = boxaCopy(boxa1, L_CLONE);
    else
        boxa2 = boxaTransform(boxa1, 0, 0, 2.0, 2.0);
    pixd1 = pixConvertTo8(pixs, 1);
    pixRenderBoxaArb(pixd1, boxa2, 2, 255, 0, 0);
    regTestWritePixAndCheck(rp, pixd1, IFF_PNG);
    pixDisplayWithTitle(pixd1, 800, 100, NULL, rp->display);
    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);

        /* Do it again with this interface.  The result should be the same. */
    pixGetWordBoxesInTextlines(pixs, reduction, minwidth, minheight,
                               maxwidth, maxheight, &boxa1, NULL);
    if (reduction == 1)
        boxa2 = boxaCopy(boxa1, L_CLONE);
    else
        boxa2 = boxaTransform(boxa1, 0, 0, 2.0, 2.0);
    pixd2 = pixConvertTo8(pixs, 1);
    pixRenderBoxaArb(pixd2, boxa2, 2, 255, 0, 0);
    if (regTestComparePix(rp, pixd1, pixd2)) {
        L_ERROR("pix not the same", "MakeWordBoxes2");
        pixDisplayWithTitle(pixd2, 800, 100, NULL, rp->display);
    }
    pixDestroy(&pixd1);
    pixDestroy(&pixd2);
    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);
    return;
}
Beispiel #2
0
/*!
 * \brief   boxaSelectByWHRatio()
 *
 * \param[in]    boxas
 * \param[in]    ratio     width/height threshold value
 * \param[in]    relation  L_SELECT_IF_LT, L_SELECT_IF_GT,
 *                         L_SELECT_IF_LTE, L_SELECT_IF_GTE
 * \param[out]   pchanged  [optional] 1 if changed; 0 if clone returned
 * \return  boxad filtered set, or NULL on error
 *
 * <pre>
 * Notes:
 *      (1) Uses box copies in the new boxa.
 *      (2) To keep narrow components, use relation = L_SELECT_IF_LT or
 *          L_SELECT_IF_LTE.
 *          To keep wide components, use relation = L_SELECT_IF_GT or
 *          L_SELECT_IF_GTE.
 * </pre>
 */
BOXA *
boxaSelectByWHRatio(BOXA      *boxas,
                    l_float32  ratio,
                    l_int32    relation,
                    l_int32   *pchanged)
{
BOXA  *boxad;
NUMA  *na;

    PROCNAME("boxaSelectByWHRatio");

    if (pchanged) *pchanged = FALSE;
    if (!boxas)
        return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
    if (boxaGetCount(boxas) == 0) {
        L_WARNING("boxas is empty\n", procName);
        return boxaCopy(boxas, L_COPY);
    }
    if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
        relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
        return (BOXA *)ERROR_PTR("invalid relation", procName, NULL);

        /* Compute the indicator array for saving components */
    na = boxaMakeWHRatioIndicator(boxas, ratio, relation);

        /* Filter to get output */
    boxad = boxaSelectWithIndicator(boxas, na, pchanged);

    numaDestroy(&na);
    return boxad;
}
Beispiel #3
0
/*!
 *  boxaRotateOrth()
 *
 *      Input:  boxa
 *              w, h (of image in which the boxa is embedded)
 *              rotation (0 = noop, 1 = 90 deg, 2 = 180 deg, 3 = 270 deg;
 *                        all rotations are clockwise)
 *      Return: boxad, or null on error
 *
 *  Notes:
 *      (1) See boxRotateOrth() for details.
 */
BOXA *
boxaRotateOrth(BOXA    *boxas,
               l_int32  w,
               l_int32  h,
               l_int32  rotation)
{
l_int32  i, n;
BOX     *boxs, *boxd;
BOXA    *boxad;

    PROCNAME("boxaRotateOrth");

    if (!boxas)
        return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
    if (rotation == 0)
        return boxaCopy(boxas, L_COPY);
    if (rotation < 1 || rotation > 3)
        return (BOXA *)ERROR_PTR("rotation not in {0,1,2,3}", procName, NULL);

    n = boxaGetCount(boxas);
    if ((boxad = boxaCreate(n)) == NULL)
        return (BOXA *)ERROR_PTR("boxad not made", procName, NULL);
    for (i = 0; i < n; i++) {
        if ((boxs = boxaGetBox(boxas, i, L_CLONE)) == NULL)
            return (BOXA *)ERROR_PTR("boxs not found", procName, NULL);
        boxd = boxRotateOrth(boxs, w, h, rotation);
        boxDestroy(&boxs);
        boxaAddBox(boxad, boxd, L_INSERT);
    }

    return boxad;
}
/*!
 *  boxaaAddBoxa()
 *
 *      Input:  boxaa
 *              boxa     (to be added)
 *              copyflag  (L_INSERT, L_COPY, L_CLONE)
 *      Return: 0 if OK, 1 on error
 */
l_int32
boxaaAddBoxa(BOXAA   *baa,
             BOXA    *ba,
             l_int32  copyflag)
{
l_int32  n;
BOXA    *bac;

    PROCNAME("boxaaAddBoxa");

    if (!baa)
        return ERROR_INT("baa not defined", procName, 1);
    if (!ba)
        return ERROR_INT("ba not defined", procName, 1);
    if (copyflag != L_INSERT && copyflag != L_COPY && copyflag != L_CLONE)
        return ERROR_INT("invalid copyflag", procName, 1);

    if (copyflag == L_INSERT)
        bac = ba;
    else
        bac = boxaCopy(ba, copyflag);

    n = boxaaGetCount(baa);
    if (n >= baa->nalloc)
        boxaaExtendArray(baa);
    baa->boxa[n] = bac;
    baa->n++;
    return 0;
}
Beispiel #5
0
/*!
 * \brief   boxaPermuteRandom()
 *
 * \param[in]    boxad    [optional]   can be null or equal to boxas
 * \param[in]    boxas    input boxa
 * \return  boxad with boxes permuted, or NULL on error
 *
 * <pre>
 * Notes:
 *      (1) If boxad is null, make a copy of boxas and permute the copy.
 *          Otherwise, boxad must be equal to boxas, and the operation
 *          is done in-place.
 *      (2) If boxas is empty, return an empty boxad.
 *      (3) This does a random in-place permutation of the boxes,
 *          by swapping each box in turn with a random box.  The
 *          result is almost guaranteed not to have any boxes in their
 *          original position.
 *      (4) MSVC rand() has MAX_RAND = 2^15 - 1, so it will not do
 *          a proper permutation is the number of boxes exceeds this.
 * </pre>
 */
BOXA *
boxaPermuteRandom(BOXA  *boxad,
                  BOXA  *boxas)
{
l_int32  i, n, index;

    PROCNAME("boxaPermuteRandom");

    if (!boxas)
        return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL);
    if (boxad && (boxad != boxas))
        return (BOXA *)ERROR_PTR("boxad defined but in-place", procName, NULL);

    if (!boxad)
        boxad = boxaCopy(boxas, L_COPY);
    if ((n = boxaGetCount(boxad)) == 0)
        return boxad;
    index = (l_uint32)rand() % n;
    index = L_MAX(1, index);
    boxaSwapBoxes(boxad, 0, index);
    for (i = 1; i < n; i++) {
        index = (l_uint32)rand() % n;
        if (index == i) index--;
        boxaSwapBoxes(boxad, i, index);
    }

    return boxad;
}
/*!
 *  pixaaGetBoxa()
 *
 *      Input:  pixaa
 *              accesstype  (L_COPY, L_CLONE)
 *      Return: boxa, or null on error
 *
 *  Notes:
 *      (1) L_COPY returns a copy; L_CLONE returns a new reference to the boxa.
 *      (2) In both cases, invoke boxaDestroy() on the returned boxa.
 */
BOXA *
pixaaGetBoxa(PIXAA   *pixaa,
             l_int32  accesstype)
{
    PROCNAME("pixaaGetBoxa");

    if (!pixaa)
        return (BOXA *)ERROR_PTR("pixaa not defined", procName, NULL);
    if (accesstype != L_COPY && accesstype != L_CLONE)
        return (BOXA *)ERROR_PTR("invalid access type", procName, NULL);

    return boxaCopy(pixaa->boxa, accesstype);
}
/*!
 *  boxaaGetBoxa()
 *
 *      Input:  boxaa
 *              index  (to the index-th boxa)
 *              accessflag   (L_COPY or L_CLONE)
 *      Return: boxa, or null on error
 */
BOXA *
boxaaGetBoxa(BOXAA   *baa,
             l_int32  index,
             l_int32  accessflag)
{
l_int32  n;

    PROCNAME("boxaaGetBoxa");

    if (!baa)
        return (BOXA *)ERROR_PTR("baa not defined", procName, NULL);
    n = boxaaGetCount(baa);
    if (index < 0 || index >= n)
        return (BOXA *)ERROR_PTR("index not valid", procName, NULL);
    if (accessflag != L_COPY && accessflag != L_CLONE)
        return (BOXA *)ERROR_PTR("invalid accessflag", procName, NULL);

    return boxaCopy(baa->boxa[index], accessflag);
}
Beispiel #8
0
/*!
 * \brief   boxaSelectRange()
 *
 * \param[in]    boxas
 * \param[in]    first      use 0 to select from the beginning
 * \param[in]    last       use -1 to select to the end
 * \param[in]    copyflag   L_COPY, L_CLONE
 * \return  boxad, or NULL on error
 *
 * <pre>
 * Notes:
 *      (1) The copyflag specifies what we do with each box from boxas.
 *          Specifically, L_CLONE inserts a clone into boxad of each
 *          selected box from boxas.
 * </pre>
 */
BOXA *
boxaSelectRange(BOXA    *boxas,
                l_int32  first,
                l_int32  last,
                l_int32  copyflag)
{
l_int32  n, nbox, i;
BOX     *box;
BOXA    *boxad;

    PROCNAME("boxaSelectRange");

    if (!boxas)
        return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
    if (copyflag != L_COPY && copyflag != L_CLONE)
        return (BOXA *)ERROR_PTR("invalid copyflag", procName, NULL);
    if ((n = boxaGetCount(boxas)) == 0) {
        L_WARNING("boxas is empty\n", procName);
        return boxaCopy(boxas, copyflag);
    }
    first = L_MAX(0, first);
    if (last < 0) last = n - 1;
    if (first >= n)
        return (BOXA *)ERROR_PTR("invalid first", procName, NULL);
    if (last >= n) {
        L_WARNING("last = %d is beyond max index = %d; adjusting\n",
                  procName, last, n - 1);
        last = n - 1;
    }
    if (first > last)
        return (BOXA *)ERROR_PTR("first > last", procName, NULL);

    nbox = last - first + 1;
    boxad = boxaCreate(nbox);
    for (i = first; i <= last; i++) {
        box = boxaGetBox(boxas, i, copyflag);
        boxaAddBox(boxad, box, L_INSERT);
    }
    return boxad;
}
Beispiel #9
0
/*!
 * \brief   boxaSelectWithIndicator()
 *
 * \param[in]    boxas
 * \param[in]    na         indicator numa
 * \param[out]   pchanged   [optional] 1 if changed; 0 if clone returned
 * \return  boxad, or NULL on error
 *
 * <pre>
 * Notes:
 *      (1) Returns a copy of the boxa if no components are removed.
 *      (2) Uses box copies in the new boxa.
 *      (3) The indicator numa has values 0 (ignore) and 1 (accept).
 *      (4) If all indicator values are 0, the returned boxa is empty.
 * </pre>
 */
BOXA *
boxaSelectWithIndicator(BOXA     *boxas,
                        NUMA     *na,
                        l_int32  *pchanged)
{
l_int32  i, n, ival, nsave;
BOX     *box;
BOXA    *boxad;

    PROCNAME("boxaSelectWithIndicator");

    if (pchanged) *pchanged = FALSE;
    if (!boxas)
        return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
    if (!na)
        return (BOXA *)ERROR_PTR("na not defined", procName, NULL);

    nsave = 0;
    n = numaGetCount(na);
    for (i = 0; i < n; i++) {
        numaGetIValue(na, i, &ival);
        if (ival == 1) nsave++;
    }

    if (nsave == n) {
        if (pchanged) *pchanged = FALSE;
        return boxaCopy(boxas, L_COPY);
    }
    if (pchanged) *pchanged = TRUE;
    boxad = boxaCreate(nsave);
    for (i = 0; i < n; i++) {
        numaGetIValue(na, i, &ival);
        if (ival == 0) continue;
        box = boxaGetBox(boxas, i, L_COPY);
        boxaAddBox(boxad, box, L_INSERT);
    }

    return boxad;
}
Beispiel #10
0
/*!
 *  boxaaInitFull()
 *
 *      Input:  boxaa (typically empty)
 *              boxa (to be replicated into the entire ptr array)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) This initializes a boxaa by filling up the entire boxa ptr array
 *          with copies of @boxa.  Any existing boxa are destroyed.
 *          After this operation, the number of boxa is equal to
 *          the number of allocated ptrs.
 *      (2) Note that we use boxaaReplaceBox() instead of boxaInsertBox().
 *          They both have the same effect when inserting into a NULL ptr
 *          in the boxa ptr array
 *      (3) Example usage.  This function is useful to prepare for a
 *          random insertion (or replacement) of boxa into a boxaa.
 *          To randomly insert boxa into a boxaa, up to some index "max":
 *             Boxaa *baa = boxaaCreate(max);
 *               // initialize the boxa
 *             Boxa *boxa = boxaCreate(...);
 *             ...  [optionally fix with boxes]
 *             boxaaInitFull(baa, boxa);
 *          A typical use is to initialize the array with empty boxa,
 *          and to replace only a subset that must be aligned with
 *          something else, such as a pixa.
 */
l_int32
boxaaInitFull(BOXAA  *baa,
              BOXA   *boxa)
{
l_int32  i, n;
BOXA    *boxat;

    PROCNAME("boxaaInitFull");

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

    n = baa->nalloc;
    baa->n = n;
    for (i = 0; i < n; i++) {
        boxat = boxaCopy(boxa, L_COPY);
        boxaaReplaceBoxa(baa, i, boxat);
    }
    return 0;
}
Beispiel #11
0
/*!
 * \brief   boxaSelectBySize()
 *
 * \param[in]    boxas
 * \param[in]    width, height    threshold dimensions
 * \param[in]    type             L_SELECT_WIDTH, L_SELECT_HEIGHT,
 *                                L_SELECT_IF_EITHER, L_SELECT_IF_BOTH
 * \param[in]    relation         L_SELECT_IF_LT, L_SELECT_IF_GT,
 *                                L_SELECT_IF_LTE, L_SELECT_IF_GTE
 * \param[out]   pchanged         [optional] 1 if changed; 0 if clone returned
 * \return  boxad filtered set, or NULL on error
 *
 * <pre>
 * Notes:
 *      (1) The args specify constraints on the size of the
 *          components that are kept.
 *      (2) Uses box copies in the new boxa.
 *      (3) If the selection type is L_SELECT_WIDTH, the input
 *          height is ignored, and v.v.
 *      (4) To keep small components, use relation = L_SELECT_IF_LT or
 *          L_SELECT_IF_LTE.
 *          To keep large components, use relation = L_SELECT_IF_GT or
 *          L_SELECT_IF_GTE.
 * </pre>
 */
BOXA *
boxaSelectBySize(BOXA     *boxas,
                 l_int32   width,
                 l_int32   height,
                 l_int32   type,
                 l_int32   relation,
                 l_int32  *pchanged)
{
BOXA  *boxad;
NUMA  *na;

    PROCNAME("boxaSelectBySize");

    if (pchanged) *pchanged = FALSE;
    if (!boxas)
        return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
    if (boxaGetCount(boxas) == 0) {
        L_WARNING("boxas is empty\n", procName);
        return boxaCopy(boxas, L_COPY);
    }
    if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT &&
        type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH)
        return (BOXA *)ERROR_PTR("invalid type", procName, NULL);
    if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
        relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
        return (BOXA *)ERROR_PTR("invalid relation", procName, NULL);

        /* Compute the indicator array for saving components */
    if ((na =
         boxaMakeSizeIndicator(boxas, width, height, type, relation)) == NULL)
        return (BOXA *)ERROR_PTR("na not made", procName, NULL);

        /* Filter to get output */
    boxad = boxaSelectWithIndicator(boxas, na, pchanged);

    numaDestroy(&na);
    return boxad;
}
Beispiel #12
0
main(int    argc,
     char **argv)
{
l_int32     i, n;
l_float32   pi, angle, val;
BOX        *box;
BOXA       *boxa, *boxa1, *boxa2;
NUMA       *na1, *na2;
PIX        *pix, *pix1, *pix2, *pix3, *pixd;
PIXA       *pixa1, *pixa2, *pixa3, *pixa4;
static char     mainName[] = "inserttest";

#if 1
    pi = 3.1415926535;
    na1 = numaCreate(500);
    for (i = 0; i < 500; i++) {
        angle = 0.02293 * i * pi;
        val = (l_float32)sin(angle);
        numaAddNumber(na1, val);
    }
    numaWrite("/tmp/junknuma1", na1);
    na2 = numaCopy(na1);
    n = numaGetCount(na2);
    for (i = 0; i < n; i++) {
      numaGetFValue(na2, i, &val);
      numaRemoveNumber(na2, i);
      numaInsertNumber(na2, i, val);
    }
    numaWrite("/tmp/junknuma2", na2);
    numaDestroy(&na1);
    numaDestroy(&na2);
#endif

#if 1
    pix1 = pixRead("feyn.tif");
    box = boxCreate(1138, 1666, 1070, 380);
    pix2 = pixClipRectangle(pix1, box, NULL);
    boxDestroy(&box);
    boxa1 = pixConnComp(pix2, NULL, 8);
    boxaWrite("/tmp/junkboxa1", boxa1);
    boxa2 = boxaCopy(boxa1, L_COPY);
    n = boxaGetCount(boxa2);
    for (i = 0; i < n; i++) {
      box = boxaGetBox(boxa2, i, L_COPY);
      boxaRemoveBox(boxa2, i);
      boxaInsertBox(boxa2, i, box);
    }
    boxaWrite("/tmp/junkboxa2", boxa2);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);
#endif

#if 1
    pix1 = pixRead("feyn.tif");
    box = boxCreate(1138, 1666, 1070, 380);
    pix2 = pixClipRectangle(pix1, box, NULL);
    boxDestroy(&box);
    boxa = pixConnComp(pix2, &pixa1, 8);
    boxaDestroy(&boxa);
    pixaWrite("/tmp/junkpixa1", pixa1);

    pixa2 = pixaCopy(pixa1, L_COPY);
    n = pixaGetCount(pixa2);
        /* Remove and insert each one */
    for (i = 0; i < n; i++) {
      pix = pixaGetPix(pixa2, i, L_COPY);
      box = pixaGetBox(pixa2, i, L_COPY);
      pixaRemovePix(pixa2, i);
      pixaInsertPix(pixa2, i, pix, box);
    }
    pixaWrite("/tmp/junkpixa2", pixa2);

        /* Move the last to the beginning; do it n times */
    pixa3 = pixaCopy(pixa2, L_COPY);
    for (i = 0; i < n; i++) {
      pix = pixaGetPix(pixa3, n - 1, L_CLONE);
      box = pixaGetBox(pixa3, n - 1, L_CLONE);
      pixaInsertPix(pixa3, 0, pix, box);
      pixaRemovePix(pixa3, n);
    }
    pixaWrite("/tmp/junkpixa3", pixa3);

        /* Move the first one to the end; do it n times */
    pixa4 = pixaCopy(pixa3, L_COPY);
    for (i = 0; i < n; i++) {
      pix = pixaGetPix(pixa4, 0, L_CLONE);
      box = pixaGetBox(pixa4, 0, L_CLONE);
      pixaInsertPix(pixa4, n, pix, box);  /* make sure insert works at end */
      pixaRemovePix(pixa4, 0);
    }
    pixaWrite("/tmp/junkpixa4", pixa4);

    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixaDestroy(&pixa1);
    pixaDestroy(&pixa2);
    pixaDestroy(&pixa3);
    pixaDestroy(&pixa4);
#endif

    return 0;
}
Beispiel #13
0
/*!
 * \brief   pixConnCompPixa()
 *
 * \param[in]    pixs 1 bpp
 * \param[out]   ppixa pixa of each c.c.
 * \param[in]    connectivity 4 or 8
 * \return  boxa, or NULL on error
 *
 * <pre>
 * Notes:
 *      (1) This finds bounding boxes of 4- or 8-connected components
 *          in a binary image, and saves images of each c.c
 *          in a pixa array.
 *      (2) It sets up 2 temporary pix, and for each c.c. that is
 *          located in raster order, it erases the c.c. from one pix,
 *          then uses the b.b. to extract the c.c. from the two pix using
 *          an XOR, and finally erases the c.c. from the second pix.
 *      (3) A clone of the returned boxa (where all boxes in the array
 *          are clones) is inserted into the pixa.
 *      (4) If the input is valid, this always returns a boxa and a pixa.
 *          If pixs is empty, the boxa and pixa will be empty.
 * </pre>
 */
BOXA *
pixConnCompPixa(PIX     *pixs,
                PIXA   **ppixa,
                l_int32  connectivity)
{
    l_int32   h, iszero;
    l_int32   x, y, xstart, ystart;
    PIX      *pix1, *pix2, *pix3, *pix4;
    PIXA     *pixa;
    BOX      *box;
    BOXA     *boxa;
    L_STACK  *stack, *auxstack;

    PROCNAME("pixConnCompPixa");

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

    boxa = NULL;
    pix1 = pix2 = pix3 = pix4 = NULL;
    stack = NULL;

    pixZero(pixs, &iszero);
    if (iszero)
        return boxaCreate(1);  /* return empty boxa */

    pix1 = pixCopy(NULL, pixs);
    pix2 = pixCopy(NULL, pixs);
    if (!pix1 || !pix2) {
        L_ERROR("pix1 or pix2 not made\n", procName);
        goto cleanup;
    }

    h = pixGetHeight(pixs);
    if ((stack = lstackCreate(h)) == NULL) {
        L_ERROR("stack not made\n", procName);
        goto cleanup;
    }
    auxstack = lstackCreate(0);
    stack->auxstack = auxstack;
    pixa = pixaCreate(0);
    boxa = boxaCreate(0);

    xstart = 0;
    ystart = 0;
    while (1) {
        if (!nextOnPixelInRaster(pix1, xstart, ystart, &x, &y))
            break;

        if ((box = pixSeedfillBB(pix1, stack, x, y, connectivity)) == NULL) {
            L_ERROR("box not made\n", procName);
            pixaDestroy(&pixa);
            boxaDestroy(&boxa);
            goto cleanup;
        }
        boxaAddBox(boxa, box, L_INSERT);

        /* Save the c.c. and remove from pix2 as well */
        pix3 = pixClipRectangle(pix1, box, NULL);
        pix4 = pixClipRectangle(pix2, box, NULL);
        pixXor(pix3, pix3, pix4);
        pixRasterop(pix2, box->x, box->y, box->w, box->h, PIX_SRC ^ PIX_DST,
                    pix3, 0, 0);
        pixaAddPix(pixa, pix3, L_INSERT);
        pixDestroy(&pix4);

        xstart = x;
        ystart = y;
    }

#if  DEBUG
    pixCountPixels(pix1, &iszero, NULL);
    fprintf(stderr, "Number of remaining pixels = %d\n", iszero);
    pixWrite("junkremain", pix1, IFF_PNG);
#endif  /* DEBUG */

    /* Remove old boxa of pixa and replace with a clone copy */
    boxaDestroy(&pixa->boxa);
    pixa->boxa = boxaCopy(boxa, L_CLONE);
    *ppixa = pixa;

    /* Cleanup, freeing the fillsegs on each stack */
cleanup:
    lstackDestroy(&stack, TRUE);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    return boxa;
}
Beispiel #14
0
/*!
 *  boxaCombineOverlaps()
 *
 *      Input:  boxas
 *      Return: boxad (where each set of boxes in boxas that overlap are
 *                     combined into a single bounding box in boxad), or
 *                     null on error.
 *
 *  Notes:
 *      (1) If there are no overlapping boxes, it simply returns a copy
 *          of @boxas.
 *      (2) The alternative method of painting each rectanle and finding
 *          the 4-connected components gives the wrong result, because
 *          two non-overlapping rectangles, when rendered, can still
 *          be 4-connected, and hence they will be joined.
 *      (3) A bad case is to have n boxes, none of which overlap.
 *          Then you have one iteration with O(n^2) compares.  This
 *          is still faster than painting each rectangle and finding
 *          the connected components, even for thousands of rectangles.
 */
BOXA *
boxaCombineOverlaps(BOXA  *boxas)
{
l_int32  i, j, n1, n2, inter, interfound, niters;
BOX     *box1, *box2, *box3;
BOXA    *boxat1, *boxat2;

    PROCNAME("boxaCombineOverlaps");

    if (!boxas)
        return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);

    boxat1 = boxaCopy(boxas, L_COPY);
    n1 = boxaGetCount(boxat1);
    niters = 0;
/*    fprintf(stderr, "%d iters: %d boxes\n", niters, n1); */
    while (1) {  /* loop until no change from previous iteration */
        niters++;
        boxat2 = boxaCreate(n1);
        for (i = 0; i < n1; i++) {
            box1 = boxaGetBox(boxat1, i, L_COPY);
            if (i == 0) {
                boxaAddBox(boxat2, box1, L_INSERT);
                continue;
            }
            n2 = boxaGetCount(boxat2);
                /* Now test box1 against all boxes already put in boxat2.
                 * If it is found to intersect with an existing box,
                 * replace that box by the union of the two boxes,
                 * and break to the outer loop.  If no overlap is
                 * found, add box1 to boxat2. */
            interfound = FALSE;
            for (j = 0; j < n2; j++) {
                box2 = boxaGetBox(boxat2, j, L_CLONE);
                boxIntersects(box1, box2, &inter);
                if (inter == 1) {
                    box3 = boxBoundingRegion(box1, box2);
                    boxaReplaceBox(boxat2, j, box3);
                    boxDestroy(&box1);
                    boxDestroy(&box2);
                    interfound = TRUE;
                    break;
                }
                boxDestroy(&box2);
            }
            if (interfound == FALSE)
                boxaAddBox(boxat2, box1, L_INSERT);
        }
        n2 = boxaGetCount(boxat2);
/*        fprintf(stderr, "%d iters: %d boxes\n", niters, n2); */
        if (n2 == n1)  /* we're done */
            break;
        else {
            n1 = n2;
            boxaDestroy(&boxat1);
            boxat1 = boxat2;
        }
    }
    boxaDestroy(&boxat1);
    return boxat2;
}
Beispiel #15
0
int main(int    argc,
         char **argv)
{
l_int32       i, n;
l_float32     pi, angle, val;
BOX          *box;
BOXA         *boxa, *boxa1, *boxa2;
NUMA         *na1, *na2;
PIX          *pix, *pix1, *pix2;
PIXA         *pixa1, *pixa2, *pixa3, *pixa4;
L_REGPARAMS  *rp;

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

    lept_rmfile("/tmp/regout/insert3.ba");
    lept_rmfile("/tmp/regout/insert4.ba");
    lept_rmfile("/tmp/regout/insert6.pa");
    lept_rmfile("/tmp/regout/insert7.pa");
    lept_rmfile("/tmp/regout/insert9.pa");
    lept_rmfile("/tmp/regout/insert10.pa");

    /* ----------------- Test numa operations -------------------- */
    pi = 3.1415926535;
    na1 = numaCreate(500);
    for (i = 0; i < 500; i++) {
        angle = 0.02293 * i * pi;
        val = (l_float32)sin(angle);
        numaAddNumber(na1, val);
    }
    numaWrite("/tmp/regout/insert0.na", na1);
    na2 = numaCopy(na1);
    n = numaGetCount(na2);
    for (i = 0; i < n; i++) {
        numaGetFValue(na2, i, &val);
        numaRemoveNumber(na2, i);
        numaInsertNumber(na2, i, val);
    }
    numaWrite("/tmp/regout/insert1.na", na2);
    regTestCheckFile(rp, "/tmp/regout/insert0.na");  /* 0 */
    regTestCheckFile(rp, "/tmp/regout/insert1.na");  /* 1 */
    regTestCompareFiles(rp, 0, 1);  /* 2 */
    numaDestroy(&na1);
    numaDestroy(&na2);

    /* ----------------- Test boxa operations -------------------- */
    pix1 = pixRead("feyn.tif");
    box = boxCreate(1138, 1666, 1070, 380);
    pix2 = pixClipRectangle(pix1, box, NULL);
    boxDestroy(&box);
    boxa1 = pixConnComp(pix2, NULL, 8);
    boxaWrite("/tmp/regout/insert3.ba", boxa1);
    boxa2 = boxaCopy(boxa1, L_COPY);
    n = boxaGetCount(boxa2);
    for (i = 0; i < n; i++) {
        boxaRemoveBoxAndSave(boxa2, i, &box);
        boxaInsertBox(boxa2, i, box);
    }
    boxaWrite("/tmp/regout/insert4.ba", boxa2);
    regTestCheckFile(rp, "/tmp/regout/insert3.ba");  /* 3 */
    regTestCheckFile(rp, "/tmp/regout/insert4.ba");  /* 4 */
    regTestCompareFiles(rp, 3, 4);  /* 5 */
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);

    /* ----------------- Test pixa operations -------------------- */
    pix1 = pixRead("feyn.tif");
    box = boxCreate(1138, 1666, 1070, 380);
    pix2 = pixClipRectangle(pix1, box, NULL);
    boxDestroy(&box);
    boxa = pixConnComp(pix2, &pixa1, 8);
    boxaDestroy(&boxa);
    pixaWrite("/tmp/regout/insert6.pa", pixa1);
    regTestCheckFile(rp, "/tmp/regout/insert6.pa");  /* 6 */
    pixDestroy(&pix1);
    pixDestroy(&pix2);

        /* Remove and insert each one */
    pixa2 = pixaCopy(pixa1, L_COPY);
    n = pixaGetCount(pixa2);
    for (i = 0; i < n; i++) {
        pixaRemovePixAndSave(pixa2, i, &pix, &box);
        pixaInsertPix(pixa2, i, pix, box);
    }
    pixaWrite("/tmp/regout/insert7.pa", pixa2);
    regTestCheckFile(rp, "/tmp/regout/insert7.pa");  /* 7 */
    regTestCompareFiles(rp, 6, 7);  /* 8 */

        /* Move the last to the beginning; do it n times */
    pixa3 = pixaCopy(pixa2, L_COPY);
    for (i = 0; i < n; i++) {
        pix = pixaGetPix(pixa3, n - 1, L_CLONE);
        box = pixaGetBox(pixa3, n - 1, L_CLONE);
        pixaInsertPix(pixa3, 0, pix, box);
        pixaRemovePix(pixa3, n);
    }
    pixaWrite("/tmp/regout/insert9.pa", pixa3);
    regTestCheckFile(rp, "/tmp/regout/insert9.pa");  /* 9 */

        /* Move the first one to the end; do it n times */
    pixa4 = pixaCopy(pixa3, L_COPY);
    for (i = 0; i < n; i++) {
        pix = pixaGetPix(pixa4, 0, L_CLONE);
        box = pixaGetBox(pixa4, 0, L_CLONE);
        pixaInsertPix(pixa4, n, pix, box);  /* make sure insert works at end */
        pixaRemovePix(pixa4, 0);
    }
    pixaWrite("/tmp/regout/insert10.pa", pixa4);
    regTestCheckFile(rp, "/tmp/regout/insert10.pa");  /* 10 */
    regTestCompareFiles(rp, 9, 10);  /* 11 */
    pixaDestroy(&pixa1);
    pixaDestroy(&pixa2);
    pixaDestroy(&pixa3);
    pixaDestroy(&pixa4);

    return regTestCleanup(rp);
}
Beispiel #16
0
l_int32 main(int    argc,
             char **argv)
{
l_int32       i, w, h, n, val, ne, no, nbins, minw, maxw, minh, maxh;
l_int32       mine, mino, maxe, maxo;
l_int32       w_diff, h_diff, median_w_diff, median_h_diff;
l_int32       noutw, nouth;
l_float32     medwe, medhe, medwo, medho;
BOXA         *boxa1, *boxa2, *boxae, *boxao;
NUMA         *na1, *nawe, *nahe, *nawo, *naho;
NUMA         *nadiffw, *nadiffh;  /* diff from median w and h */
NUMA         *naiw, *naih;  /* indicator arrays for small outlier dimensions */
NUMA         *narbwe, *narbhe, *narbwo, *narbho;  /* rank-binned w and h */
PIX          *pix1;
PIXA         *pixa1;
L_REGPARAMS  *rp;

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

    lept_mkdir("lept/boxa");
    boxa1 = boxaRead("boxa4.ba");

        /* Fill invalid boxes */
    n = boxaGetCount(boxa1);
    na1 = boxaFindInvalidBoxes(boxa1);
    if (na1)
        boxa2 = boxaFillSequence(boxa1, L_USE_SAME_PARITY_BOXES, 0);
    else
        boxa2 = boxaCopy(boxa1, L_CLONE);
    boxaDestroy(&boxa1);

        /* Get the widths and heights for even and odd parity */
    boxaSplitEvenOdd(boxa2, 0, &boxae, &boxao);
    boxaGetSizes(boxae, &nawe, &nahe);
    boxaGetSizes(boxao, &nawo, &naho);
    boxaDestroy(&boxa2);

        /* Find the medians */
    numaGetMedian(nawe, &medwe);
    numaGetMedian(nahe, &medhe);
    numaGetMedian(nawo, &medwo);
    numaGetMedian(naho, &medho);

        /* Find the median even/odd differences for width and height */
    median_w_diff = L_ABS(medwe - medwo);
    median_h_diff = L_ABS(medhe - medho);
    regTestCompareValues(rp, 210, median_w_diff, 0.0);  /* 0 */
    regTestCompareValues(rp, 15, median_h_diff, 0.0);  /* 1 */
    if (rp->display) {
        fprintf(stderr, "diff of e/o median widths = %d\n", median_w_diff);
        fprintf(stderr, "diff of e/o median heights = %d\n", median_h_diff);
    }

        /* Find the differences of box width and height from the median */
    nadiffw = numaMakeConstant(0, n);
    nadiffh = numaMakeConstant(0, n);
    ne = numaGetCount(nawe);
    no = numaGetCount(nawo);
    for (i = 0; i < ne; i++) {
        numaGetIValue(nawe, i, &val);
        numaSetValue(nadiffw, 2 * i, L_ABS(val - medwe));
        numaGetIValue(nahe, i, &val);
        numaSetValue(nadiffh, 2 * i, L_ABS(val - medhe));
    }
    for (i = 0; i < no; i++) {
        numaGetIValue(nawo, i, &val);
        numaSetValue(nadiffw, 2 * i + 1, L_ABS(val - medwo));
        numaGetIValue(naho, i, &val);
        numaSetValue(nadiffh, 2 * i + 1, L_ABS(val - medho));
    }

        /* Don't count invalid boxes; set the diffs to 0 for them */
    if (na1) {
        for (i = 0; i < n; i++) {
            numaGetIValue(na1, i, &val);
            if (val == 1) {
                numaSetValue(nadiffw, i, 0);
                numaSetValue(nadiffh, i, 0);
            }
        }
    }

        /* Make an indicator array for boxes that differ from the
         * median by more than a threshold value for outliers */
    naiw = numaMakeThresholdIndicator(nadiffw, 90, L_SELECT_IF_GT);
    naih = numaMakeThresholdIndicator(nadiffh, 90, L_SELECT_IF_GT);
    numaGetCountRelativeToZero(naiw, L_GREATER_THAN_ZERO, &noutw);
    numaGetCountRelativeToZero(naih, L_GREATER_THAN_ZERO, &nouth);
    regTestCompareValues(rp, 24, noutw, 0.0);  /* 2 */
    regTestCompareValues(rp, 0, nouth, 0.0);  /* 3 */
    if (rp->display)
        fprintf(stderr, "num width outliers = %d, num height outliers = %d\n",
                noutw, nouth);
    numaDestroy(&nadiffw);
    numaDestroy(&nadiffh);
    numaDestroy(&naiw);
    numaDestroy(&naih);

        /* Find the rank bins for width and height */
    nbins = L_MAX(5, ne / 50);  // up to 50 pages/bin
    numaGetRankBinValues(nawe, nbins, NULL, &narbwe);
    numaGetRankBinValues(nawo, nbins, NULL, &narbwo);
    numaGetRankBinValues(nahe, nbins, NULL, &narbhe);
    numaGetRankBinValues(naho, nbins, NULL, &narbho);
    numaDestroy(&nawe);
    numaDestroy(&nawo);
    numaDestroy(&nahe);
    numaDestroy(&naho);

        /* Find min and max binned widths and heights; get the max diffs */
    numaGetIValue(narbwe, 0, &mine);
    numaGetIValue(narbwe, nbins - 1, &maxe);
    numaGetIValue(narbwo, 0, &mino);
    numaGetIValue(narbwo, nbins - 1, &maxo);
    minw = L_MIN(mine, mino);
    maxw = L_MAX(maxe, maxo);
    w_diff = maxw - minw;
    numaGetIValue(narbhe, 0, &mine);
    numaGetIValue(narbhe, nbins - 1, &maxe);
    numaGetIValue(narbho, 0, &mino);
    numaGetIValue(narbho, nbins - 1, &maxo);
    minh = L_MIN(mine, mino);
    maxh = L_MAX(maxe, maxo);
    h_diff = maxh - minh;
    numaDestroy(&narbwe);
    numaDestroy(&narbhe);
    numaDestroy(&narbwo);
    numaDestroy(&narbho);
    regTestCompareValues(rp, 409, w_diff, 0.0);  /* 4 */
    regTestCompareValues(rp, 49, h_diff, 0.0);  /* 5 */
    if (rp->display)
        fprintf(stderr, "Binned rank results: w_diff = %d, h_diff = %d\n",
                w_diff, h_diff);

        /* Plot the results */
    if (noutw > 0 || nouth > 0) {
        pixa1 = pixaCreate(2);
        boxaPlotSizes(boxae, "even", NULL, NULL, &pix1);
        pixaAddPix(pixa1, pix1, L_INSERT);
        boxaPlotSizes(boxao, "odd", NULL, NULL, &pix1);
        pixaAddPix(pixa1, pix1, L_INSERT);
        pix1 = pixaDisplayTiledInRows(pixa1, 32, 1500, 1.0, 0, 30, 2);
        regTestWritePixAndCheck(rp, pix1, IFF_PNG);  /* 6 */
        pixDisplayWithTitle(pix1, 100, 100, NULL, rp->display);
        pixDestroy(&pix1);
        pixaDestroy(&pixa1);
    }

    boxaDestroy(&boxae);
    boxaDestroy(&boxao);
    return regTestCleanup(rp);
}
Beispiel #17
0
/*!
 *  dewarpaApplyDisparityBoxa()
 *
 *      Input:  dewa
 *              pageno (of page model to be used; may be a ref model)
 *              pixs (initial pix reference; for alignment and debugging)
 *              boxas (boxa to be mapped)
 *              mapdir (1 if mapping forward from original to dewarped;
 *                      0 if backward)
 *              x, y (origin for generation of disparity arrays with
 *                    respect to the source region)
 *              &boxad (<return> disparity corrected boxa)
 *              debugfile (use null to skip writing this)
 *      Return: 0 if OK, 1 on error (no models or ref models available)
 *
 *  Notes:
 *      (1) This applies the disparity arrays in one of two mapping directions
 *          to the specified boxa.  It can be used in the backward direction
 *          to locate a box in the original coordinates that would have
 *          been dewarped to to the specified image.
 *      (2) If there is no model for @pageno, this will use the model for
 *          'refpage' and put the result in the dew for @pageno.
 *      (3) This works with both stripped and full resolution page models.
 *          If the full res disparity array(s) are missing, they are remade.
 *      (4) If an error occurs, a copy of the input boxa is returned.
 */
l_int32
dewarpaApplyDisparityBoxa(L_DEWARPA   *dewa,
                          l_int32      pageno,
                          PIX         *pixs,
                          BOXA        *boxas,
                          l_int32      mapdir,
                          l_int32      x,
                          l_int32      y,
                          BOXA       **pboxad,
                          const char  *debugfile)
{
l_int32    debug_out;
L_DEWARP  *dew1, *dew;
BOXA      *boxav, *boxah;
PIX       *pixv, *pixh;

    PROCNAME("dewarpaApplyDisparityBoxa");

        /* Initialize the output with the input, so we'll have that
         * in case we can't apply the page model. */
    if (!pboxad)
        return ERROR_INT("&boxad not defined", procName, 1);
    *pboxad = boxaCopy(boxas, L_CLONE);

        /* Find the appropriate dew to use and fully populate its array(s) */
    if (dewarpaApplyInit(dewa, pageno, pixs, x, y, &dew, debugfile))
        return ERROR_INT("no model available", procName, 1);

        /* Correct for vertical disparity and save the result */
    if ((boxav = boxaApplyDisparity(dew, boxas, L_VERT, mapdir)) == NULL) {
        dewarpMinimize(dew);
        return ERROR_INT("boxa1 not made", procName, 1);
    }
    boxaDestroy(pboxad);
    *pboxad = boxav;
    pixv = NULL;
    pixh = NULL;
    if (debugfile && mapdir != 1)
        L_INFO("Reverse map direction; no debug output\n", procName);
    debug_out = debugfile && (mapdir == 1);
    if (debug_out) {
        PIX  *pix1;
        lept_rmdir("lept/dewboxa");  /* remove previous images */
        lept_mkdir("lept/dewboxa");
        pix1 = pixConvertTo32(pixs);
        pixRenderBoxaArb(pix1, boxas, 2, 255, 0, 0);
        pixWrite("/tmp/lept/dewboxa/01.png", pix1, IFF_PNG);
        pixDestroy(&pix1);
        pixv = pixApplyVertDisparity(dew, pixs, 255);
        pix1 = pixConvertTo32(pixv);
        pixRenderBoxaArb(pix1, boxav, 2, 0, 255, 0);
        pixWrite("/tmp/lept/dewboxa/02.png", pix1, IFF_PNG);
        pixDestroy(&pix1);
    }

        /* Optionally, correct for horizontal disparity */
    if (dewa->useboth && dew->hsuccess) {
        if (dew->hvalid == FALSE) {
            L_INFO("invalid horiz model for page %d\n", procName, pageno);
        } else {
            boxah = boxaApplyDisparity(dew, boxav, L_HORIZ, mapdir);
            if (!boxah) {
                L_ERROR("horiz disparity fails on page %d\n", procName, pageno);
            } else {
                boxaDestroy(pboxad);
                *pboxad = boxah;
                if (debug_out) {
                    PIX  *pix1;
                    pixh = pixApplyHorizDisparity(dew, pixv, 255);
                    pix1 = pixConvertTo32(pixh);
                    pixRenderBoxaArb(pix1, boxah, 2, 0, 0, 255);
                    pixWrite("/tmp/lept/dewboxa/03.png", pix1, IFF_PNG);
                    pixDestroy(&pixh);
                    pixDestroy(&pix1);
                }
            }
        }
    }

    if (debug_out) {
        pixDestroy(&pixv);
        dew1 = dewarpaGetDewarp(dewa, pageno);
        dewarpDebug(dew1, "lept/dewapply", 0);
        convertFilesToPdf("/tmp/lept/dewboxa", NULL, 135, 1.0, 0, 0,
                         "Dewarp Apply Disparity Boxa", debugfile);
        fprintf(stderr, "Dewarp Apply Disparity Boxa pdf file: %s\n",
                debugfile);
    }

        /* Get rid of the large full res disparity arrays */
    dewarpMinimize(dew);

    return 0;
}