blob: 0766545f5619c866a1dbfaae958e4ccb93432886 [file] [log] [blame]
/*====================================================================*
- 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;
}