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 colorcontent.c
29  * <pre>
30  *
31  *      Builds an image of the color content, on a per-pixel basis,
32  *      as a measure of the amount of divergence of each color
33  *      component (R,G,B) from gray.
34  *         l_int32    pixColorContent()
35  *
36  *      Finds the 'amount' of color in an image, on a per-pixel basis,
37  *      as a measure of the difference of the pixel color from gray.
38  *         PIX       *pixColorMagnitude()
39  *
40  *      Generates a mask over pixels that have sufficient color and
41  *      are not too close to gray pixels.
42  *         PIX       *pixMaskOverColorPixels()
43  *
44  *      Generates mask over pixels within a prescribed cube in RGB space
45  *         PIX       *pixMaskOverColorRange()
46  *
47  *      Finds the fraction of pixels with "color" that are not close to black
48  *         l_int32    pixColorFraction()
49  *
50  *      Determine if there are significant color regions that are
51  *      not background in a page image
52  *         l_int32    pixFindColorRegions()
53  *
54  *      Finds the number of perceptually significant gray intensities
55  *      in a grayscale image.
56  *         l_int32    pixNumSignificantGrayColors()
57  *
58  *      Identifies images where color quantization will cause posterization
59  *      due to the existence of many colors in low-gradient regions.
60  *         l_int32    pixColorsForQuantization()
61  *
62  *      Finds the number of unique colors in an image
63  *         l_int32    pixNumColors()
64  *
65  *      Find the most "populated" colors in the image (and quantize)
66  *         l_int32    pixGetMostPopulatedColors()
67  *         PIX       *pixSimpleColorQuantize()
68  *
69  *      Constructs a color histogram based on rgb indices
70  *         NUMA      *pixGetRGBHistogram()
71  *         l_int32    makeRGBIndexTables()
72  *         l_int32    getRGBFromIndex()
73  *
74  *      Identify images that have highlight (red) color
75  *         l_int32    pixHasHighlightRed()
76  *
77  *  Color is tricky.  If we consider gray (r = g = b) to have no color
78  *  content, how should we define the color content in each component
79  *  of an arbitrary pixel, as well as the overall color magnitude?
80  *
81  *  I can think of three ways to define the color content in each component:
82  *
83  *  (1) Linear.  For each component, take the difference from the average
84  *      of all three.
85  *  (2) Linear.  For each component, take the difference from the average
86  *      of the other two.
87  *  (3) Nonlinear.  For each component, take the minimum of the differences
88  *      from the other two.
89  *
90  *  How might one choose from among these?  Consider two different situations:
91  *  (a) r = g = 0, b = 255            {255}   /255/
92  *  (b) r = 0, g = 127, b = 255       {191}   /128/
93  *  How much g is in each of these?  The three methods above give:
94  *  (a)  1: 85   2: 127   3: 0        [85]
95  *  (b)  1: 0    2: 0     3: 127      [0]
96  *  How much b is in each of these?
97  *  (a)  1: 170  2: 255   3: 255      [255]
98  *  (b)  1: 127  2: 191   3: 127      [191]
99  *  The number I'd "like" to give is in [].  (Please don't ask why, it's
100  *  just a feeling.
101  *
102  *  So my preferences seem to be somewhere between (1) and (2).
103  *  (3) is just too "decisive!"  Let's pick (2).
104  *
105  *  We also allow compensation for white imbalance.  For each
106  *  component, we do a linear TRC (gamma = 1.0), where the black
107  *  point remains at 0 and the white point is given by the input
108  *  parameter.  This is equivalent to doing a global remapping,
109  *  as with pixGlobalNormRGB(), followed by color content (or magnitude)
110  *  computation, but without the overhead of first creating the
111  *  white point normalized image.
112  *
113  *  Another useful property is the overall color magnitude in the pixel.
114  *  For this there are again several choices, such as:
115  *      (a) rms deviation from the mean
116  *      (b) the average L1 deviation from the mean
117  *      (c) the maximum (over components) of one of the color
118  *          content measures given above.
119  *
120  *  For now, we will choose two of the methods in (c):
121  *     L_MAX_DIFF_FROM_AVERAGE_2
122  *        Define the color magnitude as the maximum over components
123  *        of the difference between the component value and the
124  *        average of the other two.  It is easy to show that
125  *        this is equivalent to selecting the two component values
126  *        that are closest to each other, averaging them, and
127  *        using the distance from that average to the third component.
128  *        For (a) and (b) above, this value is in {..}.
129  *    L_MAX_MIN_DIFF_FROM_2
130  *        Define the color magnitude as the maximum over components
131  *        of the minimum difference between the component value and the
132  *        other two values.  It is easy to show that this is equivalent
133  *        to selecting the intermediate value of the three differences
134  *        between the three components.  For (a) and (b) above,
135  *        this value is in /../.
136  * </pre>
137  */
138 
139 #include "allheaders.h"
140 
141 /* ----------------------------------------------------------------------- *
142  *      Builds an image of the color content, on a per-pixel basis,        *
143  *      as a measure of the amount of divergence of each color             *
144  *      component (R,G,B) from gray.                                       *
145  * ----------------------------------------------------------------------- */
146 /*!
147  * \brief   pixColorContent()
148  *
149  * \param[in]    pixs  32 bpp rgb or 8 bpp colormapped
150  * \param[in]    rwhite, gwhite, bwhite color value associated with white point
151  * \param[in]    mingray min gray value for which color is measured
152  * \param[out]   ppixr [optional] 8 bpp red 'content'
153  * \param[out]   ppixg [optional] 8 bpp green 'content'
154  * \param[out]   ppixb [optional] 8 bpp blue 'content'
155  * \return  0 if OK, 1 on error
156  *
157  * <pre>
158  * Notes:
159  *      (1) This returns the color content in each component, which is
160  *          a measure of the deviation from gray, and is defined
161  *          as the difference between the component and the average of
162  *          the other two components.  See the discussion at the
163  *          top of this file.
164  *      (2) The three numbers (rwhite, gwhite and bwhite) can be thought
165  *          of as the values in the image corresponding to white.
166  *          They are used to compensate for an unbalanced color white point.
167  *          They must either be all 0 or all non-zero.  To turn this
168  *          off, set them all to 0.
169  *      (3) If the maximum component after white point correction,
170  *          max(r,g,b), is less than mingray, all color components
171  *          for that pixel are set to zero.
172  *          Use mingray = 0 to turn off this filtering of dark pixels.
173  *      (4) Therefore, use 0 for all four input parameters if the color
174  *          magnitude is to be calculated without either white balance
175  *          correction or dark filtering.
176  * </pre>
177  */
178 l_int32
pixColorContent(PIX * pixs,l_int32 rwhite,l_int32 gwhite,l_int32 bwhite,l_int32 mingray,PIX ** ppixr,PIX ** ppixg,PIX ** ppixb)179 pixColorContent(PIX     *pixs,
180                 l_int32  rwhite,
181                 l_int32  gwhite,
182                 l_int32  bwhite,
183                 l_int32  mingray,
184                 PIX    **ppixr,
185                 PIX    **ppixg,
186                 PIX    **ppixb)
187 {
188 l_int32    w, h, d, i, j, wplc, wplr, wplg, wplb;
189 l_int32    rval, gval, bval, rgdiff, rbdiff, gbdiff, maxval, colorval;
190 l_int32   *rtab, *gtab, *btab;
191 l_uint32   pixel;
192 l_uint32  *datac, *datar, *datag, *datab, *linec, *liner, *lineg, *lineb;
193 NUMA      *nar, *nag, *nab;
194 PIX       *pixc;   /* rgb */
195 PIX       *pixr, *pixg, *pixb;   /* 8 bpp grayscale */
196 PIXCMAP   *cmap;
197 
198     PROCNAME("pixColorContent");
199 
200     if (!ppixr && !ppixg && !ppixb)
201         return ERROR_INT("no return val requested", procName, 1);
202     if (ppixr) *ppixr = NULL;
203     if (ppixg) *ppixg = NULL;
204     if (ppixb) *ppixb = NULL;
205     if (!pixs)
206         return ERROR_INT("pixs not defined", procName, 1);
207     if (mingray < 0) mingray = 0;
208     pixGetDimensions(pixs, &w, &h, &d);
209     if (mingray > 255)
210         return ERROR_INT("mingray > 255", procName, 1);
211     if (rwhite < 0 || gwhite < 0 || bwhite < 0)
212         return ERROR_INT("some white vals are negative", procName, 1);
213     if ((rwhite || gwhite || bwhite) && (rwhite * gwhite * bwhite == 0))
214         return ERROR_INT("white vals not all zero or all nonzero", procName, 1);
215 
216     cmap = pixGetColormap(pixs);
217     if (!cmap && d != 32)
218         return ERROR_INT("pixs neither cmapped nor 32 bpp", procName, 1);
219     if (cmap)
220         pixc = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR);
221     else
222         pixc = pixClone(pixs);
223 
224     pixr = pixg = pixb = NULL;
225     pixGetDimensions(pixc, &w, &h, NULL);
226     if (ppixr) {
227         pixr = pixCreate(w, h, 8);
228         datar = pixGetData(pixr);
229         wplr = pixGetWpl(pixr);
230         *ppixr = pixr;
231     }
232     if (ppixg) {
233         pixg = pixCreate(w, h, 8);
234         datag = pixGetData(pixg);
235         wplg = pixGetWpl(pixg);
236         *ppixg = pixg;
237     }
238     if (ppixb) {
239         pixb = pixCreate(w, h, 8);
240         datab = pixGetData(pixb);
241         wplb = pixGetWpl(pixb);
242         *ppixb = pixb;
243     }
244 
245     datac = pixGetData(pixc);
246     wplc = pixGetWpl(pixc);
247     if (rwhite) {  /* all white pt vals are nonzero */
248         nar = numaGammaTRC(1.0, 0, rwhite);
249         rtab = numaGetIArray(nar);
250         nag = numaGammaTRC(1.0, 0, gwhite);
251         gtab = numaGetIArray(nag);
252         nab = numaGammaTRC(1.0, 0, bwhite);
253         btab = numaGetIArray(nab);
254     }
255     for (i = 0; i < h; i++) {
256         linec = datac + i * wplc;
257         if (pixr)
258             liner = datar + i * wplr;
259         if (pixg)
260             lineg = datag + i * wplg;
261         if (pixb)
262             lineb = datab + i * wplb;
263         for (j = 0; j < w; j++) {
264             pixel = linec[j];
265             extractRGBValues(pixel, &rval, &gval, &bval);
266             if (rwhite) {  /* color correct for white point */
267                 rval = rtab[rval];
268                 gval = gtab[gval];
269                 bval = btab[bval];
270             }
271             if (mingray > 0) {  /* dark pixels have no color value */
272                 maxval = L_MAX(rval, gval);
273                 maxval = L_MAX(maxval, bval);
274                 if (maxval < mingray)
275                     continue;  /* colorval = 0 for each component */
276             }
277             rgdiff = L_ABS(rval - gval);
278             rbdiff = L_ABS(rval - bval);
279             gbdiff = L_ABS(gval - bval);
280             if (pixr) {
281                 colorval = (rgdiff + rbdiff) / 2;
282                 SET_DATA_BYTE(liner, j, colorval);
283             }
284             if (pixg) {
285                 colorval = (rgdiff + gbdiff) / 2;
286                 SET_DATA_BYTE(lineg, j, colorval);
287             }
288             if (pixb) {
289                 colorval = (rbdiff + gbdiff) / 2;
290                 SET_DATA_BYTE(lineb, j, colorval);
291             }
292         }
293     }
294 
295     if (rwhite) {
296         numaDestroy(&nar);
297         numaDestroy(&nag);
298         numaDestroy(&nab);
299         LEPT_FREE(rtab);
300         LEPT_FREE(gtab);
301         LEPT_FREE(btab);
302     }
303     pixDestroy(&pixc);
304     return 0;
305 }
306 
307 
308 /* ----------------------------------------------------------------------- *
309  *      Finds the 'amount' of color in an image, on a per-pixel basis,     *
310  *      as a measure of the difference of the pixel color from gray.       *
311  * ----------------------------------------------------------------------- */
312 /*!
313  * \brief   pixColorMagnitude()
314  *
315  * \param[in]    pixs  32 bpp rgb or 8 bpp colormapped
316  * \param[in]    rwhite, gwhite, bwhite color value associated with white point
317  * \param[in]    type chooses the method for calculating the color magnitude:
318  *                    L_MAX_DIFF_FROM_AVERAGE_2, L_MAX_MIN_DIFF_FROM_2,
319  *                    L_MAX_DIFF
320  * \return  pixd 8 bpp, amount of color in each source pixel,
321  *                    or NULL on error
322  *
323  * <pre>
324  * Notes:
325  *      (1) For an RGB image, a gray pixel is one where all three components
326  *          are equal.  We define the amount of color in an RGB pixel as
327  *          a function depending on the absolute value of the differences
328  *          between the three color components.  Consider the two largest
329  *          of these differences.  The pixel component in common to these
330  *          two differences is the color farthest from the other two.
331  *          The color magnitude in an RGB pixel can be taken as one
332  *          of these three definitions:
333  *            (a) The average of these two differences.  This is the
334  *                average distance from the two components that are
335  *                nearest to each other to the third component.
336  *            (b) The minimum value of these two differences.  This is
337  *                the intermediate value of the three distances between
338  *                component values.  Stated otherwise, it is the
339  *                maximum over all components of the minimum distance
340  *                from that component to the other two components.
341  *            (c) The maximum difference between component values.
342  *      (2) As an example, suppose that R and G are the closest in
343  *          magnitude.  Then the color is determined as either:
344  *            (a) The average distance of B from these two:
345  *                   (|B - R| + |B - G|) / 2
346  *            (b) The minimum distance of B from these two:
347  *                   min(|B - R|, |B - G|).
348  *            (c) The maximum distance of B from these two:
349  *                   max(|B - R|, |B - G|)
350  *      (3) The three methods for choosing the color magnitude from
351  *          the components are selected with these flags:
352  *            (a) L_MAX_DIFF_FROM_AVERAGE_2
353  *            (b) L_MAX_MIN_DIFF_FROM_2
354  *            (c) L_MAX_DIFF
355  *      (4) The three numbers (rwhite, gwhite and bwhite) can be thought
356  *          of as the values in the image corresponding to white.
357  *          They are used to compensate for an unbalanced color white point.
358  *          They must either be all 0 or all non-zero.  To turn this
359  *          off, set them all to 0.
360  * </pre>
361  */
362 PIX *
pixColorMagnitude(PIX * pixs,l_int32 rwhite,l_int32 gwhite,l_int32 bwhite,l_int32 type)363 pixColorMagnitude(PIX     *pixs,
364                   l_int32  rwhite,
365                   l_int32  gwhite,
366                   l_int32  bwhite,
367                   l_int32  type)
368 {
369 l_int32    w, h, d, i, j, wplc, wpld;
370 l_int32    rval, gval, bval, rdist, gdist, bdist, colorval;
371 l_int32    rgdist, rbdist, gbdist, mindist, maxdist, minval, maxval;
372 l_int32   *rtab, *gtab, *btab;
373 l_uint32   pixel;
374 l_uint32  *datac, *datad, *linec, *lined;
375 NUMA      *nar, *nag, *nab;
376 PIX       *pixc, *pixd;
377 PIXCMAP   *cmap;
378 
379     PROCNAME("pixColorMagnitude");
380 
381     if (!pixs)
382         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
383     pixGetDimensions(pixs, &w, &h, &d);
384     if (type != L_MAX_DIFF_FROM_AVERAGE_2 && type != L_MAX_MIN_DIFF_FROM_2 &&
385         type != L_MAX_DIFF)
386         return (PIX *)ERROR_PTR("invalid type", procName, NULL);
387     if (rwhite < 0 || gwhite < 0 || bwhite < 0)
388         return (PIX *)ERROR_PTR("some white vals are negative", procName, NULL);
389     if ((rwhite || gwhite || bwhite) && (rwhite * gwhite * bwhite == 0))
390         return (PIX *)ERROR_PTR("white vals not all zero or all nonzero",
391                                 procName, NULL);
392 
393     cmap = pixGetColormap(pixs);
394     if (!cmap && d != 32)
395         return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL);
396     if (cmap)
397         pixc = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR);
398     else
399         pixc = pixClone(pixs);
400 
401     pixd = pixCreate(w, h, 8);
402     datad = pixGetData(pixd);
403     wpld = pixGetWpl(pixd);
404     datac = pixGetData(pixc);
405     wplc = pixGetWpl(pixc);
406     if (rwhite) {  /* all white pt vals are nonzero */
407         nar = numaGammaTRC(1.0, 0, rwhite);
408         rtab = numaGetIArray(nar);
409         nag = numaGammaTRC(1.0, 0, gwhite);
410         gtab = numaGetIArray(nag);
411         nab = numaGammaTRC(1.0, 0, bwhite);
412         btab = numaGetIArray(nab);
413     }
414     for (i = 0; i < h; i++) {
415         linec = datac + i * wplc;
416         lined = datad + i * wpld;
417         for (j = 0; j < w; j++) {
418             pixel = linec[j];
419             extractRGBValues(pixel, &rval, &gval, &bval);
420             if (rwhite) {  /* color correct for white point */
421                 rval = rtab[rval];
422                 gval = gtab[gval];
423                 bval = btab[bval];
424             }
425             if (type == L_MAX_DIFF_FROM_AVERAGE_2) {
426                 rdist = ((gval + bval ) / 2 - rval);
427                 rdist = L_ABS(rdist);
428                 gdist = ((rval + bval ) / 2 - gval);
429                 gdist = L_ABS(gdist);
430                 bdist = ((rval + gval ) / 2 - bval);
431                 bdist = L_ABS(bdist);
432                 colorval = L_MAX(rdist, gdist);
433                 colorval = L_MAX(colorval, bdist);
434             } else if (type == L_MAX_MIN_DIFF_FROM_2) {  /* intermediate dist */
435                 rgdist = L_ABS(rval - gval);
436                 rbdist = L_ABS(rval - bval);
437                 gbdist = L_ABS(gval - bval);
438                 maxdist = L_MAX(rgdist, rbdist);
439                 if (gbdist >= maxdist) {
440                     colorval = maxdist;
441                 } else {  /* gbdist is smallest or intermediate */
442                     mindist = L_MIN(rgdist, rbdist);
443                     colorval = L_MAX(mindist, gbdist);
444                 }
445             } else {  /* type == L_MAX_DIFF */
446                 minval = L_MIN(rval, gval);
447                 minval = L_MIN(minval, bval);
448                 maxval = L_MAX(rval, gval);
449                 maxval = L_MAX(maxval, bval);
450                 colorval = maxval - minval;
451             }
452             SET_DATA_BYTE(lined, j, colorval);
453         }
454     }
455 
456     if (rwhite) {
457         numaDestroy(&nar);
458         numaDestroy(&nag);
459         numaDestroy(&nab);
460         LEPT_FREE(rtab);
461         LEPT_FREE(gtab);
462         LEPT_FREE(btab);
463     }
464     pixDestroy(&pixc);
465     return pixd;
466 }
467 
468 
469 /* ----------------------------------------------------------------------- *
470  *      Generates a mask over pixels that have sufficient color and        *
471  *      are not too close to gray pixels.                                  *
472  * ----------------------------------------------------------------------- */
473 /*!
474  * \brief   pixMaskOverColorPixels()
475  *
476  * \param[in]    pixs  32 bpp rgb or 8 bpp colormapped
477  * \param[in]    threshdiff threshold for minimum of the max difference
478  *                          between components
479  * \param[in]    mindist minimum allowed distance from nearest non-color pixel
480  * \return  pixd 1 bpp, mask over color pixels, or NULL on error
481  *
482  * <pre>
483  * Notes:
484  *      (1) The generated mask identifies each pixel as either color or
485  *          non-color.  For a pixel to be color, it must satisfy two
486  *          constraints:
487  *            (a) The max difference between the r,g and b components must
488  *                equal or exceed a threshold %threshdiff.
489  *            (b) It must be at least %mindist (in an 8-connected way)
490  *                from the nearest non-color pixel.
491  *      (2) The distance constraint (b) is only applied if %mindist > 1.
492  *          For example, if %mindist == 2, the color pixels identified
493  *          by (a) are eroded by a 3x3 Sel.  In general, the Sel size
494  *          for erosion is 2 * (%mindist - 1) + 1.
495  *          Why have this constraint?  In scanned images that are
496  *          essentially gray, color artifacts are typically introduced
497  *          in transition regions near sharp edges that go from dark
498  *          to light, so this allows these transition regions to be removed.
499  * </pre>
500  */
501 PIX *
pixMaskOverColorPixels(PIX * pixs,l_int32 threshdiff,l_int32 mindist)502 pixMaskOverColorPixels(PIX     *pixs,
503                        l_int32  threshdiff,
504                        l_int32  mindist)
505 {
506 l_int32    w, h, d, i, j, wpls, wpld, size;
507 l_int32    rval, gval, bval, minval, maxval;
508 l_uint32  *datas, *datad, *lines, *lined;
509 PIX       *pixc, *pixd;
510 PIXCMAP   *cmap;
511 
512     PROCNAME("pixMaskOverColorPixels");
513 
514     if (!pixs)
515         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
516     pixGetDimensions(pixs, &w, &h, &d);
517 
518     cmap = pixGetColormap(pixs);
519     if (!cmap && d != 32)
520         return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL);
521     if (cmap)
522         pixc = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR);
523     else
524         pixc = pixClone(pixs);
525 
526     pixd = pixCreate(w, h, 1);
527     datad = pixGetData(pixd);
528     wpld = pixGetWpl(pixd);
529     datas = pixGetData(pixc);
530     wpls = pixGetWpl(pixc);
531     for (i = 0; i < h; i++) {
532         lines = datas + i * wpls;
533         lined = datad + i * wpld;
534         for (j = 0; j < w; j++) {
535             extractRGBValues(lines[j], &rval, &gval, &bval);
536             minval = L_MIN(rval, gval);
537             minval = L_MIN(minval, bval);
538             maxval = L_MAX(rval, gval);
539             maxval = L_MAX(maxval, bval);
540             if (maxval - minval >= threshdiff)
541                 SET_DATA_BIT(lined, j);
542         }
543     }
544 
545     if (mindist > 1) {
546         size = 2 * (mindist - 1) + 1;
547         pixErodeBrick(pixd, pixd, size, size);
548     }
549 
550     pixDestroy(&pixc);
551     return pixd;
552 }
553 
554 
555 /* ----------------------------------------------------------------------- *
556  *      Generates a mask over pixels that have RGB color components        *
557  *      within the prescribed range (a cube in RGB color space)            *
558  * ----------------------------------------------------------------------- */
559 /*!
560  * \brief   pixMaskOverColorRange()
561  *
562  * \param[in]    pixs  32 bpp rgb or 8 bpp colormapped
563  * \param[in]    rmin, rmax min and max allowed values for red component
564  * \param[in]    gmin, gmax
565  * \param[in]    bmin, bmax
566  * \return  pixd 1 bpp, mask over color pixels, or NULL on error
567  */
568 PIX *
pixMaskOverColorRange(PIX * pixs,l_int32 rmin,l_int32 rmax,l_int32 gmin,l_int32 gmax,l_int32 bmin,l_int32 bmax)569 pixMaskOverColorRange(PIX     *pixs,
570                       l_int32  rmin,
571                       l_int32  rmax,
572                       l_int32  gmin,
573                       l_int32  gmax,
574                       l_int32  bmin,
575                       l_int32  bmax)
576 {
577 l_int32    w, h, d, i, j, wpls, wpld;
578 l_int32    rval, gval, bval;
579 l_uint32  *datas, *datad, *lines, *lined;
580 PIX       *pixc, *pixd;
581 PIXCMAP   *cmap;
582 
583     PROCNAME("pixMaskOverColorRange");
584 
585     if (!pixs)
586         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
587     pixGetDimensions(pixs, &w, &h, &d);
588 
589     cmap = pixGetColormap(pixs);
590     if (!cmap && d != 32)
591         return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL);
592     if (cmap)
593         pixc = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR);
594     else
595         pixc = pixClone(pixs);
596 
597     pixd = pixCreate(w, h, 1);
598     datad = pixGetData(pixd);
599     wpld = pixGetWpl(pixd);
600     datas = pixGetData(pixc);
601     wpls = pixGetWpl(pixc);
602     for (i = 0; i < h; i++) {
603         lines = datas + i * wpls;
604         lined = datad + i * wpld;
605         for (j = 0; j < w; j++) {
606             extractRGBValues(lines[j], &rval, &gval, &bval);
607             if (rval < rmin || rval > rmax) continue;
608             if (gval < gmin || gval > gmax) continue;
609             if (bval < bmin || bval > bmax) continue;
610             SET_DATA_BIT(lined, j);
611         }
612     }
613 
614     pixDestroy(&pixc);
615     return pixd;
616 }
617 
618 
619 /* ----------------------------------------------------------------------- *
620  *   Finds the fraction of pixels with "color" that are not close to black *
621  * ----------------------------------------------------------------------- */
622 /*!
623  * \brief   pixColorFraction()
624  *
625  * \param[in]    pixs  32 bpp rgb
626  * \param[in]    darkthresh threshold near black; if the lightest component
627  *                          is below this, the pixel is not considered in
628  *                          the statistics; typ. 20
629  * \param[in]    lightthresh threshold near white; if the darkest component
630  *                           is above this, the pixel is not considered in
631  *                           the statistics; typ. 244
632  * \param[in]    diffthresh thresh for the maximum difference between
633  *                          component value; below this the pixel is not
634  *                          considered to have sufficient color
635  * \param[in]    factor subsampling factor
636  * \param[out]   ppixfract fraction of pixels in intermediate
637  *                         brightness range that were considered
638  *                         for color content
639  * \param[out]   pcolorfract fraction of pixels that meet the
640  *                           criterion for sufficient color; 0.0 on error
641  * \return  0 if OK, 1 on error
642  *
643  * <pre>
644  * Notes:
645  *      (1) This function is asking the question: to what extent does the
646  *          image appear to have color?   The amount of color a pixel
647  *          appears to have depends on both the deviation of the
648  *          individual components from their average and on the average
649  *          intensity itself.  For example, the color will be much more
650  *          obvious with a small deviation from white than the same
651  *          deviation from black.
652  *      (2) Any pixel that meets these three tests is considered a
653  *          colorful pixel:
654  *            (a) the lightest component must equal or exceed %darkthresh
655  *            (b) the darkest component must not exceed %lightthresh
656  *            (c) the max difference between components must equal or
657  *                exceed %diffthresh.
658  *      (3) The dark pixels are removed from consideration because
659  *          they don't appear to have color.
660  *      (4) The very lightest pixels are removed because if an image
661  *          has a lot of "white", the color fraction will be artificially
662  *          low, even if all the other pixels are colorful.
663  *      (5) If pixfract is very small, there are few pixels that are neither
664  *          black nor white.  If colorfract is very small, the pixels
665  *          that are neither black nor white have very little color
666  *          content.  The product 'pixfract * colorfract' gives the
667  *          fraction of pixels with significant color content.
668  *      (6) One use of this function is as a preprocessing step for median
669  *          cut quantization (colorquant2.c), which does a very poor job
670  *          splitting the color space into rectangular volume elements when
671  *          all the pixels are near the diagonal of the color cube.  For
672  *          octree quantization of an image with only gray values, the
673  *          2^(level) octcubes on the diagonal are the only ones
674  *          that can be occupied.
675  * </pre>
676  */
677 l_int32
pixColorFraction(PIX * pixs,l_int32 darkthresh,l_int32 lightthresh,l_int32 diffthresh,l_int32 factor,l_float32 * ppixfract,l_float32 * pcolorfract)678 pixColorFraction(PIX        *pixs,
679                  l_int32     darkthresh,
680                  l_int32     lightthresh,
681                  l_int32     diffthresh,
682                  l_int32     factor,
683                  l_float32  *ppixfract,
684                  l_float32  *pcolorfract)
685 {
686 l_int32    i, j, w, h, wpl, rval, gval, bval, minval, maxval;
687 l_int32    total, npix, ncolor;
688 l_uint32   pixel;
689 l_uint32  *data, *line;
690 
691     PROCNAME("pixColorFraction");
692 
693     if (ppixfract) *ppixfract = 0.0;
694     if (pcolorfract) *pcolorfract = 0.0;
695     if (!ppixfract || !pcolorfract)
696         return ERROR_INT("&pixfract and &colorfract not defined",
697                          procName, 1);
698     if (!pixs || pixGetDepth(pixs) != 32)
699         return ERROR_INT("pixs not defined or not 32 bpp", procName, 1);
700 
701     pixGetDimensions(pixs, &w, &h, NULL);
702     data = pixGetData(pixs);
703     wpl = pixGetWpl(pixs);
704     npix = ncolor = total = 0;
705     for (i = 0; i < h; i += factor) {
706         line = data + i * wpl;
707         for (j = 0; j < w; j += factor) {
708             total++;
709             pixel = line[j];
710             extractRGBValues(pixel, &rval, &gval, &bval);
711             minval = L_MIN(rval, gval);
712             minval = L_MIN(minval, bval);
713             if (minval > lightthresh)  /* near white */
714                 continue;
715             maxval = L_MAX(rval, gval);
716             maxval = L_MAX(maxval, bval);
717             if (maxval < darkthresh)  /* near black */
718                 continue;
719 
720             npix++;
721             if (maxval - minval >= diffthresh)
722                 ncolor++;
723         }
724     }
725 
726     if (npix == 0) {
727         L_WARNING("No pixels found for consideration\n", procName);
728         return 0;
729     }
730     *ppixfract = (l_float32)npix / (l_float32)total;
731     *pcolorfract = (l_float32)ncolor / (l_float32)npix;
732     return 0;
733 }
734 
735 
736 /* ----------------------------------------------------------------------- *
737  *     Determine if there are significant color regions in a page image    *
738  * ----------------------------------------------------------------------- */
739 /*!
740  * \brief   pixFindColorRegions()
741  *
742  * \param[in]    pixs        32 bpp rgb
743  * \param[in]    pixm        [optional] 1 bpp mask image
744  * \param[in]    factor      subsample factor; integer >= 1
745  * \param[in]    lightthresh threshold for component average in lightest
746  *                           of 10 buckets; typ. 210; -1 for default
747  * \param[in]    darkthresh  threshold to eliminate dark pixels (e.g., text)
748  *                           from consideration; typ. 70; -1 for default.
749  * \param[in]    mindiff     minimum difference (b - r) and (g - r), used to
750  *                           find blue or green pixels; typ. 10; -1 for default
751  * \param[in]    colordiff   minimum difference in (max - min) component to
752  *                           qualify as a color pixel; typ. 90; -1 for default
753  * \param[in]    edgefract   fraction of image half-width and half-height
754  *                           for which color pixels are ignored; typ. 0.05.
755  * \param[out]   pcolorfract fraction of 'color' pixels found
756  * \param[out]   pcolormask1 [optional] mask over background color, if any
757  * \param[out]   pcolormask2 [optional] filtered mask over background color
758  * \param[out]   pixadb      [optional] debug intermediate results
759  * \return  0 if OK, 1 on error
760  *
761  * <pre>
762  * Notes:
763  *      (1) This function tries to determine if there is a significant
764  *          color or darker region on a scanned page image, where part
765  *          of the image is background that is either white or reddish.
766  *          This also allows extraction of regions of colored pixels that
767  *          have a smaller red component than blue or green components.
768  *      (2) If %pixm exists, pixels under its fg are combined with
769  *          dark pixels to make a mask of pixels not to be considered
770  *          as color candidates.
771  *      (3) There are four thresholds.
772  *          * %lightthresh: compute the average value of each rgb pixel,
773  *            and make 10 buckets by value.  If the lightest bucket gray
774  *            value is below %lightthresh, the image is not considered
775  *            to have a light bg, and this returns 0.0 for %colorfract.
776  *          * %darkthresh: ignore pixels darker than this (typ. fg text).
777  *            We make a 1 bpp mask of these pixels, and then dilate it to
778  *            remove all vestiges of fg from their vicinity.
779  *          * %mindiff: consider pixels with either (b - r) or (g - r)
780  *            being at least this value, as having color.
781  *          * %colordiff: consider pixels where the (max - min) difference
782  *            of the pixel components exceeds this value, as having color.
783  *      (4) All components of color pixels that are touching the image
784  *          border are removed.  Additionally, all pixels within some
785  *          normalized distance %edgefract from the image border can
786  *          be removed.  This insures that dark pixels near the edge
787  *          of the image are not included.
788  *      (5) This returns in %pcolorfract the fraction of pixels that have
789  *          color and are not in the set consisting of an OR between
790  *          %pixm and the dilated dark pixel mask.
791  *      (6) No masks are returned unless light color pixels are found.
792  *          If colorfract > 0.0 and %pcolormask1 is defined, this returns
793  *          a 1 bpp mask with fg pixels over the color background.
794  *          This mask may have some holes in it.
795  *      (7) If colorfract > 0.0 and %pcolormask2 is defined, this returns
796  *          a version of colormask1 where small holes have been filled.
797  *      (8) To generate a boxa of rectangular regions from the overlap
798  *          of components in the filtered mask:
799  *                boxa1 = pixConnCompBB(colormask2, 8);
800  *                boxa2 = boxaCombineOverlaps(boxa1, NULL);
801  *          This is done here in debug mode.
802  * </pre>
803  */
804 l_int32
pixFindColorRegions(PIX * pixs,PIX * pixm,l_int32 factor,l_int32 lightthresh,l_int32 darkthresh,l_int32 mindiff,l_int32 colordiff,l_float32 edgefract,l_float32 * pcolorfract,PIX ** pcolormask1,PIX ** pcolormask2,PIXA * pixadb)805 pixFindColorRegions(PIX        *pixs,
806                     PIX        *pixm,
807                     l_int32     factor,
808                     l_int32     lightthresh,
809                     l_int32     darkthresh,
810                     l_int32     mindiff,
811                     l_int32     colordiff,
812                     l_float32   edgefract,
813                     l_float32  *pcolorfract,
814                     PIX       **pcolormask1,
815                     PIX       **pcolormask2,
816                     PIXA       *pixadb)
817 {
818 l_int32    w, h, count, rval, gval, bval, aveval, proceed;
819 l_float32  ratio;
820 l_uint32  *carray;
821 BOXA      *boxa1, *boxa2;
822 PIX       *pix1, *pix2, *pix3, *pix4, *pix5, *pixm1, *pixm2, *pixm3;
823 
824     PROCNAME("pixFindColorRegions");
825 
826     if (pcolormask1) *pcolormask1 = NULL;
827     if (pcolormask2) *pcolormask2 = NULL;
828     if (!pcolorfract)
829         return ERROR_INT("&colorfract not defined", procName, 1);
830     *pcolorfract = 0.0;
831     if (!pixs || pixGetDepth(pixs) != 32)
832         return ERROR_INT("pixs not defined or not 32 bpp", procName, 1);
833     if (factor < 1) factor = 1;
834     if (lightthresh < 0) lightthresh = 210;  /* defaults */
835     if (darkthresh < 0) darkthresh = 70;
836     if (mindiff < 0) mindiff = 10;
837     if (colordiff < 0) colordiff = 90;
838     if (edgefract < 0.0 || edgefract > 1.0) edgefract = 0.05;
839 
840         /* Check if pixm covers most of the image.  If so, just return. */
841     pixGetDimensions(pixs, &w, &h, NULL);
842     if (pixm) {
843         pixCountPixels(pixm, &count, NULL);
844         ratio = (l_float32)count / ((l_float32)(w) * h);
845         if (ratio > 0.7) {
846             if (pixadb) L_INFO("pixm has big fg: %f5.2\n", procName, ratio);
847             return 0;
848         }
849     }
850 
851         /* Get the light background color.  Use the average component value
852          * and select the lightest of 10 buckets.  Require that it is
853          * reddish and, using lightthresh, not too dark. */
854     pixGetRankColorArray(pixs, 10, L_SELECT_AVERAGE, factor, &carray, 0, 0);
855     if (!carray)
856         return ERROR_INT("rank color array not made", procName, 1);
857     extractRGBValues(carray[9], &rval, &gval, &bval);
858     if (pixadb) L_INFO("lightest background color: (r,g,b) = (%d,%d,%d)\n",
859                        procName, rval, gval, bval);
860     proceed = TRUE;
861     if ((rval < bval - 2) || (rval < gval - 2)) {
862         if (pixadb) L_INFO("background not reddish\n", procName);
863         proceed = FALSE;
864     }
865     aveval = (rval + gval + bval) / 3;
866     if (aveval < lightthresh) {
867         if (pixadb) L_INFO("background too dark\n", procName);
868         proceed = FALSE;
869     }
870     if (pixadb) {
871         pix1 = pixDisplayColorArray(carray, 10, 120, 3, 6);
872         pixaAddPix(pixadb, pix1, L_INSERT);
873     }
874     LEPT_FREE(carray);
875     if (proceed == FALSE) return 0;
876 
877         /* Make a mask pixm1 over the dark pixels in the image:
878          * convert to gray using the average of the components;
879          * threshold using darkthresh; do a small dilation;
880          * combine with pixm. */
881     pix1 = pixConvertRGBToGray(pixs, 0.33, 0.34, 0.33);
882     if (pixadb) pixaAddPix(pixadb, pix1, L_COPY);
883     pixm1 = pixThresholdToBinary(pix1, darkthresh);
884     pixDilateBrick(pixm1, pixm1, 7, 7);
885     if (pixadb) pixaAddPix(pixadb, pixm1, L_COPY);
886     if (pixm) {
887         pixOr(pixm1, pixm1, pixm);
888         if (pixadb) pixaAddPix(pixadb, pixm1, L_COPY);
889     }
890     pixDestroy(&pix1);
891 
892         /* Make masks over pixels that are bluish, or greenish, or
893            have a very large color saturation (max - min) value. */
894     pixm2 = pixConvertRGBToBinaryArb(pixs, -1.0, 0.0, 1.0, mindiff,
895                                      L_SELECT_IF_GTE);  /* b - r */
896     if (pixadb) pixaAddPix(pixadb, pixm2, L_COPY);
897     pix1 = pixConvertRGBToBinaryArb(pixs, -1.0, 1.0, 0.0, mindiff,
898                                     L_SELECT_IF_GTE);  /* g - r */
899     if (pixadb) pixaAddPix(pixadb, pix1, L_COPY);
900     pixOr(pixm2, pixm2, pix1);
901     pixDestroy(&pix1);
902     pix1 = pixConvertRGBToGrayMinMax(pixs, L_CHOOSE_MAXDIFF);
903     pix2 = pixThresholdToBinary(pix1, colordiff);
904     pixInvert(pix2, pix2);
905     if (pixadb) pixaAddPix(pixadb, pix2, L_COPY);
906     pixOr(pixm2, pixm2, pix2);
907     if (pixadb) pixaAddPix(pixadb, pixm2, L_COPY);
908     pixDestroy(&pix1);
909     pixDestroy(&pix2);
910 
911         /* Subtract the dark pixels represented by pixm1.
912          * pixm2 now holds all the color pixels of interest  */
913     pixSubtract(pixm2, pixm2, pixm1);
914     pixDestroy(&pixm1);
915     if (pixadb) pixaAddPix(pixadb, pixm2, L_COPY);
916 
917         /* But we're not quite finished.  Remove pixels from any component
918          * that is touching the image border.  False color pixels can
919          * sometimes be found there if the image is much darker near
920          * the border, due to oxidation or reduced illumination.  Also
921          * remove any pixels within the normalized fraction %distfract
922          * of the image border. */
923     pixm3 = pixRemoveBorderConnComps(pixm2, 8);
924     pixDestroy(&pixm2);
925     if (edgefract > 0.0) {
926         pix2 = pixMakeFrameMask(w, h, edgefract, 1.0, edgefract, 1.0);
927         pixAnd(pixm3, pixm3, pix2);
928         pixDestroy(&pix2);
929     }
930     if (pixadb) pixaAddPix(pixadb, pixm3, L_COPY);
931 
932         /* Get the fraction of light color pixels */
933     pixCountPixels(pixm3, &count, NULL);
934     *pcolorfract = (l_float32)count / ((l_float32)(w) * h);
935     if (pixadb) {
936         if (count == 0)
937             L_INFO("no light color pixels found\n", procName);
938         else
939             L_INFO("fraction of light color pixels = %5.3f\n", procName,
940                    *pcolorfract);
941     }
942 
943         /* Debug: extract the color pixels from pixs */
944     if (pixadb && count > 0) {
945             /* Use pixm3 to extract the color pixels */
946         pix3 = pixCreateTemplate(pixs);
947         pixSetAll(pix3);
948         pixCombineMasked(pix3, pixs, pixm3);
949         pixaAddPix(pixadb, pix3, L_INSERT);
950 
951             /* Use additional filtering to extract the color pixels */
952         pix3 = pixCloseSafeBrick(NULL, pixm3, 15, 15);
953         pixaAddPix(pixadb, pix3, L_INSERT);
954         pix5 = pixCreateTemplate(pixs);
955         pixSetAll(pix5);
956         pixCombineMasked(pix5, pixs, pix3);
957         pixaAddPix(pixadb, pix5, L_INSERT);
958 
959             /* Get the combined bounding boxes of the mask components
960              * in pix3, and extract those pixels from pixs. */
961         boxa1 = pixConnCompBB(pix3, 8);
962         boxa2 = boxaCombineOverlaps(boxa1, NULL);
963         pix4 = pixCreateTemplate(pix3);
964         pixMaskBoxa(pix4, pix4, boxa2, L_SET_PIXELS);
965         pixaAddPix(pixadb, pix4, L_INSERT);
966         pix5 = pixCreateTemplate(pixs);
967         pixSetAll(pix5);
968         pixCombineMasked(pix5, pixs, pix4);
969         pixaAddPix(pixadb, pix5, L_INSERT);
970         boxaDestroy(&boxa1);
971         boxaDestroy(&boxa2);
972     }
973     pixaAddPix(pixadb, pixs, L_COPY);
974 
975         /* Optional colormask returns */
976     if (pcolormask2 && count > 0)
977         *pcolormask2 = pixCloseSafeBrick(NULL, pixm3, 15, 15);
978     if (pcolormask1 && count > 0)
979         *pcolormask1 = pixm3;
980     else
981         pixDestroy(&pixm3);
982     return 0;
983 }
984 
985 
986 /* ----------------------------------------------------------------------- *
987  *      Finds the number of perceptually significant gray intensities      *
988  *      in a grayscale image.                                              *
989  * ----------------------------------------------------------------------- */
990 /*!
991  * \brief   pixNumSignificantGrayColors()
992  *
993  * \param[in]    pixs  8 bpp gray
994  * \param[in]    darkthresh dark threshold for minimum intensity to be
995  *                          considered; typ. 20
996  * \param[in]    lightthresh threshold near white, for maximum intensity
997  *                           to be considered; typ. 236
998  * \param[in]    minfract minimum fraction of all pixels to include a level
999  *                        as significant; typ. 0.0001; should be < 0.001
1000  * \param[in]    factor subsample factor; integer >= 1
1001  * \param[out]   pncolors number of significant colors; 0 on error
1002  * \return  0 if OK, 1 on error
1003  *
1004  * <pre>
1005  * Notes:
1006  *      (1) This function is asking the question: how many perceptually
1007  *          significant gray color levels is in this pix?
1008  *          A color level must meet 3 criteria to be significant:
1009  *            ~ it can't be too close to black
1010  *            ~ it can't be too close to white
1011  *            ~ it must have at least some minimum fractional population
1012  *      (2) Use -1 for default values for darkthresh, lightthresh and minfract.
1013  *      (3) Choose default of darkthresh = 20, because variations in very
1014  *          dark pixels are not visually significant.
1015  *      (4) Choose default of lightthresh = 236, because document images
1016  *          that have been jpeg'd typically have near-white pixels in the
1017  *          8x8 jpeg blocks, and these should not be counted.  It is desirable
1018  *          to obtain a clean image by quantizing this noise away.
1019  * </pre>
1020  */
1021 l_int32
pixNumSignificantGrayColors(PIX * pixs,l_int32 darkthresh,l_int32 lightthresh,l_float32 minfract,l_int32 factor,l_int32 * pncolors)1022 pixNumSignificantGrayColors(PIX       *pixs,
1023                             l_int32    darkthresh,
1024                             l_int32    lightthresh,
1025                             l_float32  minfract,
1026                             l_int32    factor,
1027                             l_int32   *pncolors)
1028 {
1029 l_int32  i, w, h, count, mincount, ncolors;
1030 NUMA    *na;
1031 
1032     PROCNAME("pixNumSignificantGrayColors");
1033 
1034     if (!pncolors)
1035         return ERROR_INT("&ncolors not defined", procName, 1);
1036     *pncolors = 0;
1037     if (!pixs || pixGetDepth(pixs) != 8)
1038         return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
1039     if (darkthresh < 0) darkthresh = 20;  /* defaults */
1040     if (lightthresh < 0) lightthresh = 236;
1041     if (minfract < 0.0) minfract = 0.0001;
1042     if (minfract > 1.0)
1043         return ERROR_INT("minfract > 1.0", procName, 1);
1044     if (minfract >= 0.001)
1045         L_WARNING("minfract too big; likely to underestimate ncolors\n",
1046                   procName);
1047     if (lightthresh > 255 || darkthresh >= lightthresh)
1048         return ERROR_INT("invalid thresholds", procName, 1);
1049     if (factor < 1) factor = 1;
1050 
1051     pixGetDimensions(pixs, &w, &h, NULL);
1052     mincount = (l_int32)(minfract * w * h * factor * factor);
1053     if ((na = pixGetGrayHistogram(pixs, factor)) == NULL)
1054         return ERROR_INT("na not made", procName, 1);
1055     ncolors = 2;  /* add in black and white */
1056     for (i = darkthresh; i <= lightthresh; i++) {
1057         numaGetIValue(na, i, &count);
1058         if (count >= mincount)
1059             ncolors++;
1060     }
1061 
1062     *pncolors = ncolors;
1063     numaDestroy(&na);
1064     return 0;
1065 }
1066 
1067 
1068 /* ----------------------------------------------------------------------- *
1069  *   Identifies images where color quantization will cause posterization   *
1070  *   due to the existence of many colors in low-gradient regions.          *
1071  * ----------------------------------------------------------------------- */
1072 /*!
1073  * \brief   pixColorsForQuantization()
1074  * \param[in]    pixs 8 bpp gray or 32 bpp rgb; with or without colormap
1075  * \param[in]    thresh binary threshold on edge gradient; 0 for default
1076  * \param[out]   pncolors the number of colors found
1077  * \param[out]   piscolor [optional] 1 if significant color is found;
1078  *                        0 otherwise.  If pixs is 8 bpp, and does not have
1079  *                        a colormap with color entries, this is 0
1080  * \param[in]    debug 1 to output masked image that is tested for colors;
1081  *                     0 otherwise
1082  * \return  0 if OK, 1 on error.
1083  *
1084  * <pre>
1085  * Notes:
1086  *      (1) This function finds a measure of the number of colors that are
1087  *          found in low-gradient regions of an image.  By its
1088  *          magnitude relative to some threshold (not specified in
1089  *          this function), it gives a good indication of whether
1090  *          quantization will generate posterization.   This number
1091  *          is larger for images with regions of slowly varying
1092  *          intensity (if 8 bpp) or color (if rgb). Such images, if
1093  *          quantized, may require dithering to avoid posterization,
1094  *          and lossless compression is then expected to be poor.
1095  *      (2) If pixs has a colormap, the number of colors returned is
1096  *          the number in the colormap.
1097  *      (3) It is recommended that document images be reduced to a width
1098  *          of 800 pixels before applying this function.  Then it can
1099  *          be expected that color detection will be fairly accurate
1100  *          and the number of colors will reflect both the content and
1101  *          the type of compression to be used.  For less than 15 colors,
1102  *          there is unlikely to be a halftone image, and lossless
1103  *          quantization should give both a good visual result and
1104  *          better compression.
1105  *      (4) When using the default threshold on the gradient (15),
1106  *          images (both gray and rgb) where ncolors is greater than
1107  *          about 15 will compress poorly with either lossless
1108  *          compression or dithered quantization, and they may be
1109  *          posterized with non-dithered quantization.
1110  *      (5) For grayscale images, or images without significant color,
1111  *          this returns the number of significant gray levels in
1112  *          the low-gradient regions.  The actual number of gray levels
1113  *          can be large due to jpeg compression noise in the background.
1114  *      (6) Similarly, for color images, the actual number of different
1115  *          (r,g,b) colors in the low-gradient regions (rather than the
1116  *          number of occupied level 4 octcubes) can be quite large, e.g.,
1117  *          due to jpeg compression noise, even for regions that appear
1118  *          to be of a single color.  By quantizing to level 4 octcubes,
1119  *          most of these superfluous colors are removed from the counting.
1120  *      (7) The image is tested for color.  If there is very little color,
1121  *          it is thresholded to gray and the number of gray levels in
1122  *          the low gradient regions is found.  If the image has color,
1123  *          the number of occupied level 4 octcubes is found.
1124  *      (8) The number of colors in the low-gradient regions increases
1125  *          monotonically with the threshold %thresh on the edge gradient.
1126  *      (9) Background: grayscale and color quantization is often useful
1127  *          to achieve highly compressed images with little visible
1128  *          distortion.  However, gray or color washes (regions of
1129  *          low gradient) can defeat this approach to high compression.
1130  *          How can one determine if an image is expected to compress
1131  *          well using gray or color quantization?  We use the fact that
1132  *            * gray washes, when quantized with less than 50 intensities,
1133  *              have posterization (visible boundaries between regions
1134  *              of uniform 'color') and poor lossless compression
1135  *            * color washes, when quantized with level 4 octcubes,
1136  *              typically result in both posterization and the occupancy
1137  *              of many level 4 octcubes.
1138  *          Images can have colors either intrinsically or as jpeg
1139  *          compression artifacts.  This function reduces but does not
1140  *          completely eliminate measurement of jpeg quantization noise
1141  *          in the white background of grayscale or color images.
1142  * </pre>
1143  */
1144 l_int32
pixColorsForQuantization(PIX * pixs,l_int32 thresh,l_int32 * pncolors,l_int32 * piscolor,l_int32 debug)1145 pixColorsForQuantization(PIX      *pixs,
1146                          l_int32   thresh,
1147                          l_int32  *pncolors,
1148                          l_int32  *piscolor,
1149                          l_int32   debug)
1150 {
1151 l_int32    w, h, d, minside, factor;
1152 l_float32  pixfract, colorfract;
1153 PIX       *pixt, *pixsc, *pixg, *pixe, *pixb, *pixm;
1154 PIXCMAP   *cmap;
1155 
1156     PROCNAME("pixColorsForQuantization");
1157 
1158     if (piscolor) *piscolor = 0;
1159     if (!pncolors)
1160         return ERROR_INT("&ncolors not defined", procName, 1);
1161     *pncolors = 0;
1162     if (!pixs)
1163         return ERROR_INT("pixs not defined", procName, 1);
1164     if ((cmap = pixGetColormap(pixs)) != NULL) {
1165         *pncolors = pixcmapGetCount(cmap);
1166         if (piscolor)
1167             pixcmapHasColor(cmap, piscolor);
1168         return 0;
1169     }
1170 
1171     pixGetDimensions(pixs, &w, &h, &d);
1172     if (d != 8 && d != 32)
1173         return ERROR_INT("pixs not 8 or 32 bpp", procName, 1);
1174     if (thresh <= 0)
1175         thresh = 15;
1176 
1177         /* First test if 32 bpp has any significant color; if not,
1178          * convert it to gray.  Colors whose average values are within
1179          * 20 of black or 8 of white are ignored because they're not
1180          * very 'colorful'.  If less than 2.5/10000 of the pixels have
1181          * significant color, consider the image to be gray. */
1182     minside = L_MIN(w, h);
1183     if (d == 8) {
1184         pixt = pixClone(pixs);
1185     } else {  /* d == 32 */
1186         factor = L_MAX(1, minside / 400);
1187         pixColorFraction(pixs, 20, 248, 30, factor, &pixfract, &colorfract);
1188         if (pixfract * colorfract < 0.00025) {
1189             pixt = pixGetRGBComponent(pixs, COLOR_RED);
1190             d = 8;
1191         } else {  /* d == 32 */
1192             pixt = pixClone(pixs);
1193             if (piscolor)
1194                 *piscolor = 1;
1195         }
1196     }
1197 
1198         /* If the smallest side is less than 1000, do not downscale.
1199          * If it is in [1000 ... 2000), downscale by 2x.  If it is >= 2000,
1200          * downscale by 4x.  Factors of 2 are chosen for speed.  The
1201          * actual resolution at which subsequent calculations take place
1202          * is not strongly dependent on downscaling.  */
1203     factor = L_MAX(1, minside / 500);
1204     if (factor == 1)
1205         pixsc = pixCopy(NULL, pixt);  /* to be sure pixs is unchanged */
1206     else if (factor == 2 || factor == 3)
1207         pixsc = pixScaleAreaMap2(pixt);
1208     else
1209         pixsc = pixScaleAreaMap(pixt, 0.25, 0.25);
1210 
1211         /* Basic edge mask generation procedure:
1212          *   ~ work on a grayscale image
1213          *   ~ get a 1 bpp edge mask by using an edge filter and
1214          *     thresholding to get fg pixels at the edges
1215          *   ~ for gray, dilate with a 3x3 brick Sel to get mask over
1216          *     all pixels within a distance of 1 pixel from the nearest
1217          *     edge pixel
1218          *   ~ for color, dilate with a 7x7 brick Sel to get mask over
1219          *     all pixels within a distance of 3 pixels from the nearest
1220          *     edge pixel  */
1221     if (d == 8)
1222         pixg = pixClone(pixsc);
1223     else  /* d == 32 */
1224         pixg = pixConvertRGBToLuminance(pixsc);
1225     pixe = pixSobelEdgeFilter(pixg, L_ALL_EDGES);
1226     pixb = pixThresholdToBinary(pixe, thresh);
1227     pixInvert(pixb, pixb);
1228     if (d == 8)
1229         pixm = pixMorphSequence(pixb, "d3.3", 0);
1230     else
1231         pixm = pixMorphSequence(pixb, "d7.7", 0);
1232 
1233         /* Mask the near-edge pixels to white, and count the colors.
1234          * If grayscale, don't count colors within 20 levels of
1235          * black or white, and only count colors with a fraction
1236          * of at least 1/10000 of the image pixels.
1237          * If color, count the number of level 4 octcubes that
1238          * contain at least 20 pixels.  These magic numbers are guesses
1239          * as to what might work, based on a small data set.  Results
1240          * should not be overly sensitive to their actual values. */
1241     if (d == 8) {
1242         pixSetMasked(pixg, pixm, 0xff);
1243         if (debug) pixWrite("junkpix8.png", pixg, IFF_PNG);
1244         pixNumSignificantGrayColors(pixg, 20, 236, 0.0001, 1, pncolors);
1245     } else {  /* d == 32 */
1246         pixSetMasked(pixsc, pixm, 0xffffffff);
1247         if (debug) pixWrite("junkpix32.png", pixsc, IFF_PNG);
1248         pixNumberOccupiedOctcubes(pixsc, 4, 20, -1, pncolors);
1249     }
1250 
1251     pixDestroy(&pixt);
1252     pixDestroy(&pixsc);
1253     pixDestroy(&pixg);
1254     pixDestroy(&pixe);
1255     pixDestroy(&pixb);
1256     pixDestroy(&pixm);
1257     return 0;
1258 }
1259 
1260 
1261 /* ----------------------------------------------------------------------- *
1262  *               Finds the number of unique colors in an image             *
1263  * ----------------------------------------------------------------------- */
1264 /*!
1265  * \brief   pixNumColors()
1266  * \param[in]    pixs 2, 4, 8, 32 bpp
1267  * \param[in]    factor subsampling factor; integer
1268  * \param[out]   pncolors the number of colors found, or 0 if
1269  *                        there are more than 256
1270  * \return  0 if OK, 1 on error.
1271  *
1272  * <pre>
1273  * Notes:
1274  *      (1) This returns the actual number of colors found in the image,
1275  *          even if there is a colormap.  If %factor == 1 and the
1276  *          number of colors differs from the number of entries
1277  *          in the colormap, a warning is issued.
1278  *      (2) Use %factor == 1 to find the actual number of colors.
1279  *          Use %factor > 1 to quickly find the approximate number of colors.
1280  *      (3) For d = 2, 4 or 8 bpp grayscale, this returns the number
1281  *          of colors found in the image in 'ncolors'.
1282  *      (4) For d = 32 bpp (rgb), if the number of colors is
1283  *          greater than 256, this returns 0 in 'ncolors'.
1284  * </pre>
1285  */
1286 l_int32
pixNumColors(PIX * pixs,l_int32 factor,l_int32 * pncolors)1287 pixNumColors(PIX      *pixs,
1288              l_int32   factor,
1289              l_int32  *pncolors)
1290 {
1291 l_int32    w, h, d, i, j, wpl, hashsize, sum, count;
1292 l_int32    rval, gval, bval, val;
1293 l_int32   *inta;
1294 l_uint32   pixel;
1295 l_uint32  *data, *line;
1296 PIXCMAP   *cmap;
1297 
1298     PROCNAME("pixNumColors");
1299 
1300     if (!pncolors)
1301         return ERROR_INT("&ncolors not defined", procName, 1);
1302     *pncolors = 0;
1303     if (!pixs)
1304         return ERROR_INT("pixs not defined", procName, 1);
1305     pixGetDimensions(pixs, &w, &h, &d);
1306     if (d != 2 && d != 4 && d != 8 && d != 32)
1307         return ERROR_INT("d not in {2, 4, 8, 32}", procName, 1);
1308     if (factor < 1) factor = 1;
1309 
1310     data = pixGetData(pixs);
1311     wpl = pixGetWpl(pixs);
1312     sum = 0;
1313     if (d != 32) {  /* grayscale */
1314         if ((inta = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32))) == NULL)
1315             return ERROR_INT("calloc failure for inta", procName, 1);
1316         for (i = 0; i < h; i += factor) {
1317             line = data + i * wpl;
1318             for (j = 0; j < w; j += factor) {
1319                 if (d == 8)
1320                     val = GET_DATA_BYTE(line, j);
1321                 else if (d == 4)
1322                     val = GET_DATA_QBIT(line, j);
1323                 else  /* d == 2 */
1324                     val = GET_DATA_DIBIT(line, j);
1325                 inta[val] = 1;
1326             }
1327         }
1328         for (i = 0; i < 256; i++)
1329             if (inta[i]) sum++;
1330         *pncolors = sum;
1331         LEPT_FREE(inta);
1332 
1333         cmap = pixGetColormap(pixs);
1334         if (cmap && factor == 1) {
1335             count = pixcmapGetCount(cmap);
1336             if (sum != count)
1337                 L_WARNING("colormap size %d differs from actual colors\n",
1338                           procName, count);
1339         }
1340         return 0;
1341     }
1342 
1343         /* 32 bpp rgb; quit if we get above 256 colors */
1344     hashsize = 5507;  /* big and prime; collisions are not likely */
1345     if ((inta = (l_int32 *)LEPT_CALLOC(hashsize, sizeof(l_int32))) == NULL)
1346         return ERROR_INT("calloc failure with hashsize", procName, 1);
1347     for (i = 0; i < h; i += factor) {
1348         line = data + i * wpl;
1349         for (j = 0; j < w; j += factor) {
1350             pixel = line[j];
1351             extractRGBValues(pixel, &rval, &gval, &bval);
1352             val = (137 * rval + 269 * gval + 353 * bval) % hashsize;
1353             if (inta[val] == 0) {
1354                 inta[val] = 1;
1355                 sum++;
1356                 if (sum > 256) {
1357                     LEPT_FREE(inta);
1358                     return 0;
1359                 }
1360             }
1361         }
1362     }
1363 
1364     *pncolors = sum;
1365     LEPT_FREE(inta);
1366     return 0;
1367 }
1368 
1369 
1370 /* ----------------------------------------------------------------------- *
1371  *       Find the most "populated" colors in the image (and quantize)      *
1372  * ----------------------------------------------------------------------- */
1373 /*!
1374  * \brief   pixGetMostPopulatedColors()
1375  * \param[in]    pixs 32 bpp rgb
1376  * \param[in]    sigbits 2-6, significant bits retained in the quantizer
1377  *                       for each component of the input image
1378  * \param[in]    factor subsampling factor; use 1 for no subsampling
1379  * \param[in]    ncolors the number of most populated colors to select
1380  * \param[out]   parray [optional] array of colors, each as 0xrrggbb00
1381  * \param[out]   pcmap [optional] colormap of the colors
1382  * \return  0 if OK, 1 on error
1383  *
1384  * <pre>
1385  * Notes:
1386  *      (1) This finds the %ncolors most populated cubes in rgb colorspace,
1387  *          where the cube size depends on %sigbits as
1388  *               cube side = (256 >> sigbits)
1389  *      (2) The rgb color components are found at the center of the cube.
1390  *      (3) The output array of colors can be displayed using
1391  *               pixDisplayColorArray(array, ncolors, ...);
1392  * </pre>
1393  */
1394 l_int32
pixGetMostPopulatedColors(PIX * pixs,l_int32 sigbits,l_int32 factor,l_int32 ncolors,l_uint32 ** parray,PIXCMAP ** pcmap)1395 pixGetMostPopulatedColors(PIX        *pixs,
1396                           l_int32     sigbits,
1397                           l_int32     factor,
1398                           l_int32     ncolors,
1399                           l_uint32  **parray,
1400                           PIXCMAP   **pcmap)
1401 {
1402 l_int32  n, i, rgbindex, rval, gval, bval;
1403 NUMA    *nahisto, *naindex;
1404 
1405     PROCNAME("pixGetMostPopulatedColors");
1406 
1407     if (!parray && !pcmap)
1408         return ERROR_INT("no return val requested", procName, 1);
1409     if (parray) *parray = NULL;
1410     if (pcmap) *pcmap = NULL;
1411     if (!pixs || pixGetDepth(pixs) != 32)
1412         return ERROR_INT("pixs not defined", procName, 1);
1413     if (sigbits < 2 || sigbits > 6)
1414         return ERROR_INT("sigbits not in [2 ... 6]", procName, 1);
1415     if (factor < 1 || ncolors < 1)
1416         return ERROR_INT("factor < 1 or ncolors < 1", procName, 1);
1417 
1418     if ((nahisto = pixGetRGBHistogram(pixs, sigbits, factor)) == NULL)
1419         return ERROR_INT("nahisto not made", procName, 1);
1420 
1421         /* naindex contains the index into nahisto, which is the rgbindex */
1422     naindex = numaSortIndexAutoSelect(nahisto, L_SORT_DECREASING);
1423     numaDestroy(&nahisto);
1424     if (!naindex)
1425         return ERROR_INT("naindex not made", procName, 1);
1426 
1427     n = numaGetCount(naindex);
1428     ncolors = L_MIN(n, ncolors);
1429     if (parray) *parray = (l_uint32 *)LEPT_CALLOC(ncolors, sizeof(l_uint32));
1430     if (pcmap) *pcmap = pixcmapCreate(8);
1431     for (i = 0; i < ncolors; i++) {
1432         numaGetIValue(naindex, i, &rgbindex);  /* rgb index */
1433         getRGBFromIndex(rgbindex, sigbits, &rval, &gval, &bval);
1434         if (parray) composeRGBPixel(rval, gval, bval, *parray + i);
1435         if (pcmap) pixcmapAddColor(*pcmap, rval, gval, bval);
1436     }
1437 
1438     numaDestroy(&naindex);
1439     return 0;
1440 }
1441 
1442 
1443 /*!
1444  * \brief   pixSimpleColorQuantize()
1445  * \param[in]    pixs 32 bpp rgb
1446  * \param[in]    sigbits 2-4, significant bits retained in the quantizer
1447  *                       for each component of the input image
1448  * \param[in]    factor subsampling factor; use 1 for no subsampling
1449  * \param[in]    ncolors the number of most populated colors to select
1450  * \return  pixd 8 bpp cmapped or NULL on error
1451  *
1452  * <pre>
1453  * Notes:
1454  *      (1) If you want to do color quantization for real, use octcube
1455  *          or modified median cut.  This function shows that it is
1456  *          easy to make a simple quantizer based solely on the population
1457  *          in cells of a given size in rgb color space.
1458  *      (2) The %ncolors most populated cells at the %sigbits level form
1459  *          the colormap for quantizing, and this uses octcube indexing
1460  *          under the covers to assign each pixel to the nearest color.
1461  *      (3) %sigbits is restricted to 2, 3 and 4.  At the low end, the
1462  *          color discrimination is very crude; at the upper end, a set of
1463  *          similar colors can dominate the result.  Interesting results
1464  *          are generally found for %sigbits = 3 and ncolors ~ 20.
1465  *      (4) See also pixColorSegment() for a method of quantizing the
1466  *          colors to generate regions of similar color.
1467  * </pre>
1468  */
1469 PIX *
pixSimpleColorQuantize(PIX * pixs,l_int32 sigbits,l_int32 factor,l_int32 ncolors)1470 pixSimpleColorQuantize(PIX        *pixs,
1471                        l_int32     sigbits,
1472                        l_int32     factor,
1473                        l_int32     ncolors)
1474 {
1475 l_int32   w, h;
1476 PIX      *pixd;
1477 PIXCMAP  *cmap;
1478 
1479     PROCNAME("pixSimpleColorQuantize");
1480 
1481     if (!pixs || pixGetDepth(pixs) != 32)
1482         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1483     if (sigbits < 2 || sigbits > 4)
1484         return (PIX *)ERROR_PTR("sigbits not in {2,3,4}", procName, NULL);
1485 
1486     pixGetMostPopulatedColors(pixs, sigbits, factor, ncolors, NULL, &cmap);
1487     pixGetDimensions(pixs, &w, &h, NULL);
1488     pixd = pixCreate(w, h, 8);
1489     pixSetColormap(pixd, cmap);
1490     pixAssignToNearestColor(pixd, pixs, NULL, 4, NULL);
1491     return pixd;
1492 }
1493 
1494 
1495 /* ----------------------------------------------------------------------- *
1496  *            Constructs a color histogram based on rgb indices            *
1497  * ----------------------------------------------------------------------- */
1498 /*!
1499  * \brief   pixGetRGBHistogram()
1500  * \param[in]    pixs 32 bpp rgb
1501  * \param[in]    sigbits 2-6, significant bits retained in the quantizer
1502  *                       for each component of the input image
1503  * \param[in]    factor subsampling factor; use 1 for no subsampling
1504  * \return  numa histogram of colors, indexed by RGB
1505  *                    components, or NULL on error
1506  *
1507  * <pre>
1508  * Notes:
1509  *      (1) This uses a simple, fast method of indexing into an rgb image.
1510  *      (2) The output is a 1D histogram of count vs. rgb-index, which
1511  *          uses red sigbits as the most significant and blue as the least.
1512  *      (3) This function produces the same result as pixMedianCutHisto().
1513  * </pre>
1514  */
1515 NUMA *
pixGetRGBHistogram(PIX * pixs,l_int32 sigbits,l_int32 factor)1516 pixGetRGBHistogram(PIX     *pixs,
1517                    l_int32  sigbits,
1518                    l_int32  factor)
1519 {
1520 l_int32     w, h, i, j, size, wpl, rval, gval, bval, npts;
1521 l_uint32    val32, rgbindex;
1522 l_float32  *array;
1523 l_uint32   *data, *line, *rtab, *gtab, *btab;
1524 NUMA       *na;
1525 
1526     PROCNAME("pixGetRGBHistogram");
1527 
1528     if (!pixs || pixGetDepth(pixs) != 32)
1529         return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL);
1530     if (sigbits < 2 || sigbits > 6)
1531         return (NUMA *)ERROR_PTR("sigbits not in [2 ... 6]", procName, NULL);
1532     if (factor < 1)
1533         return (NUMA *)ERROR_PTR("factor < 1", procName, NULL);
1534 
1535         /* Get histogram size: 2^(3 * sigbits) */
1536     size = 1 << (3 * sigbits);  /* 64, 512, 4096, 32768, 262144 */
1537     na = numaMakeConstant(0, size);  /* init to all 0 */
1538     array = numaGetFArray(na, L_NOCOPY);
1539 
1540     makeRGBIndexTables(&rtab, &gtab, &btab, sigbits);
1541 
1542         /* Check the number of sampled pixels */
1543     pixGetDimensions(pixs, &w, &h, NULL);
1544     npts = ((w + factor - 1) / factor) * ((h + factor - 1) / factor);
1545     if (npts < 1000)
1546         L_WARNING("only sampling %d pixels\n", procName, npts);
1547     wpl = pixGetWpl(pixs);
1548     data = pixGetData(pixs);
1549     for (i = 0; i < h; i += factor) {
1550         line = data + i * wpl;
1551         for (j = 0; j < w; j += factor) {
1552             val32 = *(line + j);
1553             extractRGBValues(val32, &rval, &gval, &bval);
1554             rgbindex = rtab[rval] | gtab[gval] | btab[bval];
1555             array[rgbindex]++;
1556         }
1557     }
1558 
1559     LEPT_FREE(rtab);
1560     LEPT_FREE(gtab);
1561     LEPT_FREE(btab);
1562     return na;
1563 }
1564 
1565 
1566 /*!
1567  * \brief   makeRGBIndexTables()
1568  *
1569  * \param[out]   prtab, pgtab, pbtab 256-entry index tables
1570  * \param[in]    sigbits 2-6, significant bits retained in the quantizer
1571  *                       for each component of the input image
1572  * \return  0 if OK, 1 on error
1573  *
1574  * <pre>
1575  * Notes:
1576  *      (1) These tables are used to map from rgb sample values to
1577  *          an rgb index, using
1578  *             rgbindex = rtab[rval] | gtab[gval] | btab[bval]
1579  *          where, e.g., if sigbits = 3, the index is a 9 bit integer:
1580  *             r7 r6 r5 g7 g6 g5 b7 b6 b5
1581  * </pre>
1582  */
1583 l_int32
makeRGBIndexTables(l_uint32 ** prtab,l_uint32 ** pgtab,l_uint32 ** pbtab,l_int32 sigbits)1584 makeRGBIndexTables(l_uint32  **prtab,
1585                    l_uint32  **pgtab,
1586                    l_uint32  **pbtab,
1587                    l_int32     sigbits)
1588 {
1589 l_int32    i;
1590 l_uint32  *rtab, *gtab, *btab;
1591 
1592     PROCNAME("makeRGBIndexTables");
1593 
1594     if (prtab) *prtab = NULL;
1595     if (pgtab) *pgtab = NULL;
1596     if (pbtab) *pbtab = NULL;
1597     if (!prtab || !pgtab || !pbtab)
1598         return ERROR_INT("not all table ptrs defined", procName, 1);
1599     if (sigbits < 2 || sigbits > 6)
1600         return ERROR_INT("sigbits not in [2 ... 6]", procName, 1);
1601 
1602     rtab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32));
1603     gtab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32));
1604     btab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32));
1605     if (!rtab || !gtab || !btab)
1606         return ERROR_INT("calloc fail for tab", procName, 1);
1607     *prtab = rtab;
1608     *pgtab = gtab;
1609     *pbtab = btab;
1610     switch (sigbits) {
1611     case 2:
1612         for (i = 0; i < 256; i++) {
1613             rtab[i] = (i & 0xc0) >> 2;
1614             gtab[i] = (i & 0xc0) >> 4;
1615             btab[i] = (i & 0xc0) >> 6;
1616         }
1617         break;
1618     case 3:
1619         for (i = 0; i < 256; i++) {
1620             rtab[i] = (i & 0xe0) << 1;
1621             gtab[i] = (i & 0xe0) >> 2;
1622             btab[i] = (i & 0xe0) >> 5;
1623         }
1624         break;
1625     case 4:
1626         for (i = 0; i < 256; i++) {
1627             rtab[i] = (i & 0xf0) << 4;
1628             gtab[i] = (i & 0xf0);
1629             btab[i] = (i & 0xf0) >> 4;
1630         }
1631         break;
1632     case 5:
1633         for (i = 0; i < 256; i++) {
1634           rtab[i] = (i & 0xf8) << 7;
1635           gtab[i] = (i & 0xf8) << 2;
1636           btab[i] = (i & 0xf8) >> 3;
1637         }
1638         break;
1639     case 6:
1640         for (i = 0; i < 256; i++) {
1641           rtab[i] = (i & 0xfc) << 10;
1642           gtab[i] = (i & 0xfc) << 4;
1643           btab[i] = (i & 0xfc) >> 2;
1644         }
1645         break;
1646     default:
1647         L_ERROR("Illegal sigbits = %d\n", procName, sigbits);
1648         return ERROR_INT("sigbits not in [2 ... 6]", procName, 1);
1649     }
1650 
1651     return 0;
1652 }
1653 
1654 
1655 /*!
1656  * \brief   getRGBFromIndex()
1657  *
1658  * \param[in]    index rgbindex
1659  * \param[in]    sigbits 2-6, significant bits retained in the quantizer
1660  *                       for each component of the input image
1661  * \param[out]   prval, pgval, pbval rgb values
1662  * \return  0 if OK, 1 on error
1663  *
1664  * <pre>
1665  * Notes:
1666  *      (1) The %index is expressed in bits, based on the the
1667  *          %sigbits of the r, g and b components, as
1668  *             r7 r6 ... g7 g6 ... b7 b6 ...
1669  *      (2) The computed rgb values are in the center of the quantized cube.
1670  *          The extra bit that is OR'd accomplishes this.
1671  * </pre>
1672  */
1673 l_int32
getRGBFromIndex(l_uint32 index,l_int32 sigbits,l_int32 * prval,l_int32 * pgval,l_int32 * pbval)1674 getRGBFromIndex(l_uint32  index,
1675                 l_int32   sigbits,
1676                 l_int32  *prval,
1677                 l_int32  *pgval,
1678                 l_int32  *pbval)
1679 {
1680     PROCNAME("getRGBFromIndex");
1681 
1682     if (prval) *prval = 0;
1683     if (pgval) *pgval = 0;
1684     if (pbval) *pbval = 0;
1685     if (!prval || !pgval || !pbval)
1686         return ERROR_INT("not all component ptrs defined", procName, 1);
1687     if (sigbits < 2 || sigbits > 6)
1688         return ERROR_INT("sigbits not in [2 ... 6]", procName, 1);
1689 
1690     switch (sigbits) {
1691     case 2:
1692         *prval = ((index << 2) & 0xc0) | 0x20;
1693         *pgval = ((index << 4) & 0xc0) | 0x20;
1694         *pbval = ((index << 6) & 0xc0) | 0x20;
1695         break;
1696     case 3:
1697         *prval = ((index >> 1) & 0xe0) | 0x10;
1698         *pgval = ((index << 2) & 0xe0) | 0x10;
1699         *pbval = ((index << 5) & 0xe0) | 0x10;
1700         break;
1701     case 4:
1702         *prval = ((index >> 4) & 0xf0) | 0x08;
1703         *pgval = (index & 0xf0) | 0x08;
1704         *pbval = ((index << 4) & 0xf0) | 0x08;
1705         break;
1706     case 5:
1707         *prval = ((index >> 7) & 0xf8) | 0x04;
1708         *pgval = ((index >> 2) & 0xf8) | 0x04;
1709         *pbval = ((index << 3) & 0xf8) | 0x04;
1710         break;
1711     case 6:
1712         *prval = ((index >> 10) & 0xfc) | 0x02;
1713         *pgval = ((index >> 4) & 0xfc) | 0x02;
1714         *pbval = ((index << 2) & 0xfc) | 0x02;
1715         break;
1716     default:
1717         L_ERROR("Illegal sigbits = %d\n", procName, sigbits);
1718         return ERROR_INT("sigbits not in [2 ... 6]", procName, 1);
1719     }
1720 
1721     return 0;
1722 }
1723 
1724 
1725 /* ----------------------------------------------------------------------- *
1726  *             Identify images that have highlight (red) color             *
1727  * ----------------------------------------------------------------------- */
1728 /*!
1729  * \brief   pixHasHighlightRed()
1730  *
1731  * \param[in]    pixs  32 bpp rgb
1732  * \param[in]    factor subsampling; an integer >= 1; use 1 for all pixels
1733  * \param[in]    fract threshold fraction of all image pixels
1734  * \param[in]    fthresh threshold on a function of the components; typ. ~2.5
1735  * \param[out]   phasred 1 if red pixels are above threshold
1736  * \param[out]   pratio [optional] normalized fraction of threshold
1737  *                      red pixels that is actually observed
1738  * \param[out]   ppixdb [optional] seed pixel mask
1739  * \return  0 if OK, 1 on error
1740  *
1741  * <pre>
1742  * Notes:
1743  *      (1) Pixels are identified as red if they satisfy two conditions:
1744  *          (a) The components satisfy (R-B)/B > %fthresh   (red or dark fg)
1745  *          (b) The red component satisfied R > 128  (red or light bg)
1746  *          Masks are generated for (a) and (b), and the intersection
1747  *          gives the pixels that are red but not either light bg or
1748  *          dark fg.
1749  *      (2) A typical value for fract = 0.0001, which gives sensitivity
1750  *          to an image where a small fraction of the pixels are printed
1751  *          in red.
1752  *      (3) A typical value for fthresh = 2.5.  Higher values give less
1753  *          sensitivity to red, and fewer false positives.
1754  * </pre>
1755  */
1756 l_int32
pixHasHighlightRed(PIX * pixs,l_int32 factor,l_float32 fract,l_float32 fthresh,l_int32 * phasred,l_float32 * pratio,PIX ** ppixdb)1757 pixHasHighlightRed(PIX        *pixs,
1758                    l_int32     factor,
1759                    l_float32   fract,
1760                    l_float32   fthresh,
1761                    l_int32    *phasred,
1762                    l_float32  *pratio,
1763                    PIX       **ppixdb)
1764 {
1765 l_int32    w, h, count;
1766 l_float32  ratio;
1767 PIX       *pix1, *pix2, *pix3, *pix4;
1768 FPIX      *fpix;
1769 
1770     PROCNAME("pixHasHighlightRed");
1771 
1772     if (pratio) *pratio = 0.0;
1773     if (ppixdb) *ppixdb = NULL;
1774     if (phasred) *phasred = 0;
1775     if (!pratio && !ppixdb)
1776         return ERROR_INT("no return val requested", procName, 1);
1777     if (!phasred)
1778         return ERROR_INT("&hasred not defined", procName, 1);
1779     if (!pixs || pixGetDepth(pixs) != 32)
1780         return ERROR_INT("pixs not defined or not 32 bpp", procName, 1);
1781     if (fthresh < 1.5 || fthresh > 3.5)
1782         L_WARNING("fthresh = %f is out of normal bounds\n", procName, fthresh);
1783 
1784     if (factor > 1)
1785         pix1 = pixScaleByIntSampling(pixs, factor);
1786     else
1787         pix1 = pixClone(pixs);
1788 
1789         /* Identify pixels that are either red or dark foreground */
1790     fpix = pixComponentFunction(pix1, 1.0, 0.0, -1.0, 0.0, 0.0, 1.0);
1791     pix2 = fpixThresholdToPix(fpix, fthresh);
1792     pixInvert(pix2, pix2);
1793 
1794         /* Identify pixels that are either red or light background */
1795     pix3 = pixGetRGBComponent(pix1, COLOR_RED);
1796     pix4 = pixThresholdToBinary(pix3, 130);
1797     pixInvert(pix4, pix4);
1798 
1799     pixAnd(pix4, pix4, pix2);
1800     pixCountPixels(pix4, &count, NULL);
1801     pixGetDimensions(pix4, &w, &h, NULL);
1802     L_INFO("count = %d, thresh = %d\n", procName, count,
1803            (l_int32)(fract * w * h));
1804     ratio = (l_float32)count / (fract * w * h);
1805     if (pratio) *pratio = ratio;
1806     if (ratio >= 1.0)
1807         *phasred = 1;
1808     if (ppixdb)
1809         *ppixdb = pix4;
1810     else
1811         pixDestroy(&pix4);
1812     pixDestroy(&pix1);
1813     pixDestroy(&pix2);
1814     pixDestroy(&pix3);
1815     fpixDestroy(&fpix);
1816     return 0;
1817 }
1818