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 colorspace.c
29 * <pre>
30 *
31 * Colorspace conversion between RGB and HSV
32 * PIX *pixConvertRGBToHSV()
33 * PIX *pixConvertHSVToRGB()
34 * l_int32 convertRGBToHSV()
35 * l_int32 convertHSVToRGB()
36 * l_int32 pixcmapConvertRGBToHSV()
37 * l_int32 pixcmapConvertHSVToRGB()
38 * PIX *pixConvertRGBToHue()
39 * PIX *pixConvertRGBToSaturation()
40 * PIX *pixConvertRGBToValue()
41 *
42 * Selection and display of range of colors in HSV space
43 * PIX *pixMakeRangeMaskHS()
44 * PIX *pixMakeRangeMaskHV()
45 * PIX *pixMakeRangeMaskSV()
46 * PIX *pixMakeHistoHS()
47 * PIX *pixMakeHistoHV()
48 * PIX *pixMakeHistoSV()
49 * PIX *pixFindHistoPeaksHSV()
50 * PIX *displayHSVColorRange()
51 *
52 * Colorspace conversion between RGB and YUV
53 * PIX *pixConvertRGBToYUV()
54 * PIX *pixConvertYUVToRGB()
55 * l_int32 convertRGBToYUV()
56 * l_int32 convertYUVToRGB()
57 * l_int32 pixcmapConvertRGBToYUV()
58 * l_int32 pixcmapConvertYUVToRGB()
59 *
60 * Colorspace conversion between RGB and XYZ
61 * FPIXA *pixConvertRGBToXYZ()
62 * PIX *fpixaConvertXYZToRGB()
63 * l_int32 convertRGBToXYZ()
64 * l_int32 convertXYZToRGB()
65 *
66 * Colorspace conversion between XYZ and LAB
67 * FPIXA *fpixaConvertXYZToLAB()
68 * PIX *fpixaConvertLABToXYZ()
69 * l_int32 convertXYZToLAB()
70 * l_int32 convertLABToXYZ()
71 * static l_float32 lab_forward()
72 * static l_float32 lab_reverse()
73 *
74 * Colorspace conversion between RGB and LAB
75 * FPIXA *pixConvertRGBToLAB()
76 * PIX *fpixaConvertLABToRGB()
77 * l_int32 convertRGBToLAB()
78 * l_int32 convertLABToRGB()
79 * </pre>
80 */
81
82 #include <string.h>
83 #include <math.h>
84 #include "allheaders.h"
85
86 #ifndef NO_CONSOLE_IO
87 #define DEBUG_HISTO 0
88 #define SLOW_CUBE_ROOT 0
89 #endif /* ~NO_CONSOLE_IO */
90
91 /* Functions used in xyz <--> lab conversions */
92 static l_float32 lab_forward(l_float32 v);
93 static l_float32 lab_reverse(l_float32 v);
94
95
96 /*---------------------------------------------------------------------------*
97 * Colorspace conversion between RGB and HSB *
98 *---------------------------------------------------------------------------*/
99 /*!
100 * \brief pixConvertRGBToHSV()
101 *
102 * \param[in] pixd can be NULL; if not NULL, must == pixs
103 * \param[in] pixs
104 * \return pixd always
105 *
106 * <pre>
107 * Notes:
108 * (1) For pixs = pixd, this is in-place; otherwise pixd must be NULL.
109 * (2) The definition of our HSV space is given in convertRGBToHSV().
110 * (3) The h, s and v values are stored in the same places as
111 * the r, g and b values, respectively. Here, they are explicitly
112 * placed in the 3 MS bytes in the pixel.
113 * (4) Normalizing to 1 and considering the r,g,b components,
114 * a simple way to understand the HSV space is:
115 * ~ v = max(r,g,b)
116 * ~ s = (max - min) / max
117 * ~ h ~ (mid - min) / (max - min) [apart from signs and constants]
118 * (5) Normalizing to 1, some properties of the HSV space are:
119 * ~ For gray values (r = g = b) along the continuum between
120 * black and white:
121 * s = 0 (becoming undefined as you approach black)
122 * h is undefined everywhere
123 * ~ Where one component is saturated and the others are zero:
124 * v = 1
125 * s = 1
126 * h = 0 (r = max), 1/3 (g = max), 2/3 (b = max)
127 * ~ Where two components are saturated and the other is zero:
128 * v = 1
129 * s = 1
130 * h = 1/2 (if r = 0), 5/6 (if g = 0), 1/6 (if b = 0)
131 * </pre>
132 */
133 PIX *
pixConvertRGBToHSV(PIX * pixd,PIX * pixs)134 pixConvertRGBToHSV(PIX *pixd,
135 PIX *pixs)
136 {
137 l_int32 w, h, d, wpl, i, j, rval, gval, bval, hval, sval, vval;
138 l_uint32 *line, *data;
139 PIXCMAP *cmap;
140
141 PROCNAME("pixConvertRGBToHSV");
142
143 if (!pixs)
144 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
145 if (pixd && pixd != pixs)
146 return (PIX *)ERROR_PTR("pixd defined and not inplace", procName, pixd);
147
148 d = pixGetDepth(pixs);
149 cmap = pixGetColormap(pixs);
150 if (!cmap && d != 32)
151 return (PIX *)ERROR_PTR("not cmapped or rgb", procName, pixd);
152
153 if (!pixd)
154 pixd = pixCopy(NULL, pixs);
155
156 cmap = pixGetColormap(pixd);
157 if (cmap) { /* just convert the colormap */
158 pixcmapConvertRGBToHSV(cmap);
159 return pixd;
160 }
161
162 /* Convert RGB image */
163 pixGetDimensions(pixd, &w, &h, NULL);
164 wpl = pixGetWpl(pixd);
165 data = pixGetData(pixd);
166 for (i = 0; i < h; i++) {
167 line = data + i * wpl;
168 for (j = 0; j < w; j++) {
169 extractRGBValues(line[j], &rval, &gval, &bval);
170 convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval);
171 line[j] = (hval << 24) | (sval << 16) | (vval << 8);
172 }
173 }
174
175 return pixd;
176 }
177
178
179 /*!
180 * \brief pixConvertHSVToRGB()
181 *
182 * \param[in] pixd can be NULL; if not NULL, must == pixs
183 * \param[in] pixs
184 * \return pixd always
185 *
186 * <pre>
187 * Notes:
188 * (1) For pixs = pixd, this is in-place; otherwise pixd must be NULL.
189 * (2) The user takes responsibility for making sure that pixs is
190 * in our HSV space. The definition of our HSV space is given
191 * in convertRGBToHSV().
192 * (3) The h, s and v values are stored in the same places as
193 * the r, g and b values, respectively. Here, they are explicitly
194 * placed in the 3 MS bytes in the pixel.
195 * </pre>
196 */
197 PIX *
pixConvertHSVToRGB(PIX * pixd,PIX * pixs)198 pixConvertHSVToRGB(PIX *pixd,
199 PIX *pixs)
200 {
201 l_int32 w, h, d, wpl, i, j, rval, gval, bval, hval, sval, vval;
202 l_uint32 pixel;
203 l_uint32 *line, *data;
204 PIXCMAP *cmap;
205
206 PROCNAME("pixConvertHSVToRGB");
207
208 if (!pixs)
209 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
210 if (pixd && pixd != pixs)
211 return (PIX *)ERROR_PTR("pixd defined and not inplace", procName, pixd);
212
213 d = pixGetDepth(pixs);
214 cmap = pixGetColormap(pixs);
215 if (!cmap && d != 32)
216 return (PIX *)ERROR_PTR("not cmapped or hsv", procName, pixd);
217
218 if (!pixd)
219 pixd = pixCopy(NULL, pixs);
220
221 cmap = pixGetColormap(pixd);
222 if (cmap) { /* just convert the colormap */
223 pixcmapConvertHSVToRGB(cmap);
224 return pixd;
225 }
226
227 /* Convert HSV image */
228 pixGetDimensions(pixd, &w, &h, NULL);
229 wpl = pixGetWpl(pixd);
230 data = pixGetData(pixd);
231 for (i = 0; i < h; i++) {
232 line = data + i * wpl;
233 for (j = 0; j < w; j++) {
234 pixel = line[j];
235 hval = pixel >> 24;
236 sval = (pixel >> 16) & 0xff;
237 vval = (pixel >> 8) & 0xff;
238 convertHSVToRGB(hval, sval, vval, &rval, &gval, &bval);
239 composeRGBPixel(rval, gval, bval, line + j);
240 }
241 }
242
243 return pixd;
244 }
245
246
247 /*!
248 * \brief convertRGBToHSV()
249 *
250 * \param[in] rval, gval, bval RGB input
251 * \param[out] phval, psval, pvval HSV values
252 * \return 0 if OK, 1 on error
253 *
254 * <pre>
255 * Notes:
256 * (1) The range of returned values is:
257 * h [0 ... 239]
258 * s [0 ... 255]
259 * v [0 ... 255]
260 * (2) If r = g = b, the pixel is gray (s = 0), and we define h = 0.
261 * (3) h wraps around, so that h = 0 and h = 240 are equivalent
262 * in hue space.
263 * (4) h has the following correspondence to color:
264 * h = 0 magenta
265 * h = 40 red
266 * h = 80 yellow
267 * h = 120 green
268 * h = 160 cyan
269 * h = 200 blue
270 * </pre>
271 */
272 l_int32
convertRGBToHSV(l_int32 rval,l_int32 gval,l_int32 bval,l_int32 * phval,l_int32 * psval,l_int32 * pvval)273 convertRGBToHSV(l_int32 rval,
274 l_int32 gval,
275 l_int32 bval,
276 l_int32 *phval,
277 l_int32 *psval,
278 l_int32 *pvval)
279 {
280 l_int32 minrg, maxrg, min, max, delta;
281 l_float32 h;
282
283 PROCNAME("convertRGBToHSV");
284
285 if (phval) *phval = 0;
286 if (psval) *psval = 0;
287 if (pvval) *pvval = 0;
288 if (!phval || !psval || !pvval)
289 return ERROR_INT("&hval, &sval, &vval not all defined", procName, 1);
290
291 minrg = L_MIN(rval, gval);
292 min = L_MIN(minrg, bval);
293 maxrg = L_MAX(rval, gval);
294 max = L_MAX(maxrg, bval);
295 delta = max - min;
296
297 *pvval = max;
298 if (delta == 0) { /* gray; no chroma */
299 *phval = 0;
300 *psval = 0;
301 } else {
302 *psval = (l_int32)(255. * (l_float32)delta / (l_float32)max + 0.5);
303 if (rval == max) /* between magenta and yellow */
304 h = (l_float32)(gval - bval) / (l_float32)delta;
305 else if (gval == max) /* between yellow and cyan */
306 h = 2. + (l_float32)(bval - rval) / (l_float32)delta;
307 else /* between cyan and magenta */
308 h = 4. + (l_float32)(rval - gval) / (l_float32)delta;
309 h *= 40.0;
310 if (h < 0.0)
311 h += 240.0;
312 if (h >= 239.5)
313 h = 0.0;
314 *phval = (l_int32)(h + 0.5);
315 }
316
317 return 0;
318 }
319
320
321 /*!
322 * \brief convertHSVToRGB()
323 *
324 * \param[in] hval, sval, vval
325 * \param[out] prval, pgval, pbval RGB values
326 * \return 0 if OK, 1 on error
327 *
328 * <pre>
329 * Notes:
330 * (1) See convertRGBToHSV() for valid input range of HSV values
331 * and their interpretation in color space.
332 * </pre>
333 */
334 l_int32
convertHSVToRGB(l_int32 hval,l_int32 sval,l_int32 vval,l_int32 * prval,l_int32 * pgval,l_int32 * pbval)335 convertHSVToRGB(l_int32 hval,
336 l_int32 sval,
337 l_int32 vval,
338 l_int32 *prval,
339 l_int32 *pgval,
340 l_int32 *pbval)
341 {
342 l_int32 i, x, y, z;
343 l_float32 h, f, s;
344
345 PROCNAME("convertHSVToRGB");
346
347 if (prval) *prval = 0;
348 if (pgval) *pgval = 0;
349 if (pbval) *pbval = 0;
350 if (!prval || !pgval || !pbval)
351 return ERROR_INT("&rval, &gval, &bval not all defined", procName, 1);
352
353 if (sval == 0) { /* gray */
354 *prval = vval;
355 *pgval = vval;
356 *pbval = vval;
357 } else {
358 if (hval < 0 || hval > 240)
359 return ERROR_INT("invalid hval", procName, 1);
360 if (hval == 240)
361 hval = 0;
362 h = (l_float32)hval / 40.;
363 i = (l_int32)h;
364 f = h - i;
365 s = (l_float32)sval / 255.;
366 x = (l_int32)(vval * (1. - s) + 0.5);
367 y = (l_int32)(vval * (1. - s * f) + 0.5);
368 z = (l_int32)(vval * (1. - s * (1. - f)) + 0.5);
369 switch (i)
370 {
371 case 0:
372 *prval = vval;
373 *pgval = z;
374 *pbval = x;
375 break;
376 case 1:
377 *prval = y;
378 *pgval = vval;
379 *pbval = x;
380 break;
381 case 2:
382 *prval = x;
383 *pgval = vval;
384 *pbval = z;
385 break;
386 case 3:
387 *prval = x;
388 *pgval = y;
389 *pbval = vval;
390 break;
391 case 4:
392 *prval = z;
393 *pgval = x;
394 *pbval = vval;
395 break;
396 case 5:
397 *prval = vval;
398 *pgval = x;
399 *pbval = y;
400 break;
401 default: /* none possible */
402 return 1;
403 }
404 }
405
406 return 0;
407 }
408
409
410 /*!
411 * \brief pixcmapConvertRGBToHSV()
412 *
413 * \param[in] cmap colormap
414 * \return 0 if OK; 1 on error
415 *
416 * <pre>
417 * Notes:
418 * ~ in-place transform
419 * ~ See convertRGBToHSV() for def'n of HSV space.
420 * ~ replaces: r --> h, g --> s, b --> v
421 * </pre>
422 */
423 l_int32
pixcmapConvertRGBToHSV(PIXCMAP * cmap)424 pixcmapConvertRGBToHSV(PIXCMAP *cmap)
425 {
426 l_int32 i, ncolors, rval, gval, bval, hval, sval, vval;
427
428 PROCNAME("pixcmapConvertRGBToHSV");
429
430 if (!cmap)
431 return ERROR_INT("cmap not defined", procName, 1);
432
433 ncolors = pixcmapGetCount(cmap);
434 for (i = 0; i < ncolors; i++) {
435 pixcmapGetColor(cmap, i, &rval, &gval, &bval);
436 convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval);
437 pixcmapResetColor(cmap, i, hval, sval, vval);
438 }
439 return 0;
440 }
441
442
443 /*!
444 * \brief pixcmapConvertHSVToRGB()
445 *
446 * \param[in] cmap colormap
447 * \return 0 if OK; 1 on error
448 *
449 * <pre>
450 * Notes:
451 * ~ in-place transform
452 * ~ See convertRGBToHSV() for def'n of HSV space.
453 * ~ replaces: h --> r, s --> g, v --> b
454 * </pre>
455 */
456 l_int32
pixcmapConvertHSVToRGB(PIXCMAP * cmap)457 pixcmapConvertHSVToRGB(PIXCMAP *cmap)
458 {
459 l_int32 i, ncolors, rval, gval, bval, hval, sval, vval;
460
461 PROCNAME("pixcmapConvertHSVToRGB");
462
463 if (!cmap)
464 return ERROR_INT("cmap not defined", procName, 1);
465
466 ncolors = pixcmapGetCount(cmap);
467 for (i = 0; i < ncolors; i++) {
468 pixcmapGetColor(cmap, i, &hval, &sval, &vval);
469 convertHSVToRGB(hval, sval, vval, &rval, &gval, &bval);
470 pixcmapResetColor(cmap, i, rval, gval, bval);
471 }
472 return 0;
473 }
474
475
476 /*!
477 * \brief pixConvertRGBToHue()
478 *
479 * \param[in] pixs 32 bpp RGB or 8 bpp with colormap
480 * \return pixd 8 bpp hue of HSV, or NULL on error
481 *
482 * <pre>
483 * Notes:
484 * (1) The conversion to HSV hue is in-lined here.
485 * (2) If there is a colormap, it is removed.
486 * (3) If you just want the hue component, this does it
487 * at about 10 Mpixels/sec/GHz, which is about
488 * 2x faster than using pixConvertRGBToHSV()
489 * </pre>
490 */
491 PIX *
pixConvertRGBToHue(PIX * pixs)492 pixConvertRGBToHue(PIX *pixs)
493 {
494 l_int32 w, h, d, wplt, wpld;
495 l_int32 i, j, rval, gval, bval, hval, minrg, min, maxrg, max, delta;
496 l_float32 fh;
497 l_uint32 pixel;
498 l_uint32 *linet, *lined, *datat, *datad;
499 PIX *pixt, *pixd;
500
501 PROCNAME("pixConvertRGBToHue");
502
503 if (!pixs)
504 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
505
506 pixGetDimensions(pixs, &w, &h, &d);
507 if (d != 32 && !pixGetColormap(pixs))
508 return (PIX *)ERROR_PTR("not cmapped or rgb", procName, NULL);
509 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR);
510
511 /* Convert RGB image */
512 pixd = pixCreate(w, h, 8);
513 pixCopyResolution(pixd, pixs);
514 wplt = pixGetWpl(pixt);
515 datat = pixGetData(pixt);
516 wpld = pixGetWpl(pixd);
517 datad = pixGetData(pixd);
518 for (i = 0; i < h; i++) {
519 linet = datat + i * wplt;
520 lined = datad + i * wpld;
521 for (j = 0; j < w; j++) {
522 pixel = linet[j];
523 extractRGBValues(pixel, &rval, &gval, &bval);
524 minrg = L_MIN(rval, gval);
525 min = L_MIN(minrg, bval);
526 maxrg = L_MAX(rval, gval);
527 max = L_MAX(maxrg, bval);
528 delta = max - min;
529 if (delta == 0) { /* gray; no chroma */
530 hval = 0;
531 } else {
532 if (rval == max) /* between magenta and yellow */
533 fh = (l_float32)(gval - bval) / (l_float32)delta;
534 else if (gval == max) /* between yellow and cyan */
535 fh = 2. + (l_float32)(bval - rval) / (l_float32)delta;
536 else /* between cyan and magenta */
537 fh = 4. + (l_float32)(rval - gval) / (l_float32)delta;
538 fh *= 40.0;
539 if (fh < 0.0)
540 fh += 240.0;
541 hval = (l_int32)(fh + 0.5);
542 }
543 SET_DATA_BYTE(lined, j, hval);
544 }
545 }
546 pixDestroy(&pixt);
547
548 return pixd;
549 }
550
551
552
553 /*!
554 * \brief pixConvertRGBToSaturation()
555 *
556 * \param[in] pixs 32 bpp RGB or 8 bpp with colormap
557 * \return pixd 8 bpp sat of HSV, or NULL on error
558 *
559 * <pre>
560 * Notes:
561 * (1) The conversion to HSV sat is in-lined here.
562 * (2) If there is a colormap, it is removed.
563 * (3) If you just want the saturation component, this does it
564 * at about 12 Mpixels/sec/GHz.
565 * </pre>
566 */
567 PIX *
pixConvertRGBToSaturation(PIX * pixs)568 pixConvertRGBToSaturation(PIX *pixs)
569 {
570 l_int32 w, h, d, wplt, wpld;
571 l_int32 i, j, rval, gval, bval, sval, minrg, min, maxrg, max, delta;
572 l_uint32 pixel;
573 l_uint32 *linet, *lined, *datat, *datad;
574 PIX *pixt, *pixd;
575
576 PROCNAME("pixConvertRGBToSaturation");
577
578 if (!pixs)
579 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
580
581 pixGetDimensions(pixs, &w, &h, &d);
582 if (d != 32 && !pixGetColormap(pixs))
583 return (PIX *)ERROR_PTR("not cmapped or rgb", procName, NULL);
584 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR);
585
586 /* Convert RGB image */
587 pixd = pixCreate(w, h, 8);
588 pixCopyResolution(pixd, pixs);
589 wplt = pixGetWpl(pixt);
590 datat = pixGetData(pixt);
591 wpld = pixGetWpl(pixd);
592 datad = pixGetData(pixd);
593 for (i = 0; i < h; i++) {
594 linet = datat + i * wplt;
595 lined = datad + i * wpld;
596 for (j = 0; j < w; j++) {
597 pixel = linet[j];
598 extractRGBValues(pixel, &rval, &gval, &bval);
599 minrg = L_MIN(rval, gval);
600 min = L_MIN(minrg, bval);
601 maxrg = L_MAX(rval, gval);
602 max = L_MAX(maxrg, bval);
603 delta = max - min;
604 if (delta == 0) /* gray; no chroma */
605 sval = 0;
606 else
607 sval = (l_int32)(255. *
608 (l_float32)delta / (l_float32)max + 0.5);
609 SET_DATA_BYTE(lined, j, sval);
610 }
611 }
612
613 pixDestroy(&pixt);
614 return pixd;
615 }
616
617
618 /*!
619 * \brief pixConvertRGBToValue()
620 *
621 * \param[in] pixs 32 bpp RGB or 8 bpp with colormap
622 * \return pixd 8 bpp max component intensity of HSV, or NULL on error
623 *
624 * <pre>
625 * Notes:
626 * (1) The conversion to HSV sat is in-lined here.
627 * (2) If there is a colormap, it is removed.
628 * (3) If you just want the value component, this does it
629 * at about 35 Mpixels/sec/GHz.
630 * </pre>
631 */
632 PIX *
pixConvertRGBToValue(PIX * pixs)633 pixConvertRGBToValue(PIX *pixs)
634 {
635 l_int32 w, h, d, wplt, wpld;
636 l_int32 i, j, rval, gval, bval, maxrg, max;
637 l_uint32 pixel;
638 l_uint32 *linet, *lined, *datat, *datad;
639 PIX *pixt, *pixd;
640
641 PROCNAME("pixConvertRGBToValue");
642
643 if (!pixs)
644 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
645
646 pixGetDimensions(pixs, &w, &h, &d);
647 if (d != 32 && !pixGetColormap(pixs))
648 return (PIX *)ERROR_PTR("not cmapped or rgb", procName, NULL);
649 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR);
650
651 /* Convert RGB image */
652 pixd = pixCreate(w, h, 8);
653 pixCopyResolution(pixd, pixs);
654 wplt = pixGetWpl(pixt);
655 datat = pixGetData(pixt);
656 wpld = pixGetWpl(pixd);
657 datad = pixGetData(pixd);
658 for (i = 0; i < h; i++) {
659 linet = datat + i * wplt;
660 lined = datad + i * wpld;
661 for (j = 0; j < w; j++) {
662 pixel = linet[j];
663 extractRGBValues(pixel, &rval, &gval, &bval);
664 maxrg = L_MAX(rval, gval);
665 max = L_MAX(maxrg, bval);
666 SET_DATA_BYTE(lined, j, max);
667 }
668 }
669
670 pixDestroy(&pixt);
671 return pixd;
672 }
673
674
675 /*---------------------------------------------------------------------------*
676 * Selection and display of range of colors in HSV space *
677 *---------------------------------------------------------------------------*/
678 /*!
679 * \brief pixMakeRangeMaskHS()
680 *
681 * \param[in] pixs 32 bpp rgb
682 * \param[in] huecenter center value of hue range
683 * \param[in] huehw half-width of hue range
684 * \param[in] satcenter center value of saturation range
685 * \param[in] sathw half-width of saturation range
686 * \param[in] regionflag L_INCLUDE_REGION, L_EXCLUDE_REGION
687 * \return pixd 1 bpp mask over selected pixels, or NULL on error
688 *
689 * <pre>
690 * Notes:
691 * (1) The pixels are selected based on the specified ranges of
692 * hue and saturation. For selection or exclusion, the pixel
693 * HS component values must be within both ranges. Care must
694 * be taken in finding the hue range because of wrap-around.
695 * (2) Use %regionflag == L_INCLUDE_REGION to take only those
696 * pixels within the rectangular region specified in HS space.
697 * Use %regionflag == L_EXCLUDE_REGION to take all pixels except
698 * those within the rectangular region specified in HS space.
699 * </pre>
700 */
701 PIX *
pixMakeRangeMaskHS(PIX * pixs,l_int32 huecenter,l_int32 huehw,l_int32 satcenter,l_int32 sathw,l_int32 regionflag)702 pixMakeRangeMaskHS(PIX *pixs,
703 l_int32 huecenter,
704 l_int32 huehw,
705 l_int32 satcenter,
706 l_int32 sathw,
707 l_int32 regionflag)
708 {
709 l_int32 i, j, w, h, wplt, wpld, hstart, hend, sstart, send, hval, sval;
710 l_int32 *hlut, *slut;
711 l_uint32 pixel;
712 l_uint32 *datat, *datad, *linet, *lined;
713 PIX *pixt, *pixd;
714
715 PROCNAME("pixMakeRangeMaskHS");
716
717 if (!pixs || pixGetDepth(pixs) != 32)
718 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
719 if (regionflag != L_INCLUDE_REGION && regionflag != L_EXCLUDE_REGION)
720 return (PIX *)ERROR_PTR("invalid regionflag", procName, NULL);
721
722 /* Set up LUTs for hue and saturation. These have the value 1
723 * within the specified intervals of hue and saturation. */
724 hlut = (l_int32 *)LEPT_CALLOC(240, sizeof(l_int32));
725 slut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
726 sstart = L_MAX(0, satcenter - sathw);
727 send = L_MIN(255, satcenter + sathw);
728 for (i = sstart; i <= send; i++)
729 slut[i] = 1;
730 hstart = (huecenter - huehw + 240) % 240;
731 hend = (huecenter + huehw + 240) % 240;
732 if (hstart < hend) {
733 for (i = hstart; i <= hend; i++)
734 hlut[i] = 1;
735 } else { /* wrap */
736 for (i = hstart; i < 240; i++)
737 hlut[i] = 1;
738 for (i = 0; i <= hend; i++)
739 hlut[i] = 1;
740 }
741
742 /* Generate the mask */
743 pixt = pixConvertRGBToHSV(NULL, pixs);
744 pixGetDimensions(pixs, &w, &h, NULL);
745 pixd = pixCreateNoInit(w, h, 1);
746 if (regionflag == L_INCLUDE_REGION)
747 pixClearAll(pixd);
748 else /* L_EXCLUDE_REGION */
749 pixSetAll(pixd);
750 datat = pixGetData(pixt);
751 datad = pixGetData(pixd);
752 wplt = pixGetWpl(pixt);
753 wpld = pixGetWpl(pixd);
754 for (i = 0; i < h; i++) {
755 linet = datat + i * wplt;
756 lined = datad + i * wpld;
757 for (j = 0; j < w; j++) {
758 pixel = linet[j];
759 hval = (pixel >> L_RED_SHIFT) & 0xff;
760 sval = (pixel >> L_GREEN_SHIFT) & 0xff;
761 if (hlut[hval] == 1 && slut[sval] == 1) {
762 if (regionflag == L_INCLUDE_REGION)
763 SET_DATA_BIT(lined, j);
764 else /* L_EXCLUDE_REGION */
765 CLEAR_DATA_BIT(lined, j);
766 }
767 }
768 }
769
770 LEPT_FREE(hlut);
771 LEPT_FREE(slut);
772 pixDestroy(&pixt);
773 return pixd;
774 }
775
776
777 /*!
778 * \brief pixMakeRangeMaskHV()
779 *
780 * \param[in] pixs 32 bpp rgb
781 * \param[in] huecenter center value of hue range
782 * \param[in] huehw half-width of hue range
783 * \param[in] valcenter center value of max intensity range
784 * \param[in] valhw half-width of max intensity range
785 * \param[in] regionflag L_INCLUDE_REGION, L_EXCLUDE_REGION
786 * \return pixd 1 bpp mask over selected pixels, or NULL on error
787 *
788 * <pre>
789 * Notes:
790 * (1) The pixels are selected based on the specified ranges of
791 * hue and max intensity values. For selection or exclusion,
792 * the pixel HV component values must be within both ranges.
793 * Care must be taken in finding the hue range because of wrap-around.
794 * (2) Use %regionflag == L_INCLUDE_REGION to take only those
795 * pixels within the rectangular region specified in HV space.
796 * Use %regionflag == L_EXCLUDE_REGION to take all pixels except
797 * those within the rectangular region specified in HV space.
798 * </pre>
799 */
800 PIX *
pixMakeRangeMaskHV(PIX * pixs,l_int32 huecenter,l_int32 huehw,l_int32 valcenter,l_int32 valhw,l_int32 regionflag)801 pixMakeRangeMaskHV(PIX *pixs,
802 l_int32 huecenter,
803 l_int32 huehw,
804 l_int32 valcenter,
805 l_int32 valhw,
806 l_int32 regionflag)
807 {
808 l_int32 i, j, w, h, wplt, wpld, hstart, hend, vstart, vend, hval, vval;
809 l_int32 *hlut, *vlut;
810 l_uint32 pixel;
811 l_uint32 *datat, *datad, *linet, *lined;
812 PIX *pixt, *pixd;
813
814 PROCNAME("pixMakeRangeMaskHV");
815
816 if (!pixs || pixGetDepth(pixs) != 32)
817 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
818 if (regionflag != L_INCLUDE_REGION && regionflag != L_EXCLUDE_REGION)
819 return (PIX *)ERROR_PTR("invalid regionflag", procName, NULL);
820
821 /* Set up LUTs for hue and maximum intensity (val). These have
822 * the value 1 within the specified intervals of hue and value. */
823 hlut = (l_int32 *)LEPT_CALLOC(240, sizeof(l_int32));
824 vlut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
825 vstart = L_MAX(0, valcenter - valhw);
826 vend = L_MIN(255, valcenter + valhw);
827 for (i = vstart; i <= vend; i++)
828 vlut[i] = 1;
829 hstart = (huecenter - huehw + 240) % 240;
830 hend = (huecenter + huehw + 240) % 240;
831 if (hstart < hend) {
832 for (i = hstart; i <= hend; i++)
833 hlut[i] = 1;
834 } else {
835 for (i = hstart; i < 240; i++)
836 hlut[i] = 1;
837 for (i = 0; i <= hend; i++)
838 hlut[i] = 1;
839 }
840
841 /* Generate the mask */
842 pixt = pixConvertRGBToHSV(NULL, pixs);
843 pixGetDimensions(pixs, &w, &h, NULL);
844 pixd = pixCreateNoInit(w, h, 1);
845 if (regionflag == L_INCLUDE_REGION)
846 pixClearAll(pixd);
847 else /* L_EXCLUDE_REGION */
848 pixSetAll(pixd);
849 datat = pixGetData(pixt);
850 datad = pixGetData(pixd);
851 wplt = pixGetWpl(pixt);
852 wpld = pixGetWpl(pixd);
853 for (i = 0; i < h; i++) {
854 linet = datat + i * wplt;
855 lined = datad + i * wpld;
856 for (j = 0; j < w; j++) {
857 pixel = linet[j];
858 hval = (pixel >> L_RED_SHIFT) & 0xff;
859 vval = (pixel >> L_BLUE_SHIFT) & 0xff;
860 if (hlut[hval] == 1 && vlut[vval] == 1) {
861 if (regionflag == L_INCLUDE_REGION)
862 SET_DATA_BIT(lined, j);
863 else /* L_EXCLUDE_REGION */
864 CLEAR_DATA_BIT(lined, j);
865 }
866 }
867 }
868
869 LEPT_FREE(hlut);
870 LEPT_FREE(vlut);
871 pixDestroy(&pixt);
872 return pixd;
873 }
874
875
876 /*!
877 * \brief pixMakeRangeMaskSV()
878 *
879 * \param[in] pixs 32 bpp rgb
880 * \param[in] satcenter center value of saturation range
881 * \param[in] sathw half-width of saturation range
882 * \param[in] valcenter center value of max intensity range
883 * \param[in] valhw half-width of max intensity range
884 * \param[in] regionflag L_INCLUDE_REGION, L_EXCLUDE_REGION
885 * \return pixd 1 bpp mask over selected pixels, or NULL on error
886 *
887 * <pre>
888 * Notes:
889 * (1) The pixels are selected based on the specified ranges of
890 * saturation and max intensity (val). For selection or
891 * exclusion, the pixel SV component values must be within both ranges.
892 * (2) Use %regionflag == L_INCLUDE_REGION to take only those
893 * pixels within the rectangular region specified in SV space.
894 * Use %regionflag == L_EXCLUDE_REGION to take all pixels except
895 * those within the rectangular region specified in SV space.
896 * </pre>
897 */
898 PIX *
pixMakeRangeMaskSV(PIX * pixs,l_int32 satcenter,l_int32 sathw,l_int32 valcenter,l_int32 valhw,l_int32 regionflag)899 pixMakeRangeMaskSV(PIX *pixs,
900 l_int32 satcenter,
901 l_int32 sathw,
902 l_int32 valcenter,
903 l_int32 valhw,
904 l_int32 regionflag)
905 {
906 l_int32 i, j, w, h, wplt, wpld, sval, vval, sstart, send, vstart, vend;
907 l_int32 *slut, *vlut;
908 l_uint32 pixel;
909 l_uint32 *datat, *datad, *linet, *lined;
910 PIX *pixt, *pixd;
911
912 PROCNAME("pixMakeRangeMaskSV");
913
914 if (!pixs || pixGetDepth(pixs) != 32)
915 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
916 if (regionflag != L_INCLUDE_REGION && regionflag != L_EXCLUDE_REGION)
917 return (PIX *)ERROR_PTR("invalid regionflag", procName, NULL);
918
919 /* Set up LUTs for saturation and max intensity (val).
920 * These have the value 1 within the specified intervals of
921 * saturation and max intensity. */
922 slut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
923 vlut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
924 sstart = L_MAX(0, satcenter - sathw);
925 send = L_MIN(255, satcenter + sathw);
926 vstart = L_MAX(0, valcenter - valhw);
927 vend = L_MIN(255, valcenter + valhw);
928 for (i = sstart; i <= send; i++)
929 slut[i] = 1;
930 for (i = vstart; i <= vend; i++)
931 vlut[i] = 1;
932
933 /* Generate the mask */
934 pixt = pixConvertRGBToHSV(NULL, pixs);
935 pixGetDimensions(pixs, &w, &h, NULL);
936 pixd = pixCreateNoInit(w, h, 1);
937 if (regionflag == L_INCLUDE_REGION)
938 pixClearAll(pixd);
939 else /* L_EXCLUDE_REGION */
940 pixSetAll(pixd);
941 datat = pixGetData(pixt);
942 datad = pixGetData(pixd);
943 wplt = pixGetWpl(pixt);
944 wpld = pixGetWpl(pixd);
945 for (i = 0; i < h; i++) {
946 linet = datat + i * wplt;
947 lined = datad + i * wpld;
948 for (j = 0; j < w; j++) {
949 pixel = linet[j];
950 sval = (pixel >> L_GREEN_SHIFT) & 0xff;
951 vval = (pixel >> L_BLUE_SHIFT) & 0xff;
952 if (slut[sval] == 1 && vlut[vval] == 1) {
953 if (regionflag == L_INCLUDE_REGION)
954 SET_DATA_BIT(lined, j);
955 else /* L_EXCLUDE_REGION */
956 CLEAR_DATA_BIT(lined, j);
957 }
958 }
959 }
960
961 LEPT_FREE(slut);
962 LEPT_FREE(vlut);
963 pixDestroy(&pixt);
964 return pixd;
965 }
966
967
968 /*!
969 * \brief pixMakeHistoHS()
970 *
971 * \param[in] pixs HSV colorspace
972 * \param[in] factor subsampling factor; integer
973 * \param[out] pnahue [optional] hue histogram
974 * \param[out] pnasat [optional] saturation histogram
975 * \return pixd 32 bpp histogram in hue and saturation, or NULL on error
976 *
977 * <pre>
978 * Notes:
979 * (1) pixs is a 32 bpp image in HSV colorspace; hue is in the "red"
980 * byte, saturation is in the "green" byte.
981 * (2) In pixd, hue is displayed vertically; saturation horizontally.
982 * The dimensions of pixd are w = 256, h = 240, and the depth
983 * is 32 bpp. The value at each point is simply the number
984 * of pixels found at that value of hue and saturation.
985 * </pre>
986 */
987 PIX *
pixMakeHistoHS(PIX * pixs,l_int32 factor,NUMA ** pnahue,NUMA ** pnasat)988 pixMakeHistoHS(PIX *pixs,
989 l_int32 factor,
990 NUMA **pnahue,
991 NUMA **pnasat)
992 {
993 l_int32 i, j, w, h, wplt, hval, sval, nd;
994 l_uint32 pixel;
995 l_uint32 *datat, *linet;
996 void **lined32;
997 NUMA *nahue, *nasat;
998 PIX *pixt, *pixd;
999
1000 PROCNAME("pixMakeHistoHS");
1001
1002 if (pnahue) *pnahue = NULL;
1003 if (pnasat) *pnasat = NULL;
1004 if (!pixs || pixGetDepth(pixs) != 32)
1005 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
1006
1007 if (pnahue) {
1008 nahue = numaCreate(240);
1009 numaSetCount(nahue, 240);
1010 *pnahue = nahue;
1011 }
1012 if (pnasat) {
1013 nasat = numaCreate(256);
1014 numaSetCount(nasat, 256);
1015 *pnasat = nasat;
1016 }
1017
1018 if (factor <= 1)
1019 pixt = pixClone(pixs);
1020 else
1021 pixt = pixScaleBySampling(pixs, 1.0 / (l_float32)factor,
1022 1.0 / (l_float32)factor);
1023
1024 /* Create the hue-saturation histogram */
1025 pixd = pixCreate(256, 240, 32);
1026 lined32 = pixGetLinePtrs(pixd, NULL);
1027 pixGetDimensions(pixt, &w, &h, NULL);
1028 datat = pixGetData(pixt);
1029 wplt = pixGetWpl(pixt);
1030 for (i = 0; i < h; i++) {
1031 linet = datat + i * wplt;
1032 for (j = 0; j < w; j++) {
1033 pixel = linet[j];
1034 hval = (pixel >> L_RED_SHIFT) & 0xff;
1035
1036 #if DEBUG_HISTO
1037 if (hval > 239) {
1038 fprintf(stderr, "hval = %d for (%d,%d)\n", hval, i, j);
1039 continue;
1040 }
1041 #endif /* DEBUG_HISTO */
1042
1043 sval = (pixel >> L_GREEN_SHIFT) & 0xff;
1044 if (pnahue)
1045 numaShiftValue(nahue, hval, 1.0);
1046 if (pnasat)
1047 numaShiftValue(nasat, sval, 1.0);
1048 nd = GET_DATA_FOUR_BYTES(lined32[hval], sval);
1049 SET_DATA_FOUR_BYTES(lined32[hval], sval, nd + 1);
1050 }
1051 }
1052
1053 LEPT_FREE(lined32);
1054 pixDestroy(&pixt);
1055 return pixd;
1056 }
1057
1058
1059 /*!
1060 * \brief pixMakeHistoHV()
1061 *
1062 * \param[in] pixs HSV colorspace
1063 * \param[in] factor subsampling factor; integer
1064 * \param[out] pnahue [optional] hue histogram
1065 * \param[out] pnaval [optional] max intensity (value) histogram
1066 * \return pixd 32 bpp histogram in hue and value, or NULL on error
1067 *
1068 * <pre>
1069 * Notes:
1070 * (1) pixs is a 32 bpp image in HSV colorspace; hue is in the "red"
1071 * byte, max intensity ("value") is in the "blue" byte.
1072 * (2) In pixd, hue is displayed vertically; intensity horizontally.
1073 * The dimensions of pixd are w = 256, h = 240, and the depth
1074 * is 32 bpp. The value at each point is simply the number
1075 * of pixels found at that value of hue and intensity.
1076 * </pre>
1077 */
1078 PIX *
pixMakeHistoHV(PIX * pixs,l_int32 factor,NUMA ** pnahue,NUMA ** pnaval)1079 pixMakeHistoHV(PIX *pixs,
1080 l_int32 factor,
1081 NUMA **pnahue,
1082 NUMA **pnaval)
1083 {
1084 l_int32 i, j, w, h, wplt, hval, vval, nd;
1085 l_uint32 pixel;
1086 l_uint32 *datat, *linet;
1087 void **lined32;
1088 NUMA *nahue, *naval;
1089 PIX *pixt, *pixd;
1090
1091 PROCNAME("pixMakeHistoHV");
1092
1093 if (pnahue) *pnahue = NULL;
1094 if (pnaval) *pnaval = NULL;
1095 if (!pixs || pixGetDepth(pixs) != 32)
1096 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
1097
1098 if (pnahue) {
1099 nahue = numaCreate(240);
1100 numaSetCount(nahue, 240);
1101 *pnahue = nahue;
1102 }
1103 if (pnaval) {
1104 naval = numaCreate(256);
1105 numaSetCount(naval, 256);
1106 *pnaval = naval;
1107 }
1108
1109 if (factor <= 1)
1110 pixt = pixClone(pixs);
1111 else
1112 pixt = pixScaleBySampling(pixs, 1.0 / (l_float32)factor,
1113 1.0 / (l_float32)factor);
1114
1115 /* Create the hue-value histogram */
1116 pixd = pixCreate(256, 240, 32);
1117 lined32 = pixGetLinePtrs(pixd, NULL);
1118 pixGetDimensions(pixt, &w, &h, NULL);
1119 datat = pixGetData(pixt);
1120 wplt = pixGetWpl(pixt);
1121 for (i = 0; i < h; i++) {
1122 linet = datat + i * wplt;
1123 for (j = 0; j < w; j++) {
1124 pixel = linet[j];
1125 hval = (pixel >> L_RED_SHIFT) & 0xff;
1126 vval = (pixel >> L_BLUE_SHIFT) & 0xff;
1127 if (pnahue)
1128 numaShiftValue(nahue, hval, 1.0);
1129 if (pnaval)
1130 numaShiftValue(naval, vval, 1.0);
1131 nd = GET_DATA_FOUR_BYTES(lined32[hval], vval);
1132 SET_DATA_FOUR_BYTES(lined32[hval], vval, nd + 1);
1133 }
1134 }
1135
1136 LEPT_FREE(lined32);
1137 pixDestroy(&pixt);
1138 return pixd;
1139 }
1140
1141
1142 /*!
1143 * \brief pixMakeHistoSV()
1144 *
1145 * \param[in] pixs HSV colorspace
1146 * \param[in] factor subsampling factor; integer
1147 * \param[out] pnasat [optional] sat histogram
1148 * \param[out] pnaval [optional] max intensity (value) histogram
1149 * \return pixd 32 bpp histogram in sat and value, or NULL on error
1150 *
1151 * <pre>
1152 * Notes:
1153 * (1) pixs is a 32 bpp image in HSV colorspace; sat is in the "green"
1154 * byte, max intensity ("value") is in the "blue" byte.
1155 * (2) In pixd, sat is displayed vertically; intensity horizontally.
1156 * The dimensions of pixd are w = 256, h = 256, and the depth
1157 * is 32 bpp. The value at each point is simply the number
1158 * of pixels found at that value of saturation and intensity.
1159 * </pre>
1160 */
1161 PIX *
pixMakeHistoSV(PIX * pixs,l_int32 factor,NUMA ** pnasat,NUMA ** pnaval)1162 pixMakeHistoSV(PIX *pixs,
1163 l_int32 factor,
1164 NUMA **pnasat,
1165 NUMA **pnaval)
1166 {
1167 l_int32 i, j, w, h, wplt, sval, vval, nd;
1168 l_uint32 pixel;
1169 l_uint32 *datat, *linet;
1170 void **lined32;
1171 NUMA *nasat, *naval;
1172 PIX *pixt, *pixd;
1173
1174 PROCNAME("pixMakeHistoSV");
1175
1176 if (pnasat) *pnasat = NULL;
1177 if (pnaval) *pnaval = NULL;
1178 if (!pixs || pixGetDepth(pixs) != 32)
1179 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
1180
1181 if (pnasat) {
1182 nasat = numaCreate(256);
1183 numaSetCount(nasat, 256);
1184 *pnasat = nasat;
1185 }
1186 if (pnaval) {
1187 naval = numaCreate(256);
1188 numaSetCount(naval, 256);
1189 *pnaval = naval;
1190 }
1191
1192 if (factor <= 1)
1193 pixt = pixClone(pixs);
1194 else
1195 pixt = pixScaleBySampling(pixs, 1.0 / (l_float32)factor,
1196 1.0 / (l_float32)factor);
1197
1198 /* Create the hue-value histogram */
1199 pixd = pixCreate(256, 256, 32);
1200 lined32 = pixGetLinePtrs(pixd, NULL);
1201 pixGetDimensions(pixt, &w, &h, NULL);
1202 datat = pixGetData(pixt);
1203 wplt = pixGetWpl(pixt);
1204 for (i = 0; i < h; i++) {
1205 linet = datat + i * wplt;
1206 for (j = 0; j < w; j++) {
1207 pixel = linet[j];
1208 sval = (pixel >> L_GREEN_SHIFT) & 0xff;
1209 vval = (pixel >> L_BLUE_SHIFT) & 0xff;
1210 if (pnasat)
1211 numaShiftValue(nasat, sval, 1.0);
1212 if (pnaval)
1213 numaShiftValue(naval, vval, 1.0);
1214 nd = GET_DATA_FOUR_BYTES(lined32[sval], vval);
1215 SET_DATA_FOUR_BYTES(lined32[sval], vval, nd + 1);
1216 }
1217 }
1218
1219 LEPT_FREE(lined32);
1220 pixDestroy(&pixt);
1221 return pixd;
1222 }
1223
1224
1225 /*!
1226 * \brief pixFindHistoPeaksHSV()
1227 *
1228 * \param[in] pixs 32 bpp; HS, HV or SV histogram; not changed
1229 * \param[in] type L_HS_HISTO, L_HV_HISTO or L_SV_HISTO
1230 * \param[in] width half width of sliding window
1231 * \param[in] height half height of sliding window
1232 * \param[in] npeaks number of peaks to look for
1233 * \param[in] erasefactor ratio of erase window size to sliding window size
1234 * \param[out] ppta locations of max for each integrated peak area
1235 * \param[out] pnatot integrated peak areas
1236 * \param[out] ppixa [optional] pixa for debugging; NULL to skip
1237 * \return 0 if OK, 1 on error
1238 *
1239 * <pre>
1240 * Notes:
1241 * (1) pixs is a 32 bpp histogram in a pair of HSV colorspace. It
1242 * should be thought of as a single sample with 32 bps (bits/sample).
1243 * (2) After each peak is found, the peak is erased with a window
1244 * that is centered on the peak and scaled from the sliding
1245 * window by %erasefactor. Typically, %erasefactor is chosen
1246 * to be > 1.0.
1247 * (3) Data for a maximum of %npeaks is returned in %pta and %natot.
1248 * (4) For debugging, after the pixa is returned, display with:
1249 * pixd = pixaDisplayTiledInRows(pixa, 32, 1000, 1.0, 0, 30, 2);
1250 * </pre>
1251 */
1252 l_int32
pixFindHistoPeaksHSV(PIX * pixs,l_int32 type,l_int32 width,l_int32 height,l_int32 npeaks,l_float32 erasefactor,PTA ** ppta,NUMA ** pnatot,PIXA ** ppixa)1253 pixFindHistoPeaksHSV(PIX *pixs,
1254 l_int32 type,
1255 l_int32 width,
1256 l_int32 height,
1257 l_int32 npeaks,
1258 l_float32 erasefactor,
1259 PTA **ppta,
1260 NUMA **pnatot,
1261 PIXA **ppixa)
1262 {
1263 l_int32 i, xmax, ymax, ewidth, eheight;
1264 l_uint32 maxval;
1265 BOX *box;
1266 NUMA *natot;
1267 PIX *pixh, *pixw, *pix1, *pix2, *pix3;
1268 PTA *pta;
1269
1270 PROCNAME("pixFindHistoPeaksHSV");
1271
1272 if (ppixa) *ppixa = NULL;
1273 if (ppta) *ppta = NULL;
1274 if (pnatot) *pnatot = NULL;
1275 if (!pixs || pixGetDepth(pixs) != 32)
1276 return ERROR_INT("pixs undefined or not 32 bpp", procName, 1);
1277 if (!ppta || !pnatot)
1278 return ERROR_INT("&pta and &natot not both defined", procName, 1);
1279 if (type != L_HS_HISTO && type != L_HV_HISTO && type != L_SV_HISTO)
1280 return ERROR_INT("invalid HSV histo type", procName, 1);
1281
1282 if ((pta = ptaCreate(npeaks)) == NULL)
1283 return ERROR_INT("pta not made", procName, 1);
1284 *ppta = pta;
1285 if ((natot = numaCreate(npeaks)) == NULL)
1286 return ERROR_INT("natot not made", procName, 1);
1287 *pnatot = natot;
1288
1289 *ppta = pta;
1290 if (type == L_SV_HISTO)
1291 pixh = pixAddMirroredBorder(pixs, width + 1, width + 1, height + 1,
1292 height + 1);
1293 else /* type == L_HS_HISTO or type == L_HV_HISTO */
1294 pixh = pixAddMixedBorder(pixs, width + 1, width + 1, height + 1,
1295 height + 1);
1296
1297 /* Get the total count in the sliding window. If the window
1298 * fully covers the peak, this will be the integrated
1299 * volume under the peak. */
1300 pixw = pixWindowedMean(pixh, width, height, 1, 0);
1301 pixDestroy(&pixh);
1302
1303 /* Sequentially identify and erase peaks in the histogram.
1304 * If requested for debugging, save a pixa of the sequence of
1305 * false color histograms. */
1306 if (ppixa)
1307 *ppixa = pixaCreate(0);
1308 for (i = 0; i < npeaks; i++) {
1309 pixGetMaxValueInRect(pixw, NULL, &maxval, &xmax, &ymax);
1310 if (maxval == 0) break;
1311 numaAddNumber(natot, maxval);
1312 ptaAddPt(pta, xmax, ymax);
1313 ewidth = (l_int32)(width * erasefactor);
1314 eheight = (l_int32)(height * erasefactor);
1315 box = boxCreate(xmax - ewidth, ymax - eheight, 2 * ewidth + 1,
1316 2 * eheight + 1);
1317
1318 if (ppixa) {
1319 pix1 = pixMaxDynamicRange(pixw, L_LINEAR_SCALE);
1320 pixaAddPix(*ppixa, pix1, L_INSERT);
1321 pix2 = pixConvertGrayToFalseColor(pix1, 1.0);
1322 pixaAddPix(*ppixa, pix2, L_INSERT);
1323 pix1 = pixMaxDynamicRange(pixw, L_LOG_SCALE);
1324 pix2 = pixConvertGrayToFalseColor(pix1, 1.0);
1325 pixaAddPix(*ppixa, pix2, L_INSERT);
1326 pix3 = pixConvertTo32(pix1);
1327 pixRenderHashBoxArb(pix3, box, 6, 2, L_NEG_SLOPE_LINE,
1328 1, 255, 100, 100);
1329 pixaAddPix(*ppixa, pix3, L_INSERT);
1330 pixDestroy(&pix1);
1331 }
1332
1333 pixClearInRect(pixw, box);
1334 boxDestroy(&box);
1335 if (type == L_HS_HISTO || type == L_HV_HISTO) {
1336 /* clear wraps at bottom and top */
1337 if (ymax - eheight < 0) { /* overlap to bottom */
1338 box = boxCreate(xmax - ewidth, 240 + ymax - eheight,
1339 2 * ewidth + 1, eheight - ymax);
1340 } else if (ymax + eheight > 239) { /* overlap to top */
1341 box = boxCreate(xmax - ewidth, 0, 2 * ewidth + 1,
1342 ymax + eheight - 239);
1343 } else {
1344 box = NULL;
1345 }
1346 if (box) {
1347 pixClearInRect(pixw, box);
1348 boxDestroy(&box);
1349 }
1350 }
1351 }
1352
1353 pixDestroy(&pixw);
1354 return 0;
1355 }
1356
1357
1358 /*!
1359 * \brief displayHSVColorRange()
1360 *
1361 * \param[in] hval hue center value; in range [0 ... 240]
1362 * \param[in] sval saturation center value; in range [0 ... 255]
1363 * \param[in] vval max intensity value; in range [0 ... 255]
1364 * \param[in] huehw half-width of hue range; > 0
1365 * \param[in] sathw half-width of saturation range; > 0
1366 * \param[in] nsamp number of samplings in each half-width in hue and sat
1367 * \param[in] factor linear size of each color square, in pixels; > 3
1368 * \return pixd 32 bpp set of color squares over input range,
1369 * or NULL on error
1370 *
1371 * <pre>
1372 * Notes:
1373 * (1) The total number of color samplings in each of the hue
1374 * and saturation directions is 2 * nsamp + 1.
1375 * </pre>
1376 */
1377 PIX *
displayHSVColorRange(l_int32 hval,l_int32 sval,l_int32 vval,l_int32 huehw,l_int32 sathw,l_int32 nsamp,l_int32 factor)1378 displayHSVColorRange(l_int32 hval,
1379 l_int32 sval,
1380 l_int32 vval,
1381 l_int32 huehw,
1382 l_int32 sathw,
1383 l_int32 nsamp,
1384 l_int32 factor)
1385 {
1386 l_int32 i, j, w, huedelta, satdelta, hue, sat, rval, gval, bval;
1387 PIX *pixt, *pixd;
1388
1389 PROCNAME("displayHSVColorRange");
1390
1391 if (hval < 0 || hval > 240)
1392 return (PIX *)ERROR_PTR("invalid hval", procName, NULL);
1393 if (huehw < 5 || huehw > 120)
1394 return (PIX *)ERROR_PTR("invalid huehw", procName, NULL);
1395 if (sval - sathw < 0 || sval + sathw > 255)
1396 return (PIX *)ERROR_PTR("invalid sval/sathw", procName, NULL);
1397 if (nsamp < 1 || factor < 3)
1398 return (PIX *)ERROR_PTR("invalid nsamp or rep. factor", procName, NULL);
1399 if (vval < 0 || vval > 255)
1400 return (PIX *)ERROR_PTR("invalid vval", procName, NULL);
1401
1402 w = (2 * nsamp + 1);
1403 huedelta = (l_int32)((l_float32)huehw / (l_float32)nsamp);
1404 satdelta = (l_int32)((l_float32)sathw / (l_float32)nsamp);
1405 pixt = pixCreate(w, w, 32);
1406 for (i = 0; i < w; i++) {
1407 hue = hval + huedelta * (i - nsamp);
1408 if (hue < 0) hue += 240;
1409 if (hue >= 240) hue -= 240;
1410 for (j = 0; j < w; j++) {
1411 sat = sval + satdelta * (j - nsamp);
1412 convertHSVToRGB(hue, sat, vval, &rval, &gval, &bval);
1413 pixSetRGBPixel(pixt, j, i, rval, gval, bval);
1414 }
1415 }
1416
1417 pixd = pixExpandReplicate(pixt, factor);
1418 pixDestroy(&pixt);
1419 return pixd;
1420 }
1421
1422
1423 /*---------------------------------------------------------------------------*
1424 * Colorspace conversion between RGB and YUV *
1425 *---------------------------------------------------------------------------*/
1426 /*!
1427 * \brief pixConvertRGBToYUV()
1428 *
1429 * \param[in] pixd can be NULL; if not NULL, must == pixs
1430 * \param[in] pixs
1431 * \return pixd always
1432 *
1433 * <pre>
1434 * Notes:
1435 * (1) For pixs = pixd, this is in-place; otherwise pixd must be NULL.
1436 * (2) The Y, U and V values are stored in the same places as
1437 * the r, g and b values, respectively. Here, they are explicitly
1438 * placed in the 3 MS bytes in the pixel.
1439 * (3) Normalizing to 1 and considering the r,g,b components,
1440 * a simple way to understand the YUV space is:
1441 * ~ Y = weighted sum of (r,g,b)
1442 * ~ U = weighted difference between Y and B
1443 * ~ V = weighted difference between Y and R
1444 * (4) Following video conventions, Y, U and V are in the range:
1445 * Y: [16, 235]
1446 * U: [16, 240]
1447 * V: [16, 240]
1448 * (5) For the coefficients in the transform matrices, see eq. 4 in
1449 * "Frequently Asked Questions about Color" by Charles Poynton,
1450 * //http://user.engineering.uiowa.edu/~aip/Misc/ColorFAQ.html
1451 * </pre>
1452 */
1453 PIX *
pixConvertRGBToYUV(PIX * pixd,PIX * pixs)1454 pixConvertRGBToYUV(PIX *pixd,
1455 PIX *pixs)
1456 {
1457 l_int32 w, h, d, wpl, i, j, rval, gval, bval, yval, uval, vval;
1458 l_uint32 *line, *data;
1459 PIXCMAP *cmap;
1460
1461 PROCNAME("pixConvertRGBToYUV");
1462
1463 if (!pixs)
1464 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
1465 if (pixd && pixd != pixs)
1466 return (PIX *)ERROR_PTR("pixd defined and not inplace", procName, pixd);
1467
1468 d = pixGetDepth(pixs);
1469 cmap = pixGetColormap(pixs);
1470 if (!cmap && d != 32)
1471 return (PIX *)ERROR_PTR("not cmapped or rgb", procName, pixd);
1472
1473 if (!pixd)
1474 pixd = pixCopy(NULL, pixs);
1475
1476 cmap = pixGetColormap(pixd);
1477 if (cmap) { /* just convert the colormap */
1478 pixcmapConvertRGBToYUV(cmap);
1479 return pixd;
1480 }
1481
1482 /* Convert RGB image */
1483 pixGetDimensions(pixd, &w, &h, NULL);
1484 wpl = pixGetWpl(pixd);
1485 data = pixGetData(pixd);
1486 for (i = 0; i < h; i++) {
1487 line = data + i * wpl;
1488 for (j = 0; j < w; j++) {
1489 extractRGBValues(line[j], &rval, &gval, &bval);
1490 convertRGBToYUV(rval, gval, bval, &yval, &uval, &vval);
1491 line[j] = (yval << 24) | (uval << 16) | (vval << 8);
1492 }
1493 }
1494
1495 return pixd;
1496 }
1497
1498
1499 /*!
1500 * \brief pixConvertYUVToRGB()
1501 *
1502 * \param[in] pixd can be NULL; if not NULL, must == pixs
1503 * \param[in] pixs
1504 * \return pixd always
1505 *
1506 * <pre>
1507 * Notes:
1508 * (1) For pixs = pixd, this is in-place; otherwise pixd must be NULL.
1509 * (2) The user takes responsibility for making sure that pixs is
1510 * in YUV space.
1511 * (3) The Y, U and V values are stored in the same places as
1512 * the r, g and b values, respectively. Here, they are explicitly
1513 * placed in the 3 MS bytes in the pixel.
1514 * </pre>
1515 */
1516 PIX *
pixConvertYUVToRGB(PIX * pixd,PIX * pixs)1517 pixConvertYUVToRGB(PIX *pixd,
1518 PIX *pixs)
1519 {
1520 l_int32 w, h, d, wpl, i, j, rval, gval, bval, yval, uval, vval;
1521 l_uint32 pixel;
1522 l_uint32 *line, *data;
1523 PIXCMAP *cmap;
1524
1525 PROCNAME("pixConvertYUVToRGB");
1526
1527 if (!pixs)
1528 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
1529 if (pixd && pixd != pixs)
1530 return (PIX *)ERROR_PTR("pixd defined and not inplace", procName, pixd);
1531
1532 d = pixGetDepth(pixs);
1533 cmap = pixGetColormap(pixs);
1534 if (!cmap && d != 32)
1535 return (PIX *)ERROR_PTR("not cmapped or hsv", procName, pixd);
1536
1537 if (!pixd)
1538 pixd = pixCopy(NULL, pixs);
1539
1540 cmap = pixGetColormap(pixd);
1541 if (cmap) { /* just convert the colormap */
1542 pixcmapConvertYUVToRGB(cmap);
1543 return pixd;
1544 }
1545
1546 /* Convert YUV image */
1547 pixGetDimensions(pixd, &w, &h, NULL);
1548 wpl = pixGetWpl(pixd);
1549 data = pixGetData(pixd);
1550 for (i = 0; i < h; i++) {
1551 line = data + i * wpl;
1552 for (j = 0; j < w; j++) {
1553 pixel = line[j];
1554 yval = pixel >> 24;
1555 uval = (pixel >> 16) & 0xff;
1556 vval = (pixel >> 8) & 0xff;
1557 convertYUVToRGB(yval, uval, vval, &rval, &gval, &bval);
1558 composeRGBPixel(rval, gval, bval, line + j);
1559 }
1560 }
1561
1562 return pixd;
1563 }
1564
1565
1566 /*!
1567 * \brief convertRGBToYUV()
1568 *
1569 * \param[in] rval, gval, bval RGB input
1570 * \param[out] pyval, puval, pvval YUV values
1571 * \return 0 if OK, 1 on error
1572 *
1573 * <pre>
1574 * Notes:
1575 * (1) The range of returned values is:
1576 * Y [16 ... 235]
1577 * U [16 ... 240]
1578 * V [16 ... 240]
1579 * </pre>
1580 */
1581 l_int32
convertRGBToYUV(l_int32 rval,l_int32 gval,l_int32 bval,l_int32 * pyval,l_int32 * puval,l_int32 * pvval)1582 convertRGBToYUV(l_int32 rval,
1583 l_int32 gval,
1584 l_int32 bval,
1585 l_int32 *pyval,
1586 l_int32 *puval,
1587 l_int32 *pvval)
1588 {
1589 l_float32 norm;
1590
1591 PROCNAME("convertRGBToYUV");
1592
1593 if (pyval) *pyval = 0;
1594 if (puval) *puval = 0;
1595 if (pvval) *pvval = 0;
1596 if (!pyval || !puval || !pvval)
1597 return ERROR_INT("&yval, &uval, &vval not all defined", procName, 1);
1598
1599 norm = 1.0 / 256.;
1600 *pyval = (l_int32)(16.0 +
1601 norm * (65.738 * rval + 129.057 * gval + 25.064 * bval) + 0.5);
1602 *puval = (l_int32)(128.0 +
1603 norm * (-37.945 * rval -74.494 * gval + 112.439 * bval) + 0.5);
1604 *pvval = (l_int32)(128.0 +
1605 norm * (112.439 * rval - 94.154 * gval - 18.285 * bval) + 0.5);
1606 return 0;
1607 }
1608
1609
1610 /*!
1611 * \brief convertYUVToRGB()
1612 *
1613 * \param[in] yval, uval, vval
1614 * \param[out] prval, pgval, pbval RGB values
1615 * \return 0 if OK, 1 on error
1616 *
1617 * <pre>
1618 * Notes:
1619 * (1) The range of valid input values is:
1620 * Y [16 ... 235]
1621 * U [16 ... 240]
1622 * V [16 ... 240]
1623 * (2) Conversion of RGB --> YUV --> RGB leaves the image unchanged.
1624 * (3) The YUV gamut is larger than the RBG gamut; many YUV values
1625 * will result in an invalid RGB value. We clip individual
1626 * r,g,b components to the range [0, 255], and do not test input.
1627 * </pre>
1628 */
1629 l_int32
convertYUVToRGB(l_int32 yval,l_int32 uval,l_int32 vval,l_int32 * prval,l_int32 * pgval,l_int32 * pbval)1630 convertYUVToRGB(l_int32 yval,
1631 l_int32 uval,
1632 l_int32 vval,
1633 l_int32 *prval,
1634 l_int32 *pgval,
1635 l_int32 *pbval)
1636 {
1637 l_int32 rval, gval, bval;
1638 l_float32 norm, ym, um, vm;
1639
1640 PROCNAME("convertYUVToRGB");
1641
1642 if (prval) *prval = 0;
1643 if (pgval) *pgval = 0;
1644 if (pbval) *pbval = 0;
1645 if (!prval || !pgval || !pbval)
1646 return ERROR_INT("&rval, &gval, &bval not all defined", procName, 1);
1647
1648 norm = 1.0 / 256.;
1649 ym = yval - 16.0;
1650 um = uval - 128.0;
1651 vm = vval - 128.0;
1652 rval = (l_int32)(norm * (298.082 * ym + 408.583 * vm) + 0.5);
1653 gval = (l_int32)(norm * (298.082 * ym - 100.291 * um - 208.120 * vm) +
1654 0.5);
1655 bval = (l_int32)(norm * (298.082 * ym + 516.411 * um) + 0.5);
1656 *prval = L_MIN(255, L_MAX(0, rval));
1657 *pgval = L_MIN(255, L_MAX(0, gval));
1658 *pbval = L_MIN(255, L_MAX(0, bval));
1659
1660 return 0;
1661 }
1662
1663
1664 /*!
1665 * \brief pixcmapConvertRGBToYUV()
1666 *
1667 * \param[in] cmap colormap
1668 * \return 0 if OK; 1 on error
1669 *
1670 * <pre>
1671 * Notes:
1672 * ~ in-place transform
1673 * ~ See convertRGBToYUV() for def'n of YUV space.
1674 * ~ replaces: r --> y, g --> u, b --> v
1675 * </pre>
1676 */
1677 l_int32
pixcmapConvertRGBToYUV(PIXCMAP * cmap)1678 pixcmapConvertRGBToYUV(PIXCMAP *cmap)
1679 {
1680 l_int32 i, ncolors, rval, gval, bval, yval, uval, vval;
1681
1682 PROCNAME("pixcmapConvertRGBToYUV");
1683
1684 if (!cmap)
1685 return ERROR_INT("cmap not defined", procName, 1);
1686
1687 ncolors = pixcmapGetCount(cmap);
1688 for (i = 0; i < ncolors; i++) {
1689 pixcmapGetColor(cmap, i, &rval, &gval, &bval);
1690 convertRGBToYUV(rval, gval, bval, &yval, &uval, &vval);
1691 pixcmapResetColor(cmap, i, yval, uval, vval);
1692 }
1693 return 0;
1694 }
1695
1696
1697 /*!
1698 * \brief pixcmapConvertYUVToRGB()
1699 *
1700 * \param[in] cmap colormap
1701 * \return 0 if OK; 1 on error
1702 *
1703 * <pre>
1704 * Notes:
1705 * ~ in-place transform
1706 * ~ See convertRGBToYUV() for def'n of YUV space.
1707 * ~ replaces: y --> r, u --> g, v --> b
1708 * </pre>
1709 */
1710 l_int32
pixcmapConvertYUVToRGB(PIXCMAP * cmap)1711 pixcmapConvertYUVToRGB(PIXCMAP *cmap)
1712 {
1713 l_int32 i, ncolors, rval, gval, bval, yval, uval, vval;
1714
1715 PROCNAME("pixcmapConvertYUVToRGB");
1716
1717 if (!cmap)
1718 return ERROR_INT("cmap not defined", procName, 1);
1719
1720 ncolors = pixcmapGetCount(cmap);
1721 for (i = 0; i < ncolors; i++) {
1722 pixcmapGetColor(cmap, i, &yval, &uval, &vval);
1723 convertYUVToRGB(yval, uval, vval, &rval, &gval, &bval);
1724 pixcmapResetColor(cmap, i, rval, gval, bval);
1725 }
1726 return 0;
1727 }
1728
1729
1730 /*---------------------------------------------------------------------------*
1731 * Colorspace conversion between RGB and XYZ *
1732 *---------------------------------------------------------------------------*/
1733 /*!
1734 * \brief pixConvertRGBToXYZ()
1735 *
1736 * \param[in] pixs rgb
1737 * \return fpixa xyz
1738 *
1739 * <pre>
1740 * Notes:
1741 * (1) The [x,y,z] values are stored as float values in three fpix
1742 * that are returned in a fpixa.
1743 * (2) The XYZ color space was defined in 1931 as a reference model that
1744 * simulates human color perception. When Y is taken as luminance,
1745 * the values of X and Z constitute a color plane representing
1746 * all the hues that can be perceived. This gamut of colors
1747 * is larger than the gamuts that can be displayed or printed.
1748 * For example, although all rgb values map to XYZ, the converse
1749 * is not true.
1750 * (3) The value of the coefficients depends on the illuminant. We use
1751 * coefficients for converting sRGB under D65 (the spectrum from
1752 * a 6500 degree K black body; an approximation to daylight color).
1753 * See, e.g.,
1754 * http://www.cs.rit.edu/~ncs/color/t_convert.html
1755 * For more general information on color transforms, see:
1756 * http://www.brucelindbloom.com/
1757 * http://user.engineering.uiowa.edu/~aip/Misc/ColorFAQ.html
1758 * http://en.wikipedia.org/wiki/CIE_1931_color_space
1759 * </pre>
1760 */
1761 FPIXA *
pixConvertRGBToXYZ(PIX * pixs)1762 pixConvertRGBToXYZ(PIX *pixs)
1763 {
1764 l_int32 w, h, wpls, wpld, i, j, rval, gval, bval;
1765 l_uint32 *lines, *datas;
1766 l_float32 fxval, fyval, fzval;
1767 l_float32 *linex, *liney, *linez, *datax, *datay, *dataz;
1768 FPIX *fpix;
1769 FPIXA *fpixa;
1770
1771 PROCNAME("pixConvertRGBToXYZ");
1772
1773 if (!pixs || pixGetDepth(pixs) != 32)
1774 return (FPIXA *)ERROR_PTR("pixs undefined or not rgb", procName, NULL);
1775
1776 /* Convert RGB image */
1777 pixGetDimensions(pixs, &w, &h, NULL);
1778 fpixa = fpixaCreate(3);
1779 for (i = 0; i < 3; i++) {
1780 fpix = fpixCreate(w, h);
1781 fpixaAddFPix(fpixa, fpix, L_INSERT);
1782 }
1783 wpls = pixGetWpl(pixs);
1784 wpld = fpixGetWpl(fpix);
1785 datas = pixGetData(pixs);
1786 datax = fpixaGetData(fpixa, 0);
1787 datay = fpixaGetData(fpixa, 1);
1788 dataz = fpixaGetData(fpixa, 2);
1789 for (i = 0; i < h; i++) {
1790 lines = datas + i * wpls;
1791 linex = datax + i * wpld;
1792 liney = datay + i * wpld;
1793 linez = dataz + i * wpld;
1794 for (j = 0; j < w; j++) {
1795 extractRGBValues(lines[j], &rval, &gval, &bval);
1796 convertRGBToXYZ(rval, gval, bval, &fxval, &fyval, &fzval);
1797 *(linex + j) = fxval;
1798 *(liney + j) = fyval;
1799 *(linez + j) = fzval;
1800 }
1801 }
1802
1803 return fpixa;
1804 }
1805
1806
1807 /*!
1808 * \brief fpixaConvertXYZToRGB()
1809 *
1810 * \param[in] fpixa three fpix: x,y,z
1811 * \return pixd rgb
1812 *
1813 * <pre>
1814 * Notes:
1815 * (1) The xyz image is stored in three fpix.
1816 * (2) For values of xyz that are out of gamut for rgb, the rgb
1817 * components are set to the closest valid color.
1818 * </pre>
1819 */
1820 PIX *
fpixaConvertXYZToRGB(FPIXA * fpixa)1821 fpixaConvertXYZToRGB(FPIXA *fpixa)
1822 {
1823 l_int32 w, h, wpls, wpld, i, j, rval, gval, bval;
1824 l_float32 fxval, fyval, fzval;
1825 l_float32 *linex, *liney, *linez, *datax, *datay, *dataz;
1826 l_uint32 *lined, *datad;
1827 PIX *pixd;
1828 FPIX *fpix;
1829
1830 PROCNAME("fpixaConvertXYZToRGB");
1831
1832 if (!fpixa || fpixaGetCount(fpixa) != 3)
1833 return (PIX *)ERROR_PTR("fpixa undefined or invalid", procName, NULL);
1834
1835 /* Convert XYZ image */
1836 if (fpixaGetFPixDimensions(fpixa, 0, &w, &h))
1837 return (PIX *)ERROR_PTR("fpixa dimensions not found", procName, NULL);
1838 pixd = pixCreate(w, h, 32);
1839 wpld = pixGetWpl(pixd);
1840 datad = pixGetData(pixd);
1841 datax = fpixaGetData(fpixa, 0);
1842 datay = fpixaGetData(fpixa, 1);
1843 dataz = fpixaGetData(fpixa, 2);
1844 fpix = fpixaGetFPix(fpixa, 0, L_CLONE);
1845 wpls = fpixGetWpl(fpix);
1846 fpixDestroy(&fpix);
1847 for (i = 0; i < h; i++) {
1848 linex = datax + i * wpls;
1849 liney = datay + i * wpls;
1850 linez = dataz + i * wpls;
1851 lined = datad + i * wpld;
1852 for (j = 0; j < w; j++) {
1853 fxval = linex[j];
1854 fyval = liney[j];
1855 fzval = linez[j];
1856 convertXYZToRGB(fxval, fyval, fzval, 0, &rval, &gval, &bval);
1857 composeRGBPixel(rval, gval, bval, lined + j);
1858 }
1859 }
1860
1861 return pixd;
1862 }
1863
1864
1865 /*!
1866 * \brief convertRGBToXYZ()
1867 *
1868 * \param[in] rval, gval, bval rgb input
1869 * \param[out] pfxval, pfyval, pfzval xyz values
1870 * \return 0 if OK, 1 on error
1871 *
1872 * <pre>
1873 * Notes:
1874 * (1) These conversions are for illuminant D65 acting on linear sRGB
1875 * values.
1876 * </pre>
1877 */
1878 l_int32
convertRGBToXYZ(l_int32 rval,l_int32 gval,l_int32 bval,l_float32 * pfxval,l_float32 * pfyval,l_float32 * pfzval)1879 convertRGBToXYZ(l_int32 rval,
1880 l_int32 gval,
1881 l_int32 bval,
1882 l_float32 *pfxval,
1883 l_float32 *pfyval,
1884 l_float32 *pfzval)
1885 {
1886 PROCNAME("convertRGBToXYZ");
1887
1888 if (pfxval) *pfxval = 0.0;
1889 if (pfyval) *pfyval = 0.0;
1890 if (pfzval) *pfzval = 0.0;
1891 if (!pfxval || !pfyval || !pfzval)
1892 return ERROR_INT("&xval, &yval, &zval not all defined", procName, 1);
1893
1894 *pfxval = 0.4125 * rval + 0.3576 * gval + 0.1804 * bval;
1895 *pfyval = 0.2127 * rval + 0.7152 * gval + 0.0722 * bval;
1896 *pfzval = 0.0193 * rval + 0.1192 * gval + 0.9502 * bval;
1897 return 0;
1898 }
1899
1900
1901 /*!
1902 * \brief convertXYZToRGB()
1903 *
1904 * \param[in] fxval, fyval, fzval
1905 * \param[in] blackout 0 to output nearest color if out of gamut;
1906 * 1 to output black
1907 * \param[out] prval, pgval, pbval rgb values
1908 * \return 0 if OK, 1 on error
1909 *
1910 * <pre>
1911 * Notes:
1912 * (1) For values of xyz that are out of gamut for rgb, at least
1913 * one of the r, g or b components will be either less than 0
1914 * or greater than 255. For that situation:
1915 * * if blackout == 0, the individual component(s) that are out
1916 * of gamut will be set to 0 or 255, respectively.
1917 * * if blackout == 1, the output color will be set to black
1918 * </pre>
1919 */
1920 l_int32
convertXYZToRGB(l_float32 fxval,l_float32 fyval,l_float32 fzval,l_int32 blackout,l_int32 * prval,l_int32 * pgval,l_int32 * pbval)1921 convertXYZToRGB(l_float32 fxval,
1922 l_float32 fyval,
1923 l_float32 fzval,
1924 l_int32 blackout,
1925 l_int32 *prval,
1926 l_int32 *pgval,
1927 l_int32 *pbval)
1928 {
1929 l_int32 rval, gval, bval;
1930
1931 PROCNAME("convertXYZToRGB");
1932
1933 if (prval) *prval = 0;
1934 if (pgval) *pgval = 0;
1935 if (pbval) *pbval = 0;
1936 if (!prval || !pgval ||!pbval)
1937 return ERROR_INT("&rval, &gval, &bval not all defined", procName, 1);
1938 *prval = *pgval = *pbval = 0;
1939
1940 rval = (l_int32)(3.2405 * fxval - 1.5372 * fyval - 0.4985 * fzval + 0.5);
1941 gval = (l_int32)(-0.9693 * fxval + 1.8760 * fyval + 0.0416 * fzval + 0.5);
1942 bval = (l_int32)(0.0556 * fxval - 0.2040 * fyval + 1.0573 * fzval + 0.5);
1943 if (blackout == 0) { /* the usual situation; use nearest rgb color */
1944 *prval = L_MAX(0, L_MIN(rval, 255));
1945 *pgval = L_MAX(0, L_MIN(gval, 255));
1946 *pbval = L_MAX(0, L_MIN(bval, 255));
1947 } else { /* use black for out of gamut */
1948 if (rval >= 0 && rval < 256 && gval >= 0 && gval < 256 &&
1949 bval >= 0 && bval < 256) { /* in gamut */
1950 *prval = rval;
1951 *pgval = gval;
1952 *pbval = bval;
1953 }
1954 }
1955 return 0;
1956 }
1957
1958
1959 /*---------------------------------------------------------------------------*
1960 * Colorspace conversion between XYZ and LAB *
1961 *---------------------------------------------------------------------------*/
1962 /*!
1963 * \brief fpixaConvertXYZToLAB()
1964 *
1965 * \param[in] fpixas xyz
1966 * \return fpixa lab
1967 *
1968 * <pre>
1969 * Notes:
1970 * (1) The input [x,y,z] and output [l,a,b] values are stored as
1971 * float values, each set in three fpix.
1972 * (2) The CIE LAB color space was invented in 1976, as an
1973 * absolute reference for specifying colors that we can
1974 * perceive, independently of the rendering device. It was
1975 * invented to align color display and print images.
1976 * For information, see:
1977 * http://www.brucelindbloom.com/
1978 * http://en.wikipedia.org/wiki/Lab_color_space
1979 * </pre>
1980 */
1981 FPIXA *
fpixaConvertXYZToLAB(FPIXA * fpixas)1982 fpixaConvertXYZToLAB(FPIXA *fpixas)
1983 {
1984 l_int32 w, h, wpl, i, j;
1985 l_float32 fxval, fyval, fzval, flval, faval, fbval;
1986 l_float32 *linex, *liney, *linez, *datax, *datay, *dataz;
1987 l_float32 *linel, *linea, *lineb, *datal, *dataa, *datab;
1988 FPIX *fpix;
1989 FPIXA *fpixad;
1990
1991 PROCNAME("fpixaConvertXYZToLAB");
1992
1993 if (!fpixas || fpixaGetCount(fpixas) != 3)
1994 return (FPIXA *)ERROR_PTR("fpixas undefined/invalid", procName, NULL);
1995
1996 /* Convert XYZ image */
1997 if (fpixaGetFPixDimensions(fpixas, 0, &w, &h))
1998 return (FPIXA *)ERROR_PTR("fpixas sizes not found", procName, NULL);
1999 fpixad = fpixaCreate(3);
2000 for (i = 0; i < 3; i++) {
2001 fpix = fpixCreate(w, h);
2002 fpixaAddFPix(fpixad, fpix, L_INSERT);
2003 }
2004 wpl = fpixGetWpl(fpix);
2005 datax = fpixaGetData(fpixas, 0);
2006 datay = fpixaGetData(fpixas, 1);
2007 dataz = fpixaGetData(fpixas, 2);
2008 datal = fpixaGetData(fpixad, 0);
2009 dataa = fpixaGetData(fpixad, 1);
2010 datab = fpixaGetData(fpixad, 2);
2011
2012 /* Convert XYZ image */
2013 for (i = 0; i < h; i++) {
2014 linex = datax + i * wpl;
2015 liney = datay + i * wpl;
2016 linez = dataz + i * wpl;
2017 linel = datal + i * wpl;
2018 linea = dataa + i * wpl;
2019 lineb = datab + i * wpl;
2020 for (j = 0; j < w; j++) {
2021 fxval = *(linex + j);
2022 fyval = *(liney + j);
2023 fzval = *(linez + j);
2024 convertXYZToLAB(fxval, fyval, fzval, &flval, &faval, &fbval);
2025 *(linel + j) = flval;
2026 *(linea + j) = faval;
2027 *(lineb + j) = fbval;
2028 }
2029 }
2030
2031 return fpixad;
2032 }
2033
2034
2035 /*!
2036 * \brief fpixaConvertLABToXYZ()
2037 *
2038 * \param[in] fpixas lab
2039 * \return fpixa xyz
2040 *
2041 * <pre>
2042 * Notes:
2043 * (1) The input [l,a,b] and output [x,y,z] values are stored as
2044 * float values, each set in three fpix.
2045 * </pre>
2046 */
2047 FPIXA *
fpixaConvertLABToXYZ(FPIXA * fpixas)2048 fpixaConvertLABToXYZ(FPIXA *fpixas)
2049 {
2050 l_int32 w, h, wpl, i, j;
2051 l_float32 fxval, fyval, fzval, flval, faval, fbval;
2052 l_float32 *linel, *linea, *lineb, *datal, *dataa, *datab;
2053 l_float32 *linex, *liney, *linez, *datax, *datay, *dataz;
2054 FPIX *fpix;
2055 FPIXA *fpixad;
2056
2057 PROCNAME("fpixaConvertLABToXYZ");
2058
2059 if (!fpixas || fpixaGetCount(fpixas) != 3)
2060 return (FPIXA *)ERROR_PTR("fpixas undefined/invalid", procName, NULL);
2061
2062 /* Convert LAB image */
2063 if (fpixaGetFPixDimensions(fpixas, 0, &w, &h))
2064 return (FPIXA *)ERROR_PTR("fpixas sizes not found", procName, NULL);
2065 fpixad = fpixaCreate(3);
2066 for (i = 0; i < 3; i++) {
2067 fpix = fpixCreate(w, h);
2068 fpixaAddFPix(fpixad, fpix, L_INSERT);
2069 }
2070 wpl = fpixGetWpl(fpix);
2071 datal = fpixaGetData(fpixas, 0);
2072 dataa = fpixaGetData(fpixas, 1);
2073 datab = fpixaGetData(fpixas, 2);
2074 datax = fpixaGetData(fpixad, 0);
2075 datay = fpixaGetData(fpixad, 1);
2076 dataz = fpixaGetData(fpixad, 2);
2077
2078 /* Convert XYZ image */
2079 for (i = 0; i < h; i++) {
2080 linel = datal + i * wpl;
2081 linea = dataa + i * wpl;
2082 lineb = datab + i * wpl;
2083 linex = datax + i * wpl;
2084 liney = datay + i * wpl;
2085 linez = dataz + i * wpl;
2086 for (j = 0; j < w; j++) {
2087 flval = *(linel + j);
2088 faval = *(linea + j);
2089 fbval = *(lineb + j);
2090 convertLABToXYZ(flval, faval, fbval, &fxval, &fyval, &fzval);
2091 *(linex + j) = fxval;
2092 *(liney + j) = fyval;
2093 *(linez + j) = fzval;
2094 }
2095 }
2096
2097 return fpixad;
2098 }
2099
2100
2101 /*!
2102 * \brief convertXYZToLAB()
2103 *
2104 * \param[in] xval, yval, zval xyz input
2105 * \param[out] plval, paval, pbval lab values
2106 * \return 0 if OK, 1 on error
2107 */
2108 l_int32
convertXYZToLAB(l_float32 xval,l_float32 yval,l_float32 zval,l_float32 * plval,l_float32 * paval,l_float32 * pbval)2109 convertXYZToLAB(l_float32 xval,
2110 l_float32 yval,
2111 l_float32 zval,
2112 l_float32 *plval,
2113 l_float32 *paval,
2114 l_float32 *pbval)
2115 {
2116 l_float32 xn, yn, zn, fx, fy, fz;
2117
2118 PROCNAME("convertXYZToLAB");
2119
2120 if (plval) *plval = 0.0;
2121 if (paval) *paval = 0.0;
2122 if (pbval) *pbval = 0.0;
2123 if (!plval || !paval || !pbval)
2124 return ERROR_INT("&lval, &aval, &bval not all defined", procName, 1);
2125
2126 /* First normalize to the corresponding white values */
2127 xn = 0.0041259 * xval;
2128 yn = 0.0039216 * yval;
2129 zn = 0.0036012 * zval;
2130 /* Then apply the lab_forward function */
2131 fx = lab_forward(xn);
2132 fy = lab_forward(yn);
2133 fz = lab_forward(zn);
2134 *plval = 116.0 * fy - 16.0;
2135 *paval = 500.0 * (fx - fy);
2136 *pbval = 200.0 * (fy - fz);
2137 return 0;
2138 }
2139
2140
2141 /*!
2142 * \brief convertLABToXYZ()
2143 *
2144 * \param[in] lval, aval, bval
2145 * \param[out] pxval, pyval, pzval xyz values
2146 * \return 0 if OK, 1 on error
2147 */
2148 l_int32
convertLABToXYZ(l_float32 lval,l_float32 aval,l_float32 bval,l_float32 * pxval,l_float32 * pyval,l_float32 * pzval)2149 convertLABToXYZ(l_float32 lval,
2150 l_float32 aval,
2151 l_float32 bval,
2152 l_float32 *pxval,
2153 l_float32 *pyval,
2154 l_float32 *pzval)
2155 {
2156 l_float32 fx, fy, fz;
2157 l_float32 xw = 242.37; /* x component corresponding to rgb white */
2158 l_float32 yw = 255.0; /* y component corresponding to rgb white */
2159 l_float32 zw = 277.69; /* z component corresponding to rgb white */
2160
2161 PROCNAME("convertLABToXYZ");
2162
2163 if (pxval) *pxval = 0.0;
2164 if (pyval) *pyval = 0.0;
2165 if (pzval) *pzval = 0.0;
2166 if (!pxval || !pyval || !pzval)
2167 return ERROR_INT("&xval, &yval, &zval not all defined", procName, 1);
2168
2169 fy = 0.0086207 * (16.0 + lval);
2170 fx = fy + 0.002 * aval;
2171 fz = fy - 0.005 * bval;
2172 *pxval = xw * lab_reverse(fx);
2173 *pyval = yw * lab_reverse(fy);
2174 *pzval = zw * lab_reverse(fz);
2175 return 0;
2176 }
2177
2178
2179 /*
2180 * See http://en.wikipedia.org/wiki/Lab_color_space for formulas.
2181 * This is the forward function: from xyz to lab. It includes a rational
2182 * function approximation over [0.008856 ... 1] to the cube root, from
2183 * "Fast Color Space Transformations Using Minimax Approximations",
2184 * M. Celebi et al, http://arxiv.org/pdf/1009.0854v1.pdf.
2185 */
2186 static l_float32
lab_forward(l_float32 v)2187 lab_forward(l_float32 v)
2188 {
2189 const l_float32 f_thresh = 0.008856; /* (6/29)^3 */
2190 const l_float32 f_factor = 7.787; /* (1/3) * (29/6)^2) */
2191 const l_float32 f_offset = 0.13793; /* 4/29 */
2192
2193 if (v > f_thresh) {
2194 #if SLOW_CUBE_ROOT
2195 return powf(v, 0.333333);
2196 #else
2197 l_float32 num, den;
2198 num = 4.37089e-04 + v * (9.52695e-02 + v * (1.25201 + v * 1.30273));
2199 den = 3.91236e-03 + v * (2.95408e-01 + v * (1.71714 + v * 6.34341e-01));
2200 return num / den;
2201 #endif
2202 } else {
2203 return f_factor * v + f_offset;
2204 }
2205 }
2206
2207
2208 /*
2209 * See http://en.wikipedia.org/wiki/Lab_color_space for formulas.
2210 * This is the reverse (inverse) function: from lab to xyz.
2211 */
2212 static l_float32
lab_reverse(l_float32 v)2213 lab_reverse(l_float32 v)
2214 {
2215 const l_float32 r_thresh = 0.20690; /* 6/29 */
2216 const l_float32 r_factor = 0.12842; /* 3 * (6/29)^2 */
2217 const l_float32 r_offset = 0.13793; /* 4/29 */
2218
2219 if (v > r_thresh) {
2220 return v * v * v;
2221 } else {
2222 return r_factor * (v - r_offset);
2223 }
2224 }
2225
2226
2227 /*---------------------------------------------------------------------------*
2228 * Colorspace conversion between RGB and LAB *
2229 *---------------------------------------------------------------------------*/
2230 /*!
2231 * \brief pixConvertRGBToLAB()
2232 *
2233 * \param[in] pixs rgb
2234 * \return fpixa lab
2235 *
2236 * <pre>
2237 * Notes:
2238 * (1) The [l,a,b] values are stored as float values in three fpix
2239 * that are returned in a fpixa.
2240 * </pre>
2241 */
2242 FPIXA *
pixConvertRGBToLAB(PIX * pixs)2243 pixConvertRGBToLAB(PIX *pixs)
2244 {
2245 l_int32 w, h, wpls, wpld, i, j, rval, gval, bval;
2246 l_uint32 *lines, *datas;
2247 l_float32 flval, faval, fbval;
2248 l_float32 *linel, *linea, *lineb, *datal, *dataa, *datab;
2249 FPIX *fpix;
2250 FPIXA *fpixa;
2251
2252 PROCNAME("pixConvertRGBToLAB");
2253
2254 if (!pixs || pixGetDepth(pixs) != 32)
2255 return (FPIXA *)ERROR_PTR("pixs undefined or not rgb", procName, NULL);
2256
2257 /* Convert RGB image */
2258 pixGetDimensions(pixs, &w, &h, NULL);
2259 fpixa = fpixaCreate(3);
2260 for (i = 0; i < 3; i++) {
2261 fpix = fpixCreate(w, h);
2262 fpixaAddFPix(fpixa, fpix, L_INSERT);
2263 }
2264 wpls = pixGetWpl(pixs);
2265 wpld = fpixGetWpl(fpix);
2266 datas = pixGetData(pixs);
2267 datal = fpixaGetData(fpixa, 0);
2268 dataa = fpixaGetData(fpixa, 1);
2269 datab = fpixaGetData(fpixa, 2);
2270 for (i = 0; i < h; i++) {
2271 lines = datas + i * wpls;
2272 linel = datal + i * wpld;
2273 linea = dataa + i * wpld;
2274 lineb = datab + i * wpld;
2275 for (j = 0; j < w; j++) {
2276 extractRGBValues(lines[j], &rval, &gval, &bval);
2277 convertRGBToLAB(rval, gval, bval, &flval, &faval, &fbval);
2278 *(linel + j) = flval;
2279 *(linea + j) = faval;
2280 *(lineb + j) = fbval;
2281 }
2282 }
2283
2284 return fpixa;
2285 }
2286
2287
2288 /*!
2289 * \brief fpixaConvertLABToRGB()
2290 *
2291 * \param[in] fpixa three fpix: l,a,b
2292 * \return pixd rgb
2293 *
2294 * <pre>
2295 * Notes:
2296 * (1) The lab image is stored in three fpix.
2297 * </pre>
2298 */
2299 PIX *
fpixaConvertLABToRGB(FPIXA * fpixa)2300 fpixaConvertLABToRGB(FPIXA *fpixa)
2301 {
2302 l_int32 w, h, wpls, wpld, i, j, rval, gval, bval;
2303 l_float32 flval, faval, fbval;
2304 l_float32 *linel, *linea, *lineb, *datal, *dataa, *datab;
2305 l_uint32 *lined, *datad;
2306 PIX *pixd;
2307 FPIX *fpix;
2308
2309 PROCNAME("fpixaConvertLABToRGB");
2310
2311 if (!fpixa || fpixaGetCount(fpixa) != 3)
2312 return (PIX *)ERROR_PTR("fpixa undefined or invalid", procName, NULL);
2313
2314 /* Convert LAB image */
2315 if (fpixaGetFPixDimensions(fpixa, 0, &w, &h))
2316 return (PIX *)ERROR_PTR("fpixa dimensions not found", procName, NULL);
2317 pixd = pixCreate(w, h, 32);
2318 wpld = pixGetWpl(pixd);
2319 datad = pixGetData(pixd);
2320 datal = fpixaGetData(fpixa, 0);
2321 dataa = fpixaGetData(fpixa, 1);
2322 datab = fpixaGetData(fpixa, 2);
2323 fpix = fpixaGetFPix(fpixa, 0, L_CLONE);
2324 wpls = fpixGetWpl(fpix);
2325 fpixDestroy(&fpix);
2326 for (i = 0; i < h; i++) {
2327 linel = datal + i * wpls;
2328 linea = dataa + i * wpls;
2329 lineb = datab + i * wpls;
2330 lined = datad + i * wpld;
2331 for (j = 0; j < w; j++) {
2332 flval = linel[j];
2333 faval = linea[j];
2334 fbval = lineb[j];
2335 convertLABToRGB(flval, faval, fbval, &rval, &gval, &bval);
2336 composeRGBPixel(rval, gval, bval, lined + j);
2337 }
2338 }
2339
2340 return pixd;
2341 }
2342
2343
2344 /*!
2345 * \brief convertRGBToLAB()
2346 *
2347 * \param[in] rval, gval, bval rgb input
2348 * \param[out] pflval, pfaval, pfbval lab values
2349 * \return 0 if OK, 1 on error
2350 *
2351 * <pre>
2352 * Notes:
2353 * (1) These conversions are for illuminant D65 acting on linear sRGB
2354 * values.
2355 * </pre>
2356 */
2357 l_int32
convertRGBToLAB(l_int32 rval,l_int32 gval,l_int32 bval,l_float32 * pflval,l_float32 * pfaval,l_float32 * pfbval)2358 convertRGBToLAB(l_int32 rval,
2359 l_int32 gval,
2360 l_int32 bval,
2361 l_float32 *pflval,
2362 l_float32 *pfaval,
2363 l_float32 *pfbval)
2364 {
2365 l_float32 fxval, fyval, fzval;
2366
2367 PROCNAME("convertRGBToLAB");
2368
2369 if (pflval) *pflval = 0.0;
2370 if (pfaval) *pfaval = 0.0;
2371 if (pfbval) *pfbval = 0.0;
2372 if (!pflval || !pfaval || !pfbval)
2373 return ERROR_INT("&flval, &faval, &fbval not all defined", procName, 1);
2374
2375 convertRGBToXYZ(rval, gval, bval, &fxval, &fyval, &fzval);
2376 convertXYZToLAB(fxval, fyval, fzval, pflval, pfaval, pfbval);
2377 return 0;
2378 }
2379
2380
2381 /*!
2382 * \brief convertLABToRGB()
2383 *
2384 * \param[in] flval, faval, fbval
2385 * \param[out] prval, pgval, pbval rgb values
2386 * \return 0 if OK, 1 on error
2387 *
2388 * <pre>
2389 * Notes:
2390 * (1) For values of lab that are out of gamut for rgb, the rgb
2391 * components are set to the closest valid color.
2392 * </pre>
2393 */
2394 l_int32
convertLABToRGB(l_float32 flval,l_float32 faval,l_float32 fbval,l_int32 * prval,l_int32 * pgval,l_int32 * pbval)2395 convertLABToRGB(l_float32 flval,
2396 l_float32 faval,
2397 l_float32 fbval,
2398 l_int32 *prval,
2399 l_int32 *pgval,
2400 l_int32 *pbval)
2401 {
2402 l_float32 fxval, fyval, fzval;
2403
2404 PROCNAME("convertLABToRGB");
2405
2406 if (prval) *prval = 0;
2407 if (pgval) *pgval = 0;
2408 if (pbval) *pbval = 0;
2409 if (!prval || !pgval || !pbval)
2410 return ERROR_INT("&rval, &gval, &bval not all defined", procName, 1);
2411
2412 convertLABToXYZ(flval, faval, fbval, &fxval, &fyval, &fzval);
2413 convertXYZToRGB(fxval, fyval, fzval, 0, prval, pgval, pbval);
2414 return 0;
2415 }
2416