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