Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/pixafunc1.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 pixafunc1.c | |
| 29 * <pre> | |
| 30 * | |
| 31 * Filters | |
| 32 * PIX *pixSelectBySize() | |
| 33 * PIXA *pixaSelectBySize() | |
| 34 * NUMA *pixaMakeSizeIndicator() | |
| 35 * | |
| 36 * PIX *pixSelectByPerimToAreaRatio() | |
| 37 * PIXA *pixaSelectByPerimToAreaRatio() | |
| 38 * PIX *pixSelectByPerimSizeRatio() | |
| 39 * PIXA *pixaSelectByPerimSizeRatio() | |
| 40 * PIX *pixSelectByAreaFraction() | |
| 41 * PIXA *pixaSelectByAreaFraction() | |
| 42 * PIX *pixSelectByArea() | |
| 43 * PIXA *pixaSelectByArea() | |
| 44 * PIX *pixSelectByWidthHeightRatio() | |
| 45 * PIXA *pixaSelectByWidthHeightRatio() | |
| 46 * PIXA *pixaSelectByNumConnComp() | |
| 47 * | |
| 48 * PIXA *pixaSelectWithIndicator() | |
| 49 * l_int32 pixRemoveWithIndicator() | |
| 50 * l_int32 pixAddWithIndicator() | |
| 51 * PIXA *pixaSelectWithString() | |
| 52 * PIX *pixaRenderComponent() | |
| 53 * | |
| 54 * Sort functions | |
| 55 * PIXA *pixaSort() | |
| 56 * PIXA *pixaBinSort() | |
| 57 * PIXA *pixaSortByIndex() | |
| 58 * PIXAA *pixaSort2dByIndex() | |
| 59 * | |
| 60 * Pixa and Pixaa range selection | |
| 61 * PIXA *pixaSelectRange() | |
| 62 * PIXAA *pixaaSelectRange() | |
| 63 * | |
| 64 * Pixa and Pixaa scaling | |
| 65 * PIXAA *pixaaScaleToSize() | |
| 66 * PIXAA *pixaaScaleToSizeVar() | |
| 67 * PIXA *pixaScaleToSize() | |
| 68 * PIXA *pixaScaleToSizeRel() | |
| 69 * PIXA *pixaScale() | |
| 70 * PIXA *pixaScaleBySampling() | |
| 71 * | |
| 72 * Pixa rotation and translation | |
| 73 * PIXA *pixaRotate() | |
| 74 * PIXA *pixaRotateOrth() | |
| 75 * PIXA *pixaTranslate() | |
| 76 * | |
| 77 * Miscellaneous | |
| 78 * PIXA *pixaAddBorderGeneral() | |
| 79 * PIXA *pixaaFlattenToPixa() | |
| 80 * l_int32 pixaaSizeRange() | |
| 81 * l_int32 pixaSizeRange() | |
| 82 * PIXA *pixaClipToPix() | |
| 83 * PIXA *pixaClipToForeground() | |
| 84 * l_int32 pixaGetRenderingDepth() | |
| 85 * l_int32 pixaHasColor() | |
| 86 * l_int32 pixaAnyColormaps() | |
| 87 * l_int32 pixaGetDepthInfo() | |
| 88 * PIXA *pixaConvertToSameDepth() | |
| 89 * PIXA *pixaConvertToGivenDepth() | |
| 90 * l_int32 pixaEqual() | |
| 91 * l_int32 pixaSetFullSizeBoxa() | |
| 92 * </pre> | |
| 93 */ | |
| 94 | |
| 95 #ifdef HAVE_CONFIG_H | |
| 96 #include <config_auto.h> | |
| 97 #endif /* HAVE_CONFIG_H */ | |
| 98 | |
| 99 #include <string.h> | |
| 100 #include "allheaders.h" | |
| 101 #include "pix_internal.h" | |
| 102 | |
| 103 /* For more than this number of c.c. in a binarized image of | |
| 104 * semi-perimeter (w + h) about 5000 or less, the O(n) binsort | |
| 105 * is faster than the O(nlogn) shellsort. */ | |
| 106 static const l_int32 MinCompsForBinSort = 200; | |
| 107 | |
| 108 /* Don't rotate any angle smaller than this */ | |
| 109 static const l_float32 MinAngleToRotate = 0.001f; /* radians; ~0.06 deg */ | |
| 110 | |
| 111 /*---------------------------------------------------------------------* | |
| 112 * Filters * | |
| 113 *---------------------------------------------------------------------*/ | |
| 114 /* | |
| 115 * These filters work on the connected components of 1 bpp images. | |
| 116 * They are typically used on pixa that have been generated from a Pix | |
| 117 * using pixConnComp(), so that the corresponding Boxa is available. | |
| 118 * | |
| 119 * The filters remove or retain c.c. based on these properties: | |
| 120 * (a) size [pixaFindDimensions()] | |
| 121 * (b) area-to-perimeter ratio [pixaFindAreaPerimRatio()] | |
| 122 * (c) foreground area as a fraction of bounding box area (w * h) | |
| 123 * [pixaFindForegroundArea()] | |
| 124 * (d) number of foreground pixels [pixaCountPixels()] | |
| 125 * (e) width/height aspect ratio [pixFindWidthHeightRatio()] | |
| 126 * | |
| 127 * We provide two different high-level interfaces: | |
| 128 * (1) Functions that use one of the filters on either | |
| 129 * a pix or the pixa of components. | |
| 130 * (2) A general method that generates numas of indicator functions, | |
| 131 * logically combines them, and efficiently removes or adds | |
| 132 * the selected components. | |
| 133 * | |
| 134 * For interface (1), the filtering is performed with a single function call. | |
| 135 * This is the easiest way to do simple filtering. These functions | |
| 136 * are named pixSelectBy*() and pixaSelectBy*(), where the '*' is one of: | |
| 137 * Size | |
| 138 * PerimToAreaRatio | |
| 139 * PerimSizeRatio | |
| 140 * Area | |
| 141 * AreaFraction | |
| 142 * WidthHeightRatio | |
| 143 * | |
| 144 * For more complicated filtering, use the general method (2). | |
| 145 * The numa indicator functions for a pixa are generated by these functions: | |
| 146 * pixaFindDimensions() | |
| 147 * pixaFindPerimToAreaRatio() | |
| 148 * pixaFindPerimSizeRatio() | |
| 149 * pixaFindAreaFraction() | |
| 150 * pixaCountPixels() | |
| 151 * pixaFindWidthHeightRatio() | |
| 152 * pixaFindWidthHeightProduct() | |
| 153 * | |
| 154 * Here is an illustration using the general method. Suppose you want | |
| 155 * all 8-connected components that have a height greater than 40 pixels, | |
| 156 * a width not more than 30 pixels, between 150 and 300 fg pixels, | |
| 157 * and a perimeter-to-size ratio between 1.2 and 2.0. | |
| 158 * | |
| 159 * // Generate the pixa of 8 cc pieces. | |
| 160 * boxa = pixConnComp(pixs, &pixa, 8); | |
| 161 * | |
| 162 * // Extract the data we need about each component. | |
| 163 * pixaFindDimensions(pixa, &naw, &nah); | |
| 164 * nas = pixaCountPixels(pixa); | |
| 165 * nar = pixaFindPerimSizeRatio(pixa); | |
| 166 * | |
| 167 * // Build the indicator arrays for the set of components, | |
| 168 * // based on thresholds and selection criteria. | |
| 169 * na1 = numaMakeThresholdIndicator(nah, 40, L_SELECT_IF_GT); | |
| 170 * na2 = numaMakeThresholdIndicator(naw, 30, L_SELECT_IF_LTE); | |
| 171 * na3 = numaMakeThresholdIndicator(nas, 150, L_SELECT_IF_GTE); | |
| 172 * na4 = numaMakeThresholdIndicator(nas, 300, L_SELECT_IF_LTE); | |
| 173 * na5 = numaMakeThresholdIndicator(nar, 1.2, L_SELECT_IF_GTE); | |
| 174 * na6 = numaMakeThresholdIndicator(nar, 2.0, L_SELECT_IF_LTE); | |
| 175 * | |
| 176 * // Combine the indicator arrays logically to find | |
| 177 * // the components that will be retained. | |
| 178 * nad = numaLogicalOp(NULL, na1, na2, L_INTERSECTION); | |
| 179 * numaLogicalOp(nad, nad, na3, L_INTERSECTION); | |
| 180 * numaLogicalOp(nad, nad, na4, L_INTERSECTION); | |
| 181 * numaLogicalOp(nad, nad, na5, L_INTERSECTION); | |
| 182 * numaLogicalOp(nad, nad, na6, L_INTERSECTION); | |
| 183 * | |
| 184 * // Invert to get the components that will be removed. | |
| 185 * numaInvert(nad, nad); | |
| 186 * | |
| 187 * // Remove the components, in-place. | |
| 188 * pixRemoveWithIndicator(pixs, pixa, nad); | |
| 189 */ | |
| 190 | |
| 191 | |
| 192 /*! | |
| 193 * \brief pixSelectBySize() | |
| 194 * | |
| 195 * \param[in] pixs 1 bpp | |
| 196 * \param[in] width, height threshold dimensions | |
| 197 * \param[in] connectivity 4 or 8 | |
| 198 * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT, | |
| 199 * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH | |
| 200 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 201 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 202 * \param[out] pchanged [optional] 1 if changed; 0 otherwise | |
| 203 * \return filtered pixd, or NULL on error | |
| 204 * | |
| 205 * <pre> | |
| 206 * Notes: | |
| 207 * (1) The args specify constraints on the size of the | |
| 208 * components that are kept. | |
| 209 * (2) If unchanged, returns a copy of pixs. Otherwise, | |
| 210 * returns a new pix with the filtered components. | |
| 211 * (3) If the selection type is L_SELECT_WIDTH, the input | |
| 212 * height is ignored, and v.v. | |
| 213 * (4) To keep small components, use relation = L_SELECT_IF_LT or | |
| 214 * L_SELECT_IF_LTE. | |
| 215 * To keep large components, use relation = L_SELECT_IF_GT or | |
| 216 * L_SELECT_IF_GTE. | |
| 217 * </pre> | |
| 218 */ | |
| 219 PIX * | |
| 220 pixSelectBySize(PIX *pixs, | |
| 221 l_int32 width, | |
| 222 l_int32 height, | |
| 223 l_int32 connectivity, | |
| 224 l_int32 type, | |
| 225 l_int32 relation, | |
| 226 l_int32 *pchanged) | |
| 227 { | |
| 228 l_int32 w, h, empty, changed, count; | |
| 229 BOXA *boxa; | |
| 230 PIX *pixd; | |
| 231 PIXA *pixas, *pixad; | |
| 232 | |
| 233 if (!pixs) | |
| 234 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 235 if (connectivity != 4 && connectivity != 8) | |
| 236 return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL); | |
| 237 if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && | |
| 238 type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) | |
| 239 return (PIX *)ERROR_PTR("invalid type", __func__, NULL); | |
| 240 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && | |
| 241 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) | |
| 242 return (PIX *)ERROR_PTR("invalid relation", __func__, NULL); | |
| 243 if (pchanged) *pchanged = FALSE; | |
| 244 | |
| 245 /* Check if any components exist */ | |
| 246 pixZero(pixs, &empty); | |
| 247 if (empty) | |
| 248 return pixCopy(NULL, pixs); | |
| 249 | |
| 250 /* Identify and select the components */ | |
| 251 boxa = pixConnComp(pixs, &pixas, connectivity); | |
| 252 pixad = pixaSelectBySize(pixas, width, height, type, relation, &changed); | |
| 253 boxaDestroy(&boxa); | |
| 254 pixaDestroy(&pixas); | |
| 255 | |
| 256 if (!changed) { | |
| 257 pixaDestroy(&pixad); | |
| 258 return pixCopy(NULL, pixs); | |
| 259 } | |
| 260 | |
| 261 /* Render the result */ | |
| 262 if (pchanged) *pchanged = TRUE; | |
| 263 pixGetDimensions(pixs, &w, &h, NULL); | |
| 264 count = pixaGetCount(pixad); | |
| 265 if (count == 0) { /* return empty pix */ | |
| 266 pixd = pixCreateTemplate(pixs); | |
| 267 } else { | |
| 268 pixd = pixaDisplay(pixad, w, h); | |
| 269 pixCopyResolution(pixd, pixs); | |
| 270 pixCopyColormap(pixd, pixs); | |
| 271 pixCopyText(pixd, pixs); | |
| 272 pixCopyInputFormat(pixd, pixs); | |
| 273 } | |
| 274 pixaDestroy(&pixad); | |
| 275 return pixd; | |
| 276 } | |
| 277 | |
| 278 | |
| 279 /*! | |
| 280 * \brief pixaSelectBySize() | |
| 281 * | |
| 282 * \param[in] pixas | |
| 283 * \param[in] width, height threshold dimensions | |
| 284 * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT, | |
| 285 * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH | |
| 286 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 287 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 288 * \param[out] pchanged [optional] 1 if changed; 0 otherwise | |
| 289 * \return pixad, or NULL on error | |
| 290 * | |
| 291 * <pre> | |
| 292 * Notes: | |
| 293 * (1) The args specify constraints on the size of the | |
| 294 * components that are kept. | |
| 295 * (2) Uses pix and box clones in the new pixa. | |
| 296 * (3) If the selection type is L_SELECT_WIDTH, the input | |
| 297 * height is ignored, and v.v. | |
| 298 * (4) To keep small components, use relation = L_SELECT_IF_LT or | |
| 299 * L_SELECT_IF_LTE. | |
| 300 * To keep large components, use relation = L_SELECT_IF_GT or | |
| 301 * L_SELECT_IF_GTE. | |
| 302 * </pre> | |
| 303 */ | |
| 304 PIXA * | |
| 305 pixaSelectBySize(PIXA *pixas, | |
| 306 l_int32 width, | |
| 307 l_int32 height, | |
| 308 l_int32 type, | |
| 309 l_int32 relation, | |
| 310 l_int32 *pchanged) | |
| 311 { | |
| 312 NUMA *na; | |
| 313 PIXA *pixad; | |
| 314 | |
| 315 if (!pixas) | |
| 316 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 317 if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && | |
| 318 type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) | |
| 319 return (PIXA *)ERROR_PTR("invalid type", __func__, NULL); | |
| 320 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && | |
| 321 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) | |
| 322 return (PIXA *)ERROR_PTR("invalid relation", __func__, NULL); | |
| 323 | |
| 324 /* Compute the indicator array for saving components */ | |
| 325 na = pixaMakeSizeIndicator(pixas, width, height, type, relation); | |
| 326 | |
| 327 /* Filter to get output */ | |
| 328 pixad = pixaSelectWithIndicator(pixas, na, pchanged); | |
| 329 | |
| 330 numaDestroy(&na); | |
| 331 return pixad; | |
| 332 } | |
| 333 | |
| 334 | |
| 335 /*! | |
| 336 * \brief pixaMakeSizeIndicator() | |
| 337 * | |
| 338 * \param[in] pixa | |
| 339 * \param[in] width, height threshold dimensions | |
| 340 * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT, | |
| 341 * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH | |
| 342 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 343 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 344 * \return na indicator array, or NULL on error | |
| 345 * | |
| 346 * <pre> | |
| 347 * Notes: | |
| 348 * (1) The args specify constraints on the size of the | |
| 349 * components that are kept. | |
| 350 * (2) If the selection type is L_SELECT_WIDTH, the input | |
| 351 * height is ignored, and v.v. | |
| 352 * (3) To keep small components, use relation = L_SELECT_IF_LT or | |
| 353 * L_SELECT_IF_LTE. | |
| 354 * To keep large components, use relation = L_SELECT_IF_GT or | |
| 355 * L_SELECT_IF_GTE. | |
| 356 * </pre> | |
| 357 */ | |
| 358 NUMA * | |
| 359 pixaMakeSizeIndicator(PIXA *pixa, | |
| 360 l_int32 width, | |
| 361 l_int32 height, | |
| 362 l_int32 type, | |
| 363 l_int32 relation) | |
| 364 { | |
| 365 l_int32 i, n, w, h, ival; | |
| 366 NUMA *na; | |
| 367 | |
| 368 if (!pixa) | |
| 369 return (NUMA *)ERROR_PTR("pixa not defined", __func__, NULL); | |
| 370 if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && | |
| 371 type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) | |
| 372 return (NUMA *)ERROR_PTR("invalid type", __func__, NULL); | |
| 373 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && | |
| 374 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) | |
| 375 return (NUMA *)ERROR_PTR("invalid relation", __func__, NULL); | |
| 376 | |
| 377 n = pixaGetCount(pixa); | |
| 378 na = numaCreate(n); | |
| 379 for (i = 0; i < n; i++) { | |
| 380 ival = 0; | |
| 381 pixaGetPixDimensions(pixa, i, &w, &h, NULL); | |
| 382 switch (type) | |
| 383 { | |
| 384 case L_SELECT_WIDTH: | |
| 385 if ((relation == L_SELECT_IF_LT && w < width) || | |
| 386 (relation == L_SELECT_IF_GT && w > width) || | |
| 387 (relation == L_SELECT_IF_LTE && w <= width) || | |
| 388 (relation == L_SELECT_IF_GTE && w >= width)) | |
| 389 ival = 1; | |
| 390 break; | |
| 391 case L_SELECT_HEIGHT: | |
| 392 if ((relation == L_SELECT_IF_LT && h < height) || | |
| 393 (relation == L_SELECT_IF_GT && h > height) || | |
| 394 (relation == L_SELECT_IF_LTE && h <= height) || | |
| 395 (relation == L_SELECT_IF_GTE && h >= height)) | |
| 396 ival = 1; | |
| 397 break; | |
| 398 case L_SELECT_IF_EITHER: | |
| 399 if (((relation == L_SELECT_IF_LT) && (w < width || h < height)) || | |
| 400 ((relation == L_SELECT_IF_GT) && (w > width || h > height)) || | |
| 401 ((relation == L_SELECT_IF_LTE) && (w <= width || h <= height)) || | |
| 402 ((relation == L_SELECT_IF_GTE) && (w >= width || h >= height))) | |
| 403 ival = 1; | |
| 404 break; | |
| 405 case L_SELECT_IF_BOTH: | |
| 406 if (((relation == L_SELECT_IF_LT) && (w < width && h < height)) || | |
| 407 ((relation == L_SELECT_IF_GT) && (w > width && h > height)) || | |
| 408 ((relation == L_SELECT_IF_LTE) && (w <= width && h <= height)) || | |
| 409 ((relation == L_SELECT_IF_GTE) && (w >= width && h >= height))) | |
| 410 ival = 1; | |
| 411 break; | |
| 412 default: | |
| 413 L_WARNING("can't get here!\n", __func__); | |
| 414 break; | |
| 415 } | |
| 416 numaAddNumber(na, ival); | |
| 417 } | |
| 418 | |
| 419 return na; | |
| 420 } | |
| 421 | |
| 422 | |
| 423 /*! | |
| 424 * \brief pixSelectByPerimToAreaRatio() | |
| 425 * | |
| 426 * \param[in] pixs 1 bpp | |
| 427 * \param[in] thresh threshold ratio of fg boundary to fg pixels | |
| 428 * \param[in] connectivity 4 or 8 | |
| 429 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 430 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 431 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned | |
| 432 * \return pixd, or NULL on error | |
| 433 * | |
| 434 * <pre> | |
| 435 * Notes: | |
| 436 * (1) The args specify constraints on the size of the | |
| 437 * components that are kept. | |
| 438 * (2) If unchanged, returns a copy of pixs. Otherwise, | |
| 439 * returns a new pix with the filtered components. | |
| 440 * (3) This filters "thick" components, where a thick component | |
| 441 * is defined to have a ratio of boundary to interior pixels | |
| 442 * that is smaller than a given threshold value. | |
| 443 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save the thicker | |
| 444 * components, and L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. | |
| 445 * </pre> | |
| 446 */ | |
| 447 PIX * | |
| 448 pixSelectByPerimToAreaRatio(PIX *pixs, | |
| 449 l_float32 thresh, | |
| 450 l_int32 connectivity, | |
| 451 l_int32 type, | |
| 452 l_int32 *pchanged) | |
| 453 { | |
| 454 l_int32 w, h, empty, changed, count; | |
| 455 BOXA *boxa; | |
| 456 PIX *pixd; | |
| 457 PIXA *pixas, *pixad; | |
| 458 | |
| 459 if (!pixs) | |
| 460 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 461 if (connectivity != 4 && connectivity != 8) | |
| 462 return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL); | |
| 463 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && | |
| 464 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) | |
| 465 return (PIX *)ERROR_PTR("invalid type", __func__, NULL); | |
| 466 if (pchanged) *pchanged = FALSE; | |
| 467 | |
| 468 /* Check if any components exist */ | |
| 469 pixZero(pixs, &empty); | |
| 470 if (empty) | |
| 471 return pixCopy(NULL, pixs); | |
| 472 | |
| 473 /* Filter thin components */ | |
| 474 boxa = pixConnComp(pixs, &pixas, connectivity); | |
| 475 pixad = pixaSelectByPerimToAreaRatio(pixas, thresh, type, &changed); | |
| 476 boxaDestroy(&boxa); | |
| 477 pixaDestroy(&pixas); | |
| 478 | |
| 479 if (!changed) { | |
| 480 pixaDestroy(&pixad); | |
| 481 return pixCopy(NULL, pixs); | |
| 482 } | |
| 483 | |
| 484 /* Render the result */ | |
| 485 if (pchanged) *pchanged = TRUE; | |
| 486 pixGetDimensions(pixs, &w, &h, NULL); | |
| 487 count = pixaGetCount(pixad); | |
| 488 if (count == 0) { /* return empty pix */ | |
| 489 pixd = pixCreateTemplate(pixs); | |
| 490 } else { | |
| 491 pixd = pixaDisplay(pixad, w, h); | |
| 492 pixCopyResolution(pixd, pixs); | |
| 493 pixCopyColormap(pixd, pixs); | |
| 494 pixCopyText(pixd, pixs); | |
| 495 pixCopyInputFormat(pixd, pixs); | |
| 496 } | |
| 497 pixaDestroy(&pixad); | |
| 498 return pixd; | |
| 499 } | |
| 500 | |
| 501 | |
| 502 /*! | |
| 503 * \brief pixaSelectByPerimToAreaRatio() | |
| 504 * | |
| 505 * \param[in] pixas | |
| 506 * \param[in] thresh threshold ratio of fg boundary to fg pixels | |
| 507 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 508 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 509 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned | |
| 510 * \return pixad, or NULL on error | |
| 511 * | |
| 512 * <pre> | |
| 513 * Notes: | |
| 514 * (1) Returns a pixa clone if no components are removed. | |
| 515 * (2) Uses pix and box clones in the new pixa. | |
| 516 * (3) See pixSelectByPerimToAreaRatio(). | |
| 517 * </pre> | |
| 518 */ | |
| 519 PIXA * | |
| 520 pixaSelectByPerimToAreaRatio(PIXA *pixas, | |
| 521 l_float32 thresh, | |
| 522 l_int32 type, | |
| 523 l_int32 *pchanged) | |
| 524 { | |
| 525 NUMA *na, *nai; | |
| 526 PIXA *pixad; | |
| 527 | |
| 528 if (!pixas) | |
| 529 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 530 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && | |
| 531 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) | |
| 532 return (PIXA *)ERROR_PTR("invalid type", __func__, NULL); | |
| 533 | |
| 534 /* Compute component ratios. */ | |
| 535 na = pixaFindPerimToAreaRatio(pixas); | |
| 536 | |
| 537 /* Generate indicator array for elements to be saved. */ | |
| 538 nai = numaMakeThresholdIndicator(na, thresh, type); | |
| 539 numaDestroy(&na); | |
| 540 | |
| 541 /* Filter to get output */ | |
| 542 pixad = pixaSelectWithIndicator(pixas, nai, pchanged); | |
| 543 | |
| 544 numaDestroy(&nai); | |
| 545 return pixad; | |
| 546 } | |
| 547 | |
| 548 | |
| 549 /*! | |
| 550 * \brief pixSelectByPerimSizeRatio() | |
| 551 * | |
| 552 * \param[in] pixs 1 bpp | |
| 553 * \param[in] thresh threshold ratio of fg boundary to fg pixels | |
| 554 * \param[in] connectivity 4 or 8 | |
| 555 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 556 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 557 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned | |
| 558 * \return pixd, or NULL on error | |
| 559 * | |
| 560 * <pre> | |
| 561 * Notes: | |
| 562 * (1) The args specify constraints on the size of the | |
| 563 * components that are kept. | |
| 564 * (2) If unchanged, returns a copy of pixs. Otherwise, | |
| 565 * returns a new pix with the filtered components. | |
| 566 * (3) This filters components with smooth vs. dendritic shape, using | |
| 567 * the ratio of the fg boundary pixels to the circumference of | |
| 568 * the bounding box, and comparing it to a threshold value. | |
| 569 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save the smooth | |
| 570 * boundary components, and L_SELECT_IF_GT or L_SELECT_IF_GTE | |
| 571 * to remove them. | |
| 572 * </pre> | |
| 573 */ | |
| 574 PIX * | |
| 575 pixSelectByPerimSizeRatio(PIX *pixs, | |
| 576 l_float32 thresh, | |
| 577 l_int32 connectivity, | |
| 578 l_int32 type, | |
| 579 l_int32 *pchanged) | |
| 580 { | |
| 581 l_int32 w, h, empty, changed, count; | |
| 582 BOXA *boxa; | |
| 583 PIX *pixd; | |
| 584 PIXA *pixas, *pixad; | |
| 585 | |
| 586 if (!pixs) | |
| 587 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 588 if (connectivity != 4 && connectivity != 8) | |
| 589 return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL); | |
| 590 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && | |
| 591 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) | |
| 592 return (PIX *)ERROR_PTR("invalid type", __func__, NULL); | |
| 593 if (pchanged) *pchanged = FALSE; | |
| 594 | |
| 595 /* Check if any components exist */ | |
| 596 pixZero(pixs, &empty); | |
| 597 if (empty) | |
| 598 return pixCopy(NULL, pixs); | |
| 599 | |
| 600 /* Filter thin components */ | |
| 601 boxa = pixConnComp(pixs, &pixas, connectivity); | |
| 602 pixad = pixaSelectByPerimSizeRatio(pixas, thresh, type, &changed); | |
| 603 boxaDestroy(&boxa); | |
| 604 pixaDestroy(&pixas); | |
| 605 | |
| 606 if (!changed) { | |
| 607 pixaDestroy(&pixad); | |
| 608 return pixCopy(NULL, pixs); | |
| 609 } | |
| 610 | |
| 611 /* Render the result */ | |
| 612 if (pchanged) *pchanged = TRUE; | |
| 613 pixGetDimensions(pixs, &w, &h, NULL); | |
| 614 count = pixaGetCount(pixad); | |
| 615 if (count == 0) { /* return empty pix */ | |
| 616 pixd = pixCreateTemplate(pixs); | |
| 617 } else { | |
| 618 pixd = pixaDisplay(pixad, w, h); | |
| 619 pixCopyResolution(pixd, pixs); | |
| 620 pixCopyColormap(pixd, pixs); | |
| 621 pixCopyText(pixd, pixs); | |
| 622 pixCopyInputFormat(pixd, pixs); | |
| 623 } | |
| 624 pixaDestroy(&pixad); | |
| 625 return pixd; | |
| 626 } | |
| 627 | |
| 628 | |
| 629 /*! | |
| 630 * \brief pixaSelectByPerimSizeRatio() | |
| 631 * | |
| 632 * \param[in] pixas | |
| 633 * \param[in] thresh threshold ratio of fg boundary to b.b. circumference | |
| 634 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 635 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 636 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned | |
| 637 * \return pixad, or NULL on error | |
| 638 * | |
| 639 * <pre> | |
| 640 * Notes: | |
| 641 * (1) Returns a pixa clone if no components are removed. | |
| 642 * (2) Uses pix and box clones in the new pixa. | |
| 643 * (3) See pixSelectByPerimSizeRatio(). | |
| 644 * </pre> | |
| 645 */ | |
| 646 PIXA * | |
| 647 pixaSelectByPerimSizeRatio(PIXA *pixas, | |
| 648 l_float32 thresh, | |
| 649 l_int32 type, | |
| 650 l_int32 *pchanged) | |
| 651 { | |
| 652 NUMA *na, *nai; | |
| 653 PIXA *pixad; | |
| 654 | |
| 655 if (!pixas) | |
| 656 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 657 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && | |
| 658 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) | |
| 659 return (PIXA *)ERROR_PTR("invalid type", __func__, NULL); | |
| 660 | |
| 661 /* Compute component ratios. */ | |
| 662 na = pixaFindPerimSizeRatio(pixas); | |
| 663 | |
| 664 /* Generate indicator array for elements to be saved. */ | |
| 665 nai = numaMakeThresholdIndicator(na, thresh, type); | |
| 666 numaDestroy(&na); | |
| 667 | |
| 668 /* Filter to get output */ | |
| 669 pixad = pixaSelectWithIndicator(pixas, nai, pchanged); | |
| 670 | |
| 671 numaDestroy(&nai); | |
| 672 return pixad; | |
| 673 } | |
| 674 | |
| 675 | |
| 676 /*! | |
| 677 * \brief pixSelectByAreaFraction() | |
| 678 * | |
| 679 * \param[in] pixs 1 bpp | |
| 680 * \param[in] thresh threshold ratio of fg pixels to (w * h) | |
| 681 * \param[in] connectivity 4 or 8 | |
| 682 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 683 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 684 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned | |
| 685 * \return pixd, or NULL on error | |
| 686 * | |
| 687 * <pre> | |
| 688 * Notes: | |
| 689 * (1) The args specify constraints on the amount of foreground | |
| 690 * coverage of the components that are kept. | |
| 691 * (2) If unchanged, returns a copy of pixs. Otherwise, | |
| 692 * returns a new pix with the filtered components. | |
| 693 * (3) This filters components based on the fraction of fg pixels | |
| 694 * of the component in its bounding box. | |
| 695 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components | |
| 696 * with less than the threshold fraction of foreground, and | |
| 697 * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. | |
| 698 * </pre> | |
| 699 */ | |
| 700 PIX * | |
| 701 pixSelectByAreaFraction(PIX *pixs, | |
| 702 l_float32 thresh, | |
| 703 l_int32 connectivity, | |
| 704 l_int32 type, | |
| 705 l_int32 *pchanged) | |
| 706 { | |
| 707 l_int32 w, h, empty, changed, count; | |
| 708 BOXA *boxa; | |
| 709 PIX *pixd; | |
| 710 PIXA *pixas, *pixad; | |
| 711 | |
| 712 if (!pixs) | |
| 713 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 714 if (connectivity != 4 && connectivity != 8) | |
| 715 return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL); | |
| 716 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && | |
| 717 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) | |
| 718 return (PIX *)ERROR_PTR("invalid type", __func__, NULL); | |
| 719 if (pchanged) *pchanged = FALSE; | |
| 720 | |
| 721 /* Check if any components exist */ | |
| 722 pixZero(pixs, &empty); | |
| 723 if (empty) | |
| 724 return pixCopy(NULL, pixs); | |
| 725 | |
| 726 /* Filter components */ | |
| 727 boxa = pixConnComp(pixs, &pixas, connectivity); | |
| 728 pixad = pixaSelectByAreaFraction(pixas, thresh, type, &changed); | |
| 729 boxaDestroy(&boxa); | |
| 730 pixaDestroy(&pixas); | |
| 731 | |
| 732 if (!changed) { | |
| 733 pixaDestroy(&pixad); | |
| 734 return pixCopy(NULL, pixs); | |
| 735 } | |
| 736 | |
| 737 /* Render the result */ | |
| 738 if (pchanged) *pchanged = TRUE; | |
| 739 pixGetDimensions(pixs, &w, &h, NULL); | |
| 740 count = pixaGetCount(pixad); | |
| 741 if (count == 0) { /* return empty pix */ | |
| 742 pixd = pixCreateTemplate(pixs); | |
| 743 } else { | |
| 744 pixd = pixaDisplay(pixad, w, h); | |
| 745 pixCopyResolution(pixd, pixs); | |
| 746 pixCopyColormap(pixd, pixs); | |
| 747 pixCopyText(pixd, pixs); | |
| 748 pixCopyInputFormat(pixd, pixs); | |
| 749 } | |
| 750 pixaDestroy(&pixad); | |
| 751 return pixd; | |
| 752 } | |
| 753 | |
| 754 | |
| 755 /*! | |
| 756 * \brief pixaSelectByAreaFraction() | |
| 757 * | |
| 758 * \param[in] pixas | |
| 759 * \param[in] thresh threshold ratio of fg pixels to (w * h) | |
| 760 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 761 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 762 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned | |
| 763 * \return pixad, or NULL on error | |
| 764 * | |
| 765 * <pre> | |
| 766 * Notes: | |
| 767 * (1) Returns a pixa clone if no components are removed. | |
| 768 * (2) Uses pix and box clones in the new pixa. | |
| 769 * (3) This filters components based on the fraction of fg pixels | |
| 770 * of the component in its bounding box. | |
| 771 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components | |
| 772 * with less than the threshold fraction of foreground, and | |
| 773 * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. | |
| 774 * </pre> | |
| 775 */ | |
| 776 PIXA * | |
| 777 pixaSelectByAreaFraction(PIXA *pixas, | |
| 778 l_float32 thresh, | |
| 779 l_int32 type, | |
| 780 l_int32 *pchanged) | |
| 781 { | |
| 782 NUMA *na, *nai; | |
| 783 PIXA *pixad; | |
| 784 | |
| 785 if (!pixas) | |
| 786 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 787 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && | |
| 788 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) | |
| 789 return (PIXA *)ERROR_PTR("invalid type", __func__, NULL); | |
| 790 | |
| 791 /* Compute component ratios. */ | |
| 792 na = pixaFindAreaFraction(pixas); | |
| 793 | |
| 794 /* Generate indicator array for elements to be saved. */ | |
| 795 nai = numaMakeThresholdIndicator(na, thresh, type); | |
| 796 numaDestroy(&na); | |
| 797 | |
| 798 /* Filter to get output */ | |
| 799 pixad = pixaSelectWithIndicator(pixas, nai, pchanged); | |
| 800 | |
| 801 numaDestroy(&nai); | |
| 802 return pixad; | |
| 803 } | |
| 804 | |
| 805 | |
| 806 /*! | |
| 807 * \brief pixSelectByArea() | |
| 808 * | |
| 809 * \param[in] pixs 1 bpp | |
| 810 * \param[in] thresh threshold number of FG pixels | |
| 811 * \param[in] connectivity 4 or 8 | |
| 812 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 813 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 814 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned | |
| 815 * \return pixd, or NULL on error | |
| 816 * | |
| 817 * <pre> | |
| 818 * Notes: | |
| 819 * (1) The args specify constraints on the number of foreground | |
| 820 * pixels in the components that are kept. | |
| 821 * (2) If unchanged, returns a copy of pixs. Otherwise, | |
| 822 * returns a new pix with the filtered components. | |
| 823 * (3) This filters components based on the number of fg pixels | |
| 824 * in each component. | |
| 825 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components | |
| 826 * with less than the threshold number of fg pixels, and | |
| 827 * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. | |
| 828 * </pre> | |
| 829 */ | |
| 830 PIX * | |
| 831 pixSelectByArea(PIX *pixs, | |
| 832 l_float32 thresh, | |
| 833 l_int32 connectivity, | |
| 834 l_int32 type, | |
| 835 l_int32 *pchanged) | |
| 836 { | |
| 837 l_int32 w, h, empty, changed, count; | |
| 838 BOXA *boxa; | |
| 839 PIX *pixd; | |
| 840 PIXA *pixas, *pixad; | |
| 841 | |
| 842 if (!pixs) | |
| 843 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 844 if (connectivity != 4 && connectivity != 8) | |
| 845 return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL); | |
| 846 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && | |
| 847 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) | |
| 848 return (PIX *)ERROR_PTR("invalid type", __func__, NULL); | |
| 849 if (pchanged) *pchanged = FALSE; | |
| 850 | |
| 851 /* Check if any components exist */ | |
| 852 pixZero(pixs, &empty); | |
| 853 if (empty) | |
| 854 return pixCopy(NULL, pixs); | |
| 855 | |
| 856 /* Filter components */ | |
| 857 boxa = pixConnComp(pixs, &pixas, connectivity); | |
| 858 pixad = pixaSelectByArea(pixas, thresh, type, &changed); | |
| 859 boxaDestroy(&boxa); | |
| 860 pixaDestroy(&pixas); | |
| 861 | |
| 862 if (!changed) { | |
| 863 pixaDestroy(&pixad); | |
| 864 return pixCopy(NULL, pixs); | |
| 865 } | |
| 866 | |
| 867 /* Render the result */ | |
| 868 if (pchanged) *pchanged = TRUE; | |
| 869 pixGetDimensions(pixs, &w, &h, NULL); | |
| 870 count = pixaGetCount(pixad); | |
| 871 if (count == 0) { /* return empty pix */ | |
| 872 pixd = pixCreateTemplate(pixs); | |
| 873 } else { | |
| 874 pixd = pixaDisplay(pixad, w, h); | |
| 875 pixCopyResolution(pixd, pixs); | |
| 876 pixCopyColormap(pixd, pixs); | |
| 877 pixCopyText(pixd, pixs); | |
| 878 pixCopyInputFormat(pixd, pixs); | |
| 879 } | |
| 880 pixaDestroy(&pixad); | |
| 881 return pixd; | |
| 882 } | |
| 883 | |
| 884 | |
| 885 /*! | |
| 886 * \brief pixaSelectByArea() | |
| 887 * | |
| 888 * \param[in] pixas | |
| 889 * \param[in] thresh threshold number of fg pixels | |
| 890 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 891 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 892 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned | |
| 893 * \return pixad, or NULL on error | |
| 894 * | |
| 895 * <pre> | |
| 896 * Notes: | |
| 897 * (1) Returns a pixa clone if no components are removed. | |
| 898 * (2) Uses pix and box clones in the new pixa. | |
| 899 * (3) This filters components based on the number of fg pixels | |
| 900 * in the component. | |
| 901 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components | |
| 902 * with less than the threshold number of fg pixels, and | |
| 903 * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. | |
| 904 * </pre> | |
| 905 */ | |
| 906 PIXA * | |
| 907 pixaSelectByArea(PIXA *pixas, | |
| 908 l_float32 thresh, | |
| 909 l_int32 type, | |
| 910 l_int32 *pchanged) | |
| 911 { | |
| 912 NUMA *na, *nai; | |
| 913 PIXA *pixad; | |
| 914 | |
| 915 if (!pixas) | |
| 916 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 917 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && | |
| 918 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) | |
| 919 return (PIXA *)ERROR_PTR("invalid type", __func__, NULL); | |
| 920 | |
| 921 /* Compute area of each component */ | |
| 922 na = pixaCountPixels(pixas); | |
| 923 | |
| 924 /* Generate indicator array for elements to be saved. */ | |
| 925 nai = numaMakeThresholdIndicator(na, thresh, type); | |
| 926 numaDestroy(&na); | |
| 927 | |
| 928 /* Filter to get output */ | |
| 929 pixad = pixaSelectWithIndicator(pixas, nai, pchanged); | |
| 930 | |
| 931 numaDestroy(&nai); | |
| 932 return pixad; | |
| 933 } | |
| 934 | |
| 935 | |
| 936 /*! | |
| 937 * \brief pixSelectByWidthHeightRatio() | |
| 938 * | |
| 939 * \param[in] pixs 1 bpp | |
| 940 * \param[in] thresh threshold ratio of width/height | |
| 941 * \param[in] connectivity 4 or 8 | |
| 942 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 943 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 944 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned | |
| 945 * \return pixd, or NULL on error | |
| 946 * | |
| 947 * <pre> | |
| 948 * Notes: | |
| 949 * (1) The args specify constraints on the width-to-height ratio | |
| 950 * for components that are kept. | |
| 951 * (2) If unchanged, returns a copy of pixs. Otherwise, | |
| 952 * returns a new pix with the filtered components. | |
| 953 * (3) This filters components based on the width-to-height ratios. | |
| 954 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components | |
| 955 * with less than the threshold ratio, and | |
| 956 * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. | |
| 957 * </pre> | |
| 958 */ | |
| 959 PIX * | |
| 960 pixSelectByWidthHeightRatio(PIX *pixs, | |
| 961 l_float32 thresh, | |
| 962 l_int32 connectivity, | |
| 963 l_int32 type, | |
| 964 l_int32 *pchanged) | |
| 965 { | |
| 966 l_int32 w, h, empty, changed, count; | |
| 967 BOXA *boxa; | |
| 968 PIX *pixd; | |
| 969 PIXA *pixas, *pixad; | |
| 970 | |
| 971 if (!pixs) | |
| 972 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 973 if (connectivity != 4 && connectivity != 8) | |
| 974 return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL); | |
| 975 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && | |
| 976 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) | |
| 977 return (PIX *)ERROR_PTR("invalid type", __func__, NULL); | |
| 978 if (pchanged) *pchanged = FALSE; | |
| 979 | |
| 980 /* Check if any components exist */ | |
| 981 pixZero(pixs, &empty); | |
| 982 if (empty) | |
| 983 return pixCopy(NULL, pixs); | |
| 984 | |
| 985 /* Filter components */ | |
| 986 boxa = pixConnComp(pixs, &pixas, connectivity); | |
| 987 pixad = pixaSelectByWidthHeightRatio(pixas, thresh, type, &changed); | |
| 988 boxaDestroy(&boxa); | |
| 989 pixaDestroy(&pixas); | |
| 990 | |
| 991 if (!changed) { | |
| 992 pixaDestroy(&pixad); | |
| 993 return pixCopy(NULL, pixs); | |
| 994 } | |
| 995 | |
| 996 /* Render the result */ | |
| 997 if (pchanged) *pchanged = TRUE; | |
| 998 pixGetDimensions(pixs, &w, &h, NULL); | |
| 999 count = pixaGetCount(pixad); | |
| 1000 if (count == 0) { /* return empty pix */ | |
| 1001 pixd = pixCreateTemplate(pixs); | |
| 1002 } else { | |
| 1003 pixd = pixaDisplay(pixad, w, h); | |
| 1004 pixCopyResolution(pixd, pixs); | |
| 1005 pixCopyColormap(pixd, pixs); | |
| 1006 pixCopyText(pixd, pixs); | |
| 1007 pixCopyInputFormat(pixd, pixs); | |
| 1008 } | |
| 1009 pixaDestroy(&pixad); | |
| 1010 return pixd; | |
| 1011 } | |
| 1012 | |
| 1013 | |
| 1014 /*! | |
| 1015 * \brief pixaSelectByWidthHeightRatio() | |
| 1016 * | |
| 1017 * \param[in] pixas | |
| 1018 * \param[in] thresh threshold ratio of width/height | |
| 1019 * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 1020 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 1021 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned | |
| 1022 * \return pixad, or NULL on error | |
| 1023 * | |
| 1024 * <pre> | |
| 1025 * Notes: | |
| 1026 * (1) Returns a pixa clone if no components are removed. | |
| 1027 * (2) Uses pix and box clones in the new pixa. | |
| 1028 * (3) This filters components based on the width-to-height ratio | |
| 1029 * of each pix. | |
| 1030 * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components | |
| 1031 * with less than the threshold ratio, and | |
| 1032 * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. | |
| 1033 * </pre> | |
| 1034 */ | |
| 1035 PIXA * | |
| 1036 pixaSelectByWidthHeightRatio(PIXA *pixas, | |
| 1037 l_float32 thresh, | |
| 1038 l_int32 type, | |
| 1039 l_int32 *pchanged) | |
| 1040 { | |
| 1041 NUMA *na, *nai; | |
| 1042 PIXA *pixad; | |
| 1043 | |
| 1044 if (!pixas) | |
| 1045 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 1046 if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && | |
| 1047 type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) | |
| 1048 return (PIXA *)ERROR_PTR("invalid type", __func__, NULL); | |
| 1049 | |
| 1050 /* Compute component ratios. */ | |
| 1051 na = pixaFindWidthHeightRatio(pixas); | |
| 1052 | |
| 1053 /* Generate indicator array for elements to be saved. */ | |
| 1054 nai = numaMakeThresholdIndicator(na, thresh, type); | |
| 1055 numaDestroy(&na); | |
| 1056 | |
| 1057 /* Filter to get output */ | |
| 1058 pixad = pixaSelectWithIndicator(pixas, nai, pchanged); | |
| 1059 | |
| 1060 numaDestroy(&nai); | |
| 1061 return pixad; | |
| 1062 } | |
| 1063 | |
| 1064 | |
| 1065 /*! | |
| 1066 * \brief pixaSelectByNumConnComp() | |
| 1067 * | |
| 1068 * \param[in] pixas | |
| 1069 * \param[in] nmin minimum number of components | |
| 1070 * \param[in] nmax maximum number of components | |
| 1071 * \param[in] connectivity 4 or 8 | |
| 1072 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned | |
| 1073 * \return pixad, or NULL on error | |
| 1074 * | |
| 1075 * <pre> | |
| 1076 * Notes: | |
| 1077 * (1) Returns a pixa clone if no components are removed. | |
| 1078 * (2) Uses pix and box clones in the new pixa. | |
| 1079 * (3) This filters by the number of connected components in | |
| 1080 * a given range. | |
| 1081 * </pre> | |
| 1082 */ | |
| 1083 PIXA * | |
| 1084 pixaSelectByNumConnComp(PIXA *pixas, | |
| 1085 l_int32 nmin, | |
| 1086 l_int32 nmax, | |
| 1087 l_int32 connectivity, | |
| 1088 l_int32 *pchanged) | |
| 1089 { | |
| 1090 l_int32 n, i, count; | |
| 1091 NUMA *na; | |
| 1092 PIX *pix; | |
| 1093 PIXA *pixad; | |
| 1094 | |
| 1095 if (pchanged) *pchanged = 0; | |
| 1096 if (!pixas) | |
| 1097 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 1098 if (nmin > nmax) | |
| 1099 return (PIXA *)ERROR_PTR("nmin > nmax", __func__, NULL); | |
| 1100 if (connectivity != 4 && connectivity != 8) | |
| 1101 return (PIXA *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL); | |
| 1102 | |
| 1103 /* Get indicator array based on number of c.c. */ | |
| 1104 n = pixaGetCount(pixas); | |
| 1105 na = numaCreate(n); | |
| 1106 for (i = 0; i < n; i++) { | |
| 1107 pix = pixaGetPix(pixas, i, L_CLONE); | |
| 1108 pixCountConnComp(pix, connectivity, &count); | |
| 1109 if (count >= nmin && count <= nmax) | |
| 1110 numaAddNumber(na, 1); | |
| 1111 else | |
| 1112 numaAddNumber(na, 0); | |
| 1113 pixDestroy(&pix); | |
| 1114 } | |
| 1115 | |
| 1116 /* Filter to get output */ | |
| 1117 pixad = pixaSelectWithIndicator(pixas, na, pchanged); | |
| 1118 numaDestroy(&na); | |
| 1119 return pixad; | |
| 1120 } | |
| 1121 | |
| 1122 | |
| 1123 /*! | |
| 1124 * \brief pixaSelectWithIndicator() | |
| 1125 * | |
| 1126 * \param[in] pixas | |
| 1127 * \param[in] na indicator numa | |
| 1128 * \param[out] pchanged [optional] 1 if changed; 0 if clone returned | |
| 1129 * \return pixad, or NULL on error | |
| 1130 * | |
| 1131 * <pre> | |
| 1132 * Notes: | |
| 1133 * (1) Returns a pixa clone if no components are removed. | |
| 1134 * (2) Uses pix and box clones in the new pixa. | |
| 1135 * (3) The indicator numa has values 0 (ignore) and 1 (accept). | |
| 1136 * (4) If the source boxa is not fully populated, it is left | |
| 1137 * empty in the dest pixa. | |
| 1138 * </pre> | |
| 1139 */ | |
| 1140 PIXA * | |
| 1141 pixaSelectWithIndicator(PIXA *pixas, | |
| 1142 NUMA *na, | |
| 1143 l_int32 *pchanged) | |
| 1144 { | |
| 1145 l_int32 i, n, nbox, ival, nsave; | |
| 1146 BOX *box; | |
| 1147 PIX *pix1; | |
| 1148 PIXA *pixad; | |
| 1149 | |
| 1150 if (!pixas) | |
| 1151 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 1152 if (!na) | |
| 1153 return (PIXA *)ERROR_PTR("na not defined", __func__, NULL); | |
| 1154 | |
| 1155 nsave = 0; | |
| 1156 n = numaGetCount(na); | |
| 1157 for (i = 0; i < n; i++) { | |
| 1158 numaGetIValue(na, i, &ival); | |
| 1159 if (ival == 1) nsave++; | |
| 1160 } | |
| 1161 | |
| 1162 if (nsave == n) { | |
| 1163 if (pchanged) *pchanged = FALSE; | |
| 1164 return pixaCopy(pixas, L_CLONE); | |
| 1165 } | |
| 1166 if (pchanged) *pchanged = TRUE; | |
| 1167 pixad = pixaCreate(nsave); | |
| 1168 nbox = pixaGetBoxaCount(pixas); | |
| 1169 for (i = 0; i < n; i++) { | |
| 1170 numaGetIValue(na, i, &ival); | |
| 1171 if (ival == 0) continue; | |
| 1172 pix1 = pixaGetPix(pixas, i, L_CLONE); | |
| 1173 pixaAddPix(pixad, pix1, L_INSERT); | |
| 1174 if (nbox == n) { /* fully populated boxa */ | |
| 1175 box = pixaGetBox(pixas, i, L_CLONE); | |
| 1176 pixaAddBox(pixad, box, L_INSERT); | |
| 1177 } | |
| 1178 } | |
| 1179 | |
| 1180 return pixad; | |
| 1181 } | |
| 1182 | |
| 1183 | |
| 1184 /*! | |
| 1185 * \brief pixRemoveWithIndicator() | |
| 1186 * | |
| 1187 * \param[in] pixs 1 bpp pix from which components are removed; in-place | |
| 1188 * \param[in] pixa of connected components in pixs | |
| 1189 * \param[in] na numa indicator: remove components corresponding to 1s | |
| 1190 * \return 0 if OK, 1 on error | |
| 1191 * | |
| 1192 * <pre> | |
| 1193 * Notes: | |
| 1194 * (1) This complements pixAddWithIndicator(). Here, the selected | |
| 1195 * components are set subtracted from pixs. | |
| 1196 * </pre> | |
| 1197 */ | |
| 1198 l_ok | |
| 1199 pixRemoveWithIndicator(PIX *pixs, | |
| 1200 PIXA *pixa, | |
| 1201 NUMA *na) | |
| 1202 { | |
| 1203 l_int32 i, n, ival, x, y, w, h; | |
| 1204 BOX *box; | |
| 1205 PIX *pix; | |
| 1206 | |
| 1207 if (!pixs) | |
| 1208 return ERROR_INT("pixs not defined", __func__, 1); | |
| 1209 if (!pixa) | |
| 1210 return ERROR_INT("pixa not defined", __func__, 1); | |
| 1211 if (!na) | |
| 1212 return ERROR_INT("na not defined", __func__, 1); | |
| 1213 n = pixaGetCount(pixa); | |
| 1214 if (n != numaGetCount(na)) | |
| 1215 return ERROR_INT("pixa and na sizes not equal", __func__, 1); | |
| 1216 | |
| 1217 for (i = 0; i < n; i++) { | |
| 1218 numaGetIValue(na, i, &ival); | |
| 1219 if (ival == 1) { | |
| 1220 pix = pixaGetPix(pixa, i, L_CLONE); | |
| 1221 box = pixaGetBox(pixa, i, L_CLONE); | |
| 1222 boxGetGeometry(box, &x, &y, &w, &h); | |
| 1223 pixRasterop(pixs, x, y, w, h, PIX_DST & PIX_NOT(PIX_SRC), | |
| 1224 pix, 0, 0); | |
| 1225 boxDestroy(&box); | |
| 1226 pixDestroy(&pix); | |
| 1227 } | |
| 1228 } | |
| 1229 | |
| 1230 return 0; | |
| 1231 } | |
| 1232 | |
| 1233 | |
| 1234 /*! | |
| 1235 * \brief pixAddWithIndicator() | |
| 1236 * | |
| 1237 * \param[in] pixs 1 bpp pix from which components are added; in-place | |
| 1238 * \param[in] pixa of connected components, some of which will be put | |
| 1239 * into pixs | |
| 1240 * \param[in] na numa indicator: add components corresponding to 1s | |
| 1241 * \return 0 if OK, 1 on error | |
| 1242 * | |
| 1243 * <pre> | |
| 1244 * Notes: | |
| 1245 * (1) This complements pixRemoveWithIndicator(). Here, the selected | |
| 1246 * components are added to pixs. | |
| 1247 * </pre> | |
| 1248 */ | |
| 1249 l_ok | |
| 1250 pixAddWithIndicator(PIX *pixs, | |
| 1251 PIXA *pixa, | |
| 1252 NUMA *na) | |
| 1253 { | |
| 1254 l_int32 i, n, ival, x, y, w, h; | |
| 1255 BOX *box; | |
| 1256 PIX *pix; | |
| 1257 | |
| 1258 if (!pixs) | |
| 1259 return ERROR_INT("pixs not defined", __func__, 1); | |
| 1260 if (!pixa) | |
| 1261 return ERROR_INT("pixa not defined", __func__, 1); | |
| 1262 if (!na) | |
| 1263 return ERROR_INT("na not defined", __func__, 1); | |
| 1264 n = pixaGetCount(pixa); | |
| 1265 if (n != numaGetCount(na)) | |
| 1266 return ERROR_INT("pixa and na sizes not equal", __func__, 1); | |
| 1267 | |
| 1268 for (i = 0; i < n; i++) { | |
| 1269 numaGetIValue(na, i, &ival); | |
| 1270 if (ival == 1) { | |
| 1271 pix = pixaGetPix(pixa, i, L_CLONE); | |
| 1272 box = pixaGetBox(pixa, i, L_CLONE); | |
| 1273 boxGetGeometry(box, &x, &y, &w, &h); | |
| 1274 pixRasterop(pixs, x, y, w, h, PIX_SRC | PIX_DST, pix, 0, 0); | |
| 1275 boxDestroy(&box); | |
| 1276 pixDestroy(&pix); | |
| 1277 } | |
| 1278 } | |
| 1279 | |
| 1280 return 0; | |
| 1281 } | |
| 1282 | |
| 1283 | |
| 1284 /*! | |
| 1285 * \brief pixaSelectWithString() | |
| 1286 * | |
| 1287 * \param[in] pixas | |
| 1288 * \param[in] str string of indices into pixa, giving the pix to | |
| 1289 * be selected | |
| 1290 * \param[out] perror [optional] 1 if any indices are invalid; | |
| 1291 * 0 if all indices are valid | |
| 1292 * \return pixad, or NULL on error | |
| 1293 * | |
| 1294 * <pre> | |
| 1295 * Notes: | |
| 1296 * (1) Returns a pixa with copies of selected pix. | |
| 1297 * (2) Associated boxes are also copied, if fully populated. | |
| 1298 * </pre> | |
| 1299 */ | |
| 1300 PIXA * | |
| 1301 pixaSelectWithString(PIXA *pixas, | |
| 1302 const char *str, | |
| 1303 l_int32 *perror) | |
| 1304 { | |
| 1305 l_int32 i, nval, npix, nbox, val, imaxval; | |
| 1306 l_float32 maxval; | |
| 1307 BOX *box; | |
| 1308 NUMA *na; | |
| 1309 PIX *pix1; | |
| 1310 PIXA *pixad; | |
| 1311 | |
| 1312 if (perror) *perror = 0; | |
| 1313 if (!pixas) | |
| 1314 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 1315 if (!str) | |
| 1316 return (PIXA *)ERROR_PTR("str not defined", __func__, NULL); | |
| 1317 | |
| 1318 if ((na = numaCreateFromString(str)) == NULL) | |
| 1319 return (PIXA *)ERROR_PTR("na not made", __func__, NULL); | |
| 1320 if ((nval = numaGetCount(na)) == 0) { | |
| 1321 numaDestroy(&na); | |
| 1322 return (PIXA *)ERROR_PTR("no indices found", __func__, NULL); | |
| 1323 } | |
| 1324 numaGetMax(na, &maxval, NULL); | |
| 1325 imaxval = (l_int32)(maxval + 0.1); | |
| 1326 nbox = pixaGetBoxaCount(pixas); | |
| 1327 npix = pixaGetCount(pixas); | |
| 1328 if (imaxval >= npix) { | |
| 1329 if (perror) *perror = 1; | |
| 1330 L_ERROR("max index = %d, size of pixa = %d\n", __func__, imaxval, npix); | |
| 1331 } | |
| 1332 | |
| 1333 pixad = pixaCreate(nval); | |
| 1334 for (i = 0; i < nval; i++) { | |
| 1335 numaGetIValue(na, i, &val); | |
| 1336 if (val < 0 || val >= npix) { | |
| 1337 L_ERROR("index %d out of range of pix\n", __func__, val); | |
| 1338 continue; | |
| 1339 } | |
| 1340 pix1 = pixaGetPix(pixas, val, L_COPY); | |
| 1341 pixaAddPix(pixad, pix1, L_INSERT); | |
| 1342 if (nbox == npix) { /* fully populated boxa */ | |
| 1343 box = pixaGetBox(pixas, val, L_COPY); | |
| 1344 pixaAddBox(pixad, box, L_INSERT); | |
| 1345 } | |
| 1346 } | |
| 1347 numaDestroy(&na); | |
| 1348 return pixad; | |
| 1349 } | |
| 1350 | |
| 1351 | |
| 1352 /*! | |
| 1353 * \brief pixaRenderComponent() | |
| 1354 * | |
| 1355 * \param[in] pixs [optional] 1 bpp pix | |
| 1356 * \param[in] pixa of 1 bpp connected components, one of which will | |
| 1357 * be rendered in pixs, with its origin determined | |
| 1358 * by the associated box. | |
| 1359 * \param[in] index of component to be rendered | |
| 1360 * \return pixd, or NULL on error | |
| 1361 * | |
| 1362 * <pre> | |
| 1363 * Notes: | |
| 1364 * (1) If pixs is null, this generates an empty pix of a size determined | |
| 1365 * by union of the component bounding boxes, and including the origin. | |
| 1366 * (2) The selected component is blitted into pixs. | |
| 1367 * </pre> | |
| 1368 */ | |
| 1369 PIX * | |
| 1370 pixaRenderComponent(PIX *pixs, | |
| 1371 PIXA *pixa, | |
| 1372 l_int32 index) | |
| 1373 { | |
| 1374 l_int32 n, x, y, w, h, same, maxd; | |
| 1375 BOX *box; | |
| 1376 BOXA *boxa; | |
| 1377 PIX *pix; | |
| 1378 | |
| 1379 if (!pixa) | |
| 1380 return (PIX *)ERROR_PTR("pixa not defined", __func__, pixs); | |
| 1381 n = pixaGetCount(pixa); | |
| 1382 if (index < 0 || index >= n) | |
| 1383 return (PIX *)ERROR_PTR("invalid index", __func__, pixs); | |
| 1384 if (pixs && (pixGetDepth(pixs) != 1)) | |
| 1385 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixs); | |
| 1386 pixaVerifyDepth(pixa, &same, &maxd); | |
| 1387 if (maxd > 1) | |
| 1388 return (PIX *)ERROR_PTR("not all pix with d == 1", __func__, pixs); | |
| 1389 | |
| 1390 boxa = pixaGetBoxa(pixa, L_CLONE); | |
| 1391 if (!pixs) { | |
| 1392 boxaGetExtent(boxa, &w, &h, NULL); | |
| 1393 pixs = pixCreate(w, h, 1); | |
| 1394 } | |
| 1395 | |
| 1396 pix = pixaGetPix(pixa, index, L_CLONE); | |
| 1397 box = boxaGetBox(boxa, index, L_CLONE); | |
| 1398 boxGetGeometry(box, &x, &y, &w, &h); | |
| 1399 pixRasterop(pixs, x, y, w, h, PIX_SRC | PIX_DST, pix, 0, 0); | |
| 1400 boxDestroy(&box); | |
| 1401 pixDestroy(&pix); | |
| 1402 boxaDestroy(&boxa); | |
| 1403 | |
| 1404 return pixs; | |
| 1405 } | |
| 1406 | |
| 1407 | |
| 1408 /*---------------------------------------------------------------------* | |
| 1409 * Sort functions * | |
| 1410 *---------------------------------------------------------------------*/ | |
| 1411 /*! | |
| 1412 * \brief pixaSort() | |
| 1413 * | |
| 1414 * \param[in] pixas | |
| 1415 * \param[in] sorttype L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH, | |
| 1416 * L_SORT_BY_HEIGHT, L_SORT_BY_MIN_DIMENSION, | |
| 1417 * L_SORT_BY_MAX_DIMENSION, L_SORT_BY_PERIMETER, | |
| 1418 * L_SORT_BY_AREA, L_SORT_BY_ASPECT_RATIO | |
| 1419 * \param[in] sortorder L_SORT_INCREASING, L_SORT_DECREASING | |
| 1420 * \param[out] pnaindex [optional] index of sorted order into | |
| 1421 * original array | |
| 1422 * \param[in] copyflag L_COPY, L_CLONE | |
| 1423 * \return pixad sorted version of pixas, or NULL on error | |
| 1424 * | |
| 1425 * <pre> | |
| 1426 * Notes: | |
| 1427 * (1) This sorts based on the data in the boxa. If the boxa | |
| 1428 * count is not the same as the pixa count, this returns an error. | |
| 1429 * (2) If the boxa is empty, it makes one corresponding to the | |
| 1430 * dimensions of each pix, which allows meaningful sorting on | |
| 1431 * all types except x and y. | |
| 1432 * (3) The copyflag refers to the pix and box copies that are | |
| 1433 * inserted into the sorted pixa. These are either L_COPY | |
| 1434 * or L_CLONE. | |
| 1435 * </pre> | |
| 1436 */ | |
| 1437 PIXA * | |
| 1438 pixaSort(PIXA *pixas, | |
| 1439 l_int32 sorttype, | |
| 1440 l_int32 sortorder, | |
| 1441 NUMA **pnaindex, | |
| 1442 l_int32 copyflag) | |
| 1443 { | |
| 1444 l_int32 i, n, nb, x, y, w, h; | |
| 1445 BOXA *boxa; | |
| 1446 NUMA *na, *naindex; | |
| 1447 PIXA *pixad; | |
| 1448 | |
| 1449 if (pnaindex) *pnaindex = NULL; | |
| 1450 if (!pixas) | |
| 1451 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 1452 if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && | |
| 1453 sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT && | |
| 1454 sorttype != L_SORT_BY_MIN_DIMENSION && | |
| 1455 sorttype != L_SORT_BY_MAX_DIMENSION && | |
| 1456 sorttype != L_SORT_BY_PERIMETER && | |
| 1457 sorttype != L_SORT_BY_AREA && | |
| 1458 sorttype != L_SORT_BY_ASPECT_RATIO) | |
| 1459 return (PIXA *)ERROR_PTR("invalid sort type", __func__, NULL); | |
| 1460 if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) | |
| 1461 return (PIXA *)ERROR_PTR("invalid sort order", __func__, NULL); | |
| 1462 if (copyflag != L_COPY && copyflag != L_CLONE) | |
| 1463 return (PIXA *)ERROR_PTR("invalid copy flag", __func__, NULL); | |
| 1464 | |
| 1465 /* Check the pixa and boxa counts. Make a boxa if required. */ | |
| 1466 if ((n = pixaGetCount(pixas)) == 0) { | |
| 1467 L_INFO("no pix in pixa\n", __func__); | |
| 1468 return pixaCopy(pixas, copyflag); | |
| 1469 } | |
| 1470 if ((boxa = pixas->boxa) == NULL) /* not owned; do not destroy */ | |
| 1471 return (PIXA *)ERROR_PTR("boxa not found!", __func__, NULL); | |
| 1472 nb = boxaGetCount(boxa); | |
| 1473 if (nb == 0) { | |
| 1474 pixaSetFullSizeBoxa(pixas); | |
| 1475 nb = n; | |
| 1476 boxa = pixas->boxa; /* not owned */ | |
| 1477 if (sorttype == L_SORT_BY_X || sorttype == L_SORT_BY_Y) | |
| 1478 L_WARNING("sort by x or y where all values are 0\n", __func__); | |
| 1479 } | |
| 1480 if (nb != n) | |
| 1481 return (PIXA *)ERROR_PTR("boxa and pixa counts differ", __func__, NULL); | |
| 1482 | |
| 1483 /* Use O(n) binsort if possible */ | |
| 1484 if (n > MinCompsForBinSort && | |
| 1485 ((sorttype == L_SORT_BY_X) || (sorttype == L_SORT_BY_Y) || | |
| 1486 (sorttype == L_SORT_BY_WIDTH) || (sorttype == L_SORT_BY_HEIGHT) || | |
| 1487 (sorttype == L_SORT_BY_PERIMETER))) | |
| 1488 return pixaBinSort(pixas, sorttype, sortorder, pnaindex, copyflag); | |
| 1489 | |
| 1490 /* Build up numa of specific data */ | |
| 1491 if ((na = numaCreate(n)) == NULL) | |
| 1492 return (PIXA *)ERROR_PTR("na not made", __func__, NULL); | |
| 1493 for (i = 0; i < n; i++) { | |
| 1494 boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); | |
| 1495 switch (sorttype) | |
| 1496 { | |
| 1497 case L_SORT_BY_X: | |
| 1498 numaAddNumber(na, x); | |
| 1499 break; | |
| 1500 case L_SORT_BY_Y: | |
| 1501 numaAddNumber(na, y); | |
| 1502 break; | |
| 1503 case L_SORT_BY_WIDTH: | |
| 1504 numaAddNumber(na, w); | |
| 1505 break; | |
| 1506 case L_SORT_BY_HEIGHT: | |
| 1507 numaAddNumber(na, h); | |
| 1508 break; | |
| 1509 case L_SORT_BY_MIN_DIMENSION: | |
| 1510 numaAddNumber(na, L_MIN(w, h)); | |
| 1511 break; | |
| 1512 case L_SORT_BY_MAX_DIMENSION: | |
| 1513 numaAddNumber(na, L_MAX(w, h)); | |
| 1514 break; | |
| 1515 case L_SORT_BY_PERIMETER: | |
| 1516 numaAddNumber(na, w + h); | |
| 1517 break; | |
| 1518 case L_SORT_BY_AREA: | |
| 1519 numaAddNumber(na, w * h); | |
| 1520 break; | |
| 1521 case L_SORT_BY_ASPECT_RATIO: | |
| 1522 numaAddNumber(na, (l_float32)w / (l_float32)h); | |
| 1523 break; | |
| 1524 default: | |
| 1525 L_WARNING("invalid sort type\n", __func__); | |
| 1526 } | |
| 1527 } | |
| 1528 | |
| 1529 /* Get the sort index for data array */ | |
| 1530 naindex = numaGetSortIndex(na, sortorder); | |
| 1531 numaDestroy(&na); | |
| 1532 if (!naindex) | |
| 1533 return (PIXA *)ERROR_PTR("naindex not made", __func__, NULL); | |
| 1534 | |
| 1535 /* Build up sorted pixa using sort index */ | |
| 1536 if ((pixad = pixaSortByIndex(pixas, naindex, copyflag)) == NULL) { | |
| 1537 numaDestroy(&naindex); | |
| 1538 return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL); | |
| 1539 } | |
| 1540 | |
| 1541 if (pnaindex) | |
| 1542 *pnaindex = naindex; | |
| 1543 else | |
| 1544 numaDestroy(&naindex); | |
| 1545 return pixad; | |
| 1546 } | |
| 1547 | |
| 1548 | |
| 1549 /*! | |
| 1550 * \brief pixaBinSort() | |
| 1551 * | |
| 1552 * \param[in] pixas | |
| 1553 * \param[in] sorttype L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH, | |
| 1554 * L_SORT_BY_HEIGHT, L_SORT_BY_PERIMETER | |
| 1555 * \param[in] sortorder L_SORT_INCREASING, L_SORT_DECREASING | |
| 1556 * \param[out] pnaindex [optional] index of sorted order into | |
| 1557 * original array | |
| 1558 * \param[in] copyflag L_COPY, L_CLONE | |
| 1559 * \return pixad sorted version of pixas, or NULL on error | |
| 1560 * | |
| 1561 * <pre> | |
| 1562 * Notes: | |
| 1563 * (1) This sorts based on the data in the boxa. If the boxa | |
| 1564 * count is not the same as the pixa count, this returns an error. | |
| 1565 * (2) The copyflag refers to the pix and box copies that are | |
| 1566 * inserted into the sorted pixa. These are either L_COPY | |
| 1567 * or L_CLONE. | |
| 1568 * (3) For a large number of boxes (say, greater than 1000), this | |
| 1569 * O(n) binsort is much faster than the O(nlogn) shellsort. | |
| 1570 * For 5000 components, this is over 20x faster than boxaSort(). | |
| 1571 * (4) Consequently, pixaSort() calls this function if it will | |
| 1572 * likely go much faster. | |
| 1573 * </pre> | |
| 1574 */ | |
| 1575 PIXA * | |
| 1576 pixaBinSort(PIXA *pixas, | |
| 1577 l_int32 sorttype, | |
| 1578 l_int32 sortorder, | |
| 1579 NUMA **pnaindex, | |
| 1580 l_int32 copyflag) | |
| 1581 { | |
| 1582 l_int32 i, n, x, y, w, h; | |
| 1583 BOXA *boxa; | |
| 1584 NUMA *na, *naindex; | |
| 1585 PIXA *pixad; | |
| 1586 | |
| 1587 if (pnaindex) *pnaindex = NULL; | |
| 1588 if (!pixas) | |
| 1589 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 1590 if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && | |
| 1591 sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT && | |
| 1592 sorttype != L_SORT_BY_PERIMETER) | |
| 1593 return (PIXA *)ERROR_PTR("invalid sort type", __func__, NULL); | |
| 1594 if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) | |
| 1595 return (PIXA *)ERROR_PTR("invalid sort order", __func__, NULL); | |
| 1596 if (copyflag != L_COPY && copyflag != L_CLONE) | |
| 1597 return (PIXA *)ERROR_PTR("invalid copy flag", __func__, NULL); | |
| 1598 | |
| 1599 /* Verify that the pixa and its boxa have the same count */ | |
| 1600 if ((boxa = pixas->boxa) == NULL) /* not owned; do not destroy */ | |
| 1601 return (PIXA *)ERROR_PTR("boxa not found", __func__, NULL); | |
| 1602 n = pixaGetCount(pixas); | |
| 1603 if (boxaGetCount(boxa) != n) | |
| 1604 return (PIXA *)ERROR_PTR("boxa and pixa counts differ", __func__, NULL); | |
| 1605 | |
| 1606 /* Generate Numa of appropriate box dimensions */ | |
| 1607 if ((na = numaCreate(n)) == NULL) | |
| 1608 return (PIXA *)ERROR_PTR("na not made", __func__, NULL); | |
| 1609 for (i = 0; i < n; i++) { | |
| 1610 boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); | |
| 1611 switch (sorttype) | |
| 1612 { | |
| 1613 case L_SORT_BY_X: | |
| 1614 numaAddNumber(na, x); | |
| 1615 break; | |
| 1616 case L_SORT_BY_Y: | |
| 1617 numaAddNumber(na, y); | |
| 1618 break; | |
| 1619 case L_SORT_BY_WIDTH: | |
| 1620 numaAddNumber(na, w); | |
| 1621 break; | |
| 1622 case L_SORT_BY_HEIGHT: | |
| 1623 numaAddNumber(na, h); | |
| 1624 break; | |
| 1625 case L_SORT_BY_PERIMETER: | |
| 1626 numaAddNumber(na, w + h); | |
| 1627 break; | |
| 1628 default: | |
| 1629 L_WARNING("invalid sort type\n", __func__); | |
| 1630 } | |
| 1631 } | |
| 1632 | |
| 1633 /* Get the sort index for data array */ | |
| 1634 naindex = numaGetBinSortIndex(na, sortorder); | |
| 1635 numaDestroy(&na); | |
| 1636 if (!naindex) | |
| 1637 return (PIXA *)ERROR_PTR("naindex not made", __func__, NULL); | |
| 1638 | |
| 1639 /* Build up sorted pixa using sort index */ | |
| 1640 if ((pixad = pixaSortByIndex(pixas, naindex, copyflag)) == NULL) { | |
| 1641 numaDestroy(&naindex); | |
| 1642 return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL); | |
| 1643 } | |
| 1644 | |
| 1645 if (pnaindex) | |
| 1646 *pnaindex = naindex; | |
| 1647 else | |
| 1648 numaDestroy(&naindex); | |
| 1649 return pixad; | |
| 1650 } | |
| 1651 | |
| 1652 | |
| 1653 /*! | |
| 1654 * \brief pixaSortByIndex() | |
| 1655 * | |
| 1656 * \param[in] pixas | |
| 1657 * \param[in] naindex na that maps from the new pixa to the input pixa | |
| 1658 * \param[in] copyflag L_COPY, L_CLONE | |
| 1659 * \return pixad sorted, or NULL on error | |
| 1660 */ | |
| 1661 PIXA * | |
| 1662 pixaSortByIndex(PIXA *pixas, | |
| 1663 NUMA *naindex, | |
| 1664 l_int32 copyflag) | |
| 1665 { | |
| 1666 l_int32 i, n, index; | |
| 1667 BOX *box; | |
| 1668 PIX *pix; | |
| 1669 PIXA *pixad; | |
| 1670 | |
| 1671 if (!pixas) | |
| 1672 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 1673 if (!naindex) | |
| 1674 return (PIXA *)ERROR_PTR("naindex not defined", __func__, NULL); | |
| 1675 if (copyflag != L_CLONE && copyflag != L_COPY) | |
| 1676 return (PIXA *)ERROR_PTR("invalid copyflag", __func__, NULL); | |
| 1677 | |
| 1678 n = pixaGetCount(pixas); | |
| 1679 pixad = pixaCreate(n); | |
| 1680 for (i = 0; i < n; i++) { | |
| 1681 numaGetIValue(naindex, i, &index); | |
| 1682 pix = pixaGetPix(pixas, index, copyflag); | |
| 1683 box = pixaGetBox(pixas, index, copyflag); | |
| 1684 pixaAddPix(pixad, pix, L_INSERT); | |
| 1685 pixaAddBox(pixad, box, L_INSERT); | |
| 1686 } | |
| 1687 | |
| 1688 return pixad; | |
| 1689 } | |
| 1690 | |
| 1691 | |
| 1692 /*! | |
| 1693 * \brief pixaSort2dByIndex() | |
| 1694 * | |
| 1695 * \param[in] pixas | |
| 1696 * \param[in] naa numaa that maps from the new pixaa to the input pixas | |
| 1697 * \param[in] copyflag L_CLONE or L_COPY | |
| 1698 * \return paa sorted, or NULL on error | |
| 1699 */ | |
| 1700 PIXAA * | |
| 1701 pixaSort2dByIndex(PIXA *pixas, | |
| 1702 NUMAA *naa, | |
| 1703 l_int32 copyflag) | |
| 1704 { | |
| 1705 l_int32 pixtot, ntot, i, j, n, nn, index; | |
| 1706 BOX *box; | |
| 1707 NUMA *na; | |
| 1708 PIX *pix; | |
| 1709 PIXA *pixa; | |
| 1710 PIXAA *paa; | |
| 1711 | |
| 1712 if (!pixas) | |
| 1713 return (PIXAA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 1714 if (!naa) | |
| 1715 return (PIXAA *)ERROR_PTR("naindex not defined", __func__, NULL); | |
| 1716 | |
| 1717 /* Check counts */ | |
| 1718 ntot = numaaGetNumberCount(naa); | |
| 1719 pixtot = pixaGetCount(pixas); | |
| 1720 if (ntot != pixtot) | |
| 1721 return (PIXAA *)ERROR_PTR("element count mismatch", __func__, NULL); | |
| 1722 | |
| 1723 n = numaaGetCount(naa); | |
| 1724 paa = pixaaCreate(n); | |
| 1725 for (i = 0; i < n; i++) { | |
| 1726 na = numaaGetNuma(naa, i, L_CLONE); | |
| 1727 nn = numaGetCount(na); | |
| 1728 pixa = pixaCreate(nn); | |
| 1729 for (j = 0; j < nn; j++) { | |
| 1730 numaGetIValue(na, j, &index); | |
| 1731 pix = pixaGetPix(pixas, index, copyflag); | |
| 1732 box = pixaGetBox(pixas, index, copyflag); | |
| 1733 pixaAddPix(pixa, pix, L_INSERT); | |
| 1734 pixaAddBox(pixa, box, L_INSERT); | |
| 1735 } | |
| 1736 pixaaAddPixa(paa, pixa, L_INSERT); | |
| 1737 numaDestroy(&na); | |
| 1738 } | |
| 1739 | |
| 1740 return paa; | |
| 1741 } | |
| 1742 | |
| 1743 | |
| 1744 /*---------------------------------------------------------------------* | |
| 1745 * Pixa and Pixaa range selection * | |
| 1746 *---------------------------------------------------------------------*/ | |
| 1747 /*! | |
| 1748 * \brief pixaSelectRange() | |
| 1749 * | |
| 1750 * \param[in] pixas | |
| 1751 * \param[in] first use 0 to select from the beginning | |
| 1752 * \param[in] last use -1 to select to the end | |
| 1753 * \param[in] copyflag L_COPY, L_CLONE | |
| 1754 * \return pixad, or NULL on error | |
| 1755 * | |
| 1756 * <pre> | |
| 1757 * Notes: | |
| 1758 * (1) The copyflag specifies what we do with each pix from pixas. | |
| 1759 * Specifically, L_CLONE inserts a clone into pixad of each | |
| 1760 * selected pix from pixas. | |
| 1761 * </pre> | |
| 1762 */ | |
| 1763 PIXA * | |
| 1764 pixaSelectRange(PIXA *pixas, | |
| 1765 l_int32 first, | |
| 1766 l_int32 last, | |
| 1767 l_int32 copyflag) | |
| 1768 { | |
| 1769 l_int32 n, npix, i; | |
| 1770 PIX *pix; | |
| 1771 PIXA *pixad; | |
| 1772 | |
| 1773 if (!pixas) | |
| 1774 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 1775 if (copyflag != L_COPY && copyflag != L_CLONE) | |
| 1776 return (PIXA *)ERROR_PTR("invalid copyflag", __func__, NULL); | |
| 1777 n = pixaGetCount(pixas); | |
| 1778 first = L_MAX(0, first); | |
| 1779 if (last < 0) last = n - 1; | |
| 1780 if (first >= n) | |
| 1781 return (PIXA *)ERROR_PTR("invalid first", __func__, NULL); | |
| 1782 if (last >= n) { | |
| 1783 L_WARNING("last = %d is beyond max index = %d; adjusting\n", | |
| 1784 __func__, last, n - 1); | |
| 1785 last = n - 1; | |
| 1786 } | |
| 1787 if (first > last) | |
| 1788 return (PIXA *)ERROR_PTR("first > last", __func__, NULL); | |
| 1789 | |
| 1790 npix = last - first + 1; | |
| 1791 pixad = pixaCreate(npix); | |
| 1792 for (i = first; i <= last; i++) { | |
| 1793 pix = pixaGetPix(pixas, i, copyflag); | |
| 1794 pixaAddPix(pixad, pix, L_INSERT); | |
| 1795 } | |
| 1796 return pixad; | |
| 1797 } | |
| 1798 | |
| 1799 | |
| 1800 /*! | |
| 1801 * \brief pixaaSelectRange() | |
| 1802 * | |
| 1803 * \param[in] paas | |
| 1804 * \param[in] first use 0 to select from the beginning | |
| 1805 * \param[in] last use -1 to select to the end | |
| 1806 * \param[in] copyflag L_COPY, L_CLONE | |
| 1807 * \return paad, or NULL on error | |
| 1808 * | |
| 1809 * <pre> | |
| 1810 * Notes: | |
| 1811 * (1) The copyflag specifies what we do with each pixa from paas. | |
| 1812 * Specifically, L_CLONE inserts a clone into paad of each | |
| 1813 * selected pixa from paas. | |
| 1814 * </pre> | |
| 1815 */ | |
| 1816 PIXAA * | |
| 1817 pixaaSelectRange(PIXAA *paas, | |
| 1818 l_int32 first, | |
| 1819 l_int32 last, | |
| 1820 l_int32 copyflag) | |
| 1821 { | |
| 1822 l_int32 n, npixa, i; | |
| 1823 PIXA *pixa; | |
| 1824 PIXAA *paad; | |
| 1825 | |
| 1826 if (!paas) | |
| 1827 return (PIXAA *)ERROR_PTR("paas not defined", __func__, NULL); | |
| 1828 if (copyflag != L_COPY && copyflag != L_CLONE) | |
| 1829 return (PIXAA *)ERROR_PTR("invalid copyflag", __func__, NULL); | |
| 1830 n = pixaaGetCount(paas, NULL); | |
| 1831 first = L_MAX(0, first); | |
| 1832 if (last < 0) last = n - 1; | |
| 1833 if (first >= n) | |
| 1834 return (PIXAA *)ERROR_PTR("invalid first", __func__, NULL); | |
| 1835 if (last >= n) { | |
| 1836 L_WARNING("last = %d is beyond max index = %d; adjusting\n", | |
| 1837 __func__, last, n - 1); | |
| 1838 last = n - 1; | |
| 1839 } | |
| 1840 if (first > last) | |
| 1841 return (PIXAA *)ERROR_PTR("first > last", __func__, NULL); | |
| 1842 | |
| 1843 npixa = last - first + 1; | |
| 1844 paad = pixaaCreate(npixa); | |
| 1845 for (i = first; i <= last; i++) { | |
| 1846 pixa = pixaaGetPixa(paas, i, copyflag); | |
| 1847 pixaaAddPixa(paad, pixa, L_INSERT); | |
| 1848 } | |
| 1849 return paad; | |
| 1850 } | |
| 1851 | |
| 1852 | |
| 1853 /*---------------------------------------------------------------------* | |
| 1854 * Pixa and Pixaa scaling * | |
| 1855 *---------------------------------------------------------------------*/ | |
| 1856 /*! | |
| 1857 * \brief pixaaScaleToSize() | |
| 1858 * | |
| 1859 * \param[in] paas | |
| 1860 * \param[in] wd target width; use 0 if using height as target | |
| 1861 * \param[in] hd target height; use 0 if using width as target | |
| 1862 * \return paad, or NULL on error | |
| 1863 * | |
| 1864 * <pre> | |
| 1865 * Notes: | |
| 1866 * (1) This guarantees that each output scaled image has the | |
| 1867 * dimension(s) you specify. | |
| 1868 * ~ To specify the width with isotropic scaling, set %hd = 0. | |
| 1869 * ~ To specify the height with isotropic scaling, set %wd = 0. | |
| 1870 * ~ If both %wd and %hd are specified, the image is scaled | |
| 1871 * (in general, anisotropically) to that size. | |
| 1872 * ~ It is an error to set both %wd and %hd to 0. | |
| 1873 * </pre> | |
| 1874 */ | |
| 1875 PIXAA * | |
| 1876 pixaaScaleToSize(PIXAA *paas, | |
| 1877 l_int32 wd, | |
| 1878 l_int32 hd) | |
| 1879 { | |
| 1880 l_int32 n, i; | |
| 1881 PIXA *pixa1, *pixa2; | |
| 1882 PIXAA *paad; | |
| 1883 | |
| 1884 if (!paas) | |
| 1885 return (PIXAA *)ERROR_PTR("paas not defined", __func__, NULL); | |
| 1886 if (wd <= 0 && hd <= 0) | |
| 1887 return (PIXAA *)ERROR_PTR("neither wd nor hd > 0", __func__, NULL); | |
| 1888 | |
| 1889 n = pixaaGetCount(paas, NULL); | |
| 1890 paad = pixaaCreate(n); | |
| 1891 for (i = 0; i < n; i++) { | |
| 1892 pixa1 = pixaaGetPixa(paas, i, L_CLONE); | |
| 1893 pixa2 = pixaScaleToSize(pixa1, wd, hd); | |
| 1894 pixaaAddPixa(paad, pixa2, L_INSERT); | |
| 1895 pixaDestroy(&pixa1); | |
| 1896 } | |
| 1897 return paad; | |
| 1898 } | |
| 1899 | |
| 1900 | |
| 1901 /*! | |
| 1902 * \brief pixaaScaleToSizeVar() | |
| 1903 * | |
| 1904 * \param[in] paas | |
| 1905 * \param[in] nawd [optional] target widths; use NULL if using height | |
| 1906 * \param[in] nahd [optional] target height; use NULL if using width | |
| 1907 * \return paad, or NULL on error | |
| 1908 * | |
| 1909 * <pre> | |
| 1910 * Notes: | |
| 1911 * (1) This guarantees that the scaled images in each pixa have the | |
| 1912 * dimension(s) you specify in the numas. | |
| 1913 * ~ To specify the width with isotropic scaling, set %nahd = NULL. | |
| 1914 * ~ To specify the height with isotropic scaling, set %nawd = NULL. | |
| 1915 * ~ If both %nawd and %nahd are specified, the image is scaled | |
| 1916 * (in general, anisotropically) to that size. | |
| 1917 * ~ It is an error to set both %nawd and %nahd to NULL. | |
| 1918 * (2) If either nawd and/or nahd is defined, it must have the same | |
| 1919 * count as the number of pixa in paas. | |
| 1920 * </pre> | |
| 1921 */ | |
| 1922 PIXAA * | |
| 1923 pixaaScaleToSizeVar(PIXAA *paas, | |
| 1924 NUMA *nawd, | |
| 1925 NUMA *nahd) | |
| 1926 { | |
| 1927 l_int32 n, i, wd, hd; | |
| 1928 PIXA *pixa1, *pixa2; | |
| 1929 PIXAA *paad; | |
| 1930 | |
| 1931 if (!paas) | |
| 1932 return (PIXAA *)ERROR_PTR("paas not defined", __func__, NULL); | |
| 1933 if (!nawd && !nahd) | |
| 1934 return (PIXAA *)ERROR_PTR("!nawd && !nahd", __func__, NULL); | |
| 1935 | |
| 1936 n = pixaaGetCount(paas, NULL); | |
| 1937 if (nawd && (n != numaGetCount(nawd))) | |
| 1938 return (PIXAA *)ERROR_PTR("nawd wrong size", __func__, NULL); | |
| 1939 if (nahd && (n != numaGetCount(nahd))) | |
| 1940 return (PIXAA *)ERROR_PTR("nahd wrong size", __func__, NULL); | |
| 1941 paad = pixaaCreate(n); | |
| 1942 for (i = 0; i < n; i++) { | |
| 1943 wd = hd = 0; | |
| 1944 if (nawd) numaGetIValue(nawd, i, &wd); | |
| 1945 if (nahd) numaGetIValue(nahd, i, &hd); | |
| 1946 pixa1 = pixaaGetPixa(paas, i, L_CLONE); | |
| 1947 pixa2 = pixaScaleToSize(pixa1, wd, hd); | |
| 1948 pixaaAddPixa(paad, pixa2, L_INSERT); | |
| 1949 pixaDestroy(&pixa1); | |
| 1950 } | |
| 1951 return paad; | |
| 1952 } | |
| 1953 | |
| 1954 | |
| 1955 /*! | |
| 1956 * \brief pixaScaleToSize() | |
| 1957 * | |
| 1958 * \param[in] pixas | |
| 1959 * \param[in] wd target width; use 0 if using height as target | |
| 1960 * \param[in] hd target height; use 0 if using width as target | |
| 1961 * \return pixad, or NULL on error | |
| 1962 * | |
| 1963 * <pre> | |
| 1964 * Notes: | |
| 1965 * (1) See pixaaScaleToSize() | |
| 1966 * </pre> | |
| 1967 */ | |
| 1968 PIXA * | |
| 1969 pixaScaleToSize(PIXA *pixas, | |
| 1970 l_int32 wd, | |
| 1971 l_int32 hd) | |
| 1972 { | |
| 1973 l_int32 n, i; | |
| 1974 PIX *pix1, *pix2; | |
| 1975 PIXA *pixad; | |
| 1976 | |
| 1977 if (!pixas) | |
| 1978 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 1979 | |
| 1980 if (wd <= 0 && hd <= 0) /* no scaling requested */ | |
| 1981 return pixaCopy(pixas, L_CLONE); | |
| 1982 | |
| 1983 n = pixaGetCount(pixas); | |
| 1984 pixad = pixaCreate(n); | |
| 1985 for (i = 0; i < n; i++) { | |
| 1986 pix1 = pixaGetPix(pixas, i, L_CLONE); | |
| 1987 pix2 = pixScaleToSize(pix1, wd, hd); | |
| 1988 pixCopyText(pix2, pix1); | |
| 1989 pixaAddPix(pixad, pix2, L_INSERT); | |
| 1990 pixDestroy(&pix1); | |
| 1991 } | |
| 1992 return pixad; | |
| 1993 } | |
| 1994 | |
| 1995 | |
| 1996 /*! | |
| 1997 * \brief pixaScaleToSizeRel() | |
| 1998 * | |
| 1999 * \param[in] pixas | |
| 2000 * \param[in] delw change in width, in pixels; 0 means no change | |
| 2001 * \param[in] delh change in height, in pixels; 0 means no change | |
| 2002 * return pixad, or NULL on error | |
| 2003 * | |
| 2004 * <pre> | |
| 2005 * Notes: | |
| 2006 * (1) If a requested change in a pix is not possible because | |
| 2007 * either the requested width or height is <= 0, issue a | |
| 2008 * warning and return a copy. | |
| 2009 * </pre> | |
| 2010 */ | |
| 2011 PIXA * | |
| 2012 pixaScaleToSizeRel(PIXA *pixas, | |
| 2013 l_int32 delw, | |
| 2014 l_int32 delh) | |
| 2015 { | |
| 2016 l_int32 n, i; | |
| 2017 PIX *pix1, *pix2; | |
| 2018 PIXA *pixad; | |
| 2019 | |
| 2020 if (!pixas) | |
| 2021 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 2022 | |
| 2023 n = pixaGetCount(pixas); | |
| 2024 pixad = pixaCreate(n); | |
| 2025 for (i = 0; i < n; i++) { | |
| 2026 pix1 = pixaGetPix(pixas, i, L_CLONE); | |
| 2027 pix2 = pixScaleToSizeRel(pix1, delw, delh); | |
| 2028 if (pix2) { | |
| 2029 pixaAddPix(pixad, pix2, L_INSERT); | |
| 2030 } else { | |
| 2031 L_WARNING("relative scale to size failed; use a copy\n", __func__); | |
| 2032 pixaAddPix(pixad, pix1, L_COPY); | |
| 2033 } | |
| 2034 pixDestroy(&pix1); | |
| 2035 } | |
| 2036 return pixad; | |
| 2037 } | |
| 2038 | |
| 2039 | |
| 2040 /*! | |
| 2041 * \brief pixaScale() | |
| 2042 * | |
| 2043 * \param[in] pixas | |
| 2044 * \param[in] scalex | |
| 2045 * \param[in] scaley | |
| 2046 * \return pixad, or NULL on error | |
| 2047 * | |
| 2048 * <pre> | |
| 2049 * Notes: | |
| 2050 * (1) If pixas has a full boxes, it is scaled as well. | |
| 2051 * </pre> | |
| 2052 */ | |
| 2053 PIXA * | |
| 2054 pixaScale(PIXA *pixas, | |
| 2055 l_float32 scalex, | |
| 2056 l_float32 scaley) | |
| 2057 { | |
| 2058 l_int32 i, n, nb; | |
| 2059 BOXA *boxa1, *boxa2; | |
| 2060 PIX *pix1, *pix2; | |
| 2061 PIXA *pixad; | |
| 2062 | |
| 2063 if (!pixas) | |
| 2064 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 2065 if (scalex <= 0.0 || scaley <= 0.0) | |
| 2066 return (PIXA *)ERROR_PTR("invalid scaling parameters", __func__, NULL); | |
| 2067 | |
| 2068 n = pixaGetCount(pixas); | |
| 2069 pixad = pixaCreate(n); | |
| 2070 for (i = 0; i < n; i++) { | |
| 2071 pix1 = pixaGetPix(pixas, i, L_CLONE); | |
| 2072 pix2 = pixScale(pix1, scalex, scaley); | |
| 2073 pixCopyText(pix2, pix1); | |
| 2074 pixaAddPix(pixad, pix2, L_INSERT); | |
| 2075 pixDestroy(&pix1); | |
| 2076 } | |
| 2077 | |
| 2078 boxa1 = pixaGetBoxa(pixas, L_CLONE); | |
| 2079 nb = boxaGetCount(boxa1); | |
| 2080 if (nb == n) { | |
| 2081 boxa2 = boxaTransform(boxa1, 0, 0, scalex, scaley); | |
| 2082 pixaSetBoxa(pixad, boxa2, L_INSERT); | |
| 2083 } | |
| 2084 boxaDestroy(&boxa1); | |
| 2085 return pixad; | |
| 2086 } | |
| 2087 | |
| 2088 | |
| 2089 /*! | |
| 2090 * \brief pixaScaleBySampling() | |
| 2091 * | |
| 2092 * \param[in] pixas | |
| 2093 * \param[in] scalex | |
| 2094 * \param[in] scaley | |
| 2095 * \return pixad, or NULL on error | |
| 2096 * | |
| 2097 * <pre> | |
| 2098 * Notes: | |
| 2099 * (1) If pixas has a full boxes, it is scaled as well. | |
| 2100 * </pre> | |
| 2101 */ | |
| 2102 PIXA * | |
| 2103 pixaScaleBySampling(PIXA *pixas, | |
| 2104 l_float32 scalex, | |
| 2105 l_float32 scaley) | |
| 2106 { | |
| 2107 l_int32 i, n, nb; | |
| 2108 BOXA *boxa1, *boxa2; | |
| 2109 PIX *pix1, *pix2; | |
| 2110 PIXA *pixad; | |
| 2111 | |
| 2112 if (!pixas) | |
| 2113 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 2114 if (scalex <= 0.0 || scaley <= 0.0) | |
| 2115 return (PIXA *)ERROR_PTR("invalid scaling parameters", __func__, NULL); | |
| 2116 | |
| 2117 n = pixaGetCount(pixas); | |
| 2118 pixad = pixaCreate(n); | |
| 2119 for (i = 0; i < n; i++) { | |
| 2120 pix1 = pixaGetPix(pixas, i, L_CLONE); | |
| 2121 pix2 = pixScaleBySampling(pix1, scalex, scaley); | |
| 2122 pixCopyText(pix2, pix1); | |
| 2123 pixaAddPix(pixad, pix2, L_INSERT); | |
| 2124 pixDestroy(&pix1); | |
| 2125 } | |
| 2126 | |
| 2127 boxa1 = pixaGetBoxa(pixas, L_CLONE); | |
| 2128 nb = boxaGetCount(boxa1); | |
| 2129 if (nb == n) { | |
| 2130 boxa2 = boxaTransform(boxa1, 0, 0, scalex, scaley); | |
| 2131 pixaSetBoxa(pixad, boxa2, L_INSERT); | |
| 2132 } | |
| 2133 boxaDestroy(&boxa1); | |
| 2134 return pixad; | |
| 2135 } | |
| 2136 | |
| 2137 | |
| 2138 /*---------------------------------------------------------------------* | |
| 2139 * Pixa rotation and translation * | |
| 2140 *---------------------------------------------------------------------*/ | |
| 2141 /*! | |
| 2142 * \brief pixaRotate() | |
| 2143 * | |
| 2144 * \param[in] pixas 1, 2, 4, 8, 32 bpp rgb | |
| 2145 * \param[in] angle rotation angle in radians; clockwise is positive | |
| 2146 * \param[in] type L_ROTATE_AREA_MAP, L_ROTATE_SHEAR, L_ROTATE_SAMPLING | |
| 2147 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK | |
| 2148 * \param[in] width original width; use 0 to avoid embedding | |
| 2149 * \param[in] height original height; use 0 to avoid embedding | |
| 2150 * \return pixad, or NULL on error | |
| 2151 * | |
| 2152 * <pre> | |
| 2153 * Notes: | |
| 2154 * (1) Each pix is rotated about its center. See pixRotate() for details. | |
| 2155 * (2) The boxa array is copied. Why is it not rotated? | |
| 2156 * If a boxa exists, the array of boxes is in 1-to-1 | |
| 2157 * correspondence with the array of pix, and each box typically | |
| 2158 * represents the location of the pix relative to an image from | |
| 2159 * which it has been extracted. Like the pix, we could rotate | |
| 2160 * each box around its center, and then generate a box that | |
| 2161 * contains all four corners, as is done in boxaRotate(), but | |
| 2162 * this seems unnecessary. | |
| 2163 * </pre> | |
| 2164 */ | |
| 2165 PIXA * | |
| 2166 pixaRotate(PIXA *pixas, | |
| 2167 l_float32 angle, | |
| 2168 l_int32 type, | |
| 2169 l_int32 incolor, | |
| 2170 l_int32 width, | |
| 2171 l_int32 height) | |
| 2172 { | |
| 2173 l_int32 i, n; | |
| 2174 BOXA *boxa; | |
| 2175 PIX *pixs, *pixd; | |
| 2176 PIXA *pixad; | |
| 2177 | |
| 2178 if (!pixas) | |
| 2179 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 2180 if (type != L_ROTATE_SHEAR && type != L_ROTATE_AREA_MAP && | |
| 2181 type != L_ROTATE_SAMPLING) | |
| 2182 return (PIXA *)ERROR_PTR("invalid type", __func__, NULL); | |
| 2183 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) | |
| 2184 return (PIXA *)ERROR_PTR("invalid incolor", __func__, NULL); | |
| 2185 if (L_ABS(angle) < MinAngleToRotate) | |
| 2186 return pixaCopy(pixas, L_COPY); | |
| 2187 | |
| 2188 n = pixaGetCount(pixas); | |
| 2189 if ((pixad = pixaCreate(n)) == NULL) | |
| 2190 return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL); | |
| 2191 boxa = pixaGetBoxa(pixad, L_COPY); | |
| 2192 pixaSetBoxa(pixad, boxa, L_INSERT); | |
| 2193 for (i = 0; i < n; i++) { | |
| 2194 if ((pixs = pixaGetPix(pixas, i, L_CLONE)) == NULL) { | |
| 2195 pixaDestroy(&pixad); | |
| 2196 return (PIXA *)ERROR_PTR("pixs not found", __func__, NULL); | |
| 2197 } | |
| 2198 pixd = pixRotate(pixs, angle, type, incolor, width, height); | |
| 2199 pixaAddPix(pixad, pixd, L_INSERT); | |
| 2200 pixDestroy(&pixs); | |
| 2201 } | |
| 2202 | |
| 2203 return pixad; | |
| 2204 } | |
| 2205 | |
| 2206 | |
| 2207 /*! | |
| 2208 * \brief pixaRotateOrth() | |
| 2209 * | |
| 2210 * \param[in] pixas | |
| 2211 * \param[in] rotation 0 = noop, 1 = 90 deg, 2 = 180 deg, 3 = 270 deg; | |
| 2212 * all rotations are clockwise | |
| 2213 * \return pixad, or NULL on error | |
| 2214 * | |
| 2215 * <pre> | |
| 2216 * Notes: | |
| 2217 * (1) Rotates each pix in the pixa. Rotates and saves the boxes in | |
| 2218 * the boxa if the boxa is full. | |
| 2219 * </pre> | |
| 2220 */ | |
| 2221 PIXA * | |
| 2222 pixaRotateOrth(PIXA *pixas, | |
| 2223 l_int32 rotation) | |
| 2224 { | |
| 2225 l_int32 i, n, nb, w, h; | |
| 2226 BOX *boxs, *boxd; | |
| 2227 PIX *pixs, *pixd; | |
| 2228 PIXA *pixad; | |
| 2229 | |
| 2230 if (!pixas) | |
| 2231 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 2232 if (rotation < 0 || rotation > 3) | |
| 2233 return (PIXA *)ERROR_PTR("rotation not in {0,1,2,3}", __func__, NULL); | |
| 2234 if (rotation == 0) | |
| 2235 return pixaCopy(pixas, L_COPY); | |
| 2236 | |
| 2237 n = pixaGetCount(pixas); | |
| 2238 nb = pixaGetBoxaCount(pixas); | |
| 2239 if ((pixad = pixaCreate(n)) == NULL) | |
| 2240 return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL); | |
| 2241 for (i = 0; i < n; i++) { | |
| 2242 if ((pixs = pixaGetPix(pixas, i, L_CLONE)) == NULL) { | |
| 2243 pixaDestroy(&pixad); | |
| 2244 return (PIXA *)ERROR_PTR("pixs not found", __func__, NULL); | |
| 2245 } | |
| 2246 pixd = pixRotateOrth(pixs, rotation); | |
| 2247 pixaAddPix(pixad, pixd, L_INSERT); | |
| 2248 if (n == nb) { | |
| 2249 boxs = pixaGetBox(pixas, i, L_COPY); | |
| 2250 pixGetDimensions(pixs, &w, &h, NULL); | |
| 2251 boxd = boxRotateOrth(boxs, w, h, rotation); | |
| 2252 pixaAddBox(pixad, boxd, L_INSERT); | |
| 2253 boxDestroy(&boxs); | |
| 2254 } | |
| 2255 pixDestroy(&pixs); | |
| 2256 } | |
| 2257 | |
| 2258 return pixad; | |
| 2259 } | |
| 2260 | |
| 2261 | |
| 2262 /*! | |
| 2263 * \brief pixaTranslate() | |
| 2264 * | |
| 2265 * \param[in] pixas | |
| 2266 * \param[in] hshift horizontal shift; hshift > 0 is to right | |
| 2267 * \param[in] vshift vertical shift; vshift > 0 is down | |
| 2268 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK | |
| 2269 * \return pixad, or NULL on error. | |
| 2270 */ | |
| 2271 PIXA * | |
| 2272 pixaTranslate(PIXA *pixas, | |
| 2273 l_int32 hshift, | |
| 2274 l_int32 vshift, | |
| 2275 l_int32 incolor) | |
| 2276 { | |
| 2277 l_int32 i, n, nb; | |
| 2278 BOXA *boxas, *boxad; | |
| 2279 PIX *pixs, *pixd; | |
| 2280 PIXA *pixad; | |
| 2281 | |
| 2282 if (!pixas) | |
| 2283 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 2284 if (hshift == 0 && vshift == 0) | |
| 2285 return pixaCopy(pixas, L_COPY); | |
| 2286 | |
| 2287 n = pixaGetCount(pixas); | |
| 2288 nb = pixaGetBoxaCount(pixas); | |
| 2289 if ((pixad = pixaCreate(n)) == NULL) | |
| 2290 return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL); | |
| 2291 for (i = 0; i < n; i++) { | |
| 2292 if ((pixs = pixaGetPix(pixas, i, L_CLONE)) == NULL) { | |
| 2293 pixaDestroy(&pixad); | |
| 2294 return (PIXA *)ERROR_PTR("pixs not found", __func__, NULL); | |
| 2295 } | |
| 2296 pixd = pixTranslate(NULL, pixs, hshift, vshift, incolor); | |
| 2297 pixaAddPix(pixad, pixd, L_INSERT); | |
| 2298 pixDestroy(&pixs); | |
| 2299 } | |
| 2300 if (n == nb) { | |
| 2301 boxas = pixaGetBoxa(pixas, L_CLONE); | |
| 2302 boxad = boxaTransform(boxas, hshift, vshift, 1.0, 1.0); | |
| 2303 pixaSetBoxa(pixad, boxad, L_INSERT); | |
| 2304 boxaDestroy(&boxas); | |
| 2305 } | |
| 2306 | |
| 2307 return pixad; | |
| 2308 } | |
| 2309 | |
| 2310 | |
| 2311 /*---------------------------------------------------------------------* | |
| 2312 * Miscellaneous functions * | |
| 2313 *---------------------------------------------------------------------*/ | |
| 2314 /*! | |
| 2315 * \brief pixaAddBorderGeneral() | |
| 2316 * | |
| 2317 * \param[in] pixad can be null or equal to pixas | |
| 2318 * \param[in] pixas containing pix of all depths; colormap ok | |
| 2319 * \param[in] left, right, top, bot number of pixels added | |
| 2320 * \param[in] val value of added border pixels | |
| 2321 * \return pixad with border added to each pix, including on error | |
| 2322 * | |
| 2323 * <pre> | |
| 2324 * Notes: | |
| 2325 * (1) For binary images: | |
| 2326 * white: val = 0 | |
| 2327 * black: val = 1 | |
| 2328 * For grayscale images: | |
| 2329 * white: val = 2 ** d - 1 | |
| 2330 * black: val = 0 | |
| 2331 * For rgb color images: | |
| 2332 * white: val = 0xffffff00 | |
| 2333 * black: val = 0 | |
| 2334 * For colormapped images, use 'index' found this way: | |
| 2335 * white: pixcmapGetRankIntensity(cmap, 1.0, &index); | |
| 2336 * black: pixcmapGetRankIntensity(cmap, 0.0, &index); | |
| 2337 * (2) For in-place replacement of each pix with a bordered version, | |
| 2338 * use %pixad = %pixas. To make a new pixa, use %pixad = NULL. | |
| 2339 * (3) In both cases, the boxa has sides adjusted as if it were | |
| 2340 * expanded by the border. | |
| 2341 * </pre> | |
| 2342 */ | |
| 2343 PIXA * | |
| 2344 pixaAddBorderGeneral(PIXA *pixad, | |
| 2345 PIXA *pixas, | |
| 2346 l_int32 left, | |
| 2347 l_int32 right, | |
| 2348 l_int32 top, | |
| 2349 l_int32 bot, | |
| 2350 l_uint32 val) | |
| 2351 { | |
| 2352 l_int32 i, n, nbox; | |
| 2353 BOX *box; | |
| 2354 BOXA *boxad; | |
| 2355 PIX *pixs, *pixd; | |
| 2356 | |
| 2357 if (!pixas) | |
| 2358 return (PIXA *)ERROR_PTR("pixas not defined", __func__, pixad); | |
| 2359 if (left < 0 || right < 0 || top < 0 || bot < 0) | |
| 2360 return (PIXA *)ERROR_PTR("negative border added!", __func__, pixad); | |
| 2361 if (pixad && (pixad != pixas)) | |
| 2362 return (PIXA *)ERROR_PTR("pixad defined but != pixas", __func__, pixad); | |
| 2363 | |
| 2364 n = pixaGetCount(pixas); | |
| 2365 if (!pixad) | |
| 2366 pixad = pixaCreate(n); | |
| 2367 for (i = 0; i < n; i++) { | |
| 2368 pixs = pixaGetPix(pixas, i, L_CLONE); | |
| 2369 pixd = pixAddBorderGeneral(pixs, left, right, top, bot, val); | |
| 2370 if (pixad == pixas) /* replace */ | |
| 2371 pixaReplacePix(pixad, i, pixd, NULL); | |
| 2372 else | |
| 2373 pixaAddPix(pixad, pixd, L_INSERT); | |
| 2374 pixDestroy(&pixs); | |
| 2375 } | |
| 2376 | |
| 2377 nbox = pixaGetBoxaCount(pixas); | |
| 2378 boxad = pixaGetBoxa(pixad, L_CLONE); | |
| 2379 for (i = 0; i < nbox; i++) { | |
| 2380 if ((box = pixaGetBox(pixas, i, L_COPY)) == NULL) { | |
| 2381 L_WARNING("box %d not found\n", __func__, i); | |
| 2382 break; | |
| 2383 } | |
| 2384 boxAdjustSides(box, box, -left, right, -top, bot); | |
| 2385 if (pixad == pixas) /* replace */ | |
| 2386 boxaReplaceBox(boxad, i, box); | |
| 2387 else | |
| 2388 boxaAddBox(boxad, box, L_INSERT); | |
| 2389 } | |
| 2390 boxaDestroy(&boxad); | |
| 2391 | |
| 2392 return pixad; | |
| 2393 } | |
| 2394 | |
| 2395 | |
| 2396 /*! | |
| 2397 * \brief pixaaFlattenToPixa() | |
| 2398 * | |
| 2399 * \param[in] paa | |
| 2400 * \param[out] pnaindex [optional] the pixa index in the pixaa | |
| 2401 * \param[in] copyflag L_COPY or L_CLONE | |
| 2402 * \return pixa, or NULL on error | |
| 2403 * | |
| 2404 * <pre> | |
| 2405 * Notes: | |
| 2406 * (1) This 'flattens' the pixaa to a pixa, taking the pix in | |
| 2407 * order in the first pixa, then the second, etc. | |
| 2408 * (2) If &naindex is defined, we generate a Numa that gives, for | |
| 2409 * each pix in the pixaa, the index of the pixa to which it belongs. | |
| 2410 * </pre> | |
| 2411 */ | |
| 2412 PIXA * | |
| 2413 pixaaFlattenToPixa(PIXAA *paa, | |
| 2414 NUMA **pnaindex, | |
| 2415 l_int32 copyflag) | |
| 2416 { | |
| 2417 l_int32 i, j, m, mb, n; | |
| 2418 BOX *box; | |
| 2419 NUMA *naindex = NULL; | |
| 2420 PIX *pix; | |
| 2421 PIXA *pixa, *pixat; | |
| 2422 | |
| 2423 if (pnaindex) *pnaindex = NULL; | |
| 2424 if (!paa) | |
| 2425 return (PIXA *)ERROR_PTR("paa not defined", __func__, NULL); | |
| 2426 if (copyflag != L_COPY && copyflag != L_CLONE) | |
| 2427 return (PIXA *)ERROR_PTR("invalid copyflag", __func__, NULL); | |
| 2428 | |
| 2429 if (pnaindex) { | |
| 2430 naindex = numaCreate(0); | |
| 2431 *pnaindex = naindex; | |
| 2432 } | |
| 2433 | |
| 2434 n = pixaaGetCount(paa, NULL); | |
| 2435 pixa = pixaCreate(n); | |
| 2436 for (i = 0; i < n; i++) { | |
| 2437 pixat = pixaaGetPixa(paa, i, L_CLONE); | |
| 2438 m = pixaGetCount(pixat); | |
| 2439 mb = pixaGetBoxaCount(pixat); | |
| 2440 for (j = 0; j < m; j++) { | |
| 2441 pix = pixaGetPix(pixat, j, copyflag); | |
| 2442 pixaAddPix(pixa, pix, L_INSERT); | |
| 2443 if (j < mb) { | |
| 2444 box = pixaGetBox(pixat, j, copyflag); | |
| 2445 pixaAddBox(pixa, box, L_INSERT); | |
| 2446 } | |
| 2447 if (pnaindex) | |
| 2448 numaAddNumber(naindex, i); /* save 'row' number */ | |
| 2449 } | |
| 2450 pixaDestroy(&pixat); | |
| 2451 } | |
| 2452 | |
| 2453 return pixa; | |
| 2454 } | |
| 2455 | |
| 2456 | |
| 2457 /*! | |
| 2458 * \brief pixaaSizeRange() | |
| 2459 * | |
| 2460 * \param[in] paa | |
| 2461 * \param[out] pminw, pminh, pmaxw, pmaxh [optional] range of | |
| 2462 * dimensions of all boxes | |
| 2463 * \return 0 if OK, 1 on error | |
| 2464 */ | |
| 2465 l_ok | |
| 2466 pixaaSizeRange(PIXAA *paa, | |
| 2467 l_int32 *pminw, | |
| 2468 l_int32 *pminh, | |
| 2469 l_int32 *pmaxw, | |
| 2470 l_int32 *pmaxh) | |
| 2471 { | |
| 2472 l_int32 minw, minh, maxw, maxh, minpw, minph, maxpw, maxph, i, n; | |
| 2473 PIXA *pixa; | |
| 2474 | |
| 2475 if (pminw) *pminw = 0; | |
| 2476 if (pminh) *pminh = 0; | |
| 2477 if (pmaxw) *pmaxw = 0; | |
| 2478 if (pmaxh) *pmaxh = 0; | |
| 2479 if (!paa) | |
| 2480 return ERROR_INT("paa not defined", __func__, 1); | |
| 2481 if (!pminw && !pmaxw && !pminh && !pmaxh) | |
| 2482 return ERROR_INT("no data can be returned", __func__, 1); | |
| 2483 | |
| 2484 minw = minh = 100000000; | |
| 2485 maxw = maxh = 0; | |
| 2486 n = pixaaGetCount(paa, NULL); | |
| 2487 for (i = 0; i < n; i++) { | |
| 2488 pixa = pixaaGetPixa(paa, i, L_CLONE); | |
| 2489 pixaSizeRange(pixa, &minpw, &minph, &maxpw, &maxph); | |
| 2490 if (minpw < minw) | |
| 2491 minw = minpw; | |
| 2492 if (minph < minh) | |
| 2493 minh = minph; | |
| 2494 if (maxpw > maxw) | |
| 2495 maxw = maxpw; | |
| 2496 if (maxph > maxh) | |
| 2497 maxh = maxph; | |
| 2498 pixaDestroy(&pixa); | |
| 2499 } | |
| 2500 | |
| 2501 if (pminw) *pminw = minw; | |
| 2502 if (pminh) *pminh = minh; | |
| 2503 if (pmaxw) *pmaxw = maxw; | |
| 2504 if (pmaxh) *pmaxh = maxh; | |
| 2505 return 0; | |
| 2506 } | |
| 2507 | |
| 2508 | |
| 2509 /*! | |
| 2510 * \brief pixaSizeRange() | |
| 2511 * | |
| 2512 * \param[in] pixa | |
| 2513 * \param[out] pminw, pminh, pmaxw, pmaxh [optional] range of | |
| 2514 * dimensions of pix in the array | |
| 2515 * \return 0 if OK, 1 on error | |
| 2516 */ | |
| 2517 l_ok | |
| 2518 pixaSizeRange(PIXA *pixa, | |
| 2519 l_int32 *pminw, | |
| 2520 l_int32 *pminh, | |
| 2521 l_int32 *pmaxw, | |
| 2522 l_int32 *pmaxh) | |
| 2523 { | |
| 2524 l_int32 minw, minh, maxw, maxh, i, n, w, h; | |
| 2525 PIX *pix; | |
| 2526 | |
| 2527 if (pminw) *pminw = 0; | |
| 2528 if (pminh) *pminh = 0; | |
| 2529 if (pmaxw) *pmaxw = 0; | |
| 2530 if (pmaxh) *pmaxh = 0; | |
| 2531 if (!pixa) | |
| 2532 return ERROR_INT("pixa not defined", __func__, 1); | |
| 2533 if (!pminw && !pmaxw && !pminh && !pmaxh) | |
| 2534 return ERROR_INT("no data can be returned", __func__, 1); | |
| 2535 | |
| 2536 minw = minh = 1000000; | |
| 2537 maxw = maxh = 0; | |
| 2538 n = pixaGetCount(pixa); | |
| 2539 for (i = 0; i < n; i++) { | |
| 2540 pix = pixaGetPix(pixa, i, L_CLONE); | |
| 2541 w = pixGetWidth(pix); | |
| 2542 h = pixGetHeight(pix); | |
| 2543 if (w < minw) | |
| 2544 minw = w; | |
| 2545 if (h < minh) | |
| 2546 minh = h; | |
| 2547 if (w > maxw) | |
| 2548 maxw = w; | |
| 2549 if (h > maxh) | |
| 2550 maxh = h; | |
| 2551 pixDestroy(&pix); | |
| 2552 } | |
| 2553 | |
| 2554 if (pminw) *pminw = minw; | |
| 2555 if (pminh) *pminh = minh; | |
| 2556 if (pmaxw) *pmaxw = maxw; | |
| 2557 if (pmaxh) *pmaxh = maxh; | |
| 2558 | |
| 2559 return 0; | |
| 2560 } | |
| 2561 | |
| 2562 | |
| 2563 /*! | |
| 2564 * \brief pixaClipToPix() | |
| 2565 * | |
| 2566 * \param[in] pixas | |
| 2567 * \param[in] pixs | |
| 2568 * \return pixad, or NULL on error | |
| 2569 * | |
| 2570 * <pre> | |
| 2571 * Notes: | |
| 2572 * (1) This is intended for use in situations where pixas | |
| 2573 * was originally generated from the input pixs. | |
| 2574 * (2) Returns a pixad where each pix in pixas is ANDed | |
| 2575 * with its associated region of the input pixs. This | |
| 2576 * region is specified by the the box that is associated | |
| 2577 * with the pix. | |
| 2578 * (3) In a typical application of this function, pixas has | |
| 2579 * a set of region masks, so this generates a pixa of | |
| 2580 * the parts of pixs that correspond to each region | |
| 2581 * mask component, along with the bounding box for | |
| 2582 * the region. | |
| 2583 * </pre> | |
| 2584 */ | |
| 2585 PIXA * | |
| 2586 pixaClipToPix(PIXA *pixas, | |
| 2587 PIX *pixs) | |
| 2588 { | |
| 2589 l_int32 i, n; | |
| 2590 BOX *box; | |
| 2591 PIX *pix, *pixc; | |
| 2592 PIXA *pixad; | |
| 2593 | |
| 2594 if (!pixas) | |
| 2595 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 2596 if (!pixs) | |
| 2597 return (PIXA *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2598 | |
| 2599 n = pixaGetCount(pixas); | |
| 2600 if ((pixad = pixaCreate(n)) == NULL) | |
| 2601 return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL); | |
| 2602 | |
| 2603 for (i = 0; i < n; i++) { | |
| 2604 pix = pixaGetPix(pixas, i, L_CLONE); | |
| 2605 box = pixaGetBox(pixas, i, L_COPY); | |
| 2606 pixc = pixClipRectangle(pixs, box, NULL); | |
| 2607 pixAnd(pixc, pixc, pix); | |
| 2608 pixaAddPix(pixad, pixc, L_INSERT); | |
| 2609 pixaAddBox(pixad, box, L_INSERT); | |
| 2610 pixDestroy(&pix); | |
| 2611 } | |
| 2612 | |
| 2613 return pixad; | |
| 2614 } | |
| 2615 | |
| 2616 | |
| 2617 /*! | |
| 2618 * \brief pixaClipToForeground() | |
| 2619 * | |
| 2620 * \param[in] pixas | |
| 2621 * \param[out] ppixad [optional] pixa of clipped pix returned | |
| 2622 * \param[out] pboxa [optional] clipping boxes returned | |
| 2623 * \return 0 if OK, 1 on error | |
| 2624 * | |
| 2625 * <pre> | |
| 2626 * Notes: | |
| 2627 * (1) At least one of [&pixd, &boxa] must be specified. | |
| 2628 * (2) Any pix with no fg pixels is skipped. | |
| 2629 * (3) See pixClipToForeground(). | |
| 2630 * </pre> | |
| 2631 */ | |
| 2632 l_ok | |
| 2633 pixaClipToForeground(PIXA *pixas, | |
| 2634 PIXA **ppixad, | |
| 2635 BOXA **pboxa) | |
| 2636 { | |
| 2637 l_int32 i, n; | |
| 2638 BOX *box1; | |
| 2639 PIX *pix1, *pix2; | |
| 2640 | |
| 2641 if (ppixad) *ppixad = NULL; | |
| 2642 if (pboxa) *pboxa = NULL; | |
| 2643 if (!pixas) | |
| 2644 return ERROR_INT("pixas not defined", __func__, 1); | |
| 2645 if (!ppixad && !pboxa) | |
| 2646 return ERROR_INT("no output requested", __func__, 1); | |
| 2647 | |
| 2648 n = pixaGetCount(pixas); | |
| 2649 if (ppixad) *ppixad = pixaCreate(n); | |
| 2650 if (pboxa) *pboxa = boxaCreate(n); | |
| 2651 for (i = 0; i < n; i++) { | |
| 2652 pix1 = pixaGetPix(pixas, i, L_CLONE); | |
| 2653 pixClipToForeground(pix1, &pix2, &box1); | |
| 2654 pixDestroy(&pix1); | |
| 2655 if (ppixad) | |
| 2656 pixaAddPix(*ppixad, pix2, L_INSERT); | |
| 2657 else | |
| 2658 pixDestroy(&pix2); | |
| 2659 if (pboxa) | |
| 2660 boxaAddBox(*pboxa, box1, L_INSERT); | |
| 2661 else | |
| 2662 boxDestroy(&box1); | |
| 2663 } | |
| 2664 | |
| 2665 return 0; | |
| 2666 } | |
| 2667 | |
| 2668 | |
| 2669 /*! | |
| 2670 * \brief pixaGetRenderingDepth() | |
| 2671 * | |
| 2672 * \param[in] pixa | |
| 2673 * \param[out] pdepth depth required to render if all colormaps are removed | |
| 2674 * \return 0 if OK; 1 on error | |
| 2675 * | |
| 2676 * <pre> | |
| 2677 * Notes: | |
| 2678 * (1) Any pix with 16 bpp will be considered as having 8 bpp. | |
| 2679 * If all pix have bpp = 1, this returns 1. | |
| 2680 * If any pix has color (either rgb or a colormap with color), | |
| 2681 * this return 32. | |
| 2682 * Otherwise, this returns the maximum of 8 and the max depth | |
| 2683 * of all the pix. | |
| 2684 * (2) This can be used to allow lossless rendering onto a single pix. | |
| 2685 * </pre> | |
| 2686 */ | |
| 2687 l_ok | |
| 2688 pixaGetRenderingDepth(PIXA *pixa, | |
| 2689 l_int32 *pdepth) | |
| 2690 { | |
| 2691 l_int32 hascolor, maxdepth; | |
| 2692 | |
| 2693 if (!pdepth) | |
| 2694 return ERROR_INT("&depth not defined", __func__, 1); | |
| 2695 *pdepth = 0; | |
| 2696 if (!pixa) | |
| 2697 return ERROR_INT("pixa not defined", __func__, 1); | |
| 2698 | |
| 2699 pixaHasColor(pixa, &hascolor); | |
| 2700 if (hascolor) { | |
| 2701 *pdepth = 32; | |
| 2702 return 0; | |
| 2703 } | |
| 2704 | |
| 2705 pixaGetDepthInfo(pixa, &maxdepth, NULL); | |
| 2706 if (maxdepth == 1) | |
| 2707 *pdepth = 1; | |
| 2708 else /* 2, 4, 8 or 16 */ | |
| 2709 *pdepth = 8; | |
| 2710 return 0; | |
| 2711 } | |
| 2712 | |
| 2713 | |
| 2714 /*! | |
| 2715 * \brief pixaHasColor() | |
| 2716 * | |
| 2717 * \param[in] pixa | |
| 2718 * \param[out] phascolor 1 if any pix is rgb or has a colormap with color; | |
| 2719 * 0 otherwise | |
| 2720 * \return 0 if OK; 1 on error | |
| 2721 */ | |
| 2722 l_ok | |
| 2723 pixaHasColor(PIXA *pixa, | |
| 2724 l_int32 *phascolor) | |
| 2725 { | |
| 2726 l_int32 i, n, hascolor, d; | |
| 2727 PIX *pix; | |
| 2728 PIXCMAP *cmap; | |
| 2729 | |
| 2730 if (!phascolor) | |
| 2731 return ERROR_INT("&hascolor not defined", __func__, 1); | |
| 2732 *phascolor = 0; | |
| 2733 if (!pixa) | |
| 2734 return ERROR_INT("pixa not defined", __func__, 1); | |
| 2735 | |
| 2736 n = pixaGetCount(pixa); | |
| 2737 hascolor = 0; | |
| 2738 for (i = 0; i < n; i++) { | |
| 2739 pix = pixaGetPix(pixa, i, L_CLONE); | |
| 2740 if ((cmap = pixGetColormap(pix)) != NULL) | |
| 2741 pixcmapHasColor(cmap, &hascolor); | |
| 2742 d = pixGetDepth(pix); | |
| 2743 pixDestroy(&pix); | |
| 2744 if (d == 32 || hascolor == 1) { | |
| 2745 *phascolor = 1; | |
| 2746 break; | |
| 2747 } | |
| 2748 } | |
| 2749 | |
| 2750 return 0; | |
| 2751 } | |
| 2752 | |
| 2753 | |
| 2754 /*! | |
| 2755 * \brief pixaAnyColormaps() | |
| 2756 * | |
| 2757 * \param[in] pixa | |
| 2758 * \param[out] phascmap 1 if any pix has a colormap; 0 otherwise | |
| 2759 * \return 0 if OK; 1 on error | |
| 2760 */ | |
| 2761 l_ok | |
| 2762 pixaAnyColormaps(PIXA *pixa, | |
| 2763 l_int32 *phascmap) | |
| 2764 { | |
| 2765 l_int32 i, n; | |
| 2766 PIX *pix; | |
| 2767 PIXCMAP *cmap; | |
| 2768 | |
| 2769 if (!phascmap) | |
| 2770 return ERROR_INT("&hascmap not defined", __func__, 1); | |
| 2771 *phascmap = 0; | |
| 2772 if (!pixa) | |
| 2773 return ERROR_INT("pixa not defined", __func__, 1); | |
| 2774 | |
| 2775 n = pixaGetCount(pixa); | |
| 2776 for (i = 0; i < n; i++) { | |
| 2777 pix = pixaGetPix(pixa, i, L_CLONE); | |
| 2778 cmap = pixGetColormap(pix); | |
| 2779 pixDestroy(&pix); | |
| 2780 if (cmap) { | |
| 2781 *phascmap = 1; | |
| 2782 return 0; | |
| 2783 } | |
| 2784 } | |
| 2785 | |
| 2786 return 0; | |
| 2787 } | |
| 2788 | |
| 2789 | |
| 2790 /*! | |
| 2791 * \brief pixaGetDepthInfo() | |
| 2792 * | |
| 2793 * \param[in] pixa | |
| 2794 * \param[out] pmaxdepth [optional] max pixel depth of pix in pixa | |
| 2795 * \param[out] psame [optional] true if all depths are equal | |
| 2796 * \return 0 if OK; 1 on error | |
| 2797 */ | |
| 2798 l_ok | |
| 2799 pixaGetDepthInfo(PIXA *pixa, | |
| 2800 l_int32 *pmaxdepth, | |
| 2801 l_int32 *psame) | |
| 2802 { | |
| 2803 l_int32 i, n, d, d0; | |
| 2804 l_int32 maxd, same; /* depth info */ | |
| 2805 | |
| 2806 if (pmaxdepth) *pmaxdepth = 0; | |
| 2807 if (psame) *psame = TRUE; | |
| 2808 if (!pmaxdepth && !psame) return 0; | |
| 2809 if (!pixa) | |
| 2810 return ERROR_INT("pixa not defined", __func__, 1); | |
| 2811 if ((n = pixaGetCount(pixa)) == 0) | |
| 2812 return ERROR_INT("pixa is empty", __func__, 1); | |
| 2813 | |
| 2814 same = TRUE; | |
| 2815 maxd = 0; | |
| 2816 for (i = 0; i < n; i++) { | |
| 2817 pixaGetPixDimensions(pixa, i, NULL, NULL, &d); | |
| 2818 if (i == 0) | |
| 2819 d0 = d; | |
| 2820 else if (d != d0) | |
| 2821 same = FALSE; | |
| 2822 if (d > maxd) maxd = d; | |
| 2823 } | |
| 2824 | |
| 2825 if (pmaxdepth) *pmaxdepth = maxd; | |
| 2826 if (psame) *psame = same; | |
| 2827 return 0; | |
| 2828 } | |
| 2829 | |
| 2830 | |
| 2831 /*! | |
| 2832 * \brief pixaConvertToSameDepth() | |
| 2833 * | |
| 2834 * \param[in] pixas | |
| 2835 * \return pixad, or NULL on error | |
| 2836 * | |
| 2837 * <pre> | |
| 2838 * Notes: | |
| 2839 * (1) Any pix with 16 bpp will be converted to 8 bpp. | |
| 2840 * If all pix have bpp = 1, the output depth will be 1. | |
| 2841 * If any pix has color (either rgb or a colormap with color), | |
| 2842 * the output depth will be 32. | |
| 2843 * Otherwise, the output depth is the maximum of 8 and the | |
| 2844 * the max depth of all the pix. | |
| 2845 * (2) This can be used to allow lossless rendering onto | |
| 2846 * a single pix. (Except: 16 bpp gets converted to 8.) | |
| 2847 * </pre> | |
| 2848 */ | |
| 2849 PIXA * | |
| 2850 pixaConvertToSameDepth(PIXA *pixas) | |
| 2851 { | |
| 2852 l_int32 i, n, depth, same, hascmap, maxdepth; | |
| 2853 BOXA *boxa; | |
| 2854 PIX *pix1, *pix2; | |
| 2855 PIXA *pixa1, *pixad; | |
| 2856 | |
| 2857 if (!pixas) | |
| 2858 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 2859 if ((n = pixaGetCount(pixas)) == 0) | |
| 2860 return (PIXA *)ERROR_PTR("no components", __func__, NULL); | |
| 2861 | |
| 2862 | |
| 2863 /* Remove colormaps if necessary */ | |
| 2864 pixaGetRenderingDepth(pixas, &depth); | |
| 2865 pixaAnyColormaps(pixas, &hascmap); | |
| 2866 if (hascmap) { | |
| 2867 pixa1 = pixaCreate(n); | |
| 2868 for (i = 0; i < n; i++) { | |
| 2869 pix1 = pixaGetPix(pixas, i, L_CLONE); | |
| 2870 if (depth == 32) | |
| 2871 pix2 = pixConvertTo32(pix1); | |
| 2872 else /* depth = 8 */ | |
| 2873 pix2 = pixConvertTo8(pix1, 0); | |
| 2874 pixaAddPix(pixa1, pix2, L_INSERT); | |
| 2875 pixDestroy(&pix1); | |
| 2876 } | |
| 2877 } else { | |
| 2878 pixa1 = pixaCopy(pixas, L_CLONE); | |
| 2879 } | |
| 2880 | |
| 2881 pixaGetDepthInfo(pixa1, &maxdepth, &same); | |
| 2882 if (!same) { /* at least one pix has depth < maxdepth */ | |
| 2883 pixad = pixaCreate(n); | |
| 2884 for (i = 0; i < n; i++) { | |
| 2885 pix1 = pixaGetPix(pixa1, i, L_CLONE); | |
| 2886 if (maxdepth <= 16) | |
| 2887 pix2 = pixConvertTo8(pix1, 0); | |
| 2888 else | |
| 2889 pix2 = pixConvertTo32(pix1); | |
| 2890 pixaAddPix(pixad, pix2, L_INSERT); | |
| 2891 pixDestroy(&pix1); | |
| 2892 } | |
| 2893 } else { | |
| 2894 pixad = pixaCopy(pixa1, L_CLONE); | |
| 2895 } | |
| 2896 | |
| 2897 boxa = pixaGetBoxa(pixas, L_COPY); | |
| 2898 pixaSetBoxa(pixad, boxa, L_INSERT); | |
| 2899 pixaDestroy(&pixa1); | |
| 2900 return pixad; | |
| 2901 } | |
| 2902 | |
| 2903 | |
| 2904 /*! | |
| 2905 * \brief pixaConvertToGivenDepth() | |
| 2906 * | |
| 2907 * \param[in] pixas | |
| 2908 * \param[in] depth specify either 8 or 32 bpp | |
| 2909 * \return pixad, or NULL on error | |
| 2910 * | |
| 2911 * <pre> | |
| 2912 * Notes: | |
| 2913 * (1) Use this to remove any colormaps and convert all pix to either | |
| 2914 * 8 or 32 bpp. | |
| 2915 * (2) To convert losslessly, get %depth from pixaGetRenderingDepth(). | |
| 2916 * (3) Clone pix may be in the returned pixa if conversion is to 32 bpp. | |
| 2917 * </pre> | |
| 2918 */ | |
| 2919 PIXA * | |
| 2920 pixaConvertToGivenDepth(PIXA *pixas, | |
| 2921 l_int32 depth) | |
| 2922 { | |
| 2923 l_int32 i, n, maxd; | |
| 2924 BOXA *boxa; | |
| 2925 PIX *pix1, *pix2; | |
| 2926 PIXA *pixad; | |
| 2927 | |
| 2928 if (!pixas) | |
| 2929 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL); | |
| 2930 if ((n = pixaGetCount(pixas)) == 0) | |
| 2931 return (PIXA *)ERROR_PTR("no components", __func__, NULL); | |
| 2932 if (depth != 8 && depth != 32) | |
| 2933 return (PIXA *)ERROR_PTR("depth not 8 or 32", __func__, NULL); | |
| 2934 | |
| 2935 /* Warn with 1 --> {8,32} or lossy conversions */ | |
| 2936 pixaGetRenderingDepth(pixas, &maxd); | |
| 2937 if (maxd == 1) | |
| 2938 L_WARNING("All pix are 1 bpp; converting to %d bpp\n", __func__, depth); | |
| 2939 if (maxd > depth) | |
| 2940 L_WARNING("Lossy conversion: max rendering depth %d > input %d\n", | |
| 2941 __func__, maxd, depth); | |
| 2942 | |
| 2943 pixad = pixaCreate(n); | |
| 2944 for (i = 0; i < n; i++) { | |
| 2945 pix1 = pixaGetPix(pixas, i, L_CLONE); | |
| 2946 if (depth == 32) { | |
| 2947 pix2 = (pixGetDepth(pix1) == 32) ? pixClone(pix1) : | |
| 2948 pixConvertTo32(pix1); | |
| 2949 } else { /* depth = 8 */ | |
| 2950 pix2 = pixConvertTo8(pix1, 0); | |
| 2951 } | |
| 2952 pixaAddPix(pixad, pix2, L_INSERT); | |
| 2953 pixDestroy(&pix1); | |
| 2954 } | |
| 2955 | |
| 2956 boxa = pixaGetBoxa(pixas, L_COPY); | |
| 2957 pixaSetBoxa(pixad, boxa, L_INSERT); | |
| 2958 return pixad; | |
| 2959 } | |
| 2960 | |
| 2961 | |
| 2962 /*! | |
| 2963 * \brief pixaEqual() | |
| 2964 * | |
| 2965 * \param[in] pixa1 | |
| 2966 * \param[in] pixa2 | |
| 2967 * \param[in] maxdist | |
| 2968 * \param[out] pnaindex [optional] index array of correspondences | |
| 2969 * \param[out] psame 1 if equal; 0 otherwise | |
| 2970 * \return 0 if OK, 1 on error | |
| 2971 * | |
| 2972 * <pre> | |
| 2973 * Notes: | |
| 2974 * (1) The two pixa are the "same" if they contain the same | |
| 2975 * boxa and the same ordered set of pix. However, if they | |
| 2976 * have boxa, the pix in each pixa can differ in ordering | |
| 2977 * by an amount given by the parameter %maxdist. If they | |
| 2978 * don't have a boxa, the %maxdist parameter is ignored, | |
| 2979 * and the ordering of the pix must be identical. | |
| 2980 * (2) This applies only to boxa geometry, pixels and ordering; | |
| 2981 * other fields in the pix are ignored. | |
| 2982 * (3) naindex[i] gives the position of the box in pixa2 that | |
| 2983 * corresponds to box i in pixa1. It is only returned if the | |
| 2984 * pixa have boxa and the boxa are equal. | |
| 2985 * (4) In situations where the ordering is very different, so that | |
| 2986 * a large %maxdist is required for "equality", this should be | |
| 2987 * implemented with a hash function for efficiency. | |
| 2988 * </pre> | |
| 2989 */ | |
| 2990 l_ok | |
| 2991 pixaEqual(PIXA *pixa1, | |
| 2992 PIXA *pixa2, | |
| 2993 l_int32 maxdist, | |
| 2994 NUMA **pnaindex, | |
| 2995 l_int32 *psame) | |
| 2996 { | |
| 2997 l_int32 i, j, n, empty1, empty2, same, sameboxa; | |
| 2998 BOXA *boxa1, *boxa2; | |
| 2999 NUMA *na; | |
| 3000 PIX *pix1, *pix2; | |
| 3001 | |
| 3002 if (pnaindex) *pnaindex = NULL; | |
| 3003 if (!psame) | |
| 3004 return ERROR_INT("&same not defined", __func__, 1); | |
| 3005 *psame = 0; | |
| 3006 sameboxa = 0; | |
| 3007 na = NULL; | |
| 3008 if (!pixa1 || !pixa2) | |
| 3009 return ERROR_INT("pixa1 and pixa2 not both defined", __func__, 1); | |
| 3010 n = pixaGetCount(pixa1); | |
| 3011 if (n != pixaGetCount(pixa2)) | |
| 3012 return 0; | |
| 3013 | |
| 3014 /* If there are no boxes, strict ordering of the pix in each | |
| 3015 * pixa is required. */ | |
| 3016 boxa1 = pixaGetBoxa(pixa1, L_CLONE); | |
| 3017 boxa2 = pixaGetBoxa(pixa2, L_CLONE); | |
| 3018 empty1 = (boxaGetCount(boxa1) == 0) ? 1 : 0; | |
| 3019 empty2 = (boxaGetCount(boxa2) == 0) ? 1 : 0; | |
| 3020 if (!empty1 && !empty2) { | |
| 3021 boxaEqual(boxa1, boxa2, maxdist, &na, &sameboxa); | |
| 3022 if (!sameboxa) { | |
| 3023 boxaDestroy(&boxa1); | |
| 3024 boxaDestroy(&boxa2); | |
| 3025 numaDestroy(&na); | |
| 3026 return 0; | |
| 3027 } | |
| 3028 } | |
| 3029 boxaDestroy(&boxa1); | |
| 3030 boxaDestroy(&boxa2); | |
| 3031 if ((!empty1 && empty2) || (empty1 && !empty2)) | |
| 3032 return 0; | |
| 3033 | |
| 3034 for (i = 0; i < n; i++) { | |
| 3035 pix1 = pixaGetPix(pixa1, i, L_CLONE); | |
| 3036 if (na) | |
| 3037 numaGetIValue(na, i, &j); | |
| 3038 else | |
| 3039 j = i; | |
| 3040 pix2 = pixaGetPix(pixa2, j, L_CLONE); | |
| 3041 pixEqual(pix1, pix2, &same); | |
| 3042 pixDestroy(&pix1); | |
| 3043 pixDestroy(&pix2); | |
| 3044 if (!same) { | |
| 3045 numaDestroy(&na); | |
| 3046 return 0; | |
| 3047 } | |
| 3048 } | |
| 3049 | |
| 3050 *psame = 1; | |
| 3051 if (pnaindex) | |
| 3052 *pnaindex = na; | |
| 3053 else | |
| 3054 numaDestroy(&na); | |
| 3055 return 0; | |
| 3056 } | |
| 3057 | |
| 3058 | |
| 3059 /*! | |
| 3060 * \brief pixaSetFullSizeBoxa() | |
| 3061 * | |
| 3062 * \param[in] pixa | |
| 3063 * \return 0 if OK, 1 on error | |
| 3064 * | |
| 3065 * <pre> | |
| 3066 * Notes: | |
| 3067 * (1) Replaces the existing boxa. Each box gives the dimensions | |
| 3068 * of the corresponding pix. This is needed for functions | |
| 3069 * like pixaSort() that sort based on the boxes. | |
| 3070 * </pre> | |
| 3071 */ | |
| 3072 l_ok | |
| 3073 pixaSetFullSizeBoxa(PIXA *pixa) | |
| 3074 { | |
| 3075 l_int32 i, n, w, h; | |
| 3076 BOX *box; | |
| 3077 BOXA *boxa; | |
| 3078 PIX *pix; | |
| 3079 | |
| 3080 if (!pixa) | |
| 3081 return ERROR_INT("pixa not defined", __func__, 1); | |
| 3082 if ((n = pixaGetCount(pixa)) == 0) { | |
| 3083 L_INFO("pixa contains no pix\n", __func__); | |
| 3084 return 0; | |
| 3085 } | |
| 3086 | |
| 3087 boxa = boxaCreate(n); | |
| 3088 pixaSetBoxa(pixa, boxa, L_INSERT); | |
| 3089 for (i = 0; i < n; i++) { | |
| 3090 pix = pixaGetPix(pixa, i, L_CLONE); | |
| 3091 pixGetDimensions(pix, &w, &h, NULL); | |
| 3092 box = boxCreate(0, 0, w, h); | |
| 3093 boxaAddBox(boxa, box, L_INSERT); | |
| 3094 pixDestroy(&pix); | |
| 3095 } | |
| 3096 return 0; | |
| 3097 } | |
| 3098 |
