Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/grayquant.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 grayquant.c | |
| 29 * <pre> | |
| 30 * | |
| 31 * Thresholding from 8 bpp to 1 bpp | |
| 32 * | |
| 33 * Floyd-Steinberg dithering to binary | |
| 34 * PIX *pixDitherToBinary() | |
| 35 * PIX *pixDitherToBinarySpec() | |
| 36 * static void ditherToBinaryLow() | |
| 37 * void ditherToBinaryLineLow() | |
| 38 * | |
| 39 * Simple (pixelwise) binarization with fixed threshold | |
| 40 * PIX *pixThresholdToBinary() | |
| 41 * static void thresholdToBinaryLow() | |
| 42 * void thresholdToBinaryLineLow() | |
| 43 * | |
| 44 * Binarization with variable threshold | |
| 45 * PIX *pixVarThresholdToBinary() | |
| 46 * | |
| 47 * Binarization by adaptive mapping | |
| 48 * PIX *pixAdaptThresholdToBinary() | |
| 49 * PIX *pixAdaptThresholdToBinaryGen() | |
| 50 * | |
| 51 * Generate a binary mask from pixels of particular values | |
| 52 * PIX *pixGenerateMaskByValue() | |
| 53 * PIX *pixGenerateMaskByBand() | |
| 54 * | |
| 55 * Thresholding from 8 bpp to 2 bpp | |
| 56 * | |
| 57 * Floyd-Steinberg-like dithering to 2 bpp | |
| 58 * PIX *pixDitherTo2bpp() | |
| 59 * PIX *pixDitherTo2bppSpec() | |
| 60 * static void ditherTo2bppLow() | |
| 61 * static void ditherTo2bppLineLow() | |
| 62 * static l_int32 make8To2DitherTables() | |
| 63 * | |
| 64 * Simple (pixelwise) thresholding to 2 bpp with optional cmap | |
| 65 * PIX *pixThresholdTo2bpp() | |
| 66 * static void thresholdTo2bppLow() | |
| 67 * | |
| 68 * Simple (pixelwise) thresholding from 8 bpp to 4 bpp | |
| 69 * PIX *pixThresholdTo4bpp() | |
| 70 * static void thresholdTo4bppLow() | |
| 71 * | |
| 72 * Simple (pixelwise) quantization on 8 bpp grayscale | |
| 73 * PIX *pixThresholdOn8bpp() | |
| 74 * | |
| 75 * Arbitrary (pixelwise) thresholding from 8 bpp to 2, 4 or 8 bpp | |
| 76 * PIX *pixThresholdGrayArb() | |
| 77 * | |
| 78 * Quantization tables for linear thresholds of grayscale images | |
| 79 * l_int32 *makeGrayQuantIndexTable() | |
| 80 * static l_int32 *makeGrayQuantTargetTable() | |
| 81 * | |
| 82 * Quantization table for arbitrary thresholding of grayscale images | |
| 83 * l_int32 makeGrayQuantTableArb() | |
| 84 * static l_int32 makeGrayQuantColormapArb() | |
| 85 * | |
| 86 * Thresholding from 32 bpp rgb to 1 bpp | |
| 87 * (really color quantization, but it's better placed in this file) | |
| 88 * PIX *pixGenerateMaskByBand32() | |
| 89 * PIX *pixGenerateMaskByDiscr32() | |
| 90 * | |
| 91 * Histogram-based grayscale quantization | |
| 92 * PIX *pixGrayQuantFromHisto() | |
| 93 * static l_int32 numaFillCmapFromHisto() | |
| 94 * | |
| 95 * Color quantize grayscale image using existing colormap | |
| 96 * PIX *pixGrayQuantFromCmap() | |
| 97 * </pre> | |
| 98 */ | |
| 99 | |
| 100 #ifdef HAVE_CONFIG_H | |
| 101 #include <config_auto.h> | |
| 102 #endif /* HAVE_CONFIG_H */ | |
| 103 | |
| 104 #include <string.h> | |
| 105 #include <math.h> | |
| 106 #include "allheaders.h" | |
| 107 | |
| 108 static void ditherToBinaryLow(l_uint32 *datad, l_int32 w, l_int32 h, | |
| 109 l_int32 wpld, l_uint32 *datas, l_int32 wpls, | |
| 110 l_uint32 *bufs1, l_uint32 *bufs2, | |
| 111 l_int32 lowerclip, l_int32 upperclip); | |
| 112 static void thresholdToBinaryLow(l_uint32 *datad, l_int32 w, l_int32 h, | |
| 113 l_int32 wpld, l_uint32 *datas, l_int32 d, | |
| 114 l_int32 wpls, l_int32 thresh); | |
| 115 static void ditherTo2bppLow(l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpld, | |
| 116 l_uint32 *datas, l_int32 wpls, l_uint32 *bufs1, | |
| 117 l_uint32 *bufs2, l_int32 *tabval, l_int32 *tab38, | |
| 118 l_int32 *tab14); | |
| 119 static void ditherTo2bppLineLow(l_uint32 *lined, l_int32 w, l_uint32 *bufs1, | |
| 120 l_uint32 *bufs2, l_int32 *tabval, | |
| 121 l_int32 *tab38, l_int32 *tab14, | |
| 122 l_int32 lastlineflag); | |
| 123 static l_int32 make8To2DitherTables(l_int32 **ptabval, l_int32 **ptab38, | |
| 124 l_int32 **ptab14, l_int32 cliptoblack, | |
| 125 l_int32 cliptowhite); | |
| 126 static void thresholdTo2bppLow(l_uint32 *datad, l_int32 h, l_int32 wpld, | |
| 127 l_uint32 *datas, l_int32 wpls, l_int32 *tab); | |
| 128 static void thresholdTo4bppLow(l_uint32 *datad, l_int32 h, l_int32 wpld, | |
| 129 l_uint32 *datas, l_int32 wpls, l_int32 *tab); | |
| 130 static l_int32 *makeGrayQuantTargetTable(l_int32 nlevels, l_int32 depth); | |
| 131 static l_int32 makeGrayQuantColormapArb(PIX *pixs, l_int32 *tab, | |
| 132 l_int32 outdepth, PIXCMAP **pcmap); | |
| 133 static l_int32 numaFillCmapFromHisto(NUMA *na, PIXCMAP *cmap, | |
| 134 l_float32 minfract, l_int32 maxsize, | |
| 135 l_int32 **plut); | |
| 136 | |
| 137 #ifndef NO_CONSOLE_IO | |
| 138 #define DEBUG_UNROLLING 0 | |
| 139 #endif /* ~NO_CONSOLE_IO */ | |
| 140 | |
| 141 /*------------------------------------------------------------------* | |
| 142 * Binarization by Floyd-Steinberg dithering * | |
| 143 *------------------------------------------------------------------*/ | |
| 144 /*! | |
| 145 * \brief pixDitherToBinary() | |
| 146 * | |
| 147 * \param[in] pixs | |
| 148 * \return pixd dithered binary, or NULL on error | |
| 149 * | |
| 150 * The Floyd-Steinberg error diffusion dithering algorithm | |
| 151 * binarizes an 8 bpp grayscale image to a threshold of 128. | |
| 152 * If a pixel has a value above 127, it is binarized to white | |
| 153 * and the excess below 255 is subtracted from three | |
| 154 * neighboring pixels in the fractions 3/8 to i, j+1, | |
| 155 * 3/8 to i+1, j) and 1/4 to (i+1,j+1, truncating to 0 | |
| 156 * if necessary. Likewise, if it the pixel has a value | |
| 157 * below 128, it is binarized to black and the excess above 0 | |
| 158 * is added to the neighboring pixels, truncating to 255 if necessary. | |
| 159 * | |
| 160 * This function differs from straight dithering in that it allows | |
| 161 * clipping of grayscale to 0 or 255 if the values are | |
| 162 * sufficiently close, without distribution of the excess. | |
| 163 * This uses default values to specify the range of lower | |
| 164 * and upper values near 0 and 255, rsp that are clipped | |
| 165 * to black and white without propagating the excess. | |
| 166 * Not propagating the excess has the effect of reducing the | |
| 167 * snake patterns in parts of the image that are nearly black or white; | |
| 168 * however, it also prevents the attempt to reproduce gray for those values. | |
| 169 * | |
| 170 * The implementation is straightforward. It uses a pair of | |
| 171 * line buffers to avoid changing pixs. It is about the same speed | |
| 172 * as pixDitherToBinaryLUT(), which uses three LUTs. | |
| 173 */ | |
| 174 PIX * | |
| 175 pixDitherToBinary(PIX *pixs) | |
| 176 { | |
| 177 if (!pixs) | |
| 178 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 179 if (pixGetDepth(pixs) != 8) | |
| 180 return (PIX *)ERROR_PTR("must be 8 bpp for dithering", __func__, NULL); | |
| 181 | |
| 182 return pixDitherToBinarySpec(pixs, DEFAULT_CLIP_LOWER_1, | |
| 183 DEFAULT_CLIP_UPPER_1); | |
| 184 } | |
| 185 | |
| 186 | |
| 187 /*! | |
| 188 * \brief pixDitherToBinarySpec() | |
| 189 * | |
| 190 * \param[in] pixs | |
| 191 * \param[in] lowerclip lower clip distance to black; use 0 for default | |
| 192 * \param[in] upperclip upper clip distance to white; use 0 for default | |
| 193 * \return pixd dithered binary, or NULL on error | |
| 194 * | |
| 195 * <pre> | |
| 196 * Notes: | |
| 197 * (1) See comments above in pixDitherToBinary() for details. | |
| 198 * (2) The input parameters lowerclip and upperclip specify the range | |
| 199 * of lower and upper values (near 0 and 255, rsp) that are | |
| 200 * clipped to black and white without propagating the excess. | |
| 201 * For that reason, lowerclip and upperclip should be small numbers. | |
| 202 * </pre> | |
| 203 */ | |
| 204 PIX * | |
| 205 pixDitherToBinarySpec(PIX *pixs, | |
| 206 l_int32 lowerclip, | |
| 207 l_int32 upperclip) | |
| 208 { | |
| 209 l_int32 w, h, d, wplt, wpld; | |
| 210 l_uint32 *datat, *datad; | |
| 211 l_uint32 *bufs1, *bufs2; | |
| 212 PIX *pixt, *pixd; | |
| 213 | |
| 214 if (!pixs) | |
| 215 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 216 pixGetDimensions(pixs, &w, &h, &d); | |
| 217 if (d != 8) | |
| 218 return (PIX *)ERROR_PTR("must be 8 bpp for dithering", __func__, NULL); | |
| 219 if (lowerclip < 0 || lowerclip > 255) | |
| 220 return (PIX *)ERROR_PTR("invalid value for lowerclip", __func__, NULL); | |
| 221 if (upperclip < 0 || upperclip > 255) | |
| 222 return (PIX *)ERROR_PTR("invalid value for upperclip", __func__, NULL); | |
| 223 | |
| 224 if ((pixd = pixCreate(w, h, 1)) == NULL) | |
| 225 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 226 pixCopyResolution(pixd, pixs); | |
| 227 pixCopyInputFormat(pixd, pixs); | |
| 228 datad = pixGetData(pixd); | |
| 229 wpld = pixGetWpl(pixd); | |
| 230 | |
| 231 /* Remove colormap if it exists */ | |
| 232 if ((pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE)) == NULL) { | |
| 233 pixDestroy(&pixd); | |
| 234 return (PIX *)ERROR_PTR("pixt not made", __func__, NULL); | |
| 235 } | |
| 236 datat = pixGetData(pixt); | |
| 237 wplt = pixGetWpl(pixt); | |
| 238 | |
| 239 /* Two line buffers, 1 for current line and 2 for next line */ | |
| 240 bufs1 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); | |
| 241 bufs2 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); | |
| 242 if (!bufs1 || !bufs2) { | |
| 243 LEPT_FREE(bufs1); | |
| 244 LEPT_FREE(bufs2); | |
| 245 pixDestroy(&pixd); | |
| 246 pixDestroy(&pixt); | |
| 247 return (PIX *)ERROR_PTR("bufs1, bufs2 not both made", __func__, NULL); | |
| 248 } | |
| 249 | |
| 250 ditherToBinaryLow(datad, w, h, wpld, datat, wplt, bufs1, bufs2, | |
| 251 lowerclip, upperclip); | |
| 252 | |
| 253 LEPT_FREE(bufs1); | |
| 254 LEPT_FREE(bufs2); | |
| 255 pixDestroy(&pixt); | |
| 256 return pixd; | |
| 257 } | |
| 258 | |
| 259 | |
| 260 /*! | |
| 261 * \brief ditherToBinaryLow() | |
| 262 * | |
| 263 * See comments in pixDitherToBinary() in binarize.c | |
| 264 */ | |
| 265 static void | |
| 266 ditherToBinaryLow(l_uint32 *datad, | |
| 267 l_int32 w, | |
| 268 l_int32 h, | |
| 269 l_int32 wpld, | |
| 270 l_uint32 *datas, | |
| 271 l_int32 wpls, | |
| 272 l_uint32 *bufs1, | |
| 273 l_uint32 *bufs2, | |
| 274 l_int32 lowerclip, | |
| 275 l_int32 upperclip) | |
| 276 { | |
| 277 l_int32 i; | |
| 278 l_uint32 *lined; | |
| 279 | |
| 280 /* do all lines except last line */ | |
| 281 memcpy(bufs2, datas, 4 * wpls); /* prime the buffer */ | |
| 282 for (i = 0; i < h - 1; i++) { | |
| 283 memcpy(bufs1, bufs2, 4 * wpls); | |
| 284 memcpy(bufs2, datas + (i + 1) * wpls, 4 * wpls); | |
| 285 lined = datad + i * wpld; | |
| 286 ditherToBinaryLineLow(lined, w, bufs1, bufs2, lowerclip, upperclip, 0); | |
| 287 } | |
| 288 | |
| 289 /* do last line */ | |
| 290 memcpy(bufs1, bufs2, 4 * wpls); | |
| 291 lined = datad + (h - 1) * wpld; | |
| 292 ditherToBinaryLineLow(lined, w, bufs1, bufs2, lowerclip, upperclip, 1); | |
| 293 } | |
| 294 | |
| 295 | |
| 296 /*! | |
| 297 * \brief ditherToBinaryLineLow() | |
| 298 * | |
| 299 * \param[in] lined ptr to beginning of dest line | |
| 300 * \param[in] w width of image in pixels | |
| 301 * \param[in] bufs1 buffer of current source line | |
| 302 * \param[in] bufs2 buffer of next source line | |
| 303 * \param[in] lowerclip lower clip distance to black | |
| 304 * \param[in] upperclip upper clip distance to white | |
| 305 * \param[in] lastlineflag 0 if not last dest line, 1 if last dest line | |
| 306 * \return void | |
| 307 * | |
| 308 * Dispatches FS error diffusion dithering for | |
| 309 * a single line of the image. If lastlineflag == 0, | |
| 310 * both source buffers are used; otherwise, only bufs1 | |
| 311 * is used. We use source buffers because the error | |
| 312 * is propagated into them, and we don't want to change | |
| 313 * the input src image. | |
| 314 * | |
| 315 * We break dithering out line by line to make it | |
| 316 * easier to combine functions like interpolative | |
| 317 * scaling and error diffusion dithering, as such a | |
| 318 * combination of operations obviates the need to | |
| 319 * generate a 2x grayscale image as an intermediary. | |
| 320 */ | |
| 321 void | |
| 322 ditherToBinaryLineLow(l_uint32 *lined, | |
| 323 l_int32 w, | |
| 324 l_uint32 *bufs1, | |
| 325 l_uint32 *bufs2, | |
| 326 l_int32 lowerclip, | |
| 327 l_int32 upperclip, | |
| 328 l_int32 lastlineflag) | |
| 329 { | |
| 330 l_int32 j; | |
| 331 l_int32 oval, eval; | |
| 332 l_uint8 fval1, fval2, rval, bval, dval; | |
| 333 | |
| 334 if (lastlineflag == 0) { | |
| 335 for (j = 0; j < w - 1; j++) { | |
| 336 oval = GET_DATA_BYTE(bufs1, j); | |
| 337 if (oval > 127) { /* binarize to OFF */ | |
| 338 if ((eval = 255 - oval) > upperclip) { | |
| 339 /* subtract from neighbors */ | |
| 340 fval1 = (3 * eval) / 8; | |
| 341 fval2 = eval / 4; | |
| 342 rval = GET_DATA_BYTE(bufs1, j + 1); | |
| 343 rval = L_MAX(0, rval - fval1); | |
| 344 SET_DATA_BYTE(bufs1, j + 1, rval); | |
| 345 bval = GET_DATA_BYTE(bufs2, j); | |
| 346 bval = L_MAX(0, bval - fval1); | |
| 347 SET_DATA_BYTE(bufs2, j, bval); | |
| 348 dval = GET_DATA_BYTE(bufs2, j + 1); | |
| 349 dval = L_MAX(0, dval - fval2); | |
| 350 SET_DATA_BYTE(bufs2, j + 1, dval); | |
| 351 } | |
| 352 } else { /* oval <= 127; binarize to ON */ | |
| 353 SET_DATA_BIT(lined, j); /* ON pixel */ | |
| 354 if (oval > lowerclip) { | |
| 355 /* add to neighbors */ | |
| 356 fval1 = (3 * oval) / 8; | |
| 357 fval2 = oval / 4; | |
| 358 rval = GET_DATA_BYTE(bufs1, j + 1); | |
| 359 rval = L_MIN(255, rval + fval1); | |
| 360 SET_DATA_BYTE(bufs1, j + 1, rval); | |
| 361 bval = GET_DATA_BYTE(bufs2, j); | |
| 362 bval = L_MIN(255, bval + fval1); | |
| 363 SET_DATA_BYTE(bufs2, j, bval); | |
| 364 dval = GET_DATA_BYTE(bufs2, j + 1); | |
| 365 dval = L_MIN(255, dval + fval2); | |
| 366 SET_DATA_BYTE(bufs2, j + 1, dval); | |
| 367 } | |
| 368 } | |
| 369 } | |
| 370 | |
| 371 /* do last column: j = w - 1 */ | |
| 372 oval = GET_DATA_BYTE(bufs1, j); | |
| 373 if (oval > 127) { /* binarize to OFF */ | |
| 374 if ((eval = 255 - oval) > upperclip) { | |
| 375 /* subtract from neighbors */ | |
| 376 fval1 = (3 * eval) / 8; | |
| 377 bval = GET_DATA_BYTE(bufs2, j); | |
| 378 bval = L_MAX(0, bval - fval1); | |
| 379 SET_DATA_BYTE(bufs2, j, bval); | |
| 380 } | |
| 381 } else { /*oval <= 127; binarize to ON */ | |
| 382 SET_DATA_BIT(lined, j); /* ON pixel */ | |
| 383 if (oval > lowerclip) { | |
| 384 /* add to neighbors */ | |
| 385 fval1 = (3 * oval) / 8; | |
| 386 bval = GET_DATA_BYTE(bufs2, j); | |
| 387 bval = L_MIN(255, bval + fval1); | |
| 388 SET_DATA_BYTE(bufs2, j, bval); | |
| 389 } | |
| 390 } | |
| 391 } else { /* lastlineflag == 1 */ | |
| 392 for (j = 0; j < w - 1; j++) { | |
| 393 oval = GET_DATA_BYTE(bufs1, j); | |
| 394 if (oval > 127) { /* binarize to OFF */ | |
| 395 if ((eval = 255 - oval) > upperclip) { | |
| 396 /* subtract from neighbors */ | |
| 397 fval1 = (3 * eval) / 8; | |
| 398 rval = GET_DATA_BYTE(bufs1, j + 1); | |
| 399 rval = L_MAX(0, rval - fval1); | |
| 400 SET_DATA_BYTE(bufs1, j + 1, rval); | |
| 401 } | |
| 402 } else { /* oval <= 127; binarize to ON */ | |
| 403 SET_DATA_BIT(lined, j); /* ON pixel */ | |
| 404 if (oval > lowerclip) { | |
| 405 /* add to neighbors */ | |
| 406 fval1 = (3 * oval) / 8; | |
| 407 rval = GET_DATA_BYTE(bufs1, j + 1); | |
| 408 rval = L_MIN(255, rval + fval1); | |
| 409 SET_DATA_BYTE(bufs1, j + 1, rval); | |
| 410 } | |
| 411 } | |
| 412 } | |
| 413 | |
| 414 /* do last pixel: (i, j) = (h - 1, w - 1) */ | |
| 415 oval = GET_DATA_BYTE(bufs1, j); | |
| 416 if (oval < 128) | |
| 417 SET_DATA_BIT(lined, j); /* ON pixel */ | |
| 418 } | |
| 419 } | |
| 420 | |
| 421 | |
| 422 /*------------------------------------------------------------------* | |
| 423 * Simple (pixelwise) binarization with fixed threshold * | |
| 424 *------------------------------------------------------------------*/ | |
| 425 /*! | |
| 426 * \brief pixThresholdToBinary() | |
| 427 * | |
| 428 * \param[in] pixs 4 or 8 bpp | |
| 429 * \param[in] thresh threshold value | |
| 430 * \return pixd 1 bpp, or NULL on error | |
| 431 * | |
| 432 * <pre> | |
| 433 * Notes: | |
| 434 * (1) If the source pixel is less than the threshold value, | |
| 435 * the dest will be 1; otherwise, it will be 0. | |
| 436 * (2) For example, for 8 bpp src pix, if %thresh == 256, the dest | |
| 437 * 1 bpp pix is all ones (fg), and if %thresh == 0, the dest | |
| 438 * pix is all zeros (bg). | |
| 439 * | |
| 440 * </pre> | |
| 441 */ | |
| 442 PIX * | |
| 443 pixThresholdToBinary(PIX *pixs, | |
| 444 l_int32 thresh) | |
| 445 { | |
| 446 l_int32 d, w, h, wplt, wpld; | |
| 447 l_uint32 *datat, *datad; | |
| 448 PIX *pixt, *pixd; | |
| 449 | |
| 450 if (!pixs) | |
| 451 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 452 pixGetDimensions(pixs, &w, &h, &d); | |
| 453 if (d != 4 && d != 8) | |
| 454 return (PIX *)ERROR_PTR("pixs must be 4 or 8 bpp", __func__, NULL); | |
| 455 if (thresh < 0) | |
| 456 return (PIX *)ERROR_PTR("thresh must be non-negative", __func__, NULL); | |
| 457 if (d == 4 && thresh > 16) | |
| 458 return (PIX *)ERROR_PTR("4 bpp thresh not in {0-16}", __func__, NULL); | |
| 459 if (d == 8 && thresh > 256) | |
| 460 return (PIX *)ERROR_PTR("8 bpp thresh not in {0-256}", __func__, NULL); | |
| 461 | |
| 462 if ((pixd = pixCreate(w, h, 1)) == NULL) | |
| 463 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 464 pixCopyResolution(pixd, pixs); | |
| 465 pixCopyInputFormat(pixd, pixs); | |
| 466 datad = pixGetData(pixd); | |
| 467 wpld = pixGetWpl(pixd); | |
| 468 | |
| 469 /* Remove colormap if it exists. If there is a colormap, | |
| 470 * pixt will be 8 bpp regardless of the depth of pixs. */ | |
| 471 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 472 datat = pixGetData(pixt); | |
| 473 wplt = pixGetWpl(pixt); | |
| 474 if (pixGetColormap(pixs) && d == 4) { /* promoted to 8 bpp */ | |
| 475 d = 8; | |
| 476 thresh *= 16; | |
| 477 } | |
| 478 | |
| 479 thresholdToBinaryLow(datad, w, h, wpld, datat, d, wplt, thresh); | |
| 480 pixDestroy(&pixt); | |
| 481 return pixd; | |
| 482 } | |
| 483 | |
| 484 | |
| 485 /*! | |
| 486 * \brief thresholdToBinaryLow() | |
| 487 * | |
| 488 * If the source pixel is less than thresh, | |
| 489 * the dest will be 1; otherwise, it will be 0 | |
| 490 */ | |
| 491 static void | |
| 492 thresholdToBinaryLow(l_uint32 *datad, | |
| 493 l_int32 w, | |
| 494 l_int32 h, | |
| 495 l_int32 wpld, | |
| 496 l_uint32 *datas, | |
| 497 l_int32 d, | |
| 498 l_int32 wpls, | |
| 499 l_int32 thresh) | |
| 500 { | |
| 501 l_int32 i; | |
| 502 l_uint32 *lines, *lined; | |
| 503 | |
| 504 for (i = 0; i < h; i++) { | |
| 505 lines = datas + i * wpls; | |
| 506 lined = datad + i * wpld; | |
| 507 thresholdToBinaryLineLow(lined, w, lines, d, thresh); | |
| 508 } | |
| 509 } | |
| 510 | |
| 511 | |
| 512 /* | |
| 513 * thresholdToBinaryLineLow() | |
| 514 * | |
| 515 */ | |
| 516 void | |
| 517 thresholdToBinaryLineLow(l_uint32 *lined, | |
| 518 l_int32 w, | |
| 519 l_uint32 *lines, | |
| 520 l_int32 d, | |
| 521 l_int32 thresh) | |
| 522 { | |
| 523 l_int32 j, k, gval, scount, dcount; | |
| 524 l_uint32 sword, dword; | |
| 525 | |
| 526 switch (d) | |
| 527 { | |
| 528 case 4: | |
| 529 /* Unrolled as 4 source words, 1 dest word */ | |
| 530 for (j = 0, scount = 0, dcount = 0; j + 31 < w; j += 32) { | |
| 531 dword = 0; | |
| 532 for (k = 0; k < 4; k++) { | |
| 533 sword = lines[scount++]; | |
| 534 dword <<= 8; | |
| 535 gval = (sword >> 28) & 0xf; | |
| 536 /* Trick used here and below: if gval < thresh then | |
| 537 * gval - thresh < 0, so its high-order bit is 1, and | |
| 538 * ((gval - thresh) >> 31) & 1 == 1; likewise, if | |
| 539 * gval >= thresh, then ((gval - thresh) >> 31) & 1 == 0 | |
| 540 * Doing it this way avoids a random (and thus easily | |
| 541 * mispredicted) branch on each pixel. */ | |
| 542 dword |= ((gval - thresh) >> 24) & 128; | |
| 543 gval = (sword >> 24) & 0xf; | |
| 544 dword |= ((gval - thresh) >> 25) & 64; | |
| 545 gval = (sword >> 20) & 0xf; | |
| 546 dword |= ((gval - thresh) >> 26) & 32; | |
| 547 gval = (sword >> 16) & 0xf; | |
| 548 dword |= ((gval - thresh) >> 27) & 16; | |
| 549 gval = (sword >> 12) & 0xf; | |
| 550 dword |= ((gval - thresh) >> 28) & 8; | |
| 551 gval = (sword >> 8) & 0xf; | |
| 552 dword |= ((gval - thresh) >> 29) & 4; | |
| 553 gval = (sword >> 4) & 0xf; | |
| 554 dword |= ((gval - thresh) >> 30) & 2; | |
| 555 gval = sword & 0xf; | |
| 556 dword |= ((gval - thresh) >> 31) & 1; | |
| 557 } | |
| 558 lined[dcount++] = dword; | |
| 559 } | |
| 560 | |
| 561 if (j < w) { | |
| 562 dword = 0; | |
| 563 for (; j < w; j++) { | |
| 564 if ((j & 7) == 0) { | |
| 565 sword = lines[scount++]; | |
| 566 } | |
| 567 gval = (sword >> 28) & 0xf; | |
| 568 sword <<= 4; | |
| 569 dword |= (((gval - thresh) >> 31) & 1) << (31 - (j & 31)); | |
| 570 } | |
| 571 lined[dcount] = dword; | |
| 572 } | |
| 573 #if DEBUG_UNROLLING | |
| 574 #define CHECK_BIT(a, b, c) if (GET_DATA_BIT(a, b) != c) { \ | |
| 575 lept_stderr("Error: mismatch at %d/%d(%d), %d vs %d\n", \ | |
| 576 j, w, d, GET_DATA_BIT(a, b), c); } | |
| 577 for (j = 0; j < w; j++) { | |
| 578 gval = GET_DATA_QBIT(lines, j); | |
| 579 CHECK_BIT(lined, j, gval < thresh ? 1 : 0); | |
| 580 } | |
| 581 #endif | |
| 582 break; | |
| 583 case 8: | |
| 584 /* Unrolled as 8 source words, 1 dest word */ | |
| 585 for (j = 0, scount = 0, dcount = 0; j + 31 < w; j += 32) { | |
| 586 dword = 0; | |
| 587 for (k = 0; k < 8; k++) { | |
| 588 sword = lines[scount++]; | |
| 589 dword <<= 4; | |
| 590 gval = (sword >> 24) & 0xff; | |
| 591 dword |= ((gval - thresh) >> 28) & 8; | |
| 592 gval = (sword >> 16) & 0xff; | |
| 593 dword |= ((gval - thresh) >> 29) & 4; | |
| 594 gval = (sword >> 8) & 0xff; | |
| 595 dword |= ((gval - thresh) >> 30) & 2; | |
| 596 gval = sword & 0xff; | |
| 597 dword |= ((gval - thresh) >> 31) & 1; | |
| 598 } | |
| 599 lined[dcount++] = dword; | |
| 600 } | |
| 601 | |
| 602 if (j < w) { | |
| 603 dword = 0; | |
| 604 for (; j < w; j++) { | |
| 605 if ((j & 3) == 0) { | |
| 606 sword = lines[scount++]; | |
| 607 } | |
| 608 gval = (sword >> 24) & 0xff; | |
| 609 sword <<= 8; | |
| 610 dword |= (l_uint64)(((gval - thresh) >> 31) & 1) | |
| 611 << (31 - (j & 31)); | |
| 612 } | |
| 613 lined[dcount] = dword; | |
| 614 } | |
| 615 #if DEBUG_UNROLLING | |
| 616 for (j = 0; j < w; j++) { | |
| 617 gval = GET_DATA_BYTE(lines, j); | |
| 618 CHECK_BIT(lined, j, gval < thresh ? 1 : 0); | |
| 619 } | |
| 620 #undef CHECK_BIT | |
| 621 #endif | |
| 622 break; | |
| 623 default: | |
| 624 L_ERROR("src depth not 4 or 8 bpp\n", __func__); | |
| 625 break; | |
| 626 } | |
| 627 } | |
| 628 | |
| 629 | |
| 630 /*------------------------------------------------------------------* | |
| 631 * Binarization with variable threshold * | |
| 632 *------------------------------------------------------------------*/ | |
| 633 /*! | |
| 634 * \brief pixVarThresholdToBinary() | |
| 635 * | |
| 636 * \param[in] pixs 8 bpp | |
| 637 * \param[in] pixg 8 bpp; contains threshold values for each pixel | |
| 638 * \return pixd 1 bpp, or NULL on error | |
| 639 * | |
| 640 * <pre> | |
| 641 * Notes: | |
| 642 * (1) If the pixel in pixs is less than the corresponding pixel | |
| 643 * in pixg, the dest will be 1; otherwise it will be 0. | |
| 644 * </pre> | |
| 645 */ | |
| 646 PIX * | |
| 647 pixVarThresholdToBinary(PIX *pixs, | |
| 648 PIX *pixg) | |
| 649 { | |
| 650 l_int32 i, j, vals, valg, w, h, d, wpls, wplg, wpld; | |
| 651 l_uint32 *datas, *datag, *datad, *lines, *lineg, *lined; | |
| 652 PIX *pixd; | |
| 653 | |
| 654 if (!pixs) | |
| 655 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 656 if (!pixg) | |
| 657 return (PIX *)ERROR_PTR("pixg not defined", __func__, NULL); | |
| 658 if (!pixSizesEqual(pixs, pixg)) | |
| 659 return (PIX *)ERROR_PTR("pix sizes not equal", __func__, NULL); | |
| 660 pixGetDimensions(pixs, &w, &h, &d); | |
| 661 if (d != 8) | |
| 662 return (PIX *)ERROR_PTR("pixs must be 8 bpp", __func__, NULL); | |
| 663 | |
| 664 pixd = pixCreate(w, h, 1); | |
| 665 pixCopyResolution(pixd, pixs); | |
| 666 pixCopyInputFormat(pixd, pixs); | |
| 667 datad = pixGetData(pixd); | |
| 668 wpld = pixGetWpl(pixd); | |
| 669 datas = pixGetData(pixs); | |
| 670 wpls = pixGetWpl(pixs); | |
| 671 datag = pixGetData(pixg); | |
| 672 wplg = pixGetWpl(pixg); | |
| 673 for (i = 0; i < h; i++) { | |
| 674 lines = datas + i * wpls; | |
| 675 lineg = datag + i * wplg; | |
| 676 lined = datad + i * wpld; | |
| 677 for (j = 0; j < w; j++) { | |
| 678 vals = GET_DATA_BYTE(lines, j); | |
| 679 valg = GET_DATA_BYTE(lineg, j); | |
| 680 if (vals < valg) | |
| 681 SET_DATA_BIT(lined, j); | |
| 682 } | |
| 683 } | |
| 684 | |
| 685 return pixd; | |
| 686 } | |
| 687 | |
| 688 | |
| 689 /*------------------------------------------------------------------* | |
| 690 * Binarization by adaptive mapping * | |
| 691 *------------------------------------------------------------------*/ | |
| 692 /*! | |
| 693 * \brief pixAdaptThresholdToBinary() | |
| 694 * | |
| 695 * \param[in] pixs 8 bpp | |
| 696 * \param[in] pixm [optional] 1 bpp image mask; can be null | |
| 697 * \param[in] gamma gamma correction; must be > 0.0; typically ~1.0 | |
| 698 * \return pixd 1 bpp, or NULL on error | |
| 699 * | |
| 700 * <pre> | |
| 701 * Notes: | |
| 702 * (1) This is a simple convenience function for doing adaptive | |
| 703 * thresholding on a grayscale image with variable background. | |
| 704 * It uses default parameters appropriate for typical text images. | |
| 705 * Other high-level adaptive thresholding functions are | |
| 706 * pixConvertTo1Adaptive() and pixCleanImage(). | |
| 707 * (2) %pixm is a 1 bpp mask over "image" regions, which are not | |
| 708 * expected to have a white background. The mask inhibits | |
| 709 * background finding under the fg pixels of the mask. For | |
| 710 * images with both text and image, the image regions would | |
| 711 * be binarized (or quantized) by a different set of operations. | |
| 712 * (3) As %gamma is increased, the foreground pixels are reduced. | |
| 713 * (4) Under the covers: The default background value for normalization | |
| 714 * is 200, so we choose 170 for 'maxval' in pixGammaTRC. Likewise, | |
| 715 * the default foreground threshold for normalization is 60, | |
| 716 * so we choose 50 for 'minval' in pixGammaTRC. Because | |
| 717 * 170 was mapped to 255, choosing 200 for the threshold is | |
| 718 * quite safe for avoiding speckle noise from the background. | |
| 719 * </pre> | |
| 720 */ | |
| 721 PIX * | |
| 722 pixAdaptThresholdToBinary(PIX *pixs, | |
| 723 PIX *pixm, | |
| 724 l_float32 gamma) | |
| 725 { | |
| 726 if (!pixs || pixGetDepth(pixs) != 8) | |
| 727 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, NULL); | |
| 728 | |
| 729 return pixAdaptThresholdToBinaryGen(pixs, pixm, gamma, 50, 170, 200); | |
| 730 } | |
| 731 | |
| 732 | |
| 733 /*! | |
| 734 * \brief pixAdaptThresholdToBinaryGen() | |
| 735 * | |
| 736 * \param[in] pixs 8 bpp | |
| 737 * \param[in] pixm [optional] 1 bpp image mask; can be null | |
| 738 * \param[in] gamma gamma correction; must be > 0.0; typically ~1.0 | |
| 739 * \param[in] blackval dark value to set to black (0) | |
| 740 * \param[in] whiteval light value to set to white (255) | |
| 741 * \param[in] thresh final threshold for binarization | |
| 742 * \return pixd 1 bpp, or NULL on error | |
| 743 * | |
| 744 * <pre> | |
| 745 * Notes: | |
| 746 * (1) This is a convenience function for doing adaptive thresholding | |
| 747 * on a grayscale image with variable background. Also see notes | |
| 748 * in pixAdaptThresholdToBinary(). | |
| 749 * (2) Reducing %gamma increases the foreground (text) pixels. | |
| 750 * Use a low value (e.g., 0.5) for images with light text. | |
| 751 * (3) For normal images, see default args in pixAdaptThresholdToBinary(). | |
| 752 * For images with very light text, these values are appropriate: | |
| 753 * gamma ~0.5 | |
| 754 * blackval ~70 | |
| 755 * whiteval ~190 | |
| 756 * thresh ~200 | |
| 757 * </pre> | |
| 758 */ | |
| 759 PIX * | |
| 760 pixAdaptThresholdToBinaryGen(PIX *pixs, | |
| 761 PIX *pixm, | |
| 762 l_float32 gamma, | |
| 763 l_int32 blackval, | |
| 764 l_int32 whiteval, | |
| 765 l_int32 thresh) | |
| 766 { | |
| 767 PIX *pix1, *pixd; | |
| 768 | |
| 769 if (!pixs || pixGetDepth(pixs) != 8) | |
| 770 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, NULL); | |
| 771 | |
| 772 if ((pix1 = pixBackgroundNormSimple(pixs, pixm, NULL)) == NULL) | |
| 773 return (PIX *)ERROR_PTR("pix1 not made", __func__, NULL); | |
| 774 pixGammaTRC(pix1, pix1, gamma, blackval, whiteval); | |
| 775 pixd = pixThresholdToBinary(pix1, thresh); | |
| 776 pixDestroy(&pix1); | |
| 777 return pixd; | |
| 778 } | |
| 779 | |
| 780 | |
| 781 /*--------------------------------------------------------------------* | |
| 782 * Generate a binary mask from pixels of particular value(s) * | |
| 783 *--------------------------------------------------------------------*/ | |
| 784 /*! | |
| 785 * \brief pixGenerateMaskByValue() | |
| 786 * | |
| 787 * \param[in] pixs 2, 4 or 8 bpp, or colormapped | |
| 788 * \param[in] val of pixels for which we set 1 in dest | |
| 789 * \param[in] usecmap 1 to retain cmap values; 0 to convert to gray | |
| 790 * \return pixd 1 bpp, or NULL on error | |
| 791 * | |
| 792 * <pre> | |
| 793 * Notes: | |
| 794 * (1) %val is the pixel value that we are selecting. It can be | |
| 795 * either a gray value or a colormap index. | |
| 796 * (2) If pixs is colormapped, %usecmap determines if the colormap | |
| 797 * index values are used, or if the colormap is removed to gray and | |
| 798 * the gray values are used. For the latter, it generates | |
| 799 * an approximate grayscale value for each pixel, and then looks | |
| 800 * for gray pixels with the value %val. | |
| 801 * </pre> | |
| 802 */ | |
| 803 PIX * | |
| 804 pixGenerateMaskByValue(PIX *pixs, | |
| 805 l_int32 val, | |
| 806 l_int32 usecmap) | |
| 807 { | |
| 808 l_int32 i, j, w, h, d, wplg, wpld; | |
| 809 l_uint32 *datag, *datad, *lineg, *lined; | |
| 810 PIX *pixg, *pixd; | |
| 811 | |
| 812 if (!pixs) | |
| 813 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 814 d = pixGetDepth(pixs); | |
| 815 if (d != 2 && d != 4 && d != 8) | |
| 816 return (PIX *)ERROR_PTR("not 2, 4 or 8 bpp", __func__, NULL); | |
| 817 | |
| 818 if (!usecmap && pixGetColormap(pixs)) | |
| 819 pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 820 else | |
| 821 pixg = pixClone(pixs); | |
| 822 pixGetDimensions(pixg, &w, &h, &d); | |
| 823 if (d == 8 && (val < 0 || val > 255)) { | |
| 824 pixDestroy(&pixg); | |
| 825 return (PIX *)ERROR_PTR("val out of 8 bpp range", __func__, NULL); | |
| 826 } | |
| 827 if (d == 4 && (val < 0 || val > 15)) { | |
| 828 pixDestroy(&pixg); | |
| 829 return (PIX *)ERROR_PTR("val out of 4 bpp range", __func__, NULL); | |
| 830 } | |
| 831 if (d == 2 && (val < 0 || val > 3)) { | |
| 832 pixDestroy(&pixg); | |
| 833 return (PIX *)ERROR_PTR("val out of 2 bpp range", __func__, NULL); | |
| 834 } | |
| 835 | |
| 836 pixd = pixCreate(w, h, 1); | |
| 837 pixCopyResolution(pixd, pixg); | |
| 838 pixCopyInputFormat(pixd, pixs); | |
| 839 datag = pixGetData(pixg); | |
| 840 wplg = pixGetWpl(pixg); | |
| 841 datad = pixGetData(pixd); | |
| 842 wpld = pixGetWpl(pixd); | |
| 843 for (i = 0; i < h; i++) { | |
| 844 lineg = datag + i * wplg; | |
| 845 lined = datad + i * wpld; | |
| 846 for (j = 0; j < w; j++) { | |
| 847 if (d == 8) { | |
| 848 if (GET_DATA_BYTE(lineg, j) == val) | |
| 849 SET_DATA_BIT(lined, j); | |
| 850 } else if (d == 4) { | |
| 851 if (GET_DATA_QBIT(lineg, j) == val) | |
| 852 SET_DATA_BIT(lined, j); | |
| 853 } else { /* d == 2 */ | |
| 854 if (GET_DATA_DIBIT(lineg, j) == val) | |
| 855 SET_DATA_BIT(lined, j); | |
| 856 } | |
| 857 } | |
| 858 } | |
| 859 | |
| 860 pixDestroy(&pixg); | |
| 861 return pixd; | |
| 862 } | |
| 863 | |
| 864 | |
| 865 /*! | |
| 866 * \brief pixGenerateMaskByBand() | |
| 867 * | |
| 868 * \param[in] pixs 2, 4 or 8 bpp, or colormapped | |
| 869 * \param[in] lower, upper two pixel values from which a range, either | |
| 870 * between (inband) or outside of (!inband), | |
| 871 * determines which pixels in pixs cause us to | |
| 872 * set a 1 in the dest mask | |
| 873 * \param[in] inband 1 for finding pixels in [lower, upper]; | |
| 874 * 0 for finding pixels in | |
| 875 * [0, lower) union (upper, 255] | |
| 876 * \param[in] usecmap 1 to retain cmap values; 0 to convert to gray | |
| 877 * \return pixd 1 bpp, or NULL on error | |
| 878 * | |
| 879 * <pre> | |
| 880 * Notes: | |
| 881 * (1) Generates a 1 bpp mask pixd, the same size as pixs, where | |
| 882 * the fg pixels in the mask are those either within the specified | |
| 883 * band (for inband == 1) or outside the specified band | |
| 884 * (for inband == 0). | |
| 885 * (2) If pixs is colormapped, %usecmap determines if the colormap | |
| 886 * values are used, or if the colormap is removed to gray and | |
| 887 * the gray values are used. For the latter, it generates | |
| 888 * an approximate grayscale value for each pixel, and then looks | |
| 889 * for gray pixels with the value %val. | |
| 890 * </pre> | |
| 891 */ | |
| 892 PIX * | |
| 893 pixGenerateMaskByBand(PIX *pixs, | |
| 894 l_int32 lower, | |
| 895 l_int32 upper, | |
| 896 l_int32 inband, | |
| 897 l_int32 usecmap) | |
| 898 { | |
| 899 l_int32 i, j, w, h, d, wplg, wpld, val; | |
| 900 l_uint32 *datag, *datad, *lineg, *lined; | |
| 901 PIX *pixg, *pixd; | |
| 902 | |
| 903 if (!pixs) | |
| 904 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 905 d = pixGetDepth(pixs); | |
| 906 if (d != 2 && d != 4 && d != 8) | |
| 907 return (PIX *)ERROR_PTR("not 2, 4 or 8 bpp", __func__, NULL); | |
| 908 if (lower < 0 || lower > upper) | |
| 909 return (PIX *)ERROR_PTR("lower < 0 or lower > upper!", __func__, NULL); | |
| 910 | |
| 911 if (!usecmap && pixGetColormap(pixs)) | |
| 912 pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 913 else | |
| 914 pixg = pixClone(pixs); | |
| 915 pixGetDimensions(pixg, &w, &h, &d); | |
| 916 if (d == 8 && upper > 255) { | |
| 917 pixDestroy(&pixg); | |
| 918 return (PIX *)ERROR_PTR("d == 8 and upper > 255", __func__, NULL); | |
| 919 } | |
| 920 if (d == 4 && upper > 15) { | |
| 921 pixDestroy(&pixg); | |
| 922 return (PIX *)ERROR_PTR("d == 4 and upper > 15", __func__, NULL); | |
| 923 } | |
| 924 if (d == 2 && upper > 3) { | |
| 925 pixDestroy(&pixg); | |
| 926 return (PIX *)ERROR_PTR("d == 2 and upper > 3", __func__, NULL); | |
| 927 } | |
| 928 | |
| 929 pixd = pixCreate(w, h, 1); | |
| 930 pixCopyResolution(pixd, pixg); | |
| 931 pixCopyInputFormat(pixd, pixs); | |
| 932 datag = pixGetData(pixg); | |
| 933 wplg = pixGetWpl(pixg); | |
| 934 datad = pixGetData(pixd); | |
| 935 wpld = pixGetWpl(pixd); | |
| 936 for (i = 0; i < h; i++) { | |
| 937 lineg = datag + i * wplg; | |
| 938 lined = datad + i * wpld; | |
| 939 for (j = 0; j < w; j++) { | |
| 940 if (d == 8) | |
| 941 val = GET_DATA_BYTE(lineg, j); | |
| 942 else if (d == 4) | |
| 943 val = GET_DATA_QBIT(lineg, j); | |
| 944 else /* d == 2 */ | |
| 945 val = GET_DATA_DIBIT(lineg, j); | |
| 946 if (inband) { | |
| 947 if (val >= lower && val <= upper) | |
| 948 SET_DATA_BIT(lined, j); | |
| 949 } else { /* out of band */ | |
| 950 if (val < lower || val > upper) | |
| 951 SET_DATA_BIT(lined, j); | |
| 952 } | |
| 953 } | |
| 954 } | |
| 955 | |
| 956 pixDestroy(&pixg); | |
| 957 return pixd; | |
| 958 } | |
| 959 | |
| 960 | |
| 961 /*------------------------------------------------------------------* | |
| 962 * Thresholding to 2 bpp by dithering * | |
| 963 *------------------------------------------------------------------*/ | |
| 964 /*! | |
| 965 * \brief pixDitherTo2bpp() | |
| 966 * | |
| 967 * \param[in] pixs 8 bpp | |
| 968 * \param[in] cmapflag 1 to generate a colormap | |
| 969 * \return pixd dithered 2 bpp, or NULL on error | |
| 970 * | |
| 971 * An analog of the Floyd-Steinberg error diffusion dithering | |
| 972 * algorithm is used to "dibitize" an 8 bpp grayscale image | |
| 973 * to 2 bpp, using equally spaced gray values of 0, 85, 170, and 255, | |
| 974 * which are served by thresholds of 43, 128 and 213. | |
| 975 * If cmapflag == 1, the colormap values are set to 0, 85, 170 and 255. | |
| 976 * If a pixel has a value between 0 and 42, it is dibitized | |
| 977 * to 0, and the excess above 0 is added to the | |
| 978 * three neighboring pixels, in the fractions 3/8 to i, j+1, | |
| 979 * 3/8 to i+1, j) and 1/4 to (i+1, j+1, truncating to 255 if | |
| 980 * necessary. If a pixel has a value between 43 and 127, it is | |
| 981 * dibitized to 1, and the excess above 85 is added to the three | |
| 982 * neighboring pixels as before. If the value is below 85, the | |
| 983 * excess is subtracted. With a value between 128 | |
| 984 * and 212, it is dibitized to 2, with the excess on either side | |
| 985 * of 170 distributed as before. Finally, with a value between | |
| 986 * 213 and 255, it is dibitized to 3, with the excess below 255 | |
| 987 * subtracted from the neighbors. We always truncate to 0 or 255. | |
| 988 * The details can be seen in the lookup table generation. | |
| 989 * | |
| 990 * This function differs from straight dithering in that it allows | |
| 991 * clipping of grayscale to 0 or 255 if the values are | |
| 992 * sufficiently close, without distribution of the excess. | |
| 993 * This uses default values from pix.h to specify the range of lower | |
| 994 * and upper values near 0 and 255, rsp that are clipped to black | |
| 995 * and white without propagating the excess. | |
| 996 * Not propagating the excess has the effect of reducing the snake | |
| 997 * patterns in parts of the image that are nearly black or white; | |
| 998 * however, it also prevents any attempt to reproduce gray for those values. | |
| 999 * | |
| 1000 * The implementation uses 3 lookup tables for simplicity, and | |
| 1001 * a pair of line buffers to avoid modifying pixs. | |
| 1002 */ | |
| 1003 PIX * | |
| 1004 pixDitherTo2bpp(PIX *pixs, | |
| 1005 l_int32 cmapflag) | |
| 1006 { | |
| 1007 if (!pixs) | |
| 1008 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1009 if (pixGetDepth(pixs) != 8) | |
| 1010 return (PIX *)ERROR_PTR("must be 8 bpp for dithering", __func__, NULL); | |
| 1011 | |
| 1012 return pixDitherTo2bppSpec(pixs, DEFAULT_CLIP_LOWER_2, | |
| 1013 DEFAULT_CLIP_UPPER_2, cmapflag); | |
| 1014 } | |
| 1015 | |
| 1016 | |
| 1017 /*! | |
| 1018 * \brief pixDitherTo2bppSpec() | |
| 1019 * | |
| 1020 * \param[in] pixs 8 bpp | |
| 1021 * \param[in] lowerclip lower clip distance to black; use 0 for default | |
| 1022 * \param[in] upperclip upper clip distance to white; use 0 for default | |
| 1023 * \param[in] cmapflag 1 to generate a colormap | |
| 1024 * \return pixd dithered 2 bpp, or NULL on error | |
| 1025 * | |
| 1026 * <pre> | |
| 1027 * Notes: | |
| 1028 * (1) See comments above in pixDitherTo2bpp() for details. | |
| 1029 * (2) The input parameters lowerclip and upperclip specify the range | |
| 1030 * of lower and upper values (near 0 and 255, rsp) that are | |
| 1031 * clipped to black and white without propagating the excess. | |
| 1032 * For that reason, lowerclip and upperclip should be small numbers. | |
| 1033 * </pre> | |
| 1034 */ | |
| 1035 PIX * | |
| 1036 pixDitherTo2bppSpec(PIX *pixs, | |
| 1037 l_int32 lowerclip, | |
| 1038 l_int32 upperclip, | |
| 1039 l_int32 cmapflag) | |
| 1040 { | |
| 1041 l_int32 w, h, d, wplt, wpld; | |
| 1042 l_int32 *tabval, *tab38, *tab14; | |
| 1043 l_uint32 *datat, *datad; | |
| 1044 l_uint32 *bufs1, *bufs2; | |
| 1045 PIX *pixt, *pixd; | |
| 1046 PIXCMAP *cmap; | |
| 1047 | |
| 1048 if (!pixs) | |
| 1049 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1050 pixGetDimensions(pixs, &w, &h, &d); | |
| 1051 if (d != 8) | |
| 1052 return (PIX *)ERROR_PTR("must be 8 bpp for dithering", __func__, NULL); | |
| 1053 if (lowerclip < 0 || lowerclip > 255) | |
| 1054 return (PIX *)ERROR_PTR("invalid value for lowerclip", __func__, NULL); | |
| 1055 if (upperclip < 0 || upperclip > 255) | |
| 1056 return (PIX *)ERROR_PTR("invalid value for upperclip", __func__, NULL); | |
| 1057 | |
| 1058 if ((pixd = pixCreate(w, h, 2)) == NULL) | |
| 1059 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 1060 pixCopyResolution(pixd, pixs); | |
| 1061 pixCopyInputFormat(pixd, pixs); | |
| 1062 datad = pixGetData(pixd); | |
| 1063 wpld = pixGetWpl(pixd); | |
| 1064 | |
| 1065 /* If there is a colormap, remove it */ | |
| 1066 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 1067 datat = pixGetData(pixt); | |
| 1068 wplt = pixGetWpl(pixt); | |
| 1069 | |
| 1070 /* Two line buffers, 1 for current line and 2 for next line */ | |
| 1071 bufs1 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); | |
| 1072 bufs2 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); | |
| 1073 if (!bufs1 || !bufs2) { | |
| 1074 LEPT_FREE(bufs1); | |
| 1075 LEPT_FREE(bufs2); | |
| 1076 pixDestroy(&pixd); | |
| 1077 pixDestroy(&pixt); | |
| 1078 return (PIX *)ERROR_PTR("bufs1, bufs2 not both made", __func__, NULL); | |
| 1079 } | |
| 1080 | |
| 1081 /* 3 lookup tables: 2-bit value, (3/8)excess, and (1/4)excess */ | |
| 1082 make8To2DitherTables(&tabval, &tab38, &tab14, lowerclip, upperclip); | |
| 1083 | |
| 1084 ditherTo2bppLow(datad, w, h, wpld, datat, wplt, bufs1, bufs2, | |
| 1085 tabval, tab38, tab14); | |
| 1086 | |
| 1087 if (cmapflag) { | |
| 1088 cmap = pixcmapCreateLinear(2, 4); | |
| 1089 pixSetColormap(pixd, cmap); | |
| 1090 } | |
| 1091 | |
| 1092 LEPT_FREE(bufs1); | |
| 1093 LEPT_FREE(bufs2); | |
| 1094 LEPT_FREE(tabval); | |
| 1095 LEPT_FREE(tab38); | |
| 1096 LEPT_FREE(tab14); | |
| 1097 pixDestroy(&pixt); | |
| 1098 return pixd; | |
| 1099 } | |
| 1100 | |
| 1101 | |
| 1102 /*! | |
| 1103 * \brief ditherTo2bppLow() | |
| 1104 * | |
| 1105 * Low-level function for doing Floyd-Steinberg error diffusion | |
| 1106 * dithering from 8 bpp (datas) to 2 bpp (datad). Two source | |
| 1107 * line buffers, bufs1 and bufs2, are provided, along with three | |
| 1108 * 256-entry lookup tables: tabval gives the output pixel value, | |
| 1109 * tab38 gives the extra (plus or minus) transferred to the pixels | |
| 1110 * directly to the left and below, and tab14 gives the extra | |
| 1111 * transferred to the diagonal below. The choice of 3/8 and 1/4 | |
| 1112 * is traditional but arbitrary when you use a lookup table; the | |
| 1113 * only constraint is that the sum is 1. See other comments | |
| 1114 * below and in grayquant.c. | |
| 1115 */ | |
| 1116 static void | |
| 1117 ditherTo2bppLow(l_uint32 *datad, | |
| 1118 l_int32 w, | |
| 1119 l_int32 h, | |
| 1120 l_int32 wpld, | |
| 1121 l_uint32 *datas, | |
| 1122 l_int32 wpls, | |
| 1123 l_uint32 *bufs1, | |
| 1124 l_uint32 *bufs2, | |
| 1125 l_int32 *tabval, | |
| 1126 l_int32 *tab38, | |
| 1127 l_int32 *tab14) | |
| 1128 { | |
| 1129 l_int32 i; | |
| 1130 l_uint32 *lined; | |
| 1131 | |
| 1132 /* do all lines except last line */ | |
| 1133 memcpy(bufs2, datas, 4 * wpls); /* prime the buffer */ | |
| 1134 for (i = 0; i < h - 1; i++) { | |
| 1135 memcpy(bufs1, bufs2, 4 * wpls); | |
| 1136 memcpy(bufs2, datas + (i + 1) * wpls, 4 * wpls); | |
| 1137 lined = datad + i * wpld; | |
| 1138 ditherTo2bppLineLow(lined, w, bufs1, bufs2, tabval, tab38, tab14, 0); | |
| 1139 } | |
| 1140 | |
| 1141 /* do last line */ | |
| 1142 memcpy(bufs1, bufs2, 4 * wpls); | |
| 1143 lined = datad + (h - 1) * wpld; | |
| 1144 ditherTo2bppLineLow(lined, w, bufs1, bufs2, tabval, tab38, tab14, 1); | |
| 1145 } | |
| 1146 | |
| 1147 | |
| 1148 /*! | |
| 1149 * \brief ditherTo2bppLineLow() | |
| 1150 * | |
| 1151 * \param[in] lined ptr to beginning of dest line | |
| 1152 * \param[in] w width of image in pixels | |
| 1153 * \param[in] bufs1 buffer of current source line | |
| 1154 * \param[in] bufs2 buffer of next source line | |
| 1155 * \param[in] tabval value to assign for current pixel | |
| 1156 * \param[in] tab38 excess value to give to neighboring 3/8 pixels | |
| 1157 * \param[in] tab14 excess value to give to neighboring 1/4 pixel | |
| 1158 * \param[in] lastlineflag 0 if not last dest line, 1 if last dest line | |
| 1159 * \return void | |
| 1160 * | |
| 1161 * Dispatches error diffusion dithering for | |
| 1162 * a single line of the image. If lastlineflag == 0, | |
| 1163 * both source buffers are used; otherwise, only bufs1 | |
| 1164 * is used. We use source buffers because the error | |
| 1165 * is propagated into them, and we don't want to change | |
| 1166 * the input src image. | |
| 1167 * | |
| 1168 * We break dithering out line by line to make it | |
| 1169 * easier to combine functions like interpolative | |
| 1170 * scaling and error diffusion dithering, as such a | |
| 1171 * combination of operations obviates the need to | |
| 1172 * generate a 2x grayscale image as an intermediary. | |
| 1173 */ | |
| 1174 static void | |
| 1175 ditherTo2bppLineLow(l_uint32 *lined, | |
| 1176 l_int32 w, | |
| 1177 l_uint32 *bufs1, | |
| 1178 l_uint32 *bufs2, | |
| 1179 l_int32 *tabval, | |
| 1180 l_int32 *tab38, | |
| 1181 l_int32 *tab14, | |
| 1182 l_int32 lastlineflag) | |
| 1183 { | |
| 1184 l_int32 j; | |
| 1185 l_int32 oval, tab38val, tab14val; | |
| 1186 l_uint8 rval, bval, dval; | |
| 1187 | |
| 1188 if (lastlineflag == 0) { | |
| 1189 for (j = 0; j < w - 1; j++) { | |
| 1190 oval = GET_DATA_BYTE(bufs1, j); | |
| 1191 SET_DATA_DIBIT(lined, j, tabval[oval]); | |
| 1192 rval = GET_DATA_BYTE(bufs1, j + 1); | |
| 1193 bval = GET_DATA_BYTE(bufs2, j); | |
| 1194 dval = GET_DATA_BYTE(bufs2, j + 1); | |
| 1195 tab38val = tab38[oval]; | |
| 1196 tab14val = tab14[oval]; | |
| 1197 if (tab38val < 0) { | |
| 1198 rval = L_MAX(0, rval + tab38val); | |
| 1199 bval = L_MAX(0, bval + tab38val); | |
| 1200 dval = L_MAX(0, dval + tab14val); | |
| 1201 } else { | |
| 1202 rval = L_MIN(255, rval + tab38val); | |
| 1203 bval = L_MIN(255, bval + tab38val); | |
| 1204 dval = L_MIN(255, dval + tab14val); | |
| 1205 } | |
| 1206 SET_DATA_BYTE(bufs1, j + 1, rval); | |
| 1207 SET_DATA_BYTE(bufs2, j, bval); | |
| 1208 SET_DATA_BYTE(bufs2, j + 1, dval); | |
| 1209 } | |
| 1210 | |
| 1211 /* do last column: j = w - 1 */ | |
| 1212 oval = GET_DATA_BYTE(bufs1, j); | |
| 1213 SET_DATA_DIBIT(lined, j, tabval[oval]); | |
| 1214 bval = GET_DATA_BYTE(bufs2, j); | |
| 1215 tab38val = tab38[oval]; | |
| 1216 if (tab38val < 0) | |
| 1217 bval = L_MAX(0, bval + tab38val); | |
| 1218 else | |
| 1219 bval = L_MIN(255, bval + tab38val); | |
| 1220 SET_DATA_BYTE(bufs2, j, bval); | |
| 1221 } else { /* lastlineflag == 1 */ | |
| 1222 for (j = 0; j < w - 1; j++) { | |
| 1223 oval = GET_DATA_BYTE(bufs1, j); | |
| 1224 SET_DATA_DIBIT(lined, j, tabval[oval]); | |
| 1225 rval = GET_DATA_BYTE(bufs1, j + 1); | |
| 1226 tab38val = tab38[oval]; | |
| 1227 if (tab38val < 0) | |
| 1228 rval = L_MAX(0, rval + tab38val); | |
| 1229 else | |
| 1230 rval = L_MIN(255, rval + tab38val); | |
| 1231 SET_DATA_BYTE(bufs1, j + 1, rval); | |
| 1232 } | |
| 1233 | |
| 1234 /* do last pixel: (i, j) = (h - 1, w - 1) */ | |
| 1235 oval = GET_DATA_BYTE(bufs1, j); | |
| 1236 SET_DATA_DIBIT(lined, j, tabval[oval]); | |
| 1237 } | |
| 1238 } | |
| 1239 | |
| 1240 | |
| 1241 /*! | |
| 1242 * \brief make8To2DitherTables() | |
| 1243 * | |
| 1244 * \param[out] ptabval value assigned to output pixel; 0, 1, 2 or 3 | |
| 1245 * \param[out] ptab38 amount propagated to pixels left and below | |
| 1246 * \param[out] ptab14 amount propagated to pixel to left and down | |
| 1247 * \param[in] cliptoblack values near 0 where the excess is not propagated | |
| 1248 * \param[in] cliptowhite values near 255 where the deficit is not propagated | |
| 1249 * | |
| 1250 * \return 0 if OK, 1 on error | |
| 1251 */ | |
| 1252 static l_int32 | |
| 1253 make8To2DitherTables(l_int32 **ptabval, | |
| 1254 l_int32 **ptab38, | |
| 1255 l_int32 **ptab14, | |
| 1256 l_int32 cliptoblack, | |
| 1257 l_int32 cliptowhite) | |
| 1258 { | |
| 1259 l_int32 i; | |
| 1260 l_int32 *tabval, *tab38, *tab14; | |
| 1261 | |
| 1262 /* 3 lookup tables: 2-bit value, (3/8)excess, and (1/4)excess */ | |
| 1263 tabval = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); | |
| 1264 tab38 = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); | |
| 1265 tab14 = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); | |
| 1266 *ptabval = tabval; | |
| 1267 *ptab38 = tab38; | |
| 1268 *ptab14 = tab14; | |
| 1269 | |
| 1270 for (i = 0; i < 256; i++) { | |
| 1271 if (i <= cliptoblack) { | |
| 1272 tabval[i] = 0; | |
| 1273 tab38[i] = 0; | |
| 1274 tab14[i] = 0; | |
| 1275 } else if (i < 43) { | |
| 1276 tabval[i] = 0; | |
| 1277 tab38[i] = (3 * i + 4) / 8; | |
| 1278 tab14[i] = (i + 2) / 4; | |
| 1279 } else if (i < 85) { | |
| 1280 tabval[i] = 1; | |
| 1281 tab38[i] = (3 * (i - 85) - 4) / 8; | |
| 1282 tab14[i] = ((i - 85) - 2) / 4; | |
| 1283 } else if (i < 128) { | |
| 1284 tabval[i] = 1; | |
| 1285 tab38[i] = (3 * (i - 85) + 4) / 8; | |
| 1286 tab14[i] = ((i - 85) + 2) / 4; | |
| 1287 } else if (i < 170) { | |
| 1288 tabval[i] = 2; | |
| 1289 tab38[i] = (3 * (i - 170) - 4) / 8; | |
| 1290 tab14[i] = ((i - 170) - 2) / 4; | |
| 1291 } else if (i < 213) { | |
| 1292 tabval[i] = 2; | |
| 1293 tab38[i] = (3 * (i - 170) + 4) / 8; | |
| 1294 tab14[i] = ((i - 170) + 2) / 4; | |
| 1295 } else if (i < 255 - cliptowhite) { | |
| 1296 tabval[i] = 3; | |
| 1297 tab38[i] = (3 * (i - 255) - 4) / 8; | |
| 1298 tab14[i] = ((i - 255) - 2) / 4; | |
| 1299 } else { /* i >= 255 - cliptowhite */ | |
| 1300 tabval[i] = 3; | |
| 1301 tab38[i] = 0; | |
| 1302 tab14[i] = 0; | |
| 1303 } | |
| 1304 } | |
| 1305 | |
| 1306 return 0; | |
| 1307 } | |
| 1308 | |
| 1309 | |
| 1310 /*--------------------------------------------------------------------* | |
| 1311 * Simple (pixelwise) thresholding to 2 bpp with optional colormap * | |
| 1312 *--------------------------------------------------------------------*/ | |
| 1313 /*! | |
| 1314 * \brief pixThresholdTo2bpp() | |
| 1315 * | |
| 1316 * \param[in] pixs 8 bpp | |
| 1317 * \param[in] nlevels equally spaced; must be between 2 and 4 | |
| 1318 * \param[in] cmapflag 1 to build colormap; 0 otherwise | |
| 1319 * \return pixd 2 bpp, optionally with colormap, or NULL on error | |
| 1320 * | |
| 1321 * <pre> | |
| 1322 * Notes: | |
| 1323 * (1) Valid values for nlevels is the set {2, 3, 4}. | |
| 1324 * (2) Any colormap on the input pixs is removed to 8 bpp grayscale. | |
| 1325 * (3) This function is typically invoked with cmapflag == 1. | |
| 1326 * In the situation where no colormap is desired, nlevels is | |
| 1327 * ignored and pixs is thresholded to 4 levels. | |
| 1328 * (4) The target output colors are equally spaced, with the | |
| 1329 * darkest at 0 and the lightest at 255. The thresholds are | |
| 1330 * chosen halfway between adjacent output values. A table | |
| 1331 * is built that specifies the mapping from src to dest. | |
| 1332 * (5) If cmapflag == 1, a colormap of size 'nlevels' is made, | |
| 1333 * and the pixel values in pixs are replaced by their | |
| 1334 * appropriate color indices. The number of holdouts, | |
| 1335 * 4 - nlevels, will be between 0 and 2. | |
| 1336 * (6) If you don't want the thresholding to be equally spaced, | |
| 1337 * either first transform the 8 bpp src using pixGammaTRC(). | |
| 1338 * or, if cmapflag == 1, after calling this function you can use | |
| 1339 * pixcmapResetColor() to change any individual colors. | |
| 1340 * (7) If a colormap is generated, it will specify (to display | |
| 1341 * programs) exactly how each level is to be represented in RGB | |
| 1342 * space. When representing text, 3 levels is far better than | |
| 1343 * 2 because of the antialiasing of the single gray level, | |
| 1344 * and 4 levels (black, white and 2 gray levels) is getting | |
| 1345 * close to the perceptual quality of a (nearly continuous) | |
| 1346 * grayscale image. With 2 bpp, you can set up a colormap | |
| 1347 * and allocate from 2 to 4 levels to represent antialiased text. | |
| 1348 * Any left over colormap entries can be used for coloring regions. | |
| 1349 * For the same number of levels, the file size of a 2 bpp image | |
| 1350 * is about 10% smaller than that of a 4 bpp result for the same | |
| 1351 * number of levels. For both 2 bpp and 4 bpp, using 4 levels you | |
| 1352 * get compression far better than that of jpeg, because the | |
| 1353 * quantization to 4 levels will remove the jpeg ringing in the | |
| 1354 * background near character edges. | |
| 1355 * </pre> | |
| 1356 */ | |
| 1357 PIX * | |
| 1358 pixThresholdTo2bpp(PIX *pixs, | |
| 1359 l_int32 nlevels, | |
| 1360 l_int32 cmapflag) | |
| 1361 { | |
| 1362 l_int32 *qtab; | |
| 1363 l_int32 w, h, d, wplt, wpld; | |
| 1364 l_uint32 *datat, *datad; | |
| 1365 PIX *pixt, *pixd; | |
| 1366 PIXCMAP *cmap; | |
| 1367 | |
| 1368 if (!pixs) | |
| 1369 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1370 pixGetDimensions(pixs, &w, &h, &d); | |
| 1371 if (d != 8) | |
| 1372 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); | |
| 1373 if (nlevels < 2 || nlevels > 4) | |
| 1374 return (PIX *)ERROR_PTR("nlevels not in {2, 3, 4}", __func__, NULL); | |
| 1375 | |
| 1376 if ((pixd = pixCreate(w, h, 2)) == NULL) | |
| 1377 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 1378 pixCopyResolution(pixd, pixs); | |
| 1379 pixCopyInputFormat(pixd, pixs); | |
| 1380 datad = pixGetData(pixd); | |
| 1381 wpld = pixGetWpl(pixd); | |
| 1382 | |
| 1383 if (cmapflag) { /* hold out (4 - nlevels) cmap entries */ | |
| 1384 cmap = pixcmapCreateLinear(2, nlevels); | |
| 1385 pixSetColormap(pixd, cmap); | |
| 1386 } | |
| 1387 | |
| 1388 /* If there is a colormap in the src, remove it */ | |
| 1389 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 1390 datat = pixGetData(pixt); | |
| 1391 wplt = pixGetWpl(pixt); | |
| 1392 | |
| 1393 /* Make the appropriate table */ | |
| 1394 if (cmapflag) | |
| 1395 qtab = makeGrayQuantIndexTable(nlevels); | |
| 1396 else | |
| 1397 qtab = makeGrayQuantTargetTable(4, 2); | |
| 1398 | |
| 1399 thresholdTo2bppLow(datad, h, wpld, datat, wplt, qtab); | |
| 1400 | |
| 1401 LEPT_FREE(qtab); | |
| 1402 pixDestroy(&pixt); | |
| 1403 return pixd; | |
| 1404 } | |
| 1405 | |
| 1406 | |
| 1407 /*! | |
| 1408 * \brief thresholdTo2bppLow() | |
| 1409 * | |
| 1410 * Low-level function for thresholding from 8 bpp (datas) to | |
| 1411 * 2 bpp (datad), using thresholds implicitly defined through %tab, | |
| 1412 * a 256-entry lookup table that gives a 2-bit output value | |
| 1413 * for each possible input. | |
| 1414 * | |
| 1415 * For each line, unroll the loop so that for each 32 bit src word, | |
| 1416 * representing four consecutive 8-bit pixels, we compose one byte | |
| 1417 * of output consisiting of four 2-bit pixels. | |
| 1418 */ | |
| 1419 static void | |
| 1420 thresholdTo2bppLow(l_uint32 *datad, | |
| 1421 l_int32 h, | |
| 1422 l_int32 wpld, | |
| 1423 l_uint32 *datas, | |
| 1424 l_int32 wpls, | |
| 1425 l_int32 *tab) | |
| 1426 { | |
| 1427 l_uint8 sval1, sval2, sval3, sval4, dval; | |
| 1428 l_int32 i, j, k; | |
| 1429 l_uint32 *lines, *lined; | |
| 1430 | |
| 1431 for (i = 0; i < h; i++) { | |
| 1432 lines = datas + i * wpls; | |
| 1433 lined = datad + i * wpld; | |
| 1434 for (j = 0; j < wpls; j++) { | |
| 1435 k = 4 * j; | |
| 1436 sval1 = GET_DATA_BYTE(lines, k); | |
| 1437 sval2 = GET_DATA_BYTE(lines, k + 1); | |
| 1438 sval3 = GET_DATA_BYTE(lines, k + 2); | |
| 1439 sval4 = GET_DATA_BYTE(lines, k + 3); | |
| 1440 dval = (tab[sval1] << 6) | (tab[sval2] << 4) | | |
| 1441 (tab[sval3] << 2) | tab[sval4]; | |
| 1442 SET_DATA_BYTE(lined, j, dval); | |
| 1443 } | |
| 1444 } | |
| 1445 } | |
| 1446 | |
| 1447 | |
| 1448 /*----------------------------------------------------------------------* | |
| 1449 * Simple (pixelwise) thresholding to 4 bpp * | |
| 1450 *----------------------------------------------------------------------*/ | |
| 1451 /*! | |
| 1452 * \brief pixThresholdTo4bpp() | |
| 1453 * | |
| 1454 * \param[in] pixs 8 bpp, can have colormap | |
| 1455 * \param[in] nlevels equally spaced; must be between 2 and 16 | |
| 1456 * \param[in] cmapflag 1 to build colormap; 0 otherwise | |
| 1457 * \return pixd 4 bpp, optionally with colormap, or NULL on error | |
| 1458 * | |
| 1459 * <pre> | |
| 1460 * Notes: | |
| 1461 * (1) Valid values for nlevels is the set {2, ... 16}. | |
| 1462 * (2) Any colormap on the input pixs is removed to 8 bpp grayscale. | |
| 1463 * (3) This function is typically invoked with cmapflag == 1. | |
| 1464 * In the situation where no colormap is desired, nlevels is | |
| 1465 * ignored and pixs is thresholded to 16 levels. | |
| 1466 * (4) The target output colors are equally spaced, with the | |
| 1467 * darkest at 0 and the lightest at 255. The thresholds are | |
| 1468 * chosen halfway between adjacent output values. A table | |
| 1469 * is built that specifies the mapping from src to dest. | |
| 1470 * (5) If cmapflag == 1, a colormap of size 'nlevels' is made, | |
| 1471 * and the pixel values in pixs are replaced by their | |
| 1472 * appropriate color indices. The number of holdouts, | |
| 1473 * 16 - nlevels, will be between 0 and 14. | |
| 1474 * (6) If you don't want the thresholding to be equally spaced, | |
| 1475 * either first transform the 8 bpp src using pixGammaTRC(). | |
| 1476 * or, if cmapflag == 1, after calling this function you can use | |
| 1477 * pixcmapResetColor() to change any individual colors. | |
| 1478 * (7) If a colormap is generated, it will specify, to display | |
| 1479 * programs, exactly how each level is to be represented in RGB | |
| 1480 * space. When representing text, 3 levels is far better than | |
| 1481 * 2 because of the antialiasing of the single gray level, | |
| 1482 * and 4 levels (black, white and 2 gray levels) is getting | |
| 1483 * close to the perceptual quality of a (nearly continuous) | |
| 1484 * grayscale image. Therefore, with 4 bpp, you can set up a | |
| 1485 * colormap, allocate a relatively small fraction of the 16 | |
| 1486 * possible values to represent antialiased text, and use the | |
| 1487 * other colormap entries for other things, such as coloring | |
| 1488 * text or background. Two other reasons for using a small number | |
| 1489 * of gray values for antialiased text are (1) PNG compression | |
| 1490 * gets worse as the number of levels that are used is increased, | |
| 1491 * and (2) using a small number of levels will filter out most of | |
| 1492 * the jpeg ringing that is typically introduced near sharp edges | |
| 1493 * of text. This filtering is partly responsible for the improved | |
| 1494 * compression. | |
| 1495 * </pre> | |
| 1496 */ | |
| 1497 PIX * | |
| 1498 pixThresholdTo4bpp(PIX *pixs, | |
| 1499 l_int32 nlevels, | |
| 1500 l_int32 cmapflag) | |
| 1501 { | |
| 1502 l_int32 *qtab; | |
| 1503 l_int32 w, h, d, wplt, wpld; | |
| 1504 l_uint32 *datat, *datad; | |
| 1505 PIX *pixt, *pixd; | |
| 1506 PIXCMAP *cmap; | |
| 1507 | |
| 1508 if (!pixs) | |
| 1509 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1510 pixGetDimensions(pixs, &w, &h, &d); | |
| 1511 if (d != 8) | |
| 1512 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); | |
| 1513 if (nlevels < 2 || nlevels > 16) | |
| 1514 return (PIX *)ERROR_PTR("nlevels not in [2,...,16]", __func__, NULL); | |
| 1515 | |
| 1516 if ((pixd = pixCreate(w, h, 4)) == NULL) | |
| 1517 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 1518 pixCopyResolution(pixd, pixs); | |
| 1519 pixCopyInputFormat(pixd, pixs); | |
| 1520 datad = pixGetData(pixd); | |
| 1521 wpld = pixGetWpl(pixd); | |
| 1522 | |
| 1523 if (cmapflag) { /* hold out (16 - nlevels) cmap entries */ | |
| 1524 cmap = pixcmapCreateLinear(4, nlevels); | |
| 1525 pixSetColormap(pixd, cmap); | |
| 1526 } | |
| 1527 | |
| 1528 /* If there is a colormap in the src, remove it */ | |
| 1529 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 1530 datat = pixGetData(pixt); | |
| 1531 wplt = pixGetWpl(pixt); | |
| 1532 | |
| 1533 /* Make the appropriate table */ | |
| 1534 if (cmapflag) | |
| 1535 qtab = makeGrayQuantIndexTable(nlevels); | |
| 1536 else | |
| 1537 qtab = makeGrayQuantTargetTable(16, 4); | |
| 1538 | |
| 1539 thresholdTo4bppLow(datad, h, wpld, datat, wplt, qtab); | |
| 1540 | |
| 1541 LEPT_FREE(qtab); | |
| 1542 pixDestroy(&pixt); | |
| 1543 return pixd; | |
| 1544 } | |
| 1545 | |
| 1546 | |
| 1547 /*! | |
| 1548 * \brief thresholdTo4bppLow() | |
| 1549 * | |
| 1550 * Low-level function for thresholding from 8 bpp (datas) to | |
| 1551 * 4 bpp (datad), using thresholds implicitly defined through %tab, | |
| 1552 * a 256-entry lookup table that gives a 4-bit output value | |
| 1553 * for each possible input. | |
| 1554 * | |
| 1555 * For each line, unroll the loop so that for each 32 bit src word, | |
| 1556 * representing four consecutive 8-bit pixels, we compose two bytes | |
| 1557 * of output consisiting of four 4-bit pixels. | |
| 1558 */ | |
| 1559 static void | |
| 1560 thresholdTo4bppLow(l_uint32 *datad, | |
| 1561 l_int32 h, | |
| 1562 l_int32 wpld, | |
| 1563 l_uint32 *datas, | |
| 1564 l_int32 wpls, | |
| 1565 l_int32 *tab) | |
| 1566 { | |
| 1567 l_uint8 sval1, sval2, sval3, sval4; | |
| 1568 l_uint16 dval; | |
| 1569 l_int32 i, j, k; | |
| 1570 l_uint32 *lines, *lined; | |
| 1571 | |
| 1572 for (i = 0; i < h; i++) { | |
| 1573 lines = datas + i * wpls; | |
| 1574 lined = datad + i * wpld; | |
| 1575 for (j = 0; j < wpls; j++) { | |
| 1576 k = 4 * j; | |
| 1577 sval1 = GET_DATA_BYTE(lines, k); | |
| 1578 sval2 = GET_DATA_BYTE(lines, k + 1); | |
| 1579 sval3 = GET_DATA_BYTE(lines, k + 2); | |
| 1580 sval4 = GET_DATA_BYTE(lines, k + 3); | |
| 1581 dval = (tab[sval1] << 12) | (tab[sval2] << 8) | | |
| 1582 (tab[sval3] << 4) | tab[sval4]; | |
| 1583 SET_DATA_TWO_BYTES(lined, j, dval); | |
| 1584 } | |
| 1585 } | |
| 1586 } | |
| 1587 | |
| 1588 | |
| 1589 /*----------------------------------------------------------------------* | |
| 1590 * Simple (pixelwise) thresholding on 8 bpp with optional colormap * | |
| 1591 *----------------------------------------------------------------------*/ | |
| 1592 /*! | |
| 1593 * \brief pixThresholdOn8bpp() | |
| 1594 * | |
| 1595 * \param[in] pixs 8 bpp, can have colormap | |
| 1596 * \param[in] nlevels equally spaced; must be between 2 and 256 | |
| 1597 * \param[in] cmapflag 1 to build colormap; 0 otherwise | |
| 1598 * \return pixd 8 bpp, optionally with colormap, or NULL on error | |
| 1599 * | |
| 1600 * <pre> | |
| 1601 * Notes: | |
| 1602 * (1) Valid values for nlevels is the set {2,...,256}. | |
| 1603 * (2) Any colormap on the input pixs is removed to 8 bpp grayscale. | |
| 1604 * (3) If cmapflag == 1, a colormap of size 'nlevels' is made, | |
| 1605 * and the pixel values in pixs are replaced by their | |
| 1606 * appropriate color indices. Otherwise, the pixel values | |
| 1607 * are the actual thresholded (i.e., quantized) grayscale values. | |
| 1608 * (4) If you don't want the thresholding to be equally spaced, | |
| 1609 * first transform the input 8 bpp src using pixGammaTRC(). | |
| 1610 * </pre> | |
| 1611 */ | |
| 1612 PIX * | |
| 1613 pixThresholdOn8bpp(PIX *pixs, | |
| 1614 l_int32 nlevels, | |
| 1615 l_int32 cmapflag) | |
| 1616 { | |
| 1617 l_int32 *qtab; /* quantization table */ | |
| 1618 l_int32 i, j, w, h, wpld, val, newval; | |
| 1619 l_uint32 *datad, *lined; | |
| 1620 PIX *pixd; | |
| 1621 PIXCMAP *cmap; | |
| 1622 | |
| 1623 if (!pixs) | |
| 1624 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1625 if (pixGetDepth(pixs) != 8) | |
| 1626 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); | |
| 1627 if (nlevels < 2 || nlevels > 256) | |
| 1628 return (PIX *)ERROR_PTR("nlevels not in [2,...,256]", __func__, NULL); | |
| 1629 | |
| 1630 /* Get a new pixd; if there is a colormap in the src, remove it */ | |
| 1631 if (pixGetColormap(pixs)) | |
| 1632 pixd = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 1633 else | |
| 1634 pixd = pixCopy(NULL, pixs); | |
| 1635 | |
| 1636 if (cmapflag) { /* hold out (256 - nlevels) cmap entries */ | |
| 1637 cmap = pixcmapCreateLinear(8, nlevels); | |
| 1638 pixSetColormap(pixd, cmap); | |
| 1639 } | |
| 1640 | |
| 1641 if (cmapflag) | |
| 1642 qtab = makeGrayQuantIndexTable(nlevels); | |
| 1643 else | |
| 1644 qtab = makeGrayQuantTargetTable(nlevels, 8); | |
| 1645 | |
| 1646 pixGetDimensions(pixd, &w, &h, NULL); | |
| 1647 pixCopyResolution(pixd, pixs); | |
| 1648 pixCopyInputFormat(pixd, pixs); | |
| 1649 datad = pixGetData(pixd); | |
| 1650 wpld = pixGetWpl(pixd); | |
| 1651 for (i = 0; i < h; i++) { | |
| 1652 lined = datad + i * wpld; | |
| 1653 for (j = 0; j < w; j++) { | |
| 1654 val = GET_DATA_BYTE(lined, j); | |
| 1655 newval = qtab[val]; | |
| 1656 SET_DATA_BYTE(lined, j, newval); | |
| 1657 } | |
| 1658 } | |
| 1659 | |
| 1660 LEPT_FREE(qtab); | |
| 1661 return pixd; | |
| 1662 } | |
| 1663 | |
| 1664 | |
| 1665 /*----------------------------------------------------------------------* | |
| 1666 * Arbitrary (pixelwise) thresholding from 8 bpp to 2, 4 or 8 bpp * | |
| 1667 *----------------------------------------------------------------------*/ | |
| 1668 /*! | |
| 1669 * \brief pixThresholdGrayArb() | |
| 1670 * | |
| 1671 * \param[in] pixs 8 bpp grayscale; can have colormap | |
| 1672 * \param[in] edgevals string giving edge value of each bin | |
| 1673 * \param[in] outdepth 0, 2, 4 or 8 bpp; 0 is default for min depth | |
| 1674 * \param[in] use_average 1 if use the average pixel value in colormap | |
| 1675 * \param[in] setblack 1 if darkest color is set to black | |
| 1676 * \param[in] setwhite 1 if lightest color is set to white | |
| 1677 * \return pixd 2, 4 or 8 bpp quantized image with colormap, | |
| 1678 * or NULL on error | |
| 1679 * | |
| 1680 * <pre> | |
| 1681 * Notes: | |
| 1682 * (1) This function allows exact specification of the quantization bins. | |
| 1683 * The string %edgevals is a space-separated set of values | |
| 1684 * specifying the dividing points between output quantization bins. | |
| 1685 * These threshold values are assigned to the bin with higher | |
| 1686 * values, so that each of them is the smallest value in their bin. | |
| 1687 * (2) The output image (pixd) depth is specified by %outdepth. The | |
| 1688 * number of bins is the number of edgevals + 1. The | |
| 1689 * relation between outdepth and the number of bins is: | |
| 1690 * outdepth = 2 nbins <= 4 | |
| 1691 * outdepth = 4 nbins <= 16 | |
| 1692 * outdepth = 8 nbins <= 256 | |
| 1693 * With %outdepth == 0, the minimum required depth for the | |
| 1694 * given number of bins is used. | |
| 1695 * The output pixd has a colormap. | |
| 1696 * (3) The last 3 args determine the specific values that go into | |
| 1697 * the colormap. | |
| 1698 * (4) For %use_average: | |
| 1699 * ~ if TRUE, the average value of pixels falling in the bin is | |
| 1700 * chosen as the representative gray value. Otherwise, | |
| 1701 * ~ if FALSE, the central value of each bin is chosen as | |
| 1702 * the representative value. | |
| 1703 * The colormap holds the representative value. | |
| 1704 * (5) For %setblack, if TRUE the darkest color is set to (0,0,0). | |
| 1705 * (6) For %setwhite, if TRUE the lightest color is set to (255,255,255). | |
| 1706 * (7) An alternative to using this function to quantize to | |
| 1707 * unequally-spaced bins is to first transform the 8 bpp pixs | |
| 1708 * using pixGammaTRC(), and follow this with pixThresholdTo4bpp(). | |
| 1709 * </pre> | |
| 1710 */ | |
| 1711 PIX * | |
| 1712 pixThresholdGrayArb(PIX *pixs, | |
| 1713 const char *edgevals, | |
| 1714 l_int32 outdepth, | |
| 1715 l_int32 use_average, | |
| 1716 l_int32 setblack, | |
| 1717 l_int32 setwhite) | |
| 1718 { | |
| 1719 l_int32 *qtab; | |
| 1720 l_int32 w, h, d, i, j, n, wplt, wpld, val, newval; | |
| 1721 l_uint32 *datat, *datad, *linet, *lined; | |
| 1722 NUMA *na; | |
| 1723 PIX *pixt, *pixd; | |
| 1724 PIXCMAP *cmap; | |
| 1725 | |
| 1726 if (!pixs) | |
| 1727 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1728 pixGetDimensions(pixs, &w, &h, &d); | |
| 1729 if (d != 8) | |
| 1730 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); | |
| 1731 if (!edgevals) | |
| 1732 return (PIX *)ERROR_PTR("edgevals not defined", __func__, NULL); | |
| 1733 if (outdepth != 0 && outdepth != 2 && outdepth != 4 && outdepth != 8) | |
| 1734 return (PIX *)ERROR_PTR("invalid outdepth", __func__, NULL); | |
| 1735 | |
| 1736 /* Parse and sort (if required) the bin edge values */ | |
| 1737 na = parseStringForNumbers(edgevals, " \t\n,"); | |
| 1738 n = numaGetCount(na); | |
| 1739 if (n > 255) { | |
| 1740 numaDestroy(&na); | |
| 1741 return (PIX *)ERROR_PTR("more than 256 levels", __func__, NULL); | |
| 1742 } | |
| 1743 if (outdepth == 0) { | |
| 1744 if (n <= 3) | |
| 1745 outdepth = 2; | |
| 1746 else if (n <= 15) | |
| 1747 outdepth = 4; | |
| 1748 else | |
| 1749 outdepth = 8; | |
| 1750 } else if (n + 1 > (1 << outdepth)) { | |
| 1751 L_WARNING("outdepth too small; setting to 8 bpp\n", __func__); | |
| 1752 outdepth = 8; | |
| 1753 } | |
| 1754 numaSort(na, na, L_SORT_INCREASING); | |
| 1755 | |
| 1756 /* Make the quantization LUT and the colormap */ | |
| 1757 makeGrayQuantTableArb(na, outdepth, &qtab, &cmap); | |
| 1758 if (use_average) { /* use the average value in each bin */ | |
| 1759 pixcmapDestroy(&cmap); | |
| 1760 makeGrayQuantColormapArb(pixs, qtab, outdepth, &cmap); | |
| 1761 } | |
| 1762 pixcmapSetBlackAndWhite(cmap, setblack, setwhite); | |
| 1763 numaDestroy(&na); | |
| 1764 | |
| 1765 if ((pixd = pixCreate(w, h, outdepth)) == NULL) { | |
| 1766 LEPT_FREE(qtab); | |
| 1767 pixcmapDestroy(&cmap); | |
| 1768 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 1769 } | |
| 1770 pixCopyResolution(pixd, pixs); | |
| 1771 pixCopyInputFormat(pixd, pixs); | |
| 1772 pixSetColormap(pixd, cmap); | |
| 1773 datad = pixGetData(pixd); | |
| 1774 wpld = pixGetWpl(pixd); | |
| 1775 | |
| 1776 /* If there is a colormap in the src, remove it */ | |
| 1777 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 1778 datat = pixGetData(pixt); | |
| 1779 wplt = pixGetWpl(pixt); | |
| 1780 | |
| 1781 if (outdepth == 2) { | |
| 1782 thresholdTo2bppLow(datad, h, wpld, datat, wplt, qtab); | |
| 1783 } else if (outdepth == 4) { | |
| 1784 thresholdTo4bppLow(datad, h, wpld, datat, wplt, qtab); | |
| 1785 } else { | |
| 1786 for (i = 0; i < h; i++) { | |
| 1787 lined = datad + i * wpld; | |
| 1788 linet = datat + i * wplt; | |
| 1789 for (j = 0; j < w; j++) { | |
| 1790 val = GET_DATA_BYTE(linet, j); | |
| 1791 newval = qtab[val]; | |
| 1792 SET_DATA_BYTE(lined, j, newval); | |
| 1793 } | |
| 1794 } | |
| 1795 } | |
| 1796 | |
| 1797 LEPT_FREE(qtab); | |
| 1798 pixDestroy(&pixt); | |
| 1799 return pixd; | |
| 1800 } | |
| 1801 | |
| 1802 | |
| 1803 /*----------------------------------------------------------------------* | |
| 1804 * Quantization tables for linear thresholds of grayscale images * | |
| 1805 *----------------------------------------------------------------------*/ | |
| 1806 /*! | |
| 1807 * \brief makeGrayQuantIndexTable() | |
| 1808 * | |
| 1809 * \param[in] nlevels number of output levels | |
| 1810 * \return table maps input gray level to colormap index, | |
| 1811 * or NULL on error | |
| 1812 * <pre> | |
| 1813 * Notes: | |
| 1814 * (1) 'nlevels' is some number between 2 and 256 (typically 8 or less). | |
| 1815 * (2) The table is typically used for quantizing 2, 4 and 8 bpp | |
| 1816 * grayscale src pix, and generating a colormapped dest pix. | |
| 1817 * </pre> | |
| 1818 */ | |
| 1819 l_int32 * | |
| 1820 makeGrayQuantIndexTable(l_int32 nlevels) | |
| 1821 { | |
| 1822 l_int32 *tab; | |
| 1823 l_int32 i, j, thresh; | |
| 1824 | |
| 1825 tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); | |
| 1826 for (i = 0; i < 256; i++) { | |
| 1827 for (j = 0; j < nlevels; j++) { | |
| 1828 thresh = 255 * (2 * j + 1) / (2 * nlevels - 2); | |
| 1829 if (i <= thresh) { | |
| 1830 tab[i] = j; | |
| 1831 /* lept_stderr("tab[%d] = %d\n", i, j); */ | |
| 1832 break; | |
| 1833 } | |
| 1834 } | |
| 1835 } | |
| 1836 return tab; | |
| 1837 } | |
| 1838 | |
| 1839 | |
| 1840 /*! | |
| 1841 * \brief makeGrayQuantTargetTable() | |
| 1842 * | |
| 1843 * \param[in] nlevels number of output levels | |
| 1844 * \param[in] depth of dest pix, in bpp; 2, 4 or 8 bpp | |
| 1845 * \return table maps input gray level to thresholded gray level, | |
| 1846 * or NULL on error | |
| 1847 * | |
| 1848 * <pre> | |
| 1849 * Notes: | |
| 1850 * (1) nlevels is some number between 2 and 2^(depth) | |
| 1851 * (2) The table is used in two similar ways: | |
| 1852 * ~ for 8 bpp, it quantizes to a given number of target levels | |
| 1853 * ~ for 2 and 4 bpp, it thresholds to appropriate target values | |
| 1854 * that will use the full dynamic range of the dest pix. | |
| 1855 * (3) For depth = 8, the number of thresholds chosen is | |
| 1856 * ('nlevels' - 1), and the 'nlevels' values stored in the | |
| 1857 * table are at the two at the extreme ends, (0, 255), plus | |
| 1858 * plus ('nlevels' - 2) values chosen at equal intervals between. | |
| 1859 * For example, for depth = 8 and 'nlevels' = 3, the two | |
| 1860 * threshold values are 3f and bf, and the three target pixel | |
| 1861 * values are 0, 7f and ff. | |
| 1862 * (4) For depth < 8, we ignore nlevels, and always use the maximum | |
| 1863 * number of levels, which is 2^(depth). | |
| 1864 * If you want nlevels < the maximum number, you should always | |
| 1865 * use a colormap. | |
| 1866 * </pre> | |
| 1867 */ | |
| 1868 static l_int32 * | |
| 1869 makeGrayQuantTargetTable(l_int32 nlevels, | |
| 1870 l_int32 depth) | |
| 1871 { | |
| 1872 l_int32 *tab; | |
| 1873 l_int32 i, j, thresh, maxval, quantval; | |
| 1874 | |
| 1875 tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); | |
| 1876 maxval = (1 << depth) - 1; | |
| 1877 if (depth < 8) | |
| 1878 nlevels = 1 << depth; | |
| 1879 for (i = 0; i < 256; i++) { | |
| 1880 for (j = 0; j < nlevels; j++) { | |
| 1881 thresh = 255 * (2 * j + 1) / (2 * nlevels - 2); | |
| 1882 if (i <= thresh) { | |
| 1883 quantval = maxval * j / (nlevels - 1); | |
| 1884 tab[i] = quantval; | |
| 1885 /* lept_stderr("tab[%d] = %d\n", i, tab[i]); */ | |
| 1886 break; | |
| 1887 } | |
| 1888 } | |
| 1889 } | |
| 1890 return tab; | |
| 1891 } | |
| 1892 | |
| 1893 | |
| 1894 /*----------------------------------------------------------------------* | |
| 1895 * Quantization table for arbitrary thresholding of grayscale images * | |
| 1896 *----------------------------------------------------------------------*/ | |
| 1897 /*! | |
| 1898 * \brief makeGrayQuantTableArb() | |
| 1899 * | |
| 1900 * \param[in] na numa of bin boundaries | |
| 1901 * \param[in] outdepth of colormap: 1, 2, 4 or 8 | |
| 1902 * \param[out] ptab table mapping input gray level to cmap index | |
| 1903 * \param[out] pcmap colormap | |
| 1904 * \return 0 if OK, 1 on error | |
| 1905 * | |
| 1906 * <pre> | |
| 1907 * Notes: | |
| 1908 * (1) The number of bins is the count of %na + 1. | |
| 1909 * (2) The bin boundaries in na must be sorted in increasing order. | |
| 1910 * (3) The table is an inverse colormap: it maps input gray level | |
| 1911 * to colormap index (the bin number). | |
| 1912 * (4) The colormap generated here has quantized values at the | |
| 1913 * center of each bin. If you want to use the average gray | |
| 1914 * value of pixels within the bin, discard the colormap and | |
| 1915 * compute it using makeGrayQuantColormapArb(). | |
| 1916 * (5) Returns an error if there are not enough levels in the | |
| 1917 * output colormap for the number of bins. The number | |
| 1918 * of bins must not exceed 2^outdepth. | |
| 1919 * </pre> | |
| 1920 */ | |
| 1921 l_ok | |
| 1922 makeGrayQuantTableArb(NUMA *na, | |
| 1923 l_int32 outdepth, | |
| 1924 l_int32 **ptab, | |
| 1925 PIXCMAP **pcmap) | |
| 1926 { | |
| 1927 l_int32 i, j, n, jstart, ave, val; | |
| 1928 l_int32 *tab; | |
| 1929 PIXCMAP *cmap; | |
| 1930 | |
| 1931 if (!ptab) | |
| 1932 return ERROR_INT("&tab not defined", __func__, 1); | |
| 1933 *ptab = NULL; | |
| 1934 if (!pcmap) | |
| 1935 return ERROR_INT("&cmap not defined", __func__, 1); | |
| 1936 *pcmap = NULL; | |
| 1937 if (!na) | |
| 1938 return ERROR_INT("na not defined", __func__, 1); | |
| 1939 n = numaGetCount(na); | |
| 1940 if (n + 1 > (1 << outdepth)) | |
| 1941 return ERROR_INT("more bins than cmap levels", __func__, 1); | |
| 1942 | |
| 1943 if ((cmap = pixcmapCreate(outdepth)) == NULL) | |
| 1944 return ERROR_INT("cmap not made", __func__, 1); | |
| 1945 tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); | |
| 1946 *ptab = tab; | |
| 1947 *pcmap = cmap; | |
| 1948 | |
| 1949 /* First n bins */ | |
| 1950 jstart = 0; | |
| 1951 for (i = 0; i < n; i++) { | |
| 1952 numaGetIValue(na, i, &val); | |
| 1953 ave = (jstart + val) / 2; | |
| 1954 pixcmapAddColor(cmap, ave, ave, ave); | |
| 1955 for (j = jstart; j < val; j++) | |
| 1956 tab[j] = i; | |
| 1957 jstart = val; | |
| 1958 } | |
| 1959 | |
| 1960 /* Last bin */ | |
| 1961 ave = (jstart + 255) / 2; | |
| 1962 pixcmapAddColor(cmap, ave, ave, ave); | |
| 1963 for (j = jstart; j < 256; j++) | |
| 1964 tab[j] = n; | |
| 1965 | |
| 1966 return 0; | |
| 1967 } | |
| 1968 | |
| 1969 | |
| 1970 /*! | |
| 1971 * \brief makeGrayQuantColormapArb() | |
| 1972 * | |
| 1973 * \param[in] pixs 8 bpp | |
| 1974 * \param[in] tab table mapping input gray level to cmap index | |
| 1975 * \param[in] outdepth of colormap: 1, 2, 4 or 8 | |
| 1976 * \param[out] pcmap colormap | |
| 1977 * \return 0 if OK, 1 on error | |
| 1978 * | |
| 1979 * <pre> | |
| 1980 * Notes: | |
| 1981 * (1) The table is a 256-entry inverse colormap: it maps input gray | |
| 1982 * level to colormap index (the bin number). It is computed | |
| 1983 * using makeGrayQuantTableArb(). | |
| 1984 * (2) The colormap generated here has quantized values at the | |
| 1985 * average gray value of the pixels that are in each bin. | |
| 1986 * (3) Returns an error if there are not enough levels in the | |
| 1987 * output colormap for the number of bins. The number | |
| 1988 * of bins must not exceed 2^outdepth. | |
| 1989 * </pre> | |
| 1990 */ | |
| 1991 static l_int32 | |
| 1992 makeGrayQuantColormapArb(PIX *pixs, | |
| 1993 l_int32 *tab, | |
| 1994 l_int32 outdepth, | |
| 1995 PIXCMAP **pcmap) | |
| 1996 { | |
| 1997 l_int32 i, j, index, w, h, d, nbins, wpl, factor, val; | |
| 1998 l_int32 *bincount, *binave, *binstart; | |
| 1999 l_uint32 *line, *data; | |
| 2000 | |
| 2001 if (!pcmap) | |
| 2002 return ERROR_INT("&cmap not defined", __func__, 1); | |
| 2003 *pcmap = NULL; | |
| 2004 if (!pixs) | |
| 2005 return ERROR_INT("pixs not defined", __func__, 1); | |
| 2006 pixGetDimensions(pixs, &w, &h, &d); | |
| 2007 if (d != 8) | |
| 2008 return ERROR_INT("pixs not 8 bpp", __func__, 1); | |
| 2009 if (!tab) | |
| 2010 return ERROR_INT("tab not defined", __func__, 1); | |
| 2011 nbins = tab[255] + 1; | |
| 2012 if (nbins > (1 << outdepth)) | |
| 2013 return ERROR_INT("more bins than cmap levels", __func__, 1); | |
| 2014 | |
| 2015 /* Find the count and weighted count for each bin */ | |
| 2016 if ((bincount = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32))) == NULL) | |
| 2017 return ERROR_INT("calloc fail for bincount", __func__, 1); | |
| 2018 if ((binave = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32))) == NULL) { | |
| 2019 LEPT_FREE(bincount); | |
| 2020 return ERROR_INT("calloc fail for binave", __func__, 1); | |
| 2021 } | |
| 2022 factor = (l_int32)(sqrt((l_float64)(w * h) / 30000.) + 0.5); | |
| 2023 factor = L_MAX(1, factor); | |
| 2024 data = pixGetData(pixs); | |
| 2025 wpl = pixGetWpl(pixs); | |
| 2026 for (i = 0; i < h; i += factor) { | |
| 2027 line = data + i * wpl; | |
| 2028 for (j = 0; j < w; j += factor) { | |
| 2029 val = GET_DATA_BYTE(line, j); | |
| 2030 bincount[tab[val]]++; | |
| 2031 binave[tab[val]] += val; | |
| 2032 } | |
| 2033 } | |
| 2034 | |
| 2035 /* Find the smallest gray values in each bin */ | |
| 2036 binstart = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32)); | |
| 2037 for (i = 1, index = 1; i < 256; i++) { | |
| 2038 if (tab[i] < index) continue; | |
| 2039 if (tab[i] == index) | |
| 2040 binstart[index++] = i; | |
| 2041 } | |
| 2042 | |
| 2043 /* Get the averages. If there are no samples in a bin, use | |
| 2044 * the center value of the bin. */ | |
| 2045 *pcmap = pixcmapCreate(outdepth); | |
| 2046 for (i = 0; i < nbins; i++) { | |
| 2047 if (bincount[i]) { | |
| 2048 val = binave[i] / bincount[i]; | |
| 2049 } else { /* no samples in the bin */ | |
| 2050 if (i < nbins - 1) | |
| 2051 val = (binstart[i] + binstart[i + 1]) / 2; | |
| 2052 else /* last bin */ | |
| 2053 val = (binstart[i] + 255) / 2; | |
| 2054 } | |
| 2055 pixcmapAddColor(*pcmap, val, val, val); | |
| 2056 } | |
| 2057 | |
| 2058 LEPT_FREE(bincount); | |
| 2059 LEPT_FREE(binave); | |
| 2060 LEPT_FREE(binstart); | |
| 2061 return 0; | |
| 2062 } | |
| 2063 | |
| 2064 | |
| 2065 /*--------------------------------------------------------------------* | |
| 2066 * Thresholding from 32 bpp rgb to 1 bpp * | |
| 2067 *--------------------------------------------------------------------*/ | |
| 2068 /*! | |
| 2069 * \brief pixGenerateMaskByBand32() | |
| 2070 * | |
| 2071 * \param[in] pixs 32 bpp | |
| 2072 * \param[in] refval reference rgb value | |
| 2073 * \param[in] delm max amount below the ref value for any component | |
| 2074 * \param[in] delp max amount above the ref value for any component | |
| 2075 * \param[in] fractm fractional amount below ref value for all components | |
| 2076 * \param[in] fractp fractional amount above ref value for all components | |
| 2077 * \return pixd 1 bpp, or NULL on error | |
| 2078 * | |
| 2079 * <pre> | |
| 2080 * Notes: | |
| 2081 * (1) Generates a 1 bpp mask pixd, the same size as pixs, where | |
| 2082 * the fg pixels in the mask within a band of rgb values | |
| 2083 * surrounding %refval. The band can be chosen in two ways | |
| 2084 * for each component: | |
| 2085 * (a) Use (%delm, %delp) to specify how many levels down and up | |
| 2086 * (b) Use (%fractm, %fractp) to specify the fractional | |
| 2087 * distance toward 0 and 255, respectively. | |
| 2088 * Note that %delm and %delp must be in [0 ... 255], whereas | |
| 2089 * %fractm and %fractp must be in [0.0 - 1.0]. | |
| 2090 * (2) Either (%delm, %delp) or (%fractm, %fractp) can be used. | |
| 2091 * Set each value in the other pair to 0. | |
| 2092 * </pre> | |
| 2093 */ | |
| 2094 PIX * | |
| 2095 pixGenerateMaskByBand32(PIX *pixs, | |
| 2096 l_uint32 refval, | |
| 2097 l_int32 delm, | |
| 2098 l_int32 delp, | |
| 2099 l_float32 fractm, | |
| 2100 l_float32 fractp) | |
| 2101 { | |
| 2102 l_int32 i, j, w, h, d, wpls, wpld; | |
| 2103 l_int32 rref, gref, bref, rval, gval, bval; | |
| 2104 l_int32 rmin, gmin, bmin, rmax, gmax, bmax; | |
| 2105 l_uint32 pixel; | |
| 2106 l_uint32 *datas, *datad, *lines, *lined; | |
| 2107 PIX *pixd; | |
| 2108 | |
| 2109 if (!pixs) | |
| 2110 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2111 pixGetDimensions(pixs, &w, &h, &d); | |
| 2112 if (d != 32) | |
| 2113 return (PIX *)ERROR_PTR("not 32 bpp", __func__, NULL); | |
| 2114 if (delm < 0 || delp < 0) | |
| 2115 return (PIX *)ERROR_PTR("delm and delp must be >= 0", __func__, NULL); | |
| 2116 if (fractm < 0.0 || fractm > 1.0 || fractp < 0.0 || fractp > 1.0) | |
| 2117 return (PIX *)ERROR_PTR("fractm and/or fractp invalid", __func__, NULL); | |
| 2118 | |
| 2119 extractRGBValues(refval, &rref, &gref, &bref); | |
| 2120 if (fractm == 0.0 && fractp == 0.0) { | |
| 2121 rmin = rref - delm; | |
| 2122 gmin = gref - delm; | |
| 2123 bmin = bref - delm; | |
| 2124 rmax = rref + delm; | |
| 2125 gmax = gref + delm; | |
| 2126 bmax = bref + delm; | |
| 2127 } else if (delm == 0 && delp == 0) { | |
| 2128 rmin = (l_int32)((1.0 - fractm) * rref); | |
| 2129 gmin = (l_int32)((1.0 - fractm) * gref); | |
| 2130 bmin = (l_int32)((1.0 - fractm) * bref); | |
| 2131 rmax = rref + (l_int32)(fractp * (255 - rref)); | |
| 2132 gmax = gref + (l_int32)(fractp * (255 - gref)); | |
| 2133 bmax = bref + (l_int32)(fractp * (255 - bref)); | |
| 2134 } else { | |
| 2135 L_ERROR("bad input: either (delm, delp) or (fractm, fractp) " | |
| 2136 "must be 0\n", __func__); | |
| 2137 return NULL; | |
| 2138 } | |
| 2139 | |
| 2140 pixd = pixCreate(w, h, 1); | |
| 2141 pixCopyResolution(pixd, pixs); | |
| 2142 pixCopyInputFormat(pixd, pixs); | |
| 2143 datas = pixGetData(pixs); | |
| 2144 wpls = pixGetWpl(pixs); | |
| 2145 datad = pixGetData(pixd); | |
| 2146 wpld = pixGetWpl(pixd); | |
| 2147 for (i = 0; i < h; i++) { | |
| 2148 lines = datas + i * wpls; | |
| 2149 lined = datad + i * wpld; | |
| 2150 for (j = 0; j < w; j++) { | |
| 2151 pixel = lines[j]; | |
| 2152 rval = (pixel >> L_RED_SHIFT) & 0xff; | |
| 2153 if (rval < rmin || rval > rmax) | |
| 2154 continue; | |
| 2155 gval = (pixel >> L_GREEN_SHIFT) & 0xff; | |
| 2156 if (gval < gmin || gval > gmax) | |
| 2157 continue; | |
| 2158 bval = (pixel >> L_BLUE_SHIFT) & 0xff; | |
| 2159 if (bval < bmin || bval > bmax) | |
| 2160 continue; | |
| 2161 SET_DATA_BIT(lined, j); | |
| 2162 } | |
| 2163 } | |
| 2164 | |
| 2165 return pixd; | |
| 2166 } | |
| 2167 | |
| 2168 | |
| 2169 /*! | |
| 2170 * \brief pixGenerateMaskByDiscr32() | |
| 2171 * | |
| 2172 * \param[in] pixs 32 bpp | |
| 2173 * \param[in] refval1 reference rgb value | |
| 2174 * \param[in] refval2 reference rgb value | |
| 2175 * \param[in] distflag L_MANHATTAN_DISTANCE, L_EUCLIDEAN_DISTANCE | |
| 2176 * \return pixd 1 bpp, or NULL on error | |
| 2177 * | |
| 2178 * <pre> | |
| 2179 * Notes: | |
| 2180 * (1) Generates a 1 bpp mask pixd, the same size as pixs, where | |
| 2181 * the fg pixels in the mask are those where the pixel in pixs | |
| 2182 * is "closer" to refval1 than to refval2. | |
| 2183 * (2) "Closer" can be defined in several ways, such as: | |
| 2184 * ~ manhattan distance (L1) | |
| 2185 * ~ euclidean distance (L2) | |
| 2186 * ~ majority vote of the individual components | |
| 2187 * Here, we have a choice of L1 or L2. | |
| 2188 * </pre> | |
| 2189 */ | |
| 2190 PIX * | |
| 2191 pixGenerateMaskByDiscr32(PIX *pixs, | |
| 2192 l_uint32 refval1, | |
| 2193 l_uint32 refval2, | |
| 2194 l_int32 distflag) | |
| 2195 { | |
| 2196 l_int32 i, j, w, h, d, wpls, wpld; | |
| 2197 l_int32 rref1, gref1, bref1, rref2, gref2, bref2, rval, gval, bval; | |
| 2198 l_uint32 pixel, dist1, dist2; | |
| 2199 l_uint32 *datas, *datad, *lines, *lined; | |
| 2200 PIX *pixd; | |
| 2201 | |
| 2202 if (!pixs) | |
| 2203 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2204 pixGetDimensions(pixs, &w, &h, &d); | |
| 2205 if (d != 32) | |
| 2206 return (PIX *)ERROR_PTR("not 32 bpp", __func__, NULL); | |
| 2207 if (distflag != L_MANHATTAN_DISTANCE && distflag != L_EUCLIDEAN_DISTANCE) | |
| 2208 return (PIX *)ERROR_PTR("invalid distflag", __func__, NULL); | |
| 2209 | |
| 2210 extractRGBValues(refval1, &rref1, &gref1, &bref1); | |
| 2211 extractRGBValues(refval2, &rref2, &gref2, &bref2); | |
| 2212 pixd = pixCreate(w, h, 1); | |
| 2213 pixCopyResolution(pixd, pixs); | |
| 2214 pixCopyInputFormat(pixd, pixs); | |
| 2215 datas = pixGetData(pixs); | |
| 2216 wpls = pixGetWpl(pixs); | |
| 2217 datad = pixGetData(pixd); | |
| 2218 wpld = pixGetWpl(pixd); | |
| 2219 for (i = 0; i < h; i++) { | |
| 2220 lines = datas + i * wpls; | |
| 2221 lined = datad + i * wpld; | |
| 2222 for (j = 0; j < w; j++) { | |
| 2223 pixel = lines[j]; | |
| 2224 extractRGBValues(pixel, &rval, &gval, &bval); | |
| 2225 if (distflag == L_MANHATTAN_DISTANCE) { | |
| 2226 dist1 = L_ABS(rref1 - rval); | |
| 2227 dist2 = L_ABS(rref2 - rval); | |
| 2228 dist1 += L_ABS(gref1 - gval); | |
| 2229 dist2 += L_ABS(gref2 - gval); | |
| 2230 dist1 += L_ABS(bref1 - bval); | |
| 2231 dist2 += L_ABS(bref2 - bval); | |
| 2232 } else { | |
| 2233 dist1 = (rref1 - rval) * (rref1 - rval); | |
| 2234 dist2 = (rref2 - rval) * (rref2 - rval); | |
| 2235 dist1 += (gref1 - gval) * (gref1 - gval); | |
| 2236 dist2 += (gref2 - gval) * (gref2 - gval); | |
| 2237 dist1 += (bref1 - bval) * (bref1 - bval); | |
| 2238 dist2 += (bref2 - bval) * (bref2 - bval); | |
| 2239 } | |
| 2240 if (dist1 < dist2) | |
| 2241 SET_DATA_BIT(lined, j); | |
| 2242 } | |
| 2243 } | |
| 2244 | |
| 2245 return pixd; | |
| 2246 } | |
| 2247 | |
| 2248 | |
| 2249 /*----------------------------------------------------------------------* | |
| 2250 * Histogram-based grayscale quantization * | |
| 2251 *----------------------------------------------------------------------*/ | |
| 2252 /*! | |
| 2253 * \brief pixGrayQuantFromHisto() | |
| 2254 * | |
| 2255 * \param[in] pixd [optional] quantized pix with cmap; can be null | |
| 2256 * \param[in] pixs 8 bpp gray input pix; not cmapped | |
| 2257 * \param[in] pixm [optional] mask over pixels in pixs to quantize | |
| 2258 * \param[in] minfract minimum fraction of pixels in a set of adjacent | |
| 2259 * histo bins that causes the set to be automatically | |
| 2260 * set aside as a color in the colormap; must be | |
| 2261 * at least 0.01 | |
| 2262 * \param[in] maxsize maximum number of adjacent bins allowed to represent | |
| 2263 * a color, regardless of the population of pixels | |
| 2264 * in the bins; must be at least 2 | |
| 2265 * \return pixd 8 bpp, cmapped, or NULL on error | |
| 2266 * | |
| 2267 * <pre> | |
| 2268 * Notes: | |
| 2269 * (1) This is useful for quantizing images with relatively few | |
| 2270 * colors, but which may have both color and gray pixels. | |
| 2271 * If there are color pixels, it is assumed that an input | |
| 2272 * rgb image has been color quantized first so that: | |
| 2273 * ~ pixd has a colormap describing the color pixels | |
| 2274 * ~ pixm is a mask over the non-color pixels in pixd | |
| 2275 * ~ the colormap in pixd, and the color pixels in pixd, | |
| 2276 * have been repacked to go from 0 to n-1 (n colors) | |
| 2277 * If there are no color pixels, pixd and pixm are both null, | |
| 2278 * and all pixels in pixs are quantized to gray. | |
| 2279 * (2) A 256-entry histogram is built of the gray values in pixs. | |
| 2280 * If pixm exists, the pixels contributing to the histogram are | |
| 2281 * restricted to the fg of pixm. A colormap and LUT are generated | |
| 2282 * from this histogram. We break up the array into a set | |
| 2283 * of intervals, each one constituting a color in the colormap: | |
| 2284 * An interval is identified by summing histogram bins until | |
| 2285 * either the sum equals or exceeds the %minfract of the total | |
| 2286 * number of pixels, or the span itself equals or exceeds %maxsize. | |
| 2287 * The color of each bin is always an average of the pixels | |
| 2288 * that constitute it. | |
| 2289 * (3) Note that we do not specify the number of gray colors in | |
| 2290 * the colormap. Instead, we specify two parameters that | |
| 2291 * describe the accuracy of the color assignments; this and | |
| 2292 * the actual image determine the number of resulting colors. | |
| 2293 * (4) If a mask exists and it is not the same size as pixs, make | |
| 2294 * a new mask the same size as pixs, with the original mask | |
| 2295 * aligned at the UL corners. Set all additional pixels | |
| 2296 * in the (larger) new mask set to 1, causing those pixels | |
| 2297 * in pixd to be set as gray. | |
| 2298 * (5) We estimate the total number of colors (color plus gray); | |
| 2299 * if it exceeds 255, return null. | |
| 2300 * </pre> | |
| 2301 */ | |
| 2302 PIX * | |
| 2303 pixGrayQuantFromHisto(PIX *pixd, | |
| 2304 PIX *pixs, | |
| 2305 PIX *pixm, | |
| 2306 l_float32 minfract, | |
| 2307 l_int32 maxsize) | |
| 2308 { | |
| 2309 l_int32 w, h, wd, hd, wm, hm, wpls, wplm, wpld; | |
| 2310 l_int32 nc, nestim, i, j, vals, vald; | |
| 2311 l_int32 *lut; | |
| 2312 l_uint32 *datas, *datam, *datad, *lines, *linem, *lined; | |
| 2313 NUMA *na; | |
| 2314 PIX *pixmr = NULL; /* resized mask */ | |
| 2315 PIXCMAP *cmap; | |
| 2316 | |
| 2317 if (!pixs || pixGetDepth(pixs) != 8) | |
| 2318 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, NULL); | |
| 2319 if (minfract < 0.01) { | |
| 2320 L_WARNING("minfract < 0.01; setting to 0.05\n", __func__); | |
| 2321 minfract = 0.05f; | |
| 2322 } | |
| 2323 if (maxsize < 2) { | |
| 2324 L_WARNING("maxsize < 2; setting to 10\n", __func__); | |
| 2325 maxsize = 10; | |
| 2326 } | |
| 2327 if ((pixd && !pixm) || (!pixd && pixm)) | |
| 2328 return (PIX *)ERROR_PTR("(pixd,pixm) not defined together", | |
| 2329 __func__, NULL); | |
| 2330 pixGetDimensions(pixs, &w, &h, NULL); | |
| 2331 if (pixd) { | |
| 2332 if (pixGetDepth(pixm) != 1) | |
| 2333 return (PIX *)ERROR_PTR("pixm not 1 bpp", __func__, NULL); | |
| 2334 if ((cmap = pixGetColormap(pixd)) == NULL) | |
| 2335 return (PIX *)ERROR_PTR("pixd not cmapped", __func__, NULL); | |
| 2336 pixGetDimensions(pixd, &wd, &hd, NULL); | |
| 2337 if (w != wd || h != hd) | |
| 2338 return (PIX *)ERROR_PTR("pixs, pixd sizes differ", __func__, NULL); | |
| 2339 nc = pixcmapGetCount(cmap); | |
| 2340 nestim = nc + (l_int32)(1.5 * 255 / maxsize); | |
| 2341 lept_stderr( "nestim = %d\n", nestim); | |
| 2342 if (nestim > 255) { | |
| 2343 L_ERROR("Estimate %d colors!\n", __func__, nestim); | |
| 2344 return (PIX *)ERROR_PTR("probably too many colors", __func__, NULL); | |
| 2345 } | |
| 2346 pixGetDimensions(pixm, &wm, &hm, NULL); | |
| 2347 if (w != wm || h != hm) { /* resize the mask */ | |
| 2348 L_WARNING("mask and dest sizes not equal\n", __func__); | |
| 2349 pixmr = pixCreate(w, h, 1); | |
| 2350 pixRasterop(pixmr, 0, 0, wm, hm, PIX_SRC, pixm, 0, 0); | |
| 2351 pixRasterop(pixmr, wm, 0, w - wm, h, PIX_SET, NULL, 0, 0); | |
| 2352 pixRasterop(pixmr, 0, hm, wm, h - hm, PIX_SET, NULL, 0, 0); | |
| 2353 } else { | |
| 2354 pixmr = pixClone(pixm); | |
| 2355 } | |
| 2356 } else { | |
| 2357 pixd = pixCreateTemplate(pixs); | |
| 2358 cmap = pixcmapCreate(8); | |
| 2359 pixSetColormap(pixd, cmap); | |
| 2360 } | |
| 2361 pixCopyResolution(pixd, pixs); | |
| 2362 pixCopyInputFormat(pixd, pixs); | |
| 2363 | |
| 2364 /* Use original mask, if it exists, to select gray pixels */ | |
| 2365 na = pixGetGrayHistogramMasked(pixs, pixm, 0, 0, 1); | |
| 2366 | |
| 2367 /* Fill out the cmap with gray colors, and generate the lut | |
| 2368 * for pixel assignment. Issue a warning on failure. */ | |
| 2369 if (numaFillCmapFromHisto(na, cmap, minfract, maxsize, &lut)) | |
| 2370 L_ERROR("ran out of colors in cmap!\n", __func__); | |
| 2371 numaDestroy(&na); | |
| 2372 | |
| 2373 /* Assign the gray pixels to their cmap indices */ | |
| 2374 datas = pixGetData(pixs); | |
| 2375 datad = pixGetData(pixd); | |
| 2376 wpls = pixGetWpl(pixs); | |
| 2377 wpld = pixGetWpl(pixd); | |
| 2378 if (!pixm) { | |
| 2379 for (i = 0; i < h; i++) { | |
| 2380 lines = datas + i * wpls; | |
| 2381 lined = datad + i * wpld; | |
| 2382 for (j = 0; j < w; j++) { | |
| 2383 vals = GET_DATA_BYTE(lines, j); | |
| 2384 vald = lut[vals]; | |
| 2385 SET_DATA_BYTE(lined, j, vald); | |
| 2386 } | |
| 2387 } | |
| 2388 LEPT_FREE(lut); | |
| 2389 return pixd; | |
| 2390 } | |
| 2391 | |
| 2392 datam = pixGetData(pixmr); | |
| 2393 wplm = pixGetWpl(pixmr); | |
| 2394 for (i = 0; i < h; i++) { | |
| 2395 lines = datas + i * wpls; | |
| 2396 linem = datam + i * wplm; | |
| 2397 lined = datad + i * wpld; | |
| 2398 for (j = 0; j < w; j++) { | |
| 2399 if (!GET_DATA_BIT(linem, j)) | |
| 2400 continue; | |
| 2401 vals = GET_DATA_BYTE(lines, j); | |
| 2402 vald = lut[vals]; | |
| 2403 SET_DATA_BYTE(lined, j, vald); | |
| 2404 } | |
| 2405 } | |
| 2406 pixDestroy(&pixmr); | |
| 2407 LEPT_FREE(lut); | |
| 2408 return pixd; | |
| 2409 } | |
| 2410 | |
| 2411 | |
| 2412 /*! | |
| 2413 * \brief numaFillCmapFromHisto() | |
| 2414 * | |
| 2415 * \param[in] na histogram of gray values | |
| 2416 * \param[in] cmap 8 bpp cmap, possibly initialized with color value | |
| 2417 * \param[in] minfract minimum fraction of pixels in a set of adjacent | |
| 2418 * histo bins that causes the set to be automatically | |
| 2419 * set aside as a color in the colormap; must be | |
| 2420 * at least 0.01 | |
| 2421 * \param[in] maxsize maximum number of adjacent bins allowed to represent | |
| 2422 * a color, regardless of the population of pixels | |
| 2423 * in the bins; must be at least 2 | |
| 2424 * \param[out] plut lookup table from gray value to colormap index | |
| 2425 * \return 0 if OK, 1 on error | |
| 2426 * | |
| 2427 * <pre> | |
| 2428 * Notes: | |
| 2429 * (1) This static function must be called from pixGrayQuantFromHisto() | |
| 2430 * </pre> | |
| 2431 */ | |
| 2432 static l_int32 | |
| 2433 numaFillCmapFromHisto(NUMA *na, | |
| 2434 PIXCMAP *cmap, | |
| 2435 l_float32 minfract, | |
| 2436 l_int32 maxsize, | |
| 2437 l_int32 **plut) | |
| 2438 { | |
| 2439 l_int32 mincount, index, sum, wtsum, span, istart, i, val, ret; | |
| 2440 l_int32 *iahisto, *lut; | |
| 2441 l_float32 total; | |
| 2442 | |
| 2443 if (!plut) | |
| 2444 return ERROR_INT("&lut not defined", __func__, 1); | |
| 2445 *plut = NULL; | |
| 2446 if (!na) | |
| 2447 return ERROR_INT("na not defined", __func__, 1); | |
| 2448 if (!cmap) | |
| 2449 return ERROR_INT("cmap not defined", __func__, 1); | |
| 2450 | |
| 2451 numaGetSum(na, &total); | |
| 2452 mincount = (l_int32)(minfract * total); | |
| 2453 iahisto = numaGetIArray(na); | |
| 2454 lut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); | |
| 2455 *plut = lut; | |
| 2456 index = pixcmapGetCount(cmap); /* start with number of colors | |
| 2457 * already reserved */ | |
| 2458 | |
| 2459 /* March through, associating colors with sets of adjacent | |
| 2460 * gray levels. During the process, the LUT that gives | |
| 2461 * the colormap index for each gray level is computed. | |
| 2462 * To complete a color, either the total count must equal | |
| 2463 * or exceed %mincount, or the current span of colors must | |
| 2464 * equal or exceed %maxsize. An empty span is not converted | |
| 2465 * into a color; it is simply ignored. When a span is completed for a | |
| 2466 * color, the weighted color in the span is added to the colormap. */ | |
| 2467 sum = 0; | |
| 2468 wtsum = 0; | |
| 2469 istart = 0; | |
| 2470 ret = 0; | |
| 2471 for (i = 0; i < 256; i++) { | |
| 2472 lut[i] = index; | |
| 2473 sum += iahisto[i]; | |
| 2474 wtsum += i * iahisto[i]; | |
| 2475 span = i - istart + 1; | |
| 2476 if (sum < mincount && span < maxsize) | |
| 2477 continue; | |
| 2478 | |
| 2479 if (sum == 0) { /* empty span; don't save */ | |
| 2480 istart = i + 1; | |
| 2481 continue; | |
| 2482 } | |
| 2483 | |
| 2484 /* Found new color; sum > 0 */ | |
| 2485 val = (l_int32)((l_float32)wtsum / (l_float32)sum + 0.5); | |
| 2486 ret = pixcmapAddColor(cmap, val, val, val); | |
| 2487 istart = i + 1; | |
| 2488 sum = 0; | |
| 2489 wtsum = 0; | |
| 2490 index++; | |
| 2491 } | |
| 2492 if (istart < 256 && sum > 0) { /* last one */ | |
| 2493 span = 256 - istart; | |
| 2494 val = (l_int32)((l_float32)wtsum / (l_float32)sum + 0.5); | |
| 2495 ret = pixcmapAddColor(cmap, val, val, val); | |
| 2496 } | |
| 2497 | |
| 2498 LEPT_FREE(iahisto); | |
| 2499 return ret; | |
| 2500 } | |
| 2501 | |
| 2502 | |
| 2503 /*----------------------------------------------------------------------* | |
| 2504 * Color quantize grayscale image using existing colormap * | |
| 2505 *----------------------------------------------------------------------*/ | |
| 2506 /*! | |
| 2507 * \brief pixGrayQuantFromCmap() | |
| 2508 * | |
| 2509 * \param[in] pixs 8 bpp grayscale without cmap | |
| 2510 * \param[in] cmap to quantize to; of dest pix | |
| 2511 * \param[in] mindepth minimum depth of pixd: can be 2, 4 or 8 bpp | |
| 2512 * \return pixd 2, 4 or 8 bpp, colormapped, or NULL on error | |
| 2513 * | |
| 2514 * <pre> | |
| 2515 * Notes: | |
| 2516 * (1) In use, pixs is an 8 bpp grayscale image without a colormap. | |
| 2517 * If there is an existing colormap, a warning is issued and | |
| 2518 * a copy of the input pixs is returned. | |
| 2519 * </pre> | |
| 2520 */ | |
| 2521 PIX * | |
| 2522 pixGrayQuantFromCmap(PIX *pixs, | |
| 2523 PIXCMAP *cmap, | |
| 2524 l_int32 mindepth) | |
| 2525 { | |
| 2526 l_int32 i, j, index, w, h, d, depth, wpls, wpld; | |
| 2527 l_int32 hascolor, vals, vald; | |
| 2528 l_int32 *tab; | |
| 2529 l_uint32 *datas, *datad, *lines, *lined; | |
| 2530 PIXCMAP *cmapd; | |
| 2531 PIX *pixd; | |
| 2532 | |
| 2533 if (!pixs) | |
| 2534 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2535 if (pixGetColormap(pixs) != NULL) { | |
| 2536 L_WARNING("pixs already has a colormap; returning a copy\n", __func__); | |
| 2537 return pixCopy(NULL, pixs); | |
| 2538 } | |
| 2539 pixGetDimensions(pixs, &w, &h, &d); | |
| 2540 if (d != 8) | |
| 2541 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); | |
| 2542 if (!cmap) | |
| 2543 return (PIX *)ERROR_PTR("cmap not defined", __func__, NULL); | |
| 2544 if (mindepth != 2 && mindepth != 4 && mindepth != 8) | |
| 2545 return (PIX *)ERROR_PTR("invalid mindepth", __func__, NULL); | |
| 2546 | |
| 2547 /* Make sure the colormap is gray */ | |
| 2548 pixcmapHasColor(cmap, &hascolor); | |
| 2549 if (hascolor) { | |
| 2550 L_WARNING("Converting colormap colors to gray\n", __func__); | |
| 2551 cmapd = pixcmapColorToGray(cmap, 0.3f, 0.5f, 0.2f); | |
| 2552 } else { | |
| 2553 cmapd = pixcmapCopy(cmap); | |
| 2554 } | |
| 2555 | |
| 2556 /* Make LUT into colormap */ | |
| 2557 tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); | |
| 2558 for (i = 0; i < 256; i++) { | |
| 2559 pixcmapGetNearestGrayIndex(cmapd, i, &index); | |
| 2560 tab[i] = index; | |
| 2561 } | |
| 2562 | |
| 2563 pixcmapGetMinDepth(cmap, &depth); | |
| 2564 depth = L_MAX(depth, mindepth); | |
| 2565 pixd = pixCreate(w, h, depth); | |
| 2566 pixSetColormap(pixd, cmapd); | |
| 2567 pixCopyResolution(pixd, pixs); | |
| 2568 pixCopyInputFormat(pixd, pixs); | |
| 2569 datas = pixGetData(pixs); | |
| 2570 datad = pixGetData(pixd); | |
| 2571 wpls = pixGetWpl(pixs); | |
| 2572 wpld = pixGetWpl(pixd); | |
| 2573 for (i = 0; i < h; i++) { | |
| 2574 lines = datas + i * wpls; | |
| 2575 lined = datad + i * wpld; | |
| 2576 for (j = 0; j < w; j++) { | |
| 2577 vals = GET_DATA_BYTE(lines, j); | |
| 2578 vald = tab[vals]; | |
| 2579 if (depth == 2) | |
| 2580 SET_DATA_DIBIT(lined, j, vald); | |
| 2581 else if (depth == 4) | |
| 2582 SET_DATA_QBIT(lined, j, vald); | |
| 2583 else /* depth == 8 */ | |
| 2584 SET_DATA_BYTE(lined, j, vald); | |
| 2585 } | |
| 2586 } | |
| 2587 | |
| 2588 LEPT_FREE(tab); | |
| 2589 return pixd; | |
| 2590 } | |
| 2591 | |
| 2592 | |
| 2593 #if 0 /* Documentation */ | |
| 2594 /*--------------------------------------------------------------------* | |
| 2595 * Implementation of binarization by dithering using LUTs * | |
| 2596 * It is archived here. * | |
| 2597 *--------------------------------------------------------------------*/ | |
| 2598 /*! | |
| 2599 * \brief pixDitherToBinaryLUT() | |
| 2600 * | |
| 2601 * \param[in] pixs | |
| 2602 * \param[in] lowerclip lower clip distance to black; use -1 for default | |
| 2603 * \param[in] upperclip upper clip distance to white; use -1 for default | |
| 2604 * \return pixd dithered binary, or NULL on error | |
| 2605 * | |
| 2606 * We don't need two implementations of Floyd-Steinberg dithering, | |
| 2607 * and this one with LUTs is a little more complicated than | |
| 2608 * pixDitherToBinary(). It uses three lookup tables to generate the | |
| 2609 * output pixel value and the excess or deficit carried over to the | |
| 2610 * neighboring pixels. It's here for pedagogical reasons only. | |
| 2611 */ | |
| 2612 PIX * | |
| 2613 pixDitherToBinaryLUT(PIX *pixs, | |
| 2614 l_int32 lowerclip, | |
| 2615 l_int32 upperclip) | |
| 2616 { | |
| 2617 l_int32 w, h, d, wplt, wpld; | |
| 2618 l_int32 *tabval, *tab38, *tab14; | |
| 2619 l_uint32 *datat, *datad; | |
| 2620 l_uint32 *bufs1, *bufs2; | |
| 2621 PIX *pixt, *pixd; | |
| 2622 | |
| 2623 if (!pixs) | |
| 2624 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2625 pixGetDimensions(pixs, &w, &h, &d); | |
| 2626 if (d != 8) | |
| 2627 return (PIX *)ERROR_PTR("must be 8 bpp for dithering", __func__, NULL); | |
| 2628 if (lowerclip < 0) | |
| 2629 lowerclip = DEFAULT_CLIP_LOWER_1; | |
| 2630 if (upperclip < 0) | |
| 2631 upperclip = DEFAULT_CLIP_UPPER_1; | |
| 2632 | |
| 2633 if ((pixd = pixCreate(w, h, 1)) == NULL) | |
| 2634 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 2635 pixCopyResolution(pixd, pixs); | |
| 2636 pixCopyInputFormat(pixd, pixs); | |
| 2637 datad = pixGetData(pixd); | |
| 2638 wpld = pixGetWpl(pixd); | |
| 2639 | |
| 2640 /* Remove colormap if it exists */ | |
| 2641 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 2642 datat = pixGetData(pixt); | |
| 2643 wplt = pixGetWpl(pixt); | |
| 2644 | |
| 2645 /* Two line buffers, 1 for current line and 2 for next line */ | |
| 2646 bufs1 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); | |
| 2647 bufs2 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); | |
| 2648 if (!bufs1 || !bufs2) { | |
| 2649 LEPT_FREE(bufs1); | |
| 2650 LEPT_FREE(bufs2); | |
| 2651 pixDestroy(&pixd); | |
| 2652 pixDestroy(&pixt); | |
| 2653 return (PIX *)ERROR_PTR("bufs1, bufs2 not both made", __func__, NULL); | |
| 2654 } | |
| 2655 | |
| 2656 /* 3 lookup tables: 1-bit value, (3/8)excess, and (1/4)excess */ | |
| 2657 make8To1DitherTables(&tabval, &tab38, &tab14, lowerclip, upperclip); | |
| 2658 | |
| 2659 ditherToBinaryLUTLow(datad, w, h, wpld, datat, wplt, bufs1, bufs2, | |
| 2660 tabval, tab38, tab14); | |
| 2661 | |
| 2662 LEPT_FREE(bufs1); | |
| 2663 LEPT_FREE(bufs2); | |
| 2664 LEPT_FREE(tabval); | |
| 2665 LEPT_FREE(tab38); | |
| 2666 LEPT_FREE(tab14); | |
| 2667 pixDestroy(&pixt); | |
| 2668 return pixd; | |
| 2669 } | |
| 2670 | |
| 2671 /*! | |
| 2672 * \brief ditherToBinaryLUTLow() | |
| 2673 * | |
| 2674 * Low-level function for doing Floyd-Steinberg error diffusion | |
| 2675 * dithering from 8 bpp (datas) to 1 bpp (datad). Two source | |
| 2676 * line buffers, bufs1 and bufs2, are provided, along with three | |
| 2677 * 256-entry lookup tables: tabval gives the output pixel value, | |
| 2678 * tab38 gives the extra (plus or minus) transferred to the pixels | |
| 2679 * directly to the left and below, and tab14 gives the extra | |
| 2680 * transferred to the diagonal below. The choice of 3/8 and 1/4 | |
| 2681 * is traditional but arbitrary when you use a lookup table; the | |
| 2682 * only constraint is that the sum is 1. See other comments below. | |
| 2683 */ | |
| 2684 void | |
| 2685 ditherToBinaryLUTLow(l_uint32 *datad, | |
| 2686 l_int32 w, | |
| 2687 l_int32 h, | |
| 2688 l_int32 wpld, | |
| 2689 l_uint32 *datas, | |
| 2690 l_int32 wpls, | |
| 2691 l_uint32 *bufs1, | |
| 2692 l_uint32 *bufs2, | |
| 2693 l_int32 *tabval, | |
| 2694 l_int32 *tab38, | |
| 2695 l_int32 *tab14) | |
| 2696 { | |
| 2697 l_int32 i; | |
| 2698 l_uint32 *lined; | |
| 2699 | |
| 2700 /* do all lines except last line */ | |
| 2701 memcpy(bufs2, datas, 4 * wpls); /* prime the buffer */ | |
| 2702 for (i = 0; i < h - 1; i++) { | |
| 2703 memcpy(bufs1, bufs2, 4 * wpls); | |
| 2704 memcpy(bufs2, datas + (i + 1) * wpls, 4 * wpls); | |
| 2705 lined = datad + i * wpld; | |
| 2706 ditherToBinaryLineLUTLow(lined, w, bufs1, bufs2, | |
| 2707 tabval, tab38, tab14, 0); | |
| 2708 } | |
| 2709 | |
| 2710 /* do last line */ | |
| 2711 memcpy(bufs1, bufs2, 4 * wpls); | |
| 2712 lined = datad + (h - 1) * wpld; | |
| 2713 ditherToBinaryLineLUTLow(lined, w, bufs1, bufs2, tabval, tab38, tab14, 1); | |
| 2714 return; | |
| 2715 } | |
| 2716 | |
| 2717 /*! | |
| 2718 * \brief ditherToBinaryLineLUTLow() | |
| 2719 * | |
| 2720 * \param[in] lined ptr to beginning of dest line | |
| 2721 * \param[in] w width of image in pixels | |
| 2722 * \param[in] bufs1 buffer of current source line | |
| 2723 * \param[in] bufs2 buffer of next source line | |
| 2724 * \param[in] tabval value to assign for current pixel | |
| 2725 * \param[in] tab38 excess value to give to neighboring 3/8 pixels | |
| 2726 * \param[in] tab14 excess value to give to neighboring 1/4 pixel | |
| 2727 * \param[in] lastlineflag 0 if not last dest line, 1 if last dest line | |
| 2728 * \return void | |
| 2729 */ | |
| 2730 void | |
| 2731 ditherToBinaryLineLUTLow(l_uint32 *lined, | |
| 2732 l_int32 w, | |
| 2733 l_uint32 *bufs1, | |
| 2734 l_uint32 *bufs2, | |
| 2735 l_int32 *tabval, | |
| 2736 l_int32 *tab38, | |
| 2737 l_int32 *tab14, | |
| 2738 l_int32 lastlineflag) | |
| 2739 { | |
| 2740 l_int32 j; | |
| 2741 l_int32 oval, tab38val, tab14val; | |
| 2742 l_uint8 rval, bval, dval; | |
| 2743 | |
| 2744 if (lastlineflag == 0) { | |
| 2745 for (j = 0; j < w - 1; j++) { | |
| 2746 oval = GET_DATA_BYTE(bufs1, j); | |
| 2747 if (tabval[oval]) | |
| 2748 SET_DATA_BIT(lined, j); | |
| 2749 rval = GET_DATA_BYTE(bufs1, j + 1); | |
| 2750 bval = GET_DATA_BYTE(bufs2, j); | |
| 2751 dval = GET_DATA_BYTE(bufs2, j + 1); | |
| 2752 tab38val = tab38[oval]; | |
| 2753 if (tab38val == 0) | |
| 2754 continue; | |
| 2755 tab14val = tab14[oval]; | |
| 2756 if (tab38val < 0) { | |
| 2757 rval = L_MAX(0, rval + tab38val); | |
| 2758 bval = L_MAX(0, bval + tab38val); | |
| 2759 dval = L_MAX(0, dval + tab14val); | |
| 2760 } else { | |
| 2761 rval = L_MIN(255, rval + tab38val); | |
| 2762 bval = L_MIN(255, bval + tab38val); | |
| 2763 dval = L_MIN(255, dval + tab14val); | |
| 2764 } | |
| 2765 SET_DATA_BYTE(bufs1, j + 1, rval); | |
| 2766 SET_DATA_BYTE(bufs2, j, bval); | |
| 2767 SET_DATA_BYTE(bufs2, j + 1, dval); | |
| 2768 } | |
| 2769 | |
| 2770 /* do last column: j = w - 1 */ | |
| 2771 oval = GET_DATA_BYTE(bufs1, j); | |
| 2772 if (tabval[oval]) | |
| 2773 SET_DATA_BIT(lined, j); | |
| 2774 bval = GET_DATA_BYTE(bufs2, j); | |
| 2775 tab38val = tab38[oval]; | |
| 2776 if (tab38val < 0) { | |
| 2777 bval = L_MAX(0, bval + tab38val); | |
| 2778 SET_DATA_BYTE(bufs2, j, bval); | |
| 2779 } else if (tab38val > 0 ) { | |
| 2780 bval = L_MIN(255, bval + tab38val); | |
| 2781 SET_DATA_BYTE(bufs2, j, bval); | |
| 2782 } | |
| 2783 } else { /* lastlineflag == 1 */ | |
| 2784 for (j = 0; j < w - 1; j++) { | |
| 2785 oval = GET_DATA_BYTE(bufs1, j); | |
| 2786 if (tabval[oval]) | |
| 2787 SET_DATA_BIT(lined, j); | |
| 2788 rval = GET_DATA_BYTE(bufs1, j + 1); | |
| 2789 tab38val = tab38[oval]; | |
| 2790 if (tab38val == 0) | |
| 2791 continue; | |
| 2792 if (tab38val < 0) | |
| 2793 rval = L_MAX(0, rval + tab38val); | |
| 2794 else | |
| 2795 rval = L_MIN(255, rval + tab38val); | |
| 2796 SET_DATA_BYTE(bufs1, j + 1, rval); | |
| 2797 } | |
| 2798 | |
| 2799 /* do last pixel: (i, j) = (h - 1, w - 1) */ | |
| 2800 oval = GET_DATA_BYTE(bufs1, j); | |
| 2801 if (tabval[oval]) | |
| 2802 SET_DATA_BIT(lined, j); | |
| 2803 } | |
| 2804 | |
| 2805 return; | |
| 2806 } | |
| 2807 | |
| 2808 /*! | |
| 2809 * \brief make8To1DitherTables() | |
| 2810 * | |
| 2811 * \param[out] ptabval value assigned to output pixel; 0 or 1 | |
| 2812 * \param[out] ptab38 amount propagated to pixels left and below | |
| 2813 * \param[out] ptab14 amount propagated to pixel to left and down | |
| 2814 * \param[in] lowerclip values near 0 where the excess is not propagated | |
| 2815 * \param[in] upperclip values near 255 where the deficit is not propagated | |
| 2816 * | |
| 2817 * \return 0 if OK, 1 on error | |
| 2818 */ | |
| 2819 l_ok | |
| 2820 make8To1DitherTables(l_int32 **ptabval, | |
| 2821 l_int32 **ptab38, | |
| 2822 l_int32 **ptab14, | |
| 2823 l_int32 lowerclip, | |
| 2824 l_int32 upperclip) | |
| 2825 { | |
| 2826 l_int32 i; | |
| 2827 l_int32 *tabval, *tab38, *tab14; | |
| 2828 | |
| 2829 if (ptabval) *ptabval = NULL; | |
| 2830 if (ptab38) *ptab38 = NULL; | |
| 2831 if (ptab14) *ptab14 = NULL; | |
| 2832 if (!ptabval || !ptab38 || !ptab14) | |
| 2833 return ERROR_INT("table ptrs not all defined", __func__, 1); | |
| 2834 | |
| 2835 /* 3 lookup tables: 1-bit value, (3/8)excess, and (1/4)excess */ | |
| 2836 tabval = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); | |
| 2837 tab38 = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); | |
| 2838 tab14 = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); | |
| 2839 if (!tabval || !tab38 || !tab14) | |
| 2840 return ERROR_INT("calloc failure to make small table", __func__, 1); | |
| 2841 *ptabval = tabval; | |
| 2842 *ptab38 = tab38; | |
| 2843 *ptab14 = tab14; | |
| 2844 | |
| 2845 for (i = 0; i < 256; i++) { | |
| 2846 if (i <= lowerclip) { | |
| 2847 tabval[i] = 1; | |
| 2848 tab38[i] = 0; | |
| 2849 tab14[i] = 0; | |
| 2850 } else if (i < 128) { | |
| 2851 tabval[i] = 1; | |
| 2852 tab38[i] = (3 * i + 4) / 8; | |
| 2853 tab14[i] = (i + 2) / 4; | |
| 2854 } else if (i < 255 - upperclip) { | |
| 2855 tabval[i] = 0; | |
| 2856 tab38[i] = (3 * (i - 255) + 4) / 8; | |
| 2857 tab14[i] = ((i - 255) + 2) / 4; | |
| 2858 } else { /* i >= 255 - upperclip */ | |
| 2859 tabval[i] = 0; | |
| 2860 tab38[i] = 0; | |
| 2861 tab14[i] = 0; | |
| 2862 } | |
| 2863 } | |
| 2864 | |
| 2865 return 0; | |
| 2866 } | |
| 2867 #endif /* Documentation */ |
