Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/boxfunc1.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /*====================================================================* | |
| 2 - Copyright (C) 2001 Leptonica. All rights reserved. | |
| 3 - | |
| 4 - Redistribution and use in source and binary forms, with or without | |
| 5 - modification, are permitted provided that the following conditions | |
| 6 - are met: | |
| 7 - 1. Redistributions of source code must retain the above copyright | |
| 8 - notice, this list of conditions and the following disclaimer. | |
| 9 - 2. Redistributions in binary form must reproduce the above | |
| 10 - copyright notice, this list of conditions and the following | |
| 11 - disclaimer in the documentation and/or other materials | |
| 12 - provided with the distribution. | |
| 13 - | |
| 14 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 15 - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 16 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 17 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY | |
| 18 - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| 19 - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 20 - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| 21 - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
| 22 - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
| 23 - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
| 24 - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 25 *====================================================================*/ | |
| 26 | |
| 27 /*! | |
| 28 * \file boxfunc1.c | |
| 29 * <pre> | |
| 30 * | |
| 31 * Box geometry | |
| 32 * l_int32 boxContains() | |
| 33 * l_int32 boxIntersects() | |
| 34 * BOXA *boxaContainedInBox() | |
| 35 * l_int32 boxaContainedInBoxCount() | |
| 36 * l_int32 boxaContainedInBoxa() | |
| 37 * BOXA *boxaIntersectsBox() | |
| 38 * l_int32 boxaIntersectsBoxCount() | |
| 39 * BOXA *boxaClipToBox() | |
| 40 * BOXA *boxaCombineOverlaps() | |
| 41 * l_int32 boxaCombineOverlapsInPair() | |
| 42 * BOX *boxOverlapRegion() | |
| 43 * BOX *boxBoundingRegion() | |
| 44 * l_int32 boxOverlapFraction() | |
| 45 * l_int32 boxOverlapArea() | |
| 46 * BOXA *boxaHandleOverlaps() | |
| 47 * l_int32 boxOverlapDistance() | |
| 48 * l_int32 boxSeparationDistance() | |
| 49 * l_int32 boxCompareSize() | |
| 50 * l_int32 boxContainsPt() | |
| 51 * BOX *boxaGetNearestToPt() | |
| 52 * BOX *boxaGetNearestToLine() | |
| 53 * l_int32 boxaFindNearestBoxes() | |
| 54 * l_int32 boxaGetNearestByDirection() | |
| 55 * static l_int32 boxHasOverlapInXorY() | |
| 56 * static l_int32 boxGetDistanceInXorY() | |
| 57 * l_int32 boxIntersectByLine() | |
| 58 * l_int32 boxGetCenter() | |
| 59 * BOX *boxClipToRectangle() | |
| 60 * l_int32 boxClipToRectangleParams() | |
| 61 * BOX *boxRelocateOneSide() | |
| 62 * BOXA *boxaAdjustSides() | |
| 63 * BOXA *boxaAdjustBoxSides() | |
| 64 * BOX *boxAdjustSides() | |
| 65 * BOXA *boxaSetSide() | |
| 66 * l_int32 boxSetSide() | |
| 67 * BOXA *boxaAdjustWidthToTarget() | |
| 68 * BOXA *boxaAdjustHeightToTarget() | |
| 69 * l_int32 boxEqual() | |
| 70 * l_int32 boxaEqual() | |
| 71 * l_int32 boxSimilar() | |
| 72 * l_int32 boxaSimilar() | |
| 73 * | |
| 74 * Boxa combine and split | |
| 75 * l_int32 boxaJoin() | |
| 76 * l_int32 boxaaJoin() | |
| 77 * l_int32 boxaSplitEvenOdd() | |
| 78 * BOXA *boxaMergeEvenOdd() | |
| 79 * </pre> | |
| 80 */ | |
| 81 | |
| 82 #ifdef HAVE_CONFIG_H | |
| 83 #include <config_auto.h> | |
| 84 #endif /* HAVE_CONFIG_H */ | |
| 85 | |
| 86 #include "allheaders.h" | |
| 87 #include "pix_internal.h" | |
| 88 | |
| 89 static l_int32 boxHasOverlapInXorY(l_int32 c1, l_int32 s1, l_int32 c2, | |
| 90 l_int32 s2); | |
| 91 static l_int32 boxGetDistanceInXorY(l_int32 c1, l_int32 s1, l_int32 c2, | |
| 92 l_int32 s2); | |
| 93 | |
| 94 | |
| 95 /*---------------------------------------------------------------------* | |
| 96 * Box geometry * | |
| 97 *---------------------------------------------------------------------*/ | |
| 98 /*! | |
| 99 * \brief boxContains() | |
| 100 * | |
| 101 * \param[in] box1, box2 | |
| 102 * \param[out] presult 1 if box2 is entirely contained within box1; | |
| 103 * 0 otherwise | |
| 104 * \return 0 if OK, 1 on error | |
| 105 */ | |
| 106 l_ok | |
| 107 boxContains(BOX *box1, | |
| 108 BOX *box2, | |
| 109 l_int32 *presult) | |
| 110 { | |
| 111 l_int32 x1, y1, w1, h1, x2, y2, w2, h2, valid1, valid2; | |
| 112 | |
| 113 if (!presult) | |
| 114 return ERROR_INT("&result not defined", __func__, 1); | |
| 115 *presult = 0; | |
| 116 if (!box1 || !box2) | |
| 117 return ERROR_INT("boxes not both defined", __func__, 1); | |
| 118 boxIsValid(box1, &valid1); | |
| 119 boxIsValid(box2, &valid2); | |
| 120 if (!valid1 || !valid2) | |
| 121 return ERROR_INT("boxes not both valid", __func__, 1); | |
| 122 | |
| 123 boxGetGeometry(box1, &x1, &y1, &w1, &h1); | |
| 124 boxGetGeometry(box2, &x2, &y2, &w2, &h2); | |
| 125 if (x1 <= x2 && y1 <= y2 && (x1 + w1 >= x2 + w2) && (y1 + h1 >= y2 + h2)) | |
| 126 *presult = 1; | |
| 127 return 0; | |
| 128 } | |
| 129 | |
| 130 | |
| 131 /*! | |
| 132 * \brief boxIntersects() | |
| 133 * | |
| 134 * \param[in] box1, box2 | |
| 135 * \param[out] presult 1 if any part of box2 is contained in box1; | |
| 136 * 0 otherwise | |
| 137 * \return 0 if OK, 1 on error | |
| 138 */ | |
| 139 l_ok | |
| 140 boxIntersects(BOX *box1, | |
| 141 BOX *box2, | |
| 142 l_int32 *presult) | |
| 143 { | |
| 144 l_int32 l1, l2, r1, r2, t1, t2, b1, b2, w1, h1, w2, h2, valid1, valid2; | |
| 145 | |
| 146 if (!presult) | |
| 147 return ERROR_INT("&result not defined", __func__, 1); | |
| 148 *presult = 0; | |
| 149 if (!box1 || !box2) | |
| 150 return ERROR_INT("boxes not both defined", __func__, 1); | |
| 151 boxIsValid(box1, &valid1); | |
| 152 boxIsValid(box2, &valid2); | |
| 153 if (!valid1 || !valid2) | |
| 154 return ERROR_INT("boxes not both valid", __func__, 1); | |
| 155 | |
| 156 boxGetGeometry(box1, &l1, &t1, &w1, &h1); | |
| 157 boxGetGeometry(box2, &l2, &t2, &w2, &h2); | |
| 158 r1 = l1 + w1 - 1; | |
| 159 r2 = l2 + w2 - 1; | |
| 160 b1 = t1 + h1 - 1; | |
| 161 b2 = t2 + h2 - 1; | |
| 162 if (b2 < t1 || b1 < t2 || r1 < l2 || r2 < l1) | |
| 163 *presult = 0; | |
| 164 else | |
| 165 *presult = 1; | |
| 166 return 0; | |
| 167 } | |
| 168 | |
| 169 | |
| 170 /*! | |
| 171 * \brief boxaContainedInBox() | |
| 172 * | |
| 173 * \param[in] boxas | |
| 174 * \param[in] box for containment | |
| 175 * \return boxad boxa with all boxes in boxas that are entirely | |
| 176 * contained in box, or NULL on error | |
| 177 * | |
| 178 * <pre> | |
| 179 * Notes: | |
| 180 * (1) All boxes in %boxas that are entirely outside box are removed. | |
| 181 * (2) If %box is not valid, returns an empty boxa. | |
| 182 * </pre> | |
| 183 */ | |
| 184 BOXA * | |
| 185 boxaContainedInBox(BOXA *boxas, | |
| 186 BOX *box) | |
| 187 { | |
| 188 l_int32 i, n, val, valid; | |
| 189 BOX *box1; | |
| 190 BOXA *boxad; | |
| 191 | |
| 192 if (!boxas) | |
| 193 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); | |
| 194 if (!box) | |
| 195 return (BOXA *)ERROR_PTR("box not defined", __func__, NULL); | |
| 196 n = boxaGetCount(boxas); | |
| 197 boxIsValid(box, &valid); | |
| 198 if (n == 0 || !valid) | |
| 199 return boxaCreate(1); /* empty */ | |
| 200 | |
| 201 boxad = boxaCreate(0); | |
| 202 for (i = 0; i < n; i++) { | |
| 203 if ((box1 = boxaGetValidBox(boxas, i, L_CLONE)) == NULL) | |
| 204 continue; | |
| 205 boxContains(box, box1, &val); | |
| 206 if (val == 1) | |
| 207 boxaAddBox(boxad, box1, L_COPY); | |
| 208 boxDestroy(&box1); /* destroy the clone */ | |
| 209 } | |
| 210 | |
| 211 return boxad; | |
| 212 } | |
| 213 | |
| 214 | |
| 215 /*! | |
| 216 * \brief boxaContainedInBoxCount() | |
| 217 * | |
| 218 * \param[in] boxa | |
| 219 * \param[in] box for selecting contained boxes in %boxa | |
| 220 * \param[out] pcount number of boxes intersecting the box | |
| 221 * \return 0 if OK, 1 on error | |
| 222 * | |
| 223 * <pre> | |
| 224 * Notes: | |
| 225 * (1) If %box is not valid, returns a zero count. | |
| 226 * </pre> | |
| 227 */ | |
| 228 l_ok | |
| 229 boxaContainedInBoxCount(BOXA *boxa, | |
| 230 BOX *box, | |
| 231 l_int32 *pcount) | |
| 232 { | |
| 233 l_int32 i, n, val, valid; | |
| 234 BOX *box1; | |
| 235 | |
| 236 if (!pcount) | |
| 237 return ERROR_INT("&count not defined", __func__, 1); | |
| 238 *pcount = 0; | |
| 239 if (!boxa) | |
| 240 return ERROR_INT("boxa not defined", __func__, 1); | |
| 241 if (!box) | |
| 242 return ERROR_INT("box not defined", __func__, 1); | |
| 243 n = boxaGetCount(boxa); | |
| 244 boxIsValid(box, &valid); | |
| 245 if (n == 0 || !valid) | |
| 246 return 0; | |
| 247 | |
| 248 for (i = 0; i < n; i++) { | |
| 249 if ((box1 = boxaGetValidBox(boxa, i, L_CLONE)) == NULL) | |
| 250 continue; | |
| 251 boxContains(box, box1, &val); | |
| 252 if (val == 1) | |
| 253 (*pcount)++; | |
| 254 boxDestroy(&box1); | |
| 255 } | |
| 256 return 0; | |
| 257 } | |
| 258 | |
| 259 | |
| 260 /*! | |
| 261 * \brief boxaContainedInBoxa() | |
| 262 * | |
| 263 * \param[in] boxa1, boxa2 | |
| 264 * \param[out] pcontained 1 if every box in boxa2 is contained in | |
| 265 * some box in boxa1; 0 otherwise | |
| 266 * \return 0 if OK, 1 on error | |
| 267 */ | |
| 268 l_ok | |
| 269 boxaContainedInBoxa(BOXA *boxa1, | |
| 270 BOXA *boxa2, | |
| 271 l_int32 *pcontained) | |
| 272 { | |
| 273 l_int32 i, j, n1, n2, cont, result; | |
| 274 BOX *box1, *box2; | |
| 275 | |
| 276 if (!pcontained) | |
| 277 return ERROR_INT("&contained not defined", __func__, 1); | |
| 278 *pcontained = 0; | |
| 279 if (!boxa1 || !boxa2) | |
| 280 return ERROR_INT("boxa1 and boxa2 not both defined", __func__, 1); | |
| 281 | |
| 282 n1 = boxaGetCount(boxa1); | |
| 283 n2 = boxaGetCount(boxa2); | |
| 284 for (i = 0; i < n2; i++) { | |
| 285 if ((box2 = boxaGetValidBox(boxa2, i, L_CLONE)) == NULL) | |
| 286 continue; | |
| 287 cont = 0; | |
| 288 for (j = 0; j < n1; j++) { | |
| 289 if ((box1 = boxaGetValidBox(boxa1, j, L_CLONE)) == NULL) | |
| 290 continue; | |
| 291 boxContains(box1, box2, &result); | |
| 292 boxDestroy(&box1); | |
| 293 if (result) { | |
| 294 cont = 1; | |
| 295 break; | |
| 296 } | |
| 297 } | |
| 298 boxDestroy(&box2); | |
| 299 if (!cont) return 0; | |
| 300 } | |
| 301 | |
| 302 *pcontained = 1; | |
| 303 return 0; | |
| 304 } | |
| 305 | |
| 306 | |
| 307 /*! | |
| 308 * \brief boxaIntersectsBox() | |
| 309 * | |
| 310 * \param[in] boxas | |
| 311 * \param[in] box for intersecting | |
| 312 * \return boxad boxa with all boxes in boxas that intersect box, | |
| 313 * or NULL on error | |
| 314 * | |
| 315 * <pre> | |
| 316 * Notes: | |
| 317 * (1) All boxes in boxa that intersect with box (i.e., are completely | |
| 318 * or partially contained in box) are retained. | |
| 319 * </pre> | |
| 320 */ | |
| 321 BOXA * | |
| 322 boxaIntersectsBox(BOXA *boxas, | |
| 323 BOX *box) | |
| 324 { | |
| 325 l_int32 i, n, val, valid; | |
| 326 BOX *box1; | |
| 327 BOXA *boxad; | |
| 328 | |
| 329 if (!boxas) | |
| 330 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); | |
| 331 if (!box) | |
| 332 return (BOXA *)ERROR_PTR("box not defined", __func__, NULL); | |
| 333 n = boxaGetCount(boxas); | |
| 334 boxIsValid(box, &valid); | |
| 335 if (n == 0 || !valid) | |
| 336 return boxaCreate(1); /* empty */ | |
| 337 | |
| 338 boxad = boxaCreate(0); | |
| 339 for (i = 0; i < n; i++) { | |
| 340 if ((box1 = boxaGetValidBox(boxas, i, L_CLONE)) == NULL) | |
| 341 continue; | |
| 342 boxIntersects(box, box1, &val); | |
| 343 if (val == 1) | |
| 344 boxaAddBox(boxad, box1, L_COPY); | |
| 345 boxDestroy(&box1); /* destroy the clone */ | |
| 346 } | |
| 347 | |
| 348 return boxad; | |
| 349 } | |
| 350 | |
| 351 | |
| 352 /*! | |
| 353 * \brief boxaIntersectsBoxCount() | |
| 354 * | |
| 355 * \param[in] boxa | |
| 356 * \param[in] box for selecting intersecting boxes in %boxa | |
| 357 * \param[out] pcount number of boxes intersecting the box | |
| 358 * \return 0 if OK, 1 on error | |
| 359 */ | |
| 360 l_ok | |
| 361 boxaIntersectsBoxCount(BOXA *boxa, | |
| 362 BOX *box, | |
| 363 l_int32 *pcount) | |
| 364 { | |
| 365 l_int32 i, n, val, valid; | |
| 366 BOX *box1; | |
| 367 | |
| 368 if (!pcount) | |
| 369 return ERROR_INT("&count not defined", __func__, 1); | |
| 370 *pcount = 0; | |
| 371 if (!boxa) | |
| 372 return ERROR_INT("boxa not defined", __func__, 1); | |
| 373 if (!box) | |
| 374 return ERROR_INT("box not defined", __func__, 1); | |
| 375 n = boxaGetCount(boxa); | |
| 376 boxIsValid(box, &valid); | |
| 377 if (n == 0 || !valid) | |
| 378 return 0; | |
| 379 | |
| 380 for (i = 0; i < n; i++) { | |
| 381 if ((box1 = boxaGetValidBox(boxa, i, L_CLONE)) == NULL) | |
| 382 continue; | |
| 383 boxIntersects(box, box1, &val); | |
| 384 if (val == 1) | |
| 385 (*pcount)++; | |
| 386 boxDestroy(&box1); | |
| 387 } | |
| 388 return 0; | |
| 389 } | |
| 390 | |
| 391 | |
| 392 /*! | |
| 393 * \brief boxaClipToBox() | |
| 394 * | |
| 395 * \param[in] boxas | |
| 396 * \param[in] box for clipping | |
| 397 * \return boxad boxa with boxes in boxas clipped to box, or NULL on error | |
| 398 * | |
| 399 * <pre> | |
| 400 * Notes: | |
| 401 * (1) All boxes in boxa not intersecting with box are removed, and | |
| 402 * the remaining boxes are clipped to box. | |
| 403 * </pre> | |
| 404 */ | |
| 405 BOXA * | |
| 406 boxaClipToBox(BOXA *boxas, | |
| 407 BOX *box) | |
| 408 { | |
| 409 l_int32 i, n, valid; | |
| 410 BOX *box1, *boxo; | |
| 411 BOXA *boxad; | |
| 412 | |
| 413 if (!boxas) | |
| 414 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); | |
| 415 if (!box) | |
| 416 return (BOXA *)ERROR_PTR("box not defined", __func__, NULL); | |
| 417 n = boxaGetCount(boxas); | |
| 418 boxIsValid(box, &valid); | |
| 419 if (n == 0 || !valid) | |
| 420 return boxaCreate(1); /* empty */ | |
| 421 | |
| 422 boxad = boxaCreate(0); | |
| 423 for (i = 0; i < n; i++) { | |
| 424 if ((box1 = boxaGetValidBox(boxas, i, L_CLONE)) == NULL) | |
| 425 continue; | |
| 426 if ((boxo = boxOverlapRegion(box, box1)) != NULL) | |
| 427 boxaAddBox(boxad, boxo, L_INSERT); | |
| 428 boxDestroy(&box1); | |
| 429 } | |
| 430 | |
| 431 return boxad; | |
| 432 } | |
| 433 | |
| 434 | |
| 435 /*! | |
| 436 * \brief boxaCombineOverlaps() | |
| 437 * | |
| 438 * \param[in] boxas | |
| 439 * \param[in,out] pixadb debug output | |
| 440 * \return boxad where each set of boxes in boxas that overlap are combined | |
| 441 * into a single bounding box in boxad, or NULL on error. | |
| 442 * | |
| 443 * <pre> | |
| 444 * Notes: | |
| 445 * (1) If there are no overlapping boxes, it simply returns a copy | |
| 446 * of %boxas. | |
| 447 * (2) Input an empty %pixadb, using pixaCreate(0), for debug output. | |
| 448 * The output gives 2 visualizations of the boxes per iteration; | |
| 449 * boxes in red before, and added boxes in green after. Note that | |
| 450 * all pixels in the red boxes are contained in the green ones. | |
| 451 * (3) The alternative method of painting each rectangle and finding | |
| 452 * the 4-connected components gives a different result in | |
| 453 * general, because two non-overlapping (but touching) | |
| 454 * rectangles, when rendered, are 4-connected and will be joined. | |
| 455 * (4) A bad case computationally is to have n boxes, none of which | |
| 456 * overlap. Then you have one iteration with O(n^2) compares. | |
| 457 * This is still faster than painting each rectangle and finding | |
| 458 * the bounding boxes of the connected components, even for | |
| 459 * thousands of rectangles. | |
| 460 * </pre> | |
| 461 */ | |
| 462 BOXA * | |
| 463 boxaCombineOverlaps(BOXA *boxas, | |
| 464 PIXA *pixadb) | |
| 465 { | |
| 466 l_int32 i, j, w, h, n1, n2, overlap, niters; | |
| 467 BOX *box1, *box2, *box3; | |
| 468 BOXA *boxa1, *boxa2; | |
| 469 PIX *pix1 = NULL; | |
| 470 | |
| 471 if (!boxas) | |
| 472 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); | |
| 473 | |
| 474 if (pixadb) boxaGetExtent(boxas, &w, &h, NULL); | |
| 475 | |
| 476 boxa1 = boxaCopy(boxas, L_COPY); | |
| 477 n1 = boxaGetCount(boxa1); | |
| 478 niters = 0; | |
| 479 while (1) { /* loop until no change from previous iteration */ | |
| 480 niters++; | |
| 481 if (pixadb) { | |
| 482 pix1 = pixCreate(w + 5, h + 5, 32); | |
| 483 pixSetAll(pix1); | |
| 484 pixRenderBoxaArb(pix1, boxa1, 2, 255, 0, 0); | |
| 485 pixaAddPix(pixadb, pix1, L_COPY); | |
| 486 } | |
| 487 | |
| 488 /* Combine overlaps for this iteration */ | |
| 489 for (i = 0; i < n1; i++) { | |
| 490 if ((box1 = boxaGetValidBox(boxa1, i, L_COPY)) == NULL) | |
| 491 continue; | |
| 492 for (j = i + 1; j < n1; j++) { | |
| 493 if ((box2 = boxaGetValidBox(boxa1, j, L_COPY)) == NULL) | |
| 494 continue; | |
| 495 boxIntersects(box1, box2, &overlap); | |
| 496 if (overlap) { | |
| 497 box3 = boxBoundingRegion(box1, box2); | |
| 498 boxaReplaceBox(boxa1, i, box3); | |
| 499 boxaReplaceBox(boxa1, j, boxCreate(0, 0, 0, 0)); | |
| 500 boxDestroy(&box1); | |
| 501 box1 = boxCopy(box3); | |
| 502 } | |
| 503 boxDestroy(&box2); | |
| 504 } | |
| 505 boxDestroy(&box1); | |
| 506 } | |
| 507 boxa2 = boxaSaveValid(boxa1, L_COPY); | |
| 508 n2 = boxaGetCount(boxa2); | |
| 509 boxaDestroy(&boxa1); | |
| 510 boxa1 = boxa2; | |
| 511 if (n1 == n2) { | |
| 512 if (pixadb) pixDestroy(&pix1); | |
| 513 break; | |
| 514 } | |
| 515 n1 = n2; | |
| 516 if (pixadb) { | |
| 517 pixRenderBoxaArb(pix1, boxa1, 2, 0, 255, 0); | |
| 518 pixaAddPix(pixadb, pix1, L_INSERT); | |
| 519 } | |
| 520 } | |
| 521 | |
| 522 if (pixadb) | |
| 523 L_INFO("number of iterations: %d\n", __func__, niters); | |
| 524 return boxa1; | |
| 525 } | |
| 526 | |
| 527 | |
| 528 /*! | |
| 529 * \brief boxaCombineOverlapsInPair() | |
| 530 * | |
| 531 * \param[in] boxas1 input boxa1 | |
| 532 * \param[in] boxas2 input boxa2 | |
| 533 * \param[out] pboxad1 output boxa1 | |
| 534 * \param[out] pboxad2 output boxa2 | |
| 535 * \param[in,out] pixadb debug output | |
| 536 * \return 0 if OK, 1 on error | |
| 537 * | |
| 538 * <pre> | |
| 539 * Notes: | |
| 540 * (1) One of three things happens to each box in %boxa1 and %boxa2: | |
| 541 * * it gets absorbed into a larger box that it overlaps with | |
| 542 * * it absorbs a smaller (by area) box that it overlaps with | |
| 543 * and gets larger, using the bounding region of the 2 boxes | |
| 544 * * it is unchanged (including absorbing smaller boxes that | |
| 545 * are contained within it). | |
| 546 * (2) If all the boxes from one of the input boxa are absorbed, this | |
| 547 * returns an empty boxa. | |
| 548 * (3) Input an empty %pixadb, using pixaCreate(0), for debug output | |
| 549 * (4) This is useful if different operations are to be carried out | |
| 550 * on possibly overlapping rectangular regions, and it is desired | |
| 551 * to have only one operation on any rectangular region. | |
| 552 * </pre> | |
| 553 */ | |
| 554 l_ok | |
| 555 boxaCombineOverlapsInPair(BOXA *boxas1, | |
| 556 BOXA *boxas2, | |
| 557 BOXA **pboxad1, | |
| 558 BOXA **pboxad2, | |
| 559 PIXA *pixadb) | |
| 560 { | |
| 561 l_int32 i, j, w, h, w2, h2, n1, n2, n1i, n2i, niters; | |
| 562 l_int32 overlap, bigger, area1, area2; | |
| 563 BOX *box1, *box2, *box3; | |
| 564 BOXA *boxa1, *boxa2, *boxac1, *boxac2; | |
| 565 PIX *pix1; | |
| 566 | |
| 567 if (pboxad1) *pboxad1 = NULL; | |
| 568 if (pboxad2) *pboxad2 = NULL; | |
| 569 if (!boxas1 || !boxas2) | |
| 570 return ERROR_INT("boxas1 and boxas2 not both defined", __func__, 1); | |
| 571 if (!pboxad1 || !pboxad2) | |
| 572 return ERROR_INT("&boxad1 and &boxad2 not both defined", __func__, 1); | |
| 573 | |
| 574 if (pixadb) { | |
| 575 boxaGetExtent(boxas1, &w, &h, NULL); | |
| 576 boxaGetExtent(boxas2, &w2, &h2, NULL); | |
| 577 w = L_MAX(w, w2); | |
| 578 h = L_MAX(h, w2); | |
| 579 } | |
| 580 | |
| 581 /* Let the boxa with the largest area have first crack at the other */ | |
| 582 boxaGetArea(boxas1, &area1); | |
| 583 boxaGetArea(boxas2, &area2); | |
| 584 if (area1 >= area2) { | |
| 585 boxac1 = boxaCopy(boxas1, L_COPY); | |
| 586 boxac2 = boxaCopy(boxas2, L_COPY); | |
| 587 } else { | |
| 588 boxac1 = boxaCopy(boxas2, L_COPY); | |
| 589 boxac2 = boxaCopy(boxas1, L_COPY); | |
| 590 } | |
| 591 | |
| 592 n1i = boxaGetCount(boxac1); | |
| 593 n2i = boxaGetCount(boxac2); | |
| 594 niters = 0; | |
| 595 while (1) { | |
| 596 niters++; | |
| 597 if (pixadb) { | |
| 598 pix1 = pixCreate(w + 5, h + 5, 32); | |
| 599 pixSetAll(pix1); | |
| 600 pixRenderBoxaArb(pix1, boxac1, 2, 255, 0, 0); | |
| 601 pixRenderBoxaArb(pix1, boxac2, 2, 0, 255, 0); | |
| 602 pixaAddPix(pixadb, pix1, L_INSERT); | |
| 603 } | |
| 604 | |
| 605 /* First combine boxes in each set */ | |
| 606 boxa1 = boxaCombineOverlaps(boxac1, NULL); | |
| 607 boxa2 = boxaCombineOverlaps(boxac2, NULL); | |
| 608 | |
| 609 /* Now combine boxes between sets */ | |
| 610 n1 = boxaGetCount(boxa1); | |
| 611 n2 = boxaGetCount(boxa2); | |
| 612 for (i = 0; i < n1; i++) { /* 1 eats 2 */ | |
| 613 if ((box1 = boxaGetValidBox(boxa1, i, L_COPY)) == NULL) | |
| 614 continue; | |
| 615 for (j = 0; j < n2; j++) { | |
| 616 if ((box2 = boxaGetValidBox(boxa2, j, L_COPY)) == NULL) | |
| 617 continue; | |
| 618 boxIntersects(box1, box2, &overlap); | |
| 619 boxCompareSize(box1, box2, L_SORT_BY_AREA, &bigger); | |
| 620 if (overlap && (bigger == 1)) { | |
| 621 box3 = boxBoundingRegion(box1, box2); | |
| 622 boxaReplaceBox(boxa1, i, box3); | |
| 623 boxaReplaceBox(boxa2, j, boxCreate(0, 0, 0, 0)); | |
| 624 boxDestroy(&box1); | |
| 625 box1 = boxCopy(box3); | |
| 626 } | |
| 627 boxDestroy(&box2); | |
| 628 } | |
| 629 boxDestroy(&box1); | |
| 630 } | |
| 631 for (i = 0; i < n2; i++) { /* 2 eats 1 */ | |
| 632 if ((box2 = boxaGetValidBox(boxa2, i, L_COPY)) == NULL) | |
| 633 continue; | |
| 634 for (j = 0; j < n1; j++) { | |
| 635 if ((box1 = boxaGetValidBox(boxa1, j, L_COPY)) == NULL) | |
| 636 continue; | |
| 637 boxIntersects(box1, box2, &overlap); | |
| 638 boxCompareSize(box2, box1, L_SORT_BY_AREA, &bigger); | |
| 639 if (overlap && (bigger == 1)) { | |
| 640 box3 = boxBoundingRegion(box1, box2); | |
| 641 boxaReplaceBox(boxa2, i, box3); | |
| 642 boxaReplaceBox(boxa1, j, boxCreate(0, 0, 0, 0)); | |
| 643 boxDestroy(&box2); | |
| 644 box2 = boxCopy(box3); | |
| 645 } | |
| 646 boxDestroy(&box1); | |
| 647 } | |
| 648 boxDestroy(&box2); | |
| 649 } | |
| 650 boxaDestroy(&boxac1); | |
| 651 boxaDestroy(&boxac2); | |
| 652 boxac1 = boxaSaveValid(boxa1, L_COPY); /* remove invalid boxes */ | |
| 653 boxac2 = boxaSaveValid(boxa2, L_COPY); | |
| 654 boxaDestroy(&boxa1); | |
| 655 boxaDestroy(&boxa2); | |
| 656 n1 = boxaGetCount(boxac1); | |
| 657 n2 = boxaGetCount(boxac2); | |
| 658 if (n1 == n1i && n2 == n2i) break; | |
| 659 n1i = n1; | |
| 660 n2i = n2; | |
| 661 if (pixadb) { | |
| 662 pix1 = pixCreate(w + 5, h + 5, 32); | |
| 663 pixSetAll(pix1); | |
| 664 pixRenderBoxaArb(pix1, boxac1, 2, 255, 0, 0); | |
| 665 pixRenderBoxaArb(pix1, boxac2, 2, 0, 255, 0); | |
| 666 pixaAddPix(pixadb, pix1, L_INSERT); | |
| 667 } | |
| 668 } | |
| 669 | |
| 670 if (pixadb) | |
| 671 L_INFO("number of iterations: %d\n", __func__, niters); | |
| 672 *pboxad1 = boxac1; | |
| 673 *pboxad2 = boxac2; | |
| 674 return 0; | |
| 675 } | |
| 676 | |
| 677 | |
| 678 /*! | |
| 679 * \brief boxOverlapRegion() | |
| 680 * | |
| 681 * \param[in] box1, box2 | |
| 682 * \return box of overlap region between input boxes; | |
| 683 * NULL if no overlap or on error | |
| 684 * | |
| 685 * <pre> | |
| 686 * Notes: | |
| 687 * (1) This is the geometric intersection of the two rectangles. | |
| 688 * </pre> | |
| 689 */ | |
| 690 BOX * | |
| 691 boxOverlapRegion(BOX *box1, | |
| 692 BOX *box2) | |
| 693 { | |
| 694 l_int32 l1, l2, r1, r2, t1, t2, b1, b2, w1, h1, w2, h2, ld, td, rd, bd; | |
| 695 l_int32 valid1, valid2; | |
| 696 | |
| 697 if (!box1 || !box2) | |
| 698 return (BOX *)ERROR_PTR("boxes not both defined", __func__, NULL); | |
| 699 boxIsValid(box1, &valid1); | |
| 700 boxIsValid(box2, &valid2); | |
| 701 if (!valid1 || !valid2) { | |
| 702 L_WARNING("at least one box is invalid\n", __func__); | |
| 703 return NULL; | |
| 704 } | |
| 705 | |
| 706 boxGetGeometry(box1, &l1, &t1, &w1, &h1); | |
| 707 boxGetGeometry(box2, &l2, &t2, &w2, &h2); | |
| 708 r1 = l1 + w1 - 1; | |
| 709 r2 = l2 + w2 - 1; | |
| 710 b1 = t1 + h1 - 1; | |
| 711 b2 = t2 + h2 - 1; | |
| 712 if (b2 < t1 || b1 < t2 || r1 < l2 || r2 < l1) | |
| 713 return NULL; | |
| 714 | |
| 715 ld = L_MAX(l1, l2); | |
| 716 td = L_MAX(t1, t2); | |
| 717 rd = L_MIN(r1, r2); | |
| 718 bd = L_MIN(b1, b2); | |
| 719 return boxCreate(ld, td, rd - ld + 1, bd - td + 1); | |
| 720 } | |
| 721 | |
| 722 | |
| 723 /*! | |
| 724 * \brief boxBoundingRegion() | |
| 725 * | |
| 726 * \param[in] box1, box2 | |
| 727 * \return box of bounding region containing the input boxes; | |
| 728 * NULL on error | |
| 729 * | |
| 730 * <pre> | |
| 731 * Notes: | |
| 732 * (1) This is the geometric union of the two rectangles. | |
| 733 * (2) Invalid boxes are ignored. This returns an invalid box | |
| 734 * if both input boxes are invalid. | |
| 735 * (3) For the geometric union of a boxa, use boxaGetExtent(). | |
| 736 * </pre> | |
| 737 */ | |
| 738 BOX * | |
| 739 boxBoundingRegion(BOX *box1, | |
| 740 BOX *box2) | |
| 741 { | |
| 742 l_int32 l1, l2, r1, r2, t1, t2, b1, b2, w1, h1, w2, h2, ld, td, rd, bd; | |
| 743 l_int32 valid1, valid2; | |
| 744 | |
| 745 if (!box1 || !box2) | |
| 746 return (BOX *)ERROR_PTR("boxes not both defined", __func__, NULL); | |
| 747 boxIsValid(box1, &valid1); | |
| 748 boxIsValid(box2, &valid2); | |
| 749 if (!valid1 && !valid2) { | |
| 750 L_WARNING("both boxes are invalid\n", __func__); | |
| 751 return boxCreate(0, 0, 0, 0); | |
| 752 } | |
| 753 if (valid1 && !valid2) | |
| 754 return boxCopy(box1); | |
| 755 if (!valid1 && valid2) | |
| 756 return boxCopy(box2); | |
| 757 | |
| 758 boxGetGeometry(box1, &l1, &t1, &w1, &h1); | |
| 759 boxGetGeometry(box2, &l2, &t2, &w2, &h2); | |
| 760 r1 = l1 + w1 - 1; | |
| 761 r2 = l2 + w2 - 1; | |
| 762 b1 = t1 + h1 - 1; | |
| 763 b2 = t2 + h2 - 1; | |
| 764 ld = L_MIN(l1, l2); | |
| 765 td = L_MIN(t1, t2); | |
| 766 rd = L_MAX(r1, r2); | |
| 767 bd = L_MAX(b1, b2); | |
| 768 return boxCreate(ld, td, rd - ld + 1, bd - td + 1); | |
| 769 } | |
| 770 | |
| 771 | |
| 772 /*! | |
| 773 * \brief boxOverlapFraction() | |
| 774 * | |
| 775 * \param[in] box1, box2 | |
| 776 * \param[out] pfract the fraction of box2 overlapped by box1 | |
| 777 * \return 0 if OK, 1 on error. | |
| 778 * | |
| 779 * <pre> | |
| 780 * Notes: | |
| 781 * (1) The result depends on the order of the input boxes, | |
| 782 * because the overlap is taken as a fraction of box2. | |
| 783 * (2) If at least one box is not valid, there is no overlap. | |
| 784 * </pre> | |
| 785 */ | |
| 786 l_ok | |
| 787 boxOverlapFraction(BOX *box1, | |
| 788 BOX *box2, | |
| 789 l_float32 *pfract) | |
| 790 { | |
| 791 l_int32 w2, h2, w, h, valid1, valid2; | |
| 792 BOX *boxo; | |
| 793 | |
| 794 if (!pfract) | |
| 795 return ERROR_INT("&fract not defined", __func__, 1); | |
| 796 *pfract = 0.0; | |
| 797 if (!box1 || !box2) | |
| 798 return ERROR_INT("boxes not both defined", __func__, 1); | |
| 799 boxIsValid(box1, &valid1); | |
| 800 boxIsValid(box2, &valid2); | |
| 801 if (!valid1 || !valid2) { | |
| 802 L_WARNING("boxes not both valid\n", __func__); | |
| 803 return 0; | |
| 804 } | |
| 805 | |
| 806 if ((boxo = boxOverlapRegion(box1, box2)) == NULL) /* no overlap */ | |
| 807 return 0; | |
| 808 | |
| 809 boxGetGeometry(box2, NULL, NULL, &w2, &h2); | |
| 810 boxGetGeometry(boxo, NULL, NULL, &w, &h); | |
| 811 *pfract = (l_float32)(w * h) / (l_float32)(w2 * h2); | |
| 812 boxDestroy(&boxo); | |
| 813 return 0; | |
| 814 } | |
| 815 | |
| 816 | |
| 817 /*! | |
| 818 * \brief boxOverlapArea() | |
| 819 * | |
| 820 * \param[in] box1, box2 | |
| 821 * \param[out] parea the number of pixels in the overlap | |
| 822 * \return 0 if OK, 1 on error. | |
| 823 */ | |
| 824 l_ok | |
| 825 boxOverlapArea(BOX *box1, | |
| 826 BOX *box2, | |
| 827 l_int32 *parea) | |
| 828 { | |
| 829 l_int32 w, h, valid1, valid2; | |
| 830 BOX *box; | |
| 831 | |
| 832 if (!parea) | |
| 833 return ERROR_INT("&area not defined", __func__, 1); | |
| 834 *parea = 0; | |
| 835 if (!box1 || !box2) | |
| 836 return ERROR_INT("boxes not both defined", __func__, 1); | |
| 837 boxIsValid(box1, &valid1); | |
| 838 boxIsValid(box2, &valid2); | |
| 839 if (!valid1 || !valid2) | |
| 840 return ERROR_INT("boxes not both valid", __func__, 1); | |
| 841 | |
| 842 if ((box = boxOverlapRegion(box1, box2)) == NULL) /* no overlap */ | |
| 843 return 0; | |
| 844 | |
| 845 boxGetGeometry(box, NULL, NULL, &w, &h); | |
| 846 *parea = w * h; | |
| 847 boxDestroy(&box); | |
| 848 return 0; | |
| 849 } | |
| 850 | |
| 851 | |
| 852 /*! | |
| 853 * \brief boxaHandleOverlaps() | |
| 854 * | |
| 855 * \param[in] boxas | |
| 856 * \param[in] op L_COMBINE, L_REMOVE_SMALL | |
| 857 * \param[in] range forward distance over which overlaps | |
| 858 * are checked; > 0 | |
| 859 * \param[in] min_overlap minimum fraction of smaller box required for | |
| 860 * overlap to count; 0.0 to ignore | |
| 861 * \param[in] max_ratio maximum fraction of small/large areas for | |
| 862 * overlap to count; 1.0 to ignore | |
| 863 * \param[out] pnamap [optional] combining map | |
| 864 * \return boxad, or NULL on error. | |
| 865 * | |
| 866 * <pre> | |
| 867 * Notes: | |
| 868 * (1) For all n(n-1)/2 box pairings, if two boxes overlap, either: | |
| 869 * (a) op == L_COMBINE: get the bounding region for the two, | |
| 870 * replace the larger with the bounding region, and remove | |
| 871 * the smaller of the two, or | |
| 872 * (b) op == L_REMOVE_SMALL: just remove the smaller. | |
| 873 * (2) If boxas is 2D sorted, range can be small, but if it is | |
| 874 * not spatially sorted, range should be large to allow all | |
| 875 * pairwise comparisons to be made. | |
| 876 * (3) The %min_overlap parameter allows ignoring small overlaps. | |
| 877 * If %min_overlap == 1.0, only boxes fully contained in larger | |
| 878 * boxes can be considered for removal; if %min_overlap == 0.0, | |
| 879 * this constraint is ignored. | |
| 880 * (4) The %max_ratio parameter allows ignoring overlaps between | |
| 881 * boxes that are not too different in size. If %max_ratio == 0.0, | |
| 882 * no boxes can be removed; if %max_ratio == 1.0, this constraint | |
| 883 * is ignored. | |
| 884 * </pre> | |
| 885 */ | |
| 886 BOXA * | |
| 887 boxaHandleOverlaps(BOXA *boxas, | |
| 888 l_int32 op, | |
| 889 l_int32 range, | |
| 890 l_float32 min_overlap, | |
| 891 l_float32 max_ratio, | |
| 892 NUMA **pnamap) | |
| 893 { | |
| 894 l_int32 i, j, n, w, h, area1, area2, val; | |
| 895 l_int32 overlap_area; | |
| 896 l_float32 overlap_ratio, area_ratio; | |
| 897 BOX *box1, *box2, *box3; | |
| 898 BOXA *boxat, *boxad; | |
| 899 NUMA *namap; | |
| 900 | |
| 901 if (pnamap) *pnamap = NULL; | |
| 902 if (!boxas) | |
| 903 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); | |
| 904 if (op != L_COMBINE && op != L_REMOVE_SMALL) | |
| 905 return (BOXA *)ERROR_PTR("invalid op", __func__, NULL); | |
| 906 | |
| 907 n = boxaGetCount(boxas); | |
| 908 if (n == 0) | |
| 909 return boxaCreate(1); /* empty */ | |
| 910 if (range == 0) { | |
| 911 L_WARNING("range is 0\n", __func__); | |
| 912 return boxaCopy(boxas, L_COPY); | |
| 913 } | |
| 914 | |
| 915 /* Identify smaller boxes in overlap pairs, and mark to eliminate. */ | |
| 916 namap = numaMakeConstant(-1, n); | |
| 917 for (i = 0; i < n; i++) { | |
| 918 if ((box1 = boxaGetValidBox(boxas, i, L_CLONE)) == NULL) | |
| 919 continue; | |
| 920 boxGetGeometry(box1, NULL, NULL, &w, &h); | |
| 921 area1 = w * h; | |
| 922 if (area1 == 0) { | |
| 923 boxDestroy(&box1); | |
| 924 continue; | |
| 925 } | |
| 926 for (j = i + 1; j < i + 1 + range && j < n; j++) { | |
| 927 if ((box2 = boxaGetValidBox(boxas, j, L_CLONE)) == NULL) | |
| 928 continue; | |
| 929 boxOverlapArea(box1, box2, &overlap_area); | |
| 930 if (overlap_area > 0) { | |
| 931 boxGetGeometry(box2, NULL, NULL, &w, &h); | |
| 932 area2 = w * h; | |
| 933 if (area2 == 0) { | |
| 934 /* do nothing */ | |
| 935 } else if (area1 >= area2) { | |
| 936 overlap_ratio = (l_float32)overlap_area / (l_float32)area2; | |
| 937 area_ratio = (l_float32)area2 / (l_float32)area1; | |
| 938 if (overlap_ratio >= min_overlap && | |
| 939 area_ratio <= max_ratio) { | |
| 940 numaSetValue(namap, j, i); | |
| 941 } | |
| 942 } else { | |
| 943 overlap_ratio = (l_float32)overlap_area / (l_float32)area1; | |
| 944 area_ratio = (l_float32)area1 / (l_float32)area2; | |
| 945 if (overlap_ratio >= min_overlap && | |
| 946 area_ratio <= max_ratio) { | |
| 947 numaSetValue(namap, i, j); | |
| 948 } | |
| 949 } | |
| 950 } | |
| 951 boxDestroy(&box2); | |
| 952 } | |
| 953 boxDestroy(&box1); | |
| 954 } | |
| 955 | |
| 956 boxat = boxaCopy(boxas, L_COPY); | |
| 957 if (op == L_COMBINE) { | |
| 958 /* Resize the larger of the pair to the bounding region */ | |
| 959 for (i = 0; i < n; i++) { | |
| 960 numaGetIValue(namap, i, &val); | |
| 961 if (val >= 0) { | |
| 962 box1 = boxaGetBox(boxas, i, L_CLONE); /* smaller */ | |
| 963 box2 = boxaGetBox(boxas, val, L_CLONE); /* larger */ | |
| 964 box3 = boxBoundingRegion(box1, box2); | |
| 965 boxaReplaceBox(boxat, val, box3); | |
| 966 boxDestroy(&box1); | |
| 967 boxDestroy(&box2); | |
| 968 } | |
| 969 } | |
| 970 } | |
| 971 | |
| 972 /* Remove the smaller of the pairs */ | |
| 973 boxad = boxaCreate(n); | |
| 974 for (i = 0; i < n; i++) { | |
| 975 numaGetIValue(namap, i, &val); | |
| 976 if (val == -1) { | |
| 977 box1 = boxaGetBox(boxat, i, L_COPY); | |
| 978 boxaAddBox(boxad, box1, L_INSERT); | |
| 979 } | |
| 980 } | |
| 981 boxaDestroy(&boxat); | |
| 982 if (pnamap) | |
| 983 *pnamap = namap; | |
| 984 else | |
| 985 numaDestroy(&namap); | |
| 986 return boxad; | |
| 987 } | |
| 988 | |
| 989 | |
| 990 /*! | |
| 991 * \brief boxOverlapDistance() | |
| 992 * | |
| 993 * \param[in] box1, box2 two boxes, in any order | |
| 994 * \param[out] ph_ovl [optional] horizontal overlap | |
| 995 * \param[out] pv_ovl [optional] vertical overlap | |
| 996 * \return 0 if OK, 1 on error | |
| 997 * | |
| 998 * <pre> | |
| 999 * Notes: | |
| 1000 * (1) This measures horizontal and vertical overlap of the | |
| 1001 * two boxes. Horizontal and vertical overlap are measured | |
| 1002 * independently. We need to consider several cases to clarify. | |
| 1003 * (2) A positive horizontal overlap means that there is at least | |
| 1004 * one point on the the %box1 boundary with the same x-component | |
| 1005 * as some point on the %box2 boundary. Conversely, with a zero | |
| 1006 * or negative horizontal overlap, there are no boundary pixels | |
| 1007 * in %box1 that share an x-component with a boundary pixel in %box2. | |
| 1008 * (3) For a zero or negative horizontal overlap, o <= 0, the minimum | |
| 1009 * difference in the x-component between pixels on the boundaries | |
| 1010 * of the two boxes is d = -o + 1. | |
| 1011 * (4) Likewise for vertical overlaps. | |
| 1012 * </pre> | |
| 1013 */ | |
| 1014 l_ok | |
| 1015 boxOverlapDistance(BOX *box1, | |
| 1016 BOX *box2, | |
| 1017 l_int32 *ph_ovl, | |
| 1018 l_int32 *pv_ovl) | |
| 1019 { | |
| 1020 l_int32 l1, t1, w1, h1, r1, b1, l2, t2, w2, h2, r2, b2, valid1, valid2; | |
| 1021 | |
| 1022 if (!ph_ovl && !pv_ovl) | |
| 1023 return ERROR_INT("nothing to do", __func__, 1); | |
| 1024 if (ph_ovl) *ph_ovl = 0; | |
| 1025 if (pv_ovl) *pv_ovl = 0; | |
| 1026 if (!box1 || !box2) | |
| 1027 return ERROR_INT("boxes not both defined", __func__, 1); | |
| 1028 boxIsValid(box1, &valid1); | |
| 1029 boxIsValid(box2, &valid2); | |
| 1030 if (!valid1 || !valid2) | |
| 1031 return ERROR_INT("boxes not both valid", __func__, 1); | |
| 1032 | |
| 1033 if (ph_ovl) { | |
| 1034 boxGetGeometry(box1, &l1, NULL, &w1, NULL); | |
| 1035 boxGetGeometry(box2, &l2, NULL, &w2, NULL); | |
| 1036 r1 = l1 + w1; /* 1 pixel to the right of box 1 */ | |
| 1037 r2 = l2 + w2; | |
| 1038 if (l2 >= l1) | |
| 1039 *ph_ovl = r1 - l2; | |
| 1040 else | |
| 1041 *ph_ovl = r2 - l1; | |
| 1042 } | |
| 1043 if (pv_ovl) { | |
| 1044 boxGetGeometry(box1, NULL, &t1, NULL, &h1); | |
| 1045 boxGetGeometry(box2, NULL, &t2, NULL, &h2); | |
| 1046 b1 = t1 + h1; /* 1 pixel below box 1 */ | |
| 1047 b2 = t2 + h2; | |
| 1048 if (t2 >= t1) | |
| 1049 *pv_ovl = b1 - t2; | |
| 1050 else | |
| 1051 *pv_ovl = b2 - t1; | |
| 1052 } | |
| 1053 return 0; | |
| 1054 } | |
| 1055 | |
| 1056 | |
| 1057 /*! | |
| 1058 * \brief boxSeparationDistance() | |
| 1059 * | |
| 1060 * \param[in] box1, box2 two boxes, in any order | |
| 1061 * \param[out] ph_sep horizontal separation | |
| 1062 * \param[out] pv_sep vertical separation | |
| 1063 * \return 0 if OK, 1 on error | |
| 1064 * | |
| 1065 * <pre> | |
| 1066 * Notes: | |
| 1067 * (1) This measures the Manhattan distance between the closest points | |
| 1068 * on the boundaries of the two boxes. When the boxes overlap | |
| 1069 * (including touching along a line or at a corner), the | |
| 1070 * horizontal and vertical distances are 0. | |
| 1071 * (2) The distances represent the horizontal and vertical separation | |
| 1072 * of the two boxes. The boxes have a nonzero intersection when | |
| 1073 * both the horizontal and vertical overlaps are positive, and | |
| 1074 * for that case both horizontal and vertical separation | |
| 1075 * distances are 0. | |
| 1076 * (3) If the horizontal overlap of the boxes is positive, the | |
| 1077 * horizontal separation between nearest points on respective | |
| 1078 * boundaries is 0, and likewise for the vertical overlap. | |
| 1079 * (4) If the horizontal overlap ho <= 0, the horizontal | |
| 1080 * separation between nearest points is d = -ho + 1. | |
| 1081 * Likewise, if the vertical overlap vo <= 0, the vertical | |
| 1082 * separation between nearest points is d = -vo + 1. | |
| 1083 * </pre> | |
| 1084 */ | |
| 1085 l_ok | |
| 1086 boxSeparationDistance(BOX *box1, | |
| 1087 BOX *box2, | |
| 1088 l_int32 *ph_sep, | |
| 1089 l_int32 *pv_sep) | |
| 1090 { | |
| 1091 l_int32 h_ovl, v_ovl, valid1, valid2; | |
| 1092 | |
| 1093 if (ph_sep) *ph_sep = 0; | |
| 1094 if (pv_sep) *pv_sep = 0; | |
| 1095 if (!ph_sep || !pv_sep) | |
| 1096 return ERROR_INT("&h_sep and &v_sep not both defined", __func__, 1); | |
| 1097 if (!box1 || !box2) | |
| 1098 return ERROR_INT("boxes not both defined", __func__, 1); | |
| 1099 boxIsValid(box1, &valid1); | |
| 1100 boxIsValid(box2, &valid2); | |
| 1101 if (!valid1 || !valid2) | |
| 1102 return ERROR_INT("boxes not both valid", __func__, 1); | |
| 1103 | |
| 1104 boxOverlapDistance(box1, box2, &h_ovl, &v_ovl); | |
| 1105 if (h_ovl <= 0) | |
| 1106 *ph_sep = -h_ovl + 1; | |
| 1107 if (v_ovl <= 0) | |
| 1108 *pv_sep = -v_ovl + 1; | |
| 1109 return 0; | |
| 1110 } | |
| 1111 | |
| 1112 | |
| 1113 /*! | |
| 1114 * \brief boxCompareSize() | |
| 1115 * | |
| 1116 * \param[in] box1, box2 | |
| 1117 * \param[in] type L_SORT_BY_WIDTH, L_SORT_BY_HEIGHT, | |
| 1118 * L_SORT_BY_MAX_DIMENSION, L_SORT_BY_PERIMETER, | |
| 1119 * L_SORT_BY_AREA, | |
| 1120 * \param[out] prel 1 if box1 > box2, 0 if the same, -1 if box1 < box2 | |
| 1121 * \return 0 if OK, 1 on error | |
| 1122 * | |
| 1123 * <pre> | |
| 1124 * Notes: | |
| 1125 * (1) We're re-using the SORT enum for these comparisons. | |
| 1126 * </pre> | |
| 1127 */ | |
| 1128 l_ok | |
| 1129 boxCompareSize(BOX *box1, | |
| 1130 BOX *box2, | |
| 1131 l_int32 type, | |
| 1132 l_int32 *prel) | |
| 1133 { | |
| 1134 l_int32 w1, h1, w2, h2, size1, size2, valid1, valid2; | |
| 1135 | |
| 1136 if (!prel) | |
| 1137 return ERROR_INT("&rel not defined", __func__, 1); | |
| 1138 *prel = 0; | |
| 1139 if (!box1 || !box2) | |
| 1140 return ERROR_INT("boxes not both defined", __func__, 1); | |
| 1141 boxIsValid(box1, &valid1); | |
| 1142 boxIsValid(box2, &valid2); | |
| 1143 if (!valid1 || !valid2) | |
| 1144 return ERROR_INT("boxes not both valid", __func__, 1); | |
| 1145 if (type != L_SORT_BY_WIDTH && type != L_SORT_BY_HEIGHT && | |
| 1146 type != L_SORT_BY_MAX_DIMENSION && type != L_SORT_BY_PERIMETER && | |
| 1147 type != L_SORT_BY_AREA) | |
| 1148 return ERROR_INT("invalid compare type", __func__, 1); | |
| 1149 | |
| 1150 boxGetGeometry(box1, NULL, NULL, &w1, &h1); | |
| 1151 boxGetGeometry(box2, NULL, NULL, &w2, &h2); | |
| 1152 if (type == L_SORT_BY_WIDTH) { | |
| 1153 *prel = (w1 > w2) ? 1 : ((w1 == w2) ? 0 : -1); | |
| 1154 } else if (type == L_SORT_BY_HEIGHT) { | |
| 1155 *prel = (h1 > h2) ? 1 : ((h1 == h2) ? 0 : -1); | |
| 1156 } else if (type == L_SORT_BY_MAX_DIMENSION) { | |
| 1157 size1 = L_MAX(w1, h1); | |
| 1158 size2 = L_MAX(w2, h2); | |
| 1159 *prel = (size1 > size2) ? 1 : ((size1 == size2) ? 0 : -1); | |
| 1160 } else if (type == L_SORT_BY_PERIMETER) { | |
| 1161 size1 = w1 + h1; | |
| 1162 size2 = w2 + h2; | |
| 1163 *prel = (size1 > size2) ? 1 : ((size1 == size2) ? 0 : -1); | |
| 1164 } else if (type == L_SORT_BY_AREA) { | |
| 1165 size1 = w1 * h1; | |
| 1166 size2 = w2 * h2; | |
| 1167 *prel = (size1 > size2) ? 1 : ((size1 == size2) ? 0 : -1); | |
| 1168 } | |
| 1169 return 0; | |
| 1170 } | |
| 1171 | |
| 1172 | |
| 1173 /*! | |
| 1174 * \brief boxContainsPt() | |
| 1175 * | |
| 1176 * \param[in] box | |
| 1177 * \param[in] x, y a point | |
| 1178 * \param[out] pcontains 1 if box contains point; 0 otherwise | |
| 1179 * \return 0 if OK, 1 on error. | |
| 1180 */ | |
| 1181 l_ok | |
| 1182 boxContainsPt(BOX *box, | |
| 1183 l_float32 x, | |
| 1184 l_float32 y, | |
| 1185 l_int32 *pcontains) | |
| 1186 { | |
| 1187 l_int32 bx, by, bw, bh; | |
| 1188 | |
| 1189 if (!pcontains) | |
| 1190 return ERROR_INT("&contains not defined", __func__, 1); | |
| 1191 *pcontains = 0; | |
| 1192 if (!box) | |
| 1193 return ERROR_INT("&box not defined", __func__, 1); | |
| 1194 boxGetGeometry(box, &bx, &by, &bw, &bh); | |
| 1195 if (x >= bx && x < bx + bw && y >= by && y < by + bh) | |
| 1196 *pcontains = 1; | |
| 1197 return 0; | |
| 1198 } | |
| 1199 | |
| 1200 | |
| 1201 /*! | |
| 1202 * \brief boxaGetNearestToPt() | |
| 1203 * | |
| 1204 * \param[in] boxa | |
| 1205 * \param[in] x, y point | |
| 1206 * \return box with centroid closest to the given point [x,y], | |
| 1207 * or NULL if no boxes in boxa | |
| 1208 * | |
| 1209 * <pre> | |
| 1210 * Notes: | |
| 1211 * (1) Uses euclidean distance between centroid and point. | |
| 1212 * </pre> | |
| 1213 */ | |
| 1214 BOX * | |
| 1215 boxaGetNearestToPt(BOXA *boxa, | |
| 1216 l_int32 x, | |
| 1217 l_int32 y) | |
| 1218 { | |
| 1219 l_int32 i, n, minindex; | |
| 1220 l_float32 delx, dely, dist, mindist, cx, cy; | |
| 1221 BOX *box; | |
| 1222 | |
| 1223 if (!boxa) | |
| 1224 return (BOX *)ERROR_PTR("boxa not defined", __func__, NULL); | |
| 1225 if ((n = boxaGetCount(boxa)) == 0) | |
| 1226 return (BOX *)ERROR_PTR("n = 0", __func__, NULL); | |
| 1227 | |
| 1228 mindist = 1000000000.; | |
| 1229 minindex = 0; | |
| 1230 for (i = 0; i < n; i++) { | |
| 1231 if ((box = boxaGetValidBox(boxa, i, L_CLONE)) == NULL) | |
| 1232 continue; | |
| 1233 boxGetCenter(box, &cx, &cy); | |
| 1234 delx = (l_float32)(cx - x); | |
| 1235 dely = (l_float32)(cy - y); | |
| 1236 dist = delx * delx + dely * dely; | |
| 1237 if (dist < mindist) { | |
| 1238 minindex = i; | |
| 1239 mindist = dist; | |
| 1240 } | |
| 1241 boxDestroy(&box); | |
| 1242 } | |
| 1243 | |
| 1244 return boxaGetBox(boxa, minindex, L_COPY); | |
| 1245 } | |
| 1246 | |
| 1247 | |
| 1248 /*! | |
| 1249 * \brief boxaGetNearestToLine() | |
| 1250 * | |
| 1251 * \param[in] boxa | |
| 1252 * \param[in] x, y (y = -1 for vertical line; x = -1 for horiz line) | |
| 1253 * \return box with centroid closest to the given line, | |
| 1254 * or NULL if no boxes in boxa | |
| 1255 * | |
| 1256 * <pre> | |
| 1257 * Notes: | |
| 1258 * (1) For a horizontal line at some value y, get the minimum of the | |
| 1259 * distance |yc - y| from the box centroid yc value to y; | |
| 1260 * likewise minimize |xc - x| for a vertical line at x. | |
| 1261 * (2) Input y < 0, x >= 0 to indicate a vertical line at x, and | |
| 1262 * x < 0, y >= 0 for a horizontal line at y. | |
| 1263 * </pre> | |
| 1264 */ | |
| 1265 BOX * | |
| 1266 boxaGetNearestToLine(BOXA *boxa, | |
| 1267 l_int32 x, | |
| 1268 l_int32 y) | |
| 1269 { | |
| 1270 l_int32 i, n, minindex; | |
| 1271 l_float32 dist, mindist, cx, cy; | |
| 1272 BOX *box; | |
| 1273 | |
| 1274 if (!boxa) | |
| 1275 return (BOX *)ERROR_PTR("boxa not defined", __func__, NULL); | |
| 1276 if ((n = boxaGetCount(boxa)) == 0) | |
| 1277 return (BOX *)ERROR_PTR("n = 0", __func__, NULL); | |
| 1278 if (y >= 0 && x >= 0) | |
| 1279 return (BOX *)ERROR_PTR("either x or y must be < 0", __func__, NULL); | |
| 1280 if (y < 0 && x < 0) | |
| 1281 return (BOX *)ERROR_PTR("either x or y must be >= 0", __func__, NULL); | |
| 1282 | |
| 1283 mindist = 1000000000.; | |
| 1284 minindex = 0; | |
| 1285 for (i = 0; i < n; i++) { | |
| 1286 if ((box = boxaGetValidBox(boxa, i, L_CLONE)) == NULL) | |
| 1287 continue; | |
| 1288 boxGetCenter(box, &cx, &cy); | |
| 1289 if (x >= 0) | |
| 1290 dist = L_ABS(cx - (l_float32)x); | |
| 1291 else /* y >= 0 */ | |
| 1292 dist = L_ABS(cy - (l_float32)y); | |
| 1293 if (dist < mindist) { | |
| 1294 minindex = i; | |
| 1295 mindist = dist; | |
| 1296 } | |
| 1297 boxDestroy(&box); | |
| 1298 } | |
| 1299 | |
| 1300 return boxaGetBox(boxa, minindex, L_COPY); | |
| 1301 } | |
| 1302 | |
| 1303 | |
| 1304 /*! | |
| 1305 * \brief boxaFindNearestBoxes() | |
| 1306 * | |
| 1307 * \param[in] boxa either unsorted, or 2D sorted in LR/TB scan order | |
| 1308 * \param[in] dist_select L_NON_NEGATIVE, L_ALL | |
| 1309 * \param[in] range search distance from box i; use 0 to search | |
| 1310 * entire boxa (e.g., if it's not 2D sorted) | |
| 1311 * \param[out] pnaaindex for each box in %boxa, contains a numa of 4 | |
| 1312 * box indices (per direction) of the nearest box | |
| 1313 * \param[out] pnaadist for each box in %boxa, this contains a numa | |
| 1314 * \return 0 if OK, 1 on error | |
| 1315 * <pre> | |
| 1316 * Notes: | |
| 1317 * (1) See boxaGetNearestByDirection() for usage of %dist_select | |
| 1318 * and %range. | |
| 1319 * </pre> | |
| 1320 */ | |
| 1321 l_ok | |
| 1322 boxaFindNearestBoxes(BOXA *boxa, | |
| 1323 l_int32 dist_select, | |
| 1324 l_int32 range, | |
| 1325 NUMAA **pnaaindex, | |
| 1326 NUMAA **pnaadist) | |
| 1327 { | |
| 1328 l_int32 i, n, index, dist; | |
| 1329 NUMA *nai, *nad; | |
| 1330 NUMAA *naai, *naad; | |
| 1331 | |
| 1332 if (pnaaindex) *pnaaindex = NULL; | |
| 1333 if (pnaadist) *pnaadist = NULL; | |
| 1334 if (!pnaaindex) | |
| 1335 return ERROR_INT("&naaindex not defined", __func__, 1); | |
| 1336 if (!pnaadist) | |
| 1337 return ERROR_INT("&naadist not defined", __func__, 1); | |
| 1338 if (!boxa) | |
| 1339 return ERROR_INT("boxa not defined", __func__, 1); | |
| 1340 | |
| 1341 n = boxaGetCount(boxa); | |
| 1342 naai = numaaCreate(n); | |
| 1343 naad = numaaCreate(n); | |
| 1344 *pnaaindex = naai; | |
| 1345 *pnaadist = naad; | |
| 1346 for (i = 0; i < n; i++) { | |
| 1347 nai = numaCreate(4); | |
| 1348 nad = numaCreate(4); | |
| 1349 boxaGetNearestByDirection(boxa, i, L_FROM_LEFT, dist_select, | |
| 1350 range, &index, &dist); | |
| 1351 numaAddNumber(nai, index); | |
| 1352 numaAddNumber(nad, dist); | |
| 1353 boxaGetNearestByDirection(boxa, i, L_FROM_RIGHT, dist_select, | |
| 1354 range, &index, &dist); | |
| 1355 numaAddNumber(nai, index); | |
| 1356 numaAddNumber(nad, dist); | |
| 1357 boxaGetNearestByDirection(boxa, i, L_FROM_TOP, dist_select, | |
| 1358 range, &index, &dist); | |
| 1359 numaAddNumber(nai, index); | |
| 1360 numaAddNumber(nad, dist); | |
| 1361 boxaGetNearestByDirection(boxa, i, L_FROM_BOT, dist_select, | |
| 1362 range, &index, &dist); | |
| 1363 numaAddNumber(nai, index); | |
| 1364 numaAddNumber(nad, dist); | |
| 1365 numaaAddNuma(naai, nai, L_INSERT); | |
| 1366 numaaAddNuma(naad, nad, L_INSERT); | |
| 1367 } | |
| 1368 return 0; | |
| 1369 } | |
| 1370 | |
| 1371 | |
| 1372 /*! | |
| 1373 * \brief boxaGetNearestByDirection() | |
| 1374 * | |
| 1375 * \param[in] boxa either unsorted, or 2D sorted in LR/TB scan order | |
| 1376 * \param[in] i box we test against | |
| 1377 * \param[in] dir direction to look: L_FROM_LEFT, L_FROM_RIGHT, | |
| 1378 * L_FROM_TOP, L_FROM_BOT | |
| 1379 * \param[in] dist_select L_NON_NEGATIVE, L_ALL | |
| 1380 * \param[in] range search distance from box i; use 0 to search | |
| 1381 * entire boxa (e.g., if it's not 2D sorted) | |
| 1382 * \param[out] pindex index in boxa of nearest box with overlapping | |
| 1383 * coordinates in the indicated direction; | |
| 1384 * -1 if there is no box | |
| 1385 * \param[out] pdist distance of the nearest box in the indicated | |
| 1386 * direction; 100000 if no box | |
| 1387 * \return 0 if OK, 1 on error | |
| 1388 * | |
| 1389 * <pre> | |
| 1390 * Notes: | |
| 1391 * (1) For efficiency, use a LR/TD sorted %boxa, which can be | |
| 1392 * made by flattening a 2D sorted boxaa. In that case, | |
| 1393 * %range can be some positive integer like 50. | |
| 1394 * (2) If boxes overlap, the distance will be < 0. Use %dist_select | |
| 1395 * to determine if these should count or not. If L_ALL, then | |
| 1396 * one box will match as the nearest to another in 2 or more | |
| 1397 * directions. | |
| 1398 * </pre> | |
| 1399 */ | |
| 1400 l_ok | |
| 1401 boxaGetNearestByDirection(BOXA *boxa, | |
| 1402 l_int32 i, | |
| 1403 l_int32 dir, | |
| 1404 l_int32 dist_select, | |
| 1405 l_int32 range, | |
| 1406 l_int32 *pindex, | |
| 1407 l_int32 *pdist) | |
| 1408 { | |
| 1409 l_int32 j, jmin, jmax, n, mindist, dist, index; | |
| 1410 l_int32 x, y, w, h, bx, by, bw, bh; | |
| 1411 | |
| 1412 if (pindex) *pindex = -1; | |
| 1413 if (pdist) *pdist = 100000; | |
| 1414 if (!pindex) | |
| 1415 return ERROR_INT("&index not defined", __func__, 1); | |
| 1416 if (!pdist) | |
| 1417 return ERROR_INT("&dist not defined", __func__, 1); | |
| 1418 if (!boxa) | |
| 1419 return ERROR_INT("boxa not defined", __func__, 1); | |
| 1420 if (dir != L_FROM_LEFT && dir != L_FROM_RIGHT && | |
| 1421 dir != L_FROM_TOP && dir != L_FROM_BOT) | |
| 1422 return ERROR_INT("invalid dir", __func__, 1); | |
| 1423 if (dist_select != L_NON_NEGATIVE && dist_select != L_ALL) | |
| 1424 return ERROR_INT("invalid dist_select", __func__, 1); | |
| 1425 n = boxaGetCount(boxa); | |
| 1426 if (i < 0 || i >= n) | |
| 1427 return ERROR_INT("invalid box index", __func__, 1); | |
| 1428 | |
| 1429 jmin = (range <= 0) ? 0 : L_MAX(0, i - range); | |
| 1430 jmax = (range <= 0) ? n - 1 : L_MIN(n -1, i + range); | |
| 1431 boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); | |
| 1432 mindist = 100000; | |
| 1433 index = -1; | |
| 1434 if (dir == L_FROM_LEFT || dir == L_FROM_RIGHT) { | |
| 1435 for (j = jmin; j <= jmax; j++) { | |
| 1436 if (j == i) continue; | |
| 1437 boxaGetBoxGeometry(boxa, j, &bx, &by, &bw, &bh); | |
| 1438 if ((bx >= x && dir == L_FROM_LEFT) || /* not to the left */ | |
| 1439 (x >= bx && dir == L_FROM_RIGHT)) /* not to the right */ | |
| 1440 continue; | |
| 1441 if (boxHasOverlapInXorY(y, h, by, bh) == 1) { | |
| 1442 dist = boxGetDistanceInXorY(x, w, bx, bw); | |
| 1443 if (dist_select == L_NON_NEGATIVE && dist < 0) continue; | |
| 1444 if (dist < mindist) { | |
| 1445 mindist = dist; | |
| 1446 index = j; | |
| 1447 } | |
| 1448 } | |
| 1449 } | |
| 1450 } else if (dir == L_FROM_TOP || dir == L_FROM_BOT) { | |
| 1451 for (j = jmin; j <= jmax; j++) { | |
| 1452 if (j == i) continue; | |
| 1453 boxaGetBoxGeometry(boxa, j, &bx, &by, &bw, &bh); | |
| 1454 if ((by >= y && dir == L_FROM_TOP) || /* not above */ | |
| 1455 (y >= by && dir == L_FROM_BOT)) /* not below */ | |
| 1456 continue; | |
| 1457 if (boxHasOverlapInXorY(x, w, bx, bw) == 1) { | |
| 1458 dist = boxGetDistanceInXorY(y, h, by, bh); | |
| 1459 if (dist_select == L_NON_NEGATIVE && dist < 0) continue; | |
| 1460 if (dist < mindist) { | |
| 1461 mindist = dist; | |
| 1462 index = j; | |
| 1463 } | |
| 1464 } | |
| 1465 } | |
| 1466 } | |
| 1467 *pindex = index; | |
| 1468 *pdist = mindist; | |
| 1469 return 0; | |
| 1470 } | |
| 1471 | |
| 1472 | |
| 1473 /*! | |
| 1474 * \brief boxHasOverlapInXorY() | |
| 1475 * | |
| 1476 * \param[in] c1 left or top coordinate of box1 | |
| 1477 * \param[in] s1 width or height of box1 | |
| 1478 * \param[in] c2 left or top coordinate of box2 | |
| 1479 * \param[in] s2 width or height of box2 | |
| 1480 * \return 0 if no overlap; 1 if any overlap | |
| 1481 * | |
| 1482 * <pre> | |
| 1483 * Notes: | |
| 1484 * (1) Like boxGetDistanceInXorY(), this is used for overlaps both in | |
| 1485 * x (which projected vertically) and in y (projected horizontally) | |
| 1486 * </pre> | |
| 1487 */ | |
| 1488 static l_int32 | |
| 1489 boxHasOverlapInXorY(l_int32 c1, | |
| 1490 l_int32 s1, | |
| 1491 l_int32 c2, | |
| 1492 l_int32 s2) | |
| 1493 { | |
| 1494 l_int32 ovlp; | |
| 1495 | |
| 1496 if (c1 > c2) | |
| 1497 ovlp = c2 + s2 - 1 - c1; | |
| 1498 else | |
| 1499 ovlp = c1 + s1 - 1 - c2; | |
| 1500 return (ovlp < 0) ? 0 : 1; | |
| 1501 } | |
| 1502 | |
| 1503 | |
| 1504 /*! | |
| 1505 * \brief boxGetDistanceInXorY() | |
| 1506 * | |
| 1507 * \param[in] c1 left or top coordinate of box1 | |
| 1508 * \param[in] s1 width or height of box1 | |
| 1509 * \param[in] c2 left or top coordinate of box2 | |
| 1510 * \param[in] s2 width or height of box2 | |
| 1511 * \return distance between them (if < 0, box2 overlaps box1 in the | |
| 1512 * dimension considered) | |
| 1513 */ | |
| 1514 static l_int32 | |
| 1515 boxGetDistanceInXorY(l_int32 c1, | |
| 1516 l_int32 s1, | |
| 1517 l_int32 c2, | |
| 1518 l_int32 s2) | |
| 1519 { | |
| 1520 l_int32 dist; | |
| 1521 | |
| 1522 if (c1 > c2) | |
| 1523 dist = c1 - (c2 + s2 - 1); | |
| 1524 else | |
| 1525 dist = c2 - (c1 + s1 - 1); | |
| 1526 return dist; | |
| 1527 } | |
| 1528 | |
| 1529 | |
| 1530 /*! | |
| 1531 * \brief boxGetCenter() | |
| 1532 * | |
| 1533 * \param[in] box | |
| 1534 * \param[out] pcx, pcy location of center of box | |
| 1535 * \return 0 if OK, 1 on error or if box is not valid | |
| 1536 */ | |
| 1537 l_ok | |
| 1538 boxGetCenter(const BOX *box, | |
| 1539 l_float32 *pcx, | |
| 1540 l_float32 *pcy) | |
| 1541 { | |
| 1542 l_int32 x, y, w, h; | |
| 1543 | |
| 1544 if (pcx) *pcx = 0; | |
| 1545 if (pcy) *pcy = 0; | |
| 1546 if (!pcx || !pcy) | |
| 1547 return ERROR_INT("&cx, &cy not both defined", __func__, 1); | |
| 1548 if (!box) | |
| 1549 return ERROR_INT("box not defined", __func__, 1); | |
| 1550 boxGetGeometry(box, &x, &y, &w, &h); | |
| 1551 if (w == 0 || h == 0) return 1; | |
| 1552 *pcx = (l_float32)(x + 0.5 * w); | |
| 1553 *pcy = (l_float32)(y + 0.5 * h); | |
| 1554 | |
| 1555 return 0; | |
| 1556 } | |
| 1557 | |
| 1558 | |
| 1559 /*! | |
| 1560 * \brief boxIntersectByLine() | |
| 1561 * | |
| 1562 * \param[in] box | |
| 1563 * \param[in] x, y point that line goes through | |
| 1564 * \param[in] slope of line | |
| 1565 * \param[out] px1, py1 1st point of intersection with box | |
| 1566 * \param[out] px2, py2 2nd point of intersection with box | |
| 1567 * \param[out] pn number of points of intersection | |
| 1568 * \return 0 if OK, 1 on error or if box is not valid | |
| 1569 * | |
| 1570 * <pre> | |
| 1571 * Notes: | |
| 1572 * (1) If the intersection is at only one point (a corner), the | |
| 1573 * coordinates are returned in (x1, y1). | |
| 1574 * (2) Represent a vertical line by one with a large but finite slope. | |
| 1575 * </pre> | |
| 1576 */ | |
| 1577 l_ok | |
| 1578 boxIntersectByLine(const BOX *box, | |
| 1579 l_int32 x, | |
| 1580 l_int32 y, | |
| 1581 l_float32 slope, | |
| 1582 l_int32 *px1, | |
| 1583 l_int32 *py1, | |
| 1584 l_int32 *px2, | |
| 1585 l_int32 *py2, | |
| 1586 l_int32 *pn) | |
| 1587 { | |
| 1588 l_int32 bx, by, bw, bh, xp, yp, xt, yt, i, n; | |
| 1589 l_float32 invslope; | |
| 1590 PTA *pta; | |
| 1591 | |
| 1592 if (px1) *px1 = 0; | |
| 1593 if (px2) *px2 = 0; | |
| 1594 if (py1) *py1 = 0; | |
| 1595 if (py2) *py2 = 0; | |
| 1596 if (pn) *pn = 0; | |
| 1597 if (!px1 || !py1 || !px2 || !py2) | |
| 1598 return ERROR_INT("&x1, &y1, &x2, &y2 not all defined", __func__, 1); | |
| 1599 if (!pn) | |
| 1600 return ERROR_INT("&n not defined", __func__, 1); | |
| 1601 if (!box) | |
| 1602 return ERROR_INT("box not defined", __func__, 1); | |
| 1603 boxGetGeometry(box, &bx, &by, &bw, &bh); | |
| 1604 if (bw == 0 || bh == 0) return 1; | |
| 1605 | |
| 1606 if (slope == 0.0) { | |
| 1607 if (y >= by && y < by + bh) { | |
| 1608 *py1 = *py2 = y; | |
| 1609 *px1 = bx; | |
| 1610 *px2 = bx + bw - 1; | |
| 1611 } | |
| 1612 return 0; | |
| 1613 } | |
| 1614 | |
| 1615 if (slope > 1000000.0) { | |
| 1616 if (x >= bx && x < bx + bw) { | |
| 1617 *px1 = *px2 = x; | |
| 1618 *py1 = by; | |
| 1619 *py2 = by + bh - 1; | |
| 1620 } | |
| 1621 return 0; | |
| 1622 } | |
| 1623 | |
| 1624 /* Intersection with top and bottom lines of box */ | |
| 1625 pta = ptaCreate(2); | |
| 1626 invslope = 1.0 / slope; | |
| 1627 xp = (l_int32)(x + invslope * (y - by)); | |
| 1628 if (xp >= bx && xp < bx + bw) | |
| 1629 ptaAddPt(pta, xp, by); | |
| 1630 xp = (l_int32)(x + invslope * (y - by - bh + 1)); | |
| 1631 if (xp >= bx && xp < bx + bw) | |
| 1632 ptaAddPt(pta, xp, by + bh - 1); | |
| 1633 | |
| 1634 /* Intersection with left and right lines of box */ | |
| 1635 yp = (l_int32)(y + slope * (x - bx)); | |
| 1636 if (yp >= by && yp < by + bh) | |
| 1637 ptaAddPt(pta, bx, yp); | |
| 1638 yp = (l_int32)(y + slope * (x - bx - bw + 1)); | |
| 1639 if (yp >= by && yp < by + bh) | |
| 1640 ptaAddPt(pta, bx + bw - 1, yp); | |
| 1641 | |
| 1642 /* There is a maximum of 2 unique points; remove duplicates. */ | |
| 1643 n = ptaGetCount(pta); | |
| 1644 if (n > 0) { | |
| 1645 ptaGetIPt(pta, 0, px1, py1); /* accept the first one */ | |
| 1646 *pn = 1; | |
| 1647 } | |
| 1648 for (i = 1; i < n; i++) { | |
| 1649 ptaGetIPt(pta, i, &xt, &yt); | |
| 1650 if ((*px1 != xt) || (*py1 != yt)) { | |
| 1651 *px2 = xt; | |
| 1652 *py2 = yt; | |
| 1653 *pn = 2; | |
| 1654 break; | |
| 1655 } | |
| 1656 } | |
| 1657 | |
| 1658 ptaDestroy(&pta); | |
| 1659 return 0; | |
| 1660 } | |
| 1661 | |
| 1662 | |
| 1663 /*! | |
| 1664 * \brief boxClipToRectangle() | |
| 1665 * | |
| 1666 * \param[in] box | |
| 1667 * \param[in] wi, hi rectangle representing image | |
| 1668 * \return part of box within given rectangle, or NULL on error | |
| 1669 * or if box is entirely outside the rectangle | |
| 1670 * | |
| 1671 * <pre> | |
| 1672 * Notes: | |
| 1673 * (1) This can be used to clip a rectangle to an image. | |
| 1674 * The clipping rectangle is assumed to have a UL corner at (0, 0), | |
| 1675 * and a LR corner at (wi - 1, hi - 1). | |
| 1676 * </pre> | |
| 1677 */ | |
| 1678 BOX * | |
| 1679 boxClipToRectangle(BOX *box, | |
| 1680 l_int32 wi, | |
| 1681 l_int32 hi) | |
| 1682 { | |
| 1683 BOX *boxd; | |
| 1684 | |
| 1685 if (!box) | |
| 1686 return (BOX *)ERROR_PTR("box not defined", __func__, NULL); | |
| 1687 if (box->x >= wi || box->y >= hi || | |
| 1688 box->x + box->w <= 0 || box->y + box->h <= 0) | |
| 1689 return (BOX *)ERROR_PTR("box outside rectangle", __func__, NULL); | |
| 1690 | |
| 1691 boxd = boxCopy(box); | |
| 1692 if (boxd->x < 0) { | |
| 1693 boxd->w += boxd->x; | |
| 1694 boxd->x = 0; | |
| 1695 } | |
| 1696 if (boxd->y < 0) { | |
| 1697 boxd->h += boxd->y; | |
| 1698 boxd->y = 0; | |
| 1699 } | |
| 1700 if (boxd->x + boxd->w > wi) | |
| 1701 boxd->w = wi - boxd->x; | |
| 1702 if (boxd->y + boxd->h > hi) | |
| 1703 boxd->h = hi - boxd->y; | |
| 1704 return boxd; | |
| 1705 } | |
| 1706 | |
| 1707 | |
| 1708 /*! | |
| 1709 * \brief boxClipToRectangleParams() | |
| 1710 * | |
| 1711 * \param[in] box [optional] requested box; can be null | |
| 1712 * \param[in] w, h clipping box size; typ. the size of an image | |
| 1713 * \param[out] pxstart start x coordinate | |
| 1714 * \param[out] pystart start y coordinate | |
| 1715 * \param[out] pxend one pixel beyond clipping box | |
| 1716 * \param[out] pyend one pixel beyond clipping box | |
| 1717 * \param[out] pbw [optional] clipped width | |
| 1718 * \param[out] pbh [optional] clipped height | |
| 1719 * \return 0 if OK; 1 on error | |
| 1720 * | |
| 1721 * <pre> | |
| 1722 * Notes: | |
| 1723 * (1) The return value should be checked. If it is 1, the | |
| 1724 * returned parameter values are bogus. | |
| 1725 * (2) This simplifies the selection of pixel locations within | |
| 1726 * a given rectangle: | |
| 1727 * for (i = ystart; i < yend; i++ { | |
| 1728 * ... | |
| 1729 * for (j = xstart; j < xend; j++ { | |
| 1730 * .... | |
| 1731 * </pre> | |
| 1732 */ | |
| 1733 l_ok | |
| 1734 boxClipToRectangleParams(BOX *box, | |
| 1735 l_int32 w, | |
| 1736 l_int32 h, | |
| 1737 l_int32 *pxstart, | |
| 1738 l_int32 *pystart, | |
| 1739 l_int32 *pxend, | |
| 1740 l_int32 *pyend, | |
| 1741 l_int32 *pbw, | |
| 1742 l_int32 *pbh) | |
| 1743 { | |
| 1744 l_int32 bw, bh; | |
| 1745 BOX *boxc; | |
| 1746 | |
| 1747 if (pxstart) *pxstart = 0; | |
| 1748 if (pystart) *pystart = 0; | |
| 1749 if (pxend) *pxend = w; | |
| 1750 if (pyend) *pyend = h; | |
| 1751 if (pbw) *pbw = w; | |
| 1752 if (pbh) *pbh = h; | |
| 1753 if (!pxstart || !pystart || !pxend || !pyend) | |
| 1754 return ERROR_INT("invalid ptr input", __func__, 1); | |
| 1755 if (!box) return 0; | |
| 1756 | |
| 1757 if ((boxc = boxClipToRectangle(box, w, h)) == NULL) | |
| 1758 return ERROR_INT("box outside image", __func__, 1); | |
| 1759 boxGetGeometry(boxc, pxstart, pystart, &bw, &bh); | |
| 1760 boxDestroy(&boxc); | |
| 1761 | |
| 1762 if (pbw) *pbw = bw; | |
| 1763 if (pbh) *pbh = bh; | |
| 1764 if (bw == 0 || bh == 0) | |
| 1765 return ERROR_INT("invalid clipping box", __func__, 1); | |
| 1766 *pxend = *pxstart + bw; /* 1 past the end */ | |
| 1767 *pyend = *pystart + bh; /* 1 past the end */ | |
| 1768 return 0; | |
| 1769 } | |
| 1770 | |
| 1771 | |
| 1772 /*! | |
| 1773 * \brief boxRelocateOneSide() | |
| 1774 * | |
| 1775 * \param[in] boxd [optional]; this can be null, equal to boxs, | |
| 1776 * or different from boxs; | |
| 1777 * \param[in] boxs starting box; to have one side relocated | |
| 1778 * \param[in] loc new location of the side that is changing | |
| 1779 * \param[in] sideflag L_FROM_LEFT, etc., indicating the side that moves | |
| 1780 * \return boxd, or NULL on error or if the computed boxd has | |
| 1781 * width or height <= 0. | |
| 1782 * | |
| 1783 * <pre> | |
| 1784 * Notes: | |
| 1785 * (1) Set boxd == NULL to get new box; boxd == boxs for in-place; | |
| 1786 * or otherwise to resize existing boxd. | |
| 1787 * (2) For usage, suggest one of these: | |
| 1788 * boxd = boxRelocateOneSide(NULL, boxs, ...); // new | |
| 1789 * boxRelocateOneSide(boxs, boxs, ...); // in-place | |
| 1790 * boxRelocateOneSide(boxd, boxs, ...); // other | |
| 1791 * </pre> | |
| 1792 */ | |
| 1793 BOX * | |
| 1794 boxRelocateOneSide(BOX *boxd, | |
| 1795 BOX *boxs, | |
| 1796 l_int32 loc, | |
| 1797 l_int32 sideflag) | |
| 1798 { | |
| 1799 l_int32 x, y, w, h; | |
| 1800 | |
| 1801 if (!boxs) | |
| 1802 return (BOX *)ERROR_PTR("boxs not defined", __func__, NULL); | |
| 1803 if (!boxd) | |
| 1804 boxd = boxCopy(boxs); | |
| 1805 | |
| 1806 boxGetGeometry(boxs, &x, &y, &w, &h); | |
| 1807 if (w == 0 || h == 0) | |
| 1808 return boxd; | |
| 1809 if (sideflag == L_FROM_LEFT) | |
| 1810 boxSetGeometry(boxd, loc, -1, w + x - loc, -1); | |
| 1811 else if (sideflag == L_FROM_RIGHT) | |
| 1812 boxSetGeometry(boxd, -1, -1, loc - x + 1, -1); | |
| 1813 else if (sideflag == L_FROM_TOP) | |
| 1814 boxSetGeometry(boxd, -1, loc, -1, h + y - loc); | |
| 1815 else if (sideflag == L_FROM_BOT) | |
| 1816 boxSetGeometry(boxd, -1, -1, -1, loc - y + 1); | |
| 1817 return boxd; | |
| 1818 } | |
| 1819 | |
| 1820 | |
| 1821 /*! | |
| 1822 * \brief boxaAdjustSides() | |
| 1823 * | |
| 1824 * \param[in] boxas | |
| 1825 * \param[in] delleft, delright, deltop, delbot changes in location of | |
| 1826 * each side for each box | |
| 1827 * \return boxad, or NULL on error | |
| 1828 * | |
| 1829 * <pre> | |
| 1830 * Notes: | |
| 1831 * (1) New box dimensions are cropped at left and top to x >= 0 and y >= 0. | |
| 1832 * (2) If the width or height of a box goes to 0, we generate a box with | |
| 1833 * w == 1 and h == 1, as a placeholder. | |
| 1834 * (3) See boxAdjustSides(). | |
| 1835 * </pre> | |
| 1836 */ | |
| 1837 BOXA * | |
| 1838 boxaAdjustSides(BOXA *boxas, | |
| 1839 l_int32 delleft, | |
| 1840 l_int32 delright, | |
| 1841 l_int32 deltop, | |
| 1842 l_int32 delbot) | |
| 1843 { | |
| 1844 l_int32 n, i, x, y; | |
| 1845 BOX *box1, *box2; | |
| 1846 BOXA *boxad; | |
| 1847 | |
| 1848 if (!boxas) | |
| 1849 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); | |
| 1850 | |
| 1851 n = boxaGetCount(boxas); | |
| 1852 boxad = boxaCreate(n); | |
| 1853 for (i = 0; i < n; i++) { | |
| 1854 box1 = boxaGetBox(boxas, i, L_COPY); | |
| 1855 box2 = boxAdjustSides(NULL, box1, delleft, delright, deltop, delbot); | |
| 1856 if (!box2) { | |
| 1857 boxGetGeometry(box1, &x, &y, NULL, NULL); | |
| 1858 box2 = boxCreate(x, y, 1, 1); | |
| 1859 } | |
| 1860 boxaAddBox(boxad, box2, L_INSERT); | |
| 1861 boxDestroy(&box1); | |
| 1862 } | |
| 1863 | |
| 1864 return boxad; | |
| 1865 } | |
| 1866 | |
| 1867 | |
| 1868 /*! | |
| 1869 * \brief boxaAdjustBoxSides() | |
| 1870 * | |
| 1871 * \param[in] boxas | |
| 1872 * \param[in] index | |
| 1873 * \param[in] delleft, delright, deltop, delbot changes to box side locs | |
| 1874 * \return 0 if OK, 1 on error | |
| 1875 * | |
| 1876 * <pre> | |
| 1877 * Notes: | |
| 1878 * (1) In-place operation on a box in a boxa. | |
| 1879 * (2) New box dimensions are cropped at left and top to x >= 0 and y >= 0. | |
| 1880 * (3) If a box ends up with no area, an error message is emitted, | |
| 1881 * but the box dimensions are not changed. | |
| 1882 * (4) See boxaAdjustSides(). | |
| 1883 * </pre> | |
| 1884 */ | |
| 1885 l_ok | |
| 1886 boxaAdjustBoxSides(BOXA *boxa, | |
| 1887 l_int32 index, | |
| 1888 l_int32 delleft, | |
| 1889 l_int32 delright, | |
| 1890 l_int32 deltop, | |
| 1891 l_int32 delbot) | |
| 1892 { | |
| 1893 BOX *box; | |
| 1894 | |
| 1895 if (!boxa) | |
| 1896 return ERROR_INT("boxa not defined", __func__, 1); | |
| 1897 | |
| 1898 if ((box = boxaGetBox(boxa, index, L_CLONE)) == NULL) | |
| 1899 return ERROR_INT("invalid index", __func__, 1); | |
| 1900 | |
| 1901 boxAdjustSides(box, box, delleft, delright, deltop, delbot); | |
| 1902 boxDestroy(&box); /* the clone */ | |
| 1903 return 0; | |
| 1904 } | |
| 1905 | |
| 1906 | |
| 1907 /*! | |
| 1908 * \brief boxAdjustSides() | |
| 1909 * | |
| 1910 * \param[in] boxd [optional]; this can be null, equal to boxs, | |
| 1911 * or different from boxs | |
| 1912 * \param[in] boxs starting box; to have sides adjusted | |
| 1913 * \param[in] delleft, delright, deltop, delbot changes in location | |
| 1914 * of each side | |
| 1915 * \return boxd, or NULL on error or if the computed boxd has | |
| 1916 * width or height <= 0. | |
| 1917 * | |
| 1918 * <pre> | |
| 1919 * Notes: | |
| 1920 * (1) Set boxd == NULL to get new box; boxd == boxs for in-place; | |
| 1921 * or otherwise to resize existing boxd. | |
| 1922 * (2) For usage, suggest one of these: | |
| 1923 * boxd = boxAdjustSides(NULL, boxs, ...); // new | |
| 1924 * boxAdjustSides(boxs, boxs, ...); // in-place | |
| 1925 * boxAdjustSides(boxd, boxs, ...); // other | |
| 1926 * (3) New box dimensions are cropped at left and top to x >= 0 and y >= 0. | |
| 1927 * (4) For example, to expand in-place by 20 pixels on each side, use | |
| 1928 * boxAdjustSides(box, box, -20, 20, -20, 20); | |
| 1929 * </pre> | |
| 1930 */ | |
| 1931 BOX * | |
| 1932 boxAdjustSides(BOX *boxd, | |
| 1933 BOX *boxs, | |
| 1934 l_int32 delleft, | |
| 1935 l_int32 delright, | |
| 1936 l_int32 deltop, | |
| 1937 l_int32 delbot) | |
| 1938 { | |
| 1939 l_int32 x, y, w, h, xl, xr, yt, yb, wnew, hnew; | |
| 1940 | |
| 1941 if (!boxs) | |
| 1942 return (BOX *)ERROR_PTR("boxs not defined", __func__, NULL); | |
| 1943 | |
| 1944 boxGetGeometry(boxs, &x, &y, &w, &h); | |
| 1945 xl = L_MAX(0, x + delleft); | |
| 1946 yt = L_MAX(0, y + deltop); | |
| 1947 xr = x + w + delright; /* one pixel beyond right edge */ | |
| 1948 yb = y + h + delbot; /* one pixel below bottom edge */ | |
| 1949 wnew = xr - xl; | |
| 1950 hnew = yb - yt; | |
| 1951 | |
| 1952 if (wnew < 1 || hnew < 1) | |
| 1953 return (BOX *)ERROR_PTR("boxd has 0 area", __func__, NULL); | |
| 1954 if (!boxd) | |
| 1955 return boxCreate(xl, yt, wnew, hnew); | |
| 1956 | |
| 1957 boxSetGeometry(boxd, xl, yt, wnew, hnew); | |
| 1958 return boxd; | |
| 1959 } | |
| 1960 | |
| 1961 | |
| 1962 /*! | |
| 1963 * \brief boxaSetSide() | |
| 1964 * | |
| 1965 * \param[in] boxad use NULL to get a new one; same as boxas for in-place | |
| 1966 * \param[in] boxas | |
| 1967 * \param[in] side L_SET_LEFT, L_SET_RIGHT, L_SET_TOP, L_SET_BOT | |
| 1968 * \param[in] val location to set for given side, for each box | |
| 1969 * \param[in] thresh min abs difference to cause resetting to %val | |
| 1970 * \return boxad, or NULL on error | |
| 1971 * | |
| 1972 * <pre> | |
| 1973 * Notes: | |
| 1974 * (1) Sets the given side of each box. Use boxad == NULL for a new | |
| 1975 * boxa, and boxad == boxas for in-place. | |
| 1976 * (2) Use one of these: | |
| 1977 * boxad = boxaSetSide(NULL, boxas, ...); // new | |
| 1978 * boxaSetSide(boxas, boxas, ...); // in-place | |
| 1979 * </pre> | |
| 1980 */ | |
| 1981 BOXA * | |
| 1982 boxaSetSide(BOXA *boxad, | |
| 1983 BOXA *boxas, | |
| 1984 l_int32 side, | |
| 1985 l_int32 val, | |
| 1986 l_int32 thresh) | |
| 1987 { | |
| 1988 l_int32 n, i; | |
| 1989 BOX *box; | |
| 1990 | |
| 1991 if (!boxas) | |
| 1992 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); | |
| 1993 if (boxad && (boxas != boxad)) | |
| 1994 return (BOXA *)ERROR_PTR("not in-place", __func__, NULL); | |
| 1995 if (side != L_SET_LEFT && side != L_SET_RIGHT && | |
| 1996 side != L_SET_TOP && side != L_SET_BOT) | |
| 1997 return (BOXA *)ERROR_PTR("invalid side", __func__, NULL); | |
| 1998 if (val < 0) | |
| 1999 return (BOXA *)ERROR_PTR("val < 0", __func__, NULL); | |
| 2000 | |
| 2001 if (!boxad) | |
| 2002 boxad = boxaCopy(boxas, L_COPY); | |
| 2003 n = boxaGetCount(boxad); | |
| 2004 for (i = 0; i < n; i++) { | |
| 2005 box = boxaGetBox(boxad, i, L_CLONE); | |
| 2006 boxSetSide(box, side, val, thresh); | |
| 2007 boxDestroy(&box); /* the clone */ | |
| 2008 } | |
| 2009 | |
| 2010 return boxad; | |
| 2011 } | |
| 2012 | |
| 2013 | |
| 2014 /*! | |
| 2015 * \brief boxSetSide() | |
| 2016 * | |
| 2017 * \param[in] boxs | |
| 2018 * \param[in] side L_SET_LEFT, L_SET_RIGHT, L_SET_TOP, L_SET_BOT | |
| 2019 * \param[in] val location to set for given side, for each box | |
| 2020 * \param[in] thresh min abs difference to cause resetting to %val | |
| 2021 * \return 0 if OK, 1 on error | |
| 2022 * | |
| 2023 * <pre> | |
| 2024 * Notes: | |
| 2025 * (1) In-place operation. | |
| 2026 * (2) Use %thresh = 0 to definitely set the side to %val. | |
| 2027 * </pre> | |
| 2028 */ | |
| 2029 l_ok | |
| 2030 boxSetSide(BOX *boxs, | |
| 2031 l_int32 side, | |
| 2032 l_int32 val, | |
| 2033 l_int32 thresh) | |
| 2034 { | |
| 2035 l_int32 x, y, w, h, diff; | |
| 2036 | |
| 2037 if (!boxs) | |
| 2038 return ERROR_INT("box not defined", __func__, 1); | |
| 2039 if (side != L_SET_LEFT && side != L_SET_RIGHT && | |
| 2040 side != L_SET_TOP && side != L_SET_BOT) | |
| 2041 return ERROR_INT("invalid side", __func__, 1); | |
| 2042 if (val < 0) | |
| 2043 return ERROR_INT("val < 0", __func__, 1); | |
| 2044 | |
| 2045 boxGetGeometry(boxs, &x, &y, &w, &h); | |
| 2046 if (side == L_SET_LEFT) { | |
| 2047 diff = x - val; | |
| 2048 if (L_ABS(diff) >= thresh) | |
| 2049 boxSetGeometry(boxs, val, y, w + diff, h); | |
| 2050 } else if (side == L_SET_RIGHT) { | |
| 2051 diff = x + w -1 - val; | |
| 2052 if (L_ABS(diff) >= thresh) | |
| 2053 boxSetGeometry(boxs, x, y, val - x + 1, h); | |
| 2054 } else if (side == L_SET_TOP) { | |
| 2055 diff = y - val; | |
| 2056 if (L_ABS(diff) >= thresh) | |
| 2057 boxSetGeometry(boxs, x, val, w, h + diff); | |
| 2058 } else { /* side == L_SET_BOT */ | |
| 2059 diff = y + h - 1 - val; | |
| 2060 if (L_ABS(diff) >= thresh) | |
| 2061 boxSetGeometry(boxs, x, y, w, val - y + 1); | |
| 2062 } | |
| 2063 | |
| 2064 return 0; | |
| 2065 } | |
| 2066 | |
| 2067 | |
| 2068 /*! | |
| 2069 * \brief boxaAdjustWidthToTarget() | |
| 2070 * | |
| 2071 * \param[in] boxad use NULL to get a new one; same as boxas for in-place | |
| 2072 * \param[in] boxas | |
| 2073 * \param[in] sides L_ADJUST_LEFT, L_ADJUST_RIGHT, L_ADJUST_LEFT_AND_RIGHT | |
| 2074 * \param[in] target target width if differs by more than thresh | |
| 2075 * \param[in] thresh min abs difference in width to cause adjustment | |
| 2076 * \return boxad, or NULL on error | |
| 2077 * | |
| 2078 * <pre> | |
| 2079 * Notes: | |
| 2080 * (1) Conditionally adjusts the width of each box, by moving | |
| 2081 * the indicated edges (left and/or right) if the width differs | |
| 2082 * by %thresh or more from %target. | |
| 2083 * (2) Use boxad == NULL for a new boxa, and boxad == boxas for in-place. | |
| 2084 * Use one of these: | |
| 2085 * boxad = boxaAdjustWidthToTarget(NULL, boxas, ...); // new | |
| 2086 * boxaAdjustWidthToTarget(boxas, boxas, ...); // in-place | |
| 2087 * </pre> | |
| 2088 */ | |
| 2089 BOXA * | |
| 2090 boxaAdjustWidthToTarget(BOXA *boxad, | |
| 2091 BOXA *boxas, | |
| 2092 l_int32 sides, | |
| 2093 l_int32 target, | |
| 2094 l_int32 thresh) | |
| 2095 { | |
| 2096 l_int32 x, y, w, h, n, i, diff; | |
| 2097 BOX *box; | |
| 2098 | |
| 2099 if (!boxas) | |
| 2100 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); | |
| 2101 if (boxad && (boxas != boxad)) | |
| 2102 return (BOXA *)ERROR_PTR("not in-place", __func__, NULL); | |
| 2103 if (sides != L_ADJUST_LEFT && sides != L_ADJUST_RIGHT && | |
| 2104 sides != L_ADJUST_LEFT_AND_RIGHT) | |
| 2105 return (BOXA *)ERROR_PTR("invalid sides", __func__, NULL); | |
| 2106 if (target < 1) | |
| 2107 return (BOXA *)ERROR_PTR("target < 1", __func__, NULL); | |
| 2108 | |
| 2109 if (!boxad) | |
| 2110 boxad = boxaCopy(boxas, L_COPY); | |
| 2111 n = boxaGetCount(boxad); | |
| 2112 for (i = 0; i < n; i++) { | |
| 2113 if ((box = boxaGetValidBox(boxad, i, L_CLONE)) == NULL) | |
| 2114 continue; | |
| 2115 boxGetGeometry(box, &x, &y, &w, &h); | |
| 2116 diff = w - target; | |
| 2117 if (sides == L_ADJUST_LEFT) { | |
| 2118 if (L_ABS(diff) >= thresh) | |
| 2119 boxSetGeometry(box, L_MAX(0, x + diff), y, target, h); | |
| 2120 } else if (sides == L_ADJUST_RIGHT) { | |
| 2121 if (L_ABS(diff) >= thresh) | |
| 2122 boxSetGeometry(box, x, y, target, h); | |
| 2123 } else { /* sides == L_ADJUST_LEFT_AND_RIGHT */ | |
| 2124 if (L_ABS(diff) >= thresh) | |
| 2125 boxSetGeometry(box, L_MAX(0, x + diff/2), y, target, h); | |
| 2126 } | |
| 2127 boxDestroy(&box); | |
| 2128 } | |
| 2129 | |
| 2130 return boxad; | |
| 2131 } | |
| 2132 | |
| 2133 | |
| 2134 /*! | |
| 2135 * \brief boxaAdjustHeightToTarget() | |
| 2136 * | |
| 2137 * \param[in] boxad use NULL to get a new one | |
| 2138 * \param[in] boxas | |
| 2139 * \param[in] sides L_ADJUST_TOP, L_ADJUST_BOT, L_ADJUST_TOP_AND_BOT | |
| 2140 * \param[in] target target height if differs by more than thresh | |
| 2141 * \param[in] thresh min abs difference in height to cause adjustment | |
| 2142 * \return boxad, or NULL on error | |
| 2143 * | |
| 2144 * <pre> | |
| 2145 * Notes: | |
| 2146 * (1) Conditionally adjusts the height of each box, by moving | |
| 2147 * the indicated edges (top and/or bot) if the height differs | |
| 2148 * by %thresh or more from %target. | |
| 2149 * (2) Use boxad == NULL for a new boxa, and boxad == boxas for in-place. | |
| 2150 * Use one of these: | |
| 2151 * boxad = boxaAdjustHeightToTarget(NULL, boxas, ...); // new | |
| 2152 * boxaAdjustHeightToTarget(boxas, boxas, ...); // in-place | |
| 2153 * </pre> | |
| 2154 */ | |
| 2155 BOXA * | |
| 2156 boxaAdjustHeightToTarget(BOXA *boxad, | |
| 2157 BOXA *boxas, | |
| 2158 l_int32 sides, | |
| 2159 l_int32 target, | |
| 2160 l_int32 thresh) | |
| 2161 { | |
| 2162 l_int32 x, y, w, h, n, i, diff; | |
| 2163 BOX *box; | |
| 2164 | |
| 2165 if (!boxas) | |
| 2166 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); | |
| 2167 if (boxad && (boxas != boxad)) | |
| 2168 return (BOXA *)ERROR_PTR("not in-place", __func__, NULL); | |
| 2169 if (sides != L_ADJUST_TOP && sides != L_ADJUST_BOT && | |
| 2170 sides != L_ADJUST_TOP_AND_BOT) | |
| 2171 return (BOXA *)ERROR_PTR("invalid sides", __func__, NULL); | |
| 2172 if (target < 1) | |
| 2173 return (BOXA *)ERROR_PTR("target < 1", __func__, NULL); | |
| 2174 | |
| 2175 if (!boxad) | |
| 2176 boxad = boxaCopy(boxas, L_COPY); | |
| 2177 n = boxaGetCount(boxad); | |
| 2178 for (i = 0; i < n; i++) { | |
| 2179 if ((box = boxaGetValidBox(boxad, i, L_CLONE)) == NULL) | |
| 2180 continue; | |
| 2181 boxGetGeometry(box, &x, &y, &w, &h); | |
| 2182 diff = h - target; | |
| 2183 if (sides == L_ADJUST_TOP) { | |
| 2184 if (L_ABS(diff) >= thresh) | |
| 2185 boxSetGeometry(box, x, L_MAX(0, y + diff), w, target); | |
| 2186 } else if (sides == L_ADJUST_BOT) { | |
| 2187 if (L_ABS(diff) >= thresh) | |
| 2188 boxSetGeometry(box, x, y, w, target); | |
| 2189 } else { /* sides == L_ADJUST_TOP_AND_BOT */ | |
| 2190 if (L_ABS(diff) >= thresh) | |
| 2191 boxSetGeometry(box, x, L_MAX(0, y + diff/2), w, target); | |
| 2192 } | |
| 2193 boxDestroy(&box); | |
| 2194 } | |
| 2195 | |
| 2196 return boxad; | |
| 2197 } | |
| 2198 | |
| 2199 | |
| 2200 /*! | |
| 2201 * \brief boxEqual() | |
| 2202 * | |
| 2203 * \param[in] box1 | |
| 2204 * \param[in] box2 | |
| 2205 * \param[out] psame 1 if equal; 0 otherwise | |
| 2206 * \return 0 if OK, 1 on error | |
| 2207 */ | |
| 2208 l_ok | |
| 2209 boxEqual(BOX *box1, | |
| 2210 BOX *box2, | |
| 2211 l_int32 *psame) | |
| 2212 { | |
| 2213 if (!psame) | |
| 2214 return ERROR_INT("&same not defined", __func__, 1); | |
| 2215 *psame = 0; | |
| 2216 if (!box1 || !box2) | |
| 2217 return ERROR_INT("boxes not both defined", __func__, 1); | |
| 2218 if (box1->x == box2->x && box1->y == box2->y && | |
| 2219 box1->w == box2->w && box1->h == box2->h) | |
| 2220 *psame = 1; | |
| 2221 return 0; | |
| 2222 } | |
| 2223 | |
| 2224 | |
| 2225 /*! | |
| 2226 * \brief boxaEqual() | |
| 2227 * | |
| 2228 * \param[in] boxa1 | |
| 2229 * \param[in] boxa2 | |
| 2230 * \param[in] maxdist | |
| 2231 * \param[out] pnaindex [optional] index array of correspondences | |
| 2232 * \param[out] psame 1 if equal; 0 otherwise | |
| 2233 * \return 0 if OK, 1 on error | |
| 2234 * | |
| 2235 * <pre> | |
| 2236 * Notes: | |
| 2237 * (1) The two boxa are the "same" if they contain the same | |
| 2238 * boxes and each box is within %maxdist of its counterpart | |
| 2239 * in their positions within the boxa. This allows for | |
| 2240 * small rearrangements. Use 0 for maxdist if the boxa | |
| 2241 * must be identical. | |
| 2242 * (2) This applies only to geometry and ordering; refcounts | |
| 2243 * are not considered. | |
| 2244 * (3) %maxdist allows some latitude in the ordering of the boxes. | |
| 2245 * For the boxa to be the "same", corresponding boxes must | |
| 2246 * be within %maxdist of each other. Note that for large | |
| 2247 * %maxdist, we should use a hash function for efficiency. | |
| 2248 * (4) naindex[i] gives the position of the box in boxa2 that | |
| 2249 * corresponds to box i in boxa1. It is only returned if the | |
| 2250 * boxa are equal. | |
| 2251 * </pre> | |
| 2252 */ | |
| 2253 l_ok | |
| 2254 boxaEqual(BOXA *boxa1, | |
| 2255 BOXA *boxa2, | |
| 2256 l_int32 maxdist, | |
| 2257 NUMA **pnaindex, | |
| 2258 l_int32 *psame) | |
| 2259 { | |
| 2260 l_int32 i, j, n, jstart, jend, found, samebox; | |
| 2261 l_int32 *countarray; | |
| 2262 BOX *box1, *box2; | |
| 2263 NUMA *na; | |
| 2264 | |
| 2265 if (pnaindex) *pnaindex = NULL; | |
| 2266 if (!psame) | |
| 2267 return ERROR_INT("&same not defined", __func__, 1); | |
| 2268 *psame = 0; | |
| 2269 if (!boxa1 || !boxa2) | |
| 2270 return ERROR_INT("boxa1 and boxa2 not both defined", __func__, 1); | |
| 2271 n = boxaGetCount(boxa1); | |
| 2272 if (n != boxaGetCount(boxa2)) | |
| 2273 return 0; | |
| 2274 | |
| 2275 if ((countarray = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32))) == NULL) | |
| 2276 return ERROR_INT("calloc fail for countarray", __func__, 1); | |
| 2277 na = numaMakeConstant(0.0, n); | |
| 2278 | |
| 2279 for (i = 0; i < n; i++) { | |
| 2280 box1 = boxaGetBox(boxa1, i, L_CLONE); | |
| 2281 jstart = L_MAX(0, i - maxdist); | |
| 2282 jend = L_MIN(n-1, i + maxdist); | |
| 2283 found = FALSE; | |
| 2284 for (j = jstart; j <= jend; j++) { | |
| 2285 box2 = boxaGetBox(boxa2, j, L_CLONE); | |
| 2286 boxEqual(box1, box2, &samebox); | |
| 2287 if (samebox && countarray[j] == 0) { | |
| 2288 countarray[j] = 1; | |
| 2289 numaReplaceNumber(na, i, j); | |
| 2290 found = TRUE; | |
| 2291 boxDestroy(&box2); | |
| 2292 break; | |
| 2293 } | |
| 2294 boxDestroy(&box2); | |
| 2295 } | |
| 2296 boxDestroy(&box1); | |
| 2297 if (!found) { | |
| 2298 numaDestroy(&na); | |
| 2299 LEPT_FREE(countarray); | |
| 2300 return 0; | |
| 2301 } | |
| 2302 } | |
| 2303 | |
| 2304 *psame = 1; | |
| 2305 if (pnaindex) | |
| 2306 *pnaindex = na; | |
| 2307 else | |
| 2308 numaDestroy(&na); | |
| 2309 LEPT_FREE(countarray); | |
| 2310 return 0; | |
| 2311 } | |
| 2312 | |
| 2313 | |
| 2314 /*! | |
| 2315 * \brief boxSimilar() | |
| 2316 * | |
| 2317 * \param[in] box1 | |
| 2318 * \param[in] box2 | |
| 2319 * \param[in] leftdiff, rightdiff, topdiff, botdiff | |
| 2320 * \param[out] psimilar 1 if similar; 0 otherwise | |
| 2321 * \return 0 if OK, 1 on error | |
| 2322 * | |
| 2323 * <pre> | |
| 2324 * Notes: | |
| 2325 * (1) The values of leftdiff (etc) are the maximum allowed deviations | |
| 2326 * between the locations of the left (etc) sides. If any side | |
| 2327 * pairs differ by more than this amount, the boxes are not similar. | |
| 2328 * </pre> | |
| 2329 */ | |
| 2330 l_ok | |
| 2331 boxSimilar(BOX *box1, | |
| 2332 BOX *box2, | |
| 2333 l_int32 leftdiff, | |
| 2334 l_int32 rightdiff, | |
| 2335 l_int32 topdiff, | |
| 2336 l_int32 botdiff, | |
| 2337 l_int32 *psimilar) | |
| 2338 { | |
| 2339 l_int32 l1, l2, r1, r2, t1, t2, b1, b2, valid1, valid2; | |
| 2340 | |
| 2341 if (!psimilar) | |
| 2342 return ERROR_INT("&similar not defined", __func__, 1); | |
| 2343 *psimilar = 0; | |
| 2344 if (!box1 || !box2) | |
| 2345 return ERROR_INT("boxes not both defined", __func__, 1); | |
| 2346 boxIsValid(box1, &valid1); | |
| 2347 boxIsValid(box2, &valid2); | |
| 2348 if (!valid1 || !valid2) | |
| 2349 return ERROR_INT("boxes not both valid", __func__, 1); | |
| 2350 | |
| 2351 boxGetSideLocations(box1, &l1, &r1, &t1, &b1); | |
| 2352 boxGetSideLocations(box2, &l2, &r2, &t2, &b2); | |
| 2353 if (L_ABS(l1 - l2) > leftdiff) | |
| 2354 return 0; | |
| 2355 if (L_ABS(r1 - r2) > rightdiff) | |
| 2356 return 0; | |
| 2357 if (L_ABS(t1 - t2) > topdiff) | |
| 2358 return 0; | |
| 2359 if (L_ABS(b1 - b2) > botdiff) | |
| 2360 return 0; | |
| 2361 | |
| 2362 *psimilar = 1; | |
| 2363 return 0; | |
| 2364 } | |
| 2365 | |
| 2366 | |
| 2367 /*! | |
| 2368 * \brief boxaSimilar() | |
| 2369 * | |
| 2370 * \param[in] boxa1 | |
| 2371 * \param[in] boxa2 | |
| 2372 * \param[in] leftdiff, rightdiff, topdiff, botdiff | |
| 2373 * \param[in] debug output details of non-similar boxes | |
| 2374 * \param[out] psimilar 1 if similar; 0 otherwise | |
| 2375 * \param[out] pnasim [optional] na containing 1 if similar; else 0 | |
| 2376 * \return 0 if OK, 1 on error | |
| 2377 * | |
| 2378 * <pre> | |
| 2379 * Notes: | |
| 2380 * (1) See boxSimilar() for parameter usage. | |
| 2381 * (2) Corresponding boxes are taken in order in the two boxa. | |
| 2382 * (3) %nasim is an indicator array with a (0/1) for each box pair. | |
| 2383 * (4) With %nasim or debug == 1, boxes continue to be tested | |
| 2384 * after failure. | |
| 2385 * </pre> | |
| 2386 */ | |
| 2387 l_ok | |
| 2388 boxaSimilar(BOXA *boxa1, | |
| 2389 BOXA *boxa2, | |
| 2390 l_int32 leftdiff, | |
| 2391 l_int32 rightdiff, | |
| 2392 l_int32 topdiff, | |
| 2393 l_int32 botdiff, | |
| 2394 l_int32 debug, | |
| 2395 l_int32 *psimilar, | |
| 2396 NUMA **pnasim) | |
| 2397 { | |
| 2398 l_int32 i, n1, n2, match, mismatch; | |
| 2399 BOX *box1, *box2; | |
| 2400 | |
| 2401 if (psimilar) *psimilar = 0; | |
| 2402 if (pnasim) *pnasim = NULL; | |
| 2403 if (!boxa1 || !boxa2) | |
| 2404 return ERROR_INT("boxa1 and boxa2 not both defined", __func__, 1); | |
| 2405 if (!psimilar) | |
| 2406 return ERROR_INT("&similar not defined", __func__, 1); | |
| 2407 n1 = boxaGetCount(boxa1); | |
| 2408 n2 = boxaGetCount(boxa2); | |
| 2409 if (n1 != n2) { | |
| 2410 L_ERROR("boxa counts differ: %d vs %d\n", __func__, n1, n2); | |
| 2411 return 1; | |
| 2412 } | |
| 2413 if (pnasim) *pnasim = numaCreate(n1); | |
| 2414 | |
| 2415 mismatch = FALSE; | |
| 2416 for (i = 0; i < n1; i++) { | |
| 2417 box1 = boxaGetBox(boxa1, i, L_CLONE); | |
| 2418 box2 = boxaGetBox(boxa2, i, L_CLONE); | |
| 2419 boxSimilar(box1, box2, leftdiff, rightdiff, topdiff, botdiff, | |
| 2420 &match); | |
| 2421 boxDestroy(&box1); | |
| 2422 boxDestroy(&box2); | |
| 2423 if (pnasim) | |
| 2424 numaAddNumber(*pnasim, match); | |
| 2425 if (!match) { | |
| 2426 mismatch = TRUE; | |
| 2427 if (!debug && pnasim == NULL) | |
| 2428 return 0; | |
| 2429 else if (debug) | |
| 2430 L_INFO("box %d not similar\n", __func__, i); | |
| 2431 } | |
| 2432 } | |
| 2433 | |
| 2434 if (!mismatch) *psimilar = 1; | |
| 2435 return 0; | |
| 2436 } | |
| 2437 | |
| 2438 | |
| 2439 /*----------------------------------------------------------------------* | |
| 2440 * Boxa combine and split * | |
| 2441 *----------------------------------------------------------------------*/ | |
| 2442 /*! | |
| 2443 * \brief boxaJoin() | |
| 2444 * | |
| 2445 * \param[in] boxad dest boxa; add to this one | |
| 2446 * \param[in] boxas source boxa; add from this one | |
| 2447 * \param[in] istart starting index in boxas | |
| 2448 * \param[in] iend ending index in boxas; use -1 to cat all | |
| 2449 * \return 0 if OK, 1 on error | |
| 2450 * | |
| 2451 * <pre> | |
| 2452 * Notes: | |
| 2453 * (1) This appends a clone of each indicated box in boxas to boxad | |
| 2454 * (2) istart < 0 is taken to mean 'read from the start' (istart = 0) | |
| 2455 * (3) iend < 0 means 'read to the end' | |
| 2456 * (4) if boxas == NULL or has no boxes, this is a no-op. | |
| 2457 * </pre> | |
| 2458 */ | |
| 2459 l_ok | |
| 2460 boxaJoin(BOXA *boxad, | |
| 2461 BOXA *boxas, | |
| 2462 l_int32 istart, | |
| 2463 l_int32 iend) | |
| 2464 { | |
| 2465 l_int32 n, i; | |
| 2466 BOX *box; | |
| 2467 | |
| 2468 if (!boxad) | |
| 2469 return ERROR_INT("boxad not defined", __func__, 1); | |
| 2470 if (!boxas || ((n = boxaGetCount(boxas)) == 0)) | |
| 2471 return 0; | |
| 2472 | |
| 2473 if (istart < 0) | |
| 2474 istart = 0; | |
| 2475 if (iend < 0 || iend >= n) | |
| 2476 iend = n - 1; | |
| 2477 if (istart > iend) | |
| 2478 return ERROR_INT("istart > iend; nothing to add", __func__, 1); | |
| 2479 | |
| 2480 for (i = istart; i <= iend; i++) { | |
| 2481 box = boxaGetBox(boxas, i, L_CLONE); | |
| 2482 boxaAddBox(boxad, box, L_INSERT); | |
| 2483 } | |
| 2484 | |
| 2485 return 0; | |
| 2486 } | |
| 2487 | |
| 2488 | |
| 2489 /*! | |
| 2490 * \brief boxaaJoin() | |
| 2491 * | |
| 2492 * \param[in] baad dest boxaa; add to this one | |
| 2493 * \param[in] baas source boxaa; add from this one | |
| 2494 * \param[in] istart starting index in baas | |
| 2495 * \param[in] iend ending index in baas; use -1 to cat all | |
| 2496 * \return 0 if OK, 1 on error | |
| 2497 * | |
| 2498 * <pre> | |
| 2499 * Notes: | |
| 2500 * (1) This appends a clone of each indicated boxa in baas to baad | |
| 2501 * (2) istart < 0 is taken to mean 'read from the start' (istart = 0) | |
| 2502 * (3) iend < 0 means 'read to the end' | |
| 2503 * (4) if baas == NULL, this is a no-op. | |
| 2504 * </pre> | |
| 2505 */ | |
| 2506 l_ok | |
| 2507 boxaaJoin(BOXAA *baad, | |
| 2508 BOXAA *baas, | |
| 2509 l_int32 istart, | |
| 2510 l_int32 iend) | |
| 2511 { | |
| 2512 l_int32 n, i; | |
| 2513 BOXA *boxa; | |
| 2514 | |
| 2515 if (!baad) | |
| 2516 return ERROR_INT("baad not defined", __func__, 1); | |
| 2517 if (!baas) | |
| 2518 return 0; | |
| 2519 | |
| 2520 if (istart < 0) | |
| 2521 istart = 0; | |
| 2522 n = boxaaGetCount(baas); | |
| 2523 if (iend < 0 || iend >= n) | |
| 2524 iend = n - 1; | |
| 2525 if (istart > iend) | |
| 2526 return ERROR_INT("istart > iend; nothing to add", __func__, 1); | |
| 2527 | |
| 2528 for (i = istart; i <= iend; i++) { | |
| 2529 boxa = boxaaGetBoxa(baas, i, L_CLONE); | |
| 2530 boxaaAddBoxa(baad, boxa, L_INSERT); | |
| 2531 } | |
| 2532 | |
| 2533 return 0; | |
| 2534 } | |
| 2535 | |
| 2536 | |
| 2537 /*! | |
| 2538 * \brief boxaSplitEvenOdd() | |
| 2539 * | |
| 2540 * \param[in] boxa | |
| 2541 * \param[in] fillflag 1 to put invalid boxes in place; 0 to omit | |
| 2542 * \param[out] pboxae, pboxao save even and odd boxes in their separate | |
| 2543 * boxa, setting the other type to invalid boxes. | |
| 2544 * \return 0 if OK, 1 on error | |
| 2545 * | |
| 2546 * <pre> | |
| 2547 * Notes: | |
| 2548 * (1) If %fillflag == 1, boxae has copies of the even boxes | |
| 2549 * in their original location, and nvalid boxes are placed | |
| 2550 * in the odd array locations. And v.v. | |
| 2551 * (2) If %fillflag == 0, boxae has only copies of the even boxes. | |
| 2552 * </pre> | |
| 2553 */ | |
| 2554 l_ok | |
| 2555 boxaSplitEvenOdd(BOXA *boxa, | |
| 2556 l_int32 fillflag, | |
| 2557 BOXA **pboxae, | |
| 2558 BOXA **pboxao) | |
| 2559 { | |
| 2560 l_int32 i, n; | |
| 2561 BOX *box, *box1; | |
| 2562 | |
| 2563 if (pboxae) *pboxae = NULL; | |
| 2564 if (pboxao) *pboxao = NULL; | |
| 2565 if (!pboxae || !pboxao) | |
| 2566 return ERROR_INT("&boxae and &boxao not both defined", __func__, 1); | |
| 2567 if (!boxa) | |
| 2568 return ERROR_INT("boxa not defined", __func__, 1); | |
| 2569 | |
| 2570 n = boxaGetCount(boxa); | |
| 2571 *pboxae = boxaCreate(n); | |
| 2572 *pboxao = boxaCreate(n); | |
| 2573 if (fillflag == 0) { | |
| 2574 /* don't fill with invalid boxes; end up with half-size boxa */ | |
| 2575 for (i = 0; i < n; i++) { | |
| 2576 box = boxaGetBox(boxa, i, L_COPY); | |
| 2577 if ((i & 1) == 0) | |
| 2578 boxaAddBox(*pboxae, box, L_INSERT); | |
| 2579 else | |
| 2580 boxaAddBox(*pboxao, box, L_INSERT); | |
| 2581 } | |
| 2582 } else { | |
| 2583 for (i = 0; i < n; i++) { | |
| 2584 box = boxaGetBox(boxa, i, L_COPY); | |
| 2585 box1 = boxCreate(0, 0, 0, 0); /* empty placeholder */ | |
| 2586 if ((i & 1) == 0) { | |
| 2587 boxaAddBox(*pboxae, box, L_INSERT); | |
| 2588 boxaAddBox(*pboxao, box1, L_INSERT); | |
| 2589 } else { | |
| 2590 boxaAddBox(*pboxae, box1, L_INSERT); | |
| 2591 boxaAddBox(*pboxao, box, L_INSERT); | |
| 2592 } | |
| 2593 } | |
| 2594 } | |
| 2595 return 0; | |
| 2596 } | |
| 2597 | |
| 2598 | |
| 2599 /*! | |
| 2600 * \brief boxaMergeEvenOdd() | |
| 2601 * | |
| 2602 * \param[in] boxae boxes to go in even positions in merged boxa | |
| 2603 * \param[in] boxao boxes to go in odd positions in merged boxa | |
| 2604 * \param[in] fillflag 1 if there are invalid boxes in placeholders | |
| 2605 * \return boxad merged, or NULL on error | |
| 2606 * | |
| 2607 * <pre> | |
| 2608 * Notes: | |
| 2609 * (1) This is essentially the inverse of boxaSplitEvenOdd(). | |
| 2610 * Typically, boxae and boxao were generated by boxaSplitEvenOdd(), | |
| 2611 * and the value of %fillflag needs to be the same in both calls. | |
| 2612 * (2) If %fillflag == 1, both boxae and boxao are of the same size; | |
| 2613 * otherwise boxae may have one more box than boxao. | |
| 2614 * </pre> | |
| 2615 */ | |
| 2616 BOXA * | |
| 2617 boxaMergeEvenOdd(BOXA *boxae, | |
| 2618 BOXA *boxao, | |
| 2619 l_int32 fillflag) | |
| 2620 { | |
| 2621 l_int32 i, n, ne, no; | |
| 2622 BOX *box; | |
| 2623 BOXA *boxad; | |
| 2624 | |
| 2625 if (!boxae || !boxao) | |
| 2626 return (BOXA *)ERROR_PTR("boxae and boxao not defined", __func__, NULL); | |
| 2627 ne = boxaGetCount(boxae); | |
| 2628 no = boxaGetCount(boxao); | |
| 2629 if (ne < no || ne > no + 1) | |
| 2630 return (BOXA *)ERROR_PTR("boxa sizes invalid", __func__, NULL); | |
| 2631 | |
| 2632 boxad = boxaCreate(ne); | |
| 2633 if (fillflag == 0) { /* both are approx. half-sized; all valid boxes */ | |
| 2634 n = ne + no; | |
| 2635 for (i = 0; i < n; i++) { | |
| 2636 if ((i & 1) == 0) | |
| 2637 box = boxaGetBox(boxae, i / 2, L_COPY); | |
| 2638 else | |
| 2639 box = boxaGetBox(boxao, i / 2, L_COPY); | |
| 2640 boxaAddBox(boxad, box, L_INSERT); | |
| 2641 } | |
| 2642 } else { /* both are full size and have invalid placeholders */ | |
| 2643 for (i = 0; i < ne; i++) { | |
| 2644 if ((i & 1) == 0) | |
| 2645 box = boxaGetBox(boxae, i, L_COPY); | |
| 2646 else | |
| 2647 box = boxaGetBox(boxao, i, L_COPY); | |
| 2648 boxaAddBox(boxad, box, L_INSERT); | |
| 2649 } | |
| 2650 } | |
| 2651 return boxad; | |
| 2652 } |
