/src/leptonica/src/grayquant.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | { |
177 | 0 | if (!pixs) |
178 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
179 | 0 | if (pixGetDepth(pixs) != 8) |
180 | 0 | return (PIX *)ERROR_PTR("must be 8 bpp for dithering", __func__, NULL); |
181 | | |
182 | 0 | return pixDitherToBinarySpec(pixs, DEFAULT_CLIP_LOWER_1, |
183 | 0 | DEFAULT_CLIP_UPPER_1); |
184 | 0 | } |
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 | 0 | { |
209 | 0 | l_int32 w, h, d, wplt, wpld; |
210 | 0 | l_uint32 *datat, *datad; |
211 | 0 | l_uint32 *bufs1, *bufs2; |
212 | 0 | PIX *pixt, *pixd; |
213 | |
|
214 | 0 | if (!pixs) |
215 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
216 | 0 | pixGetDimensions(pixs, &w, &h, &d); |
217 | 0 | if (d != 8) |
218 | 0 | return (PIX *)ERROR_PTR("must be 8 bpp for dithering", __func__, NULL); |
219 | 0 | if (lowerclip < 0 || lowerclip > 255) |
220 | 0 | return (PIX *)ERROR_PTR("invalid value for lowerclip", __func__, NULL); |
221 | 0 | if (upperclip < 0 || upperclip > 255) |
222 | 0 | return (PIX *)ERROR_PTR("invalid value for upperclip", __func__, NULL); |
223 | | |
224 | 0 | if ((pixd = pixCreate(w, h, 1)) == NULL) |
225 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
226 | 0 | pixCopyResolution(pixd, pixs); |
227 | 0 | pixCopyInputFormat(pixd, pixs); |
228 | 0 | datad = pixGetData(pixd); |
229 | 0 | wpld = pixGetWpl(pixd); |
230 | | |
231 | | /* Remove colormap if it exists */ |
232 | 0 | if ((pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE)) == NULL) { |
233 | 0 | pixDestroy(&pixd); |
234 | 0 | return (PIX *)ERROR_PTR("pixt not made", __func__, NULL); |
235 | 0 | } |
236 | 0 | datat = pixGetData(pixt); |
237 | 0 | wplt = pixGetWpl(pixt); |
238 | | |
239 | | /* Two line buffers, 1 for current line and 2 for next line */ |
240 | 0 | bufs1 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); |
241 | 0 | bufs2 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); |
242 | 0 | if (!bufs1 || !bufs2) { |
243 | 0 | LEPT_FREE(bufs1); |
244 | 0 | LEPT_FREE(bufs2); |
245 | 0 | pixDestroy(&pixd); |
246 | 0 | pixDestroy(&pixt); |
247 | 0 | return (PIX *)ERROR_PTR("bufs1, bufs2 not both made", __func__, NULL); |
248 | 0 | } |
249 | | |
250 | 0 | ditherToBinaryLow(datad, w, h, wpld, datat, wplt, bufs1, bufs2, |
251 | 0 | lowerclip, upperclip); |
252 | |
|
253 | 0 | LEPT_FREE(bufs1); |
254 | 0 | LEPT_FREE(bufs2); |
255 | 0 | pixDestroy(&pixt); |
256 | 0 | return pixd; |
257 | 0 | } |
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 | 0 | { |
277 | 0 | l_int32 i; |
278 | 0 | l_uint32 *lined; |
279 | | |
280 | | /* do all lines except last line */ |
281 | 0 | memcpy(bufs2, datas, 4 * wpls); /* prime the buffer */ |
282 | 0 | for (i = 0; i < h - 1; i++) { |
283 | 0 | memcpy(bufs1, bufs2, 4 * wpls); |
284 | 0 | memcpy(bufs2, datas + (i + 1) * wpls, 4 * wpls); |
285 | 0 | lined = datad + i * wpld; |
286 | 0 | ditherToBinaryLineLow(lined, w, bufs1, bufs2, lowerclip, upperclip, 0); |
287 | 0 | } |
288 | | |
289 | | /* do last line */ |
290 | 0 | memcpy(bufs1, bufs2, 4 * wpls); |
291 | 0 | lined = datad + (h - 1) * wpld; |
292 | 0 | ditherToBinaryLineLow(lined, w, bufs1, bufs2, lowerclip, upperclip, 1); |
293 | 0 | } |
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 | 0 | { |
330 | 0 | l_int32 j; |
331 | 0 | l_int32 oval, eval; |
332 | 0 | l_uint8 fval1, fval2, rval, bval, dval; |
333 | |
|
334 | 0 | if (lastlineflag == 0) { |
335 | 0 | for (j = 0; j < w - 1; j++) { |
336 | 0 | oval = GET_DATA_BYTE(bufs1, j); |
337 | 0 | if (oval > 127) { /* binarize to OFF */ |
338 | 0 | if ((eval = 255 - oval) > upperclip) { |
339 | | /* subtract from neighbors */ |
340 | 0 | fval1 = (3 * eval) / 8; |
341 | 0 | fval2 = eval / 4; |
342 | 0 | rval = GET_DATA_BYTE(bufs1, j + 1); |
343 | 0 | rval = L_MAX(0, rval - fval1); |
344 | 0 | SET_DATA_BYTE(bufs1, j + 1, rval); |
345 | 0 | bval = GET_DATA_BYTE(bufs2, j); |
346 | 0 | bval = L_MAX(0, bval - fval1); |
347 | 0 | SET_DATA_BYTE(bufs2, j, bval); |
348 | 0 | dval = GET_DATA_BYTE(bufs2, j + 1); |
349 | 0 | dval = L_MAX(0, dval - fval2); |
350 | 0 | SET_DATA_BYTE(bufs2, j + 1, dval); |
351 | 0 | } |
352 | 0 | } else { /* oval <= 127; binarize to ON */ |
353 | 0 | SET_DATA_BIT(lined, j); /* ON pixel */ |
354 | 0 | if (oval > lowerclip) { |
355 | | /* add to neighbors */ |
356 | 0 | fval1 = (3 * oval) / 8; |
357 | 0 | fval2 = oval / 4; |
358 | 0 | rval = GET_DATA_BYTE(bufs1, j + 1); |
359 | 0 | rval = L_MIN(255, rval + fval1); |
360 | 0 | SET_DATA_BYTE(bufs1, j + 1, rval); |
361 | 0 | bval = GET_DATA_BYTE(bufs2, j); |
362 | 0 | bval = L_MIN(255, bval + fval1); |
363 | 0 | SET_DATA_BYTE(bufs2, j, bval); |
364 | 0 | dval = GET_DATA_BYTE(bufs2, j + 1); |
365 | 0 | dval = L_MIN(255, dval + fval2); |
366 | 0 | SET_DATA_BYTE(bufs2, j + 1, dval); |
367 | 0 | } |
368 | 0 | } |
369 | 0 | } |
370 | | |
371 | | /* do last column: j = w - 1 */ |
372 | 0 | oval = GET_DATA_BYTE(bufs1, j); |
373 | 0 | if (oval > 127) { /* binarize to OFF */ |
374 | 0 | if ((eval = 255 - oval) > upperclip) { |
375 | | /* subtract from neighbors */ |
376 | 0 | fval1 = (3 * eval) / 8; |
377 | 0 | bval = GET_DATA_BYTE(bufs2, j); |
378 | 0 | bval = L_MAX(0, bval - fval1); |
379 | 0 | SET_DATA_BYTE(bufs2, j, bval); |
380 | 0 | } |
381 | 0 | } else { /*oval <= 127; binarize to ON */ |
382 | 0 | SET_DATA_BIT(lined, j); /* ON pixel */ |
383 | 0 | if (oval > lowerclip) { |
384 | | /* add to neighbors */ |
385 | 0 | fval1 = (3 * oval) / 8; |
386 | 0 | bval = GET_DATA_BYTE(bufs2, j); |
387 | 0 | bval = L_MIN(255, bval + fval1); |
388 | 0 | SET_DATA_BYTE(bufs2, j, bval); |
389 | 0 | } |
390 | 0 | } |
391 | 0 | } else { /* lastlineflag == 1 */ |
392 | 0 | for (j = 0; j < w - 1; j++) { |
393 | 0 | oval = GET_DATA_BYTE(bufs1, j); |
394 | 0 | if (oval > 127) { /* binarize to OFF */ |
395 | 0 | if ((eval = 255 - oval) > upperclip) { |
396 | | /* subtract from neighbors */ |
397 | 0 | fval1 = (3 * eval) / 8; |
398 | 0 | rval = GET_DATA_BYTE(bufs1, j + 1); |
399 | 0 | rval = L_MAX(0, rval - fval1); |
400 | 0 | SET_DATA_BYTE(bufs1, j + 1, rval); |
401 | 0 | } |
402 | 0 | } else { /* oval <= 127; binarize to ON */ |
403 | 0 | SET_DATA_BIT(lined, j); /* ON pixel */ |
404 | 0 | if (oval > lowerclip) { |
405 | | /* add to neighbors */ |
406 | 0 | fval1 = (3 * oval) / 8; |
407 | 0 | rval = GET_DATA_BYTE(bufs1, j + 1); |
408 | 0 | rval = L_MIN(255, rval + fval1); |
409 | 0 | SET_DATA_BYTE(bufs1, j + 1, rval); |
410 | 0 | } |
411 | 0 | } |
412 | 0 | } |
413 | | |
414 | | /* do last pixel: (i, j) = (h - 1, w - 1) */ |
415 | 0 | oval = GET_DATA_BYTE(bufs1, j); |
416 | 0 | if (oval < 128) |
417 | 0 | SET_DATA_BIT(lined, j); /* ON pixel */ |
418 | 0 | } |
419 | 0 | } |
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 | 0 | { |
446 | 0 | l_int32 d, w, h, wplt, wpld; |
447 | 0 | l_uint32 *datat, *datad; |
448 | 0 | PIX *pixt, *pixd; |
449 | |
|
450 | 0 | if (!pixs) |
451 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
452 | 0 | pixGetDimensions(pixs, &w, &h, &d); |
453 | 0 | if (d != 4 && d != 8) |
454 | 0 | return (PIX *)ERROR_PTR("pixs must be 4 or 8 bpp", __func__, NULL); |
455 | 0 | if (thresh < 0) |
456 | 0 | return (PIX *)ERROR_PTR("thresh must be non-negative", __func__, NULL); |
457 | 0 | if (d == 4 && thresh > 16) |
458 | 0 | return (PIX *)ERROR_PTR("4 bpp thresh not in {0-16}", __func__, NULL); |
459 | 0 | if (d == 8 && thresh > 256) |
460 | 0 | return (PIX *)ERROR_PTR("8 bpp thresh not in {0-256}", __func__, NULL); |
461 | | |
462 | 0 | if ((pixd = pixCreate(w, h, 1)) == NULL) |
463 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
464 | 0 | pixCopyResolution(pixd, pixs); |
465 | 0 | pixCopyInputFormat(pixd, pixs); |
466 | 0 | datad = pixGetData(pixd); |
467 | 0 | 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 | 0 | pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); |
472 | 0 | datat = pixGetData(pixt); |
473 | 0 | wplt = pixGetWpl(pixt); |
474 | 0 | if (pixGetColormap(pixs) && d == 4) { /* promoted to 8 bpp */ |
475 | 0 | d = 8; |
476 | 0 | thresh *= 16; |
477 | 0 | } |
478 | |
|
479 | 0 | thresholdToBinaryLow(datad, w, h, wpld, datat, d, wplt, thresh); |
480 | 0 | pixDestroy(&pixt); |
481 | 0 | return pixd; |
482 | 0 | } |
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 | 0 | { |
501 | 0 | l_int32 i; |
502 | 0 | l_uint32 *lines, *lined; |
503 | |
|
504 | 0 | for (i = 0; i < h; i++) { |
505 | 0 | lines = datas + i * wpls; |
506 | 0 | lined = datad + i * wpld; |
507 | 0 | thresholdToBinaryLineLow(lined, w, lines, d, thresh); |
508 | 0 | } |
509 | 0 | } |
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 | 0 | { |
523 | 0 | l_int32 j, k, gval, scount, dcount; |
524 | 0 | l_uint32 sword, dword; |
525 | |
|
526 | 0 | switch (d) |
527 | 0 | { |
528 | 0 | case 4: |
529 | | /* Unrolled as 4 source words, 1 dest word */ |
530 | 0 | for (j = 0, scount = 0, dcount = 0; j + 31 < w; j += 32) { |
531 | 0 | dword = 0; |
532 | 0 | for (k = 0; k < 4; k++) { |
533 | 0 | sword = lines[scount++]; |
534 | 0 | dword <<= 8; |
535 | 0 | 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 | 0 | dword |= ((gval - thresh) >> 24) & 128; |
543 | 0 | gval = (sword >> 24) & 0xf; |
544 | 0 | dword |= ((gval - thresh) >> 25) & 64; |
545 | 0 | gval = (sword >> 20) & 0xf; |
546 | 0 | dword |= ((gval - thresh) >> 26) & 32; |
547 | 0 | gval = (sword >> 16) & 0xf; |
548 | 0 | dword |= ((gval - thresh) >> 27) & 16; |
549 | 0 | gval = (sword >> 12) & 0xf; |
550 | 0 | dword |= ((gval - thresh) >> 28) & 8; |
551 | 0 | gval = (sword >> 8) & 0xf; |
552 | 0 | dword |= ((gval - thresh) >> 29) & 4; |
553 | 0 | gval = (sword >> 4) & 0xf; |
554 | 0 | dword |= ((gval - thresh) >> 30) & 2; |
555 | 0 | gval = sword & 0xf; |
556 | 0 | dword |= ((gval - thresh) >> 31) & 1; |
557 | 0 | } |
558 | 0 | lined[dcount++] = dword; |
559 | 0 | } |
560 | |
|
561 | 0 | if (j < w) { |
562 | 0 | dword = 0; |
563 | 0 | for (; j < w; j++) { |
564 | 0 | if ((j & 7) == 0) { |
565 | 0 | sword = lines[scount++]; |
566 | 0 | } |
567 | 0 | gval = (sword >> 28) & 0xf; |
568 | 0 | sword <<= 4; |
569 | 0 | dword |= (((gval - thresh) >> 31) & 1) << (31 - (j & 31)); |
570 | 0 | } |
571 | 0 | lined[dcount] = dword; |
572 | 0 | } |
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 | 0 | break; |
583 | 0 | case 8: |
584 | | /* Unrolled as 8 source words, 1 dest word */ |
585 | 0 | for (j = 0, scount = 0, dcount = 0; j + 31 < w; j += 32) { |
586 | 0 | dword = 0; |
587 | 0 | for (k = 0; k < 8; k++) { |
588 | 0 | sword = lines[scount++]; |
589 | 0 | dword <<= 4; |
590 | 0 | gval = (sword >> 24) & 0xff; |
591 | 0 | dword |= ((gval - thresh) >> 28) & 8; |
592 | 0 | gval = (sword >> 16) & 0xff; |
593 | 0 | dword |= ((gval - thresh) >> 29) & 4; |
594 | 0 | gval = (sword >> 8) & 0xff; |
595 | 0 | dword |= ((gval - thresh) >> 30) & 2; |
596 | 0 | gval = sword & 0xff; |
597 | 0 | dword |= ((gval - thresh) >> 31) & 1; |
598 | 0 | } |
599 | 0 | lined[dcount++] = dword; |
600 | 0 | } |
601 | |
|
602 | 0 | if (j < w) { |
603 | 0 | dword = 0; |
604 | 0 | for (; j < w; j++) { |
605 | 0 | if ((j & 3) == 0) { |
606 | 0 | sword = lines[scount++]; |
607 | 0 | } |
608 | 0 | gval = (sword >> 24) & 0xff; |
609 | 0 | sword <<= 8; |
610 | 0 | dword |= (l_uint64)(((gval - thresh) >> 31) & 1) |
611 | 0 | << (31 - (j & 31)); |
612 | 0 | } |
613 | 0 | lined[dcount] = dword; |
614 | 0 | } |
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 | 0 | break; |
623 | 0 | default: |
624 | 0 | L_ERROR("src depth not 4 or 8 bpp\n", __func__); |
625 | 0 | break; |
626 | 0 | } |
627 | 0 | } |
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 | 0 | { |
650 | 0 | l_int32 i, j, vals, valg, w, h, d, wpls, wplg, wpld; |
651 | 0 | l_uint32 *datas, *datag, *datad, *lines, *lineg, *lined; |
652 | 0 | PIX *pixd; |
653 | |
|
654 | 0 | if (!pixs) |
655 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
656 | 0 | if (!pixg) |
657 | 0 | return (PIX *)ERROR_PTR("pixg not defined", __func__, NULL); |
658 | 0 | if (!pixSizesEqual(pixs, pixg)) |
659 | 0 | return (PIX *)ERROR_PTR("pix sizes not equal", __func__, NULL); |
660 | 0 | pixGetDimensions(pixs, &w, &h, &d); |
661 | 0 | if (d != 8) |
662 | 0 | return (PIX *)ERROR_PTR("pixs must be 8 bpp", __func__, NULL); |
663 | | |
664 | 0 | pixd = pixCreate(w, h, 1); |
665 | 0 | pixCopyResolution(pixd, pixs); |
666 | 0 | pixCopyInputFormat(pixd, pixs); |
667 | 0 | datad = pixGetData(pixd); |
668 | 0 | wpld = pixGetWpl(pixd); |
669 | 0 | datas = pixGetData(pixs); |
670 | 0 | wpls = pixGetWpl(pixs); |
671 | 0 | datag = pixGetData(pixg); |
672 | 0 | wplg = pixGetWpl(pixg); |
673 | 0 | for (i = 0; i < h; i++) { |
674 | 0 | lines = datas + i * wpls; |
675 | 0 | lineg = datag + i * wplg; |
676 | 0 | lined = datad + i * wpld; |
677 | 0 | for (j = 0; j < w; j++) { |
678 | 0 | vals = GET_DATA_BYTE(lines, j); |
679 | 0 | valg = GET_DATA_BYTE(lineg, j); |
680 | 0 | if (vals < valg) |
681 | 0 | SET_DATA_BIT(lined, j); |
682 | 0 | } |
683 | 0 | } |
684 | |
|
685 | 0 | return pixd; |
686 | 0 | } |
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 | 0 | { |
726 | 0 | if (!pixs || pixGetDepth(pixs) != 8) |
727 | 0 | return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, NULL); |
728 | | |
729 | 0 | return pixAdaptThresholdToBinaryGen(pixs, pixm, gamma, 50, 170, 200); |
730 | 0 | } |
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 | 0 | { |
767 | 0 | PIX *pix1, *pixd; |
768 | |
|
769 | 0 | if (!pixs || pixGetDepth(pixs) != 8) |
770 | 0 | return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, NULL); |
771 | | |
772 | 0 | if ((pix1 = pixBackgroundNormSimple(pixs, pixm, NULL)) == NULL) |
773 | 0 | return (PIX *)ERROR_PTR("pix1 not made", __func__, NULL); |
774 | 0 | pixGammaTRC(pix1, pix1, gamma, blackval, whiteval); |
775 | 0 | pixd = pixThresholdToBinary(pix1, thresh); |
776 | 0 | pixDestroy(&pix1); |
777 | 0 | return pixd; |
778 | 0 | } |
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 | 0 | { |
808 | 0 | l_int32 i, j, w, h, d, wplg, wpld; |
809 | 0 | l_uint32 *datag, *datad, *lineg, *lined; |
810 | 0 | PIX *pixg, *pixd; |
811 | |
|
812 | 0 | if (!pixs) |
813 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
814 | 0 | d = pixGetDepth(pixs); |
815 | 0 | if (d != 2 && d != 4 && d != 8) |
816 | 0 | return (PIX *)ERROR_PTR("not 2, 4 or 8 bpp", __func__, NULL); |
817 | | |
818 | 0 | if (!usecmap && pixGetColormap(pixs)) |
819 | 0 | pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); |
820 | 0 | else |
821 | 0 | pixg = pixClone(pixs); |
822 | 0 | pixGetDimensions(pixg, &w, &h, &d); |
823 | 0 | if (d == 8 && (val < 0 || val > 255)) { |
824 | 0 | pixDestroy(&pixg); |
825 | 0 | return (PIX *)ERROR_PTR("val out of 8 bpp range", __func__, NULL); |
826 | 0 | } |
827 | 0 | if (d == 4 && (val < 0 || val > 15)) { |
828 | 0 | pixDestroy(&pixg); |
829 | 0 | return (PIX *)ERROR_PTR("val out of 4 bpp range", __func__, NULL); |
830 | 0 | } |
831 | 0 | if (d == 2 && (val < 0 || val > 3)) { |
832 | 0 | pixDestroy(&pixg); |
833 | 0 | return (PIX *)ERROR_PTR("val out of 2 bpp range", __func__, NULL); |
834 | 0 | } |
835 | | |
836 | 0 | pixd = pixCreate(w, h, 1); |
837 | 0 | pixCopyResolution(pixd, pixg); |
838 | 0 | pixCopyInputFormat(pixd, pixs); |
839 | 0 | datag = pixGetData(pixg); |
840 | 0 | wplg = pixGetWpl(pixg); |
841 | 0 | datad = pixGetData(pixd); |
842 | 0 | wpld = pixGetWpl(pixd); |
843 | 0 | for (i = 0; i < h; i++) { |
844 | 0 | lineg = datag + i * wplg; |
845 | 0 | lined = datad + i * wpld; |
846 | 0 | for (j = 0; j < w; j++) { |
847 | 0 | if (d == 8) { |
848 | 0 | if (GET_DATA_BYTE(lineg, j) == val) |
849 | 0 | SET_DATA_BIT(lined, j); |
850 | 0 | } else if (d == 4) { |
851 | 0 | if (GET_DATA_QBIT(lineg, j) == val) |
852 | 0 | SET_DATA_BIT(lined, j); |
853 | 0 | } else { /* d == 2 */ |
854 | 0 | if (GET_DATA_DIBIT(lineg, j) == val) |
855 | 0 | SET_DATA_BIT(lined, j); |
856 | 0 | } |
857 | 0 | } |
858 | 0 | } |
859 | |
|
860 | 0 | pixDestroy(&pixg); |
861 | 0 | return pixd; |
862 | 0 | } |
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 | 0 | { |
899 | 0 | l_int32 i, j, w, h, d, wplg, wpld, val; |
900 | 0 | l_uint32 *datag, *datad, *lineg, *lined; |
901 | 0 | PIX *pixg, *pixd; |
902 | |
|
903 | 0 | if (!pixs) |
904 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
905 | 0 | d = pixGetDepth(pixs); |
906 | 0 | if (d != 2 && d != 4 && d != 8) |
907 | 0 | return (PIX *)ERROR_PTR("not 2, 4 or 8 bpp", __func__, NULL); |
908 | 0 | if (lower < 0 || lower > upper) |
909 | 0 | return (PIX *)ERROR_PTR("lower < 0 or lower > upper!", __func__, NULL); |
910 | | |
911 | 0 | if (!usecmap && pixGetColormap(pixs)) |
912 | 0 | pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); |
913 | 0 | else |
914 | 0 | pixg = pixClone(pixs); |
915 | 0 | pixGetDimensions(pixg, &w, &h, &d); |
916 | 0 | if (d == 8 && upper > 255) { |
917 | 0 | pixDestroy(&pixg); |
918 | 0 | return (PIX *)ERROR_PTR("d == 8 and upper > 255", __func__, NULL); |
919 | 0 | } |
920 | 0 | if (d == 4 && upper > 15) { |
921 | 0 | pixDestroy(&pixg); |
922 | 0 | return (PIX *)ERROR_PTR("d == 4 and upper > 15", __func__, NULL); |
923 | 0 | } |
924 | 0 | if (d == 2 && upper > 3) { |
925 | 0 | pixDestroy(&pixg); |
926 | 0 | return (PIX *)ERROR_PTR("d == 2 and upper > 3", __func__, NULL); |
927 | 0 | } |
928 | | |
929 | 0 | pixd = pixCreate(w, h, 1); |
930 | 0 | pixCopyResolution(pixd, pixg); |
931 | 0 | pixCopyInputFormat(pixd, pixs); |
932 | 0 | datag = pixGetData(pixg); |
933 | 0 | wplg = pixGetWpl(pixg); |
934 | 0 | datad = pixGetData(pixd); |
935 | 0 | wpld = pixGetWpl(pixd); |
936 | 0 | for (i = 0; i < h; i++) { |
937 | 0 | lineg = datag + i * wplg; |
938 | 0 | lined = datad + i * wpld; |
939 | 0 | for (j = 0; j < w; j++) { |
940 | 0 | if (d == 8) |
941 | 0 | val = GET_DATA_BYTE(lineg, j); |
942 | 0 | else if (d == 4) |
943 | 0 | val = GET_DATA_QBIT(lineg, j); |
944 | 0 | else /* d == 2 */ |
945 | 0 | val = GET_DATA_DIBIT(lineg, j); |
946 | 0 | if (inband) { |
947 | 0 | if (val >= lower && val <= upper) |
948 | 0 | SET_DATA_BIT(lined, j); |
949 | 0 | } else { /* out of band */ |
950 | 0 | if (val < lower || val > upper) |
951 | 0 | SET_DATA_BIT(lined, j); |
952 | 0 | } |
953 | 0 | } |
954 | 0 | } |
955 | |
|
956 | 0 | pixDestroy(&pixg); |
957 | 0 | return pixd; |
958 | 0 | } |
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 | 0 | { |
1007 | 0 | if (!pixs) |
1008 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
1009 | 0 | if (pixGetDepth(pixs) != 8) |
1010 | 0 | return (PIX *)ERROR_PTR("must be 8 bpp for dithering", __func__, NULL); |
1011 | | |
1012 | 0 | return pixDitherTo2bppSpec(pixs, DEFAULT_CLIP_LOWER_2, |
1013 | 0 | DEFAULT_CLIP_UPPER_2, cmapflag); |
1014 | 0 | } |
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 | 0 | { |
1041 | 0 | l_int32 w, h, d, wplt, wpld; |
1042 | 0 | l_int32 *tabval, *tab38, *tab14; |
1043 | 0 | l_uint32 *datat, *datad; |
1044 | 0 | l_uint32 *bufs1, *bufs2; |
1045 | 0 | PIX *pixt, *pixd; |
1046 | 0 | PIXCMAP *cmap; |
1047 | |
|
1048 | 0 | if (!pixs) |
1049 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
1050 | 0 | pixGetDimensions(pixs, &w, &h, &d); |
1051 | 0 | if (d != 8) |
1052 | 0 | return (PIX *)ERROR_PTR("must be 8 bpp for dithering", __func__, NULL); |
1053 | 0 | if (lowerclip < 0 || lowerclip > 255) |
1054 | 0 | return (PIX *)ERROR_PTR("invalid value for lowerclip", __func__, NULL); |
1055 | 0 | if (upperclip < 0 || upperclip > 255) |
1056 | 0 | return (PIX *)ERROR_PTR("invalid value for upperclip", __func__, NULL); |
1057 | | |
1058 | 0 | if ((pixd = pixCreate(w, h, 2)) == NULL) |
1059 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
1060 | 0 | pixCopyResolution(pixd, pixs); |
1061 | 0 | pixCopyInputFormat(pixd, pixs); |
1062 | 0 | datad = pixGetData(pixd); |
1063 | 0 | wpld = pixGetWpl(pixd); |
1064 | | |
1065 | | /* If there is a colormap, remove it */ |
1066 | 0 | pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); |
1067 | 0 | datat = pixGetData(pixt); |
1068 | 0 | wplt = pixGetWpl(pixt); |
1069 | | |
1070 | | /* Two line buffers, 1 for current line and 2 for next line */ |
1071 | 0 | bufs1 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); |
1072 | 0 | bufs2 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); |
1073 | 0 | if (!bufs1 || !bufs2) { |
1074 | 0 | LEPT_FREE(bufs1); |
1075 | 0 | LEPT_FREE(bufs2); |
1076 | 0 | pixDestroy(&pixd); |
1077 | 0 | pixDestroy(&pixt); |
1078 | 0 | return (PIX *)ERROR_PTR("bufs1, bufs2 not both made", __func__, NULL); |
1079 | 0 | } |
1080 | | |
1081 | | /* 3 lookup tables: 2-bit value, (3/8)excess, and (1/4)excess */ |
1082 | 0 | make8To2DitherTables(&tabval, &tab38, &tab14, lowerclip, upperclip); |
1083 | |
|
1084 | 0 | ditherTo2bppLow(datad, w, h, wpld, datat, wplt, bufs1, bufs2, |
1085 | 0 | tabval, tab38, tab14); |
1086 | |
|
1087 | 0 | if (cmapflag) { |
1088 | 0 | cmap = pixcmapCreateLinear(2, 4); |
1089 | 0 | pixSetColormap(pixd, cmap); |
1090 | 0 | } |
1091 | |
|
1092 | 0 | LEPT_FREE(bufs1); |
1093 | 0 | LEPT_FREE(bufs2); |
1094 | 0 | LEPT_FREE(tabval); |
1095 | 0 | LEPT_FREE(tab38); |
1096 | 0 | LEPT_FREE(tab14); |
1097 | 0 | pixDestroy(&pixt); |
1098 | 0 | return pixd; |
1099 | 0 | } |
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 | 0 | { |
1129 | 0 | l_int32 i; |
1130 | 0 | l_uint32 *lined; |
1131 | | |
1132 | | /* do all lines except last line */ |
1133 | 0 | memcpy(bufs2, datas, 4 * wpls); /* prime the buffer */ |
1134 | 0 | for (i = 0; i < h - 1; i++) { |
1135 | 0 | memcpy(bufs1, bufs2, 4 * wpls); |
1136 | 0 | memcpy(bufs2, datas + (i + 1) * wpls, 4 * wpls); |
1137 | 0 | lined = datad + i * wpld; |
1138 | 0 | ditherTo2bppLineLow(lined, w, bufs1, bufs2, tabval, tab38, tab14, 0); |
1139 | 0 | } |
1140 | | |
1141 | | /* do last line */ |
1142 | 0 | memcpy(bufs1, bufs2, 4 * wpls); |
1143 | 0 | lined = datad + (h - 1) * wpld; |
1144 | 0 | ditherTo2bppLineLow(lined, w, bufs1, bufs2, tabval, tab38, tab14, 1); |
1145 | 0 | } |
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 | 0 | { |
1184 | 0 | l_int32 j; |
1185 | 0 | l_int32 oval, tab38val, tab14val; |
1186 | 0 | l_uint8 rval, bval, dval; |
1187 | |
|
1188 | 0 | if (lastlineflag == 0) { |
1189 | 0 | for (j = 0; j < w - 1; j++) { |
1190 | 0 | oval = GET_DATA_BYTE(bufs1, j); |
1191 | 0 | SET_DATA_DIBIT(lined, j, tabval[oval]); |
1192 | 0 | rval = GET_DATA_BYTE(bufs1, j + 1); |
1193 | 0 | bval = GET_DATA_BYTE(bufs2, j); |
1194 | 0 | dval = GET_DATA_BYTE(bufs2, j + 1); |
1195 | 0 | tab38val = tab38[oval]; |
1196 | 0 | tab14val = tab14[oval]; |
1197 | 0 | if (tab38val < 0) { |
1198 | 0 | rval = L_MAX(0, rval + tab38val); |
1199 | 0 | bval = L_MAX(0, bval + tab38val); |
1200 | 0 | dval = L_MAX(0, dval + tab14val); |
1201 | 0 | } else { |
1202 | 0 | rval = L_MIN(255, rval + tab38val); |
1203 | 0 | bval = L_MIN(255, bval + tab38val); |
1204 | 0 | dval = L_MIN(255, dval + tab14val); |
1205 | 0 | } |
1206 | 0 | SET_DATA_BYTE(bufs1, j + 1, rval); |
1207 | 0 | SET_DATA_BYTE(bufs2, j, bval); |
1208 | 0 | SET_DATA_BYTE(bufs2, j + 1, dval); |
1209 | 0 | } |
1210 | | |
1211 | | /* do last column: j = w - 1 */ |
1212 | 0 | oval = GET_DATA_BYTE(bufs1, j); |
1213 | 0 | SET_DATA_DIBIT(lined, j, tabval[oval]); |
1214 | 0 | bval = GET_DATA_BYTE(bufs2, j); |
1215 | 0 | tab38val = tab38[oval]; |
1216 | 0 | if (tab38val < 0) |
1217 | 0 | bval = L_MAX(0, bval + tab38val); |
1218 | 0 | else |
1219 | 0 | bval = L_MIN(255, bval + tab38val); |
1220 | 0 | SET_DATA_BYTE(bufs2, j, bval); |
1221 | 0 | } else { /* lastlineflag == 1 */ |
1222 | 0 | for (j = 0; j < w - 1; j++) { |
1223 | 0 | oval = GET_DATA_BYTE(bufs1, j); |
1224 | 0 | SET_DATA_DIBIT(lined, j, tabval[oval]); |
1225 | 0 | rval = GET_DATA_BYTE(bufs1, j + 1); |
1226 | 0 | tab38val = tab38[oval]; |
1227 | 0 | if (tab38val < 0) |
1228 | 0 | rval = L_MAX(0, rval + tab38val); |
1229 | 0 | else |
1230 | 0 | rval = L_MIN(255, rval + tab38val); |
1231 | 0 | SET_DATA_BYTE(bufs1, j + 1, rval); |
1232 | 0 | } |
1233 | | |
1234 | | /* do last pixel: (i, j) = (h - 1, w - 1) */ |
1235 | 0 | oval = GET_DATA_BYTE(bufs1, j); |
1236 | 0 | SET_DATA_DIBIT(lined, j, tabval[oval]); |
1237 | 0 | } |
1238 | 0 | } |
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 | 0 | { |
1259 | 0 | l_int32 i; |
1260 | 0 | l_int32 *tabval, *tab38, *tab14; |
1261 | | |
1262 | | /* 3 lookup tables: 2-bit value, (3/8)excess, and (1/4)excess */ |
1263 | 0 | tabval = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); |
1264 | 0 | tab38 = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); |
1265 | 0 | tab14 = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); |
1266 | 0 | *ptabval = tabval; |
1267 | 0 | *ptab38 = tab38; |
1268 | 0 | *ptab14 = tab14; |
1269 | |
|
1270 | 0 | for (i = 0; i < 256; i++) { |
1271 | 0 | if (i <= cliptoblack) { |
1272 | 0 | tabval[i] = 0; |
1273 | 0 | tab38[i] = 0; |
1274 | 0 | tab14[i] = 0; |
1275 | 0 | } else if (i < 43) { |
1276 | 0 | tabval[i] = 0; |
1277 | 0 | tab38[i] = (3 * i + 4) / 8; |
1278 | 0 | tab14[i] = (i + 2) / 4; |
1279 | 0 | } else if (i < 85) { |
1280 | 0 | tabval[i] = 1; |
1281 | 0 | tab38[i] = (3 * (i - 85) - 4) / 8; |
1282 | 0 | tab14[i] = ((i - 85) - 2) / 4; |
1283 | 0 | } else if (i < 128) { |
1284 | 0 | tabval[i] = 1; |
1285 | 0 | tab38[i] = (3 * (i - 85) + 4) / 8; |
1286 | 0 | tab14[i] = ((i - 85) + 2) / 4; |
1287 | 0 | } else if (i < 170) { |
1288 | 0 | tabval[i] = 2; |
1289 | 0 | tab38[i] = (3 * (i - 170) - 4) / 8; |
1290 | 0 | tab14[i] = ((i - 170) - 2) / 4; |
1291 | 0 | } else if (i < 213) { |
1292 | 0 | tabval[i] = 2; |
1293 | 0 | tab38[i] = (3 * (i - 170) + 4) / 8; |
1294 | 0 | tab14[i] = ((i - 170) + 2) / 4; |
1295 | 0 | } else if (i < 255 - cliptowhite) { |
1296 | 0 | tabval[i] = 3; |
1297 | 0 | tab38[i] = (3 * (i - 255) - 4) / 8; |
1298 | 0 | tab14[i] = ((i - 255) - 2) / 4; |
1299 | 0 | } else { /* i >= 255 - cliptowhite */ |
1300 | 0 | tabval[i] = 3; |
1301 | 0 | tab38[i] = 0; |
1302 | 0 | tab14[i] = 0; |
1303 | 0 | } |
1304 | 0 | } |
1305 | |
|
1306 | 0 | return 0; |
1307 | 0 | } |
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 | 0 | { |
1362 | 0 | l_int32 *qtab; |
1363 | 0 | l_int32 w, h, d, wplt, wpld; |
1364 | 0 | l_uint32 *datat, *datad; |
1365 | 0 | PIX *pixt, *pixd; |
1366 | 0 | PIXCMAP *cmap; |
1367 | |
|
1368 | 0 | if (!pixs) |
1369 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
1370 | 0 | pixGetDimensions(pixs, &w, &h, &d); |
1371 | 0 | if (d != 8) |
1372 | 0 | return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); |
1373 | 0 | if (nlevels < 2 || nlevels > 4) |
1374 | 0 | return (PIX *)ERROR_PTR("nlevels not in {2, 3, 4}", __func__, NULL); |
1375 | | |
1376 | 0 | if ((pixd = pixCreate(w, h, 2)) == NULL) |
1377 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
1378 | 0 | pixCopyResolution(pixd, pixs); |
1379 | 0 | pixCopyInputFormat(pixd, pixs); |
1380 | 0 | datad = pixGetData(pixd); |
1381 | 0 | wpld = pixGetWpl(pixd); |
1382 | |
|
1383 | 0 | if (cmapflag) { /* hold out (4 - nlevels) cmap entries */ |
1384 | 0 | cmap = pixcmapCreateLinear(2, nlevels); |
1385 | 0 | pixSetColormap(pixd, cmap); |
1386 | 0 | } |
1387 | | |
1388 | | /* If there is a colormap in the src, remove it */ |
1389 | 0 | pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); |
1390 | 0 | datat = pixGetData(pixt); |
1391 | 0 | wplt = pixGetWpl(pixt); |
1392 | | |
1393 | | /* Make the appropriate table */ |
1394 | 0 | if (cmapflag) |
1395 | 0 | qtab = makeGrayQuantIndexTable(nlevels); |
1396 | 0 | else |
1397 | 0 | qtab = makeGrayQuantTargetTable(4, 2); |
1398 | |
|
1399 | 0 | thresholdTo2bppLow(datad, h, wpld, datat, wplt, qtab); |
1400 | |
|
1401 | 0 | LEPT_FREE(qtab); |
1402 | 0 | pixDestroy(&pixt); |
1403 | 0 | return pixd; |
1404 | 0 | } |
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 | 0 | { |
1427 | 0 | l_uint8 sval1, sval2, sval3, sval4, dval; |
1428 | 0 | l_int32 i, j, k; |
1429 | 0 | l_uint32 *lines, *lined; |
1430 | |
|
1431 | 0 | for (i = 0; i < h; i++) { |
1432 | 0 | lines = datas + i * wpls; |
1433 | 0 | lined = datad + i * wpld; |
1434 | 0 | for (j = 0; j < wpls; j++) { |
1435 | 0 | k = 4 * j; |
1436 | 0 | sval1 = GET_DATA_BYTE(lines, k); |
1437 | 0 | sval2 = GET_DATA_BYTE(lines, k + 1); |
1438 | 0 | sval3 = GET_DATA_BYTE(lines, k + 2); |
1439 | 0 | sval4 = GET_DATA_BYTE(lines, k + 3); |
1440 | 0 | dval = (tab[sval1] << 6) | (tab[sval2] << 4) | |
1441 | 0 | (tab[sval3] << 2) | tab[sval4]; |
1442 | 0 | SET_DATA_BYTE(lined, j, dval); |
1443 | 0 | } |
1444 | 0 | } |
1445 | 0 | } |
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 | 0 | { |
1502 | 0 | l_int32 *qtab; |
1503 | 0 | l_int32 w, h, d, wplt, wpld; |
1504 | 0 | l_uint32 *datat, *datad; |
1505 | 0 | PIX *pixt, *pixd; |
1506 | 0 | PIXCMAP *cmap; |
1507 | |
|
1508 | 0 | if (!pixs) |
1509 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
1510 | 0 | pixGetDimensions(pixs, &w, &h, &d); |
1511 | 0 | if (d != 8) |
1512 | 0 | return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); |
1513 | 0 | if (nlevels < 2 || nlevels > 16) |
1514 | 0 | return (PIX *)ERROR_PTR("nlevels not in [2,...,16]", __func__, NULL); |
1515 | | |
1516 | 0 | if ((pixd = pixCreate(w, h, 4)) == NULL) |
1517 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
1518 | 0 | pixCopyResolution(pixd, pixs); |
1519 | 0 | pixCopyInputFormat(pixd, pixs); |
1520 | 0 | datad = pixGetData(pixd); |
1521 | 0 | wpld = pixGetWpl(pixd); |
1522 | |
|
1523 | 0 | if (cmapflag) { /* hold out (16 - nlevels) cmap entries */ |
1524 | 0 | cmap = pixcmapCreateLinear(4, nlevels); |
1525 | 0 | pixSetColormap(pixd, cmap); |
1526 | 0 | } |
1527 | | |
1528 | | /* If there is a colormap in the src, remove it */ |
1529 | 0 | pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); |
1530 | 0 | datat = pixGetData(pixt); |
1531 | 0 | wplt = pixGetWpl(pixt); |
1532 | | |
1533 | | /* Make the appropriate table */ |
1534 | 0 | if (cmapflag) |
1535 | 0 | qtab = makeGrayQuantIndexTable(nlevels); |
1536 | 0 | else |
1537 | 0 | qtab = makeGrayQuantTargetTable(16, 4); |
1538 | |
|
1539 | 0 | thresholdTo4bppLow(datad, h, wpld, datat, wplt, qtab); |
1540 | |
|
1541 | 0 | LEPT_FREE(qtab); |
1542 | 0 | pixDestroy(&pixt); |
1543 | 0 | return pixd; |
1544 | 0 | } |
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 | 0 | { |
1567 | 0 | l_uint8 sval1, sval2, sval3, sval4; |
1568 | 0 | l_uint16 dval; |
1569 | 0 | l_int32 i, j, k; |
1570 | 0 | l_uint32 *lines, *lined; |
1571 | |
|
1572 | 0 | for (i = 0; i < h; i++) { |
1573 | 0 | lines = datas + i * wpls; |
1574 | 0 | lined = datad + i * wpld; |
1575 | 0 | for (j = 0; j < wpls; j++) { |
1576 | 0 | k = 4 * j; |
1577 | 0 | sval1 = GET_DATA_BYTE(lines, k); |
1578 | 0 | sval2 = GET_DATA_BYTE(lines, k + 1); |
1579 | 0 | sval3 = GET_DATA_BYTE(lines, k + 2); |
1580 | 0 | sval4 = GET_DATA_BYTE(lines, k + 3); |
1581 | 0 | dval = (tab[sval1] << 12) | (tab[sval2] << 8) | |
1582 | 0 | (tab[sval3] << 4) | tab[sval4]; |
1583 | 0 | SET_DATA_TWO_BYTES(lined, j, dval); |
1584 | 0 | } |
1585 | 0 | } |
1586 | 0 | } |
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 | 0 | { |
1617 | 0 | l_int32 *qtab; /* quantization table */ |
1618 | 0 | l_int32 i, j, w, h, wpld, val, newval; |
1619 | 0 | l_uint32 *datad, *lined; |
1620 | 0 | PIX *pixd; |
1621 | 0 | PIXCMAP *cmap; |
1622 | |
|
1623 | 0 | if (!pixs) |
1624 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
1625 | 0 | if (pixGetDepth(pixs) != 8) |
1626 | 0 | return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); |
1627 | 0 | if (nlevels < 2 || nlevels > 256) |
1628 | 0 | 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 | 0 | if (pixGetColormap(pixs)) |
1632 | 0 | pixd = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); |
1633 | 0 | else |
1634 | 0 | pixd = pixCopy(NULL, pixs); |
1635 | |
|
1636 | 0 | if (cmapflag) { /* hold out (256 - nlevels) cmap entries */ |
1637 | 0 | cmap = pixcmapCreateLinear(8, nlevels); |
1638 | 0 | pixSetColormap(pixd, cmap); |
1639 | 0 | } |
1640 | |
|
1641 | 0 | if (cmapflag) |
1642 | 0 | qtab = makeGrayQuantIndexTable(nlevels); |
1643 | 0 | else |
1644 | 0 | qtab = makeGrayQuantTargetTable(nlevels, 8); |
1645 | |
|
1646 | 0 | pixGetDimensions(pixd, &w, &h, NULL); |
1647 | 0 | pixCopyResolution(pixd, pixs); |
1648 | 0 | pixCopyInputFormat(pixd, pixs); |
1649 | 0 | datad = pixGetData(pixd); |
1650 | 0 | wpld = pixGetWpl(pixd); |
1651 | 0 | for (i = 0; i < h; i++) { |
1652 | 0 | lined = datad + i * wpld; |
1653 | 0 | for (j = 0; j < w; j++) { |
1654 | 0 | val = GET_DATA_BYTE(lined, j); |
1655 | 0 | newval = qtab[val]; |
1656 | 0 | SET_DATA_BYTE(lined, j, newval); |
1657 | 0 | } |
1658 | 0 | } |
1659 | |
|
1660 | 0 | LEPT_FREE(qtab); |
1661 | 0 | return pixd; |
1662 | 0 | } |
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 | 0 | { |
1719 | 0 | l_int32 *qtab; |
1720 | 0 | l_int32 w, h, d, i, j, n, wplt, wpld, val, newval; |
1721 | 0 | l_uint32 *datat, *datad, *linet, *lined; |
1722 | 0 | NUMA *na; |
1723 | 0 | PIX *pixt, *pixd; |
1724 | 0 | PIXCMAP *cmap; |
1725 | |
|
1726 | 0 | if (!pixs) |
1727 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
1728 | 0 | pixGetDimensions(pixs, &w, &h, &d); |
1729 | 0 | if (d != 8) |
1730 | 0 | return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); |
1731 | 0 | if (!edgevals) |
1732 | 0 | return (PIX *)ERROR_PTR("edgevals not defined", __func__, NULL); |
1733 | 0 | if (outdepth != 0 && outdepth != 2 && outdepth != 4 && outdepth != 8) |
1734 | 0 | return (PIX *)ERROR_PTR("invalid outdepth", __func__, NULL); |
1735 | | |
1736 | | /* Parse and sort (if required) the bin edge values */ |
1737 | 0 | na = parseStringForNumbers(edgevals, " \t\n,"); |
1738 | 0 | n = numaGetCount(na); |
1739 | 0 | if (n > 255) { |
1740 | 0 | numaDestroy(&na); |
1741 | 0 | return (PIX *)ERROR_PTR("more than 256 levels", __func__, NULL); |
1742 | 0 | } |
1743 | 0 | if (outdepth == 0) { |
1744 | 0 | if (n <= 3) |
1745 | 0 | outdepth = 2; |
1746 | 0 | else if (n <= 15) |
1747 | 0 | outdepth = 4; |
1748 | 0 | else |
1749 | 0 | outdepth = 8; |
1750 | 0 | } else if (n + 1 > (1 << outdepth)) { |
1751 | 0 | L_WARNING("outdepth too small; setting to 8 bpp\n", __func__); |
1752 | 0 | outdepth = 8; |
1753 | 0 | } |
1754 | 0 | numaSort(na, na, L_SORT_INCREASING); |
1755 | | |
1756 | | /* Make the quantization LUT and the colormap */ |
1757 | 0 | makeGrayQuantTableArb(na, outdepth, &qtab, &cmap); |
1758 | 0 | if (use_average) { /* use the average value in each bin */ |
1759 | 0 | pixcmapDestroy(&cmap); |
1760 | 0 | makeGrayQuantColormapArb(pixs, qtab, outdepth, &cmap); |
1761 | 0 | } |
1762 | 0 | pixcmapSetBlackAndWhite(cmap, setblack, setwhite); |
1763 | 0 | numaDestroy(&na); |
1764 | |
|
1765 | 0 | if ((pixd = pixCreate(w, h, outdepth)) == NULL) { |
1766 | 0 | LEPT_FREE(qtab); |
1767 | 0 | pixcmapDestroy(&cmap); |
1768 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
1769 | 0 | } |
1770 | 0 | pixCopyResolution(pixd, pixs); |
1771 | 0 | pixCopyInputFormat(pixd, pixs); |
1772 | 0 | pixSetColormap(pixd, cmap); |
1773 | 0 | datad = pixGetData(pixd); |
1774 | 0 | wpld = pixGetWpl(pixd); |
1775 | | |
1776 | | /* If there is a colormap in the src, remove it */ |
1777 | 0 | pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); |
1778 | 0 | datat = pixGetData(pixt); |
1779 | 0 | wplt = pixGetWpl(pixt); |
1780 | |
|
1781 | 0 | if (outdepth == 2) { |
1782 | 0 | thresholdTo2bppLow(datad, h, wpld, datat, wplt, qtab); |
1783 | 0 | } else if (outdepth == 4) { |
1784 | 0 | thresholdTo4bppLow(datad, h, wpld, datat, wplt, qtab); |
1785 | 0 | } else { |
1786 | 0 | for (i = 0; i < h; i++) { |
1787 | 0 | lined = datad + i * wpld; |
1788 | 0 | linet = datat + i * wplt; |
1789 | 0 | for (j = 0; j < w; j++) { |
1790 | 0 | val = GET_DATA_BYTE(linet, j); |
1791 | 0 | newval = qtab[val]; |
1792 | 0 | SET_DATA_BYTE(lined, j, newval); |
1793 | 0 | } |
1794 | 0 | } |
1795 | 0 | } |
1796 | |
|
1797 | 0 | LEPT_FREE(qtab); |
1798 | 0 | pixDestroy(&pixt); |
1799 | 0 | return pixd; |
1800 | 0 | } |
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 | 0 | { |
1822 | 0 | l_int32 *tab; |
1823 | 0 | l_int32 i, j, thresh; |
1824 | |
|
1825 | 0 | tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); |
1826 | 0 | for (i = 0; i < 256; i++) { |
1827 | 0 | for (j = 0; j < nlevels; j++) { |
1828 | 0 | thresh = 255 * (2 * j + 1) / (2 * nlevels - 2); |
1829 | 0 | if (i <= thresh) { |
1830 | 0 | tab[i] = j; |
1831 | | /* lept_stderr("tab[%d] = %d\n", i, j); */ |
1832 | 0 | break; |
1833 | 0 | } |
1834 | 0 | } |
1835 | 0 | } |
1836 | 0 | return tab; |
1837 | 0 | } |
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 | 0 | { |
1872 | 0 | l_int32 *tab; |
1873 | 0 | l_int32 i, j, thresh, maxval, quantval; |
1874 | |
|
1875 | 0 | tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); |
1876 | 0 | maxval = (1 << depth) - 1; |
1877 | 0 | if (depth < 8) |
1878 | 0 | nlevels = 1 << depth; |
1879 | 0 | for (i = 0; i < 256; i++) { |
1880 | 0 | for (j = 0; j < nlevels; j++) { |
1881 | 0 | thresh = 255 * (2 * j + 1) / (2 * nlevels - 2); |
1882 | 0 | if (i <= thresh) { |
1883 | 0 | quantval = maxval * j / (nlevels - 1); |
1884 | 0 | tab[i] = quantval; |
1885 | | /* lept_stderr("tab[%d] = %d\n", i, tab[i]); */ |
1886 | 0 | break; |
1887 | 0 | } |
1888 | 0 | } |
1889 | 0 | } |
1890 | 0 | return tab; |
1891 | 0 | } |
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 | 0 | { |
1927 | 0 | l_int32 i, j, n, jstart, ave, val; |
1928 | 0 | l_int32 *tab; |
1929 | 0 | PIXCMAP *cmap; |
1930 | |
|
1931 | 0 | if (!ptab) |
1932 | 0 | return ERROR_INT("&tab not defined", __func__, 1); |
1933 | 0 | *ptab = NULL; |
1934 | 0 | if (!pcmap) |
1935 | 0 | return ERROR_INT("&cmap not defined", __func__, 1); |
1936 | 0 | *pcmap = NULL; |
1937 | 0 | if (!na) |
1938 | 0 | return ERROR_INT("na not defined", __func__, 1); |
1939 | 0 | n = numaGetCount(na); |
1940 | 0 | if (n + 1 > (1 << outdepth)) |
1941 | 0 | return ERROR_INT("more bins than cmap levels", __func__, 1); |
1942 | | |
1943 | 0 | if ((cmap = pixcmapCreate(outdepth)) == NULL) |
1944 | 0 | return ERROR_INT("cmap not made", __func__, 1); |
1945 | 0 | tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); |
1946 | 0 | *ptab = tab; |
1947 | 0 | *pcmap = cmap; |
1948 | | |
1949 | | /* First n bins */ |
1950 | 0 | jstart = 0; |
1951 | 0 | for (i = 0; i < n; i++) { |
1952 | 0 | numaGetIValue(na, i, &val); |
1953 | 0 | ave = (jstart + val) / 2; |
1954 | 0 | pixcmapAddColor(cmap, ave, ave, ave); |
1955 | 0 | for (j = jstart; j < val; j++) |
1956 | 0 | tab[j] = i; |
1957 | 0 | jstart = val; |
1958 | 0 | } |
1959 | | |
1960 | | /* Last bin */ |
1961 | 0 | ave = (jstart + 255) / 2; |
1962 | 0 | pixcmapAddColor(cmap, ave, ave, ave); |
1963 | 0 | for (j = jstart; j < 256; j++) |
1964 | 0 | tab[j] = n; |
1965 | |
|
1966 | 0 | return 0; |
1967 | 0 | } |
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 | 0 | { |
1997 | 0 | l_int32 i, j, index, w, h, d, nbins, wpl, factor, val; |
1998 | 0 | l_int32 *bincount, *binave, *binstart; |
1999 | 0 | l_uint32 *line, *data; |
2000 | |
|
2001 | 0 | if (!pcmap) |
2002 | 0 | return ERROR_INT("&cmap not defined", __func__, 1); |
2003 | 0 | *pcmap = NULL; |
2004 | 0 | if (!pixs) |
2005 | 0 | return ERROR_INT("pixs not defined", __func__, 1); |
2006 | 0 | pixGetDimensions(pixs, &w, &h, &d); |
2007 | 0 | if (d != 8) |
2008 | 0 | return ERROR_INT("pixs not 8 bpp", __func__, 1); |
2009 | 0 | if (!tab) |
2010 | 0 | return ERROR_INT("tab not defined", __func__, 1); |
2011 | 0 | nbins = tab[255] + 1; |
2012 | 0 | if (nbins > (1 << outdepth)) |
2013 | 0 | return ERROR_INT("more bins than cmap levels", __func__, 1); |
2014 | | |
2015 | | /* Find the count and weighted count for each bin */ |
2016 | 0 | if ((bincount = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32))) == NULL) |
2017 | 0 | return ERROR_INT("calloc fail for bincount", __func__, 1); |
2018 | 0 | if ((binave = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32))) == NULL) { |
2019 | 0 | LEPT_FREE(bincount); |
2020 | 0 | return ERROR_INT("calloc fail for binave", __func__, 1); |
2021 | 0 | } |
2022 | 0 | factor = (l_int32)(sqrt((l_float64)(w * h) / 30000.) + 0.5); |
2023 | 0 | factor = L_MAX(1, factor); |
2024 | 0 | data = pixGetData(pixs); |
2025 | 0 | wpl = pixGetWpl(pixs); |
2026 | 0 | for (i = 0; i < h; i += factor) { |
2027 | 0 | line = data + i * wpl; |
2028 | 0 | for (j = 0; j < w; j += factor) { |
2029 | 0 | val = GET_DATA_BYTE(line, j); |
2030 | 0 | bincount[tab[val]]++; |
2031 | 0 | binave[tab[val]] += val; |
2032 | 0 | } |
2033 | 0 | } |
2034 | | |
2035 | | /* Find the smallest gray values in each bin */ |
2036 | 0 | binstart = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32)); |
2037 | 0 | for (i = 1, index = 1; i < 256; i++) { |
2038 | 0 | if (tab[i] < index) continue; |
2039 | 0 | if (tab[i] == index) |
2040 | 0 | binstart[index++] = i; |
2041 | 0 | } |
2042 | | |
2043 | | /* Get the averages. If there are no samples in a bin, use |
2044 | | * the center value of the bin. */ |
2045 | 0 | *pcmap = pixcmapCreate(outdepth); |
2046 | 0 | for (i = 0; i < nbins; i++) { |
2047 | 0 | if (bincount[i]) { |
2048 | 0 | val = binave[i] / bincount[i]; |
2049 | 0 | } else { /* no samples in the bin */ |
2050 | 0 | if (i < nbins - 1) |
2051 | 0 | val = (binstart[i] + binstart[i + 1]) / 2; |
2052 | 0 | else /* last bin */ |
2053 | 0 | val = (binstart[i] + 255) / 2; |
2054 | 0 | } |
2055 | 0 | pixcmapAddColor(*pcmap, val, val, val); |
2056 | 0 | } |
2057 | |
|
2058 | 0 | LEPT_FREE(bincount); |
2059 | 0 | LEPT_FREE(binave); |
2060 | 0 | LEPT_FREE(binstart); |
2061 | 0 | return 0; |
2062 | 0 | } |
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 | 0 | { |
2102 | 0 | l_int32 i, j, w, h, d, wpls, wpld; |
2103 | 0 | l_int32 rref, gref, bref, rval, gval, bval; |
2104 | 0 | l_int32 rmin, gmin, bmin, rmax, gmax, bmax; |
2105 | 0 | l_uint32 pixel; |
2106 | 0 | l_uint32 *datas, *datad, *lines, *lined; |
2107 | 0 | PIX *pixd; |
2108 | |
|
2109 | 0 | if (!pixs) |
2110 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
2111 | 0 | pixGetDimensions(pixs, &w, &h, &d); |
2112 | 0 | if (d != 32) |
2113 | 0 | return (PIX *)ERROR_PTR("not 32 bpp", __func__, NULL); |
2114 | 0 | if (delm < 0 || delp < 0) |
2115 | 0 | return (PIX *)ERROR_PTR("delm and delp must be >= 0", __func__, NULL); |
2116 | 0 | if (fractm < 0.0 || fractm > 1.0 || fractp < 0.0 || fractp > 1.0) |
2117 | 0 | return (PIX *)ERROR_PTR("fractm and/or fractp invalid", __func__, NULL); |
2118 | | |
2119 | 0 | extractRGBValues(refval, &rref, &gref, &bref); |
2120 | 0 | if (fractm == 0.0 && fractp == 0.0) { |
2121 | 0 | rmin = rref - delm; |
2122 | 0 | gmin = gref - delm; |
2123 | 0 | bmin = bref - delm; |
2124 | 0 | rmax = rref + delm; |
2125 | 0 | gmax = gref + delm; |
2126 | 0 | bmax = bref + delm; |
2127 | 0 | } else if (delm == 0 && delp == 0) { |
2128 | 0 | rmin = (l_int32)((1.0 - fractm) * rref); |
2129 | 0 | gmin = (l_int32)((1.0 - fractm) * gref); |
2130 | 0 | bmin = (l_int32)((1.0 - fractm) * bref); |
2131 | 0 | rmax = rref + (l_int32)(fractp * (255 - rref)); |
2132 | 0 | gmax = gref + (l_int32)(fractp * (255 - gref)); |
2133 | 0 | bmax = bref + (l_int32)(fractp * (255 - bref)); |
2134 | 0 | } else { |
2135 | 0 | L_ERROR("bad input: either (delm, delp) or (fractm, fractp) " |
2136 | 0 | "must be 0\n", __func__); |
2137 | 0 | return NULL; |
2138 | 0 | } |
2139 | | |
2140 | 0 | pixd = pixCreate(w, h, 1); |
2141 | 0 | pixCopyResolution(pixd, pixs); |
2142 | 0 | pixCopyInputFormat(pixd, pixs); |
2143 | 0 | datas = pixGetData(pixs); |
2144 | 0 | wpls = pixGetWpl(pixs); |
2145 | 0 | datad = pixGetData(pixd); |
2146 | 0 | wpld = pixGetWpl(pixd); |
2147 | 0 | for (i = 0; i < h; i++) { |
2148 | 0 | lines = datas + i * wpls; |
2149 | 0 | lined = datad + i * wpld; |
2150 | 0 | for (j = 0; j < w; j++) { |
2151 | 0 | pixel = lines[j]; |
2152 | 0 | rval = (pixel >> L_RED_SHIFT) & 0xff; |
2153 | 0 | if (rval < rmin || rval > rmax) |
2154 | 0 | continue; |
2155 | 0 | gval = (pixel >> L_GREEN_SHIFT) & 0xff; |
2156 | 0 | if (gval < gmin || gval > gmax) |
2157 | 0 | continue; |
2158 | 0 | bval = (pixel >> L_BLUE_SHIFT) & 0xff; |
2159 | 0 | if (bval < bmin || bval > bmax) |
2160 | 0 | continue; |
2161 | 0 | SET_DATA_BIT(lined, j); |
2162 | 0 | } |
2163 | 0 | } |
2164 | |
|
2165 | 0 | return pixd; |
2166 | 0 | } |
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 | 0 | { |
2196 | 0 | l_int32 i, j, w, h, d, wpls, wpld; |
2197 | 0 | l_int32 rref1, gref1, bref1, rref2, gref2, bref2, rval, gval, bval; |
2198 | 0 | l_uint32 pixel, dist1, dist2; |
2199 | 0 | l_uint32 *datas, *datad, *lines, *lined; |
2200 | 0 | PIX *pixd; |
2201 | |
|
2202 | 0 | if (!pixs) |
2203 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
2204 | 0 | pixGetDimensions(pixs, &w, &h, &d); |
2205 | 0 | if (d != 32) |
2206 | 0 | return (PIX *)ERROR_PTR("not 32 bpp", __func__, NULL); |
2207 | 0 | if (distflag != L_MANHATTAN_DISTANCE && distflag != L_EUCLIDEAN_DISTANCE) |
2208 | 0 | return (PIX *)ERROR_PTR("invalid distflag", __func__, NULL); |
2209 | | |
2210 | 0 | extractRGBValues(refval1, &rref1, &gref1, &bref1); |
2211 | 0 | extractRGBValues(refval2, &rref2, &gref2, &bref2); |
2212 | 0 | pixd = pixCreate(w, h, 1); |
2213 | 0 | pixCopyResolution(pixd, pixs); |
2214 | 0 | pixCopyInputFormat(pixd, pixs); |
2215 | 0 | datas = pixGetData(pixs); |
2216 | 0 | wpls = pixGetWpl(pixs); |
2217 | 0 | datad = pixGetData(pixd); |
2218 | 0 | wpld = pixGetWpl(pixd); |
2219 | 0 | for (i = 0; i < h; i++) { |
2220 | 0 | lines = datas + i * wpls; |
2221 | 0 | lined = datad + i * wpld; |
2222 | 0 | for (j = 0; j < w; j++) { |
2223 | 0 | pixel = lines[j]; |
2224 | 0 | extractRGBValues(pixel, &rval, &gval, &bval); |
2225 | 0 | if (distflag == L_MANHATTAN_DISTANCE) { |
2226 | 0 | dist1 = L_ABS(rref1 - rval); |
2227 | 0 | dist2 = L_ABS(rref2 - rval); |
2228 | 0 | dist1 += L_ABS(gref1 - gval); |
2229 | 0 | dist2 += L_ABS(gref2 - gval); |
2230 | 0 | dist1 += L_ABS(bref1 - bval); |
2231 | 0 | dist2 += L_ABS(bref2 - bval); |
2232 | 0 | } else { |
2233 | 0 | dist1 = (rref1 - rval) * (rref1 - rval); |
2234 | 0 | dist2 = (rref2 - rval) * (rref2 - rval); |
2235 | 0 | dist1 += (gref1 - gval) * (gref1 - gval); |
2236 | 0 | dist2 += (gref2 - gval) * (gref2 - gval); |
2237 | 0 | dist1 += (bref1 - bval) * (bref1 - bval); |
2238 | 0 | dist2 += (bref2 - bval) * (bref2 - bval); |
2239 | 0 | } |
2240 | 0 | if (dist1 < dist2) |
2241 | 0 | SET_DATA_BIT(lined, j); |
2242 | 0 | } |
2243 | 0 | } |
2244 | |
|
2245 | 0 | return pixd; |
2246 | 0 | } |
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 | 0 | { |
2309 | 0 | l_int32 w, h, wd, hd, wm, hm, wpls, wplm, wpld; |
2310 | 0 | l_int32 nc, nestim, i, j, vals, vald; |
2311 | 0 | l_int32 *lut; |
2312 | 0 | l_uint32 *datas, *datam, *datad, *lines, *linem, *lined; |
2313 | 0 | NUMA *na; |
2314 | 0 | PIX *pixmr = NULL; /* resized mask */ |
2315 | 0 | PIXCMAP *cmap; |
2316 | |
|
2317 | 0 | if (!pixs || pixGetDepth(pixs) != 8) |
2318 | 0 | return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, NULL); |
2319 | 0 | if (minfract < 0.01) { |
2320 | 0 | L_WARNING("minfract < 0.01; setting to 0.05\n", __func__); |
2321 | 0 | minfract = 0.05f; |
2322 | 0 | } |
2323 | 0 | if (maxsize < 2) { |
2324 | 0 | L_WARNING("maxsize < 2; setting to 10\n", __func__); |
2325 | 0 | maxsize = 10; |
2326 | 0 | } |
2327 | 0 | if ((pixd && !pixm) || (!pixd && pixm)) |
2328 | 0 | return (PIX *)ERROR_PTR("(pixd,pixm) not defined together", |
2329 | 0 | __func__, NULL); |
2330 | 0 | pixGetDimensions(pixs, &w, &h, NULL); |
2331 | 0 | if (pixd) { |
2332 | 0 | if (pixGetDepth(pixm) != 1) |
2333 | 0 | return (PIX *)ERROR_PTR("pixm not 1 bpp", __func__, NULL); |
2334 | 0 | if ((cmap = pixGetColormap(pixd)) == NULL) |
2335 | 0 | return (PIX *)ERROR_PTR("pixd not cmapped", __func__, NULL); |
2336 | 0 | pixGetDimensions(pixd, &wd, &hd, NULL); |
2337 | 0 | if (w != wd || h != hd) |
2338 | 0 | return (PIX *)ERROR_PTR("pixs, pixd sizes differ", __func__, NULL); |
2339 | 0 | nc = pixcmapGetCount(cmap); |
2340 | 0 | nestim = nc + (l_int32)(1.5 * 255 / maxsize); |
2341 | 0 | lept_stderr( "nestim = %d\n", nestim); |
2342 | 0 | if (nestim > 255) { |
2343 | 0 | L_ERROR("Estimate %d colors!\n", __func__, nestim); |
2344 | 0 | return (PIX *)ERROR_PTR("probably too many colors", __func__, NULL); |
2345 | 0 | } |
2346 | 0 | pixGetDimensions(pixm, &wm, &hm, NULL); |
2347 | 0 | if (w != wm || h != hm) { /* resize the mask */ |
2348 | 0 | L_WARNING("mask and dest sizes not equal\n", __func__); |
2349 | 0 | pixmr = pixCreate(w, h, 1); |
2350 | 0 | pixRasterop(pixmr, 0, 0, wm, hm, PIX_SRC, pixm, 0, 0); |
2351 | 0 | pixRasterop(pixmr, wm, 0, w - wm, h, PIX_SET, NULL, 0, 0); |
2352 | 0 | pixRasterop(pixmr, 0, hm, wm, h - hm, PIX_SET, NULL, 0, 0); |
2353 | 0 | } else { |
2354 | 0 | pixmr = pixClone(pixm); |
2355 | 0 | } |
2356 | 0 | } else { |
2357 | 0 | pixd = pixCreateTemplate(pixs); |
2358 | 0 | cmap = pixcmapCreate(8); |
2359 | 0 | pixSetColormap(pixd, cmap); |
2360 | 0 | } |
2361 | 0 | pixCopyResolution(pixd, pixs); |
2362 | 0 | pixCopyInputFormat(pixd, pixs); |
2363 | | |
2364 | | /* Use original mask, if it exists, to select gray pixels */ |
2365 | 0 | 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 | 0 | if (numaFillCmapFromHisto(na, cmap, minfract, maxsize, &lut)) |
2370 | 0 | L_ERROR("ran out of colors in cmap!\n", __func__); |
2371 | 0 | numaDestroy(&na); |
2372 | | |
2373 | | /* Assign the gray pixels to their cmap indices */ |
2374 | 0 | datas = pixGetData(pixs); |
2375 | 0 | datad = pixGetData(pixd); |
2376 | 0 | wpls = pixGetWpl(pixs); |
2377 | 0 | wpld = pixGetWpl(pixd); |
2378 | 0 | if (!pixm) { |
2379 | 0 | for (i = 0; i < h; i++) { |
2380 | 0 | lines = datas + i * wpls; |
2381 | 0 | lined = datad + i * wpld; |
2382 | 0 | for (j = 0; j < w; j++) { |
2383 | 0 | vals = GET_DATA_BYTE(lines, j); |
2384 | 0 | vald = lut[vals]; |
2385 | 0 | SET_DATA_BYTE(lined, j, vald); |
2386 | 0 | } |
2387 | 0 | } |
2388 | 0 | LEPT_FREE(lut); |
2389 | 0 | return pixd; |
2390 | 0 | } |
2391 | | |
2392 | 0 | datam = pixGetData(pixmr); |
2393 | 0 | wplm = pixGetWpl(pixmr); |
2394 | 0 | for (i = 0; i < h; i++) { |
2395 | 0 | lines = datas + i * wpls; |
2396 | 0 | linem = datam + i * wplm; |
2397 | 0 | lined = datad + i * wpld; |
2398 | 0 | for (j = 0; j < w; j++) { |
2399 | 0 | if (!GET_DATA_BIT(linem, j)) |
2400 | 0 | continue; |
2401 | 0 | vals = GET_DATA_BYTE(lines, j); |
2402 | 0 | vald = lut[vals]; |
2403 | 0 | SET_DATA_BYTE(lined, j, vald); |
2404 | 0 | } |
2405 | 0 | } |
2406 | 0 | pixDestroy(&pixmr); |
2407 | 0 | LEPT_FREE(lut); |
2408 | 0 | return pixd; |
2409 | 0 | } |
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 | 0 | { |
2439 | 0 | l_int32 mincount, index, sum, wtsum, span, istart, i, val, ret; |
2440 | 0 | l_int32 *iahisto, *lut; |
2441 | 0 | l_float32 total; |
2442 | |
|
2443 | 0 | if (!plut) |
2444 | 0 | return ERROR_INT("&lut not defined", __func__, 1); |
2445 | 0 | *plut = NULL; |
2446 | 0 | if (!na) |
2447 | 0 | return ERROR_INT("na not defined", __func__, 1); |
2448 | 0 | if (!cmap) |
2449 | 0 | return ERROR_INT("cmap not defined", __func__, 1); |
2450 | | |
2451 | 0 | numaGetSum(na, &total); |
2452 | 0 | mincount = (l_int32)(minfract * total); |
2453 | 0 | iahisto = numaGetIArray(na); |
2454 | 0 | lut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); |
2455 | 0 | *plut = lut; |
2456 | 0 | 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 | 0 | sum = 0; |
2468 | 0 | wtsum = 0; |
2469 | 0 | istart = 0; |
2470 | 0 | ret = 0; |
2471 | 0 | for (i = 0; i < 256; i++) { |
2472 | 0 | lut[i] = index; |
2473 | 0 | sum += iahisto[i]; |
2474 | 0 | wtsum += i * iahisto[i]; |
2475 | 0 | span = i - istart + 1; |
2476 | 0 | if (sum < mincount && span < maxsize) |
2477 | 0 | continue; |
2478 | | |
2479 | 0 | if (sum == 0) { /* empty span; don't save */ |
2480 | 0 | istart = i + 1; |
2481 | 0 | continue; |
2482 | 0 | } |
2483 | | |
2484 | | /* Found new color; sum > 0 */ |
2485 | 0 | val = (l_int32)((l_float32)wtsum / (l_float32)sum + 0.5); |
2486 | 0 | ret = pixcmapAddColor(cmap, val, val, val); |
2487 | 0 | istart = i + 1; |
2488 | 0 | sum = 0; |
2489 | 0 | wtsum = 0; |
2490 | 0 | index++; |
2491 | 0 | } |
2492 | 0 | if (istart < 256 && sum > 0) { /* last one */ |
2493 | 0 | span = 256 - istart; |
2494 | 0 | val = (l_int32)((l_float32)wtsum / (l_float32)sum + 0.5); |
2495 | 0 | ret = pixcmapAddColor(cmap, val, val, val); |
2496 | 0 | } |
2497 | |
|
2498 | 0 | LEPT_FREE(iahisto); |
2499 | 0 | return ret; |
2500 | 0 | } |
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 | 0 | { |
2526 | 0 | l_int32 i, j, index, w, h, d, depth, wpls, wpld; |
2527 | 0 | l_int32 hascolor, vals, vald; |
2528 | 0 | l_int32 *tab; |
2529 | 0 | l_uint32 *datas, *datad, *lines, *lined; |
2530 | 0 | PIXCMAP *cmapd; |
2531 | 0 | PIX *pixd; |
2532 | |
|
2533 | 0 | if (!pixs) |
2534 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
2535 | 0 | if (pixGetColormap(pixs) != NULL) { |
2536 | 0 | L_WARNING("pixs already has a colormap; returning a copy\n", __func__); |
2537 | 0 | return pixCopy(NULL, pixs); |
2538 | 0 | } |
2539 | 0 | pixGetDimensions(pixs, &w, &h, &d); |
2540 | 0 | if (d != 8) |
2541 | 0 | return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); |
2542 | 0 | if (!cmap) |
2543 | 0 | return (PIX *)ERROR_PTR("cmap not defined", __func__, NULL); |
2544 | 0 | if (mindepth != 2 && mindepth != 4 && mindepth != 8) |
2545 | 0 | return (PIX *)ERROR_PTR("invalid mindepth", __func__, NULL); |
2546 | | |
2547 | | /* Make sure the colormap is gray */ |
2548 | 0 | pixcmapHasColor(cmap, &hascolor); |
2549 | 0 | if (hascolor) { |
2550 | 0 | L_WARNING("Converting colormap colors to gray\n", __func__); |
2551 | 0 | cmapd = pixcmapColorToGray(cmap, 0.3f, 0.5f, 0.2f); |
2552 | 0 | } else { |
2553 | 0 | cmapd = pixcmapCopy(cmap); |
2554 | 0 | } |
2555 | | |
2556 | | /* Make LUT into colormap */ |
2557 | 0 | tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); |
2558 | 0 | for (i = 0; i < 256; i++) { |
2559 | 0 | pixcmapGetNearestGrayIndex(cmapd, i, &index); |
2560 | 0 | tab[i] = index; |
2561 | 0 | } |
2562 | |
|
2563 | 0 | pixcmapGetMinDepth(cmap, &depth); |
2564 | 0 | depth = L_MAX(depth, mindepth); |
2565 | 0 | pixd = pixCreate(w, h, depth); |
2566 | 0 | pixSetColormap(pixd, cmapd); |
2567 | 0 | pixCopyResolution(pixd, pixs); |
2568 | 0 | pixCopyInputFormat(pixd, pixs); |
2569 | 0 | datas = pixGetData(pixs); |
2570 | 0 | datad = pixGetData(pixd); |
2571 | 0 | wpls = pixGetWpl(pixs); |
2572 | 0 | wpld = pixGetWpl(pixd); |
2573 | 0 | for (i = 0; i < h; i++) { |
2574 | 0 | lines = datas + i * wpls; |
2575 | 0 | lined = datad + i * wpld; |
2576 | 0 | for (j = 0; j < w; j++) { |
2577 | 0 | vals = GET_DATA_BYTE(lines, j); |
2578 | 0 | vald = tab[vals]; |
2579 | 0 | if (depth == 2) |
2580 | 0 | SET_DATA_DIBIT(lined, j, vald); |
2581 | 0 | else if (depth == 4) |
2582 | 0 | SET_DATA_QBIT(lined, j, vald); |
2583 | 0 | else /* depth == 8 */ |
2584 | 0 | SET_DATA_BYTE(lined, j, vald); |
2585 | 0 | } |
2586 | 0 | } |
2587 | |
|
2588 | 0 | LEPT_FREE(tab); |
2589 | 0 | return pixd; |
2590 | 0 | } |
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 */ |