/*====================================================================* - Copyright (C) 2001 Leptonica. All rights reserved. - This software is distributed in the hope that it will be - useful, but with NO WARRANTY OF ANY KIND. - No author or distributor accepts responsibility to anyone for the - consequences of using this software, or for whether it serves any - particular purpose or works at all, unless he or she says so in - writing. Everyone is granted permission to copy, modify and - redistribute this source code, for commercial or non-commercial - purposes, with the following restrictions: (1) the origin of this - source code must not be misrepresented; (2) modified versions must - be plainly marked as such; and (3) this notice may not be removed - or altered from any source or modified source distribution. *====================================================================*/ /* * boxfunc1.c * * Box geometry * l_int32 boxContains() * l_int32 boxIntersects() * BOXA *boxaContainedInBox() * BOXA *boxaIntersectsBox() * BOXA *boxaClipToBox() * BOX *boxOverlapRegion() * BOX *boxBoundingRegion() * l_int32 boxOverlapFraction() * l_int32 boxContainsPt() * BOX *boxaGetNearestToPt() * l_int32 boxIntersectByLine() * l_int32 boxGetCentroid() * BOX *boxClipToRectangle() * BOX *boxRelocateOneSide() * BOX *boxAdjustSides() * l_int32 boxEqual() * l_int32 boxaEqual() * * Boxa combination * l_int32 boxaJoin() * * Other boxa functions * l_int32 boxaGetExtent() * l_int32 boxaGetCoverage() * l_int32 boxaSizeRange() * l_int32 boxaLocationRange() * BOXA *boxaSelectBySize() * NUMA *boxaMakeSizeIndicator() * BOXA *boxaSelectWithIndicator() * BOXA *boxaPermutePseudorandom() * BOXA *boxaPermuteRandom() * l_int32 boxaSwapBoxes() * PTA *boxaConvertToPta() * BOXA *ptaConvertToBoxa() * */ #include <stdio.h> #include <stdlib.h> #include "allheaders.h" /*---------------------------------------------------------------------* * Box geometry * *---------------------------------------------------------------------*/ /*! * boxContains() * * Input: box1, box2 * &result (<return> 1 if box2 is entirely contained within * box1, and 0 otherwise) * Return: 0 if OK, 1 on error */ l_int32 boxContains(BOX *box1, BOX *box2, l_int32 *presult) { PROCNAME("boxContains"); if (!box1 || !box2) return ERROR_INT("box1 and box2 not both defined", procName, 1); if ((box1->x <= box2->x) && (box1->y <= box2->y) && (box1->x + box1->w >= box2->x + box2->w) && (box1->y + box1->h >= box2->y + box2->h)) *presult = 1; else *presult = 0; return 0; } /*! * boxIntersects() * * Input: box1, box2 * &result (<return> 1 if any part of box2 is contained * in box1, and 0 otherwise) * Return: 0 if OK, 1 on error */ l_int32 boxIntersects(BOX *box1, BOX *box2, l_int32 *presult) { l_int32 left1, left2, top1, top2, right1, right2, bot1, bot2; PROCNAME("boxIntersects"); if (!box1 || !box2) return ERROR_INT("box1 and box2 not both defined", procName, 1); left1 = box1->x; left2 = box2->x; top1 = box1->y; top2 = box2->y; right1 = box1->x + box1->w - 1; bot1 = box1->y + box1->h - 1; right2 = box2->x + box2->w - 1; bot2 = box2->y + box2->h - 1; if ((bot2 >= top1) && (bot1 >= top2) && (right1 >= left2) && (right2 >= left1)) *presult = 1; else *presult = 0; return 0; } /*! * boxaContainedInBox() * * Input: boxas * box (for containment) * Return: boxad (boxa with all boxes in boxas that are * entirely contained in box), or null on error * * Notes: * (1) All boxes in boxa that are entirely outside box are removed. */ BOXA * boxaContainedInBox(BOXA *boxas, BOX *box) { l_int32 i, n, val; BOX *boxt; BOXA *boxad; PROCNAME("boxaContainedInBox"); if (!boxas) return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); if (!box) return (BOXA *)ERROR_PTR("box not defined", procName, NULL); if ((n = boxaGetCount(boxas)) == 0) return boxaCreate(1); /* empty */ boxad = boxaCreate(0); for (i = 0; i < n; i++) { boxt = boxaGetBox(boxas, i, L_CLONE); boxContains(box, boxt, &val); if (val == 1) boxaAddBox(boxad, boxt, L_COPY); boxDestroy(&boxt); /* destroy the clone */ } return boxad; } /*! * boxaIntersectsBox() * * Input: boxas * box (for intersecting) * Return boxad (boxa with all boxes in boxas that intersect box), * or null on error * * Notes: * (1) All boxes in boxa that intersect with box (i.e., are completely * or partially contained in box) are retained. */ BOXA * boxaIntersectsBox(BOXA *boxas, BOX *box) { l_int32 i, n, val; BOX *boxt; BOXA *boxad; PROCNAME("boxaIntersectsBox"); if (!boxas) return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); if (!box) return (BOXA *)ERROR_PTR("box not defined", procName, NULL); if ((n = boxaGetCount(boxas)) == 0) return boxaCreate(1); /* empty */ boxad = boxaCreate(0); for (i = 0; i < n; i++) { boxt = boxaGetBox(boxas, i, L_CLONE); boxIntersects(box, boxt, &val); if (val == 1) boxaAddBox(boxad, boxt, L_COPY); boxDestroy(&boxt); /* destroy the clone */ } return boxad; } /*! * boxaClipToBox() * * Input: boxas * box (for clipping) * Return boxad (boxa with boxes in boxas clipped to box), * or null on error * * Notes: * (1) All boxes in boxa not intersecting with box are removed, and * the remaining boxes are clipped to box. */ BOXA * boxaClipToBox(BOXA *boxas, BOX *box) { l_int32 i, n; BOX *boxt, *boxo; BOXA *boxad; PROCNAME("boxaClipToBox"); if (!boxas) return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); if (!box) return (BOXA *)ERROR_PTR("box not defined", procName, NULL); if ((n = boxaGetCount(boxas)) == 0) return boxaCreate(1); /* empty */ boxad = boxaCreate(0); for (i = 0; i < n; i++) { boxt = boxaGetBox(boxas, i, L_CLONE); if ((boxo = boxOverlapRegion(box, boxt)) != NULL) boxaAddBox(boxad, boxo, L_INSERT); boxDestroy(&boxt); } return boxad; } /*! * boxOverlapRegion() * * Input: box1, box2 (two boxes) * Return: box (of overlap region between input boxes), * or null if no overlap or on error */ BOX * boxOverlapRegion(BOX *box1, BOX *box2) { l_int32 x, y, w, h, left1, left2, top1, top2, right1, right2, bot1, bot2; PROCNAME("boxOverlapRegion"); if (!box1) return (BOX *)ERROR_PTR("box1 not defined", procName, NULL); if (!box2) return (BOX *)ERROR_PTR("box2 not defined", procName, NULL); left1 = box1->x; left2 = box2->x; top1 = box1->y; top2 = box2->y; right1 = box1->x + box1->w - 1; bot1 = box1->y + box1->h - 1; right2 = box2->x + box2->w - 1; bot2 = box2->y + box2->h - 1; if ((bot2 < top1) || (bot1 < top2) || (right1 < left2) || (right2 < left1)) return NULL; x = (left1 > left2) ? left1 : left2; y = (top1 > top2) ? top1 : top2; w = L_MIN(right1 - x + 1, right2 - x + 1); h = L_MIN(bot1 - y + 1, bot2 - y + 1); return boxCreate(x, y, w, h); } /*! * boxBoundingRegion() * * Input: box1, box2 (two boxes) * Return: box (of bounding region containing the input boxes), * or null on error */ BOX * boxBoundingRegion(BOX *box1, BOX *box2) { l_int32 left, top, right1, right2, right, bot1, bot2, bot; PROCNAME("boxBoundingRegion"); if (!box1) return (BOX *)ERROR_PTR("box1 not defined", procName, NULL); if (!box2) return (BOX *)ERROR_PTR("box2 not defined", procName, NULL); left = L_MIN(box1->x, box2->x); top = L_MIN(box1->y, box2->y); right1 = box1->x + box1->w - 1; right2 = box2->x + box2->w - 1; right = L_MAX(right1, right2); bot1 = box1->y + box1->h - 1; bot2 = box2->y + box2->h - 1; bot = L_MAX(bot1, bot2); return boxCreate(left, top, right - left + 1, bot - top + 1); } /*! * 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; } /*! * 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; } /*! * boxaGetNearestToPt() * * Input: boxa * x, y (point) * Return box (box with centroid closest to the given point [x,y]), * or NULL if no boxes in boxa) * * Notes: * (1) Uses euclidean distance between centroid and point. */ BOX * boxaGetNearestToPt(BOXA *boxa, l_int32 x, l_int32 y) { l_int32 i, n, cx, cy, minindex; l_float32 delx, dely, dist, mindist; BOX *box; PROCNAME("boxaGetNearestToPt"); if (!boxa) return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); if ((n = boxaGetCount(boxa)) == 0) return (BOX *)ERROR_PTR("n = 0", procName, NULL); mindist = 1000000000.; minindex = 0; for (i = 0; i < n; i++) { box = boxaGetBox(boxa, i, L_CLONE); boxGetCentroid(box, &cx, &cy); delx = (l_float32)(cx - x); dely = (l_float32)(cy - y); dist = delx * delx + dely * dely; if (dist < mindist) { minindex = i; mindist = dist; } boxDestroy(&box); } return boxaGetBox(boxa, minindex, L_COPY); } /*! * boxGetCentroid() * * Input: box * &x, &y (<return> location of center of box) * Return 0 if OK, 1 on error */ l_int32 boxGetCentroid(BOX *box, l_int32 *px, l_int32 *py) { l_int32 x, y, w, h; PROCNAME("boxGetCentroid"); if (!px || !py) return ERROR_INT("&x, &y not both defined", procName, 1); *px = *py = 0; if (!box) return ERROR_INT("box not defined", procName, 1); boxGetGeometry(box, &x, &y, &w, &h); *px = x + w / 2; *py = y + h / 2; return 0; } /*! * boxIntersectByLine() * * Input: box * x, y (point that line goes through) * slope (of line) * (&x1, &y1) (<return> 1st point of intersection with box) * (&x2, &y2) (<return> 2nd point of intersection with box) * &n (<return> number of points of intersection) * Return: 0 if OK, 1 on error * * Notes: * (1) If the intersection is at only one point (a corner), the * coordinates are returned in (x1, y1). * (2) Represent a vertical line by one with a large but finite slope. */ l_int32 boxIntersectByLine(BOX *box, l_int32 x, l_int32 y, l_float32 slope, l_int32 *px1, l_int32 *py1, l_int32 *px2, l_int32 *py2, l_int32 *pn) { l_int32 bx, by, bw, bh, xp, yp, xt, yt, i, n; l_float32 invslope; PTA *pta; PROCNAME("boxIntersectByLine"); if (!px1 || !py1 || !px2 || !py2) return ERROR_INT("&x1, &y1, &x2, &y2 not all defined", procName, 1); *px1 = *py1 = *px2 = *py2 = 0; if (!pn) return ERROR_INT("&n not defined", procName, 1); *pn = 0; if (!box) return ERROR_INT("box not defined", procName, 1); boxGetGeometry(box, &bx, &by, &bw, &bh); if (slope == 0.0) { if (y >= by && y < by + bh) { *py1 = *py2 = y; *px1 = bx; *px2 = bx + bw - 1; } return 0; } if (slope > 1000000.0) { if (x >= bx && x < bx + bw) { *px1 = *px2 = x; *py1 = by; *py2 = by + bh - 1; } return 0; } /* Intersection with top and bottom lines of box */ pta = ptaCreate(2); invslope = 1.0 / slope; xp = (l_int32)(x + invslope * (y - by)); if (xp >= bx && xp < bx + bw) ptaAddPt(pta, xp, by); xp = (l_int32)(x + invslope * (y - by - bh + 1)); if (xp >= bx && xp < bx + bw) ptaAddPt(pta, xp, by + bh - 1); /* Intersection with left and right lines of box */ yp = (l_int32)(y + slope * (x - bx)); if (yp >= by && yp < by + bh) ptaAddPt(pta, bx, yp); yp = (l_int32)(y + slope * (x - bx - bw + 1)); if (yp >= by && yp < by + bh) ptaAddPt(pta, bx + bw - 1, yp); /* There is a maximum of 2 unique points; remove duplicates. */ n = ptaGetCount(pta); if (n > 0) { ptaGetIPt(pta, 0, px1, py1); /* accept the first one */ *pn = 1; } for (i = 1; i < n; i++) { ptaGetIPt(pta, i, &xt, &yt); if ((*px1 != xt) || (*py1 != yt)) { *px2 = xt; *py2 = yt; *pn = 2; break; } } ptaDestroy(&pta); return 0; } /*! * boxClipToRectangle() * * Input: box * wi, hi (rectangle representing image) * Return: part of box within given rectangle, or NULL on error * or if box is entirely outside the rectangle * * Note: the rectangle is assumed to go from (0,0) to (wi - 1, hi - 1) */ BOX * boxClipToRectangle(BOX *box, l_int32 wi, l_int32 hi) { BOX *boxd; PROCNAME("boxClipToRectangle"); if (!box) return (BOX *)ERROR_PTR("box not defined", procName, NULL); if (box->x >= wi || box->y >= hi || box->x + box->w <= 0 || box->y + box->h <= 0) return (BOX *)ERROR_PTR("box outside rectangle", procName, NULL); boxd = boxCopy(box); if (boxd->x < 0) { boxd->w += boxd->x; boxd->x = 0; } if (boxd->y < 0) { boxd->h += boxd->y; boxd->y = 0; } if (boxd->x + boxd->w > wi) boxd->w = wi - boxd->x; if (boxd->y + boxd->h > hi) boxd->h = hi - boxd->y; return boxd; } /*! * 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; } /*! * 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; } } /*! * boxEqual() * * Input: box1 * box2 * &same (<return> 1 if equal; 0 otherwise) * Return 0 if OK, 1 on error */ l_int32 boxEqual(BOX *box1, BOX *box2, l_int32 *psame) { PROCNAME("boxEqual"); if (!psame) return ERROR_INT("&same not defined", procName, 1); *psame = 0; if (!box1 || !box2) return ERROR_INT("box1 and box2 not both defined", procName, 1); if (box1->x == box2->x && box1->y == box2->y && box1->w == box2->w && box1->h == box2->h) *psame = 1; return 0; } /*! * boxaEqual() * * Input: boxa1 * boxa2 * maxdist * &naindex (<optional return> index array of correspondences * &same (<return> 1 if equal; 0 otherwise) * Return 0 if OK, 1 on error * * Notes: * (1) The two boxa are the "same" if they contain the same * boxes and each box is within @maxdist of its counterpart * in their positions within the boxa. This allows for * small rearrangements. Use 0 for maxdist if the boxa * must be identical. * (2) This applies only to geometry and ordering; refcounts * are not considered. * (3) @maxdist allows some latitude in the ordering of the boxes. * For the boxa to be the "same", corresponding boxes must * be within @maxdist of each other. Note that for large * @maxdist, we should use a hash function for efficiency. * (4) naindex[i] gives the position of the box in boxa2 that * corresponds to box i in boxa1. It is only returned if the * boxa are equal. */ l_int32 boxaEqual(BOXA *boxa1, BOXA *boxa2, l_int32 maxdist, NUMA **pnaindex, l_int32 *psame) { l_int32 i, j, n, jstart, jend, found, samebox; l_int32 *countarray; BOX *box1, *box2; NUMA *na; PROCNAME("boxaEqual"); if (pnaindex) *pnaindex = NULL; if (!psame) return ERROR_INT("&same not defined", procName, 1); *psame = 0; if (!boxa1 || !boxa2) return ERROR_INT("boxa1 and boxa2 not both defined", procName, 1); n = boxaGetCount(boxa1); if (n != boxaGetCount(boxa2)) return 0; countarray = (l_int32 *)CALLOC(n, sizeof(l_int32)); na = numaMakeConstant(0.0, n); for (i = 0; i < n; i++) { box1 = boxaGetBox(boxa1, i, L_CLONE); jstart = L_MAX(0, i - maxdist); jend = L_MIN(n-1, i + maxdist); found = FALSE; for (j = jstart; j <= jend; j++) { box2 = boxaGetBox(boxa2, j, L_CLONE); boxEqual(box1, box2, &samebox); if (samebox && countarray[j] == 0) { countarray[j] = 1; numaReplaceNumber(na, i, j); found = TRUE; boxDestroy(&box2); break; } boxDestroy(&box2); } boxDestroy(&box1); if (!found) { numaDestroy(&na); FREE(countarray); return 0; } } *psame = 1; if (pnaindex) *pnaindex = na; else numaDestroy(&na); FREE(countarray); return 0; } /*----------------------------------------------------------------------* * Boxa Combination * *----------------------------------------------------------------------*/ /*! * boxaJoin() * * Input: boxad (dest boxa; add to this one) * boxas (source boxa; add from this one) * istart (starting index in nas) * iend (ending index in nas; use 0 to cat all) * Return: 0 if OK, 1 on error * * Notes: * (1) This appends a clone of each indicated box in boxas to boxad * (2) istart < 0 is taken to mean 'read from the start' (istart = 0) * (3) iend <= 0 means 'read to the end' */ l_int32 boxaJoin(BOXA *boxad, BOXA *boxas, l_int32 istart, l_int32 iend) { l_int32 ns, i; BOX *box; PROCNAME("boxaJoin"); if (!boxad) return ERROR_INT("boxad not defined", procName, 1); if (!boxas) return ERROR_INT("boxas not defined", procName, 1); ns = boxaGetCount(boxas); if (istart < 0) istart = 0; if (istart >= ns) return ERROR_INT("istart out of bounds", procName, 1); if (iend <= 0) iend = ns - 1; if (iend >= ns) return ERROR_INT("iend out of bounds", procName, 1); if (istart > iend) return ERROR_INT("istart > iend; nothing to add", procName, 1); for (i = istart; i <= iend; i++) { box = boxaGetBox(boxas, i, L_CLONE); boxaAddBox(boxad, box, L_INSERT); } return 0; } /*---------------------------------------------------------------------* * Other Boxa functions * *---------------------------------------------------------------------*/ /*! * boxaGetExtent() * * Input: boxa * &w (<optional return> width) * &h (<optional return> height) * &box (<optional return>, minimum box containing all boxes * in boxa) * Return: 0 if OK, 1 on error * * Notes: * (1) The returned w and h are the minimum size image * that would contain all boxes untranslated. */ l_int32 boxaGetExtent(BOXA *boxa, l_int32 *pw, l_int32 *ph, BOX **pbox) { l_int32 i, n, x, y, w, h, xmax, ymax, xmin, ymin; PROCNAME("boxaGetExtent"); if (!pw && !ph && !pbox) return ERROR_INT("no ptrs defined", procName, 1); if (pbox) *pbox = NULL; if (pw) *pw = 0; if (ph) *ph = 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); xmax = ymax = 0; xmin = ymin = 100000000; for (i = 0; i < n; i++) { boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); xmin = L_MIN(xmin, x); ymin = L_MIN(ymin, y); xmax = L_MAX(xmax, x + w); ymax = L_MAX(ymax, y + h); } if (pw) *pw = xmax; if (ph) *ph = ymax; if (pbox) *pbox = boxCreate(xmin, ymin, xmax - xmin, ymax - ymin); return 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; } /*! * boxaSizeRange() * * Input: boxa * &minw, &minh, &maxw, &maxh (<optional return> range of * dimensions of box in the array) * Return: 0 if OK, 1 on error */ l_int32 boxaSizeRange(BOXA *boxa, l_int32 *pminw, l_int32 *pminh, l_int32 *pmaxw, l_int32 *pmaxh) { l_int32 minw, minh, maxw, maxh, i, n, w, h; PROCNAME("boxaSizeRange"); if (!boxa) return ERROR_INT("boxa not defined", procName, 1); if (!pminw && !pmaxw && !pminh && !pmaxh) return ERROR_INT("no data can be returned", procName, 1); minw = minh = 100000000; maxw = maxh = 0; n = boxaGetCount(boxa); for (i = 0; i < n; i++) { boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); if (w < minw) minw = w; if (h < minh) minh = h; if (w > maxw) maxw = w; if (h > maxh) maxh = h; } if (pminw) *pminw = minw; if (pminh) *pminh = minh; if (pmaxw) *pmaxw = maxw; if (pmaxh) *pmaxh = maxh; return 0; } /*! * boxaLocationRange() * * Input: boxa * &minx, &miny, &maxx, &maxy (<optional return> range of * UL corner positions) * Return: 0 if OK, 1 on error */ l_int32 boxaLocationRange(BOXA *boxa, l_int32 *pminx, l_int32 *pminy, l_int32 *pmaxx, l_int32 *pmaxy) { l_int32 minx, miny, maxx, maxy, i, n, x, y; PROCNAME("boxaLocationRange"); if (!boxa) return ERROR_INT("boxa not defined", procName, 1); if (!pminx && !pminy && !pmaxx && !pmaxy) return ERROR_INT("no data can be returned", procName, 1); minx = miny = 100000000; maxx = maxy = 0; n = boxaGetCount(boxa); for (i = 0; i < n; i++) { boxaGetBoxGeometry(boxa, i, &x, &y, NULL, NULL); if (x < minx) minx = x; if (y < miny) miny = y; if (x > maxx) maxx = x; if (y > maxy) maxy = y; } if (pminx) *pminx = minx; if (pminy) *pminy = miny; if (pmaxx) *pmaxx = maxx; if (pmaxy) *pmaxy = maxy; return 0; } /*! * boxaSelectBySize() * * Input: boxas * width, height (threshold dimensions) * type (L_SELECT_WIDTH, L_SELECT_HEIGHT, * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH) * relation (L_SELECT_IF_LT, L_SELECT_IF_GT, * L_SELECT_IF_LTE, L_SELECT_IF_GTE) * &changed (<optional return> 1 if changed; 0 if clone returned) * Return: boxad (filtered set), or null on error * * Notes: * (1) The args specify constraints on the size of the * components that are kept. * (2) Uses box clones 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. */ 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 (!boxas) return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); 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); if (pchanged) *pchanged = FALSE; /* Compute the indicator array for saving components */ na = boxaMakeSizeIndicator(boxas, width, height, type, relation); /* Filter to get output */ boxad = boxaSelectWithIndicator(boxas, na, pchanged); numaDestroy(&na); return boxad; } /*! * boxaMakeSizeIndicator() * * Input: boxa * width, height (threshold dimensions) * type (L_SELECT_WIDTH, L_SELECT_HEIGHT, * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH) * relation (L_SELECT_IF_LT, L_SELECT_IF_GT, * L_SELECT_IF_LTE, L_SELECT_IF_GTE) * Return: na (indicator array), or null on error * * Notes: * (1) The args specify constraints on the size of the * components that are kept. * (2) If the selection type is L_SELECT_WIDTH, the input * height is ignored, and v.v. * (3) 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. */ NUMA * boxaMakeSizeIndicator(BOXA *boxa, l_int32 width, l_int32 height, l_int32 type, l_int32 relation) { l_int32 i, n, w, h, ival; NUMA *na; PROCNAME("boxaMakeSizeIndicator"); if (!boxa) return (NUMA *)ERROR_PTR("boxa not defined", procName, NULL); if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) return (NUMA *)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 (NUMA *)ERROR_PTR("invalid relation", procName, NULL); n = boxaGetCount(boxa); na = numaCreate(n); for (i = 0; i < n; i++) { ival = 0; boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); switch (type) { case L_SELECT_WIDTH: if ((relation == L_SELECT_IF_LT && w < width) || (relation == L_SELECT_IF_GT && w > width) || (relation == L_SELECT_IF_LTE && w <= width) || (relation == L_SELECT_IF_GTE && w >= width)) ival = 1; break; case L_SELECT_HEIGHT: if ((relation == L_SELECT_IF_LT && h < height) || (relation == L_SELECT_IF_GT && h > height) || (relation == L_SELECT_IF_LTE && h <= height) || (relation == L_SELECT_IF_GTE && h >= height)) ival = 1; break; case L_SELECT_IF_EITHER: if (((relation == L_SELECT_IF_LT) && (w < width || h < height)) || ((relation == L_SELECT_IF_GT) && (w > width || h > height)) || ((relation == L_SELECT_IF_LTE) && (w <= width || h <= height)) || ((relation == L_SELECT_IF_GTE) && (w >= width || h >= height))) ival = 1; break; case L_SELECT_IF_BOTH: if (((relation == L_SELECT_IF_LT) && (w < width && h < height)) || ((relation == L_SELECT_IF_GT) && (w > width && h > height)) || ((relation == L_SELECT_IF_LTE) && (w <= width && h <= height)) || ((relation == L_SELECT_IF_GTE) && (w >= width && h >= height))) ival = 1; break; default: L_WARNING("can't get here!", procName); } numaAddNumber(na, ival); } return na; } /*! * boxaSelectWithIndicator() * * Input: boxas * na (indicator numa) * &changed (<optional return> 1 if changed; 0 if clone returned) * Return: boxad, or null on error * * Notes: * (1) Returns a boxa clone if no components are removed. * (2) Uses box clones in the new boxa. * (3) The indicator numa has values 0 (ignore) and 1 (accept). */ BOXA * boxaSelectWithIndicator(BOXA *boxas, NUMA *na, l_int32 *pchanged) { l_int32 i, n, ival, nsave; BOX *box; BOXA *boxad; PROCNAME("boxaSelectWithIndicator"); 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_CLONE); } 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_CLONE); boxaAddBox(boxad, box, L_INSERT); } return boxad; } /*! * boxaPermutePseudorandom() * * Input: boxas (input boxa) * Return: boxad (with boxes permuted), or null on error * * Notes: * (1) This does a pseudorandom in-place permutation of the boxes. * (2) The result is guaranteed not to have any boxes in their * original position, but it is not very random. If you * need randomness, use boxaPermuteRandom(). */ BOXA * boxaPermutePseudorandom(BOXA *boxas) { l_int32 n; NUMA *na; BOXA *boxad; PROCNAME("boxaPermutePseudorandom"); if (!boxas) return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL); n = boxaGetCount(boxas); na = numaPseudorandomSequence(n, 0); boxad = boxaSortByIndex(boxas, na); numaDestroy(&na); return boxad; } /*! * boxaPermuteRandom() * * Input: boxad (<optional> can be null or equal to boxas) * boxas (input boxa) * Return: boxad (with boxes permuted), or null on error * * 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) 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. * (3) MSVC rand() has MAX_RAND = 2^15 - 1, so it will not do * a proper permutation is the number of boxes exceeds this. */ 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); n = boxaGetCount(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; } /*! * boxaSwapBoxes() * * Input: boxa * i, j (two indices of boxes, that are to be swapped) * Return: 0 if OK, 1 on error */ l_int32 boxaSwapBoxes(BOXA *boxa, l_int32 i, l_int32 j) { l_int32 n; BOX *box; PROCNAME("boxaSwapBoxes"); if (!boxa) return ERROR_INT("boxa not defined", procName, 1); n = boxaGetCount(boxa); if (i < 0 || i >= n) return ERROR_INT("i invalid", procName, 1); if (j < 0 || j >= n) return ERROR_INT("j invalid", procName, 1); if (i == j) return ERROR_INT("i == j", procName, 1); box = boxa->box[i]; boxa->box[i] = boxa->box[j]; boxa->box[j] = box; return 0; } /*! * boxaConvertToPta() * * Input: boxa * ncorners (2 or 4 for the representation of each box) * Return: pta (with @ncorners points for each box in the boxa), * or null on error * * 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. */ PTA * boxaConvertToPta(BOXA *boxa, l_int32 ncorners) { l_int32 i, n, x, y, w, h; PTA *pta; PROCNAME("boxaConvertToPta"); if (!boxa) return (PTA *)ERROR_PTR("boxa not defined", procName, NULL); if (ncorners != 2 && ncorners != 4) return (PTA *)ERROR_PTR("ncorners not 2 or 4", procName, NULL); n = boxaGetCount(boxa); if ((pta = ptaCreate(n)) == NULL) return (PTA *)ERROR_PTR("pta not made", procName, NULL); for (i = 0; i < n; i++) { boxaGetBoxGeometry(boxa, i, &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; } /*! * ptaConvertToBoxa() * * Input: pta * ncorners (2 or 4 for the representation of each box) * Return: boxa (with one box for each 2 or 4 points in the pta), * or null on error * * Notes: * (1) For 2 corners, the order of the 2 points is UL, LR. * For 4 corners, the order of points is UL, UR, LL, LR. * (2) Each derived box is the minimum szie containing all corners. */ BOXA * ptaConvertToBoxa(PTA *pta, l_int32 ncorners) { l_int32 i, n, nbox, x1, y1, x2, y2, x3, y3, x4, y4, x, y, xmax, ymax; BOX *box; BOXA *boxa; PROCNAME("ptaConvertToBoxa"); if (!pta) return (BOXA *)ERROR_PTR("pta not defined", procName, NULL); if (ncorners != 2 && ncorners != 4) return (BOXA *)ERROR_PTR("ncorners not 2 or 4", procName, NULL); n = ptaGetCount(pta); if (n % ncorners != 0) return (BOXA *)ERROR_PTR("size % ncorners != 0", procName, NULL); nbox = n / ncorners; if ((boxa = boxaCreate(nbox)) == NULL) return (BOXA *)ERROR_PTR("boxa not made", procName, NULL); for (i = 0; i < n; i += ncorners) { ptaGetIPt(pta, i, &x1, &y1); ptaGetIPt(pta, i + 1, &x2, &y2); if (ncorners == 2) { box = boxCreate(x1, y1, x2 - x1 + 1, y2 - y1 + 1); boxaAddBox(boxa, box, L_INSERT); continue; } ptaGetIPt(pta, i + 2, &x3, &y3); ptaGetIPt(pta, i + 3, &x4, &y4); x = L_MIN(x1, x3); y = L_MIN(y1, y2); xmax = L_MAX(x2, x4); ymax = L_MAX(y3, y4); box = boxCreate(x, y, xmax - x + 1, ymax - y + 1); boxaAddBox(boxa, box, L_INSERT); } return boxa; }