Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/pix3.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 pix3.c | |
| 29 * <pre> | |
| 30 * | |
| 31 * This file has these operations: | |
| 32 * | |
| 33 * (1) Mask-directed operations | |
| 34 * (2) Full-image bit-logical operations | |
| 35 * (3) Foreground pixel counting operations on 1 bpp images | |
| 36 * (4) Average and variance of pixel values | |
| 37 * (5) Mirrored tiling of a smaller image | |
| 38 * | |
| 39 * | |
| 40 * Masked operations | |
| 41 * l_int32 pixSetMasked() | |
| 42 * l_int32 pixSetMaskedGeneral() | |
| 43 * l_int32 pixCombineMasked() | |
| 44 * l_int32 pixCombineMaskedGeneral() | |
| 45 * l_int32 pixPaintThroughMask() | |
| 46 * l_int32 pixCopyWithBoxa() -- this is boxa-directed | |
| 47 * PIX *pixPaintSelfThroughMask() | |
| 48 * PIX *pixMakeMaskFromVal() | |
| 49 * PIX *pixMakeMaskFromLUT() | |
| 50 * PIX *pixMakeArbMaskFromRGB() | |
| 51 * PIX *pixSetUnderTransparency() | |
| 52 * PIX *pixMakeAlphaFromMask() | |
| 53 * l_int32 pixGetColorNearMaskBoundary() | |
| 54 * PIX *pixDisplaySelectedPixels() -- for debugging | |
| 55 * | |
| 56 * One and two-image boolean operations on arbitrary depth images | |
| 57 * PIX *pixInvert() | |
| 58 * PIX *pixOr() | |
| 59 * PIX *pixAnd() | |
| 60 * PIX *pixXor() | |
| 61 * PIX *pixSubtract() | |
| 62 * | |
| 63 * Foreground pixel counting in 1 bpp images | |
| 64 * l_int32 pixZero() | |
| 65 * l_int32 pixForegroundFraction() | |
| 66 * NUMA *pixaCountPixels() | |
| 67 * l_int32 pixCountPixels() | |
| 68 * l_int32 pixCountPixelsInRect() | |
| 69 * NUMA *pixCountByRow() | |
| 70 * NUMA *pixCountByColumn() | |
| 71 * NUMA *pixCountPixelsByRow() | |
| 72 * NUMA *pixCountPixelsByColumn() | |
| 73 * l_int32 pixCountPixelsInRow() | |
| 74 * NUMA *pixGetMomentByColumn() | |
| 75 * l_int32 pixThresholdPixelSum() | |
| 76 * l_int32 *makePixelSumTab8() | |
| 77 * l_int32 *makePixelCentroidTab8() | |
| 78 * | |
| 79 * Average of pixel values in gray images | |
| 80 * NUMA *pixAverageByRow() | |
| 81 * NUMA *pixAverageByColumn() | |
| 82 * l_int32 pixAverageInRect() | |
| 83 * | |
| 84 * Average of pixel values in RGB images | |
| 85 * l_int32 pixAverageInRectRGB() | |
| 86 * | |
| 87 * Variance of pixel values in gray images | |
| 88 * NUMA *pixVarianceByRow() | |
| 89 * NUMA *pixVarianceByColumn() | |
| 90 * l_int32 pixVarianceInRect() | |
| 91 * | |
| 92 * Average of absolute value of pixel differences in gray images | |
| 93 * NUMA *pixAbsDiffByRow() | |
| 94 * NUMA *pixAbsDiffByColumn() | |
| 95 * l_int32 pixAbsDiffInRect() | |
| 96 * l_int32 pixAbsDiffOnLine() | |
| 97 * | |
| 98 * Count of pixels with specific value | |
| 99 * l_int32 pixCountArbInRect() | |
| 100 * | |
| 101 * Mirrored tiling | |
| 102 * PIX *pixMirroredTiling() | |
| 103 * | |
| 104 * Representative tile near but outside region | |
| 105 * l_int32 pixFindRepCloseTile() | |
| 106 * | |
| 107 * Static helper function | |
| 108 * static BOXA *findTileRegionsForSearch() | |
| 109 * </pre> | |
| 110 */ | |
| 111 | |
| 112 #ifdef HAVE_CONFIG_H | |
| 113 #include <config_auto.h> | |
| 114 #endif /* HAVE_CONFIG_H */ | |
| 115 | |
| 116 #include <string.h> | |
| 117 #include <math.h> | |
| 118 #include "allheaders.h" | |
| 119 | |
| 120 static BOXA *findTileRegionsForSearch(BOX *box, l_int32 w, l_int32 h, | |
| 121 l_int32 searchdir, l_int32 mindist, | |
| 122 l_int32 tsize, l_int32 ntiles); | |
| 123 | |
| 124 #ifndef NO_CONSOLE_IO | |
| 125 #define EQUAL_SIZE_WARNING 0 | |
| 126 #endif /* ~NO_CONSOLE_IO */ | |
| 127 | |
| 128 /*-------------------------------------------------------------* | |
| 129 * Masked operations * | |
| 130 *-------------------------------------------------------------*/ | |
| 131 /*! | |
| 132 * \brief pixSetMasked() | |
| 133 * | |
| 134 * \param[in] pixd 1, 2, 4, 8, 16 or 32 bpp; or colormapped | |
| 135 * \param[in] pixm [optional] 1 bpp mask; no operation if NULL | |
| 136 * \param[in] val value to set at each masked pixel | |
| 137 * \return 0 if OK; 1 on error | |
| 138 * | |
| 139 * <pre> | |
| 140 * Notes: | |
| 141 * (1) In-place operation. | |
| 142 * (2) NOTE: For cmapped images, this calls pixSetMaskedCmap(). | |
| 143 * %val must be the 32-bit color representation of the RGB pixel. | |
| 144 * It is not the index into the colormap! | |
| 145 * (2) If pixm == NULL, a warning is given. | |
| 146 * (3) This is an implicitly aligned operation, where the UL | |
| 147 * corners of pixd and pixm coincide. A warning is | |
| 148 * issued if the two image sizes differ significantly, | |
| 149 * but the operation proceeds. | |
| 150 * (4) Each pixel in pixd that co-locates with an ON pixel | |
| 151 * in pixm is set to the specified input value. | |
| 152 * Other pixels in pixd are not changed. | |
| 153 * (5) You can visualize this as painting the color through | |
| 154 * the mask, as a stencil. | |
| 155 * (6) If you do not want to have the UL corners aligned, | |
| 156 * use the function pixSetMaskedGeneral(), which requires | |
| 157 * you to input the UL corner of pixm relative to pixd. | |
| 158 * (7) Implementation details: see comments in pixPaintThroughMask() | |
| 159 * for when we use rasterop to do the painting. | |
| 160 * </pre> | |
| 161 */ | |
| 162 l_ok | |
| 163 pixSetMasked(PIX *pixd, | |
| 164 PIX *pixm, | |
| 165 l_uint32 val) | |
| 166 { | |
| 167 l_int32 wd, hd, wm, hm, w, h, d, wpld, wplm; | |
| 168 l_int32 i, j, rval, gval, bval; | |
| 169 l_uint32 *datad, *datam, *lined, *linem; | |
| 170 | |
| 171 if (!pixd) | |
| 172 return ERROR_INT("pixd not defined", __func__, 1); | |
| 173 if (!pixm) { | |
| 174 L_WARNING("no mask; nothing to do\n", __func__); | |
| 175 return 0; | |
| 176 } | |
| 177 if (pixGetColormap(pixd)) { | |
| 178 extractRGBValues(val, &rval, &gval, &bval); | |
| 179 return pixSetMaskedCmap(pixd, pixm, 0, 0, rval, gval, bval); | |
| 180 } | |
| 181 | |
| 182 if (pixGetDepth(pixm) != 1) | |
| 183 return ERROR_INT("pixm not 1 bpp", __func__, 1); | |
| 184 d = pixGetDepth(pixd); | |
| 185 if (d == 1) | |
| 186 val &= 1; | |
| 187 else if (d == 2) | |
| 188 val &= 3; | |
| 189 else if (d == 4) | |
| 190 val &= 0x0f; | |
| 191 else if (d == 8) | |
| 192 val &= 0xff; | |
| 193 else if (d == 16) | |
| 194 val &= 0xffff; | |
| 195 else if (d != 32) | |
| 196 return ERROR_INT("pixd not 1, 2, 4, 8, 16 or 32 bpp", __func__, 1); | |
| 197 pixGetDimensions(pixm, &wm, &hm, NULL); | |
| 198 | |
| 199 /* If d == 1, use rasterop; it's about 25x faster */ | |
| 200 if (d == 1) { | |
| 201 if (val == 0) { | |
| 202 PIX *pixmi = pixInvert(NULL, pixm); | |
| 203 pixRasterop(pixd, 0, 0, wm, hm, PIX_MASK, pixmi, 0, 0); | |
| 204 pixDestroy(&pixmi); | |
| 205 } else { /* val == 1 */ | |
| 206 pixRasterop(pixd, 0, 0, wm, hm, PIX_PAINT, pixm, 0, 0); | |
| 207 } | |
| 208 return 0; | |
| 209 } | |
| 210 | |
| 211 /* For d < 32, use rasterop for val == 0 (black); ~3x faster. */ | |
| 212 if (d < 32 && val == 0) { | |
| 213 PIX *pixmd = pixUnpackBinary(pixm, d, 1); | |
| 214 pixRasterop(pixd, 0, 0, wm, hm, PIX_MASK, pixmd, 0, 0); | |
| 215 pixDestroy(&pixmd); | |
| 216 return 0; | |
| 217 } | |
| 218 | |
| 219 /* For d < 32, use rasterop for val == maxval (white); ~3x faster. */ | |
| 220 if (d < 32 && val == ((1 << d) - 1)) { | |
| 221 PIX *pixmd = pixUnpackBinary(pixm, d, 0); | |
| 222 pixRasterop(pixd, 0, 0, wm, hm, PIX_PAINT, pixmd, 0, 0); | |
| 223 pixDestroy(&pixmd); | |
| 224 return 0; | |
| 225 } | |
| 226 | |
| 227 pixGetDimensions(pixd, &wd, &hd, &d); | |
| 228 w = L_MIN(wd, wm); | |
| 229 h = L_MIN(hd, hm); | |
| 230 if (L_ABS(wd - wm) > 7 || L_ABS(hd - hm) > 7) /* allow a small tolerance */ | |
| 231 L_WARNING("pixd and pixm sizes differ\n", __func__); | |
| 232 | |
| 233 datad = pixGetData(pixd); | |
| 234 datam = pixGetData(pixm); | |
| 235 wpld = pixGetWpl(pixd); | |
| 236 wplm = pixGetWpl(pixm); | |
| 237 for (i = 0; i < h; i++) { | |
| 238 lined = datad + i * wpld; | |
| 239 linem = datam + i * wplm; | |
| 240 for (j = 0; j < w; j++) { | |
| 241 if (GET_DATA_BIT(linem, j)) { | |
| 242 switch(d) | |
| 243 { | |
| 244 case 2: | |
| 245 SET_DATA_DIBIT(lined, j, val); | |
| 246 break; | |
| 247 case 4: | |
| 248 SET_DATA_QBIT(lined, j, val); | |
| 249 break; | |
| 250 case 8: | |
| 251 SET_DATA_BYTE(lined, j, val); | |
| 252 break; | |
| 253 case 16: | |
| 254 SET_DATA_TWO_BYTES(lined, j, val); | |
| 255 break; | |
| 256 case 32: | |
| 257 *(lined + j) = val; | |
| 258 break; | |
| 259 default: | |
| 260 return ERROR_INT("shouldn't get here", __func__, 1); | |
| 261 } | |
| 262 } | |
| 263 } | |
| 264 } | |
| 265 | |
| 266 return 0; | |
| 267 } | |
| 268 | |
| 269 | |
| 270 /*! | |
| 271 * \brief pixSetMaskedGeneral() | |
| 272 * | |
| 273 * \param[in] pixd 8, 16 or 32 bpp | |
| 274 * \param[in] pixm [optional] 1 bpp mask; no operation if null | |
| 275 * \param[in] val value to set at each masked pixel | |
| 276 * \param[in] x, y location of UL corner of pixm relative to pixd; | |
| 277 * can be negative | |
| 278 * \return 0 if OK; 1 on error | |
| 279 * | |
| 280 * <pre> | |
| 281 * Notes: | |
| 282 * (1) This is an in-place operation. | |
| 283 * (2) Alignment is explicit. If you want the UL corners of | |
| 284 * the two images to be aligned, use pixSetMasked(). | |
| 285 * (3) A typical use would be painting through the foreground | |
| 286 * of a small binary mask pixm, located somewhere on a | |
| 287 * larger pixd. Other pixels in pixd are not changed. | |
| 288 * (4) You can visualize this as painting the color through | |
| 289 * the mask, as a stencil. | |
| 290 * (5) This uses rasterop to handle clipping and different depths of pixd. | |
| 291 * (6) If pixd has a colormap, you should call pixPaintThroughMask(). | |
| 292 * (7) Why is this function here, if pixPaintThroughMask() does the | |
| 293 * same thing, and does it more generally? I've retained it here | |
| 294 * to show how one can paint through a mask using only full | |
| 295 * image rasterops, rather than pixel peeking in pixm and poking | |
| 296 * in pixd. It's somewhat baroque, but I found it amusing. | |
| 297 * </pre> | |
| 298 */ | |
| 299 l_ok | |
| 300 pixSetMaskedGeneral(PIX *pixd, | |
| 301 PIX *pixm, | |
| 302 l_uint32 val, | |
| 303 l_int32 x, | |
| 304 l_int32 y) | |
| 305 { | |
| 306 l_int32 wm, hm, d; | |
| 307 PIX *pixmu, *pixc; | |
| 308 | |
| 309 if (!pixd) | |
| 310 return ERROR_INT("pixd not defined", __func__, 1); | |
| 311 if (!pixm) /* nothing to do */ | |
| 312 return 0; | |
| 313 | |
| 314 d = pixGetDepth(pixd); | |
| 315 if (d != 8 && d != 16 && d != 32) | |
| 316 return ERROR_INT("pixd not 8, 16 or 32 bpp", __func__, 1); | |
| 317 if (pixGetDepth(pixm) != 1) | |
| 318 return ERROR_INT("pixm not 1 bpp", __func__, 1); | |
| 319 | |
| 320 /* Unpack binary to depth d, with inversion: 1 --> 0, 0 --> 0xff... */ | |
| 321 if ((pixmu = pixUnpackBinary(pixm, d, 1)) == NULL) | |
| 322 return ERROR_INT("pixmu not made", __func__, 1); | |
| 323 | |
| 324 /* Clear stenciled pixels in pixd */ | |
| 325 pixGetDimensions(pixm, &wm, &hm, NULL); | |
| 326 pixRasterop(pixd, x, y, wm, hm, PIX_SRC & PIX_DST, pixmu, 0, 0); | |
| 327 | |
| 328 /* Generate image with requisite color */ | |
| 329 if ((pixc = pixCreateTemplate(pixmu)) == NULL) { | |
| 330 pixDestroy(&pixmu); | |
| 331 return ERROR_INT("pixc not made", __func__, 1); | |
| 332 } | |
| 333 pixSetAllArbitrary(pixc, val); | |
| 334 | |
| 335 /* Invert stencil mask, and paint color color into stencil */ | |
| 336 pixInvert(pixmu, pixmu); | |
| 337 pixAnd(pixmu, pixmu, pixc); | |
| 338 | |
| 339 /* Finally, repaint stenciled pixels, with val, in pixd */ | |
| 340 pixRasterop(pixd, x, y, wm, hm, PIX_SRC | PIX_DST, pixmu, 0, 0); | |
| 341 | |
| 342 pixDestroy(&pixmu); | |
| 343 pixDestroy(&pixc); | |
| 344 return 0; | |
| 345 } | |
| 346 | |
| 347 | |
| 348 /*! | |
| 349 * \brief pixCombineMasked() | |
| 350 * | |
| 351 * \param[in] pixd 1 bpp, 8 bpp gray or 32 bpp rgb; no cmap | |
| 352 * \param[in] pixs 1 bpp, 8 bpp gray or 32 bpp rgb; no cmap | |
| 353 * \param[in] pixm [optional] 1 bpp mask; no operation if NULL | |
| 354 * \return 0 if OK; 1 on error | |
| 355 * | |
| 356 * <pre> | |
| 357 * Notes: | |
| 358 * (1) In-place operation; pixd is changed. | |
| 359 * (2) This sets each pixel in pixd that co-locates with an ON | |
| 360 * pixel in pixm to the corresponding value of pixs. | |
| 361 * (3) pixs and pixd must be the same depth and not colormapped. | |
| 362 * (4) All three input pix are aligned at the UL corner, and the | |
| 363 * operation is clipped to the intersection of all three images. | |
| 364 * (5) If pixm == NULL, it's a no-op. | |
| 365 * (6) Implementation: see notes in pixCombineMaskedGeneral(). | |
| 366 * For 8 bpp selective masking, you might guess that it | |
| 367 * would be faster to generate an 8 bpp version of pixm, | |
| 368 * using pixConvert1To8(pixm, 0, 255), and then use a | |
| 369 * general combine operation | |
| 370 * d = (d & ~m) | (s & m) | |
| 371 * on a word-by-word basis. Not always. The word-by-word | |
| 372 * combine takes a time that is independent of the mask data. | |
| 373 * If the mask is relatively sparse, the byte-check method | |
| 374 * is actually faster! | |
| 375 * </pre> | |
| 376 */ | |
| 377 l_ok | |
| 378 pixCombineMasked(PIX *pixd, | |
| 379 PIX *pixs, | |
| 380 PIX *pixm) | |
| 381 { | |
| 382 l_int32 w, h, d, ws, hs, ds, wm, hm, dm, wmin, hmin; | |
| 383 l_int32 wpl, wpls, wplm, i, j, val; | |
| 384 l_uint32 *data, *datas, *datam, *line, *lines, *linem; | |
| 385 PIX *pixt; | |
| 386 | |
| 387 if (!pixm) /* nothing to do */ | |
| 388 return 0; | |
| 389 if (!pixd) | |
| 390 return ERROR_INT("pixd not defined", __func__, 1); | |
| 391 if (!pixs) | |
| 392 return ERROR_INT("pixs not defined", __func__, 1); | |
| 393 pixGetDimensions(pixd, &w, &h, &d); | |
| 394 pixGetDimensions(pixs, &ws, &hs, &ds); | |
| 395 pixGetDimensions(pixm, &wm, &hm, &dm); | |
| 396 if (d != ds) | |
| 397 return ERROR_INT("pixs and pixd depths differ", __func__, 1); | |
| 398 if (dm != 1) | |
| 399 return ERROR_INT("pixm not 1 bpp", __func__, 1); | |
| 400 if (d != 1 && d != 8 && d != 32) | |
| 401 return ERROR_INT("pixd not 1, 8 or 32 bpp", __func__, 1); | |
| 402 if (pixGetColormap(pixd) || pixGetColormap(pixs)) | |
| 403 return ERROR_INT("pixs and/or pixd is cmapped", __func__, 1); | |
| 404 | |
| 405 /* For d = 1, use rasterop. pixt is the part from pixs, under | |
| 406 * the fg of pixm, that is to be combined with pixd. We also | |
| 407 * use pixt to remove all fg of pixd that is under the fg of pixm. | |
| 408 * Then pixt and pixd are combined by ORing. */ | |
| 409 wmin = L_MIN(w, L_MIN(ws, wm)); | |
| 410 hmin = L_MIN(h, L_MIN(hs, hm)); | |
| 411 if (d == 1) { | |
| 412 pixt = pixAnd(NULL, pixs, pixm); | |
| 413 pixRasterop(pixd, 0, 0, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC), | |
| 414 pixm, 0, 0); | |
| 415 pixRasterop(pixd, 0, 0, wmin, hmin, PIX_SRC | PIX_DST, pixt, 0, 0); | |
| 416 pixDestroy(&pixt); | |
| 417 return 0; | |
| 418 } | |
| 419 | |
| 420 data = pixGetData(pixd); | |
| 421 datas = pixGetData(pixs); | |
| 422 datam = pixGetData(pixm); | |
| 423 wpl = pixGetWpl(pixd); | |
| 424 wpls = pixGetWpl(pixs); | |
| 425 wplm = pixGetWpl(pixm); | |
| 426 if (d == 8) { | |
| 427 for (i = 0; i < hmin; i++) { | |
| 428 line = data + i * wpl; | |
| 429 lines = datas + i * wpls; | |
| 430 linem = datam + i * wplm; | |
| 431 for (j = 0; j < wmin; j++) { | |
| 432 if (GET_DATA_BIT(linem, j)) { | |
| 433 val = GET_DATA_BYTE(lines, j); | |
| 434 SET_DATA_BYTE(line, j, val); | |
| 435 } | |
| 436 } | |
| 437 } | |
| 438 } else { /* d == 32 */ | |
| 439 for (i = 0; i < hmin; i++) { | |
| 440 line = data + i * wpl; | |
| 441 lines = datas + i * wpls; | |
| 442 linem = datam + i * wplm; | |
| 443 for (j = 0; j < wmin; j++) { | |
| 444 if (GET_DATA_BIT(linem, j)) | |
| 445 line[j] = lines[j]; | |
| 446 } | |
| 447 } | |
| 448 } | |
| 449 | |
| 450 return 0; | |
| 451 } | |
| 452 | |
| 453 | |
| 454 /*! | |
| 455 * \brief pixCombineMaskedGeneral() | |
| 456 * | |
| 457 * \param[in] pixd 1 bpp, 8 bpp gray or 32 bpp rgb | |
| 458 * \param[in] pixs 1 bpp, 8 bpp gray or 32 bpp rgb | |
| 459 * \param[in] pixm [optional] 1 bpp mask | |
| 460 * \param[in] x, y origin of pixs and pixm relative to pixd; can be negative | |
| 461 * \return 0 if OK; 1 on error | |
| 462 * | |
| 463 * <pre> | |
| 464 * Notes: | |
| 465 * (1) In-place operation; pixd is changed. | |
| 466 * (2) This is a generalized version of pixCombinedMasked(), where | |
| 467 * the source and mask can be placed at the same (arbitrary) | |
| 468 * location relative to pixd. | |
| 469 * (3) pixs and pixd must be the same depth and not colormapped. | |
| 470 * (4) The UL corners of both pixs and pixm are aligned with | |
| 471 * the point (x, y) of pixd, and the operation is clipped to | |
| 472 * the intersection of all three images. | |
| 473 * (5) If pixm == NULL, it's a no-op. | |
| 474 * (6) Implementation. There are two ways to do these. In the first, | |
| 475 * we use rasterop, ORing the part of pixs under the mask | |
| 476 * with pixd (which has been appropriately cleared there first). | |
| 477 * In the second, the mask is used one pixel at a time to | |
| 478 * selectively replace pixels of pixd with those of pixs. | |
| 479 * Here, we use rasterop for 1 bpp and pixel-wise replacement | |
| 480 * for 8 and 32 bpp. To use rasterop for 8 bpp, for example, | |
| 481 * we must first generate an 8 bpp version of the mask. | |
| 482 * The code is simple: | |
| 483 * | |
| 484 * Pix *pixm8 = pixConvert1To8(NULL, pixm, 0, 255); | |
| 485 * Pix *pixt = pixAnd(NULL, pixs, pixm8); | |
| 486 * pixRasterop(pixd, x, y, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC), | |
| 487 * pixm8, 0, 0); | |
| 488 * pixRasterop(pixd, x, y, wmin, hmin, PIX_SRC | PIX_DST, | |
| 489 * pixt, 0, 0); | |
| 490 * pixDestroy(&pixt); | |
| 491 * pixDestroy(&pixm8); | |
| 492 * </pre> | |
| 493 */ | |
| 494 l_ok | |
| 495 pixCombineMaskedGeneral(PIX *pixd, | |
| 496 PIX *pixs, | |
| 497 PIX *pixm, | |
| 498 l_int32 x, | |
| 499 l_int32 y) | |
| 500 { | |
| 501 l_int32 d, w, h, ws, hs, ds, wm, hm, dm, wmin, hmin; | |
| 502 l_int32 wpl, wpls, wplm, i, j, val; | |
| 503 l_uint32 *data, *datas, *datam, *line, *lines, *linem; | |
| 504 PIX *pixt; | |
| 505 | |
| 506 if (!pixm) /* nothing to do */ | |
| 507 return 0; | |
| 508 if (!pixd) | |
| 509 return ERROR_INT("pixd not defined", __func__, 1); | |
| 510 if (!pixs) | |
| 511 return ERROR_INT("pixs not defined", __func__, 1); | |
| 512 pixGetDimensions(pixd, &w, &h, &d); | |
| 513 pixGetDimensions(pixs, &ws, &hs, &ds); | |
| 514 pixGetDimensions(pixm, &wm, &hm, &dm); | |
| 515 if (d != ds) | |
| 516 return ERROR_INT("pixs and pixd depths differ", __func__, 1); | |
| 517 if (dm != 1) | |
| 518 return ERROR_INT("pixm not 1 bpp", __func__, 1); | |
| 519 if (d != 1 && d != 8 && d != 32) | |
| 520 return ERROR_INT("pixd not 1, 8 or 32 bpp", __func__, 1); | |
| 521 if (pixGetColormap(pixd) || pixGetColormap(pixs)) | |
| 522 return ERROR_INT("pixs and/or pixd is cmapped", __func__, 1); | |
| 523 | |
| 524 /* For d = 1, use rasterop. pixt is the part from pixs, under | |
| 525 * the fg of pixm, that is to be combined with pixd. We also | |
| 526 * use pixt to remove all fg of pixd that is under the fg of pixm. | |
| 527 * Then pixt and pixd are combined by ORing. */ | |
| 528 wmin = L_MIN(ws, wm); | |
| 529 hmin = L_MIN(hs, hm); | |
| 530 if (d == 1) { | |
| 531 pixt = pixAnd(NULL, pixs, pixm); | |
| 532 pixRasterop(pixd, x, y, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC), | |
| 533 pixm, 0, 0); | |
| 534 pixRasterop(pixd, x, y, wmin, hmin, PIX_SRC | PIX_DST, pixt, 0, 0); | |
| 535 pixDestroy(&pixt); | |
| 536 return 0; | |
| 537 } | |
| 538 | |
| 539 wpl = pixGetWpl(pixd); | |
| 540 data = pixGetData(pixd); | |
| 541 wpls = pixGetWpl(pixs); | |
| 542 datas = pixGetData(pixs); | |
| 543 wplm = pixGetWpl(pixm); | |
| 544 datam = pixGetData(pixm); | |
| 545 | |
| 546 for (i = 0; i < hmin; i++) { | |
| 547 if (y + i < 0 || y + i >= h) continue; | |
| 548 line = data + (y + i) * wpl; | |
| 549 lines = datas + i * wpls; | |
| 550 linem = datam + i * wplm; | |
| 551 for (j = 0; j < wmin; j++) { | |
| 552 if (x + j < 0 || x + j >= w) continue; | |
| 553 if (GET_DATA_BIT(linem, j)) { | |
| 554 switch (d) | |
| 555 { | |
| 556 case 8: | |
| 557 val = GET_DATA_BYTE(lines, j); | |
| 558 SET_DATA_BYTE(line, x + j, val); | |
| 559 break; | |
| 560 case 32: | |
| 561 *(line + x + j) = *(lines + j); | |
| 562 break; | |
| 563 default: | |
| 564 return ERROR_INT("shouldn't get here", __func__, 1); | |
| 565 } | |
| 566 } | |
| 567 } | |
| 568 } | |
| 569 | |
| 570 return 0; | |
| 571 } | |
| 572 | |
| 573 | |
| 574 /*! | |
| 575 * \brief pixPaintThroughMask() | |
| 576 * | |
| 577 * \param[in] pixd 1, 2, 4, 8, 16 or 32 bpp; or colormapped | |
| 578 * \param[in] pixm [optional] 1 bpp mask | |
| 579 * \param[in] x, y origin of pixm relative to pixd; can be negative | |
| 580 * \param[in] val pixel value to set at each masked pixel | |
| 581 * \return 0 if OK; 1 on error | |
| 582 * | |
| 583 * <pre> | |
| 584 * Notes: | |
| 585 * (1) In-place operation. Calls pixSetMaskedCmap() for colormapped | |
| 586 * images. | |
| 587 * (2) For 1, 2, 4, 8 and 16 bpp gray, we take the appropriate | |
| 588 * number of least significant bits of val. | |
| 589 * (3) If pixm == NULL, it's a no-op. | |
| 590 * (4) The mask origin is placed at (x,y) on pixd, and the | |
| 591 * operation is clipped to the intersection of rectangles. | |
| 592 * (5) For rgb, the components in val are in the canonical locations, | |
| 593 * with red in location COLOR_RED, etc. | |
| 594 * (6) Implementation detail 1: | |
| 595 * For painting with val == 0 or val == maxval, you can use rasterop. | |
| 596 * If val == 0, invert the mask so that it's 0 over the region | |
| 597 * into which you want to write, and use PIX_SRC & PIX_DST to | |
| 598 * clear those pixels. To write with val = maxval (all 1's), | |
| 599 * use PIX_SRC | PIX_DST to set all bits under the mask. | |
| 600 * (7) Implementation detail 2: | |
| 601 * The rasterop trick can be used for depth > 1 as well. | |
| 602 * For val == 0, generate the mask for depth d from the binary | |
| 603 * mask using | |
| 604 * pixmd = pixUnpackBinary(pixm, d, 1); | |
| 605 * and use pixRasterop() with PIX_MASK. For val == maxval, | |
| 606 * pixmd = pixUnpackBinary(pixm, d, 0); | |
| 607 * and use pixRasterop() with PIX_PAINT. | |
| 608 * But note that if d == 32 bpp, it is about 3x faster to use | |
| 609 * the general implementation (not pixRasterop()). | |
| 610 * (8) Implementation detail 3: | |
| 611 * It might be expected that the switch in the inner loop will | |
| 612 * cause large branching delays and should be avoided. | |
| 613 * This is not the case, because the entrance is always the | |
| 614 * same and the compiler can correctly predict the jump. | |
| 615 * </pre> | |
| 616 */ | |
| 617 l_ok | |
| 618 pixPaintThroughMask(PIX *pixd, | |
| 619 PIX *pixm, | |
| 620 l_int32 x, | |
| 621 l_int32 y, | |
| 622 l_uint32 val) | |
| 623 { | |
| 624 l_int32 d, w, h, wm, hm, wpl, wplm, i, j, rval, gval, bval; | |
| 625 l_uint32 *data, *datam, *line, *linem; | |
| 626 | |
| 627 if (!pixm) /* nothing to do */ | |
| 628 return 0; | |
| 629 if (!pixd) | |
| 630 return ERROR_INT("pixd not defined", __func__, 1); | |
| 631 if (pixGetColormap(pixd)) { | |
| 632 extractRGBValues(val, &rval, &gval, &bval); | |
| 633 return pixSetMaskedCmap(pixd, pixm, x, y, rval, gval, bval); | |
| 634 } | |
| 635 | |
| 636 if (pixGetDepth(pixm) != 1) | |
| 637 return ERROR_INT("pixm not 1 bpp", __func__, 1); | |
| 638 d = pixGetDepth(pixd); | |
| 639 if (d == 1) | |
| 640 val &= 1; | |
| 641 else if (d == 2) | |
| 642 val &= 3; | |
| 643 else if (d == 4) | |
| 644 val &= 0x0f; | |
| 645 else if (d == 8) | |
| 646 val &= 0xff; | |
| 647 else if (d == 16) | |
| 648 val &= 0xffff; | |
| 649 else if (d != 32) | |
| 650 return ERROR_INT("pixd not 1, 2, 4, 8, 16 or 32 bpp", __func__, 1); | |
| 651 pixGetDimensions(pixm, &wm, &hm, NULL); | |
| 652 | |
| 653 /* If d == 1, use rasterop; it's about 25x faster. */ | |
| 654 if (d == 1) { | |
| 655 if (val == 0) { | |
| 656 PIX *pixmi = pixInvert(NULL, pixm); | |
| 657 pixRasterop(pixd, x, y, wm, hm, PIX_MASK, pixmi, 0, 0); | |
| 658 pixDestroy(&pixmi); | |
| 659 } else { /* val == 1 */ | |
| 660 pixRasterop(pixd, x, y, wm, hm, PIX_PAINT, pixm, 0, 0); | |
| 661 } | |
| 662 return 0; | |
| 663 } | |
| 664 | |
| 665 /* For d < 32, use rasterop if val == 0 (black); ~3x faster. */ | |
| 666 if (d < 32 && val == 0) { | |
| 667 PIX *pixmd = pixUnpackBinary(pixm, d, 1); | |
| 668 pixRasterop(pixd, x, y, wm, hm, PIX_MASK, pixmd, 0, 0); | |
| 669 pixDestroy(&pixmd); | |
| 670 return 0; | |
| 671 } | |
| 672 | |
| 673 /* For d < 32, use rasterop if val == maxval (white); ~3x faster. */ | |
| 674 if (d < 32 && val == ((1 << d) - 1)) { | |
| 675 PIX *pixmd = pixUnpackBinary(pixm, d, 0); | |
| 676 pixRasterop(pixd, x, y, wm, hm, PIX_PAINT, pixmd, 0, 0); | |
| 677 pixDestroy(&pixmd); | |
| 678 return 0; | |
| 679 } | |
| 680 | |
| 681 /* All other cases */ | |
| 682 pixGetDimensions(pixd, &w, &h, NULL); | |
| 683 wpl = pixGetWpl(pixd); | |
| 684 data = pixGetData(pixd); | |
| 685 wplm = pixGetWpl(pixm); | |
| 686 datam = pixGetData(pixm); | |
| 687 for (i = 0; i < hm; i++) { | |
| 688 if (y + i < 0 || y + i >= h) continue; | |
| 689 line = data + (y + i) * wpl; | |
| 690 linem = datam + i * wplm; | |
| 691 for (j = 0; j < wm; j++) { | |
| 692 if (x + j < 0 || x + j >= w) continue; | |
| 693 if (GET_DATA_BIT(linem, j)) { | |
| 694 switch (d) | |
| 695 { | |
| 696 case 2: | |
| 697 SET_DATA_DIBIT(line, x + j, val); | |
| 698 break; | |
| 699 case 4: | |
| 700 SET_DATA_QBIT(line, x + j, val); | |
| 701 break; | |
| 702 case 8: | |
| 703 SET_DATA_BYTE(line, x + j, val); | |
| 704 break; | |
| 705 case 16: | |
| 706 SET_DATA_TWO_BYTES(line, x + j, val); | |
| 707 break; | |
| 708 case 32: | |
| 709 *(line + x + j) = val; | |
| 710 break; | |
| 711 default: | |
| 712 return ERROR_INT("shouldn't get here", __func__, 1); | |
| 713 } | |
| 714 } | |
| 715 } | |
| 716 } | |
| 717 | |
| 718 return 0; | |
| 719 } | |
| 720 | |
| 721 | |
| 722 /*! | |
| 723 * \brief pixCopyWithBoxa() | |
| 724 * | |
| 725 * \param[in] pixs all depths; cmap ok | |
| 726 * \param[in] boxa e.g., from components of a photomask | |
| 727 * \param[in] background L_SET_WHITE or L_SET_BLACK | |
| 728 * \return pixd or NULL on error | |
| 729 * | |
| 730 * <pre> | |
| 731 * Notes: | |
| 732 * (1) Pixels from pixs are copied ("blitted") through each box into pixd. | |
| 733 * (2) Pixels not copied are preset to either white or black. | |
| 734 * (3) This fast and simple implementation can use rasterop because | |
| 735 * each region to be copied is rectangular. | |
| 736 * (4) A much slower implementation that doesn't use rasterop would make | |
| 737 * a 1 bpp mask from the boxa and then copy, pixel by pixel, | |
| 738 * through the mask: | |
| 739 * pixGetDimensions(pixs, &w, &h, NULL); | |
| 740 * pixm = pixCreate(w, h, 1); | |
| 741 * pixm = pixMaskBoxa(pixm, pixm, boxa); | |
| 742 * pixd = pixCreateTemplate(pixs); | |
| 743 * pixSetBlackOrWhite(pixd, background); | |
| 744 * pixCombineMasked(pixd, pixs, pixm); | |
| 745 * pixDestroy(&pixm); | |
| 746 * </pre> | |
| 747 */ | |
| 748 PIX * | |
| 749 pixCopyWithBoxa(PIX *pixs, | |
| 750 BOXA *boxa, | |
| 751 l_int32 background) | |
| 752 { | |
| 753 l_int32 i, n, x, y, w, h; | |
| 754 PIX *pixd; | |
| 755 | |
| 756 if (!pixs) | |
| 757 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 758 if (!boxa) | |
| 759 return (PIX *)ERROR_PTR("boxa not defined", __func__, NULL); | |
| 760 if (background != L_SET_WHITE && background != L_SET_BLACK) | |
| 761 return (PIX *)ERROR_PTR("invalid background", __func__, NULL); | |
| 762 | |
| 763 pixd = pixCreateTemplate(pixs); | |
| 764 pixSetBlackOrWhite(pixd, background); | |
| 765 n = boxaGetCount(boxa); | |
| 766 for (i = 0; i < n; i++) { | |
| 767 boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); | |
| 768 pixRasterop(pixd, x, y, w, h, PIX_SRC, pixs, x, y); | |
| 769 } | |
| 770 return pixd; | |
| 771 } | |
| 772 | |
| 773 | |
| 774 /*! | |
| 775 * \brief pixPaintSelfThroughMask() | |
| 776 * | |
| 777 * \param[in] pixd 8 bpp gray or 32 bpp rgb; not colormapped | |
| 778 * \param[in] pixm 1 bpp mask | |
| 779 * \param[in] x, y origin of pixm relative to pixd; must not be negative | |
| 780 * \param[in] searchdir L_HORIZ, L_VERT or L_BOTH_DIRECTIONS | |
| 781 * \param[in] mindist min distance of nearest tile edge to box; >= 0 | |
| 782 * \param[in] tilesize requested size for tiling; may be reduced | |
| 783 * \param[in] ntiles number of tiles tested in each row/column | |
| 784 * \param[in] distblend distance outside the fg used for blending with pixs | |
| 785 * \return 0 if OK; 1 on error | |
| 786 * | |
| 787 * <pre> | |
| 788 * Notes: | |
| 789 * (1) In-place operation; pixd is changed. | |
| 790 * (2) If pixm == NULL, it's a no-op. | |
| 791 * (3) The mask origin is placed at (x,y) on pixd, and the | |
| 792 * operation is clipped to the intersection of pixd and the | |
| 793 * fg of the mask. | |
| 794 * (4) %tsize is the the requested size for tiling. The actual | |
| 795 * actual size for each c.c. will be bounded by the minimum | |
| 796 * dimension of the c.c. | |
| 797 * (5) For %mindist, %searchdir and %ntiles, see pixFindRepCloseTile(). | |
| 798 * They determine the set of possible tiles that can be used | |
| 799 * to build a larger mirrored tile to paint onto pixd through | |
| 800 * the c.c. of pixm. | |
| 801 * (6) %distblend is used for alpha blending. It is only applied | |
| 802 * if there is exactly one c.c. in the mask. Use distblend == 0 | |
| 803 * to skip blending and just paint through the 1 bpp mask. | |
| 804 * (7) To apply blending to more than 1 component, call this function | |
| 805 * repeatedly with %pixm, %x and %y representing one component of | |
| 806 * the mask each time. This would be done as follows, for an | |
| 807 * underlying image pixs and mask pixm of components to fill: | |
| 808 * Boxa *boxa = pixConnComp(pixm, &pixa, 8); | |
| 809 * n = boxaGetCount(boxa); | |
| 810 * for (i = 0; i < n; i++) { | |
| 811 * Pix *pix = pixaGetPix(pixa, i, L_CLONE); | |
| 812 * Box *box = pixaGetBox(pixa, i, L_CLONE); | |
| 813 * boxGetGeometry(box, &bx, &by, &bw, &bh); | |
| 814 * pixPaintSelfThroughMask(pixs, pix, bx, by, searchdir, | |
| 815 * mindist, tilesize, ntiles, distblend); | |
| 816 * pixDestroy(&pix); | |
| 817 * boxDestroy(&box); | |
| 818 * } | |
| 819 * pixaDestroy(&pixa); | |
| 820 * boxaDestroy(&boxa); | |
| 821 * (8) If no tiles can be found, this falls back to estimating the | |
| 822 * color near the boundary of the region to be textured. | |
| 823 * (9) This can be used to replace the pixels in some regions of | |
| 824 * an image by selected neighboring pixels. The mask represents | |
| 825 * the pixels to be replaced. For each connected component in | |
| 826 * the mask, this function selects up to two tiles of neighboring | |
| 827 * pixels to be used for replacement of pixels represented by | |
| 828 * the component (i.e., under the FG of that component in the mask). | |
| 829 * After selection, mirror replication is used to generate an | |
| 830 * image that is large enough to cover the component. Alpha | |
| 831 * blending can also be used outside of the component, but near the | |
| 832 * edge, to blur the transition between painted and original pixels. | |
| 833 * </pre> | |
| 834 */ | |
| 835 l_ok | |
| 836 pixPaintSelfThroughMask(PIX *pixd, | |
| 837 PIX *pixm, | |
| 838 l_int32 x, | |
| 839 l_int32 y, | |
| 840 l_int32 searchdir, | |
| 841 l_int32 mindist, | |
| 842 l_int32 tilesize, | |
| 843 l_int32 ntiles, | |
| 844 l_int32 distblend) | |
| 845 { | |
| 846 l_int32 w, h, d, wm, hm, dm, i, n, bx, by, bw, bh, edgeblend, retval, minside; | |
| 847 l_uint32 pixval; | |
| 848 BOX *box, *boxv, *boxh; | |
| 849 BOXA *boxa; | |
| 850 PIX *pixf, *pixv, *pixh, *pix1, *pix2, *pix3, *pix4, *pix5; | |
| 851 PIXA *pixa; | |
| 852 | |
| 853 if (!pixm) /* nothing to do */ | |
| 854 return 0; | |
| 855 if (!pixd) | |
| 856 return ERROR_INT("pixd not defined", __func__, 1); | |
| 857 if (pixGetColormap(pixd) != NULL) | |
| 858 return ERROR_INT("pixd has colormap", __func__, 1); | |
| 859 pixGetDimensions(pixd, &w, &h, &d); | |
| 860 if (d != 8 && d != 32) | |
| 861 return ERROR_INT("pixd not 8 or 32 bpp", __func__, 1); | |
| 862 pixGetDimensions(pixm, &wm, &hm, &dm); | |
| 863 if (dm != 1) | |
| 864 return ERROR_INT("pixm not 1 bpp", __func__, 1); | |
| 865 if (x < 0 || y < 0) | |
| 866 return ERROR_INT("x and y must be non-negative", __func__, 1); | |
| 867 if (searchdir != L_HORIZ && searchdir != L_VERT && | |
| 868 searchdir != L_BOTH_DIRECTIONS) | |
| 869 return ERROR_INT("invalid searchdir", __func__, 1); | |
| 870 if (tilesize < 2) | |
| 871 return ERROR_INT("tilesize must be >= 2", __func__, 1); | |
| 872 if (distblend < 0) | |
| 873 return ERROR_INT("distblend must be >= 0", __func__, 1); | |
| 874 | |
| 875 /* Embed mask in full sized mask */ | |
| 876 if (wm < w || hm < h) { | |
| 877 pixf = pixCreate(w, h, 1); | |
| 878 pixRasterop(pixf, x, y, wm, hm, PIX_SRC, pixm, 0, 0); | |
| 879 } else { | |
| 880 pixf = pixCopy(NULL, pixm); | |
| 881 } | |
| 882 | |
| 883 /* Get connected components of mask */ | |
| 884 boxa = pixConnComp(pixf, &pixa, 8); | |
| 885 if ((n = pixaGetCount(pixa)) == 0) { | |
| 886 L_WARNING("no fg in mask\n", __func__); | |
| 887 pixDestroy(&pixf); | |
| 888 pixaDestroy(&pixa); | |
| 889 boxaDestroy(&boxa); | |
| 890 return 1; | |
| 891 } | |
| 892 boxaDestroy(&boxa); | |
| 893 | |
| 894 /* For each c.c., generate one or two representative tiles for | |
| 895 * texturizing and apply through the mask. The input 'tilesize' | |
| 896 * is the requested value. Note that if there is exactly one | |
| 897 * component, and blending at the edge is requested, an alpha mask | |
| 898 * is generated, which is larger than the bounding box of the c.c. */ | |
| 899 edgeblend = (n == 1 && distblend > 0) ? 1 : 0; | |
| 900 if (distblend > 0 && n > 1) | |
| 901 L_WARNING("%d components; can not blend at edges\n", __func__, n); | |
| 902 retval = 0; | |
| 903 for (i = 0; i < n; i++) { | |
| 904 if (edgeblend) { | |
| 905 pix1 = pixMakeAlphaFromMask(pixf, distblend, &box); | |
| 906 } else { | |
| 907 pix1 = pixaGetPix(pixa, i, L_CLONE); | |
| 908 box = pixaGetBox(pixa, i, L_CLONE); | |
| 909 } | |
| 910 boxGetGeometry(box, &bx, &by, &bw, &bh); | |
| 911 minside = L_MIN(bw, bh); | |
| 912 | |
| 913 boxh = boxv = NULL; | |
| 914 if (searchdir == L_HORIZ || searchdir == L_BOTH_DIRECTIONS) { | |
| 915 pixFindRepCloseTile(pixd, box, L_HORIZ, mindist, | |
| 916 L_MIN(minside, tilesize), ntiles, &boxh, 0); | |
| 917 } | |
| 918 if (searchdir == L_VERT || searchdir == L_BOTH_DIRECTIONS) { | |
| 919 pixFindRepCloseTile(pixd, box, L_VERT, mindist, | |
| 920 L_MIN(minside, tilesize), ntiles, &boxv, 0); | |
| 921 } | |
| 922 if (!boxh && !boxv) { | |
| 923 L_WARNING("tile region not selected; paint color near boundary\n", | |
| 924 __func__); | |
| 925 pixDestroy(&pix1); | |
| 926 pix1 = pixaGetPix(pixa, i, L_CLONE); | |
| 927 pixaGetBoxGeometry(pixa, i, &bx, &by, NULL, NULL); | |
| 928 retval = pixGetColorNearMaskBoundary(pixd, pixm, box, distblend, | |
| 929 &pixval, 0); | |
| 930 pixSetMaskedGeneral(pixd, pix1, pixval, bx, by); | |
| 931 pixDestroy(&pix1); | |
| 932 boxDestroy(&box); | |
| 933 continue; | |
| 934 } | |
| 935 | |
| 936 /* Extract the selected squares from pixd */ | |
| 937 pixh = (boxh) ? pixClipRectangle(pixd, boxh, NULL) : NULL; | |
| 938 pixv = (boxv) ? pixClipRectangle(pixd, boxv, NULL) : NULL; | |
| 939 if (pixh && pixv) | |
| 940 pix2 = pixBlend(pixh, pixv, 0, 0, 0.5); | |
| 941 else if (pixh) | |
| 942 pix2 = pixClone(pixh); | |
| 943 else /* pixv */ | |
| 944 pix2 = pixClone(pixv); | |
| 945 pixDestroy(&pixh); | |
| 946 pixDestroy(&pixv); | |
| 947 boxDestroy(&boxh); | |
| 948 boxDestroy(&boxv); | |
| 949 | |
| 950 /* Generate an image the size of the b.b. of the c.c., | |
| 951 * possibly extended by the blending distance, which | |
| 952 * is then either painted through the c.c. mask or | |
| 953 * blended using the alpha mask for that c.c. */ | |
| 954 pix3 = pixMirroredTiling(pix2, bw, bh); | |
| 955 if (edgeblend) { | |
| 956 pix4 = pixClipRectangle(pixd, box, NULL); | |
| 957 pix5 = pixBlendWithGrayMask(pix4, pix3, pix1, 0, 0); | |
| 958 pixRasterop(pixd, bx, by, bw, bh, PIX_SRC, pix5, 0, 0); | |
| 959 pixDestroy(&pix4); | |
| 960 pixDestroy(&pix5); | |
| 961 } else { | |
| 962 pixCombineMaskedGeneral(pixd, pix3, pix1, bx, by); | |
| 963 } | |
| 964 pixDestroy(&pix1); | |
| 965 pixDestroy(&pix2); | |
| 966 pixDestroy(&pix3); | |
| 967 boxDestroy(&box); | |
| 968 } | |
| 969 | |
| 970 pixaDestroy(&pixa); | |
| 971 pixDestroy(&pixf); | |
| 972 return retval; | |
| 973 } | |
| 974 | |
| 975 | |
| 976 /*! | |
| 977 * \brief pixMakeMaskFromVal() | |
| 978 * | |
| 979 * \param[in] pixs 2, 4 or 8 bpp; can be colormapped | |
| 980 * \param[in] val pixel value | |
| 981 * \return pixd 1 bpp mask, or NULL on error | |
| 982 * | |
| 983 * <pre> | |
| 984 * Notes: | |
| 985 * (1) This generates a 1 bpp mask image, where a 1 is written in | |
| 986 * the mask for each pixel in pixs that has a value %val. | |
| 987 * (2) If no pixels have the value, an empty mask is generated. | |
| 988 * </pre> | |
| 989 */ | |
| 990 PIX * | |
| 991 pixMakeMaskFromVal(PIX *pixs, | |
| 992 l_int32 val) | |
| 993 { | |
| 994 l_int32 w, h, d, i, j, sval, wpls, wpld; | |
| 995 l_uint32 *datas, *datad, *lines, *lined; | |
| 996 PIX *pixd; | |
| 997 | |
| 998 if (!pixs) | |
| 999 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1000 pixGetDimensions(pixs, &w, &h, &d); | |
| 1001 if (d != 2 && d != 4 && d != 8) | |
| 1002 return (PIX *)ERROR_PTR("pix not 2, 4 or 8 bpp", __func__, NULL); | |
| 1003 | |
| 1004 pixd = pixCreate(w, h, 1); | |
| 1005 pixCopyResolution(pixd, pixs); | |
| 1006 pixCopyInputFormat(pixd, pixs); | |
| 1007 datas = pixGetData(pixs); | |
| 1008 datad = pixGetData(pixd); | |
| 1009 wpls = pixGetWpl(pixs); | |
| 1010 wpld = pixGetWpl(pixd); | |
| 1011 for (i = 0; i < h; i++) { | |
| 1012 lines = datas + i * wpls; | |
| 1013 lined = datad + i * wpld; | |
| 1014 for (j = 0; j < w; j++) { | |
| 1015 if (d == 2) | |
| 1016 sval = GET_DATA_DIBIT(lines, j); | |
| 1017 else if (d == 4) | |
| 1018 sval = GET_DATA_QBIT(lines, j); | |
| 1019 else /* d == 8 */ | |
| 1020 sval = GET_DATA_BYTE(lines, j); | |
| 1021 if (sval == val) | |
| 1022 SET_DATA_BIT(lined, j); | |
| 1023 } | |
| 1024 } | |
| 1025 | |
| 1026 return pixd; | |
| 1027 } | |
| 1028 | |
| 1029 | |
| 1030 /*! | |
| 1031 * \brief pixMakeMaskFromLUT() | |
| 1032 * | |
| 1033 * \param[in] pixs 2, 4 or 8 bpp; can be colormapped | |
| 1034 * \param[in] tab 256-entry LUT; 1 means to write to mask | |
| 1035 * \return pixd 1 bpp mask, or NULL on error | |
| 1036 * | |
| 1037 * <pre> | |
| 1038 * Notes: | |
| 1039 * (1) This generates a 1 bpp mask image, where a 1 is written in | |
| 1040 * the mask for each pixel in pixs that has a value corresponding | |
| 1041 * to a 1 in the LUT. | |
| 1042 * (2) The LUT should be of size 256. | |
| 1043 * </pre> | |
| 1044 */ | |
| 1045 PIX * | |
| 1046 pixMakeMaskFromLUT(PIX *pixs, | |
| 1047 l_int32 *tab) | |
| 1048 { | |
| 1049 l_int32 w, h, d, i, j, val, wpls, wpld; | |
| 1050 l_uint32 *datas, *datad, *lines, *lined; | |
| 1051 PIX *pixd; | |
| 1052 | |
| 1053 if (!pixs) | |
| 1054 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1055 if (!tab) | |
| 1056 return (PIX *)ERROR_PTR("tab not defined", __func__, NULL); | |
| 1057 pixGetDimensions(pixs, &w, &h, &d); | |
| 1058 if (d != 2 && d != 4 && d != 8) | |
| 1059 return (PIX *)ERROR_PTR("pix not 2, 4 or 8 bpp", __func__, NULL); | |
| 1060 | |
| 1061 pixd = pixCreate(w, h, 1); | |
| 1062 pixCopyResolution(pixd, pixs); | |
| 1063 pixCopyInputFormat(pixd, pixs); | |
| 1064 datas = pixGetData(pixs); | |
| 1065 datad = pixGetData(pixd); | |
| 1066 wpls = pixGetWpl(pixs); | |
| 1067 wpld = pixGetWpl(pixd); | |
| 1068 for (i = 0; i < h; i++) { | |
| 1069 lines = datas + i * wpls; | |
| 1070 lined = datad + i * wpld; | |
| 1071 for (j = 0; j < w; j++) { | |
| 1072 if (d == 2) | |
| 1073 val = GET_DATA_DIBIT(lines, j); | |
| 1074 else if (d == 4) | |
| 1075 val = GET_DATA_QBIT(lines, j); | |
| 1076 else /* d == 8 */ | |
| 1077 val = GET_DATA_BYTE(lines, j); | |
| 1078 if (tab[val] == 1) | |
| 1079 SET_DATA_BIT(lined, j); | |
| 1080 } | |
| 1081 } | |
| 1082 | |
| 1083 return pixd; | |
| 1084 } | |
| 1085 | |
| 1086 | |
| 1087 /*! | |
| 1088 * \brief pixMakeArbMaskFromRGB() | |
| 1089 * | |
| 1090 * \param[in] pixs 32 bpp RGB | |
| 1091 * \param[in] rc, gc, bc arithmetic factors; can be negative | |
| 1092 * \param[in] thresh lower threshold on weighted sum of components | |
| 1093 * \return pixd 1 bpp mask, or NULL on error | |
| 1094 * | |
| 1095 * <pre> | |
| 1096 * Notes: | |
| 1097 * (1) This generates a 1 bpp mask image, where a 1 is written in | |
| 1098 * the mask for each pixel in pixs that satisfies | |
| 1099 * rc * rval + gc * gval + bc * bval > thresh | |
| 1100 * where rval is the red component, etc. | |
| 1101 * (2) Unlike with pixConvertToGray(), there are no constraints | |
| 1102 * on the color coefficients, which can be negative. For | |
| 1103 * example, a mask that discriminates against red and in favor | |
| 1104 * of blue will have rc < 0.0 and bc > 0.0. | |
| 1105 * (3) To make the result independent of intensity (the 'V' in HSV), | |
| 1106 * select coefficients so that %thresh = 0. Then the result | |
| 1107 * is not changed when all components are multiplied by the | |
| 1108 * same constant (as long as nothing saturates). This can be | |
| 1109 * useful if, for example, the illumination is not uniform. | |
| 1110 * </pre> | |
| 1111 */ | |
| 1112 PIX * | |
| 1113 pixMakeArbMaskFromRGB(PIX *pixs, | |
| 1114 l_float32 rc, | |
| 1115 l_float32 gc, | |
| 1116 l_float32 bc, | |
| 1117 l_float32 thresh) | |
| 1118 { | |
| 1119 PIX *pix1, *pix2; | |
| 1120 | |
| 1121 if (!pixs || pixGetDepth(pixs) != 32) | |
| 1122 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", __func__, NULL); | |
| 1123 if (thresh >= 255.0) thresh = 254.0; /* avoid 8 bit overflow */ | |
| 1124 | |
| 1125 if ((pix1 = pixConvertRGBToGrayArb(pixs, rc, gc, bc)) == NULL) | |
| 1126 return (PIX *)ERROR_PTR("pix1 not made", __func__, NULL); | |
| 1127 pix2 = pixThresholdToBinary(pix1, thresh + 1); | |
| 1128 pixInvert(pix2, pix2); | |
| 1129 pixDestroy(&pix1); | |
| 1130 return pix2; | |
| 1131 } | |
| 1132 | |
| 1133 | |
| 1134 /*! | |
| 1135 * \brief pixSetUnderTransparency() | |
| 1136 * | |
| 1137 * \param[in] pixs 32 bpp rgba | |
| 1138 * \param[in] val 32 bit unsigned color to use where alpha == 0 | |
| 1139 * \param[in] debug displays layers of pixs | |
| 1140 * \return pixd 32 bpp rgba, or NULL on error | |
| 1141 * | |
| 1142 * <pre> | |
| 1143 * Notes: | |
| 1144 * (1) This sets the r, g and b components under every fully | |
| 1145 * transparent alpha component to %val. The alpha components | |
| 1146 * are unchanged. | |
| 1147 * (2) Full transparency is denoted by alpha == 0. Setting | |
| 1148 * all pixels to a constant %val where alpha is transparent | |
| 1149 * can improve compressibility by reducing the entropy. | |
| 1150 * (3) The visual result depends on how the image is displayed. | |
| 1151 * (a) For display devices that respect the use of the alpha | |
| 1152 * layer, this will not affect the appearance. | |
| 1153 * (b) For typical leptonica operations, alpha is ignored, | |
| 1154 * so there will be a change in appearance because this | |
| 1155 * resets the rgb values in the fully transparent region. | |
| 1156 * (4) pixRead() and pixWrite() will, by default, read and write | |
| 1157 * 4-component (rgba) pix in png format. To ignore the alpha | |
| 1158 * component after reading, or omit it on writing, pixSetSpp(..., 3). | |
| 1159 * (5) Here are some examples: | |
| 1160 * * To convert all fully transparent pixels in a 4 component | |
| 1161 * (rgba) png file to white: | |
| 1162 * pixs = pixRead(<infile>); | |
| 1163 * pixd = pixSetUnderTransparency(pixs, 0xffffff00, 0); | |
| 1164 * * To write pixd with the alpha component: | |
| 1165 * pixWrite(<outfile>, pixd, IFF_PNG); | |
| 1166 * * To write and rgba image without the alpha component, first do: | |
| 1167 * pixSetSpp(pixd, 3); | |
| 1168 * If you later want to use the alpha, spp must be reset to 4. | |
| 1169 * * (fancier) To remove the alpha by blending the image over | |
| 1170 * a white background: | |
| 1171 * pixRemoveAlpha() | |
| 1172 * This changes all pixel values where the alpha component is | |
| 1173 * not opaque (255). | |
| 1174 * (6) Caution. rgb images in leptonica typically have value 0 in | |
| 1175 * the alpha channel, which is fully transparent. If spp for | |
| 1176 * such an image were changed from 3 to 4, the image becomes | |
| 1177 * fully transparent, and this function will set each pixel to %val. | |
| 1178 * If you really want to set every pixel to the same value, | |
| 1179 * use pixSetAllArbitrary(). | |
| 1180 * (7) This is useful for compressing an RGBA image where the part | |
| 1181 * of the image that is fully transparent is random junk; compression | |
| 1182 * is typically improved by setting that region to a constant. | |
| 1183 * For rendering as a 3 component RGB image over a uniform | |
| 1184 * background of arbitrary color, use pixAlphaBlendUniform(). | |
| 1185 * </pre> | |
| 1186 */ | |
| 1187 PIX * | |
| 1188 pixSetUnderTransparency(PIX *pixs, | |
| 1189 l_uint32 val, | |
| 1190 l_int32 debug) | |
| 1191 { | |
| 1192 PIX *pixg, *pixm, *pixt, *pixd; | |
| 1193 | |
| 1194 if (!pixs || pixGetDepth(pixs) != 32) | |
| 1195 return (PIX *)ERROR_PTR("pixs not defined or not 32 bpp", | |
| 1196 __func__, NULL); | |
| 1197 | |
| 1198 if (pixGetSpp(pixs) != 4) { | |
| 1199 L_WARNING("no alpha channel; returning a copy\n", __func__); | |
| 1200 return pixCopy(NULL, pixs); | |
| 1201 } | |
| 1202 | |
| 1203 /* Make a mask from the alpha component with ON pixels | |
| 1204 * wherever the alpha component is fully transparent (0). | |
| 1205 * The hard way: | |
| 1206 * l_int32 *lut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); | |
| 1207 * lut[0] = 1; | |
| 1208 * pixg = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); | |
| 1209 * pixm = pixMakeMaskFromLUT(pixg, lut); | |
| 1210 * LEPT_FREE(lut); | |
| 1211 * But there's an easier way to set pixels in a mask where | |
| 1212 * the alpha component is 0 ... */ | |
| 1213 pixg = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); | |
| 1214 pixm = pixThresholdToBinary(pixg, 1); | |
| 1215 | |
| 1216 if (debug) { | |
| 1217 pixt = pixDisplayLayersRGBA(pixs, 0xffffff00, 600); | |
| 1218 pixDisplay(pixt, 0, 0); | |
| 1219 pixDestroy(&pixt); | |
| 1220 } | |
| 1221 | |
| 1222 pixd = pixCopy(NULL, pixs); | |
| 1223 pixSetMasked(pixd, pixm, (val & 0xffffff00)); | |
| 1224 pixDestroy(&pixg); | |
| 1225 pixDestroy(&pixm); | |
| 1226 return pixd; | |
| 1227 } | |
| 1228 | |
| 1229 | |
| 1230 /*! | |
| 1231 * \brief pixMakeAlphaFromMask() | |
| 1232 * | |
| 1233 * \param[in] pixs 1 bpp | |
| 1234 * \param[in] dist blending distance; typically 10 - 30 | |
| 1235 * \param[out] pbox [optional] use NULL to get the full size | |
| 1236 * \return pixd (8 bpp gray, or NULL on error | |
| 1237 * | |
| 1238 * <pre> | |
| 1239 * Notes: | |
| 1240 * (1) This generates a 8 bpp alpha layer that is opaque (256) | |
| 1241 * over the FG of pixs, and goes transparent linearly away | |
| 1242 * from the FG pixels, decaying to 0 (transparent) is an | |
| 1243 * 8-connected distance given by %dist. If %dist == 0, | |
| 1244 * this does a simple conversion from 1 to 8 bpp. | |
| 1245 * (2) If &box == NULL, this returns an alpha mask that is the | |
| 1246 * full size of pixs. Otherwise, the returned mask pixd covers | |
| 1247 * just the FG pixels of pixs, expanded by %dist in each | |
| 1248 * direction (if possible), and the returned box gives the | |
| 1249 * location of the returned mask relative to pixs. | |
| 1250 * (3) This is useful for painting through a mask and allowing | |
| 1251 * blending of the painted image with an underlying image | |
| 1252 * in the mask background for pixels near foreground mask pixels. | |
| 1253 * For example, with an underlying rgb image pix1, an overlaying | |
| 1254 * image rgb pix2, binary mask pixm, and dist > 0, this | |
| 1255 * blending is achieved with: | |
| 1256 * pix3 = pixMakeAlphaFromMask(pixm, dist, &box); | |
| 1257 * boxGetGeometry(box, &x, &y, NULL, NULL); | |
| 1258 * pix4 = pixBlendWithGrayMask(pix1, pix2, pix3, x, y); | |
| 1259 * </pre> | |
| 1260 */ | |
| 1261 PIX * | |
| 1262 pixMakeAlphaFromMask(PIX *pixs, | |
| 1263 l_int32 dist, | |
| 1264 BOX **pbox) | |
| 1265 { | |
| 1266 l_int32 w, h; | |
| 1267 BOX *box1, *box2; | |
| 1268 PIX *pix1, *pixd; | |
| 1269 | |
| 1270 if (pbox) *pbox = NULL; | |
| 1271 if (!pixs || pixGetDepth(pixs) != 1) | |
| 1272 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL); | |
| 1273 if (dist < 0) | |
| 1274 return (PIX *)ERROR_PTR("dist must be >= 0", __func__, NULL); | |
| 1275 | |
| 1276 /* If requested, extract just the region to be affected by the mask */ | |
| 1277 if (pbox) { | |
| 1278 pixClipToForeground(pixs, NULL, &box1); | |
| 1279 if (!box1) { | |
| 1280 L_WARNING("no ON pixels in mask\n", __func__); | |
| 1281 return pixCreateTemplate(pixs); /* all background (0) */ | |
| 1282 } | |
| 1283 | |
| 1284 boxAdjustSides(box1, box1, -dist, dist, -dist, dist); | |
| 1285 pixGetDimensions(pixs, &w, &h, NULL); | |
| 1286 box2 = boxClipToRectangle(box1, w, h); | |
| 1287 *pbox = box2; | |
| 1288 pix1 = pixClipRectangle(pixs, box2, NULL); | |
| 1289 boxDestroy(&box1); | |
| 1290 } else { | |
| 1291 pix1 = pixCopy(NULL, pixs); | |
| 1292 } | |
| 1293 | |
| 1294 if (dist == 0) { | |
| 1295 pixd = pixConvert1To8(NULL, pix1, 0, 255); | |
| 1296 pixDestroy(&pix1); | |
| 1297 return pixd; | |
| 1298 } | |
| 1299 | |
| 1300 /* Blur the boundary of the input mask */ | |
| 1301 pixInvert(pix1, pix1); | |
| 1302 pixd = pixDistanceFunction(pix1, 8, 8, L_BOUNDARY_FG); | |
| 1303 pixMultConstantGray(pixd, 256.0f / dist); | |
| 1304 pixInvert(pixd, pixd); | |
| 1305 pixDestroy(&pix1); | |
| 1306 return pixd; | |
| 1307 } | |
| 1308 | |
| 1309 | |
| 1310 /*! | |
| 1311 * \brief pixGetColorNearMaskBoundary() | |
| 1312 * | |
| 1313 * \param[in] pixs 32 bpp rgb | |
| 1314 * \param[in] pixm 1 bpp mask, full image | |
| 1315 * \param[in] box region of mask; typically b.b. of a component | |
| 1316 * \param[in] dist distance into BG from mask boundary to use | |
| 1317 * \param[out] pval average pixel value | |
| 1318 * \param[in] debug 1 to output mask images | |
| 1319 * \return 0 if OK, 1 on error. | |
| 1320 * | |
| 1321 * <pre> | |
| 1322 * Notes: | |
| 1323 * (1) This finds the average color in a set of pixels that are | |
| 1324 * roughly a distance %dist from the c.c. boundary and in the | |
| 1325 * background of the mask image. | |
| 1326 * </pre> | |
| 1327 */ | |
| 1328 l_ok | |
| 1329 pixGetColorNearMaskBoundary(PIX *pixs, | |
| 1330 PIX *pixm, | |
| 1331 BOX *box, | |
| 1332 l_int32 dist, | |
| 1333 l_uint32 *pval, | |
| 1334 l_int32 debug) | |
| 1335 { | |
| 1336 char op[64]; | |
| 1337 l_int32 empty, bx, by; | |
| 1338 l_float32 rval, gval, bval; | |
| 1339 BOX *box1, *box2; | |
| 1340 PIX *pix1, *pix2, *pix3; | |
| 1341 | |
| 1342 if (!pval) | |
| 1343 return ERROR_INT("&pval not defined", __func__, 1); | |
| 1344 *pval = 0xffffff00; /* white */ | |
| 1345 if (!pixs || pixGetDepth(pixs) != 32) | |
| 1346 return ERROR_INT("pixs undefined or not 32 bpp", __func__, 1); | |
| 1347 if (!pixm || pixGetDepth(pixm) != 1) | |
| 1348 return ERROR_INT("pixm undefined or not 1 bpp", __func__, 1); | |
| 1349 if (!box) | |
| 1350 return ERROR_INT("box not defined", __func__, 1); | |
| 1351 if (dist < 0) | |
| 1352 return ERROR_INT("dist must be >= 0", __func__, 1); | |
| 1353 | |
| 1354 /* Clip mask piece, expanded beyond %box by (%dist + 5) on each side. | |
| 1355 * box1 is the region requested; box2 is the actual region retrieved, | |
| 1356 * which is clipped to %pixm */ | |
| 1357 box1 = boxAdjustSides(NULL, box, -dist - 5, dist + 5, -dist - 5, dist + 5); | |
| 1358 pix1 = pixClipRectangle(pixm, box1, &box2); | |
| 1359 | |
| 1360 /* Expand FG by %dist into the BG */ | |
| 1361 if (dist == 0) { | |
| 1362 pix2 = pixCopy(NULL, pix1); | |
| 1363 } else { | |
| 1364 snprintf(op, sizeof(op), "d%d.%d", 2 * dist, 2 * dist); | |
| 1365 pix2 = pixMorphSequence(pix1, op, 0); | |
| 1366 } | |
| 1367 | |
| 1368 /* Expand again by 5 pixels on all sides (dilate 11x11) and XOR, | |
| 1369 * getting the annulus of FG pixels between %dist and %dist + 5 */ | |
| 1370 pix3 = pixCopy(NULL, pix2); | |
| 1371 pixDilateBrick(pix3, pix3, 11, 11); | |
| 1372 pixXor(pix3, pix3, pix2); | |
| 1373 pixZero(pix3, &empty); | |
| 1374 if (!empty) { | |
| 1375 /* Scan the same region in %pixs, to get average under FG in pix3 */ | |
| 1376 boxGetGeometry(box2, &bx, &by, NULL, NULL); | |
| 1377 pixGetAverageMaskedRGB(pixs, pix3, bx, by, 1, L_MEAN_ABSVAL, | |
| 1378 &rval, &gval, &bval); | |
| 1379 composeRGBPixel((l_int32)(rval + 0.5), (l_int32)(gval + 0.5), | |
| 1380 (l_int32)(bval + 0.5), pval); | |
| 1381 } else { | |
| 1382 L_WARNING("no pixels found\n", __func__); | |
| 1383 } | |
| 1384 | |
| 1385 if (debug) { | |
| 1386 lept_rmdir("masknear"); /* erase previous images */ | |
| 1387 lept_mkdir("masknear"); | |
| 1388 pixWriteDebug("/tmp/masknear/input.png", pix1, IFF_PNG); | |
| 1389 pixWriteDebug("/tmp/masknear/adjusted.png", pix2, IFF_PNG); | |
| 1390 pixWriteDebug("/tmp/masknear/outerfive.png", pix3, IFF_PNG); | |
| 1391 lept_stderr("Input box; with adjusted sides; clipped\n"); | |
| 1392 boxPrintStreamInfo(stderr, box); | |
| 1393 boxPrintStreamInfo(stderr, box1); | |
| 1394 boxPrintStreamInfo(stderr, box2); | |
| 1395 } | |
| 1396 | |
| 1397 pixDestroy(&pix1); | |
| 1398 pixDestroy(&pix2); | |
| 1399 pixDestroy(&pix3); | |
| 1400 boxDestroy(&box1); | |
| 1401 boxDestroy(&box2); | |
| 1402 return 0; | |
| 1403 } | |
| 1404 | |
| 1405 | |
| 1406 /*! | |
| 1407 * \brief pixDisplaySelectedPixels() | |
| 1408 * | |
| 1409 * \param[in] pixs [optional] any depth | |
| 1410 * \param[in] pixm 1 bpp mask, aligned UL corner with %pixs | |
| 1411 * \param[in] sel [optional] pattern to paint at each pixel in pixm | |
| 1412 * \param[in] val rgb rendering of pattern | |
| 1413 * \return pixd, or NULL on error | |
| 1414 * | |
| 1415 * <pre> | |
| 1416 * Notes: | |
| 1417 * (1) For every fg pixel in %pixm, this paints the pattern in %sel | |
| 1418 * in color %val on a copy of %pixs. | |
| 1419 * (2) The implementation is to dilate %pixm by %sel, and then | |
| 1420 * paint through the dilated mask onto %pixs. | |
| 1421 * (3) If %pixs == NULL, it paints on a white image. | |
| 1422 * (4) If %sel == NULL, it paints only the pixels in the input %pixm. | |
| 1423 * (5) This visualization would typically be used in debugging. | |
| 1424 * </pre> | |
| 1425 */ | |
| 1426 PIX * | |
| 1427 pixDisplaySelectedPixels(PIX *pixs, | |
| 1428 PIX *pixm, | |
| 1429 SEL *sel, | |
| 1430 l_uint32 val) | |
| 1431 { | |
| 1432 l_int32 w, h; | |
| 1433 PIX *pix1, *pix2; | |
| 1434 | |
| 1435 if (!pixm || pixGetDepth(pixm) != 1) | |
| 1436 return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", __func__, NULL); | |
| 1437 | |
| 1438 if (pixs) { | |
| 1439 pix1 = pixConvertTo32(pixs); | |
| 1440 } else { | |
| 1441 pixGetDimensions(pixm, &w, &h, NULL); | |
| 1442 pix1 = pixCreate(w, h, 32); | |
| 1443 pixSetAll(pix1); | |
| 1444 } | |
| 1445 | |
| 1446 if (sel) | |
| 1447 pix2 = pixDilate(NULL, pixm, sel); | |
| 1448 else | |
| 1449 pix2 = pixClone(pixm); | |
| 1450 pixSetMasked(pix1, pix2, val); | |
| 1451 pixDestroy(&pix2); | |
| 1452 return pix1; | |
| 1453 } | |
| 1454 | |
| 1455 | |
| 1456 /*-------------------------------------------------------------* | |
| 1457 * One and two-image boolean ops on arbitrary depth images * | |
| 1458 *-------------------------------------------------------------*/ | |
| 1459 /*! | |
| 1460 * \brief pixInvert() | |
| 1461 * | |
| 1462 * \param[in] pixd [optional]; this can be null, equal to pixs, | |
| 1463 * or different from pixs | |
| 1464 * \param[in] pixs | |
| 1465 * \return pixd, or NULL on error | |
| 1466 * | |
| 1467 * <pre> | |
| 1468 * Notes: | |
| 1469 * (1) This inverts pixs, for all pixel depths. | |
| 1470 * (2) There are 3 cases: | |
| 1471 * (a) pixd == null, ~src --> new pixd | |
| 1472 * (b) pixd == pixs, ~src --> src (in-place) | |
| 1473 * (c) pixd != pixs, ~src --> input pixd | |
| 1474 * (3) For clarity, if the case is known, use these patterns: | |
| 1475 * (a) pixd = pixInvert(NULL, pixs); | |
| 1476 * (b) pixInvert(pixs, pixs); | |
| 1477 * (c) pixInvert(pixd, pixs); | |
| 1478 * </pre> | |
| 1479 */ | |
| 1480 PIX * | |
| 1481 pixInvert(PIX *pixd, | |
| 1482 PIX *pixs) | |
| 1483 { | |
| 1484 if (!pixs) | |
| 1485 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1486 | |
| 1487 /* Prepare pixd for in-place operation */ | |
| 1488 if ((pixd = pixCopy(pixd, pixs)) == NULL) | |
| 1489 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 1490 | |
| 1491 pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd), | |
| 1492 PIX_NOT(PIX_DST), NULL, 0, 0); /* invert pixd */ | |
| 1493 | |
| 1494 return pixd; | |
| 1495 } | |
| 1496 | |
| 1497 | |
| 1498 /*! | |
| 1499 * \brief pixOr() | |
| 1500 * | |
| 1501 * \param[in] pixd [optional]; this can be null, equal to pixs1, | |
| 1502 * different from pixs1 | |
| 1503 * \param[in] pixs1 can be == pixd | |
| 1504 * \param[in] pixs2 must be != pixd | |
| 1505 * \return pixd always | |
| 1506 * | |
| 1507 * <pre> | |
| 1508 * Notes: | |
| 1509 * (1) This gives the union of two images with equal depth, | |
| 1510 * aligning them to the UL corner. pixs1 and pixs2 | |
| 1511 * need not have the same width and height. | |
| 1512 * (2) There are 3 cases: | |
| 1513 * (a) pixd == null, (src1 | src2) --> new pixd | |
| 1514 * (b) pixd == pixs1, (src1 | src2) --> src1 (in-place) | |
| 1515 * (c) pixd != pixs1, (src1 | src2) --> input pixd | |
| 1516 * (3) For clarity, if the case is known, use these patterns: | |
| 1517 * (a) pixd = pixOr(NULL, pixs1, pixs2); | |
| 1518 * (b) pixOr(pixs1, pixs1, pixs2); | |
| 1519 * (c) pixOr(pixd, pixs1, pixs2); | |
| 1520 * (4) The size of the result is determined by pixs1. | |
| 1521 * (5) The depths of pixs1 and pixs2 must be equal. | |
| 1522 * (6) Note carefully that the order of pixs1 and pixs2 only matters | |
| 1523 * for the in-place case. For in-place, you must have | |
| 1524 * pixd == pixs1. Setting pixd == pixs2 gives an incorrect | |
| 1525 * result: the copy puts pixs1 image data in pixs2, and | |
| 1526 * the rasterop is then between pixs2 and pixs2 (a no-op). | |
| 1527 * </pre> | |
| 1528 */ | |
| 1529 PIX * | |
| 1530 pixOr(PIX *pixd, | |
| 1531 PIX *pixs1, | |
| 1532 PIX *pixs2) | |
| 1533 { | |
| 1534 if (!pixs1) | |
| 1535 return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd); | |
| 1536 if (!pixs2) | |
| 1537 return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd); | |
| 1538 if (pixd == pixs2) | |
| 1539 return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", __func__, pixd); | |
| 1540 if (pixGetDepth(pixs1) != pixGetDepth(pixs2)) | |
| 1541 return (PIX *)ERROR_PTR("depths of pixs* unequal", __func__, pixd); | |
| 1542 | |
| 1543 #if EQUAL_SIZE_WARNING | |
| 1544 if (!pixSizesEqual(pixs1, pixs2)) | |
| 1545 L_WARNING("pixs1 and pixs2 not equal sizes\n", __func__); | |
| 1546 #endif /* EQUAL_SIZE_WARNING */ | |
| 1547 | |
| 1548 /* Prepare pixd to be a copy of pixs1 */ | |
| 1549 if ((pixd = pixCopy(pixd, pixs1)) == NULL) | |
| 1550 return (PIX *)ERROR_PTR("pixd not made", __func__, pixd); | |
| 1551 | |
| 1552 /* src1 | src2 --> dest */ | |
| 1553 pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd), | |
| 1554 PIX_SRC | PIX_DST, pixs2, 0, 0); | |
| 1555 | |
| 1556 return pixd; | |
| 1557 } | |
| 1558 | |
| 1559 | |
| 1560 /*! | |
| 1561 * \brief pixAnd() | |
| 1562 * | |
| 1563 * \param[in] pixd [optional]; this can be null, equal to pixs1, | |
| 1564 * different from pixs1 | |
| 1565 * \param[in] pixs1 can be == pixd | |
| 1566 * \param[in] pixs2 must be != pixd | |
| 1567 * \return pixd always | |
| 1568 * | |
| 1569 * <pre> | |
| 1570 * Notes: | |
| 1571 * (1) This gives the intersection of two images with equal depth, | |
| 1572 * aligning them to the the UL corner. pixs1 and pixs2 | |
| 1573 * need not have the same width and height. | |
| 1574 * (2) There are 3 cases: | |
| 1575 * (a) pixd == null, (src1 & src2) --> new pixd | |
| 1576 * (b) pixd == pixs1, (src1 & src2) --> src1 (in-place) | |
| 1577 * (c) pixd != pixs1, (src1 & src2) --> input pixd | |
| 1578 * (3) For clarity, if the case is known, use these patterns: | |
| 1579 * (a) pixd = pixAnd(NULL, pixs1, pixs2); | |
| 1580 * (b) pixAnd(pixs1, pixs1, pixs2); | |
| 1581 * (c) pixAnd(pixd, pixs1, pixs2); | |
| 1582 * (4) The size of the result is determined by pixs1. | |
| 1583 * (5) The depths of pixs1 and pixs2 must be equal. | |
| 1584 * (6) Note carefully that the order of pixs1 and pixs2 only matters | |
| 1585 * for the in-place case. For in-place, you must have | |
| 1586 * pixd == pixs1. Setting pixd == pixs2 gives an incorrect | |
| 1587 * result: the copy puts pixs1 image data in pixs2, and | |
| 1588 * the rasterop is then between pixs2 and pixs2 (a no-op). | |
| 1589 * </pre> | |
| 1590 */ | |
| 1591 PIX * | |
| 1592 pixAnd(PIX *pixd, | |
| 1593 PIX *pixs1, | |
| 1594 PIX *pixs2) | |
| 1595 { | |
| 1596 if (!pixs1) | |
| 1597 return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd); | |
| 1598 if (!pixs2) | |
| 1599 return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd); | |
| 1600 if (pixd == pixs2) | |
| 1601 return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", __func__, pixd); | |
| 1602 if (pixGetDepth(pixs1) != pixGetDepth(pixs2)) | |
| 1603 return (PIX *)ERROR_PTR("depths of pixs* unequal", __func__, pixd); | |
| 1604 | |
| 1605 #if EQUAL_SIZE_WARNING | |
| 1606 if (!pixSizesEqual(pixs1, pixs2)) | |
| 1607 L_WARNING("pixs1 and pixs2 not equal sizes\n", __func__); | |
| 1608 #endif /* EQUAL_SIZE_WARNING */ | |
| 1609 | |
| 1610 /* Prepare pixd to be a copy of pixs1 */ | |
| 1611 if ((pixd = pixCopy(pixd, pixs1)) == NULL) | |
| 1612 return (PIX *)ERROR_PTR("pixd not made", __func__, pixd); | |
| 1613 | |
| 1614 /* src1 & src2 --> dest */ | |
| 1615 pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd), | |
| 1616 PIX_SRC & PIX_DST, pixs2, 0, 0); | |
| 1617 | |
| 1618 return pixd; | |
| 1619 } | |
| 1620 | |
| 1621 | |
| 1622 /*! | |
| 1623 * \brief pixXor() | |
| 1624 * | |
| 1625 * \param[in] pixd [optional]; this can be null, equal to pixs1, | |
| 1626 * different from pixs1 | |
| 1627 * \param[in] pixs1 can be == pixd | |
| 1628 * \param[in] pixs2 must be != pixd | |
| 1629 * \return pixd always | |
| 1630 * | |
| 1631 * <pre> | |
| 1632 * Notes: | |
| 1633 * (1) This gives the XOR of two images with equal depth, | |
| 1634 * aligning them to the the UL corner. pixs1 and pixs2 | |
| 1635 * need not have the same width and height. | |
| 1636 * (2) There are 3 cases: | |
| 1637 * (a) pixd == null, (src1 ^ src2) --> new pixd | |
| 1638 * (b) pixd == pixs1, (src1 ^ src2) --> src1 (in-place) | |
| 1639 * (c) pixd != pixs1, (src1 ^ src2) --> input pixd | |
| 1640 * (3) For clarity, if the case is known, use these patterns: | |
| 1641 * (a) pixd = pixXor(NULL, pixs1, pixs2); | |
| 1642 * (b) pixXor(pixs1, pixs1, pixs2); | |
| 1643 * (c) pixXor(pixd, pixs1, pixs2); | |
| 1644 * (4) The size of the result is determined by pixs1. | |
| 1645 * (5) The depths of pixs1 and pixs2 must be equal. | |
| 1646 * (6) Note carefully that the order of pixs1 and pixs2 only matters | |
| 1647 * for the in-place case. For in-place, you must have | |
| 1648 * pixd == pixs1. Setting pixd == pixs2 gives an incorrect | |
| 1649 * result: the copy puts pixs1 image data in pixs2, and | |
| 1650 * the rasterop is then between pixs2 and pixs2 (a no-op). | |
| 1651 * </pre> | |
| 1652 */ | |
| 1653 PIX * | |
| 1654 pixXor(PIX *pixd, | |
| 1655 PIX *pixs1, | |
| 1656 PIX *pixs2) | |
| 1657 { | |
| 1658 if (!pixs1) | |
| 1659 return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd); | |
| 1660 if (!pixs2) | |
| 1661 return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd); | |
| 1662 if (pixd == pixs2) | |
| 1663 return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", __func__, pixd); | |
| 1664 if (pixGetDepth(pixs1) != pixGetDepth(pixs2)) | |
| 1665 return (PIX *)ERROR_PTR("depths of pixs* unequal", __func__, pixd); | |
| 1666 | |
| 1667 #if EQUAL_SIZE_WARNING | |
| 1668 if (!pixSizesEqual(pixs1, pixs2)) | |
| 1669 L_WARNING("pixs1 and pixs2 not equal sizes\n", __func__); | |
| 1670 #endif /* EQUAL_SIZE_WARNING */ | |
| 1671 | |
| 1672 /* Prepare pixd to be a copy of pixs1 */ | |
| 1673 if ((pixd = pixCopy(pixd, pixs1)) == NULL) | |
| 1674 return (PIX *)ERROR_PTR("pixd not made", __func__, pixd); | |
| 1675 | |
| 1676 /* src1 ^ src2 --> dest */ | |
| 1677 pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd), | |
| 1678 PIX_SRC ^ PIX_DST, pixs2, 0, 0); | |
| 1679 | |
| 1680 return pixd; | |
| 1681 } | |
| 1682 | |
| 1683 | |
| 1684 /*! | |
| 1685 * \brief pixSubtract() | |
| 1686 * | |
| 1687 * \param[in] pixd [optional]; this can be null, equal to pixs1, | |
| 1688 * equal to pixs2, or different from both pixs1 and pixs2 | |
| 1689 * \param[in] pixs1 can be == pixd | |
| 1690 * \param[in] pixs2 can be == pixd | |
| 1691 * \return pixd always | |
| 1692 * | |
| 1693 * <pre> | |
| 1694 * Notes: | |
| 1695 * (1) This gives the set subtraction of two images with equal depth, | |
| 1696 * aligning them to the the UL corner. pixs1 and pixs2 | |
| 1697 * need not have the same width and height. | |
| 1698 * (2) Source pixs2 is always subtracted from source pixs1. | |
| 1699 * The result is | |
| 1700 * pixs1 \ pixs2 = pixs1 & (~pixs2) | |
| 1701 * (3) There are 4 cases: | |
| 1702 * (a) pixd == null, (src1 - src2) --> new pixd | |
| 1703 * (b) pixd == pixs1, (src1 - src2) --> src1 (in-place) | |
| 1704 * (c) pixd == pixs2, (src1 - src2) --> src2 (in-place) | |
| 1705 * (d) pixd != pixs1 && pixd != pixs2), | |
| 1706 * (src1 - src2) --> input pixd | |
| 1707 * (4) For clarity, if the case is known, use these patterns: | |
| 1708 * (a) pixd = pixSubtract(NULL, pixs1, pixs2); | |
| 1709 * (b) pixSubtract(pixs1, pixs1, pixs2); | |
| 1710 * (c) pixSubtract(pixs2, pixs1, pixs2); | |
| 1711 * (d) pixSubtract(pixd, pixs1, pixs2); | |
| 1712 * (5) The size of the result is determined by pixs1. | |
| 1713 * (6) The depths of pixs1 and pixs2 must be equal. | |
| 1714 * </pre> | |
| 1715 */ | |
| 1716 PIX * | |
| 1717 pixSubtract(PIX *pixd, | |
| 1718 PIX *pixs1, | |
| 1719 PIX *pixs2) | |
| 1720 { | |
| 1721 l_int32 w, h; | |
| 1722 | |
| 1723 if (!pixs1) | |
| 1724 return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd); | |
| 1725 if (!pixs2) | |
| 1726 return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd); | |
| 1727 if (pixGetDepth(pixs1) != pixGetDepth(pixs2)) | |
| 1728 return (PIX *)ERROR_PTR("depths of pixs* unequal", __func__, pixd); | |
| 1729 | |
| 1730 #if EQUAL_SIZE_WARNING | |
| 1731 if (!pixSizesEqual(pixs1, pixs2)) | |
| 1732 L_WARNING("pixs1 and pixs2 not equal sizes\n", __func__); | |
| 1733 #endif /* EQUAL_SIZE_WARNING */ | |
| 1734 | |
| 1735 pixGetDimensions(pixs1, &w, &h, NULL); | |
| 1736 if (!pixd) { | |
| 1737 pixd = pixCopy(NULL, pixs1); | |
| 1738 pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC), | |
| 1739 pixs2, 0, 0); /* src1 & (~src2) */ | |
| 1740 } else if (pixd == pixs1) { | |
| 1741 pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC), | |
| 1742 pixs2, 0, 0); /* src1 & (~src2) */ | |
| 1743 } else if (pixd == pixs2) { | |
| 1744 pixRasterop(pixd, 0, 0, w, h, PIX_NOT(PIX_DST) & PIX_SRC, | |
| 1745 pixs1, 0, 0); /* src1 & (~src2) */ | |
| 1746 } else { /* pixd != pixs1 && pixd != pixs2 */ | |
| 1747 pixCopy(pixd, pixs1); /* sizes pixd to pixs1 if unequal */ | |
| 1748 pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC), | |
| 1749 pixs2, 0, 0); /* src1 & (~src2) */ | |
| 1750 } | |
| 1751 | |
| 1752 return pixd; | |
| 1753 } | |
| 1754 | |
| 1755 | |
| 1756 /*-------------------------------------------------------------* | |
| 1757 * Pixel counting * | |
| 1758 *-------------------------------------------------------------*/ | |
| 1759 /*! | |
| 1760 * \brief pixZero() | |
| 1761 * | |
| 1762 * \param[in] pix all depths; colormap OK | |
| 1763 * \param[out] pempty 1 if all bits in image data field are 0; 0 otherwise | |
| 1764 * \return 0 if OK; 1 on error | |
| 1765 * | |
| 1766 * <pre> | |
| 1767 * Notes: | |
| 1768 * (1) For a binary image, if there are no fg (black) pixels, empty = 1. | |
| 1769 * (2) For a grayscale image, if all pixels are black (0), empty = 1. | |
| 1770 * (3) For an RGB image, if all 4 components in every pixel is 0 | |
| 1771 * (i.e. opaque black), empty = 1. | |
| 1772 * (4) For a colormapped image, pixel values are 0. The colormap | |
| 1773 * is ignored. | |
| 1774 * </pre> | |
| 1775 */ | |
| 1776 l_ok | |
| 1777 pixZero(PIX *pix, | |
| 1778 l_int32 *pempty) | |
| 1779 { | |
| 1780 l_int32 w, h, wpl, i, j, fullwords, endbits; | |
| 1781 l_uint32 endmask; | |
| 1782 l_uint32 *data, *line; | |
| 1783 | |
| 1784 if (!pempty) | |
| 1785 return ERROR_INT("&empty not defined", __func__, 1); | |
| 1786 *pempty = 1; | |
| 1787 if (!pix) | |
| 1788 return ERROR_INT("pix not defined", __func__, 1); | |
| 1789 | |
| 1790 w = pixGetWidth(pix) * pixGetDepth(pix); /* in bits */ | |
| 1791 h = pixGetHeight(pix); | |
| 1792 wpl = pixGetWpl(pix); | |
| 1793 data = pixGetData(pix); | |
| 1794 fullwords = w / 32; | |
| 1795 endbits = w & 31; | |
| 1796 endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits)); | |
| 1797 | |
| 1798 for (i = 0; i < h; i++) { | |
| 1799 line = data + wpl * i; | |
| 1800 for (j = 0; j < fullwords; j++) | |
| 1801 if (*line++) { | |
| 1802 *pempty = 0; | |
| 1803 return 0; | |
| 1804 } | |
| 1805 if (endbits) { | |
| 1806 if (*line & endmask) { | |
| 1807 *pempty = 0; | |
| 1808 return 0; | |
| 1809 } | |
| 1810 } | |
| 1811 } | |
| 1812 | |
| 1813 return 0; | |
| 1814 } | |
| 1815 | |
| 1816 | |
| 1817 /*! | |
| 1818 * \brief pixForegroundFraction() | |
| 1819 * | |
| 1820 * \param[in] pix 1 bpp | |
| 1821 * \param[out] pfract fraction of ON pixels | |
| 1822 * \return 0 if OK; 1 on error | |
| 1823 */ | |
| 1824 l_ok | |
| 1825 pixForegroundFraction(PIX *pix, | |
| 1826 l_float32 *pfract) | |
| 1827 { | |
| 1828 l_int32 w, h, count; | |
| 1829 | |
| 1830 if (!pfract) | |
| 1831 return ERROR_INT("&fract not defined", __func__, 1); | |
| 1832 *pfract = 0.0; | |
| 1833 if (!pix || pixGetDepth(pix) != 1) | |
| 1834 return ERROR_INT("pix not defined or not 1 bpp", __func__, 1); | |
| 1835 | |
| 1836 pixCountPixels(pix, &count, NULL); | |
| 1837 pixGetDimensions(pix, &w, &h, NULL); | |
| 1838 *pfract = (l_float32)count / (l_float32)(w * h); | |
| 1839 return 0; | |
| 1840 } | |
| 1841 | |
| 1842 | |
| 1843 /*! | |
| 1844 * \brief pixaCountPixels() | |
| 1845 * | |
| 1846 * \param[in] pixa array of 1 bpp pix | |
| 1847 * \return na of ON pixels in each pix, or NULL on error | |
| 1848 */ | |
| 1849 NUMA * | |
| 1850 pixaCountPixels(PIXA *pixa) | |
| 1851 { | |
| 1852 l_int32 d, i, n, count; | |
| 1853 l_int32 *tab; | |
| 1854 NUMA *na; | |
| 1855 PIX *pix; | |
| 1856 | |
| 1857 if (!pixa) | |
| 1858 return (NUMA *)ERROR_PTR("pix not defined", __func__, NULL); | |
| 1859 | |
| 1860 if ((n = pixaGetCount(pixa)) == 0) | |
| 1861 return numaCreate(1); | |
| 1862 | |
| 1863 pix = pixaGetPix(pixa, 0, L_CLONE); | |
| 1864 d = pixGetDepth(pix); | |
| 1865 pixDestroy(&pix); | |
| 1866 if (d != 1) | |
| 1867 return (NUMA *)ERROR_PTR("pixa not 1 bpp", __func__, NULL); | |
| 1868 | |
| 1869 if ((na = numaCreate(n)) == NULL) | |
| 1870 return (NUMA *)ERROR_PTR("na not made", __func__, NULL); | |
| 1871 tab = makePixelSumTab8(); | |
| 1872 for (i = 0; i < n; i++) { | |
| 1873 pix = pixaGetPix(pixa, i, L_CLONE); | |
| 1874 pixCountPixels(pix, &count, tab); | |
| 1875 numaAddNumber(na, count); | |
| 1876 pixDestroy(&pix); | |
| 1877 } | |
| 1878 | |
| 1879 LEPT_FREE(tab); | |
| 1880 return na; | |
| 1881 } | |
| 1882 | |
| 1883 | |
| 1884 /*! | |
| 1885 * \brief pixCountPixels() | |
| 1886 * | |
| 1887 * \param[in] pixs 1 bpp | |
| 1888 * \param[out] pcount count of ON pixels | |
| 1889 * \param[in] tab8 [optional] 8-bit pixel lookup table | |
| 1890 * \return 0 if OK; 1 on error | |
| 1891 */ | |
| 1892 l_ok | |
| 1893 pixCountPixels(PIX *pixs, | |
| 1894 l_int32 *pcount, | |
| 1895 l_int32 *tab8) | |
| 1896 { | |
| 1897 l_uint32 endmask; | |
| 1898 l_int32 w, h, wpl, i, j; | |
| 1899 l_int32 fullwords, endbits, sum; | |
| 1900 l_int32 *tab; | |
| 1901 l_uint32 *data; | |
| 1902 | |
| 1903 if (!pcount) | |
| 1904 return ERROR_INT("&count not defined", __func__, 1); | |
| 1905 *pcount = 0; | |
| 1906 if (!pixs || pixGetDepth(pixs) != 1) | |
| 1907 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1); | |
| 1908 | |
| 1909 tab = (tab8) ? tab8 : makePixelSumTab8(); | |
| 1910 pixGetDimensions(pixs, &w, &h, NULL); | |
| 1911 wpl = pixGetWpl(pixs); | |
| 1912 data = pixGetData(pixs); | |
| 1913 fullwords = w >> 5; | |
| 1914 endbits = w & 31; | |
| 1915 endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits)); | |
| 1916 | |
| 1917 sum = 0; | |
| 1918 for (i = 0; i < h; i++, data += wpl) { | |
| 1919 for (j = 0; j < fullwords; j++) { | |
| 1920 l_uint32 word = data[j]; | |
| 1921 if (word) { | |
| 1922 sum += tab[word & 0xff] + | |
| 1923 tab[(word >> 8) & 0xff] + | |
| 1924 tab[(word >> 16) & 0xff] + | |
| 1925 tab[(word >> 24) & 0xff]; | |
| 1926 } | |
| 1927 } | |
| 1928 if (endbits) { | |
| 1929 l_uint32 word = data[j] & endmask; | |
| 1930 if (word) { | |
| 1931 sum += tab[word & 0xff] + | |
| 1932 tab[(word >> 8) & 0xff] + | |
| 1933 tab[(word >> 16) & 0xff] + | |
| 1934 tab[(word >> 24) & 0xff]; | |
| 1935 } | |
| 1936 } | |
| 1937 } | |
| 1938 *pcount = sum; | |
| 1939 | |
| 1940 if (!tab8) LEPT_FREE(tab); | |
| 1941 return 0; | |
| 1942 } | |
| 1943 | |
| 1944 | |
| 1945 /*! | |
| 1946 * \brief pixCountPixelsInRect() | |
| 1947 * | |
| 1948 * \param[in] pixs 1 bpp | |
| 1949 * \param[in] box (can be null) | |
| 1950 * \param[out] pcount count of ON pixels | |
| 1951 * \param[in] tab8 [optional] 8-bit pixel lookup table | |
| 1952 * \return 0 if OK; 1 on error | |
| 1953 */ | |
| 1954 l_ok | |
| 1955 pixCountPixelsInRect(PIX *pixs, | |
| 1956 BOX *box, | |
| 1957 l_int32 *pcount, | |
| 1958 l_int32 *tab8) | |
| 1959 { | |
| 1960 l_int32 w, h, bx, by, bw, bh; | |
| 1961 BOX *box1; | |
| 1962 PIX *pix1; | |
| 1963 | |
| 1964 if (!pcount) | |
| 1965 return ERROR_INT("&count not defined", __func__, 1); | |
| 1966 *pcount = 0; | |
| 1967 if (!pixs || pixGetDepth(pixs) != 1) | |
| 1968 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1); | |
| 1969 | |
| 1970 if (box) { | |
| 1971 pixGetDimensions(pixs, &w, &h, NULL); | |
| 1972 if ((box1 = boxClipToRectangle(box, w, h)) == NULL) | |
| 1973 return ERROR_INT("box1 not made", __func__, 1); | |
| 1974 boxGetGeometry(box1, &bx, &by, &bw, &bh); | |
| 1975 pix1 = pixCreate(bw, bh, 1); | |
| 1976 pixRasterop(pix1, 0, 0, bw, bh, PIX_SRC, pixs, bx, by); | |
| 1977 pixCountPixels(pix1, pcount, tab8); | |
| 1978 pixDestroy(&pix1); | |
| 1979 boxDestroy(&box1); | |
| 1980 } else { | |
| 1981 pixCountPixels(pixs, pcount, tab8); | |
| 1982 } | |
| 1983 | |
| 1984 return 0; | |
| 1985 } | |
| 1986 | |
| 1987 | |
| 1988 /*! | |
| 1989 * \brief pixCountByRow() | |
| 1990 * | |
| 1991 * \param[in] pix 1 bpp | |
| 1992 * \param[in] box [optional] clipping box for count; can be null | |
| 1993 * \return na of number of ON pixels by row, or NULL on error | |
| 1994 * | |
| 1995 * <pre> | |
| 1996 * Notes: | |
| 1997 * (1) To resample for a bin size different from 1, use | |
| 1998 * numaUniformSampling() on the result of this function. | |
| 1999 * </pre> | |
| 2000 */ | |
| 2001 NUMA * | |
| 2002 pixCountByRow(PIX *pix, | |
| 2003 BOX *box) | |
| 2004 { | |
| 2005 l_int32 i, j, w, h, wpl, count, xstart, xend, ystart, yend, bw, bh; | |
| 2006 l_uint32 *line, *data; | |
| 2007 NUMA *na; | |
| 2008 | |
| 2009 if (!pix || pixGetDepth(pix) != 1) | |
| 2010 return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", __func__, NULL); | |
| 2011 if (!box) | |
| 2012 return pixCountPixelsByRow(pix, NULL); | |
| 2013 | |
| 2014 pixGetDimensions(pix, &w, &h, NULL); | |
| 2015 if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, | |
| 2016 &bw, &bh) == 1) | |
| 2017 return (NUMA *)ERROR_PTR("invalid clipping box", __func__, NULL); | |
| 2018 | |
| 2019 if ((na = numaCreate(bh)) == NULL) | |
| 2020 return (NUMA *)ERROR_PTR("na not made", __func__, NULL); | |
| 2021 numaSetParameters(na, ystart, 1); | |
| 2022 data = pixGetData(pix); | |
| 2023 wpl = pixGetWpl(pix); | |
| 2024 for (i = ystart; i < yend; i++) { | |
| 2025 count = 0; | |
| 2026 line = data + i * wpl; | |
| 2027 for (j = xstart; j < xend; j++) { | |
| 2028 if (GET_DATA_BIT(line, j)) | |
| 2029 count++; | |
| 2030 } | |
| 2031 numaAddNumber(na, count); | |
| 2032 } | |
| 2033 | |
| 2034 return na; | |
| 2035 } | |
| 2036 | |
| 2037 | |
| 2038 /*! | |
| 2039 * \brief pixCountByColumn() | |
| 2040 * | |
| 2041 * \param[in] pix 1 bpp | |
| 2042 * \param[in] box [optional] clipping box for count; can be null | |
| 2043 * \return na of number of ON pixels by column, or NULL on error | |
| 2044 * | |
| 2045 * <pre> | |
| 2046 * Notes: | |
| 2047 * (1) To resample for a bin size different from 1, use | |
| 2048 * numaUniformSampling() on the result of this function. | |
| 2049 * </pre> | |
| 2050 */ | |
| 2051 NUMA * | |
| 2052 pixCountByColumn(PIX *pix, | |
| 2053 BOX *box) | |
| 2054 { | |
| 2055 l_int32 i, j, w, h, wpl, count, xstart, xend, ystart, yend, bw, bh; | |
| 2056 l_uint32 *line, *data; | |
| 2057 NUMA *na; | |
| 2058 | |
| 2059 if (!pix || pixGetDepth(pix) != 1) | |
| 2060 return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", __func__, NULL); | |
| 2061 if (!box) | |
| 2062 return pixCountPixelsByColumn(pix); | |
| 2063 | |
| 2064 pixGetDimensions(pix, &w, &h, NULL); | |
| 2065 if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, | |
| 2066 &bw, &bh) == 1) | |
| 2067 return (NUMA *)ERROR_PTR("invalid clipping box", __func__, NULL); | |
| 2068 | |
| 2069 if ((na = numaCreate(bw)) == NULL) | |
| 2070 return (NUMA *)ERROR_PTR("na not made", __func__, NULL); | |
| 2071 numaSetParameters(na, xstart, 1); | |
| 2072 data = pixGetData(pix); | |
| 2073 wpl = pixGetWpl(pix); | |
| 2074 for (j = xstart; j < xend; j++) { | |
| 2075 count = 0; | |
| 2076 for (i = ystart; i < yend; i++) { | |
| 2077 line = data + i * wpl; | |
| 2078 if (GET_DATA_BIT(line, j)) | |
| 2079 count++; | |
| 2080 } | |
| 2081 numaAddNumber(na, count); | |
| 2082 } | |
| 2083 | |
| 2084 return na; | |
| 2085 } | |
| 2086 | |
| 2087 | |
| 2088 /*! | |
| 2089 * \brief pixCountPixelsByRow() | |
| 2090 * | |
| 2091 * \param[in] pix 1 bpp | |
| 2092 * \param[in] tab8 [optional] 8-bit pixel lookup table | |
| 2093 * \return na of counts, or NULL on error | |
| 2094 */ | |
| 2095 NUMA * | |
| 2096 pixCountPixelsByRow(PIX *pix, | |
| 2097 l_int32 *tab8) | |
| 2098 { | |
| 2099 l_int32 h, i, count; | |
| 2100 l_int32 *tab; | |
| 2101 NUMA *na; | |
| 2102 | |
| 2103 if (!pix || pixGetDepth(pix) != 1) | |
| 2104 return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", __func__, NULL); | |
| 2105 | |
| 2106 h = pixGetHeight(pix); | |
| 2107 if ((na = numaCreate(h)) == NULL) | |
| 2108 return (NUMA *)ERROR_PTR("na not made", __func__, NULL); | |
| 2109 | |
| 2110 tab = (tab8) ? tab8 : makePixelSumTab8(); | |
| 2111 for (i = 0; i < h; i++) { | |
| 2112 pixCountPixelsInRow(pix, i, &count, tab); | |
| 2113 numaAddNumber(na, count); | |
| 2114 } | |
| 2115 | |
| 2116 if (!tab8) LEPT_FREE(tab); | |
| 2117 return na; | |
| 2118 } | |
| 2119 | |
| 2120 | |
| 2121 /*! | |
| 2122 * \brief pixCountPixelsByColumn() | |
| 2123 * | |
| 2124 * \param[in] pix 1 bpp | |
| 2125 * \return na of counts in each column, or NULL on error | |
| 2126 */ | |
| 2127 NUMA * | |
| 2128 pixCountPixelsByColumn(PIX *pix) | |
| 2129 { | |
| 2130 l_int32 i, j, w, h, wpl; | |
| 2131 l_uint32 *line, *data; | |
| 2132 l_float32 *array; | |
| 2133 NUMA *na; | |
| 2134 | |
| 2135 if (!pix || pixGetDepth(pix) != 1) | |
| 2136 return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", __func__, NULL); | |
| 2137 | |
| 2138 pixGetDimensions(pix, &w, &h, NULL); | |
| 2139 if ((na = numaCreate(w)) == NULL) | |
| 2140 return (NUMA *)ERROR_PTR("na not made", __func__, NULL); | |
| 2141 numaSetCount(na, w); | |
| 2142 array = numaGetFArray(na, L_NOCOPY); | |
| 2143 data = pixGetData(pix); | |
| 2144 wpl = pixGetWpl(pix); | |
| 2145 for (i = 0; i < h; i++) { | |
| 2146 line = data + wpl * i; | |
| 2147 for (j = 0; j < w; j++) { | |
| 2148 if (GET_DATA_BIT(line, j)) | |
| 2149 array[j] += 1.0; | |
| 2150 } | |
| 2151 } | |
| 2152 | |
| 2153 return na; | |
| 2154 } | |
| 2155 | |
| 2156 | |
| 2157 /*! | |
| 2158 * \brief pixCountPixelsInRow() | |
| 2159 * | |
| 2160 * \param[in] pix 1 bpp | |
| 2161 * \param[in] row number | |
| 2162 * \param[out] pcount sum of ON pixels in raster line | |
| 2163 * \param[in] tab8 [optional] 8-bit pixel lookup table | |
| 2164 * \return 0 if OK; 1 on error | |
| 2165 */ | |
| 2166 l_ok | |
| 2167 pixCountPixelsInRow(PIX *pix, | |
| 2168 l_int32 row, | |
| 2169 l_int32 *pcount, | |
| 2170 l_int32 *tab8) | |
| 2171 { | |
| 2172 l_uint32 word, endmask; | |
| 2173 l_int32 j, w, h, wpl; | |
| 2174 l_int32 fullwords, endbits, sum; | |
| 2175 l_int32 *tab; | |
| 2176 l_uint32 *line; | |
| 2177 | |
| 2178 if (!pcount) | |
| 2179 return ERROR_INT("&count not defined", __func__, 1); | |
| 2180 *pcount = 0; | |
| 2181 if (!pix || pixGetDepth(pix) != 1) | |
| 2182 return ERROR_INT("pix not defined or not 1 bpp", __func__, 1); | |
| 2183 | |
| 2184 pixGetDimensions(pix, &w, &h, NULL); | |
| 2185 if (row < 0 || row >= h) | |
| 2186 return ERROR_INT("row out of bounds", __func__, 1); | |
| 2187 wpl = pixGetWpl(pix); | |
| 2188 line = pixGetData(pix) + row * wpl; | |
| 2189 fullwords = w >> 5; | |
| 2190 endbits = w & 31; | |
| 2191 endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits)); | |
| 2192 | |
| 2193 tab = (tab8) ? tab8 : makePixelSumTab8(); | |
| 2194 sum = 0; | |
| 2195 for (j = 0; j < fullwords; j++) { | |
| 2196 word = line[j]; | |
| 2197 if (word) { | |
| 2198 sum += tab[word & 0xff] + | |
| 2199 tab[(word >> 8) & 0xff] + | |
| 2200 tab[(word >> 16) & 0xff] + | |
| 2201 tab[(word >> 24) & 0xff]; | |
| 2202 } | |
| 2203 } | |
| 2204 if (endbits) { | |
| 2205 word = line[j] & endmask; | |
| 2206 if (word) { | |
| 2207 sum += tab[word & 0xff] + | |
| 2208 tab[(word >> 8) & 0xff] + | |
| 2209 tab[(word >> 16) & 0xff] + | |
| 2210 tab[(word >> 24) & 0xff]; | |
| 2211 } | |
| 2212 } | |
| 2213 *pcount = sum; | |
| 2214 | |
| 2215 if (!tab8) LEPT_FREE(tab); | |
| 2216 return 0; | |
| 2217 } | |
| 2218 | |
| 2219 | |
| 2220 /*! | |
| 2221 * \brief pixGetMomentByColumn() | |
| 2222 * | |
| 2223 * \param[in] pix 1 bpp | |
| 2224 * \param[in] order of moment, either 1 or 2 | |
| 2225 * \return na of first moment of fg pixels, by column, or NULL on error | |
| 2226 */ | |
| 2227 NUMA * | |
| 2228 pixGetMomentByColumn(PIX *pix, | |
| 2229 l_int32 order) | |
| 2230 { | |
| 2231 l_int32 i, j, w, h, wpl; | |
| 2232 l_uint32 *line, *data; | |
| 2233 l_float32 *array; | |
| 2234 NUMA *na; | |
| 2235 | |
| 2236 if (!pix || pixGetDepth(pix) != 1) | |
| 2237 return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", __func__, NULL); | |
| 2238 if (order != 1 && order != 2) | |
| 2239 return (NUMA *)ERROR_PTR("order of moment not 1 or 2", __func__, NULL); | |
| 2240 | |
| 2241 pixGetDimensions(pix, &w, &h, NULL); | |
| 2242 if ((na = numaCreate(w)) == NULL) | |
| 2243 return (NUMA *)ERROR_PTR("na not made", __func__, NULL); | |
| 2244 numaSetCount(na, w); | |
| 2245 array = numaGetFArray(na, L_NOCOPY); | |
| 2246 data = pixGetData(pix); | |
| 2247 wpl = pixGetWpl(pix); | |
| 2248 for (i = 0; i < h; i++) { | |
| 2249 line = data + wpl * i; | |
| 2250 for (j = 0; j < w; j++) { | |
| 2251 if (GET_DATA_BIT(line, j)) { | |
| 2252 if (order == 1) | |
| 2253 array[j] += i; | |
| 2254 else /* order == 2 */ | |
| 2255 array[j] += i * i; | |
| 2256 } | |
| 2257 } | |
| 2258 } | |
| 2259 | |
| 2260 return na; | |
| 2261 } | |
| 2262 | |
| 2263 | |
| 2264 /*! | |
| 2265 * \brief pixThresholdPixelSum() | |
| 2266 * | |
| 2267 * \param[in] pix 1 bpp | |
| 2268 * \param[in] thresh threshold | |
| 2269 * \param[out] pabove 1 if above threshold; | |
| 2270 * 0 if equal to or less than threshold | |
| 2271 * \param[in] tab8 [optional] 8-bit pixel lookup table | |
| 2272 * \return 0 if OK; 1 on error | |
| 2273 * | |
| 2274 * <pre> | |
| 2275 * Notes: | |
| 2276 * (1) This sums the ON pixels and returns immediately if the count | |
| 2277 * goes above threshold. It is therefore more efficient | |
| 2278 * for matching images (by running this function on the xor of | |
| 2279 * the 2 images) than using pixCountPixels(), which counts all | |
| 2280 * pixels before returning. | |
| 2281 * </pre> | |
| 2282 */ | |
| 2283 l_ok | |
| 2284 pixThresholdPixelSum(PIX *pix, | |
| 2285 l_int32 thresh, | |
| 2286 l_int32 *pabove, | |
| 2287 l_int32 *tab8) | |
| 2288 { | |
| 2289 l_uint32 word, endmask; | |
| 2290 l_int32 *tab; | |
| 2291 l_int32 w, h, wpl, i, j; | |
| 2292 l_int32 fullwords, endbits, sum; | |
| 2293 l_uint32 *line, *data; | |
| 2294 | |
| 2295 if (!pabove) | |
| 2296 return ERROR_INT("&above not defined", __func__, 1); | |
| 2297 *pabove = 0; | |
| 2298 if (!pix || pixGetDepth(pix) != 1) | |
| 2299 return ERROR_INT("pix not defined or not 1 bpp", __func__, 1); | |
| 2300 | |
| 2301 tab = (tab8) ? tab8 : makePixelSumTab8(); | |
| 2302 pixGetDimensions(pix, &w, &h, NULL); | |
| 2303 wpl = pixGetWpl(pix); | |
| 2304 data = pixGetData(pix); | |
| 2305 fullwords = w >> 5; | |
| 2306 endbits = w & 31; | |
| 2307 endmask = 0xffffffff << (32 - endbits); | |
| 2308 | |
| 2309 sum = 0; | |
| 2310 for (i = 0; i < h; i++) { | |
| 2311 line = data + wpl * i; | |
| 2312 for (j = 0; j < fullwords; j++) { | |
| 2313 word = line[j]; | |
| 2314 if (word) { | |
| 2315 sum += tab[word & 0xff] + | |
| 2316 tab[(word >> 8) & 0xff] + | |
| 2317 tab[(word >> 16) & 0xff] + | |
| 2318 tab[(word >> 24) & 0xff]; | |
| 2319 } | |
| 2320 } | |
| 2321 if (endbits) { | |
| 2322 word = line[j] & endmask; | |
| 2323 if (word) { | |
| 2324 sum += tab[word & 0xff] + | |
| 2325 tab[(word >> 8) & 0xff] + | |
| 2326 tab[(word >> 16) & 0xff] + | |
| 2327 tab[(word >> 24) & 0xff]; | |
| 2328 } | |
| 2329 } | |
| 2330 if (sum > thresh) { | |
| 2331 *pabove = 1; | |
| 2332 if (!tab8) LEPT_FREE(tab); | |
| 2333 return 0; | |
| 2334 } | |
| 2335 } | |
| 2336 | |
| 2337 if (!tab8) LEPT_FREE(tab); | |
| 2338 return 0; | |
| 2339 } | |
| 2340 | |
| 2341 | |
| 2342 /*! | |
| 2343 * \brief makePixelSumTab8() | |
| 2344 * | |
| 2345 * \return table of 256 l_int32. | |
| 2346 * | |
| 2347 * <pre> | |
| 2348 * Notes: | |
| 2349 * (1) This table of integers gives the number of 1 bits | |
| 2350 * in the 8 bit index. | |
| 2351 * </pre> | |
| 2352 */ | |
| 2353 l_int32 * | |
| 2354 makePixelSumTab8(void) | |
| 2355 { | |
| 2356 l_uint8 byte; | |
| 2357 l_int32 i; | |
| 2358 l_int32 *tab; | |
| 2359 | |
| 2360 tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); | |
| 2361 for (i = 0; i < 256; i++) { | |
| 2362 byte = (l_uint8)i; | |
| 2363 tab[i] = (byte & 0x1) + | |
| 2364 ((byte >> 1) & 0x1) + | |
| 2365 ((byte >> 2) & 0x1) + | |
| 2366 ((byte >> 3) & 0x1) + | |
| 2367 ((byte >> 4) & 0x1) + | |
| 2368 ((byte >> 5) & 0x1) + | |
| 2369 ((byte >> 6) & 0x1) + | |
| 2370 ((byte >> 7) & 0x1); | |
| 2371 } | |
| 2372 return tab; | |
| 2373 } | |
| 2374 | |
| 2375 | |
| 2376 /*! | |
| 2377 * \brief makePixelCentroidTab8() | |
| 2378 * | |
| 2379 * \return table of 256 l_int32. | |
| 2380 * | |
| 2381 * <pre> | |
| 2382 * Notes: | |
| 2383 * (1) This table of integers gives the centroid weight of the 1 bits | |
| 2384 * in the 8 bit index. In other words, if sumtab is obtained by | |
| 2385 * makePixelSumTab8, and centroidtab is obtained by | |
| 2386 * makePixelCentroidTab8, then, for 1 <= i <= 255, | |
| 2387 * centroidtab[i] / (float)sumtab[i] | |
| 2388 * is the centroid of the 1 bits in the 8-bit index i, where the | |
| 2389 * MSB is considered to have position 0 and the LSB is considered | |
| 2390 * to have position 7. | |
| 2391 * </pre> | |
| 2392 */ | |
| 2393 l_int32 * | |
| 2394 makePixelCentroidTab8(void) | |
| 2395 { | |
| 2396 l_int32 i; | |
| 2397 l_int32 *tab; | |
| 2398 | |
| 2399 tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); | |
| 2400 tab[0] = 0; | |
| 2401 tab[1] = 7; | |
| 2402 for (i = 2; i < 4; i++) { | |
| 2403 tab[i] = tab[i - 2] + 6; | |
| 2404 } | |
| 2405 for (i = 4; i < 8; i++) { | |
| 2406 tab[i] = tab[i - 4] + 5; | |
| 2407 } | |
| 2408 for (i = 8; i < 16; i++) { | |
| 2409 tab[i] = tab[i - 8] + 4; | |
| 2410 } | |
| 2411 for (i = 16; i < 32; i++) { | |
| 2412 tab[i] = tab[i - 16] + 3; | |
| 2413 } | |
| 2414 for (i = 32; i < 64; i++) { | |
| 2415 tab[i] = tab[i - 32] + 2; | |
| 2416 } | |
| 2417 for (i = 64; i < 128; i++) { | |
| 2418 tab[i] = tab[i - 64] + 1; | |
| 2419 } | |
| 2420 for (i = 128; i < 256; i++) { | |
| 2421 tab[i] = tab[i - 128]; | |
| 2422 } | |
| 2423 return tab; | |
| 2424 } | |
| 2425 | |
| 2426 | |
| 2427 /*-------------------------------------------------------------* | |
| 2428 * Average of pixel values in gray images * | |
| 2429 *-------------------------------------------------------------*/ | |
| 2430 /*! | |
| 2431 * \brief pixAverageByRow() | |
| 2432 * | |
| 2433 * \param[in] pix 8 or 16 bpp; no colormap | |
| 2434 * \param[in] box [optional] clipping box for sum; can be null | |
| 2435 * \param[in] type L_WHITE_IS_MAX, L_BLACK_IS_MAX | |
| 2436 * \return na of pixel averages by row, or NULL on error | |
| 2437 * | |
| 2438 * <pre> | |
| 2439 * Notes: | |
| 2440 * (1) To resample for a bin size different from 1, use | |
| 2441 * numaUniformSampling() on the result of this function. | |
| 2442 * (2) If type == L_BLACK_IS_MAX, black pixels get the maximum | |
| 2443 * value (0xff for 8 bpp, 0xffff for 16 bpp) and white get 0. | |
| 2444 * </pre> | |
| 2445 */ | |
| 2446 NUMA * | |
| 2447 pixAverageByRow(PIX *pix, | |
| 2448 BOX *box, | |
| 2449 l_int32 type) | |
| 2450 { | |
| 2451 l_int32 i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh; | |
| 2452 l_uint32 *line, *data; | |
| 2453 l_float64 norm, sum; | |
| 2454 NUMA *na; | |
| 2455 | |
| 2456 if (!pix) | |
| 2457 return (NUMA *)ERROR_PTR("pix not defined", __func__, NULL); | |
| 2458 pixGetDimensions(pix, &w, &h, &d); | |
| 2459 if (d != 8 && d != 16) | |
| 2460 return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", __func__, NULL); | |
| 2461 if (type != L_WHITE_IS_MAX && type != L_BLACK_IS_MAX) | |
| 2462 return (NUMA *)ERROR_PTR("invalid type", __func__, NULL); | |
| 2463 if (pixGetColormap(pix) != NULL) | |
| 2464 return (NUMA *)ERROR_PTR("pix colormapped", __func__, NULL); | |
| 2465 | |
| 2466 if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, | |
| 2467 &bw, &bh) == 1) | |
| 2468 return (NUMA *)ERROR_PTR("invalid clipping box", __func__, NULL); | |
| 2469 | |
| 2470 norm = 1. / (l_float32)bw; | |
| 2471 if ((na = numaCreate(bh)) == NULL) | |
| 2472 return (NUMA *)ERROR_PTR("na not made", __func__, NULL); | |
| 2473 numaSetParameters(na, ystart, 1); | |
| 2474 data = pixGetData(pix); | |
| 2475 wpl = pixGetWpl(pix); | |
| 2476 for (i = ystart; i < yend; i++) { | |
| 2477 sum = 0.0; | |
| 2478 line = data + i * wpl; | |
| 2479 if (d == 8) { | |
| 2480 for (j = xstart; j < xend; j++) | |
| 2481 sum += GET_DATA_BYTE(line, j); | |
| 2482 if (type == L_BLACK_IS_MAX) | |
| 2483 sum = bw * 255 - sum; | |
| 2484 } else { /* d == 16 */ | |
| 2485 for (j = xstart; j < xend; j++) | |
| 2486 sum += GET_DATA_TWO_BYTES(line, j); | |
| 2487 if (type == L_BLACK_IS_MAX) | |
| 2488 sum = bw * 0xffff - sum; | |
| 2489 } | |
| 2490 numaAddNumber(na, (l_float32)(norm * sum)); | |
| 2491 } | |
| 2492 | |
| 2493 return na; | |
| 2494 } | |
| 2495 | |
| 2496 | |
| 2497 /*! | |
| 2498 * \brief pixAverageByColumn() | |
| 2499 * | |
| 2500 * \param[in] pix 8 or 16 bpp; no colormap | |
| 2501 * \param[in] box [optional] clipping box for sum; can be null | |
| 2502 * \param[in] type L_WHITE_IS_MAX, L_BLACK_IS_MAX | |
| 2503 * \return na of pixel averages by column, or NULL on error | |
| 2504 * | |
| 2505 * <pre> | |
| 2506 * Notes: | |
| 2507 * (1) To resample for a bin size different from 1, use | |
| 2508 * numaUniformSampling() on the result of this function. | |
| 2509 * (2) If type == L_BLACK_IS_MAX, black pixels get the maximum | |
| 2510 * value (0xff for 8 bpp, 0xffff for 16 bpp) and white get 0. | |
| 2511 * </pre> | |
| 2512 */ | |
| 2513 NUMA * | |
| 2514 pixAverageByColumn(PIX *pix, | |
| 2515 BOX *box, | |
| 2516 l_int32 type) | |
| 2517 { | |
| 2518 l_int32 i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh; | |
| 2519 l_uint32 *line, *data; | |
| 2520 l_float32 norm, sum; | |
| 2521 NUMA *na; | |
| 2522 | |
| 2523 if (!pix) | |
| 2524 return (NUMA *)ERROR_PTR("pix not defined", __func__, NULL); | |
| 2525 pixGetDimensions(pix, &w, &h, &d); | |
| 2526 | |
| 2527 if (d != 8 && d != 16) | |
| 2528 return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", __func__, NULL); | |
| 2529 if (type != L_WHITE_IS_MAX && type != L_BLACK_IS_MAX) | |
| 2530 return (NUMA *)ERROR_PTR("invalid type", __func__, NULL); | |
| 2531 if (pixGetColormap(pix) != NULL) | |
| 2532 return (NUMA *)ERROR_PTR("pix colormapped", __func__, NULL); | |
| 2533 | |
| 2534 if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, | |
| 2535 &bw, &bh) == 1) | |
| 2536 return (NUMA *)ERROR_PTR("invalid clipping box", __func__, NULL); | |
| 2537 | |
| 2538 if ((na = numaCreate(bw)) == NULL) | |
| 2539 return (NUMA *)ERROR_PTR("na not made", __func__, NULL); | |
| 2540 numaSetParameters(na, xstart, 1); | |
| 2541 norm = 1.f / (l_float32)bh; | |
| 2542 data = pixGetData(pix); | |
| 2543 wpl = pixGetWpl(pix); | |
| 2544 for (j = xstart; j < xend; j++) { | |
| 2545 sum = 0.0; | |
| 2546 if (d == 8) { | |
| 2547 for (i = ystart; i < yend; i++) { | |
| 2548 line = data + i * wpl; | |
| 2549 sum += GET_DATA_BYTE(line, j); | |
| 2550 } | |
| 2551 if (type == L_BLACK_IS_MAX) | |
| 2552 sum = bh * 255 - sum; | |
| 2553 } else { /* d == 16 */ | |
| 2554 for (i = ystart; i < yend; i++) { | |
| 2555 line = data + i * wpl; | |
| 2556 sum += GET_DATA_TWO_BYTES(line, j); | |
| 2557 } | |
| 2558 if (type == L_BLACK_IS_MAX) | |
| 2559 sum = bh * 0xffff - sum; | |
| 2560 } | |
| 2561 numaAddNumber(na, (l_float32)(norm * sum)); | |
| 2562 } | |
| 2563 | |
| 2564 return na; | |
| 2565 } | |
| 2566 | |
| 2567 | |
| 2568 /*! | |
| 2569 * \brief pixAverageInRect() | |
| 2570 * | |
| 2571 * \param[in] pixs 1, 2, 4, 8 bpp; not cmapped | |
| 2572 * \param[in] pixm [optional] 1 bpp mask; if null, use all pixels | |
| 2573 * \param[in] box [optional] if null, use entire image | |
| 2574 * \param[in] minval ignore values less than this | |
| 2575 * \param[in] maxval ignore values greater than this | |
| 2576 * \param[in] subsamp subsample factor: integer; use 1 for all pixels | |
| 2577 * \param[out] pave average of pixel values under consideration | |
| 2578 * \return 0 if OK; 1 on error; 2 if all pixels are filtered out | |
| 2579 * | |
| 2580 * <pre> | |
| 2581 * Notes: | |
| 2582 * (1) The average is computed with 4 optional filters: a rectangle, | |
| 2583 * a mask, a contiguous set of range values, and subsampling. | |
| 2584 * In practice you might use only one or two of these. | |
| 2585 * (2) The mask %pixm is a blocking mask: only count pixels in the bg. | |
| 2586 * If it exists, alignment is assumed at UL corner and computation | |
| 2587 * is over the minimum intersection of %pixs and %pixm. | |
| 2588 * If you want the average of pixels under the mask fg, invert it. | |
| 2589 * (3) Set the range limits %minval = 0 and %maxval = 255 to use | |
| 2590 * all non-masked pixels (regardless of value) in the average. | |
| 2591 * (4) If no pixels are used in the averaging, the returned average | |
| 2592 * value is 0 and the function returns 2. This is not an error, | |
| 2593 * but it says to disregard the returned average value. | |
| 2594 * (5) For example, to average all pixels in a given clipping rect %box, | |
| 2595 * pixAverageInRect(pixs, NULL, box, 0, 255, 1, &aveval); | |
| 2596 * </pre> | |
| 2597 */ | |
| 2598 l_ok | |
| 2599 pixAverageInRect(PIX *pixs, | |
| 2600 PIX *pixm, | |
| 2601 BOX *box, | |
| 2602 l_int32 minval, | |
| 2603 l_int32 maxval, | |
| 2604 l_int32 subsamp, | |
| 2605 l_float32 *pave) | |
| 2606 { | |
| 2607 l_int32 w, h, d, wpls, wm, hm, dm, wplm, val, count; | |
| 2608 l_int32 i, j, xstart, xend, ystart, yend; | |
| 2609 l_uint32 *datas, *datam = NULL, *lines, *linem = NULL; | |
| 2610 l_float64 sum; | |
| 2611 | |
| 2612 if (!pave) | |
| 2613 return ERROR_INT("&ave not defined", __func__, 1); | |
| 2614 *pave = 0; | |
| 2615 if (!pixs) | |
| 2616 return ERROR_INT("pixs not defined", __func__, 1); | |
| 2617 if (pixGetColormap(pixs) != NULL) | |
| 2618 return ERROR_INT("pixs is colormapped", __func__, 1); | |
| 2619 pixGetDimensions(pixs, &w, &h, &d); | |
| 2620 if (d != 1 && d != 2 && d != 4 && d != 8) | |
| 2621 return ERROR_INT("pixs not 1, 2, 4 or 8 bpp", __func__, 1); | |
| 2622 if (pixm) { | |
| 2623 pixGetDimensions(pixm, &wm, &hm, &dm); | |
| 2624 if (dm != 1) | |
| 2625 return ERROR_INT("pixm not 1 bpp", __func__, 1); | |
| 2626 w = L_MIN(w, wm); | |
| 2627 h = L_MIN(h, hm); | |
| 2628 } | |
| 2629 if (subsamp < 1) | |
| 2630 return ERROR_INT("subsamp must be >= 1", __func__, 1); | |
| 2631 | |
| 2632 if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, | |
| 2633 NULL, NULL) == 1) | |
| 2634 return ERROR_INT("invalid clipping box", __func__, 1); | |
| 2635 | |
| 2636 datas = pixGetData(pixs); | |
| 2637 wpls = pixGetWpl(pixs); | |
| 2638 if (pixm) { | |
| 2639 datam = pixGetData(pixm); | |
| 2640 wplm = pixGetWpl(pixm); | |
| 2641 } | |
| 2642 sum = 0.0; | |
| 2643 count = 0; | |
| 2644 for (i = ystart; i < yend; i += subsamp) { | |
| 2645 lines = datas + i * wpls; | |
| 2646 if (pixm) | |
| 2647 linem = datam + i * wplm; | |
| 2648 for (j = xstart; j < xend; j += subsamp) { | |
| 2649 if (pixm && (GET_DATA_BIT(linem, j) == 1)) | |
| 2650 continue; | |
| 2651 if (d == 1) | |
| 2652 val = GET_DATA_BIT(lines, j); | |
| 2653 else if (d == 2) | |
| 2654 val = GET_DATA_DIBIT(lines, j); | |
| 2655 else if (d == 4) | |
| 2656 val = GET_DATA_QBIT(lines, j); | |
| 2657 else /* d == 8 */ | |
| 2658 val = GET_DATA_BYTE(lines, j); | |
| 2659 if (val >= minval && val <= maxval) { | |
| 2660 sum += val; | |
| 2661 count++; | |
| 2662 } | |
| 2663 } | |
| 2664 } | |
| 2665 | |
| 2666 if (count == 0) | |
| 2667 return 2; /* not an error; don't use the average value (0.0) */ | |
| 2668 *pave = sum / (l_float32)count; | |
| 2669 return 0; | |
| 2670 } | |
| 2671 | |
| 2672 | |
| 2673 /*-------------------------------------------------------------* | |
| 2674 * Average of pixel values in RGB images * | |
| 2675 *-------------------------------------------------------------*/ | |
| 2676 /*! | |
| 2677 * \brief pixAverageInRectRGB() | |
| 2678 * | |
| 2679 * \param[in] pixs rgb; not cmapped | |
| 2680 * \param[in] pixm [optional] 1 bpp mask; if null, use all pixels | |
| 2681 * \param[in] box [optional] if null, use entire image | |
| 2682 * \param[in] subsamp subsample factor: integer; use 1 for all pixels | |
| 2683 * \param[out] pave average color of pixel values under consideration, | |
| 2684 * in format 0xrrggbb00. | |
| 2685 * \return 0 if OK; 1 on error; 2 if all pixels are filtered out | |
| 2686 * | |
| 2687 * <pre> | |
| 2688 * Notes: | |
| 2689 * (1) The average is computed with 3 optional filters: a rectangle, | |
| 2690 * a mask, and subsampling. | |
| 2691 * In practice you might use only one or two of these. | |
| 2692 * (2) The mask %pixm is a blocking mask: only count pixels in the bg. | |
| 2693 * If it exists, alignment is assumed at UL corner and computation | |
| 2694 * is over the minimum intersection of %pixs and %pixm. | |
| 2695 * If you want the average of pixels under the mask fg, invert it. | |
| 2696 * (3) If no pixels are used in the averaging, the returned average | |
| 2697 * value is 0 and the function returns 2. This is not an error, | |
| 2698 * but it says to disregard the returned average value. | |
| 2699 * (4) For example, to average all pixels in a given clipping rect %box, | |
| 2700 * pixAverageInRectRGB(pixs, NULL, box, 1, &aveval); | |
| 2701 * </pre> | |
| 2702 */ | |
| 2703 l_ok | |
| 2704 pixAverageInRectRGB(PIX *pixs, | |
| 2705 PIX *pixm, | |
| 2706 BOX *box, | |
| 2707 l_int32 subsamp, | |
| 2708 l_uint32 *pave) | |
| 2709 { | |
| 2710 l_int32 w, h, wpls, wm, hm, dm, wplm, i, j, xstart, xend, ystart, yend; | |
| 2711 l_int32 rval, gval, bval, rave, gave, bave, count; | |
| 2712 l_uint32 *datas, *datam = NULL, *lines, *linem = NULL; | |
| 2713 l_uint32 pixel; | |
| 2714 l_float64 rsum, gsum, bsum; | |
| 2715 | |
| 2716 if (!pave) | |
| 2717 return ERROR_INT("&ave not defined", __func__, 1); | |
| 2718 *pave = 0; | |
| 2719 if (!pixs || pixGetDepth(pixs) != 32) | |
| 2720 return ERROR_INT("pixs undefined or not 32 bpp", __func__, 1); | |
| 2721 pixGetDimensions(pixs, &w, &h, NULL); | |
| 2722 if (pixm) { | |
| 2723 pixGetDimensions(pixm, &wm, &hm, &dm); | |
| 2724 if (dm != 1) | |
| 2725 return ERROR_INT("pixm not 1 bpp", __func__, 1); | |
| 2726 w = L_MIN(w, wm); | |
| 2727 h = L_MIN(h, hm); | |
| 2728 } | |
| 2729 if (subsamp < 1) | |
| 2730 return ERROR_INT("subsamp must be >= 1", __func__, 1); | |
| 2731 | |
| 2732 if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, | |
| 2733 NULL, NULL) == 1) | |
| 2734 return ERROR_INT("invalid clipping box", __func__, 1); | |
| 2735 | |
| 2736 datas = pixGetData(pixs); | |
| 2737 wpls = pixGetWpl(pixs); | |
| 2738 if (pixm) { | |
| 2739 datam = pixGetData(pixm); | |
| 2740 wplm = pixGetWpl(pixm); | |
| 2741 } | |
| 2742 rsum = gsum = bsum = 0.0; | |
| 2743 count = 0; | |
| 2744 for (i = ystart; i < yend; i += subsamp) { | |
| 2745 lines = datas + i * wpls; | |
| 2746 if (pixm) | |
| 2747 linem = datam + i * wplm; | |
| 2748 for (j = xstart; j < xend; j += subsamp) { | |
| 2749 if (pixm && (GET_DATA_BIT(linem, j) == 1)) | |
| 2750 continue; | |
| 2751 pixel = *(lines + j); | |
| 2752 extractRGBValues(pixel, &rval, &gval, &bval); | |
| 2753 rsum += rval; | |
| 2754 gsum += gval; | |
| 2755 bsum += bval; | |
| 2756 count++; | |
| 2757 } | |
| 2758 } | |
| 2759 | |
| 2760 if (count == 0) | |
| 2761 return 2; /* not an error */ | |
| 2762 rave = (l_uint32)(rsum / (l_float64)count); | |
| 2763 gave = (l_uint32)(gsum / (l_float64)count); | |
| 2764 bave = (l_uint32)(bsum / (l_float64)count); | |
| 2765 composeRGBPixel(rave, gave, bave, pave); | |
| 2766 return 0; | |
| 2767 } | |
| 2768 | |
| 2769 | |
| 2770 /*------------------------------------------------------------------* | |
| 2771 * Variance of pixel values in gray images * | |
| 2772 *------------------------------------------------------------------*/ | |
| 2773 /*! | |
| 2774 * \brief pixVarianceByRow() | |
| 2775 * | |
| 2776 * \param[in] pix 8 or 16 bpp; no colormap | |
| 2777 * \param[in] box [optional] clipping box for variance; can be null | |
| 2778 * \return na of rmsdev by row, or NULL on error | |
| 2779 * | |
| 2780 * <pre> | |
| 2781 * Notes: | |
| 2782 * (1) To resample for a bin size different from 1, use | |
| 2783 * numaUniformSampling() on the result of this function. | |
| 2784 * (2) We are actually computing the RMS deviation in each row. | |
| 2785 * This is the square root of the variance. | |
| 2786 * </pre> | |
| 2787 */ | |
| 2788 NUMA * | |
| 2789 pixVarianceByRow(PIX *pix, | |
| 2790 BOX *box) | |
| 2791 { | |
| 2792 l_int32 i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh, val; | |
| 2793 l_uint32 *line, *data; | |
| 2794 l_float64 sum1, sum2, norm, ave, var, rootvar; | |
| 2795 NUMA *na; | |
| 2796 | |
| 2797 if (!pix) | |
| 2798 return (NUMA *)ERROR_PTR("pix not defined", __func__, NULL); | |
| 2799 pixGetDimensions(pix, &w, &h, &d); | |
| 2800 if (d != 8 && d != 16) | |
| 2801 return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", __func__, NULL); | |
| 2802 if (pixGetColormap(pix) != NULL) | |
| 2803 return (NUMA *)ERROR_PTR("pix colormapped", __func__, NULL); | |
| 2804 | |
| 2805 if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, | |
| 2806 &bw, &bh) == 1) | |
| 2807 return (NUMA *)ERROR_PTR("invalid clipping box", __func__, NULL); | |
| 2808 | |
| 2809 if ((na = numaCreate(bh)) == NULL) | |
| 2810 return (NUMA *)ERROR_PTR("na not made", __func__, NULL); | |
| 2811 numaSetParameters(na, ystart, 1); | |
| 2812 norm = 1. / (l_float32)bw; | |
| 2813 data = pixGetData(pix); | |
| 2814 wpl = pixGetWpl(pix); | |
| 2815 for (i = ystart; i < yend; i++) { | |
| 2816 sum1 = sum2 = 0.0; | |
| 2817 line = data + i * wpl; | |
| 2818 for (j = xstart; j < xend; j++) { | |
| 2819 if (d == 8) | |
| 2820 val = GET_DATA_BYTE(line, j); | |
| 2821 else /* d == 16 */ | |
| 2822 val = GET_DATA_TWO_BYTES(line, j); | |
| 2823 sum1 += val; | |
| 2824 sum2 += (l_float64)(val) * val; | |
| 2825 } | |
| 2826 ave = norm * sum1; | |
| 2827 var = norm * sum2 - ave * ave; | |
| 2828 rootvar = sqrt(var); | |
| 2829 numaAddNumber(na, (l_float32)rootvar); | |
| 2830 } | |
| 2831 | |
| 2832 return na; | |
| 2833 } | |
| 2834 | |
| 2835 | |
| 2836 /*! | |
| 2837 * \brief pixVarianceByColumn() | |
| 2838 * | |
| 2839 * \param[in] pix 8 or 16 bpp; no colormap | |
| 2840 * \param[in] box [optional] clipping box for variance; can be null | |
| 2841 * \return na of rmsdev by column, or NULL on error | |
| 2842 * | |
| 2843 * <pre> | |
| 2844 * Notes: | |
| 2845 * (1) To resample for a bin size different from 1, use | |
| 2846 * numaUniformSampling() on the result of this function. | |
| 2847 * (2) We are actually computing the RMS deviation in each row. | |
| 2848 * This is the square root of the variance. | |
| 2849 * </pre> | |
| 2850 */ | |
| 2851 NUMA * | |
| 2852 pixVarianceByColumn(PIX *pix, | |
| 2853 BOX *box) | |
| 2854 { | |
| 2855 l_int32 i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh, val; | |
| 2856 l_uint32 *line, *data; | |
| 2857 l_float64 sum1, sum2, norm, ave, var, rootvar; | |
| 2858 NUMA *na; | |
| 2859 | |
| 2860 if (!pix) | |
| 2861 return (NUMA *)ERROR_PTR("pix not defined", __func__, NULL); | |
| 2862 pixGetDimensions(pix, &w, &h, &d); | |
| 2863 if (d != 8 && d != 16) | |
| 2864 return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", __func__, NULL); | |
| 2865 if (pixGetColormap(pix) != NULL) | |
| 2866 return (NUMA *)ERROR_PTR("pix colormapped", __func__, NULL); | |
| 2867 | |
| 2868 if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, | |
| 2869 &bw, &bh) == 1) | |
| 2870 return (NUMA *)ERROR_PTR("invalid clipping box", __func__, NULL); | |
| 2871 | |
| 2872 if ((na = numaCreate(bw)) == NULL) | |
| 2873 return (NUMA *)ERROR_PTR("na not made", __func__, NULL); | |
| 2874 numaSetParameters(na, xstart, 1); | |
| 2875 norm = 1. / (l_float32)bh; | |
| 2876 data = pixGetData(pix); | |
| 2877 wpl = pixGetWpl(pix); | |
| 2878 for (j = xstart; j < xend; j++) { | |
| 2879 sum1 = sum2 = 0.0; | |
| 2880 for (i = ystart; i < yend; i++) { | |
| 2881 line = data + wpl * i; | |
| 2882 if (d == 8) | |
| 2883 val = GET_DATA_BYTE(line, j); | |
| 2884 else /* d == 16 */ | |
| 2885 val = GET_DATA_TWO_BYTES(line, j); | |
| 2886 sum1 += val; | |
| 2887 sum2 += (l_float64)(val) * val; | |
| 2888 } | |
| 2889 ave = norm * sum1; | |
| 2890 var = norm * sum2 - ave * ave; | |
| 2891 rootvar = sqrt(var); | |
| 2892 numaAddNumber(na, (l_float32)rootvar); | |
| 2893 } | |
| 2894 | |
| 2895 return na; | |
| 2896 } | |
| 2897 | |
| 2898 | |
| 2899 /*! | |
| 2900 * \brief pixVarianceInRect() | |
| 2901 * | |
| 2902 * \param[in] pix 1, 2, 4, 8 bpp; not cmapped | |
| 2903 * \param[in] box [optional] if null, use entire image | |
| 2904 * \param[out] prootvar sqrt variance of pixel values in region | |
| 2905 * \return 0 if OK; 1 on error | |
| 2906 */ | |
| 2907 l_ok | |
| 2908 pixVarianceInRect(PIX *pix, | |
| 2909 BOX *box, | |
| 2910 l_float32 *prootvar) | |
| 2911 { | |
| 2912 l_int32 w, h, d, wpl, i, j, xstart, xend, ystart, yend, bw, bh, val; | |
| 2913 l_uint32 *data, *line; | |
| 2914 l_float64 sum1, sum2, norm, ave, var; | |
| 2915 | |
| 2916 if (!prootvar) | |
| 2917 return ERROR_INT("&rootvar not defined", __func__, 1); | |
| 2918 *prootvar = 0.0; | |
| 2919 if (!pix) | |
| 2920 return ERROR_INT("pix not defined", __func__, 1); | |
| 2921 pixGetDimensions(pix, &w, &h, &d); | |
| 2922 if (d != 1 && d != 2 && d != 4 && d != 8) | |
| 2923 return ERROR_INT("pix not 1, 2, 4 or 8 bpp", __func__, 1); | |
| 2924 if (pixGetColormap(pix) != NULL) | |
| 2925 return ERROR_INT("pix is colormapped", __func__, 1); | |
| 2926 | |
| 2927 if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, | |
| 2928 &bw, &bh) == 1) | |
| 2929 return ERROR_INT("invalid clipping box", __func__, 1); | |
| 2930 | |
| 2931 wpl = pixGetWpl(pix); | |
| 2932 data = pixGetData(pix); | |
| 2933 sum1 = sum2 = 0.0; | |
| 2934 for (i = ystart; i < yend; i++) { | |
| 2935 line = data + i * wpl; | |
| 2936 for (j = xstart; j < xend; j++) { | |
| 2937 if (d == 1) { | |
| 2938 val = GET_DATA_BIT(line, j); | |
| 2939 sum1 += val; | |
| 2940 sum2 += (l_float64)(val) * val; | |
| 2941 } else if (d == 2) { | |
| 2942 val = GET_DATA_DIBIT(line, j); | |
| 2943 sum1 += val; | |
| 2944 sum2 += (l_float64)(val) * val; | |
| 2945 } else if (d == 4) { | |
| 2946 val = GET_DATA_QBIT(line, j); | |
| 2947 sum1 += val; | |
| 2948 sum2 += (l_float64)(val) * val; | |
| 2949 } else { /* d == 8 */ | |
| 2950 val = GET_DATA_BYTE(line, j); | |
| 2951 sum1 += val; | |
| 2952 sum2 += (l_float64)(val) * val; | |
| 2953 } | |
| 2954 } | |
| 2955 } | |
| 2956 norm = 1.0 / ((l_float64)(bw) * bh); | |
| 2957 ave = norm * sum1; | |
| 2958 var = norm * sum2 - ave * ave; | |
| 2959 *prootvar = (l_float32)sqrt(var); | |
| 2960 return 0; | |
| 2961 } | |
| 2962 | |
| 2963 | |
| 2964 /*---------------------------------------------------------------------* | |
| 2965 * Average of absolute value of pixel differences in gray images * | |
| 2966 *---------------------------------------------------------------------*/ | |
| 2967 /*! | |
| 2968 * \brief pixAbsDiffByRow() | |
| 2969 * | |
| 2970 * \param[in] pix 8 bpp; no colormap | |
| 2971 * \param[in] box [optional] clipping box for region; can be null | |
| 2972 * \return na of abs val pixel difference averages by row, or NULL on error | |
| 2973 * | |
| 2974 * <pre> | |
| 2975 * Notes: | |
| 2976 * (1) This is an average over differences of adjacent pixels along | |
| 2977 * each row. | |
| 2978 * (2) To resample for a bin size different from 1, use | |
| 2979 * numaUniformSampling() on the result of this function. | |
| 2980 * </pre> | |
| 2981 */ | |
| 2982 NUMA * | |
| 2983 pixAbsDiffByRow(PIX *pix, | |
| 2984 BOX *box) | |
| 2985 { | |
| 2986 l_int32 i, j, w, h, wpl, xstart, xend, ystart, yend, bw, bh, val0, val1; | |
| 2987 l_uint32 *line, *data; | |
| 2988 l_float64 norm, sum; | |
| 2989 NUMA *na; | |
| 2990 | |
| 2991 if (!pix || pixGetDepth(pix) != 8) | |
| 2992 return (NUMA *)ERROR_PTR("pix undefined or not 8 bpp", __func__, NULL); | |
| 2993 if (pixGetColormap(pix) != NULL) | |
| 2994 return (NUMA *)ERROR_PTR("pix colormapped", __func__, NULL); | |
| 2995 | |
| 2996 pixGetDimensions(pix, &w, &h, NULL); | |
| 2997 if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, | |
| 2998 &bw, &bh) == 1) | |
| 2999 return (NUMA *)ERROR_PTR("invalid clipping box", __func__, NULL); | |
| 3000 if (bw < 2) | |
| 3001 return (NUMA *)ERROR_PTR("row width must be >= 2", __func__, NULL); | |
| 3002 | |
| 3003 norm = 1. / (l_float32)(bw - 1); | |
| 3004 if ((na = numaCreate(bh)) == NULL) | |
| 3005 return (NUMA *)ERROR_PTR("na not made", __func__, NULL); | |
| 3006 numaSetParameters(na, ystart, 1); | |
| 3007 data = pixGetData(pix); | |
| 3008 wpl = pixGetWpl(pix); | |
| 3009 for (i = ystart; i < yend; i++) { | |
| 3010 sum = 0.0; | |
| 3011 line = data + i * wpl; | |
| 3012 val0 = GET_DATA_BYTE(line, xstart); | |
| 3013 for (j = xstart + 1; j < xend; j++) { | |
| 3014 val1 = GET_DATA_BYTE(line, j); | |
| 3015 sum += L_ABS(val1 - val0); | |
| 3016 val0 = val1; | |
| 3017 } | |
| 3018 numaAddNumber(na, (l_float32)(norm * sum)); | |
| 3019 } | |
| 3020 | |
| 3021 return na; | |
| 3022 } | |
| 3023 | |
| 3024 | |
| 3025 /*! | |
| 3026 * \brief pixAbsDiffByColumn() | |
| 3027 * | |
| 3028 * \param[in] pix 8 bpp; no colormap | |
| 3029 * \param[in] box [optional] clipping box for region; can be null | |
| 3030 * \return na of abs val pixel difference averages by column, | |
| 3031 * or NULL on error | |
| 3032 * | |
| 3033 * <pre> | |
| 3034 * Notes: | |
| 3035 * (1) This is an average over differences of adjacent pixels along | |
| 3036 * each column. | |
| 3037 * (2) To resample for a bin size different from 1, use | |
| 3038 * numaUniformSampling() on the result of this function. | |
| 3039 * </pre> | |
| 3040 */ | |
| 3041 NUMA * | |
| 3042 pixAbsDiffByColumn(PIX *pix, | |
| 3043 BOX *box) | |
| 3044 { | |
| 3045 l_int32 i, j, w, h, wpl, xstart, xend, ystart, yend, bw, bh, val0, val1; | |
| 3046 l_uint32 *line, *data; | |
| 3047 l_float64 norm, sum; | |
| 3048 NUMA *na; | |
| 3049 | |
| 3050 if (!pix || pixGetDepth(pix) != 8) | |
| 3051 return (NUMA *)ERROR_PTR("pix undefined or not 8 bpp", __func__, NULL); | |
| 3052 if (pixGetColormap(pix) != NULL) | |
| 3053 return (NUMA *)ERROR_PTR("pix colormapped", __func__, NULL); | |
| 3054 | |
| 3055 pixGetDimensions(pix, &w, &h, NULL); | |
| 3056 if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, | |
| 3057 &bw, &bh) == 1) | |
| 3058 return (NUMA *)ERROR_PTR("invalid clipping box", __func__, NULL); | |
| 3059 if (bh < 2) | |
| 3060 return (NUMA *)ERROR_PTR("column height must be >= 2", __func__, NULL); | |
| 3061 | |
| 3062 norm = 1. / (l_float32)(bh - 1); | |
| 3063 if ((na = numaCreate(bw)) == NULL) | |
| 3064 return (NUMA *)ERROR_PTR("na not made", __func__, NULL); | |
| 3065 numaSetParameters(na, xstart, 1); | |
| 3066 data = pixGetData(pix); | |
| 3067 wpl = pixGetWpl(pix); | |
| 3068 for (j = xstart; j < xend; j++) { | |
| 3069 sum = 0.0; | |
| 3070 line = data + ystart * wpl; | |
| 3071 val0 = GET_DATA_BYTE(line, j); | |
| 3072 for (i = ystart + 1; i < yend; i++) { | |
| 3073 line = data + i * wpl; | |
| 3074 val1 = GET_DATA_BYTE(line, j); | |
| 3075 sum += L_ABS(val1 - val0); | |
| 3076 val0 = val1; | |
| 3077 } | |
| 3078 numaAddNumber(na, (l_float32)(norm * sum)); | |
| 3079 } | |
| 3080 | |
| 3081 return na; | |
| 3082 } | |
| 3083 | |
| 3084 | |
| 3085 /*! | |
| 3086 * \brief pixAbsDiffInRect() | |
| 3087 * | |
| 3088 * \param[in] pix 8 bpp; not cmapped | |
| 3089 * \param[in] box [optional] if null, use entire image | |
| 3090 * \param[in] dir differences along L_HORIZONTAL_LINE or L_VERTICAL_LINE | |
| 3091 * \param[out] pabsdiff average of abs diff pixel values in region | |
| 3092 * \return 0 if OK; 1 on error | |
| 3093 * | |
| 3094 * <pre> | |
| 3095 * Notes: | |
| 3096 * (1) This gives the average over the abs val of differences of | |
| 3097 * adjacent pixels values, along either each | |
| 3098 * row: dir == L_HORIZONTAL_LINE | |
| 3099 * column: dir == L_VERTICAL_LINE | |
| 3100 * </pre> | |
| 3101 */ | |
| 3102 l_ok | |
| 3103 pixAbsDiffInRect(PIX *pix, | |
| 3104 BOX *box, | |
| 3105 l_int32 dir, | |
| 3106 l_float32 *pabsdiff) | |
| 3107 { | |
| 3108 l_int32 w, h, wpl, i, j, xstart, xend, ystart, yend, bw, bh, val0, val1; | |
| 3109 l_uint32 *data, *line; | |
| 3110 l_float64 norm, sum; | |
| 3111 | |
| 3112 if (!pabsdiff) | |
| 3113 return ERROR_INT("&absdiff not defined", __func__, 1); | |
| 3114 *pabsdiff = 0.0; | |
| 3115 if (!pix || pixGetDepth(pix) != 8) | |
| 3116 return ERROR_INT("pix undefined or not 8 bpp", __func__, 1); | |
| 3117 if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE) | |
| 3118 return ERROR_INT("invalid direction", __func__, 1); | |
| 3119 if (pixGetColormap(pix) != NULL) | |
| 3120 return ERROR_INT("pix is colormapped", __func__, 1); | |
| 3121 | |
| 3122 pixGetDimensions(pix, &w, &h, NULL); | |
| 3123 if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, | |
| 3124 &bw, &bh) == 1) | |
| 3125 return ERROR_INT("invalid clipping box", __func__, 1); | |
| 3126 | |
| 3127 wpl = pixGetWpl(pix); | |
| 3128 data = pixGetData(pix); | |
| 3129 if (dir == L_HORIZONTAL_LINE) { | |
| 3130 norm = 1. / (l_float32)(bh * (bw - 1)); | |
| 3131 sum = 0.0; | |
| 3132 for (i = ystart; i < yend; i++) { | |
| 3133 line = data + i * wpl; | |
| 3134 val0 = GET_DATA_BYTE(line, xstart); | |
| 3135 for (j = xstart + 1; j < xend; j++) { | |
| 3136 val1 = GET_DATA_BYTE(line, j); | |
| 3137 sum += L_ABS(val1 - val0); | |
| 3138 val0 = val1; | |
| 3139 } | |
| 3140 } | |
| 3141 } else { /* vertical line */ | |
| 3142 norm = 1. / (l_float32)(bw * (bh - 1)); | |
| 3143 sum = 0.0; | |
| 3144 for (j = xstart; j < xend; j++) { | |
| 3145 line = data + ystart * wpl; | |
| 3146 val0 = GET_DATA_BYTE(line, j); | |
| 3147 for (i = ystart + 1; i < yend; i++) { | |
| 3148 line = data + i * wpl; | |
| 3149 val1 = GET_DATA_BYTE(line, j); | |
| 3150 sum += L_ABS(val1 - val0); | |
| 3151 val0 = val1; | |
| 3152 } | |
| 3153 } | |
| 3154 } | |
| 3155 *pabsdiff = (l_float32)(norm * sum); | |
| 3156 return 0; | |
| 3157 } | |
| 3158 | |
| 3159 | |
| 3160 /*! | |
| 3161 * \brief pixAbsDiffOnLine() | |
| 3162 * | |
| 3163 * \param[in] pix 8 bpp; not cmapped | |
| 3164 * \param[in] x1, y1 first point; x1 <= x2, y1 <= y2 | |
| 3165 * \param[in] x2, y2 first point | |
| 3166 * \param[out] pabsdiff average of abs diff pixel values on line | |
| 3167 * \return 0 if OK; 1 on error | |
| 3168 * | |
| 3169 * <pre> | |
| 3170 * Notes: | |
| 3171 * (1) This gives the average over the abs val of differences of | |
| 3172 * adjacent pixels values, along a line that is either horizontal | |
| 3173 * or vertical. | |
| 3174 * (2) If horizontal, require x1 < x2; if vertical, require y1 < y2. | |
| 3175 * </pre> | |
| 3176 */ | |
| 3177 l_ok | |
| 3178 pixAbsDiffOnLine(PIX *pix, | |
| 3179 l_int32 x1, | |
| 3180 l_int32 y1, | |
| 3181 l_int32 x2, | |
| 3182 l_int32 y2, | |
| 3183 l_float32 *pabsdiff) | |
| 3184 { | |
| 3185 l_int32 w, h, i, j, dir, size, sum; | |
| 3186 l_uint32 val0, val1; | |
| 3187 | |
| 3188 if (!pabsdiff) | |
| 3189 return ERROR_INT("&absdiff not defined", __func__, 1); | |
| 3190 *pabsdiff = 0.0; | |
| 3191 if (!pix || pixGetDepth(pix) != 8) | |
| 3192 return ERROR_INT("pix undefined or not 8 bpp", __func__, 1); | |
| 3193 if (y1 == y2) { | |
| 3194 dir = L_HORIZONTAL_LINE; | |
| 3195 } else if (x1 == x2) { | |
| 3196 dir = L_VERTICAL_LINE; | |
| 3197 } else { | |
| 3198 return ERROR_INT("line is neither horiz nor vert", __func__, 1); | |
| 3199 } | |
| 3200 if (pixGetColormap(pix) != NULL) | |
| 3201 return ERROR_INT("pix is colormapped", __func__, 1); | |
| 3202 | |
| 3203 pixGetDimensions(pix, &w, &h, NULL); | |
| 3204 sum = 0; | |
| 3205 if (dir == L_HORIZONTAL_LINE) { | |
| 3206 x1 = L_MAX(x1, 0); | |
| 3207 x2 = L_MIN(x2, w - 1); | |
| 3208 if (x1 >= x2) | |
| 3209 return ERROR_INT("x1 >= x2", __func__, 1); | |
| 3210 size = x2 - x1; | |
| 3211 pixGetPixel(pix, x1, y1, &val0); | |
| 3212 for (j = x1 + 1; j <= x2; j++) { | |
| 3213 pixGetPixel(pix, j, y1, &val1); | |
| 3214 sum += L_ABS((l_int32)val1 - (l_int32)val0); | |
| 3215 val0 = val1; | |
| 3216 } | |
| 3217 } else { /* vertical */ | |
| 3218 y1 = L_MAX(y1, 0); | |
| 3219 y2 = L_MIN(y2, h - 1); | |
| 3220 if (y1 >= y2) | |
| 3221 return ERROR_INT("y1 >= y2", __func__, 1); | |
| 3222 size = y2 - y1; | |
| 3223 pixGetPixel(pix, x1, y1, &val0); | |
| 3224 for (i = y1 + 1; i <= y2; i++) { | |
| 3225 pixGetPixel(pix, x1, i, &val1); | |
| 3226 sum += L_ABS((l_int32)val1 - (l_int32)val0); | |
| 3227 val0 = val1; | |
| 3228 } | |
| 3229 } | |
| 3230 *pabsdiff = (l_float32)sum / (l_float32)size; | |
| 3231 return 0; | |
| 3232 } | |
| 3233 | |
| 3234 | |
| 3235 /*-------------------------------------------------------------* | |
| 3236 * Count of pixels with specific value * | |
| 3237 *-------------------------------------------------------------*/ | |
| 3238 /*! | |
| 3239 * \brief pixCountArbInRect() | |
| 3240 * | |
| 3241 * \param[in] pixs 1,2,4,8 bpp; can be colormapped | |
| 3242 * \param[in] box [optional] over which count is made; | |
| 3243 * use entire image if NULL | |
| 3244 * \param[in] val pixel value to count | |
| 3245 * \param[in] factor subsampling factor; integer >= 1 | |
| 3246 * \param[out] pcount count; estimate it if factor > 1 | |
| 3247 * \return na histogram, or NULL on error | |
| 3248 * | |
| 3249 * <pre> | |
| 3250 * Notes: | |
| 3251 * (1) If pixs is cmapped, %val is compared to the colormap index; | |
| 3252 * otherwise, %val is compared to the grayscale value. | |
| 3253 * (2) Set the subsampling %factor > 1 to reduce the amount of computation. | |
| 3254 * If %factor > 1, multiply the count by %factor * %factor. | |
| 3255 * </pre> | |
| 3256 */ | |
| 3257 l_int32 | |
| 3258 pixCountArbInRect(PIX *pixs, | |
| 3259 BOX *box, | |
| 3260 l_int32 val, | |
| 3261 l_int32 factor, | |
| 3262 l_int32 *pcount) | |
| 3263 { | |
| 3264 l_int32 i, j, bx, by, bw, bh, w, h, d, wpl, pixval; | |
| 3265 l_uint32 *data, *line; | |
| 3266 | |
| 3267 if (!pcount) | |
| 3268 return ERROR_INT("&count not defined", __func__, 1); | |
| 3269 *pcount = 0; | |
| 3270 if (!pixs) | |
| 3271 return ERROR_INT("pixs not defined", __func__, 1); | |
| 3272 d = pixGetDepth(pixs); | |
| 3273 if (d != 1 && d != 2 && d != 4 && d != 8) | |
| 3274 return ERROR_INT("pixs not 1, 2, 4 or 8 bpp", __func__, 1); | |
| 3275 if (val < 0) | |
| 3276 return ERROR_INT("val < 0", __func__, 1); | |
| 3277 if (val > (1 << d) - 1) { | |
| 3278 L_ERROR("invalid val = %d for depth %d\n", __func__, val, d); | |
| 3279 return 1; | |
| 3280 } | |
| 3281 if (factor < 1) | |
| 3282 return ERROR_INT("sampling factor < 1", __func__, 1); | |
| 3283 | |
| 3284 pixGetDimensions(pixs, &w, &h, NULL); | |
| 3285 data = pixGetData(pixs); | |
| 3286 wpl = pixGetWpl(pixs); | |
| 3287 if (!box) { | |
| 3288 for (i = 0; i < h; i += factor) { | |
| 3289 line = data + i * wpl; | |
| 3290 for (j = 0; j < w; j += factor) { | |
| 3291 if (d == 8) { | |
| 3292 pixval = GET_DATA_BYTE(line, j); | |
| 3293 } else if (d == 1) { | |
| 3294 pixval = GET_DATA_BIT(line, j); | |
| 3295 } else if (d == 2) { | |
| 3296 pixval = GET_DATA_DIBIT(line, j); | |
| 3297 } else /* d == 4 */ { | |
| 3298 pixval = GET_DATA_QBIT(line, j); | |
| 3299 } | |
| 3300 if (pixval == val) (*pcount)++; | |
| 3301 } | |
| 3302 } | |
| 3303 } else { | |
| 3304 boxGetGeometry(box, &bx, &by, &bw, &bh); | |
| 3305 for (i = 0; i < bh; i += factor) { | |
| 3306 if (by + i < 0 || by + i >= h) continue; | |
| 3307 line = data + (by + i) * wpl; | |
| 3308 for (j = 0; j < bw; j += factor) { | |
| 3309 if (bx + j < 0 || bx + j >= w) continue; | |
| 3310 if (d == 8) { | |
| 3311 pixval = GET_DATA_BYTE(line, bx + j); | |
| 3312 } else if (d == 1) { | |
| 3313 pixval = GET_DATA_BIT(line, bx + j); | |
| 3314 } else if (d == 2) { | |
| 3315 pixval = GET_DATA_DIBIT(line, bx + j); | |
| 3316 } else /* d == 4 */ { | |
| 3317 pixval = GET_DATA_QBIT(line, bx + j); | |
| 3318 } | |
| 3319 if (pixval == val) (*pcount)++; | |
| 3320 } | |
| 3321 } | |
| 3322 } | |
| 3323 | |
| 3324 if (factor > 1) /* assume pixel color is randomly distributed */ | |
| 3325 *pcount = *pcount * factor * factor; | |
| 3326 return 0; | |
| 3327 } | |
| 3328 | |
| 3329 | |
| 3330 /*-------------------------------------------------------------* | |
| 3331 * Mirrored tiling of a smaller image * | |
| 3332 *-------------------------------------------------------------*/ | |
| 3333 /*! | |
| 3334 * \brief pixMirroredTiling() | |
| 3335 * | |
| 3336 * \param[in] pixs 8 or 32 bpp, small tile; to be replicated | |
| 3337 * \param[in] w, h dimensions of output pix | |
| 3338 * \return pixd usually larger pix, mirror-tiled with pixs, | |
| 3339 * or NULL on error | |
| 3340 * | |
| 3341 * <pre> | |
| 3342 * Notes: | |
| 3343 * (1) This uses mirrored tiling, where each row alternates | |
| 3344 * with LR flips and every column alternates with TB | |
| 3345 * flips, such that the result is a tiling with identical | |
| 3346 * 2 x 2 tiles, each of which is composed of these transforms: | |
| 3347 * ----------------- | |
| 3348 * | 1 | LR | | |
| 3349 * ----------------- | |
| 3350 * | TB | LR/TB | | |
| 3351 * ----------------- | |
| 3352 * </pre> | |
| 3353 */ | |
| 3354 PIX * | |
| 3355 pixMirroredTiling(PIX *pixs, | |
| 3356 l_int32 w, | |
| 3357 l_int32 h) | |
| 3358 { | |
| 3359 l_int32 wt, ht, d, i, j, nx, ny; | |
| 3360 PIX *pixd, *pixsfx, *pixsfy, *pixsfxy, *pix; | |
| 3361 | |
| 3362 if (!pixs) | |
| 3363 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 3364 pixGetDimensions(pixs, &wt, &ht, &d); | |
| 3365 if (wt <= 0 || ht <= 0) | |
| 3366 return (PIX *)ERROR_PTR("pixs size illegal", __func__, NULL); | |
| 3367 if (d != 8 && d != 32) | |
| 3368 return (PIX *)ERROR_PTR("depth not 32 bpp", __func__, NULL); | |
| 3369 | |
| 3370 if ((pixd = pixCreate(w, h, d)) == NULL) | |
| 3371 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 3372 pixCopySpp(pixd, pixs); | |
| 3373 | |
| 3374 nx = (w + wt - 1) / wt; | |
| 3375 ny = (h + ht - 1) / ht; | |
| 3376 pixsfx = pixFlipLR(NULL, pixs); | |
| 3377 pixsfy = pixFlipTB(NULL, pixs); | |
| 3378 pixsfxy = pixFlipTB(NULL, pixsfx); | |
| 3379 for (i = 0; i < ny; i++) { | |
| 3380 for (j = 0; j < nx; j++) { | |
| 3381 pix = pixs; | |
| 3382 if ((i & 1) && !(j & 1)) | |
| 3383 pix = pixsfy; | |
| 3384 else if (!(i & 1) && (j & 1)) | |
| 3385 pix = pixsfx; | |
| 3386 else if ((i & 1) && (j & 1)) | |
| 3387 pix = pixsfxy; | |
| 3388 pixRasterop(pixd, j * wt, i * ht, wt, ht, PIX_SRC, pix, 0, 0); | |
| 3389 } | |
| 3390 } | |
| 3391 | |
| 3392 pixDestroy(&pixsfx); | |
| 3393 pixDestroy(&pixsfy); | |
| 3394 pixDestroy(&pixsfxy); | |
| 3395 return pixd; | |
| 3396 } | |
| 3397 | |
| 3398 | |
| 3399 /*! | |
| 3400 * \brief pixFindRepCloseTile() | |
| 3401 * | |
| 3402 * \param[in] pixs 32 bpp rgb | |
| 3403 * \param[in] box region of pixs to search around | |
| 3404 * \param[in] searchdir L_HORIZ or L_VERT; direction to search | |
| 3405 * \param[in] mindist min distance of selected tile edge from box; >= 0 | |
| 3406 * \param[in] tsize tile size; > 1; even; typically ~50 | |
| 3407 * \param[in] ntiles number of tiles tested in each row/column | |
| 3408 * \param[out] pboxtile region of best tile | |
| 3409 * \param[in] debug 1 for debug output | |
| 3410 * \return 0 if OK, 1 on error | |
| 3411 * | |
| 3412 * <pre> | |
| 3413 * Notes: | |
| 3414 * (1) This looks for one or two square tiles with conforming median | |
| 3415 * intensity and low variance, that is outside but near the input box. | |
| 3416 * (2) %mindist specifies the gap between the box and the | |
| 3417 * potential tiles. The tiles are given an overlap of 50%. | |
| 3418 * %ntiles specifies the number of tiles that are tested | |
| 3419 * beyond %mindist for each row or column. | |
| 3420 * (3) For example, if %mindist = 20, %tilesize = 50 and %ntiles = 3, | |
| 3421 * a horizontal search to the right will have 3 tiles in each row, | |
| 3422 * with left edges at 20, 45 and 70 from the right edge of the | |
| 3423 * input %box. The number of rows of tiles is determined by | |
| 3424 * the height of %box and %tsize, with the 50% overlap.. | |
| 3425 * </pre> | |
| 3426 */ | |
| 3427 l_ok | |
| 3428 pixFindRepCloseTile(PIX *pixs, | |
| 3429 BOX *box, | |
| 3430 l_int32 searchdir, | |
| 3431 l_int32 mindist, | |
| 3432 l_int32 tsize, | |
| 3433 l_int32 ntiles, | |
| 3434 BOX **pboxtile, | |
| 3435 l_int32 debug) | |
| 3436 { | |
| 3437 l_int32 w, h, i, n, bestindex; | |
| 3438 l_float32 var_of_mean, median_of_mean, median_of_stdev, mean_val, stdev_val; | |
| 3439 l_float32 mindels, bestdelm, delm, dels, mean, stdev; | |
| 3440 BOXA *boxa; | |
| 3441 NUMA *namean, *nastdev; | |
| 3442 PIX *pix, *pixg; | |
| 3443 PIXA *pixa; | |
| 3444 | |
| 3445 if (!pboxtile) | |
| 3446 return ERROR_INT("&boxtile not defined", __func__, 1); | |
| 3447 *pboxtile = NULL; | |
| 3448 if (!pixs) | |
| 3449 return ERROR_INT("pixs not defined", __func__, 1); | |
| 3450 if (!box) | |
| 3451 return ERROR_INT("box not defined", __func__, 1); | |
| 3452 if (searchdir != L_HORIZ && searchdir != L_VERT) | |
| 3453 return ERROR_INT("invalid searchdir", __func__, 1); | |
| 3454 if (mindist < 0) | |
| 3455 return ERROR_INT("mindist must be >= 0", __func__, 1); | |
| 3456 if (tsize < 2) | |
| 3457 return ERROR_INT("tsize must be > 1", __func__, 1); | |
| 3458 if (ntiles > 7) { | |
| 3459 L_WARNING("ntiles = %d; larger than suggested max of 7\n", | |
| 3460 __func__, ntiles); | |
| 3461 } | |
| 3462 | |
| 3463 /* Locate tile regions */ | |
| 3464 pixGetDimensions(pixs, &w, &h, NULL); | |
| 3465 boxa = findTileRegionsForSearch(box, w, h, searchdir, mindist, | |
| 3466 tsize, ntiles); | |
| 3467 if (!boxa) | |
| 3468 return ERROR_INT("no tiles found", __func__, 1); | |
| 3469 | |
| 3470 /* Generate the tiles and the mean and stdev of intensity */ | |
| 3471 pixa = pixClipRectangles(pixs, boxa); | |
| 3472 n = pixaGetCount(pixa); | |
| 3473 namean = numaCreate(n); | |
| 3474 nastdev = numaCreate(n); | |
| 3475 for (i = 0; i < n; i++) { | |
| 3476 pix = pixaGetPix(pixa, i, L_CLONE); | |
| 3477 pixg = pixConvertRGBToGray(pix, 0.33f, 0.34f, 0.33f); | |
| 3478 pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_MEAN_ABSVAL, &mean); | |
| 3479 pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_STANDARD_DEVIATION, &stdev); | |
| 3480 numaAddNumber(namean, mean); | |
| 3481 numaAddNumber(nastdev, stdev); | |
| 3482 pixDestroy(&pix); | |
| 3483 pixDestroy(&pixg); | |
| 3484 } | |
| 3485 | |
| 3486 /* Find the median and variance of the averages. We require | |
| 3487 * the best tile to have a mean pixel intensity within a standard | |
| 3488 * deviation of the median of mean intensities, and choose the | |
| 3489 * tile in that set with the smallest stdev of pixel intensities | |
| 3490 * (as a proxy for the tile with least visible structure). | |
| 3491 * The median of the stdev is used, for debugging, as a normalizing | |
| 3492 * factor for the stdev of intensities within a tile. */ | |
| 3493 numaGetStatsUsingHistogram(namean, 256, NULL, NULL, NULL, &var_of_mean, | |
| 3494 &median_of_mean, 0.0, NULL, NULL); | |
| 3495 numaGetStatsUsingHistogram(nastdev, 256, NULL, NULL, NULL, NULL, | |
| 3496 &median_of_stdev, 0.0, NULL, NULL); | |
| 3497 mindels = 1000.0; | |
| 3498 bestdelm = 1000.0; | |
| 3499 bestindex = 0; | |
| 3500 for (i = 0; i < n; i++) { | |
| 3501 numaGetFValue(namean, i, &mean_val); | |
| 3502 numaGetFValue(nastdev, i, &stdev_val); | |
| 3503 if (var_of_mean == 0.0) { /* uniform color; any box will do */ | |
| 3504 delm = 0.0; /* any value < 1.01 */ | |
| 3505 dels = 1.0; /* n'importe quoi */ | |
| 3506 } else { | |
| 3507 delm = L_ABS(mean_val - median_of_mean) / sqrt(var_of_mean); | |
| 3508 dels = stdev_val / median_of_stdev; | |
| 3509 } | |
| 3510 if (delm < 1.01) { | |
| 3511 if (dels < mindels) { | |
| 3512 if (debug) { | |
| 3513 lept_stderr("i = %d, mean = %7.3f, delm = %7.3f," | |
| 3514 " stdev = %7.3f, dels = %7.3f\n", | |
| 3515 i, mean_val, delm, stdev_val, dels); | |
| 3516 } | |
| 3517 mindels = dels; | |
| 3518 bestdelm = delm; | |
| 3519 bestindex = i; | |
| 3520 } | |
| 3521 } | |
| 3522 } | |
| 3523 *pboxtile = boxaGetBox(boxa, bestindex, L_COPY); | |
| 3524 | |
| 3525 if (debug) { | |
| 3526 L_INFO("median of mean = %7.3f\n", __func__, median_of_mean); | |
| 3527 L_INFO("standard dev of mean = %7.3f\n", __func__, sqrt(var_of_mean)); | |
| 3528 L_INFO("median of stdev = %7.3f\n", __func__, median_of_stdev); | |
| 3529 L_INFO("best tile: index = %d\n", __func__, bestindex); | |
| 3530 L_INFO("delta from median in units of stdev = %5.3f\n", | |
| 3531 __func__, bestdelm); | |
| 3532 L_INFO("stdev as fraction of median stdev = %5.3f\n", | |
| 3533 __func__, mindels); | |
| 3534 } | |
| 3535 | |
| 3536 numaDestroy(&namean); | |
| 3537 numaDestroy(&nastdev); | |
| 3538 pixaDestroy(&pixa); | |
| 3539 boxaDestroy(&boxa); | |
| 3540 return 0; | |
| 3541 } | |
| 3542 | |
| 3543 | |
| 3544 /*! | |
| 3545 * \brief findTileRegionsForSearch() | |
| 3546 * | |
| 3547 * \param[in] box region of Pix to search around | |
| 3548 * \param[in] w, h dimensions of Pix | |
| 3549 * \param[in] searchdir L_HORIZ or L_VERT; direction to search | |
| 3550 * \param[in] mindist min distance of selected tile edge from box; >= 0 | |
| 3551 * \param[in] tsize tile size; > 1; even; typically ~50 | |
| 3552 * \param[in] ntiles number of tiles tested in each row/column | |
| 3553 * \return boxa if OK, or NULL on error | |
| 3554 * | |
| 3555 * <pre> | |
| 3556 * Notes: | |
| 3557 * (1) See calling function pixfindRepCloseTile(). | |
| 3558 * </pre> | |
| 3559 */ | |
| 3560 static BOXA * | |
| 3561 findTileRegionsForSearch(BOX *box, | |
| 3562 l_int32 w, | |
| 3563 l_int32 h, | |
| 3564 l_int32 searchdir, | |
| 3565 l_int32 mindist, | |
| 3566 l_int32 tsize, | |
| 3567 l_int32 ntiles) | |
| 3568 { | |
| 3569 l_int32 bx, by, bw, bh, left, right, top, bot, i, j, nrows, ncols; | |
| 3570 l_int32 x0, y0, x, y, w_avail, w_needed, h_avail, h_needed, t_avail; | |
| 3571 BOX *box1; | |
| 3572 BOXA *boxa; | |
| 3573 | |
| 3574 if (!box) | |
| 3575 return (BOXA *)ERROR_PTR("box not defined", __func__, NULL); | |
| 3576 if (ntiles == 0) | |
| 3577 return (BOXA *)ERROR_PTR("no tiles requested", __func__, NULL); | |
| 3578 | |
| 3579 boxGetGeometry(box, &bx, &by, &bw, &bh); | |
| 3580 if (searchdir == L_HORIZ) { | |
| 3581 /* Find the tile parameters for the search. Note that the | |
| 3582 * tiles are overlapping by 50% in each direction. */ | |
| 3583 left = bx; /* distance to left of box */ | |
| 3584 right = w - bx - bw + 1; /* distance to right of box */ | |
| 3585 w_avail = L_MAX(left, right) - mindist; | |
| 3586 if (tsize & 1) tsize++; /* be sure it's even */ | |
| 3587 if (w_avail < tsize) { | |
| 3588 L_ERROR("tsize = %d, w_avail = %d\n", __func__, tsize, w_avail); | |
| 3589 return NULL; | |
| 3590 } | |
| 3591 w_needed = tsize + (ntiles - 1) * (tsize / 2); | |
| 3592 if (w_needed > w_avail) { | |
| 3593 t_avail = 1 + 2 * (w_avail - tsize) / tsize; | |
| 3594 L_WARNING("ntiles = %d; room for only %d\n", __func__, | |
| 3595 ntiles, t_avail); | |
| 3596 ntiles = t_avail; | |
| 3597 w_needed = tsize + (ntiles - 1) * (tsize / 2); | |
| 3598 } | |
| 3599 nrows = L_MAX(1, 1 + 2 * (bh - tsize) / tsize); | |
| 3600 | |
| 3601 /* Generate the tile regions to search */ | |
| 3602 boxa = boxaCreate(0); | |
| 3603 if (left > right) /* search to left */ | |
| 3604 x0 = bx - w_needed; | |
| 3605 else /* search to right */ | |
| 3606 x0 = bx + bw + mindist; | |
| 3607 for (i = 0; i < nrows; i++) { | |
| 3608 y = by + i * tsize / 2; | |
| 3609 for (j = 0; j < ntiles; j++) { | |
| 3610 x = x0 + j * tsize / 2; | |
| 3611 box1 = boxCreate(x, y, tsize, tsize); | |
| 3612 boxaAddBox(boxa, box1, L_INSERT); | |
| 3613 } | |
| 3614 } | |
| 3615 } else { /* L_VERT */ | |
| 3616 /* Find the tile parameters for the search */ | |
| 3617 top = by; /* distance above box */ | |
| 3618 bot = h - by - bh + 1; /* distance below box */ | |
| 3619 h_avail = L_MAX(top, bot) - mindist; | |
| 3620 if (h_avail < tsize) { | |
| 3621 L_ERROR("tsize = %d, h_avail = %d\n", __func__, tsize, h_avail); | |
| 3622 return NULL; | |
| 3623 } | |
| 3624 h_needed = tsize + (ntiles - 1) * (tsize / 2); | |
| 3625 if (h_needed > h_avail) { | |
| 3626 t_avail = 1 + 2 * (h_avail - tsize) / tsize; | |
| 3627 L_WARNING("ntiles = %d; room for only %d\n", __func__, | |
| 3628 ntiles, t_avail); | |
| 3629 ntiles = t_avail; | |
| 3630 h_needed = tsize + (ntiles - 1) * (tsize / 2); | |
| 3631 } | |
| 3632 ncols = L_MAX(1, 1 + 2 * (bw - tsize) / tsize); | |
| 3633 | |
| 3634 /* Generate the tile regions to search */ | |
| 3635 boxa = boxaCreate(0); | |
| 3636 if (top > bot) /* search above */ | |
| 3637 y0 = by - h_needed; | |
| 3638 else /* search below */ | |
| 3639 y0 = by + bh + mindist; | |
| 3640 for (j = 0; j < ncols; j++) { | |
| 3641 x = bx + j * tsize / 2; | |
| 3642 for (i = 0; i < ntiles; i++) { | |
| 3643 y = y0 + i * tsize / 2; | |
| 3644 box1 = boxCreate(x, y, tsize, tsize); | |
| 3645 boxaAddBox(boxa, box1, L_INSERT); | |
| 3646 } | |
| 3647 } | |
| 3648 } | |
| 3649 return boxa; | |
| 3650 } |
