comparison mupdf-source/thirdparty/leptonica/src/pixarith.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 /*====================================================================*
2 - Copyright (C) 2001 Leptonica. All rights reserved.
3 -
4 - Redistribution and use in source and binary forms, with or without
5 - modification, are permitted provided that the following conditions
6 - are met:
7 - 1. Redistributions of source code must retain the above copyright
8 - notice, this list of conditions and the following disclaimer.
9 - 2. Redistributions in binary form must reproduce the above
10 - copyright notice, this list of conditions and the following
11 - disclaimer in the documentation and/or other materials
12 - provided with the distribution.
13 -
14 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18 - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *====================================================================*/
26
27 /*!
28 * \file pixarith.c
29 * <pre>
30 *
31 * One-image grayscale arithmetic operations (8, 16, 32 bpp)
32 * l_int32 pixAddConstantGray()
33 * l_int32 pixMultConstantGray()
34 *
35 * Two-image grayscale arithmetic operations (8, 16, 32 bpp)
36 * PIX *pixAddGray()
37 * PIX *pixSubtractGray()
38 * PIX *pixMultiplyGray()
39 *
40 * Grayscale threshold operation (8, 16, 32 bpp)
41 * PIX *pixThresholdToValue()
42 *
43 * Image accumulator arithmetic operations
44 * PIX *pixInitAccumulate()
45 * PIX *pixFinalAccumulate()
46 * PIX *pixFinalAccumulateThreshold()
47 * l_int32 pixAccumulate()
48 * l_int32 pixMultConstAccumulate()
49 *
50 * Absolute value of difference
51 * PIX *pixAbsDifference()
52 *
53 * Sum of color images
54 * PIX *pixAddRGB()
55 *
56 * Two-image min and max operations (8 and 16 bpp)
57 * PIX *pixMinOrMax()
58 *
59 * Scale pix for maximum dynamic range
60 * PIX *pixMaxDynamicRange()
61 * PIX *pixMaxDynamicRangeRGB()
62 *
63 * RGB pixel value scaling
64 * l_uint32 linearScaleRGBVal()
65 * l_uint32 logScaleRGBVal()
66 *
67 * Log base2 lookup
68 * l_float32 *makeLogBase2Tab()
69 * l_float32 getLogBase2()
70 *
71 * The image accumulator operations are used when you expect
72 * overflow from 8 bits on intermediate results. For example,
73 * you might want a tophat contrast operator which is
74 * 3*I - opening(I,S) - closing(I,S)
75 * To use these operations, first use the init to generate
76 * a 16 bpp image, use the accumulate to add or subtract 8 bpp
77 * images from that, or the multiply constant to multiply
78 * by a small constant (much less than 256 -- we don't want
79 * overflow from the 16 bit images!), and when you're finished
80 * use final to bring the result back to 8 bpp, clipped
81 * if necessary. There is also a divide function, which
82 * can be used to divide one image by another, scaling the
83 * result for maximum dynamic range, and giving back the
84 * 8 bpp result.
85 *
86 * A simpler interface to the arithmetic operations is
87 * provided in pixacc.c.
88 * </pre>
89 */
90
91 #ifdef HAVE_CONFIG_H
92 #include <config_auto.h>
93 #endif /* HAVE_CONFIG_H */
94
95 #include <string.h>
96 #include <math.h>
97 #include "allheaders.h"
98
99 /*-------------------------------------------------------------*
100 * One-image grayscale arithmetic operations *
101 *-------------------------------------------------------------*/
102 /*!
103 * \brief pixAddConstantGray()
104 *
105 * \param[in] pixs 8, 16 or 32 bpp
106 * \param[in] val amount to add to each pixel
107 * \return 0 if OK, 1 on error
108 *
109 * <pre>
110 * Notes:
111 * (1) In-place operation.
112 * (2) No clipping for 32 bpp.
113 * (3) For 8 and 16 bpp, if val > 0 the result is clipped
114 * to 0xff and 0xffff, rsp.
115 * (4) For 8 and 16 bpp, if val < 0 the result is clipped to 0.
116 * </pre>
117 */
118 l_ok
119 pixAddConstantGray(PIX *pixs,
120 l_int32 val)
121 {
122 l_int32 i, j, w, h, d, wpl, pval;
123 l_uint32 *data, *line;
124
125 if (!pixs)
126 return ERROR_INT("pixs not defined", __func__, 1);
127 pixGetDimensions(pixs, &w, &h, &d);
128 if (d != 8 && d != 16 && d != 32)
129 return ERROR_INT("pixs not 8, 16 or 32 bpp", __func__, 1);
130
131 data = pixGetData(pixs);
132 wpl = pixGetWpl(pixs);
133 for (i = 0; i < h; i++) {
134 line = data + i * wpl;
135 if (d == 8) {
136 if (val < 0) {
137 for (j = 0; j < w; j++) {
138 pval = GET_DATA_BYTE(line, j);
139 pval = L_MAX(0, pval + val);
140 SET_DATA_BYTE(line, j, pval);
141 }
142 } else { /* val >= 0 */
143 for (j = 0; j < w; j++) {
144 pval = GET_DATA_BYTE(line, j);
145 pval = L_MIN(255, pval + val);
146 SET_DATA_BYTE(line, j, pval);
147 }
148 }
149 } else if (d == 16) {
150 if (val < 0) {
151 for (j = 0; j < w; j++) {
152 pval = GET_DATA_TWO_BYTES(line, j);
153 pval = L_MAX(0, pval + val);
154 SET_DATA_TWO_BYTES(line, j, pval);
155 }
156 } else { /* val >= 0 */
157 for (j = 0; j < w; j++) {
158 pval = GET_DATA_TWO_BYTES(line, j);
159 pval = L_MIN(0xffff, pval + val);
160 SET_DATA_TWO_BYTES(line, j, pval);
161 }
162 }
163 } else { /* d == 32; no check for overflow (< 0 or > 0xffffffff) */
164 for (j = 0; j < w; j++)
165 *(line + j) += val;
166 }
167 }
168
169 return 0;
170 }
171
172
173 /*!
174 * \brief pixMultConstantGray()
175 *
176 * \param[in] pixs 8, 16 or 32 bpp
177 * \param[in] val >= 0.0; amount to multiply by each pixel
178 * \return 0 if OK, 1 on error
179 *
180 * <pre>
181 * Notes:
182 * (1) In-place operation; val must be >= 0.
183 * (2) No clipping for 32 bpp.
184 * (3) For 8 and 16 bpp, the result is clipped to 0xff and 0xffff, rsp.
185 * </pre>
186 */
187 l_ok
188 pixMultConstantGray(PIX *pixs,
189 l_float32 val)
190 {
191 l_int32 i, j, w, h, d, wpl, pval;
192 l_uint32 upval;
193 l_uint32 *data, *line;
194
195 if (!pixs)
196 return ERROR_INT("pixs not defined", __func__, 1);
197 pixGetDimensions(pixs, &w, &h, &d);
198 if (d != 8 && d != 16 && d != 32)
199 return ERROR_INT("pixs not 8, 16 or 32 bpp", __func__, 1);
200 if (val < 0.0)
201 return ERROR_INT("val < 0.0", __func__, 1);
202
203 data = pixGetData(pixs);
204 wpl = pixGetWpl(pixs);
205 for (i = 0; i < h; i++) {
206 line = data + i * wpl;
207 if (d == 8) {
208 for (j = 0; j < w; j++) {
209 pval = GET_DATA_BYTE(line, j);
210 pval = (l_int32)(val * pval);
211 pval = L_MIN(255, pval);
212 SET_DATA_BYTE(line, j, pval);
213 }
214 } else if (d == 16) {
215 for (j = 0; j < w; j++) {
216 pval = GET_DATA_TWO_BYTES(line, j);
217 pval = (l_int32)(val * pval);
218 pval = L_MIN(0xffff, pval);
219 SET_DATA_TWO_BYTES(line, j, pval);
220 }
221 } else { /* d == 32; no clipping */
222 for (j = 0; j < w; j++) {
223 upval = *(line + j);
224 upval = (l_uint32)(val * upval);
225 *(line + j) = upval;
226 }
227 }
228 }
229
230 return 0;
231 }
232
233
234 /*-------------------------------------------------------------*
235 * Two-image grayscale arithmetic ops *
236 *-------------------------------------------------------------*/
237 /*!
238 * \brief pixAddGray()
239 *
240 * \param[in] pixd [optional]; this can be null, equal to pixs1, or
241 * different from pixs1
242 * \param[in] pixs1 can be equal to pixd
243 * \param[in] pixs2
244 * \return pixd always
245 *
246 * <pre>
247 * Notes:
248 * (1) Arithmetic addition of two 8, 16 or 32 bpp images.
249 * (2) For 8 and 16 bpp, we do explicit clipping to 0xff and 0xffff,
250 * respectively.
251 * (3) Alignment is to UL corner.
252 * (4) There are 3 cases. The result can go to a new dest,
253 * in-place to pixs1, or to an existing input dest:
254 * * pixd == null: (src1 + src2) --> new pixd
255 * * pixd == pixs1: (src1 + src2) --> src1 (in-place)
256 * * pixd != pixs1: (src1 + src2) --> input pixd
257 * (5) pixs2 must be different from both pixd and pixs1.
258 * </pre>
259 */
260 PIX *
261 pixAddGray(PIX *pixd,
262 PIX *pixs1,
263 PIX *pixs2)
264 {
265 l_int32 i, j, d, ws, hs, w, h, wpls, wpld, val, sum;
266 l_uint32 *datas, *datad, *lines, *lined;
267
268 if (!pixs1)
269 return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd);
270 if (!pixs2)
271 return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd);
272 if (pixs2 == pixs1)
273 return (PIX *)ERROR_PTR("pixs2 and pixs1 must differ", __func__, pixd);
274 if (pixs2 == pixd)
275 return (PIX *)ERROR_PTR("pixs2 and pixd must differ", __func__, pixd);
276 d = pixGetDepth(pixs1);
277 if (d != 8 && d != 16 && d != 32)
278 return (PIX *)ERROR_PTR("pix are not 8, 16 or 32 bpp", __func__, pixd);
279 if (pixGetDepth(pixs2) != d)
280 return (PIX *)ERROR_PTR("depths differ (pixs1, pixs2)", __func__, pixd);
281 if (pixd && (pixGetDepth(pixd) != d))
282 return (PIX *)ERROR_PTR("depths differ (pixs1, pixd)", __func__, pixd);
283
284 if (!pixSizesEqual(pixs1, pixs2))
285 L_WARNING("pixs1 and pixs2 not equal in size\n", __func__);
286 if (pixd && !pixSizesEqual(pixs1, pixd))
287 L_WARNING("pixs1 and pixd not equal in size\n", __func__);
288
289 if (pixs1 != pixd)
290 pixd = pixCopy(pixd, pixs1);
291
292 /* pixd + pixs2 ==> pixd */
293 datas = pixGetData(pixs2);
294 datad = pixGetData(pixd);
295 wpls = pixGetWpl(pixs2);
296 wpld = pixGetWpl(pixd);
297 pixGetDimensions(pixs2, &ws, &hs, NULL);
298 pixGetDimensions(pixd, &w, &h, NULL);
299 w = L_MIN(ws, w);
300 h = L_MIN(hs, h);
301 for (i = 0; i < h; i++) {
302 lined = datad + i * wpld;
303 lines = datas + i * wpls;
304 if (d == 8) {
305 for (j = 0; j < w; j++) {
306 sum = GET_DATA_BYTE(lines, j) + GET_DATA_BYTE(lined, j);
307 val = L_MIN(sum, 255);
308 SET_DATA_BYTE(lined, j, val);
309 }
310 } else if (d == 16) {
311 for (j = 0; j < w; j++) {
312 sum = GET_DATA_TWO_BYTES(lines, j)
313 + GET_DATA_TWO_BYTES(lined, j);
314 val = L_MIN(sum, 0xffff);
315 SET_DATA_TWO_BYTES(lined, j, val);
316 }
317 } else { /* d == 32; no clipping */
318 for (j = 0; j < w; j++)
319 *(lined + j) += *(lines + j);
320 }
321 }
322
323 return pixd;
324 }
325
326
327 /*!
328 * \brief pixSubtractGray()
329 *
330 * \param[in] pixd [optional]; this can be null, equal to pixs1, or
331 * different from pixs1
332 * \param[in] pixs1 can be equal to pixd
333 * \param[in] pixs2
334 * \return pixd always
335 *
336 * <pre>
337 * Notes:
338 * (1) Arithmetic subtraction of two 8, 16 or 32 bpp images.
339 * (2) Source pixs2 is always subtracted from source pixs1.
340 * (3) Do explicit clipping to 0.
341 * (4) Alignment is to UL corner.
342 * (5) There are 3 cases. The result can go to a new dest,
343 * in-place to pixs1, or to an existing input dest:
344 * (a) pixd == null (src1 - src2) --> new pixd
345 * (b) pixd == pixs1 (src1 - src2) --> src1 (in-place)
346 * (d) pixd != pixs1 (src1 - src2) --> input pixd
347 * (6) pixs2 must be different from both pixd and pixs1.
348 * </pre>
349 */
350 PIX *
351 pixSubtractGray(PIX *pixd,
352 PIX *pixs1,
353 PIX *pixs2)
354 {
355 l_int32 i, j, w, h, ws, hs, d, wpls, wpld, val, diff;
356 l_uint32 *datas, *datad, *lines, *lined;
357
358 if (!pixs1)
359 return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd);
360 if (!pixs2)
361 return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd);
362 if (pixs2 == pixs1)
363 return (PIX *)ERROR_PTR("pixs2 and pixs1 must differ", __func__, pixd);
364 if (pixs2 == pixd)
365 return (PIX *)ERROR_PTR("pixs2 and pixd must differ", __func__, pixd);
366 d = pixGetDepth(pixs1);
367 if (d != 8 && d != 16 && d != 32)
368 return (PIX *)ERROR_PTR("pix are not 8, 16 or 32 bpp", __func__, pixd);
369 if (pixGetDepth(pixs2) != d)
370 return (PIX *)ERROR_PTR("depths differ (pixs1, pixs2)", __func__, pixd);
371 if (pixd && (pixGetDepth(pixd) != d))
372 return (PIX *)ERROR_PTR("depths differ (pixs1, pixd)", __func__, pixd);
373
374 if (!pixSizesEqual(pixs1, pixs2))
375 L_WARNING("pixs1 and pixs2 not equal in size\n", __func__);
376 if (pixd && !pixSizesEqual(pixs1, pixd))
377 L_WARNING("pixs1 and pixd not equal in size\n", __func__);
378
379 if (pixs1 != pixd)
380 pixd = pixCopy(pixd, pixs1);
381
382 /* pixd - pixs2 ==> pixd */
383 datas = pixGetData(pixs2);
384 datad = pixGetData(pixd);
385 wpls = pixGetWpl(pixs2);
386 wpld = pixGetWpl(pixd);
387 pixGetDimensions(pixs2, &ws, &hs, NULL);
388 pixGetDimensions(pixd, &w, &h, NULL);
389 w = L_MIN(ws, w);
390 h = L_MIN(hs, h);
391 for (i = 0; i < h; i++) {
392 lined = datad + i * wpld;
393 lines = datas + i * wpls;
394 if (d == 8) {
395 for (j = 0; j < w; j++) {
396 diff = GET_DATA_BYTE(lined, j) - GET_DATA_BYTE(lines, j);
397 val = L_MAX(diff, 0);
398 SET_DATA_BYTE(lined, j, val);
399 }
400 } else if (d == 16) {
401 for (j = 0; j < w; j++) {
402 diff = GET_DATA_TWO_BYTES(lined, j)
403 - GET_DATA_TWO_BYTES(lines, j);
404 val = L_MAX(diff, 0);
405 SET_DATA_TWO_BYTES(lined, j, val);
406 }
407 } else { /* d == 32; no clipping */
408 for (j = 0; j < w; j++)
409 *(lined + j) -= *(lines + j);
410 }
411 }
412
413 return pixd;
414 }
415
416
417 /*!
418 * \brief pixMultiplyGray()
419 *
420 * \param[in] pixs 32 bpp rgb or 8 bpp gray
421 * \param[in] pixg 8 bpp gray
422 * \param[in] norm multiplicative factor to avoid overflow; 0 for default
423 * \return pixd, or null on error
424 *
425 * <pre>
426 * Notes:
427 * (1) This function can be used for correcting a scanned image
428 * under non-uniform illumination. For that application,
429 * %pixs is the scanned image, %pixg is an image whose values
430 * are inversely related to light from a uniform (say, white)
431 * target, and %norm is typically the inverse of the maximum
432 * pixel value in %pixg.
433 * (2) Set norm = 0 to get the default value, which is the inverse
434 * of the max value in %pixg. This avoids overflow in the product.
435 * (3) For 32 bpp %pixs, all 3 components are multiplied by the
436 * same number.
437 * (4) Alignment is to UL corner.
438 * </pre>
439 */
440 PIX *
441 pixMultiplyGray(PIX *pixs,
442 PIX *pixg,
443 l_float32 norm)
444 {
445 l_int32 i, j, w, h, d, ws, hs, ds, wpls, wplg, wpld;
446 l_int32 rval, gval, bval, rval2, gval2, bval2, vals, valg, val, maxgray;
447 l_uint32 val32;
448 l_uint32 *datas, *datag, *datad, *lines, *lineg, *lined;
449 PIX *pixd;
450
451 if (!pixs)
452 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
453 pixGetDimensions(pixs, &ws, &hs, &ds);
454 if (ds != 8 && ds != 32)
455 return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", __func__, NULL);
456 if (!pixg)
457 return (PIX *)ERROR_PTR("pixg not defined", __func__, NULL);
458 pixGetDimensions(pixg, &w, &h, &d);
459 if (d != 8)
460 return (PIX *)ERROR_PTR("pixg not 8 bpp", __func__, NULL);
461
462 if (norm <= 0.0) {
463 pixGetExtremeValue(pixg, 1, L_SELECT_MAX, NULL, NULL, NULL, &maxgray);
464 norm = (maxgray > 0) ? 1.0f / (l_float32)maxgray : 1.0f;
465 }
466
467 if ((pixd = pixCreateTemplate(pixs)) == NULL)
468 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
469 datas = pixGetData(pixs);
470 datag = pixGetData(pixg);
471 datad = pixGetData(pixd);
472 wpls = pixGetWpl(pixs);
473 wplg = pixGetWpl(pixg);
474 wpld = pixGetWpl(pixd);
475 w = L_MIN(ws, w);
476 h = L_MIN(hs, h);
477 for (i = 0; i < h; i++) {
478 lines = datas + i * wpls;
479 lineg = datag + i * wplg;
480 lined = datad + i * wpld;
481 if (ds == 8) {
482 for (j = 0; j < w; j++) {
483 vals = GET_DATA_BYTE(lines, j);
484 valg = GET_DATA_BYTE(lineg, j);
485 val = (l_int32)(vals * valg * norm + 0.5);
486 val = L_MIN(255, val);
487 SET_DATA_BYTE(lined, j, val);
488 }
489 } else { /* ds == 32 */
490 for (j = 0; j < w; j++) {
491 val32 = *(lines + j);
492 extractRGBValues(val32, &rval, &gval, &bval);
493 valg = GET_DATA_BYTE(lineg, j);
494 rval2 = (l_int32)(rval * valg * norm + 0.5);
495 rval2 = L_MIN(255, rval2);
496 gval2 = (l_int32)(gval * valg * norm + 0.5);
497 gval2 = L_MIN(255, gval2);
498 bval2 = (l_int32)(bval * valg * norm + 0.5);
499 bval2 = L_MIN(255, bval2);
500 composeRGBPixel(rval2, gval2, bval2, lined + j);
501 }
502 }
503 }
504
505 return pixd;
506 }
507
508
509 /*-------------------------------------------------------------*
510 * Grayscale threshold operation *
511 *-------------------------------------------------------------*/
512 /*!
513 * \brief pixThresholdToValue()
514 *
515 * \param[in] pixd [optional]; if not null, must be equal to pixs
516 * \param[in] pixs 8, 16, 32 bpp
517 * \param[in] threshval
518 * \param[in] setval
519 * \return pixd always
520 *
521 * <pre>
522 * Notes:
523 * ~ operation can be in-place (pixs == pixd) or to a new pixd
524 * ~ if %setval > %threshval, sets pixels with a value >= threshval to setval
525 * ~ if %setval < %threshval, sets pixels with a value <= threshval to setval
526 * ~ if %setval == %threshval, no-op
527 * </pre>
528 */
529 PIX *
530 pixThresholdToValue(PIX *pixd,
531 PIX *pixs,
532 l_int32 threshval,
533 l_int32 setval)
534 {
535 l_int32 i, j, w, h, d, wpld, setabove;
536 l_uint32 *datad, *lined;
537
538 if (!pixs)
539 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
540 d = pixGetDepth(pixs);
541 if (d != 8 && d != 16 && d != 32)
542 return (PIX *)ERROR_PTR("pixs not 8, 16 or 32 bpp", __func__, pixd);
543 if (pixd && (pixs != pixd))
544 return (PIX *)ERROR_PTR("pixd exists and is not pixs", __func__, pixd);
545 if (threshval < 0 || setval < 0)
546 return (PIX *)ERROR_PTR("threshval & setval not < 0", __func__, pixd);
547 if (d == 8 && setval > 255)
548 return (PIX *)ERROR_PTR("setval > 255 for 8 bpp", __func__, pixd);
549 if (d == 16 && setval > 0xffff)
550 return (PIX *)ERROR_PTR("setval > 0xffff for 16 bpp", __func__, pixd);
551
552 if (!pixd)
553 pixd = pixCopy(NULL, pixs);
554 if (setval == threshval) {
555 L_WARNING("setval == threshval; no operation\n", __func__);
556 return pixd;
557 }
558
559 datad = pixGetData(pixd);
560 pixGetDimensions(pixd, &w, &h, NULL);
561 wpld = pixGetWpl(pixd);
562 if (setval > threshval)
563 setabove = TRUE;
564 else
565 setabove = FALSE;
566
567 for (i = 0; i < h; i++) {
568 lined = datad + i * wpld;
569 if (setabove == TRUE) {
570 if (d == 8) {
571 for (j = 0; j < w; j++) {
572 if (GET_DATA_BYTE(lined, j) - threshval >= 0)
573 SET_DATA_BYTE(lined, j, setval);
574 }
575 } else if (d == 16) {
576 for (j = 0; j < w; j++) {
577 if (GET_DATA_TWO_BYTES(lined, j) - threshval >= 0)
578 SET_DATA_TWO_BYTES(lined, j, setval);
579 }
580 } else { /* d == 32 */
581 for (j = 0; j < w; j++) {
582 if (*(lined + j) >= threshval)
583 *(lined + j) = setval;
584 }
585 }
586 } else { /* set if below or at threshold */
587 if (d == 8) {
588 for (j = 0; j < w; j++) {
589 if (GET_DATA_BYTE(lined, j) - threshval <= 0)
590 SET_DATA_BYTE(lined, j, setval);
591 }
592 } else if (d == 16) {
593 for (j = 0; j < w; j++) {
594 if (GET_DATA_TWO_BYTES(lined, j) - threshval <= 0)
595 SET_DATA_TWO_BYTES(lined, j, setval);
596 }
597 } else { /* d == 32 */
598 for (j = 0; j < w; j++) {
599 if (*(lined + j) <= threshval)
600 *(lined + j) = setval;
601 }
602 }
603 }
604 }
605
606 return pixd;
607 }
608
609
610 /*-------------------------------------------------------------*
611 * Image accumulator arithmetic operations *
612 *-------------------------------------------------------------*/
613 /*!
614 * \brief pixInitAccumulate()
615 *
616 * \param[in] w, h of accumulate array
617 * \param[in] offset initialize the 32 bpp to have this
618 * value; not more than 0x40000000
619 * \return pixd 32 bpp, or NULL on error
620 *
621 * <pre>
622 * Notes:
623 * (1) %offset must be >= 0.
624 * (2) %offset is used so that we can do arithmetic
625 * with negative number results on l_uint32 data; it
626 * prevents the l_uint32 data from going negative.
627 * (3) Because we use l_int32 intermediate data results,
628 * these should never exceed the max of l_int32 (0x7fffffff).
629 * We do not permit the offset to be above 0x40000000,
630 * which is half way between 0 and the max of l_int32.
631 * (4) The same offset should be used for initialization,
632 * multiplication by a constant, and final extraction!
633 * (5) If you're only adding positive values, %offset can be 0.
634 * </pre>
635 */
636 PIX *
637 pixInitAccumulate(l_int32 w,
638 l_int32 h,
639 l_uint32 offset)
640 {
641 PIX *pixd;
642
643 if ((pixd = pixCreate(w, h, 32)) == NULL)
644 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
645 if (offset > 0x40000000)
646 offset = 0x40000000;
647 pixSetAllArbitrary(pixd, offset);
648 return pixd;
649 }
650
651
652 /*!
653 * \brief pixFinalAccumulate()
654 *
655 * \param[in] pixs 32 bpp
656 * \param[in] offset same as used for initialization
657 * \param[in] depth 8, 16 or 32 bpp, of destination
658 * \return pixd 8, 16 or 32 bpp, or NULL on error
659 *
660 * <pre>
661 * Notes:
662 * (1) %offset must be >= 0 and should not exceed 0x40000000.
663 * (2) %offset is subtracted from the src 32 bpp image
664 * (3) For 8 bpp dest, the result is clipped to [0, 0xff]
665 * (4) For 16 bpp dest, the result is clipped to [0, 0xffff]
666 * </pre>
667 */
668 PIX *
669 pixFinalAccumulate(PIX *pixs,
670 l_uint32 offset,
671 l_int32 depth)
672 {
673 l_int32 i, j, w, h, wpls, wpld, val;
674 l_uint32 *datas, *datad, *lines, *lined;
675 PIX *pixd;
676
677 if (!pixs)
678 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
679 if (pixGetDepth(pixs) != 32)
680 return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, NULL);
681 if (depth != 8 && depth != 16 && depth != 32)
682 return (PIX *)ERROR_PTR("dest depth not 8, 16, 32 bpp", __func__, NULL);
683 if (offset > 0x40000000)
684 offset = 0x40000000;
685
686 pixGetDimensions(pixs, &w, &h, NULL);
687 if ((pixd = pixCreate(w, h, depth)) == NULL)
688 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
689 pixCopyResolution(pixd, pixs); /* but how did pixs get it initially? */
690 datas = pixGetData(pixs);
691 datad = pixGetData(pixd);
692 wpls = pixGetWpl(pixs);
693 wpld = pixGetWpl(pixd);
694 if (depth == 8) {
695 for (i = 0; i < h; i++) {
696 lines = datas + i * wpls;
697 lined = datad + i * wpld;
698 for (j = 0; j < w; j++) {
699 val = lines[j] - offset;
700 val = L_MAX(0, val);
701 val = L_MIN(255, val);
702 SET_DATA_BYTE(lined, j, (l_uint8)val);
703 }
704 }
705 } else if (depth == 16) {
706 for (i = 0; i < h; i++) {
707 lines = datas + i * wpls;
708 lined = datad + i * wpld;
709 for (j = 0; j < w; j++) {
710 val = lines[j] - offset;
711 val = L_MAX(0, val);
712 val = L_MIN(0xffff, val);
713 SET_DATA_TWO_BYTES(lined, j, (l_uint16)val);
714 }
715 }
716 } else { /* depth == 32 */
717 for (i = 0; i < h; i++) {
718 lines = datas + i * wpls;
719 lined = datad + i * wpld;
720 for (j = 0; j < w; j++)
721 lined[j] = lines[j] - offset;
722 }
723 }
724
725 return pixd;
726 }
727
728
729 /*!
730 * \brief pixFinalAccumulateThreshold()
731 *
732 * \param[in] pixs 32 bpp
733 * \param[in] offset same as used for initialization
734 * \param[in] threshold values less than this are set in the destination
735 * \return pixd 1 bpp, or NULL on error
736 *
737 * <pre>
738 * Notes:
739 * (1) %offset must be >= 0 and should not exceed 0x40000000.
740 * (2) %offset is subtracted from the src 32 bpp image
741 * </pre>
742 */
743 PIX *
744 pixFinalAccumulateThreshold(PIX *pixs,
745 l_uint32 offset,
746 l_uint32 threshold)
747 {
748 l_int32 i, j, w, h, wpls, wpld, val;
749 l_uint32 *datas, *datad, *lines, *lined;
750 PIX *pixd;
751
752 if (!pixs)
753 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
754 if (pixGetDepth(pixs) != 32)
755 return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, NULL);
756 if (offset > 0x40000000)
757 offset = 0x40000000;
758
759 pixGetDimensions(pixs, &w, &h, NULL);
760 if ((pixd = pixCreate(w, h, 1)) == NULL)
761 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
762 pixCopyResolution(pixd, pixs); /* but how did pixs get it initially? */
763 datas = pixGetData(pixs);
764 datad = pixGetData(pixd);
765 wpls = pixGetWpl(pixs);
766 wpld = pixGetWpl(pixd);
767 for (i = 0; i < h; i++) {
768 lines = datas + i * wpls;
769 lined = datad + i * wpld;
770 for (j = 0; j < w; j++) {
771 val = lines[j] - offset;
772 if (val >= threshold) {
773 SET_DATA_BIT(lined, j);
774 }
775 }
776 }
777
778 return pixd;
779 }
780
781
782 /*!
783 * \brief pixAccumulate()
784 *
785 * \param[in] pixd 32 bpp
786 * \param[in] pixs 1, 8, 16 or 32 bpp
787 * \param[in] op L_ARITH_ADD or L_ARITH_SUBTRACT
788 * \return 0 if OK; 1 on error
789 *
790 * <pre>
791 * Notes:
792 * (1) This adds or subtracts each pixs value from pixd.
793 * (2) This clips to the minimum of pixs and pixd, so they
794 * do not need to be the same size.
795 * (3) The alignment is to the origin [UL corner] of pixs & pixd.
796 * </pre>
797 */
798 l_ok
799 pixAccumulate(PIX *pixd,
800 PIX *pixs,
801 l_int32 op)
802 {
803 l_int32 i, j, w, h, d, wd, hd, wpls, wpld;
804 l_uint32 *datas, *datad, *lines, *lined;
805
806
807 if (!pixd || (pixGetDepth(pixd) != 32))
808 return ERROR_INT("pixd not defined or not 32 bpp", __func__, 1);
809 if (!pixs)
810 return ERROR_INT("pixs not defined", __func__, 1);
811 d = pixGetDepth(pixs);
812 if (d != 1 && d != 8 && d != 16 && d != 32)
813 return ERROR_INT("pixs not 1, 8, 16 or 32 bpp", __func__, 1);
814 if (op != L_ARITH_ADD && op != L_ARITH_SUBTRACT)
815 return ERROR_INT("op must be in {L_ARITH_ADD, L_ARITH_SUBTRACT}",
816 __func__, 1);
817
818 datas = pixGetData(pixs);
819 datad = pixGetData(pixd);
820 wpls = pixGetWpl(pixs);
821 wpld = pixGetWpl(pixd);
822 pixGetDimensions(pixs, &w, &h, NULL);
823 pixGetDimensions(pixd, &wd, &hd, NULL);
824 w = L_MIN(w, wd);
825 h = L_MIN(h, hd);
826 if (d == 1) {
827 for (i = 0; i < h; i++) {
828 lines = datas + i * wpls;
829 lined = datad + i * wpld;
830 if (op == L_ARITH_ADD) {
831 for (j = 0; j < w; j++)
832 lined[j] += GET_DATA_BIT(lines, j);
833 } else { /* op == L_ARITH_SUBTRACT */
834 for (j = 0; j < w; j++)
835 lined[j] -= GET_DATA_BIT(lines, j);
836 }
837 }
838 } else if (d == 8) {
839 for (i = 0; i < h; i++) {
840 lines = datas + i * wpls;
841 lined = datad + i * wpld;
842 if (op == L_ARITH_ADD) {
843 for (j = 0; j < w; j++)
844 lined[j] += GET_DATA_BYTE(lines, j);
845 } else { /* op == L_ARITH_SUBTRACT */
846 for (j = 0; j < w; j++)
847 lined[j] -= GET_DATA_BYTE(lines, j);
848 }
849 }
850 } else if (d == 16) {
851 for (i = 0; i < h; i++) {
852 lines = datas + i * wpls;
853 lined = datad + i * wpld;
854 if (op == L_ARITH_ADD) {
855 for (j = 0; j < w; j++)
856 lined[j] += GET_DATA_TWO_BYTES(lines, j);
857 } else { /* op == L_ARITH_SUBTRACT */
858 for (j = 0; j < w; j++)
859 lined[j] -= GET_DATA_TWO_BYTES(lines, j);
860 }
861 }
862 } else { /* d == 32 */
863 for (i = 0; i < h; i++) {
864 lines = datas + i * wpls;
865 lined = datad + i * wpld;
866 if (op == L_ARITH_ADD) {
867 for (j = 0; j < w; j++)
868 lined[j] += lines[j];
869 } else { /* op == L_ARITH_SUBTRACT */
870 for (j = 0; j < w; j++)
871 lined[j] -= lines[j];
872 }
873 }
874 }
875
876 return 0;
877 }
878
879
880 /*!
881 * \brief pixMultConstAccumulate()
882 *
883 * \param[in] pixs 32 bpp
884 * \param[in] factor
885 * \param[in] offset same as used for initialization
886 * \return 0 if OK; 1 on error
887 *
888 * <pre>
889 * Notes:
890 * (1) %offset must be >= 0 and should not exceed 0x40000000.
891 * (2) This multiplies each pixel, relative to offset, by %factor.
892 * (3) The result is returned with %offset back in place.
893 * </pre>
894 */
895 l_ok
896 pixMultConstAccumulate(PIX *pixs,
897 l_float32 factor,
898 l_uint32 offset)
899 {
900 l_int32 i, j, w, h, wpl, val;
901 l_uint32 *data, *line;
902
903 if (!pixs)
904 return ERROR_INT("pixs not defined", __func__, 1);
905 if (pixGetDepth(pixs) != 32)
906 return ERROR_INT("pixs not 32 bpp", __func__, 1);
907 if (offset > 0x40000000)
908 offset = 0x40000000;
909
910 pixGetDimensions(pixs, &w, &h, NULL);
911 data = pixGetData(pixs);
912 wpl = pixGetWpl(pixs);
913 for (i = 0; i < h; i++) {
914 line = data + i * wpl;
915 for (j = 0; j < w; j++) {
916 val = line[j] - offset;
917 val = (l_int32)(val * factor);
918 val += offset;
919 line[j] = (l_uint32)val;
920 }
921 }
922
923 return 0;
924 }
925
926
927 /*-----------------------------------------------------------------------*
928 * Absolute value of difference *
929 *-----------------------------------------------------------------------*/
930 /*!
931 * \brief pixAbsDifference()
932 *
933 * \param[in] pixs1, pixs2 both either 8 or 16 bpp gray, or 32 bpp RGB
934 * \return pixd, or NULL on error
935 *
936 * <pre>
937 * Notes:
938 * (1) The depth of pixs1 and pixs2 must be equal.
939 * (2) Clips computation to the min size, aligning the UL corners
940 * (3) For 8 and 16 bpp, assumes one gray component.
941 * (4) For 32 bpp, assumes 3 color components, and ignores the
942 * LSB of each word (the alpha channel)
943 * (5) Computes the absolute value of the difference between
944 * each component value.
945 * </pre>
946 */
947 PIX *
948 pixAbsDifference(PIX *pixs1,
949 PIX *pixs2)
950 {
951 l_int32 i, j, w, h, w2, h2, d, wpls1, wpls2, wpld, val1, val2, diff;
952 l_int32 rval1, gval1, bval1, rval2, gval2, bval2, rdiff, gdiff, bdiff;
953 l_uint32 *datas1, *datas2, *datad, *lines1, *lines2, *lined;
954 PIX *pixd;
955
956 if (!pixs1)
957 return (PIX *)ERROR_PTR("pixs1 not defined", __func__, NULL);
958 if (!pixs2)
959 return (PIX *)ERROR_PTR("pixs2 not defined", __func__, NULL);
960 d = pixGetDepth(pixs1);
961 if (d != pixGetDepth(pixs2))
962 return (PIX *)ERROR_PTR("src1 and src2 depths unequal", __func__, NULL);
963 if (d != 8 && d != 16 && d != 32)
964 return (PIX *)ERROR_PTR("depths not in {8, 16, 32}", __func__, NULL);
965
966 pixGetDimensions(pixs1, &w, &h, NULL);
967 pixGetDimensions(pixs2, &w2, &h2, NULL);
968 w = L_MIN(w, w2);
969 h = L_MIN(h, h2);
970 if ((pixd = pixCreate(w, h, d)) == NULL)
971 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
972 pixCopyResolution(pixd, pixs1);
973 datas1 = pixGetData(pixs1);
974 datas2 = pixGetData(pixs2);
975 datad = pixGetData(pixd);
976 wpls1 = pixGetWpl(pixs1);
977 wpls2 = pixGetWpl(pixs2);
978 wpld = pixGetWpl(pixd);
979 if (d == 8) {
980 for (i = 0; i < h; i++) {
981 lines1 = datas1 + i * wpls1;
982 lines2 = datas2 + i * wpls2;
983 lined = datad + i * wpld;
984 for (j = 0; j < w; j++) {
985 val1 = GET_DATA_BYTE(lines1, j);
986 val2 = GET_DATA_BYTE(lines2, j);
987 diff = L_ABS(val1 - val2);
988 SET_DATA_BYTE(lined, j, diff);
989 }
990 }
991 } else if (d == 16) {
992 for (i = 0; i < h; i++) {
993 lines1 = datas1 + i * wpls1;
994 lines2 = datas2 + i * wpls2;
995 lined = datad + i * wpld;
996 for (j = 0; j < w; j++) {
997 val1 = GET_DATA_TWO_BYTES(lines1, j);
998 val2 = GET_DATA_TWO_BYTES(lines2, j);
999 diff = L_ABS(val1 - val2);
1000 SET_DATA_TWO_BYTES(lined, j, diff);
1001 }
1002 }
1003 } else { /* d == 32 */
1004 for (i = 0; i < h; i++) {
1005 lines1 = datas1 + i * wpls1;
1006 lines2 = datas2 + i * wpls2;
1007 lined = datad + i * wpld;
1008 for (j = 0; j < w; j++) {
1009 extractRGBValues(lines1[j], &rval1, &gval1, &bval1);
1010 extractRGBValues(lines2[j], &rval2, &gval2, &bval2);
1011 rdiff = L_ABS(rval1 - rval2);
1012 gdiff = L_ABS(gval1 - gval2);
1013 bdiff = L_ABS(bval1 - bval2);
1014 composeRGBPixel(rdiff, gdiff, bdiff, lined + j);
1015 }
1016 }
1017 }
1018
1019 return pixd;
1020 }
1021
1022
1023 /*-----------------------------------------------------------------------*
1024 * Sum of color images *
1025 *-----------------------------------------------------------------------*/
1026 /*!
1027 * \brief pixAddRGB()
1028 *
1029 * \param[in] pixs1, pixs2 32 bpp RGB, or colormapped
1030 * \return pixd, or NULL on error
1031 *
1032 * <pre>
1033 * Notes:
1034 * (1) Clips computation to the minimum size, aligning the UL corners.
1035 * (2) Removes any colormap to RGB, and ignores the LSB of each
1036 * pixel word (the alpha channel).
1037 * (3) Adds each component value, pixelwise, clipping to 255.
1038 * (4) This is useful to combine two images where most of the
1039 * pixels are essentially black, such as in pixPerceptualDiff().
1040 * </pre>
1041 */
1042 PIX *
1043 pixAddRGB(PIX *pixs1,
1044 PIX *pixs2)
1045 {
1046 l_int32 i, j, w, h, d, w2, h2, d2, wplc1, wplc2, wpld;
1047 l_int32 rval1, gval1, bval1, rval2, gval2, bval2, rval, gval, bval;
1048 l_uint32 *datac1, *datac2, *datad, *linec1, *linec2, *lined;
1049 PIX *pixc1, *pixc2, *pixd;
1050
1051 if (!pixs1)
1052 return (PIX *)ERROR_PTR("pixs1 not defined", __func__, NULL);
1053 if (!pixs2)
1054 return (PIX *)ERROR_PTR("pixs2 not defined", __func__, NULL);
1055 pixGetDimensions(pixs1, &w, &h, &d);
1056 pixGetDimensions(pixs2, &w2, &h2, &d2);
1057 if (!pixGetColormap(pixs1) && d != 32)
1058 return (PIX *)ERROR_PTR("pixs1 not cmapped or rgb", __func__, NULL);
1059 if (!pixGetColormap(pixs2) && d2 != 32)
1060 return (PIX *)ERROR_PTR("pixs2 not cmapped or rgb", __func__, NULL);
1061 if (pixGetColormap(pixs1))
1062 pixc1 = pixRemoveColormap(pixs1, REMOVE_CMAP_TO_FULL_COLOR);
1063 else
1064 pixc1 = pixClone(pixs1);
1065 if (pixGetColormap(pixs2))
1066 pixc2 = pixRemoveColormap(pixs2, REMOVE_CMAP_TO_FULL_COLOR);
1067 else
1068 pixc2 = pixClone(pixs2);
1069
1070 w = L_MIN(w, w2);
1071 h = L_MIN(h, h2);
1072 pixd = pixCreate(w, h, 32);
1073 pixCopyResolution(pixd, pixs1);
1074 datac1 = pixGetData(pixc1);
1075 datac2 = pixGetData(pixc2);
1076 datad = pixGetData(pixd);
1077 wplc1 = pixGetWpl(pixc1);
1078 wplc2 = pixGetWpl(pixc2);
1079 wpld = pixGetWpl(pixd);
1080 for (i = 0; i < h; i++) {
1081 linec1 = datac1 + i * wplc1;
1082 linec2 = datac2 + i * wplc2;
1083 lined = datad + i * wpld;
1084 for (j = 0; j < w; j++) {
1085 extractRGBValues(linec1[j], &rval1, &gval1, &bval1);
1086 extractRGBValues(linec2[j], &rval2, &gval2, &bval2);
1087 rval = L_MIN(255, rval1 + rval2);
1088 gval = L_MIN(255, gval1 + gval2);
1089 bval = L_MIN(255, bval1 + bval2);
1090 composeRGBPixel(rval, gval, bval, lined + j);
1091 }
1092 }
1093
1094 pixDestroy(&pixc1);
1095 pixDestroy(&pixc2);
1096 return pixd;
1097 }
1098
1099
1100 /*-----------------------------------------------------------------------*
1101 * Two-image min and max operations (8 and 16 bpp) *
1102 *-----------------------------------------------------------------------*/
1103 /*!
1104 * \brief pixMinOrMax()
1105 *
1106 * \param[in] pixd [optional] destination: this can be null,
1107 * equal to pixs1, or different from pixs1
1108 * \param[in] pixs1 can be equal to pixd
1109 * \param[in] pixs2
1110 * \param[in] type L_CHOOSE_MIN, L_CHOOSE_MAX
1111 * \return pixd always
1112 *
1113 * <pre>
1114 * Notes:
1115 * (1) This gives the min or max of two images, component-wise.
1116 * (2) The depth can be 8 or 16 bpp for 1 component, and 32 bpp
1117 * for a 3 component image. For 32 bpp, ignore the LSB
1118 * of each word (the alpha channel)
1119 * (3) There are 3 cases:
1120 * ~ if pixd == null, MinOrMax(src1, src2) --> new pixd
1121 * ~ if pixd == pixs1, MinOrMax(src1, src2) --> src1 (in-place)
1122 * ~ if pixd != pixs1, MinOrMax(src1, src2) --> input pixd
1123 * </pre>
1124 */
1125 PIX *
1126 pixMinOrMax(PIX *pixd,
1127 PIX *pixs1,
1128 PIX *pixs2,
1129 l_int32 type)
1130 {
1131 l_int32 d, ws, hs, w, h, wpls, wpld, i, j, vals, vald, val;
1132 l_int32 rval1, gval1, bval1, rval2, gval2, bval2, rval, gval, bval;
1133 l_uint32 *datas, *datad, *lines, *lined;
1134
1135 if (!pixs1)
1136 return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd);
1137 if (!pixs2)
1138 return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd);
1139 if (pixs1 == pixs2)
1140 return (PIX *)ERROR_PTR("pixs1 and pixs2 must differ", __func__, pixd);
1141 if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX)
1142 return (PIX *)ERROR_PTR("invalid type", __func__, pixd);
1143 d = pixGetDepth(pixs1);
1144 if (pixGetDepth(pixs2) != d)
1145 return (PIX *)ERROR_PTR("depths unequal", __func__, pixd);
1146 if (d != 8 && d != 16 && d != 32)
1147 return (PIX *)ERROR_PTR("depth not 8, 16 or 32 bpp", __func__, pixd);
1148
1149 if (pixs1 != pixd)
1150 pixd = pixCopy(pixd, pixs1);
1151
1152 pixGetDimensions(pixs2, &ws, &hs, NULL);
1153 pixGetDimensions(pixd, &w, &h, NULL);
1154 w = L_MIN(w, ws);
1155 h = L_MIN(h, hs);
1156 datas = pixGetData(pixs2);
1157 datad = pixGetData(pixd);
1158 wpls = pixGetWpl(pixs2);
1159 wpld = pixGetWpl(pixd);
1160 for (i = 0; i < h; i++) {
1161 lines = datas + i * wpls;
1162 lined = datad + i * wpld;
1163 if (d == 8) {
1164 for (j = 0; j < w; j++) {
1165 vals = GET_DATA_BYTE(lines, j);
1166 vald = GET_DATA_BYTE(lined, j);
1167 if (type == L_CHOOSE_MIN)
1168 val = L_MIN(vals, vald);
1169 else /* type == L_CHOOSE_MAX */
1170 val = L_MAX(vals, vald);
1171 SET_DATA_BYTE(lined, j, val);
1172 }
1173 } else if (d == 16) {
1174 for (j = 0; j < w; j++) {
1175 vals = GET_DATA_TWO_BYTES(lines, j);
1176 vald = GET_DATA_TWO_BYTES(lined, j);
1177 if (type == L_CHOOSE_MIN)
1178 val = L_MIN(vals, vald);
1179 else /* type == L_CHOOSE_MAX */
1180 val = L_MAX(vals, vald);
1181 SET_DATA_TWO_BYTES(lined, j, val);
1182 }
1183 } else { /* d == 32 */
1184 for (j = 0; j < w; j++) {
1185 extractRGBValues(lines[j], &rval1, &gval1, &bval1);
1186 extractRGBValues(lined[j], &rval2, &gval2, &bval2);
1187 if (type == L_CHOOSE_MIN) {
1188 rval = L_MIN(rval1, rval2);
1189 gval = L_MIN(gval1, gval2);
1190 bval = L_MIN(bval1, bval2);
1191 } else { /* type == L_CHOOSE_MAX */
1192 rval = L_MAX(rval1, rval2);
1193 gval = L_MAX(gval1, gval2);
1194 bval = L_MAX(bval1, bval2);
1195 }
1196 composeRGBPixel(rval, gval, bval, lined + j);
1197 }
1198 }
1199 }
1200
1201 return pixd;
1202 }
1203
1204
1205 /*-----------------------------------------------------------------------*
1206 * Scale for maximum dynamic range *
1207 *-----------------------------------------------------------------------*/
1208 /*!
1209 * \brief pixMaxDynamicRange()
1210 *
1211 * \param[in] pixs 4, 8, 16 or 32 bpp source
1212 * \param[in] type L_LINEAR_SCALE or L_LOG_SCALE
1213 * \return pixd 8 bpp, or NULL on error
1214 *
1215 * <pre>
1216 * Notes:
1217 * (1) Scales pixel values to fit maximally within the dest 8 bpp pixd
1218 * (2) Assumes the source 'pixels' are a 1-component scalar. For
1219 * a 32 bpp source, each pixel is treated as a single number --
1220 * not as a 3-component rgb pixel value.
1221 * (3) Uses a LUT for log scaling.
1222 * </pre>
1223 */
1224 PIX *
1225 pixMaxDynamicRange(PIX *pixs,
1226 l_int32 type)
1227 {
1228 l_uint8 dval;
1229 l_int32 i, j, w, h, d, wpls, wpld, max;
1230 l_uint32 *datas, *datad;
1231 l_uint32 word, sval;
1232 l_uint32 *lines, *lined;
1233 l_float32 factor;
1234 l_float32 *tab;
1235 PIX *pixd;
1236
1237 if (!pixs)
1238 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1239 pixGetDimensions(pixs, &w, &h, &d);
1240 if (d != 4 && d != 8 && d != 16 && d != 32)
1241 return (PIX *)ERROR_PTR("pixs not in {4,8,16,32} bpp", __func__, NULL);
1242 if (type != L_LINEAR_SCALE && type != L_LOG_SCALE)
1243 return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
1244
1245 if ((pixd = pixCreate(w, h, 8)) == NULL)
1246 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1247 pixCopyResolution(pixd, pixs);
1248 datas = pixGetData(pixs);
1249 datad = pixGetData(pixd);
1250 wpls = pixGetWpl(pixs);
1251 wpld = pixGetWpl(pixd);
1252
1253 /* Get max */
1254 max = 0;
1255 for (i = 0; i < h; i++) {
1256 lines = datas + i * wpls;
1257 for (j = 0; j < wpls; j++) {
1258 word = *(lines + j);
1259 if (d == 4) {
1260 max = L_MAX(max, word >> 28);
1261 max = L_MAX(max, (word >> 24) & 0xf);
1262 max = L_MAX(max, (word >> 20) & 0xf);
1263 max = L_MAX(max, (word >> 16) & 0xf);
1264 max = L_MAX(max, (word >> 12) & 0xf);
1265 max = L_MAX(max, (word >> 8) & 0xf);
1266 max = L_MAX(max, (word >> 4) & 0xf);
1267 max = L_MAX(max, word & 0xf);
1268 } else if (d == 8) {
1269 max = L_MAX(max, word >> 24);
1270 max = L_MAX(max, (word >> 16) & 0xff);
1271 max = L_MAX(max, (word >> 8) & 0xff);
1272 max = L_MAX(max, word & 0xff);
1273 } else if (d == 16) {
1274 max = L_MAX(max, word >> 16);
1275 max = L_MAX(max, word & 0xffff);
1276 } else { /* d == 32 (rgb) */
1277 max = L_MAX(max, word);
1278 }
1279 }
1280 }
1281
1282 /* Map to the full dynamic range */
1283 if (d == 4) {
1284 if (type == L_LINEAR_SCALE) {
1285 factor = 255.f / (l_float32)max;
1286 for (i = 0; i < h; i++) {
1287 lines = datas + i * wpls;
1288 lined = datad + i * wpld;
1289 for (j = 0; j < w; j++) {
1290 sval = GET_DATA_QBIT(lines, j);
1291 dval = (l_uint8)(factor * (l_float32)sval + 0.5);
1292 SET_DATA_QBIT(lined, j, dval);
1293 }
1294 }
1295 } else { /* type == L_LOG_SCALE) */
1296 tab = makeLogBase2Tab();
1297 factor = 255.f / getLogBase2(max, tab);
1298 for (i = 0; i < h; i++) {
1299 lines = datas + i * wpls;
1300 lined = datad + i * wpld;
1301 for (j = 0; j < w; j++) {
1302 sval = GET_DATA_QBIT(lines, j);
1303 dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5);
1304 SET_DATA_BYTE(lined, j, dval);
1305 }
1306 }
1307 LEPT_FREE(tab);
1308 }
1309 } else if (d == 8) {
1310 if (type == L_LINEAR_SCALE) {
1311 factor = 255.f / (l_float32)max;
1312 for (i = 0; i < h; i++) {
1313 lines = datas + i * wpls;
1314 lined = datad + i * wpld;
1315 for (j = 0; j < w; j++) {
1316 sval = GET_DATA_BYTE(lines, j);
1317 dval = (l_uint8)(factor * (l_float32)sval + 0.5);
1318 SET_DATA_BYTE(lined, j, dval);
1319 }
1320 }
1321 } else { /* type == L_LOG_SCALE) */
1322 tab = makeLogBase2Tab();
1323 factor = 255.f / getLogBase2(max, tab);
1324 for (i = 0; i < h; i++) {
1325 lines = datas + i * wpls;
1326 lined = datad + i * wpld;
1327 for (j = 0; j < w; j++) {
1328 sval = GET_DATA_BYTE(lines, j);
1329 dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5);
1330 SET_DATA_BYTE(lined, j, dval);
1331 }
1332 }
1333 LEPT_FREE(tab);
1334 }
1335 } else if (d == 16) {
1336 if (type == L_LINEAR_SCALE) {
1337 factor = 255.f / (l_float32)max;
1338 for (i = 0; i < h; i++) {
1339 lines = datas + i * wpls;
1340 lined = datad + i * wpld;
1341 for (j = 0; j < w; j++) {
1342 sval = GET_DATA_TWO_BYTES(lines, j);
1343 dval = (l_uint8)(factor * (l_float32)sval + 0.5);
1344 SET_DATA_BYTE(lined, j, dval);
1345 }
1346 }
1347 } else { /* type == L_LOG_SCALE) */
1348 tab = makeLogBase2Tab();
1349 factor = 255.f / getLogBase2(max, tab);
1350 for (i = 0; i < h; i++) {
1351 lines = datas + i * wpls;
1352 lined = datad + i * wpld;
1353 for (j = 0; j < w; j++) {
1354 sval = GET_DATA_TWO_BYTES(lines, j);
1355 dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5);
1356 SET_DATA_BYTE(lined, j, dval);
1357 }
1358 }
1359 LEPT_FREE(tab);
1360 }
1361 } else { /* d == 32 */
1362 if (type == L_LINEAR_SCALE) {
1363 factor = 255.f / (l_float32)max;
1364 for (i = 0; i < h; i++) {
1365 lines = datas + i * wpls;
1366 lined = datad + i * wpld;
1367 for (j = 0; j < w; j++) {
1368 sval = lines[j];
1369 dval = (l_uint8)(factor * (l_float32)sval + 0.5);
1370 SET_DATA_BYTE(lined, j, dval);
1371 }
1372 }
1373 } else { /* type == L_LOG_SCALE) */
1374 tab = makeLogBase2Tab();
1375 factor = 255.f / getLogBase2(max, tab);
1376 for (i = 0; i < h; i++) {
1377 lines = datas + i * wpls;
1378 lined = datad + i * wpld;
1379 for (j = 0; j < w; j++) {
1380 sval = lines[j];
1381 dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5);
1382 SET_DATA_BYTE(lined, j, dval);
1383 }
1384 }
1385 LEPT_FREE(tab);
1386 }
1387 }
1388
1389 return pixd;
1390 }
1391
1392
1393 /*!
1394 * \brief pixMaxDynamicRangeRGB()
1395 *
1396 * \param[in] pixs 32 bpp rgb source
1397 * \param[in] type L_LINEAR_SCALE or L_LOG_SCALE
1398 * \return pixd 32 bpp, or NULL on error
1399 *
1400 * <pre>
1401 * Notes:
1402 * (1) Scales pixel values to fit maximally within a 32 bpp dest pixd
1403 * (2) All color components are scaled with the same factor, based
1404 * on the maximum r, g or b component in the image. This should
1405 * not be used if the 32-bit value is a single number (e.g., a
1406 * count in a histogram generated by pixMakeHistoHS()).
1407 * (3) Uses a LUT for log scaling.
1408 * </pre>
1409 */
1410 PIX *
1411 pixMaxDynamicRangeRGB(PIX *pixs,
1412 l_int32 type)
1413 {
1414 l_int32 i, j, w, h, wpls, wpld, max;
1415 l_uint32 sval, dval, word;
1416 l_uint32 *datas, *datad;
1417 l_uint32 *lines, *lined;
1418 l_float32 factor;
1419 l_float32 *tab;
1420 PIX *pixd;
1421
1422 if (!pixs || pixGetDepth(pixs) != 32)
1423 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", __func__, NULL);
1424 if (type != L_LINEAR_SCALE && type != L_LOG_SCALE)
1425 return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
1426
1427 /* Get max */
1428 pixd = pixCreateTemplate(pixs);
1429 datas = pixGetData(pixs);
1430 datad = pixGetData(pixd);
1431 wpls = pixGetWpl(pixs);
1432 wpld = pixGetWpl(pixd);
1433 pixGetDimensions(pixs, &w, &h, NULL);
1434 max = 0;
1435 for (i = 0; i < h; i++) {
1436 lines = datas + i * wpls;
1437 for (j = 0; j < wpls; j++) {
1438 word = lines[j];
1439 max = L_MAX(max, word >> 24);
1440 max = L_MAX(max, (word >> 16) & 0xff);
1441 max = L_MAX(max, (word >> 8) & 0xff);
1442 }
1443 }
1444 if (max == 0) {
1445 L_WARNING("max = 0; setting to 1\n", __func__);
1446 max = 1;
1447 }
1448
1449 /* Map to the full dynamic range */
1450 if (type == L_LINEAR_SCALE) {
1451 factor = 255.f / (l_float32)max;
1452 for (i = 0; i < h; i++) {
1453 lines = datas + i * wpls;
1454 lined = datad + i * wpld;
1455 for (j = 0; j < w; j++) {
1456 sval = lines[j];
1457 dval = linearScaleRGBVal(sval, factor);
1458 lined[j] = dval;
1459 }
1460 }
1461 } else { /* type == L_LOG_SCALE) */
1462 tab = makeLogBase2Tab();
1463 factor = 255.f / getLogBase2(max, tab);
1464 for (i = 0; i < h; i++) {
1465 lines = datas + i * wpls;
1466 lined = datad + i * wpld;
1467 for (j = 0; j < w; j++) {
1468 sval = lines[j];
1469 dval = logScaleRGBVal(sval, tab, factor);
1470 lined[j] = dval;
1471 }
1472 }
1473 LEPT_FREE(tab);
1474 }
1475
1476 return pixd;
1477 }
1478
1479
1480 /*-----------------------------------------------------------------------*
1481 * RGB pixel value scaling *
1482 *-----------------------------------------------------------------------*/
1483 /*!
1484 * \brief linearScaleRGBVal()
1485 *
1486 * \param[in] sval 32-bit rgb pixel value
1487 * \param[in] factor multiplication factor on each component
1488 * \return dval linearly scaled version of %sval
1489 *
1490 * <pre>
1491 * Notes:
1492 * (1) %factor must be chosen to be not greater than (255 / maxcomp),
1493 * where maxcomp is the maximum value of the pixel components.
1494 * Otherwise, the product will overflow a uint8. In use, factor
1495 * is the same for all pixels in a pix.
1496 * (2) No scaling is performed on the transparency ("A") component.
1497 * </pre>
1498 */
1499 l_uint32
1500 linearScaleRGBVal(l_uint32 sval,
1501 l_float32 factor)
1502 {
1503 l_uint32 dval;
1504
1505 dval = ((l_uint8)(factor * (sval >> 24) + 0.5f) << 24) |
1506 ((l_uint8)(factor * ((sval >> 16) & 0xff) + 0.5f) << 16) |
1507 ((l_uint8)(factor * ((sval >> 8) & 0xff) + 0.5f) << 8) |
1508 (sval & 0xff);
1509 return dval;
1510 }
1511
1512
1513 /*!
1514 * \brief logScaleRGBVal()
1515 *
1516 * \param[in] sval 32-bit rgb pixel value
1517 * \param[in] tab 256 entry log-base-2 table
1518 * \param[in] factor multiplication factor on each component
1519 * \return dval log scaled version of %sval
1520 *
1521 * <pre>
1522 * Notes:
1523 * (1) %tab is made with makeLogBase2Tab().
1524 * (2) %factor must be chosen to be not greater than
1525 * 255.0 / log[base2](maxcomp), where maxcomp is the maximum
1526 * value of the pixel components. Otherwise, the product
1527 * will overflow a uint8. In use, factor is the same for
1528 * all pixels in a pix.
1529 * (3) No scaling is performed on the transparency ("A") component.
1530 * </pre>
1531 */
1532 l_uint32
1533 logScaleRGBVal(l_uint32 sval,
1534 l_float32 *tab,
1535 l_float32 factor)
1536 {
1537 l_uint32 dval;
1538
1539 dval = ((l_uint8)(factor * getLogBase2(sval >> 24, tab) + 0.5f) << 24) |
1540 ((l_uint8)(factor * getLogBase2(((sval >> 16) & 0xff), tab) + 0.5f)
1541 << 16) |
1542 ((l_uint8)(factor * getLogBase2(((sval >> 8) & 0xff), tab) + 0.5f)
1543 << 8) |
1544 (sval & 0xff);
1545 return dval;
1546 }
1547
1548
1549 /*-----------------------------------------------------------------------*
1550 * Log base2 lookup *
1551 *-----------------------------------------------------------------------*/
1552 /*
1553 * \brief makeLogBase2Tab()
1554 *
1555 * \return tab table giving the log[base2] of values from 1 to 255
1556 */
1557 l_float32 *
1558 makeLogBase2Tab(void)
1559 {
1560 l_int32 i;
1561 l_float32 log2;
1562 l_float32 *tab;
1563
1564 if ((tab = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32))) == NULL)
1565 return (l_float32 *)ERROR_PTR("tab not made", __func__, NULL);
1566
1567 log2 = (l_float32)log((l_float32)2);
1568 for (i = 0; i < 256; i++)
1569 tab[i] = (l_float32)log((l_float32)i) / log2;
1570
1571 return tab;
1572 }
1573
1574
1575 /*
1576 * \brief getLogBase2()
1577 *
1578 * \param[in] val in range [0 ... 255]
1579 * \param[in] logtab 256-entry table of logs
1580 * \return logval log[base2] of %val, or 0 on error
1581 */
1582 l_float32
1583 getLogBase2(l_int32 val,
1584 l_float32 *logtab)
1585 {
1586 if (!logtab)
1587 return ERROR_INT("logtab not defined", __func__, 0);
1588
1589 if (val < 0x100)
1590 return logtab[val];
1591 else if (val < 0x10000)
1592 return 8.0f + logtab[val >> 8];
1593 else if (val < 0x1000000)
1594 return 16.0f + logtab[val >> 16];
1595 else
1596 return 24.0f + logtab[val >> 24];
1597 }