Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/boxfunc4.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 boxfunc4.c | |
| 29 * <pre> | |
| 30 * | |
| 31 * Boxa and Boxaa range selection | |
| 32 * BOXA *boxaSelectRange() | |
| 33 * BOXAA *boxaaSelectRange() | |
| 34 * | |
| 35 * Boxa size selection | |
| 36 * BOXA *boxaSelectBySize() | |
| 37 * NUMA *boxaMakeSizeIndicator() | |
| 38 * BOXA *boxaSelectByArea() | |
| 39 * NUMA *boxaMakeAreaIndicator() | |
| 40 * BOXA *boxaSelectByWHRatio() | |
| 41 * NUMA *boxaMakeWHRatioIndicator() | |
| 42 * BOXA *boxaSelectWithIndicator() | |
| 43 * | |
| 44 * Boxa permutation | |
| 45 * BOXA *boxaPermutePseudorandom() | |
| 46 * BOXA *boxaPermuteRandom() | |
| 47 * l_int32 boxaSwapBoxes() | |
| 48 * | |
| 49 * Boxa and box conversions | |
| 50 * PTA *boxaConvertToPta() | |
| 51 * BOXA *ptaConvertToBoxa() | |
| 52 * PTA *boxConvertToPta() | |
| 53 * BOX *ptaConvertToBox() | |
| 54 * | |
| 55 * Miscellaneous boxa functions | |
| 56 * l_int32 boxaGetExtent() | |
| 57 * l_int32 boxaGetCoverage() | |
| 58 * l_int32 boxaaSizeRange() | |
| 59 * l_int32 boxaSizeRange() | |
| 60 * l_int32 boxaLocationRange() | |
| 61 * NUMA *boxaGetSizes() | |
| 62 * l_int32 boxaGetArea() | |
| 63 * PIX *boxaDisplayTiled() | |
| 64 * </pre> | |
| 65 */ | |
| 66 | |
| 67 #ifdef HAVE_CONFIG_H | |
| 68 #include <config_auto.h> | |
| 69 #endif /* HAVE_CONFIG_H */ | |
| 70 | |
| 71 #include <math.h> | |
| 72 #include "allheaders.h" | |
| 73 #include "pix_internal.h" | |
| 74 | |
| 75 /*---------------------------------------------------------------------* | |
| 76 * Boxa and boxaa range selection * | |
| 77 *---------------------------------------------------------------------*/ | |
| 78 /*! | |
| 79 * \brief boxaSelectRange() | |
| 80 * | |
| 81 * \param[in] boxas | |
| 82 * \param[in] first use 0 to select from the beginning | |
| 83 * \param[in] last use -1 to select to the end | |
| 84 * \param[in] copyflag L_COPY, L_CLONE | |
| 85 * \return boxad, or NULL on error | |
| 86 * | |
| 87 * <pre> | |
| 88 * Notes: | |
| 89 * (1) The copyflag specifies what we do with each box from boxas. | |
| 90 * Specifically, L_CLONE inserts a clone into boxad of each | |
| 91 * selected box from boxas. | |
| 92 * </pre> | |
| 93 */ | |
| 94 BOXA * | |
| 95 boxaSelectRange(BOXA *boxas, | |
| 96 l_int32 first, | |
| 97 l_int32 last, | |
| 98 l_int32 copyflag) | |
| 99 { | |
| 100 l_int32 n, nbox, i; | |
| 101 BOX *box; | |
| 102 BOXA *boxad; | |
| 103 | |
| 104 if (!boxas) | |
| 105 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); | |
| 106 if (copyflag != L_COPY && copyflag != L_CLONE) | |
| 107 return (BOXA *)ERROR_PTR("invalid copyflag", __func__, NULL); | |
| 108 if ((n = boxaGetCount(boxas)) == 0) { | |
| 109 L_WARNING("boxas is empty\n", __func__); | |
| 110 return boxaCopy(boxas, copyflag); | |
| 111 } | |
| 112 first = L_MAX(0, first); | |
| 113 if (last < 0) last = n - 1; | |
| 114 if (first >= n) | |
| 115 return (BOXA *)ERROR_PTR("invalid first", __func__, NULL); | |
| 116 if (last >= n) { | |
| 117 L_WARNING("last = %d is beyond max index = %d; adjusting\n", | |
| 118 __func__, last, n - 1); | |
| 119 last = n - 1; | |
| 120 } | |
| 121 if (first > last) | |
| 122 return (BOXA *)ERROR_PTR("first > last", __func__, NULL); | |
| 123 | |
| 124 nbox = last - first + 1; | |
| 125 boxad = boxaCreate(nbox); | |
| 126 for (i = first; i <= last; i++) { | |
| 127 box = boxaGetBox(boxas, i, copyflag); | |
| 128 boxaAddBox(boxad, box, L_INSERT); | |
| 129 } | |
| 130 return boxad; | |
| 131 } | |
| 132 | |
| 133 | |
| 134 /*! | |
| 135 * \brief boxaaSelectRange() | |
| 136 * | |
| 137 * \param[in] baas | |
| 138 * \param[in] first use 0 to select from the beginning | |
| 139 * \param[in] last use -1 to select to the end | |
| 140 * \param[in] copyflag L_COPY, L_CLONE | |
| 141 * \return baad, or NULL on error | |
| 142 * | |
| 143 * <pre> | |
| 144 * Notes: | |
| 145 * (1) The copyflag specifies what we do with each boxa from baas. | |
| 146 * Specifically, L_CLONE inserts a clone into baad of each | |
| 147 * selected boxa from baas. | |
| 148 * </pre> | |
| 149 */ | |
| 150 BOXAA * | |
| 151 boxaaSelectRange(BOXAA *baas, | |
| 152 l_int32 first, | |
| 153 l_int32 last, | |
| 154 l_int32 copyflag) | |
| 155 { | |
| 156 l_int32 n, nboxa, i; | |
| 157 BOXA *boxa; | |
| 158 BOXAA *baad; | |
| 159 | |
| 160 if (!baas) | |
| 161 return (BOXAA *)ERROR_PTR("baas not defined", __func__, NULL); | |
| 162 if (copyflag != L_COPY && copyflag != L_CLONE) | |
| 163 return (BOXAA *)ERROR_PTR("invalid copyflag", __func__, NULL); | |
| 164 if ((n = boxaaGetCount(baas)) == 0) | |
| 165 return (BOXAA *)ERROR_PTR("empty baas", __func__, NULL); | |
| 166 first = L_MAX(0, first); | |
| 167 if (last < 0) last = n - 1; | |
| 168 if (first >= n) | |
| 169 return (BOXAA *)ERROR_PTR("invalid first", __func__, NULL); | |
| 170 if (last >= n) { | |
| 171 L_WARNING("last = %d is beyond max index = %d; adjusting\n", | |
| 172 __func__, last, n - 1); | |
| 173 last = n - 1; | |
| 174 } | |
| 175 if (first > last) | |
| 176 return (BOXAA *)ERROR_PTR("first > last", __func__, NULL); | |
| 177 | |
| 178 nboxa = last - first + 1; | |
| 179 baad = boxaaCreate(nboxa); | |
| 180 for (i = first; i <= last; i++) { | |
| 181 boxa = boxaaGetBoxa(baas, i, copyflag); | |
| 182 boxaaAddBoxa(baad, boxa, L_INSERT); | |
| 183 } | |
| 184 return baad; | |
| 185 } | |
| 186 | |
| 187 | |
| 188 /*---------------------------------------------------------------------* | |
| 189 * Boxa size selection * | |
| 190 *---------------------------------------------------------------------*/ | |
| 191 /*! | |
| 192 * \brief boxaSelectBySize() | |
| 193 * | |
| 194 * \param[in] boxas | |
| 195 * \param[in] width, height threshold dimensions | |
| 196 * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT, | |
| 197 * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH | |
| 198 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 199 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 200 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned | |
| 201 * \return boxad filtered set, or NULL on error | |
| 202 * | |
| 203 * <pre> | |
| 204 * Notes: | |
| 205 * (1) The args specify constraints on the size of the | |
| 206 * components that are kept. | |
| 207 * (2) Uses box copies in the new boxa. | |
| 208 * (3) If the selection type is L_SELECT_WIDTH, the input | |
| 209 * height is ignored, and v.v. | |
| 210 * (4) To keep small components, use relation = L_SELECT_IF_LT or | |
| 211 * L_SELECT_IF_LTE. | |
| 212 * To keep large components, use relation = L_SELECT_IF_GT or | |
| 213 * L_SELECT_IF_GTE. | |
| 214 * </pre> | |
| 215 */ | |
| 216 BOXA * | |
| 217 boxaSelectBySize(BOXA *boxas, | |
| 218 l_int32 width, | |
| 219 l_int32 height, | |
| 220 l_int32 type, | |
| 221 l_int32 relation, | |
| 222 l_int32 *pchanged) | |
| 223 { | |
| 224 BOXA *boxad; | |
| 225 NUMA *na; | |
| 226 | |
| 227 if (pchanged) *pchanged = FALSE; | |
| 228 if (!boxas) | |
| 229 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); | |
| 230 if (boxaGetCount(boxas) == 0) { | |
| 231 L_WARNING("boxas is empty\n", __func__); | |
| 232 return boxaCopy(boxas, L_COPY); | |
| 233 } | |
| 234 if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && | |
| 235 type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) | |
| 236 return (BOXA *)ERROR_PTR("invalid type", __func__, NULL); | |
| 237 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && | |
| 238 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) | |
| 239 return (BOXA *)ERROR_PTR("invalid relation", __func__, NULL); | |
| 240 | |
| 241 /* Compute the indicator array for saving components */ | |
| 242 if ((na = | |
| 243 boxaMakeSizeIndicator(boxas, width, height, type, relation)) == NULL) | |
| 244 return (BOXA *)ERROR_PTR("na not made", __func__, NULL); | |
| 245 | |
| 246 /* Filter to get output */ | |
| 247 boxad = boxaSelectWithIndicator(boxas, na, pchanged); | |
| 248 | |
| 249 numaDestroy(&na); | |
| 250 return boxad; | |
| 251 } | |
| 252 | |
| 253 | |
| 254 /*! | |
| 255 * \brief boxaMakeSizeIndicator() | |
| 256 * | |
| 257 * \param[in] boxa | |
| 258 * \param[in] width, height threshold dimensions | |
| 259 * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT, | |
| 260 * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH | |
| 261 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 262 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 263 * \return na indicator array, or NULL on error | |
| 264 * | |
| 265 * <pre> | |
| 266 * Notes: | |
| 267 * (1) The args specify constraints on the size of the | |
| 268 * components that are kept. | |
| 269 * (2) If the selection type is L_SELECT_WIDTH, the input | |
| 270 * height is ignored, and v.v. | |
| 271 * (3) To keep small components, use relation = L_SELECT_IF_LT or | |
| 272 * L_SELECT_IF_LTE. | |
| 273 * To keep large components, use relation = L_SELECT_IF_GT or | |
| 274 * L_SELECT_IF_GTE. | |
| 275 * </pre> | |
| 276 */ | |
| 277 NUMA * | |
| 278 boxaMakeSizeIndicator(BOXA *boxa, | |
| 279 l_int32 width, | |
| 280 l_int32 height, | |
| 281 l_int32 type, | |
| 282 l_int32 relation) | |
| 283 { | |
| 284 l_int32 i, n, w, h, ival; | |
| 285 NUMA *na; | |
| 286 | |
| 287 if (!boxa) | |
| 288 return (NUMA *)ERROR_PTR("boxa not defined", __func__, NULL); | |
| 289 if ((n = boxaGetCount(boxa)) == 0) | |
| 290 return (NUMA *)ERROR_PTR("boxa is empty", __func__, NULL); | |
| 291 if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && | |
| 292 type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) | |
| 293 return (NUMA *)ERROR_PTR("invalid type", __func__, NULL); | |
| 294 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && | |
| 295 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) | |
| 296 return (NUMA *)ERROR_PTR("invalid relation", __func__, NULL); | |
| 297 | |
| 298 na = numaCreate(n); | |
| 299 for (i = 0; i < n; i++) { | |
| 300 ival = 0; | |
| 301 boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); | |
| 302 switch (type) | |
| 303 { | |
| 304 case L_SELECT_WIDTH: | |
| 305 if ((relation == L_SELECT_IF_LT && w < width) || | |
| 306 (relation == L_SELECT_IF_GT && w > width) || | |
| 307 (relation == L_SELECT_IF_LTE && w <= width) || | |
| 308 (relation == L_SELECT_IF_GTE && w >= width)) | |
| 309 ival = 1; | |
| 310 break; | |
| 311 case L_SELECT_HEIGHT: | |
| 312 if ((relation == L_SELECT_IF_LT && h < height) || | |
| 313 (relation == L_SELECT_IF_GT && h > height) || | |
| 314 (relation == L_SELECT_IF_LTE && h <= height) || | |
| 315 (relation == L_SELECT_IF_GTE && h >= height)) | |
| 316 ival = 1; | |
| 317 break; | |
| 318 case L_SELECT_IF_EITHER: | |
| 319 if (((relation == L_SELECT_IF_LT) && (w < width || h < height)) || | |
| 320 ((relation == L_SELECT_IF_GT) && (w > width || h > height)) || | |
| 321 ((relation == L_SELECT_IF_LTE) && (w <= width || h <= height)) || | |
| 322 ((relation == L_SELECT_IF_GTE) && (w >= width || h >= height))) | |
| 323 ival = 1; | |
| 324 break; | |
| 325 case L_SELECT_IF_BOTH: | |
| 326 if (((relation == L_SELECT_IF_LT) && (w < width && h < height)) || | |
| 327 ((relation == L_SELECT_IF_GT) && (w > width && h > height)) || | |
| 328 ((relation == L_SELECT_IF_LTE) && (w <= width && h <= height)) || | |
| 329 ((relation == L_SELECT_IF_GTE) && (w >= width && h >= height))) | |
| 330 ival = 1; | |
| 331 break; | |
| 332 default: | |
| 333 L_WARNING("can't get here!\n", __func__); | |
| 334 break; | |
| 335 } | |
| 336 numaAddNumber(na, ival); | |
| 337 } | |
| 338 | |
| 339 return na; | |
| 340 } | |
| 341 | |
| 342 | |
| 343 /*! | |
| 344 * \brief boxaSelectByArea() | |
| 345 * | |
| 346 * \param[in] boxas | |
| 347 * \param[in] area threshold value of width * height | |
| 348 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 349 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 350 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned | |
| 351 * \return boxad filtered set, or NULL on error | |
| 352 * | |
| 353 * <pre> | |
| 354 * Notes: | |
| 355 * (1) Uses box copies in the new boxa. | |
| 356 * (2) To keep small components, use relation = L_SELECT_IF_LT or | |
| 357 * L_SELECT_IF_LTE. | |
| 358 * To keep large components, use relation = L_SELECT_IF_GT or | |
| 359 * L_SELECT_IF_GTE. | |
| 360 * </pre> | |
| 361 */ | |
| 362 BOXA * | |
| 363 boxaSelectByArea(BOXA *boxas, | |
| 364 l_int32 area, | |
| 365 l_int32 relation, | |
| 366 l_int32 *pchanged) | |
| 367 { | |
| 368 BOXA *boxad; | |
| 369 NUMA *na; | |
| 370 | |
| 371 if (pchanged) *pchanged = FALSE; | |
| 372 if (!boxas) | |
| 373 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); | |
| 374 if (boxaGetCount(boxas) == 0) { | |
| 375 L_WARNING("boxas is empty\n", __func__); | |
| 376 return boxaCopy(boxas, L_COPY); | |
| 377 } | |
| 378 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && | |
| 379 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) | |
| 380 return (BOXA *)ERROR_PTR("invalid relation", __func__, NULL); | |
| 381 | |
| 382 /* Compute the indicator array for saving components */ | |
| 383 na = boxaMakeAreaIndicator(boxas, area, relation); | |
| 384 | |
| 385 /* Filter to get output */ | |
| 386 boxad = boxaSelectWithIndicator(boxas, na, pchanged); | |
| 387 | |
| 388 numaDestroy(&na); | |
| 389 return boxad; | |
| 390 } | |
| 391 | |
| 392 | |
| 393 /*! | |
| 394 * \brief boxaMakeAreaIndicator() | |
| 395 * | |
| 396 * \param[in] boxa | |
| 397 * \param[in] area threshold value of width * height | |
| 398 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 399 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 400 * \return na indicator array, or NULL on error | |
| 401 * | |
| 402 * <pre> | |
| 403 * Notes: | |
| 404 * (1) To keep small components, use relation = L_SELECT_IF_LT or | |
| 405 * L_SELECT_IF_LTE. | |
| 406 * To keep large components, use relation = L_SELECT_IF_GT or | |
| 407 * L_SELECT_IF_GTE. | |
| 408 * </pre> | |
| 409 */ | |
| 410 NUMA * | |
| 411 boxaMakeAreaIndicator(BOXA *boxa, | |
| 412 l_int32 area, | |
| 413 l_int32 relation) | |
| 414 { | |
| 415 l_int32 i, n, w, h, ival; | |
| 416 NUMA *na; | |
| 417 | |
| 418 if (!boxa) | |
| 419 return (NUMA *)ERROR_PTR("boxa not defined", __func__, NULL); | |
| 420 if ((n = boxaGetCount(boxa)) == 0) | |
| 421 return (NUMA *)ERROR_PTR("boxa is empty", __func__, NULL); | |
| 422 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && | |
| 423 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) | |
| 424 return (NUMA *)ERROR_PTR("invalid relation", __func__, NULL); | |
| 425 | |
| 426 na = numaCreate(n); | |
| 427 for (i = 0; i < n; i++) { | |
| 428 ival = 0; | |
| 429 boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); | |
| 430 | |
| 431 if ((relation == L_SELECT_IF_LT && w * h < area) || | |
| 432 (relation == L_SELECT_IF_GT && w * h > area) || | |
| 433 (relation == L_SELECT_IF_LTE && w * h <= area) || | |
| 434 (relation == L_SELECT_IF_GTE && w * h >= area)) | |
| 435 ival = 1; | |
| 436 numaAddNumber(na, ival); | |
| 437 } | |
| 438 | |
| 439 return na; | |
| 440 } | |
| 441 | |
| 442 | |
| 443 /*! | |
| 444 * \brief boxaSelectByWHRatio() | |
| 445 * | |
| 446 * \param[in] boxas | |
| 447 * \param[in] ratio width/height threshold value | |
| 448 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 449 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 450 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned | |
| 451 * \return boxad filtered set, or NULL on error | |
| 452 * | |
| 453 * <pre> | |
| 454 * Notes: | |
| 455 * (1) Uses box copies in the new boxa. | |
| 456 * (2) To keep narrow components, use relation = L_SELECT_IF_LT or | |
| 457 * L_SELECT_IF_LTE. | |
| 458 * To keep wide components, use relation = L_SELECT_IF_GT or | |
| 459 * L_SELECT_IF_GTE. | |
| 460 * </pre> | |
| 461 */ | |
| 462 BOXA * | |
| 463 boxaSelectByWHRatio(BOXA *boxas, | |
| 464 l_float32 ratio, | |
| 465 l_int32 relation, | |
| 466 l_int32 *pchanged) | |
| 467 { | |
| 468 BOXA *boxad; | |
| 469 NUMA *na; | |
| 470 | |
| 471 if (pchanged) *pchanged = FALSE; | |
| 472 if (!boxas) | |
| 473 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); | |
| 474 if (boxaGetCount(boxas) == 0) { | |
| 475 L_WARNING("boxas is empty\n", __func__); | |
| 476 return boxaCopy(boxas, L_COPY); | |
| 477 } | |
| 478 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && | |
| 479 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) | |
| 480 return (BOXA *)ERROR_PTR("invalid relation", __func__, NULL); | |
| 481 | |
| 482 /* Compute the indicator array for saving components */ | |
| 483 na = boxaMakeWHRatioIndicator(boxas, ratio, relation); | |
| 484 | |
| 485 /* Filter to get output */ | |
| 486 boxad = boxaSelectWithIndicator(boxas, na, pchanged); | |
| 487 | |
| 488 numaDestroy(&na); | |
| 489 return boxad; | |
| 490 } | |
| 491 | |
| 492 | |
| 493 /*! | |
| 494 * \brief boxaMakeWHRatioIndicator() | |
| 495 * | |
| 496 * \param[in] boxa | |
| 497 * \param[in] ratio width/height threshold value | |
| 498 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 499 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 500 * \return na indicator array, or NULL on error | |
| 501 * | |
| 502 * <pre> | |
| 503 * Notes: | |
| 504 * (1) To keep narrow components, use relation = L_SELECT_IF_LT or | |
| 505 * L_SELECT_IF_LTE. | |
| 506 * To keep wide components, use relation = L_SELECT_IF_GT or | |
| 507 * L_SELECT_IF_GTE. | |
| 508 * </pre> | |
| 509 */ | |
| 510 NUMA * | |
| 511 boxaMakeWHRatioIndicator(BOXA *boxa, | |
| 512 l_float32 ratio, | |
| 513 l_int32 relation) | |
| 514 { | |
| 515 l_int32 i, n, w, h, ival; | |
| 516 l_float32 whratio; | |
| 517 NUMA *na; | |
| 518 | |
| 519 if (!boxa) | |
| 520 return (NUMA *)ERROR_PTR("boxa not defined", __func__, NULL); | |
| 521 if ((n = boxaGetCount(boxa)) == 0) | |
| 522 return (NUMA *)ERROR_PTR("boxa is empty", __func__, NULL); | |
| 523 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && | |
| 524 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) | |
| 525 return (NUMA *)ERROR_PTR("invalid relation", __func__, NULL); | |
| 526 | |
| 527 na = numaCreate(n); | |
| 528 for (i = 0; i < n; i++) { | |
| 529 ival = 0; | |
| 530 boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); | |
| 531 whratio = (l_float32)w / (l_float32)h; | |
| 532 | |
| 533 if ((relation == L_SELECT_IF_LT && whratio < ratio) || | |
| 534 (relation == L_SELECT_IF_GT && whratio > ratio) || | |
| 535 (relation == L_SELECT_IF_LTE && whratio <= ratio) || | |
| 536 (relation == L_SELECT_IF_GTE && whratio >= ratio)) | |
| 537 ival = 1; | |
| 538 numaAddNumber(na, ival); | |
| 539 } | |
| 540 | |
| 541 return na; | |
| 542 } | |
| 543 | |
| 544 | |
| 545 /*! | |
| 546 * \brief boxaSelectWithIndicator() | |
| 547 * | |
| 548 * \param[in] boxas | |
| 549 * \param[in] na indicator numa | |
| 550 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned | |
| 551 * \return boxad, or NULL on error | |
| 552 * | |
| 553 * <pre> | |
| 554 * Notes: | |
| 555 * (1) Returns a copy of the boxa if no components are removed. | |
| 556 * (2) Uses box copies in the new boxa. | |
| 557 * (3) The indicator numa has values 0 (ignore) and 1 (accept). | |
| 558 * (4) If all indicator values are 0, the returned boxa is empty. | |
| 559 * </pre> | |
| 560 */ | |
| 561 BOXA * | |
| 562 boxaSelectWithIndicator(BOXA *boxas, | |
| 563 NUMA *na, | |
| 564 l_int32 *pchanged) | |
| 565 { | |
| 566 l_int32 i, n, ival, nsave; | |
| 567 BOX *box; | |
| 568 BOXA *boxad; | |
| 569 | |
| 570 if (pchanged) *pchanged = FALSE; | |
| 571 if (!boxas) | |
| 572 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); | |
| 573 if (!na) | |
| 574 return (BOXA *)ERROR_PTR("na not defined", __func__, NULL); | |
| 575 | |
| 576 nsave = 0; | |
| 577 n = numaGetCount(na); | |
| 578 for (i = 0; i < n; i++) { | |
| 579 numaGetIValue(na, i, &ival); | |
| 580 if (ival == 1) nsave++; | |
| 581 } | |
| 582 | |
| 583 if (nsave == n) { | |
| 584 if (pchanged) *pchanged = FALSE; | |
| 585 return boxaCopy(boxas, L_COPY); | |
| 586 } | |
| 587 if (pchanged) *pchanged = TRUE; | |
| 588 boxad = boxaCreate(nsave); | |
| 589 for (i = 0; i < n; i++) { | |
| 590 numaGetIValue(na, i, &ival); | |
| 591 if (ival == 0) continue; | |
| 592 box = boxaGetBox(boxas, i, L_COPY); | |
| 593 boxaAddBox(boxad, box, L_INSERT); | |
| 594 } | |
| 595 | |
| 596 return boxad; | |
| 597 } | |
| 598 | |
| 599 | |
| 600 /*---------------------------------------------------------------------* | |
| 601 * Boxa Permutation * | |
| 602 *---------------------------------------------------------------------*/ | |
| 603 /*! | |
| 604 * \brief boxaPermutePseudorandom() | |
| 605 * | |
| 606 * \param[in] boxas input boxa | |
| 607 * \return boxad with boxes permuted, or NULL on error | |
| 608 * | |
| 609 * <pre> | |
| 610 * Notes: | |
| 611 * (1) This does a pseudorandom in-place permutation of the boxes. | |
| 612 * (2) The result is guaranteed not to have any boxes in their | |
| 613 * original position, but it is not very random. If you | |
| 614 * need randomness, use boxaPermuteRandom(). | |
| 615 * </pre> | |
| 616 */ | |
| 617 BOXA * | |
| 618 boxaPermutePseudorandom(BOXA *boxas) | |
| 619 { | |
| 620 l_int32 n; | |
| 621 NUMA *na; | |
| 622 BOXA *boxad; | |
| 623 | |
| 624 if (!boxas) | |
| 625 return (BOXA *)ERROR_PTR("boxa not defined", __func__, NULL); | |
| 626 | |
| 627 n = boxaGetCount(boxas); | |
| 628 na = numaPseudorandomSequence(n, 0); | |
| 629 boxad = boxaSortByIndex(boxas, na); | |
| 630 numaDestroy(&na); | |
| 631 return boxad; | |
| 632 } | |
| 633 | |
| 634 | |
| 635 /*! | |
| 636 * \brief boxaPermuteRandom() | |
| 637 * | |
| 638 * \param[in] boxad [optional] can be null or equal to boxas | |
| 639 * \param[in] boxas input boxa | |
| 640 * \return boxad with boxes permuted, or NULL on error | |
| 641 * | |
| 642 * <pre> | |
| 643 * Notes: | |
| 644 * (1) If boxad is null, make a copy of boxas and permute the copy. | |
| 645 * Otherwise, boxad must be equal to boxas, and the operation | |
| 646 * is done in-place. | |
| 647 * (2) If boxas is empty, return an empty boxad. | |
| 648 * (3) This does a random in-place permutation of the boxes, | |
| 649 * by swapping each box in turn with a random box. The | |
| 650 * result is almost guaranteed not to have any boxes in their | |
| 651 * original position. | |
| 652 * (4) MSVC rand() has MAX_RAND = 2^15 - 1, so it will not do | |
| 653 * a proper permutation is the number of boxes exceeds this. | |
| 654 * </pre> | |
| 655 */ | |
| 656 BOXA * | |
| 657 boxaPermuteRandom(BOXA *boxad, | |
| 658 BOXA *boxas) | |
| 659 { | |
| 660 l_int32 i, n, index; | |
| 661 | |
| 662 if (!boxas) | |
| 663 return (BOXA *)ERROR_PTR("boxa not defined", __func__, NULL); | |
| 664 if (boxad && (boxad != boxas)) | |
| 665 return (BOXA *)ERROR_PTR("boxad defined but in-place", __func__, NULL); | |
| 666 | |
| 667 if (!boxad) | |
| 668 boxad = boxaCopy(boxas, L_COPY); | |
| 669 if ((n = boxaGetCount(boxad)) == 0) | |
| 670 return boxad; | |
| 671 index = (l_uint32)rand() % n; | |
| 672 index = L_MAX(1, index); | |
| 673 boxaSwapBoxes(boxad, 0, index); | |
| 674 for (i = 1; i < n; i++) { | |
| 675 index = (l_uint32)rand() % n; | |
| 676 if (index == i) index--; | |
| 677 boxaSwapBoxes(boxad, i, index); | |
| 678 } | |
| 679 | |
| 680 return boxad; | |
| 681 } | |
| 682 | |
| 683 | |
| 684 /*! | |
| 685 * \brief boxaSwapBoxes() | |
| 686 * | |
| 687 * \param[in] boxa | |
| 688 * \param[in] i, j two indices of boxes, that are to be swapped | |
| 689 * \return 0 if OK, 1 on error | |
| 690 */ | |
| 691 l_ok | |
| 692 boxaSwapBoxes(BOXA *boxa, | |
| 693 l_int32 i, | |
| 694 l_int32 j) | |
| 695 { | |
| 696 l_int32 n; | |
| 697 BOX *box; | |
| 698 | |
| 699 if (!boxa) | |
| 700 return ERROR_INT("boxa not defined", __func__, 1); | |
| 701 n = boxaGetCount(boxa); | |
| 702 if (i < 0 || i >= n) | |
| 703 return ERROR_INT("i invalid", __func__, 1); | |
| 704 if (j < 0 || j >= n) | |
| 705 return ERROR_INT("j invalid", __func__, 1); | |
| 706 if (i == j) | |
| 707 return ERROR_INT("i == j", __func__, 1); | |
| 708 | |
| 709 box = boxa->box[i]; | |
| 710 boxa->box[i] = boxa->box[j]; | |
| 711 boxa->box[j] = box; | |
| 712 return 0; | |
| 713 } | |
| 714 | |
| 715 | |
| 716 /*---------------------------------------------------------------------* | |
| 717 * Boxa and Box Conversions * | |
| 718 *---------------------------------------------------------------------*/ | |
| 719 /*! | |
| 720 * \brief boxaConvertToPta() | |
| 721 * | |
| 722 * \param[in] boxa | |
| 723 * \param[in] ncorners 2 or 4 for the representation of each box | |
| 724 * \return pta with %ncorners points for each box in the boxa, | |
| 725 * or NULL on error | |
| 726 * | |
| 727 * <pre> | |
| 728 * Notes: | |
| 729 * (1) If ncorners == 2, we select the UL and LR corners. | |
| 730 * Otherwise we save all 4 corners in this order: UL, UR, LL, LR. | |
| 731 * (2) Other boxa --> pta functions are: | |
| 732 * * boxaExtractAsPta(): allows extraction of any dimension | |
| 733 * and/or side location, with each in a separate pta. | |
| 734 * * boxaExtractCorners(): extracts any of the four corners as a pta. | |
| 735 * </pre> | |
| 736 */ | |
| 737 PTA * | |
| 738 boxaConvertToPta(BOXA *boxa, | |
| 739 l_int32 ncorners) | |
| 740 { | |
| 741 l_int32 i, n; | |
| 742 BOX *box; | |
| 743 PTA *pta, *pta1; | |
| 744 | |
| 745 if (!boxa) | |
| 746 return (PTA *)ERROR_PTR("boxa not defined", __func__, NULL); | |
| 747 if (ncorners != 2 && ncorners != 4) | |
| 748 return (PTA *)ERROR_PTR("ncorners not 2 or 4", __func__, NULL); | |
| 749 | |
| 750 n = boxaGetCount(boxa); | |
| 751 if ((pta = ptaCreate(n)) == NULL) | |
| 752 return (PTA *)ERROR_PTR("pta not made", __func__, NULL); | |
| 753 for (i = 0; i < n; i++) { | |
| 754 box = boxaGetBox(boxa, i, L_COPY); | |
| 755 pta1 = boxConvertToPta(box, ncorners); | |
| 756 ptaJoin(pta, pta1, 0, -1); | |
| 757 boxDestroy(&box); | |
| 758 ptaDestroy(&pta1); | |
| 759 } | |
| 760 | |
| 761 return pta; | |
| 762 } | |
| 763 | |
| 764 | |
| 765 /*! | |
| 766 * \brief ptaConvertToBoxa() | |
| 767 * | |
| 768 * \param[in] pta | |
| 769 * \param[in] ncorners 2 or 4 for the representation of each box | |
| 770 * \return boxa with one box for each 2 or 4 points in the pta, | |
| 771 * or NULL on error | |
| 772 * | |
| 773 * <pre> | |
| 774 * Notes: | |
| 775 * (1) For 2 corners, the order of the 2 points is UL, LR. | |
| 776 * For 4 corners, the order of points is UL, UR, LL, LR. | |
| 777 * (2) Each derived box is the minimum size containing all corners. | |
| 778 * </pre> | |
| 779 */ | |
| 780 BOXA * | |
| 781 ptaConvertToBoxa(PTA *pta, | |
| 782 l_int32 ncorners) | |
| 783 { | |
| 784 l_int32 i, n, nbox, x1, y1, x2, y2, x3, y3, x4, y4, x, y, xmax, ymax; | |
| 785 BOX *box; | |
| 786 BOXA *boxa; | |
| 787 | |
| 788 if (!pta) | |
| 789 return (BOXA *)ERROR_PTR("pta not defined", __func__, NULL); | |
| 790 if (ncorners != 2 && ncorners != 4) | |
| 791 return (BOXA *)ERROR_PTR("ncorners not 2 or 4", __func__, NULL); | |
| 792 n = ptaGetCount(pta); | |
| 793 if (n % ncorners != 0) | |
| 794 return (BOXA *)ERROR_PTR("size % ncorners != 0", __func__, NULL); | |
| 795 nbox = n / ncorners; | |
| 796 if ((boxa = boxaCreate(nbox)) == NULL) | |
| 797 return (BOXA *)ERROR_PTR("boxa not made", __func__, NULL); | |
| 798 for (i = 0; i < n; i += ncorners) { | |
| 799 ptaGetIPt(pta, i, &x1, &y1); | |
| 800 ptaGetIPt(pta, i + 1, &x2, &y2); | |
| 801 if (ncorners == 2) { | |
| 802 box = boxCreate(x1, y1, x2 - x1 + 1, y2 - y1 + 1); | |
| 803 boxaAddBox(boxa, box, L_INSERT); | |
| 804 continue; | |
| 805 } | |
| 806 ptaGetIPt(pta, i + 2, &x3, &y3); | |
| 807 ptaGetIPt(pta, i + 3, &x4, &y4); | |
| 808 x = L_MIN(x1, x3); | |
| 809 y = L_MIN(y1, y2); | |
| 810 xmax = L_MAX(x2, x4); | |
| 811 ymax = L_MAX(y3, y4); | |
| 812 box = boxCreate(x, y, xmax - x + 1, ymax - y + 1); | |
| 813 boxaAddBox(boxa, box, L_INSERT); | |
| 814 } | |
| 815 | |
| 816 return boxa; | |
| 817 } | |
| 818 | |
| 819 | |
| 820 /*! | |
| 821 * \brief boxConvertToPta() | |
| 822 * | |
| 823 * \param[in] box | |
| 824 * \param[in] ncorners 2 or 4 for the representation of the box | |
| 825 * \return pta with %ncorners points, or NULL on error | |
| 826 * | |
| 827 * <pre> | |
| 828 * Notes: | |
| 829 * (1) If ncorners == 2, we select the UL and LR corners. | |
| 830 * Otherwise we save all 4 corners in this order: UL, UR, LL, LR. | |
| 831 * </pre> | |
| 832 */ | |
| 833 PTA * | |
| 834 boxConvertToPta(BOX *box, | |
| 835 l_int32 ncorners) | |
| 836 { | |
| 837 l_int32 x, y, w, h; | |
| 838 PTA *pta; | |
| 839 | |
| 840 if (!box) | |
| 841 return (PTA *)ERROR_PTR("box not defined", __func__, NULL); | |
| 842 if (ncorners != 2 && ncorners != 4) | |
| 843 return (PTA *)ERROR_PTR("ncorners not 2 or 4", __func__, NULL); | |
| 844 | |
| 845 if ((pta = ptaCreate(ncorners)) == NULL) | |
| 846 return (PTA *)ERROR_PTR("pta not made", __func__, NULL); | |
| 847 boxGetGeometry(box, &x, &y, &w, &h); | |
| 848 ptaAddPt(pta, x, y); | |
| 849 if (ncorners == 2) { | |
| 850 ptaAddPt(pta, x + w - 1, y + h - 1); | |
| 851 } else { | |
| 852 ptaAddPt(pta, x + w - 1, y); | |
| 853 ptaAddPt(pta, x, y + h - 1); | |
| 854 ptaAddPt(pta, x + w - 1, y + h - 1); | |
| 855 } | |
| 856 | |
| 857 return pta; | |
| 858 } | |
| 859 | |
| 860 | |
| 861 /*! | |
| 862 * \brief ptaConvertToBox() | |
| 863 * | |
| 864 * \param[in] pta | |
| 865 * \return box minimum containing all points in the pta, or NULL on error | |
| 866 * | |
| 867 * <pre> | |
| 868 * Notes: | |
| 869 * (1) For 2 corners, the order of the 2 points is UL, LR. | |
| 870 * For 4 corners, the order of points is UL, UR, LL, LR. | |
| 871 * </pre> | |
| 872 */ | |
| 873 BOX * | |
| 874 ptaConvertToBox(PTA *pta) | |
| 875 { | |
| 876 l_int32 n, x1, y1, x2, y2, x3, y3, x4, y4, x, y, xmax, ymax; | |
| 877 | |
| 878 if (!pta) | |
| 879 return (BOX *)ERROR_PTR("pta not defined", __func__, NULL); | |
| 880 n = ptaGetCount(pta); | |
| 881 if (n != 2 && n != 4) | |
| 882 return (BOX *)ERROR_PTR("n must be 2 or 4", __func__, NULL); | |
| 883 ptaGetIPt(pta, 0, &x1, &y1); | |
| 884 ptaGetIPt(pta, 1, &x2, &y2); | |
| 885 if (n == 2) | |
| 886 return boxCreate(x1, y1, x2 - x1 + 1, y2 - y1 + 1); | |
| 887 | |
| 888 /* 4 corners */ | |
| 889 ptaGetIPt(pta, 2, &x3, &y3); | |
| 890 ptaGetIPt(pta, 3, &x4, &y4); | |
| 891 x = L_MIN(x1, x3); | |
| 892 y = L_MIN(y1, y2); | |
| 893 xmax = L_MAX(x2, x4); | |
| 894 ymax = L_MAX(y3, y4); | |
| 895 return boxCreate(x, y, xmax - x + 1, ymax - y + 1); | |
| 896 } | |
| 897 | |
| 898 | |
| 899 /*---------------------------------------------------------------------* | |
| 900 * Miscellaneous Boxa functions * | |
| 901 *---------------------------------------------------------------------*/ | |
| 902 /*! | |
| 903 * \brief boxaGetExtent() | |
| 904 * | |
| 905 * \param[in] boxa | |
| 906 * \param[out] pw [optional] width | |
| 907 * \param[out] ph [optional] height | |
| 908 * \param[out] pbox [optional] minimum box containing all boxes in boxa | |
| 909 * \return 0 if OK, 1 on error | |
| 910 * | |
| 911 * <pre> | |
| 912 * Notes: | |
| 913 * (1) This computes the minimum rectangular bounding region | |
| 914 * that contains all valid boxes in a boxa. | |
| 915 * (2) The returned w and h are the minimum size image | |
| 916 * that would contain all boxes untranslated. | |
| 917 * (3) If there are no valid boxes, returned w and h are 0 and | |
| 918 * all parameters in the returned box are 0. This | |
| 919 * is not an error, because an empty boxa is valid and | |
| 920 * boxaGetExtent() is required for serialization. | |
| 921 * </pre> | |
| 922 */ | |
| 923 l_ok | |
| 924 boxaGetExtent(BOXA *boxa, | |
| 925 l_int32 *pw, | |
| 926 l_int32 *ph, | |
| 927 BOX **pbox) | |
| 928 { | |
| 929 l_int32 i, n, x, y, w, h, xmax, ymax, xmin, ymin, found; | |
| 930 | |
| 931 if (!pw && !ph && !pbox) | |
| 932 return ERROR_INT("no ptrs defined", __func__, 1); | |
| 933 if (pw) *pw = 0; | |
| 934 if (ph) *ph = 0; | |
| 935 if (pbox) *pbox = NULL; | |
| 936 if (!boxa) | |
| 937 return ERROR_INT("boxa not defined", __func__, 1); | |
| 938 | |
| 939 n = boxaGetCount(boxa); | |
| 940 xmax = ymax = 0; | |
| 941 xmin = ymin = 100000000; | |
| 942 found = FALSE; | |
| 943 for (i = 0; i < n; i++) { | |
| 944 boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); | |
| 945 if (w <= 0 || h <= 0) | |
| 946 continue; | |
| 947 found = TRUE; | |
| 948 xmin = L_MIN(xmin, x); | |
| 949 ymin = L_MIN(ymin, y); | |
| 950 xmax = L_MAX(xmax, x + w); | |
| 951 ymax = L_MAX(ymax, y + h); | |
| 952 } | |
| 953 if (found == FALSE) /* no valid boxes in boxa */ | |
| 954 xmin = ymin = 0; | |
| 955 if (pw) *pw = xmax; | |
| 956 if (ph) *ph = ymax; | |
| 957 if (pbox) | |
| 958 *pbox = boxCreate(xmin, ymin, xmax - xmin, ymax - ymin); | |
| 959 | |
| 960 return 0; | |
| 961 } | |
| 962 | |
| 963 | |
| 964 /*! | |
| 965 * \brief boxaGetCoverage() | |
| 966 * | |
| 967 * \param[in] boxa | |
| 968 * \param[in] wc, hc dimensions of overall clipping rectangle with UL | |
| 969 * corner at (0, 0 that is covered by the boxes. | |
| 970 * \param[in] exactflag 1 for guaranteeing an exact result; 0 for getting | |
| 971 * an exact result only if the boxes do not overlap | |
| 972 * \param[out] pfract sum of box area as fraction of w * h | |
| 973 * \return 0 if OK, 1 on error | |
| 974 * | |
| 975 * <pre> | |
| 976 * Notes: | |
| 977 * (1) The boxes in boxa are clipped to the input rectangle. | |
| 978 * (2) * When %exactflag == 1, we generate a 1 bpp pix of size | |
| 979 * wc x hc, paint all the boxes black, and count the fg pixels. | |
| 980 * This can take 1 msec on a large page with many boxes. | |
| 981 * * When %exactflag == 0, we clip each box to the wc x hc region | |
| 982 * and sum the resulting areas. This is faster. | |
| 983 * * The results are the same when none of the boxes overlap | |
| 984 * within the wc x hc region. | |
| 985 * </pre> | |
| 986 */ | |
| 987 l_ok | |
| 988 boxaGetCoverage(BOXA *boxa, | |
| 989 l_int32 wc, | |
| 990 l_int32 hc, | |
| 991 l_int32 exactflag, | |
| 992 l_float32 *pfract) | |
| 993 { | |
| 994 l_int32 i, n, x, y, w, h, sum; | |
| 995 BOX *box, *boxc; | |
| 996 PIX *pixt; | |
| 997 | |
| 998 if (!pfract) | |
| 999 return ERROR_INT("&fract not defined", __func__, 1); | |
| 1000 *pfract = 0.0; | |
| 1001 if (!boxa) | |
| 1002 return ERROR_INT("boxa not defined", __func__, 1); | |
| 1003 | |
| 1004 n = boxaGetCount(boxa); | |
| 1005 if (n == 0) | |
| 1006 return ERROR_INT("no boxes in boxa", __func__, 1); | |
| 1007 | |
| 1008 if (exactflag == 0) { /* quick and dirty */ | |
| 1009 sum = 0; | |
| 1010 for (i = 0; i < n; i++) { | |
| 1011 box = boxaGetBox(boxa, i, L_CLONE); | |
| 1012 if ((boxc = boxClipToRectangle(box, wc, hc)) != NULL) { | |
| 1013 boxGetGeometry(boxc, NULL, NULL, &w, &h); | |
| 1014 sum += w * h; | |
| 1015 boxDestroy(&boxc); | |
| 1016 } | |
| 1017 boxDestroy(&box); | |
| 1018 } | |
| 1019 } else { /* slower and exact */ | |
| 1020 pixt = pixCreate(wc, hc, 1); | |
| 1021 for (i = 0; i < n; i++) { | |
| 1022 box = boxaGetBox(boxa, i, L_CLONE); | |
| 1023 boxGetGeometry(box, &x, &y, &w, &h); | |
| 1024 pixRasterop(pixt, x, y, w, h, PIX_SET, NULL, 0, 0); | |
| 1025 boxDestroy(&box); | |
| 1026 } | |
| 1027 pixCountPixels(pixt, &sum, NULL); | |
| 1028 pixDestroy(&pixt); | |
| 1029 } | |
| 1030 | |
| 1031 *pfract = (l_float32)sum / (l_float32)(wc * hc); | |
| 1032 return 0; | |
| 1033 } | |
| 1034 | |
| 1035 | |
| 1036 /*! | |
| 1037 * \brief boxaaSizeRange() | |
| 1038 * | |
| 1039 * \param[in] baa | |
| 1040 * \param[out] pminw [optional] min width of all boxes | |
| 1041 * \param[out] pmaxw [optional] max width of all boxes | |
| 1042 * \param[out] pminh [optional] min height of all boxes | |
| 1043 * \param[out] pmaxh [optional] max height of all boxes | |
| 1044 * \return 0 if OK, 1 on error | |
| 1045 */ | |
| 1046 l_ok | |
| 1047 boxaaSizeRange(BOXAA *baa, | |
| 1048 l_int32 *pminw, | |
| 1049 l_int32 *pminh, | |
| 1050 l_int32 *pmaxw, | |
| 1051 l_int32 *pmaxh) | |
| 1052 { | |
| 1053 l_int32 minw, minh, maxw, maxh, minbw, minbh, maxbw, maxbh, i, n; | |
| 1054 BOXA *boxa; | |
| 1055 | |
| 1056 if (!pminw && !pmaxw && !pminh && !pmaxh) | |
| 1057 return ERROR_INT("no data can be returned", __func__, 1); | |
| 1058 if (pminw) *pminw = 0; | |
| 1059 if (pminh) *pminh = 0; | |
| 1060 if (pmaxw) *pmaxw = 0; | |
| 1061 if (pmaxh) *pmaxh = 0; | |
| 1062 if (!baa) | |
| 1063 return ERROR_INT("baa not defined", __func__, 1); | |
| 1064 | |
| 1065 minw = minh = 100000000; | |
| 1066 maxw = maxh = 0; | |
| 1067 n = boxaaGetCount(baa); | |
| 1068 for (i = 0; i < n; i++) { | |
| 1069 boxa = boxaaGetBoxa(baa, i, L_CLONE); | |
| 1070 boxaSizeRange(boxa, &minbw, &minbh, &maxbw, &maxbh); | |
| 1071 if (minbw < minw) | |
| 1072 minw = minbw; | |
| 1073 if (minbh < minh) | |
| 1074 minh = minbh; | |
| 1075 if (maxbw > maxw) | |
| 1076 maxw = maxbw; | |
| 1077 if (maxbh > maxh) | |
| 1078 maxh = maxbh; | |
| 1079 boxaDestroy(&boxa); | |
| 1080 } | |
| 1081 | |
| 1082 if (pminw) *pminw = minw; | |
| 1083 if (pminh) *pminh = minh; | |
| 1084 if (pmaxw) *pmaxw = maxw; | |
| 1085 if (pmaxh) *pmaxh = maxh; | |
| 1086 return 0; | |
| 1087 } | |
| 1088 | |
| 1089 | |
| 1090 /*! | |
| 1091 * \brief boxaSizeRange() | |
| 1092 * | |
| 1093 * \param[in] boxa | |
| 1094 * \param[out] pminw [optional] min width of all boxes | |
| 1095 * \param[out] pmaxw [optional] max width of all boxes | |
| 1096 * \param[out] pminh [optional] min height of all boxes | |
| 1097 * \param[out] pmaxh [optional] max height of all boxes | |
| 1098 * \return 0 if OK, 1 on error | |
| 1099 */ | |
| 1100 l_ok | |
| 1101 boxaSizeRange(BOXA *boxa, | |
| 1102 l_int32 *pminw, | |
| 1103 l_int32 *pminh, | |
| 1104 l_int32 *pmaxw, | |
| 1105 l_int32 *pmaxh) | |
| 1106 { | |
| 1107 l_int32 minw, minh, maxw, maxh, i, n, w, h; | |
| 1108 | |
| 1109 if (!pminw && !pmaxw && !pminh && !pmaxh) | |
| 1110 return ERROR_INT("no data can be returned", __func__, 1); | |
| 1111 if (pminw) *pminw = 0; | |
| 1112 if (pminh) *pminh = 0; | |
| 1113 if (pmaxw) *pmaxw = 0; | |
| 1114 if (pmaxh) *pmaxh = 0; | |
| 1115 if (!boxa) | |
| 1116 return ERROR_INT("boxa not defined", __func__, 1); | |
| 1117 | |
| 1118 minw = minh = 100000000; | |
| 1119 maxw = maxh = 0; | |
| 1120 n = boxaGetCount(boxa); | |
| 1121 for (i = 0; i < n; i++) { | |
| 1122 boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); | |
| 1123 if (w < minw) | |
| 1124 minw = w; | |
| 1125 if (h < minh) | |
| 1126 minh = h; | |
| 1127 if (w > maxw) | |
| 1128 maxw = w; | |
| 1129 if (h > maxh) | |
| 1130 maxh = h; | |
| 1131 } | |
| 1132 | |
| 1133 if (pminw) *pminw = minw; | |
| 1134 if (pminh) *pminh = minh; | |
| 1135 if (pmaxw) *pmaxw = maxw; | |
| 1136 if (pmaxh) *pmaxh = maxh; | |
| 1137 return 0; | |
| 1138 } | |
| 1139 | |
| 1140 | |
| 1141 /*! | |
| 1142 * \brief boxaLocationRange() | |
| 1143 * | |
| 1144 * \param[in] boxa | |
| 1145 * \param[out] pminx [optional] min (UL corner) x value of all boxes | |
| 1146 * \param[out] pminy [optional] min (UL corner) y value of all boxes | |
| 1147 * \param[out] pmaxx [optional] max (UL corner) x value of all boxes | |
| 1148 * \param[out] pmaxy [optional] max (UL corner) y value of all boxes | |
| 1149 * \return 0 if OK, 1 on error | |
| 1150 */ | |
| 1151 l_ok | |
| 1152 boxaLocationRange(BOXA *boxa, | |
| 1153 l_int32 *pminx, | |
| 1154 l_int32 *pminy, | |
| 1155 l_int32 *pmaxx, | |
| 1156 l_int32 *pmaxy) | |
| 1157 { | |
| 1158 l_int32 minx, miny, maxx, maxy, i, n, x, y; | |
| 1159 | |
| 1160 if (!pminx && !pminy && !pmaxx && !pmaxy) | |
| 1161 return ERROR_INT("no data can be returned", __func__, 1); | |
| 1162 if (pminx) *pminx = 0; | |
| 1163 if (pminy) *pminy = 0; | |
| 1164 if (pmaxx) *pmaxx = 0; | |
| 1165 if (pmaxy) *pmaxy = 0; | |
| 1166 if (!boxa) | |
| 1167 return ERROR_INT("boxa not defined", __func__, 1); | |
| 1168 | |
| 1169 minx = miny = 100000000; | |
| 1170 maxx = maxy = 0; | |
| 1171 n = boxaGetCount(boxa); | |
| 1172 for (i = 0; i < n; i++) { | |
| 1173 boxaGetBoxGeometry(boxa, i, &x, &y, NULL, NULL); | |
| 1174 if (x < minx) | |
| 1175 minx = x; | |
| 1176 if (y < miny) | |
| 1177 miny = y; | |
| 1178 if (x > maxx) | |
| 1179 maxx = x; | |
| 1180 if (y > maxy) | |
| 1181 maxy = y; | |
| 1182 } | |
| 1183 | |
| 1184 if (pminx) *pminx = minx; | |
| 1185 if (pminy) *pminy = miny; | |
| 1186 if (pmaxx) *pmaxx = maxx; | |
| 1187 if (pmaxy) *pmaxy = maxy; | |
| 1188 | |
| 1189 return 0; | |
| 1190 } | |
| 1191 | |
| 1192 | |
| 1193 /*! | |
| 1194 * \brief boxaGetSizes() | |
| 1195 * | |
| 1196 * \param[in] boxa | |
| 1197 * \param[out] pnaw [optional] widths of valid boxes | |
| 1198 * \param[out] pnah [optional] heights of valid boxes | |
| 1199 * \return 0 if OK, 1 on error | |
| 1200 */ | |
| 1201 l_ok | |
| 1202 boxaGetSizes(BOXA *boxa, | |
| 1203 NUMA **pnaw, | |
| 1204 NUMA **pnah) | |
| 1205 { | |
| 1206 l_int32 i, n, w, h; | |
| 1207 BOX *box; | |
| 1208 | |
| 1209 if (pnaw) *pnaw = NULL; | |
| 1210 if (pnah) *pnah = NULL; | |
| 1211 if (!pnaw && !pnah) | |
| 1212 return ERROR_INT("no output requested", __func__, 1); | |
| 1213 if (!boxa) | |
| 1214 return ERROR_INT("boxa not defined", __func__, 1); | |
| 1215 | |
| 1216 n = boxaGetValidCount(boxa); | |
| 1217 if (pnaw) *pnaw = numaCreate(n); | |
| 1218 if (pnah) *pnah = numaCreate(n); | |
| 1219 for (i = 0; i < n; i++) { | |
| 1220 box = boxaGetValidBox(boxa, i, L_COPY); | |
| 1221 if (box) { | |
| 1222 boxGetGeometry(box, NULL, NULL, &w, &h); | |
| 1223 if (pnaw) numaAddNumber(*pnaw, w); | |
| 1224 if (pnah) numaAddNumber(*pnah, h); | |
| 1225 boxDestroy(&box); | |
| 1226 } | |
| 1227 } | |
| 1228 | |
| 1229 return 0; | |
| 1230 } | |
| 1231 | |
| 1232 | |
| 1233 /*! | |
| 1234 * \brief boxaGetArea() | |
| 1235 * | |
| 1236 * \param[in] boxa | |
| 1237 * \param[out] parea total area of all boxes | |
| 1238 * \return 0 if OK, 1 on error | |
| 1239 * | |
| 1240 * <pre> | |
| 1241 * Notes: | |
| 1242 * (1) Measures the total area of the boxes, without regard to overlaps. | |
| 1243 * </pre> | |
| 1244 */ | |
| 1245 l_ok | |
| 1246 boxaGetArea(BOXA *boxa, | |
| 1247 l_int32 *parea) | |
| 1248 { | |
| 1249 l_int32 i, n, w, h; | |
| 1250 | |
| 1251 if (!parea) | |
| 1252 return ERROR_INT("&area not defined", __func__, 1); | |
| 1253 *parea = 0; | |
| 1254 if (!boxa) | |
| 1255 return ERROR_INT("boxa not defined", __func__, 1); | |
| 1256 | |
| 1257 n = boxaGetCount(boxa); | |
| 1258 for (i = 0; i < n; i++) { | |
| 1259 boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); | |
| 1260 *parea += w * h; | |
| 1261 } | |
| 1262 return 0; | |
| 1263 } | |
| 1264 | |
| 1265 | |
| 1266 /*! | |
| 1267 * \brief boxaDisplayTiled() | |
| 1268 * | |
| 1269 * \param[in] boxas | |
| 1270 * \param[in] pixa [optional] background for each box | |
| 1271 * \param[in] first index of first box | |
| 1272 * \param[in] last index of last box; use -1 to go to end | |
| 1273 * \param[in] maxwidth of output image | |
| 1274 * \param[in] linewidth width of box outlines, before scaling | |
| 1275 * \param[in] scalefactor applied to every box; use 1.0 for no scaling | |
| 1276 * \param[in] background 0 for white, 1 for black; this is the color | |
| 1277 * of the spacing between the images | |
| 1278 * \param[in] spacing between images, and on outside | |
| 1279 * \param[in] border width of black border added to each image; | |
| 1280 * use 0 for no border | |
| 1281 * \return pixd of tiled images of boxes, or NULL on error | |
| 1282 * | |
| 1283 * <pre> | |
| 1284 * Notes: | |
| 1285 * (1) Displays each box separately in a tiled 32 bpp image. | |
| 1286 * (2) If pixa is defined, it must have the same count as the boxa, | |
| 1287 * and it will be a background over with each box is rendered. | |
| 1288 * If pixa is not defined, the boxes will be rendered over | |
| 1289 * blank images of identical size. | |
| 1290 * (3) See pixaDisplayTiledInRows() for other parameters. | |
| 1291 * </pre> | |
| 1292 */ | |
| 1293 PIX * | |
| 1294 boxaDisplayTiled(BOXA *boxas, | |
| 1295 PIXA *pixa, | |
| 1296 l_int32 first, | |
| 1297 l_int32 last, | |
| 1298 l_int32 maxwidth, | |
| 1299 l_int32 linewidth, | |
| 1300 l_float32 scalefactor, | |
| 1301 l_int32 background, | |
| 1302 l_int32 spacing, | |
| 1303 l_int32 border) | |
| 1304 { | |
| 1305 char buf[32]; | |
| 1306 l_int32 i, n, npix, w, h, fontsize; | |
| 1307 L_BMF *bmf; | |
| 1308 BOX *box; | |
| 1309 BOXA *boxa; | |
| 1310 PIX *pix1, *pix2, *pixd; | |
| 1311 PIXA *pixat; | |
| 1312 | |
| 1313 if (!boxas) | |
| 1314 return (PIX *)ERROR_PTR("boxas not defined", __func__, NULL); | |
| 1315 | |
| 1316 boxa = boxaSaveValid(boxas, L_COPY); | |
| 1317 n = boxaGetCount(boxa); | |
| 1318 if (pixa) { | |
| 1319 npix = pixaGetCount(pixa); | |
| 1320 if (n != npix) { | |
| 1321 boxaDestroy(&boxa); | |
| 1322 return (PIX *)ERROR_PTR("boxa and pixa counts differ", | |
| 1323 __func__, NULL); | |
| 1324 } | |
| 1325 } | |
| 1326 first = L_MAX(0, first); | |
| 1327 if (last < 0) last = n - 1; | |
| 1328 if (first >= n) { | |
| 1329 boxaDestroy(&boxa); | |
| 1330 return (PIX *)ERROR_PTR("invalid first", __func__, NULL); | |
| 1331 } | |
| 1332 if (last >= n) { | |
| 1333 L_WARNING("last = %d is beyond max index = %d; adjusting\n", | |
| 1334 __func__, last, n - 1); | |
| 1335 last = n - 1; | |
| 1336 } | |
| 1337 if (first > last) { | |
| 1338 boxaDestroy(&boxa); | |
| 1339 return (PIX *)ERROR_PTR("first > last", __func__, NULL); | |
| 1340 } | |
| 1341 | |
| 1342 /* Because the bitmap font will be reduced when tiled, choose the | |
| 1343 * font size inversely with the scale factor. */ | |
| 1344 if (scalefactor > 0.8) | |
| 1345 fontsize = 6; | |
| 1346 else if (scalefactor > 0.6) | |
| 1347 fontsize = 10; | |
| 1348 else if (scalefactor > 0.4) | |
| 1349 fontsize = 14; | |
| 1350 else if (scalefactor > 0.3) | |
| 1351 fontsize = 18; | |
| 1352 else fontsize = 20; | |
| 1353 bmf = bmfCreate(NULL, fontsize); | |
| 1354 | |
| 1355 pixat = pixaCreate(n); | |
| 1356 boxaGetExtent(boxa, &w, &h, NULL); | |
| 1357 for (i = first; i <= last; i++) { | |
| 1358 box = boxaGetBox(boxa, i, L_CLONE); | |
| 1359 if (!pixa) { | |
| 1360 pix1 = pixCreate(w, h, 32); | |
| 1361 pixSetAll(pix1); | |
| 1362 } else { | |
| 1363 pix1 = pixaGetPix(pixa, i, L_COPY); | |
| 1364 } | |
| 1365 pixSetBorderVal(pix1, 0, 0, 0, 2, 0x0000ff00); | |
| 1366 snprintf(buf, sizeof(buf), "%d", i); | |
| 1367 pix2 = pixAddSingleTextblock(pix1, bmf, buf, 0x00ff0000, | |
| 1368 L_ADD_BELOW, NULL); | |
| 1369 pixDestroy(&pix1); | |
| 1370 pixRenderBoxArb(pix2, box, linewidth, 255, 0, 0); | |
| 1371 pixaAddPix(pixat, pix2, L_INSERT); | |
| 1372 boxDestroy(&box); | |
| 1373 } | |
| 1374 bmfDestroy(&bmf); | |
| 1375 boxaDestroy(&boxa); | |
| 1376 | |
| 1377 pixd = pixaDisplayTiledInRows(pixat, 32, maxwidth, scalefactor, background, | |
| 1378 spacing, border); | |
| 1379 pixaDestroy(&pixat); | |
| 1380 return pixd; | |
| 1381 } |
