mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-17 14:16:10 -06:00
1522 lines
43 KiB
C
1522 lines
43 KiB
C
/*====================================================================*
|
|
- 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;
|
|
}
|
|
|
|
|