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; }
/*! * \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; }
/*! * 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; }
/*! * \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); }
/*! * \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; }
/*! * \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; }
/*! * 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; }
/*! * \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; }
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; }
/*! * \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; }
/*! * 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; }
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); }
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); }
/*! * 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; }