Leptonica 1.85.0
Image processing and image analysis suite
Loading...
Searching...
No Matches
colorcontent.c
Go to the documentation of this file.
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
151#ifdef HAVE_CONFIG_H
152#include <config_auto.h>
153#endif /* HAVE_CONFIG_H */
154
155#include "allheaders.h"
156
157/* ----------------------------------------------------------------------- *
158 * Build an image of the color content, on a per-pixel basis, *
159 * as a measure of the amount of divergence of each color *
160 * component (R,G,B) from gray. *
161 * ----------------------------------------------------------------------- */
201l_ok
203 l_int32 rref,
204 l_int32 gref,
205 l_int32 bref,
206 l_int32 mingray,
207 PIX **ppixr,
208 PIX **ppixg,
209 PIX **ppixb)
210{
211l_int32 w, h, i, j, wpl1, wplr, wplg, wplb;
212l_int32 rval, gval, bval, rgdiff, rbdiff, gbdiff, maxval, colorval;
213l_uint32 pixel;
214l_uint32 *data1, *datar, *datag, *datab, *line1, *liner, *lineg, *lineb;
215PIX *pix1, *pixr, *pixg, *pixb;
216
217 if (!ppixr && !ppixg && !ppixb)
218 return ERROR_INT("no return val requested", __func__, 1);
219 if (ppixr) *ppixr = NULL;
220 if (ppixg) *ppixg = NULL;
221 if (ppixb) *ppixb = NULL;
222 if (!pixs)
223 return ERROR_INT("pixs not defined", __func__, 1);
224 if (mingray < 0) mingray = 0;
225 if (mingray > 255)
226 return ERROR_INT("mingray > 255", __func__, 1);
227
228 /* Do the optional linear color map; this checks the ref vals */
229 if ((pix1 = pixColorShiftWhitePoint(pixs, rref, gref, bref)) == NULL)
230 return ERROR_INT("pix1 not returned", __func__, 1);
231
232 pixr = pixg = pixb = NULL;
233 datar = datag = datab = NULL;
234 liner = lineg = lineb = NULL;
235
236 pixGetDimensions(pix1, &w, &h, NULL);
237 if (ppixr) {
238 pixr = pixCreate(w, h, 8);
239 datar = pixGetData(pixr);
240 wplr = pixGetWpl(pixr);
241 *ppixr = pixr;
242 }
243 if (ppixg) {
244 pixg = pixCreate(w, h, 8);
245 datag = pixGetData(pixg);
246 wplg = pixGetWpl(pixg);
247 *ppixg = pixg;
248 }
249 if (ppixb) {
250 pixb = pixCreate(w, h, 8);
251 datab = pixGetData(pixb);
252 wplb = pixGetWpl(pixb);
253 *ppixb = pixb;
254 }
255
256 data1 = pixGetData(pix1);
257 wpl1 = pixGetWpl(pix1);
258 for (i = 0; i < h; i++) {
259 line1 = data1 + i * wpl1;
260 if (pixr)
261 liner = datar + i * wplr;
262 if (pixg)
263 lineg = datag + i * wplg;
264 if (pixb)
265 lineb = datab + i * wplb;
266 for (j = 0; j < w; j++) {
267 pixel = line1[j];
268 extractRGBValues(pixel, &rval, &gval, &bval);
269 if (mingray > 0) { /* dark pixels have no color value */
270 maxval = L_MAX(rval, gval);
271 maxval = L_MAX(maxval, bval);
272 if (maxval < mingray)
273 continue; /* colorval = 0 for each component */
274 }
275 rgdiff = L_ABS(rval - gval);
276 rbdiff = L_ABS(rval - bval);
277 gbdiff = L_ABS(gval - bval);
278 if (pixr) {
279 colorval = (rgdiff + rbdiff) / 2;
280 SET_DATA_BYTE(liner, j, colorval);
281 }
282 if (pixg) {
283 colorval = (rgdiff + gbdiff) / 2;
284 SET_DATA_BYTE(lineg, j, colorval);
285 }
286 if (pixb) {
287 colorval = (rbdiff + gbdiff) / 2;
288 SET_DATA_BYTE(lineb, j, colorval);
289 }
290 }
291 }
292
293 pixDestroy(&pix1);
294 return 0;
295}
296
297
298/* ----------------------------------------------------------------------- *
299 * Find the 'amount' of color in an image, on a per-pixel basis, *
300 * as a measure of the difference of the pixel color from gray. *
301 * ----------------------------------------------------------------------- */
362PIX *
364 l_int32 rref,
365 l_int32 gref,
366 l_int32 bref,
367 l_int32 type)
368{
369l_int32 w, h, i, j, wpl1, wpld;
370l_int32 rval, gval, bval, rdist, gdist, bdist, colorval;
371l_int32 rgdist, rbdist, gbdist, mindist, maxdist, minval, maxval;
372l_uint32 pixel;
373l_uint32 *data1, *datad, *line1, *lined;
374PIX *pix1, *pixd;
375
376 if (!pixs)
377 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
378 if (type != L_INTERMED_DIFF && type != L_AVE_MAX_DIFF_2 &&
379 type != L_MAX_DIFF)
380 return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
381
382 /* Do the optional linear color map; this checks the ref vals */
383 if ((pix1 = pixColorShiftWhitePoint(pixs, rref, gref, bref)) == NULL)
384 return (PIX *)ERROR_PTR("pix1 not returned", __func__, NULL);
385
386 pixGetDimensions(pix1, &w, &h, NULL);
387 pixd = pixCreate(w, h, 8);
388 datad = pixGetData(pixd);
389 wpld = pixGetWpl(pixd);
390 data1 = pixGetData(pix1);
391 wpl1 = pixGetWpl(pix1);
392 for (i = 0; i < h; i++) {
393 line1 = data1 + i * wpl1;
394 lined = datad + i * wpld;
395 for (j = 0; j < w; j++) {
396 pixel = line1[j];
397 extractRGBValues(pixel, &rval, &gval, &bval);
398 if (type == L_INTERMED_DIFF) {
399 rgdist = L_ABS(rval - gval);
400 rbdist = L_ABS(rval - bval);
401 gbdist = L_ABS(gval - bval);
402 maxdist = L_MAX(rgdist, rbdist);
403 if (gbdist >= maxdist) {
404 colorval = maxdist;
405 } else { /* gbdist is smallest or intermediate */
406 mindist = L_MIN(rgdist, rbdist);
407 colorval = L_MAX(mindist, gbdist);
408 }
409 } else if (type == L_AVE_MAX_DIFF_2) {
410 rdist = ((gval + bval ) / 2 - rval);
411 rdist = L_ABS(rdist);
412 gdist = ((rval + bval ) / 2 - gval);
413 gdist = L_ABS(gdist);
414 bdist = ((rval + gval ) / 2 - bval);
415 bdist = L_ABS(bdist);
416 colorval = L_MAX(rdist, gdist);
417 colorval = L_MAX(colorval, bdist);
418 } else { /* type == L_MAX_DIFF */
419 minval = L_MIN(rval, gval);
420 minval = L_MIN(minval, bval);
421 maxval = L_MAX(rval, gval);
422 maxval = L_MAX(maxval, bval);
423 colorval = maxval - minval;
424 }
425 SET_DATA_BYTE(lined, j, colorval);
426 }
427 }
428
429 pixDestroy(&pix1);
430 return pixd;
431}
432
433
434/* ----------------------------------------------------------------------- *
435 * Find the fraction of pixels with "color" that are not close to black *
436 * ----------------------------------------------------------------------- */
492l_ok
494 l_int32 darkthresh,
495 l_int32 lightthresh,
496 l_int32 diffthresh,
497 l_int32 factor,
498 l_float32 *ppixfract,
499 l_float32 *pcolorfract)
500{
501l_int32 i, j, w, h, wpl, rval, gval, bval, minval, maxval;
502l_int32 total, npix, ncolor;
503l_uint32 pixel;
504l_uint32 *data, *line;
505
506 if (ppixfract) *ppixfract = 0.0;
507 if (pcolorfract) *pcolorfract = 0.0;
508 if (!ppixfract && !pcolorfract)
509 return ERROR_INT("neither &pixfract nor &colorfract are defined",
510 __func__, 1);
511 if (!pixs || pixGetDepth(pixs) != 32)
512 return ERROR_INT("pixs not defined or not 32 bpp", __func__, 1);
513
514 pixGetDimensions(pixs, &w, &h, NULL);
515 data = pixGetData(pixs);
516 wpl = pixGetWpl(pixs);
517 npix = ncolor = total = 0;
518 for (i = 0; i < h; i += factor) {
519 line = data + i * wpl;
520 for (j = 0; j < w; j += factor) {
521 total++;
522 pixel = line[j];
523 extractRGBValues(pixel, &rval, &gval, &bval);
524 minval = L_MIN(rval, gval);
525 minval = L_MIN(minval, bval);
526 if (minval > lightthresh) /* near white */
527 continue;
528 maxval = L_MAX(rval, gval);
529 maxval = L_MAX(maxval, bval);
530 if (maxval < darkthresh) /* near black */
531 continue;
532
533 npix++;
534 if (maxval - minval >= diffthresh)
535 ncolor++;
536 }
537 }
538
539 if (npix == 0) {
540 L_WARNING("No pixels found for consideration\n", __func__);
541 return 0;
542 }
543 if (ppixfract) *ppixfract = (l_float32)npix / (l_float32)total;
544 if (pcolorfract) *pcolorfract = (l_float32)ncolor / (l_float32)npix;
545 return 0;
546}
547
548
549/* ----------------------------------------------------------------------- *
550 * Do a linear TRC to map colors so that the three input reference *
551 * values go to white. These three numbers are typically the median *
552 * or average background values. *
553 * ----------------------------------------------------------------------- */
579PIX *
581 l_int32 rref,
582 l_int32 gref,
583 l_int32 bref)
584{
585l_int32 w, h, i, j, wpl1, wpl2, rval, gval, bval;
586l_int32 *rtab, *gtab, *btab;
587l_uint32 pixel;
588l_uint32 *data1, *data2, *line1, *line2;
589NUMA *nar, *nag, *nab;
590PIX *pix1, *pix2;
591PIXCMAP *cmap;
592
593 if (!pixs)
594 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
595
596 cmap = pixGetColormap(pixs);
597 if (!cmap && pixGetDepth(pixs) != 32)
598 return (PIX *)ERROR_PTR("pixs neither cmapped nor 32 bpp",
599 __func__, NULL);
600 if (cmap)
601 pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR);
602 else
603 pix1 = pixClone(pixs);
604
605 if (!rref && !gref && !bref) /* all 0; no transform requested */
606 return pix1;
607
608 /* Some ref values are < 0, or some (but not all) are 0 */
609 if ((rref < 0 || gref < 0 || bref < 0) || (rref * gref * bref == 0)) {
610 L_WARNING("invalid set of ref values\n", __func__);
611 return pix1;
612 }
613
614 /* All white point ref values > 0; do transformation */
615 pixGetDimensions(pix1, &w, &h, NULL);
616 pix2 = pixCreate(w, h, 32);
617 data1 = pixGetData(pix1);
618 wpl1 = pixGetWpl(pix1);
619 data2 = pixGetData(pix2);
620 wpl2 = pixGetWpl(pix2);
621 nar = numaGammaTRC(1.0, 0, rref);
622 rtab = numaGetIArray(nar);
623 nag = numaGammaTRC(1.0, 0, gref);
624 gtab = numaGetIArray(nag);
625 nab = numaGammaTRC(1.0, 0, bref);
626 btab = numaGetIArray(nab);
627 for (i = 0; i < h; i++) {
628 line1 = data1 + i * wpl1;
629 line2 = data2 + i * wpl2;
630 for (j = 0; j < w; j++) {
631 pixel = line1[j];
632 extractRGBValues(pixel, &rval, &gval, &bval);
633 rval = rtab[rval];
634 gval = gtab[gval];
635 bval = btab[bval];
636 composeRGBPixel(rval, gval, bval, line2 + j);
637 }
638 }
639 numaDestroy(&nar);
640 numaDestroy(&nag);
641 numaDestroy(&nab);
642 LEPT_FREE(rtab);
643 LEPT_FREE(gtab);
644 LEPT_FREE(btab);
645 pixDestroy(&pix1);
646 return pix2;
647}
648
649
650/* ----------------------------------------------------------------------- *
651 * Generate a mask over pixels that have sufficient color and *
652 * are not too close to gray pixels. *
653 * ----------------------------------------------------------------------- */
682PIX *
684 l_int32 threshdiff,
685 l_int32 mindist)
686{
687l_int32 w, h, d, i, j, wpls, wpld, size;
688l_int32 rval, gval, bval, minval, maxval;
689l_uint32 *datas, *datad, *lines, *lined;
690PIX *pixc, *pixd;
691PIXCMAP *cmap;
692
693 if (!pixs)
694 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
695 pixGetDimensions(pixs, &w, &h, &d);
696
697 cmap = pixGetColormap(pixs);
698 if (!cmap && d != 32)
699 return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", __func__, NULL);
700 if (cmap)
701 pixc = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR);
702 else
703 pixc = pixClone(pixs);
704 if (!pixc || pixGetDepth(pixc) != 32) {
705 pixDestroy(&pixc);
706 return (PIX *)ERROR_PTR("rgb pix not made", __func__, NULL);
707 }
708
709 pixd = pixCreate(w, h, 1);
710 datad = pixGetData(pixd);
711 wpld = pixGetWpl(pixd);
712 datas = pixGetData(pixc);
713 wpls = pixGetWpl(pixc);
714 for (i = 0; i < h; i++) {
715 lines = datas + i * wpls;
716 lined = datad + i * wpld;
717 for (j = 0; j < w; j++) {
718 extractRGBValues(lines[j], &rval, &gval, &bval);
719 minval = L_MIN(rval, gval);
720 minval = L_MIN(minval, bval);
721 maxval = L_MAX(rval, gval);
722 maxval = L_MAX(maxval, bval);
723 if (maxval - minval >= threshdiff)
724 SET_DATA_BIT(lined, j);
725 }
726 }
727
728 if (mindist > 1) {
729 size = 2 * (mindist - 1) + 1;
730 pixErodeBrick(pixd, pixd, size, size);
731 }
732
733 pixDestroy(&pixc);
734 return pixd;
735}
736
737
738/* ----------------------------------------------------------------------- *
739 * Generate a mask over dark pixels with little color *
740 * ----------------------------------------------------------------------- */
762PIX *
764 l_int32 maxlimit,
765 l_int32 satlimit)
766{
767l_int32 w, h, i, j, wpls, wpld;
768l_int32 rval, gval, bval, minrg, min, maxrg, max, sat;
769l_uint32 *datas, *datad, *lines, *lined;
770PIX *pixd;
771
772 if (!pixs || pixGetDepth(pixs) != 32)
773 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", __func__, NULL);
774 if (maxlimit < 0 || maxlimit > 255)
775 return (PIX *)ERROR_PTR("invalid maxlimit", __func__, NULL);
776 if (satlimit < 1)
777 return (PIX *)ERROR_PTR("invalid satlimit", __func__, NULL);
778
779 pixGetDimensions(pixs, &w, &h, NULL);
780 datas = pixGetData(pixs);
781 wpls = pixGetWpl(pixs);
782 if ((pixd = pixCreate(w, h, 1)) == NULL)
783 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
784 datad = pixGetData(pixd);
785 wpld = pixGetWpl(pixd);
786
787 for (i = 0; i < h; i++) {
788 lines = datas + i * wpls;
789 lined = datad + i * wpld;
790 for (j = 0; j < w; j++) {
791 extractRGBValues(lines[j], &rval, &gval, &bval);
792 minrg = L_MIN(rval, gval);
793 min = L_MIN(minrg, bval);
794 maxrg = L_MAX(rval, gval);
795 max = L_MAX(maxrg, bval);
796 sat = max - min;
797 if (max <= maxlimit && sat <= satlimit)
798 SET_DATA_BIT(lined, j);
799 }
800 }
801 return pixd;
802}
803
804
805/* ----------------------------------------------------------------------- *
806 * Generate a mask over pixels that have RGB color components *
807 * within the prescribed range (a cube in RGB color space) *
808 * ----------------------------------------------------------------------- */
818PIX *
820 l_int32 rmin,
821 l_int32 rmax,
822 l_int32 gmin,
823 l_int32 gmax,
824 l_int32 bmin,
825 l_int32 bmax)
826{
827l_int32 w, h, d, i, j, wpls, wpld;
828l_int32 rval, gval, bval;
829l_uint32 *datas, *datad, *lines, *lined;
830PIX *pixc, *pixd;
831PIXCMAP *cmap;
832
833 if (!pixs)
834 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
835 pixGetDimensions(pixs, &w, &h, &d);
836
837 cmap = pixGetColormap(pixs);
838 if (!cmap && d != 32)
839 return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", __func__, NULL);
840 if (cmap)
841 pixc = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR);
842 else
843 pixc = pixClone(pixs);
844
845 pixd = pixCreate(w, h, 1);
846 datad = pixGetData(pixd);
847 wpld = pixGetWpl(pixd);
848 datas = pixGetData(pixc);
849 wpls = pixGetWpl(pixc);
850 for (i = 0; i < h; i++) {
851 lines = datas + i * wpls;
852 lined = datad + i * wpld;
853 for (j = 0; j < w; j++) {
854 extractRGBValues(lines[j], &rval, &gval, &bval);
855 if (rval < rmin || rval > rmax) continue;
856 if (gval < gmin || gval > gmax) continue;
857 if (bval < bmin || bval > bmax) continue;
858 SET_DATA_BIT(lined, j);
859 }
860 }
861
862 pixDestroy(&pixc);
863 return pixd;
864}
865
866
867/* ----------------------------------------------------------------------- *
868 * Determine if there are significant color regions in a page image *
869 * ----------------------------------------------------------------------- */
935l_ok
937 PIX *pixm,
938 l_int32 factor,
939 l_int32 lightthresh,
940 l_int32 darkthresh,
941 l_int32 mindiff,
942 l_int32 colordiff,
943 l_float32 edgefract,
944 l_float32 *pcolorfract,
945 PIX **pcolormask1,
946 PIX **pcolormask2,
947 PIXA *pixadb)
948{
949l_int32 w, h, count, rval, gval, bval, aveval, proceed;
950l_float32 ratio;
951l_uint32 *carray;
952BOXA *boxa1, *boxa2;
953PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pixm1, *pixm2, *pixm3;
954
955 if (pcolormask1) *pcolormask1 = NULL;
956 if (pcolormask2) *pcolormask2 = NULL;
957 if (!pcolorfract)
958 return ERROR_INT("&colorfract not defined", __func__, 1);
959 *pcolorfract = 0.0;
960 if (!pixs || pixGetDepth(pixs) != 32)
961 return ERROR_INT("pixs not defined or not 32 bpp", __func__, 1);
962 if (factor < 1) factor = 1;
963 if (lightthresh < 0) lightthresh = 210; /* defaults */
964 if (darkthresh < 0) darkthresh = 70;
965 if (mindiff < 0) mindiff = 10;
966 if (colordiff < 0) colordiff = 90;
967 if (edgefract < 0.0 || edgefract > 1.0) edgefract = 0.05f;
968
969 /* Check if pixm covers most of the image. If so, just return. */
970 if (pixm) {
971 pixForegroundFraction(pixm, &ratio);
972 if (ratio > 0.7) {
973 if (pixadb) L_INFO("pixm has big fg: %f5.2\n", __func__, ratio);
974 return 0;
975 }
976 }
977
978 /* Get the light background color. Use the average component value
979 * and select the lightest of 10 buckets. Require that it is
980 * reddish and, using lightthresh, not too dark. */
981 pixGetRankColorArray(pixs, 10, L_SELECT_AVERAGE, factor, &carray, NULL, 0);
982 if (!carray)
983 return ERROR_INT("rank color array not made", __func__, 1);
984 extractRGBValues(carray[9], &rval, &gval, &bval);
985 if (pixadb) L_INFO("lightest background color: (r,g,b) = (%d,%d,%d)\n",
986 __func__, rval, gval, bval);
987 proceed = TRUE;
988 if ((rval < bval - 2) || (rval < gval - 2)) {
989 if (pixadb) L_INFO("background not reddish\n", __func__);
990 proceed = FALSE;
991 }
992 aveval = (rval + gval + bval) / 3;
993 if (aveval < lightthresh) {
994 if (pixadb) L_INFO("background too dark\n", __func__);
995 proceed = FALSE;
996 }
997 if (pixadb) {
998 pix1 = pixDisplayColorArray(carray, 10, 120, 3, 6);
999 pixaAddPix(pixadb, pix1, L_INSERT);
1000 }
1001 LEPT_FREE(carray);
1002 if (proceed == FALSE) return 0;
1003
1004 /* Make a mask pixm1 over the dark pixels in the image:
1005 * convert to gray using the average of the components;
1006 * threshold using darkthresh; do a small dilation;
1007 * combine with pixm. */
1008 pix1 = pixConvertRGBToGray(pixs, 0.33f, 0.34f, 0.33f);
1009 if (pixadb) pixaAddPix(pixadb, pix1, L_COPY);
1010 pixm1 = pixThresholdToBinary(pix1, darkthresh);
1011 pixDilateBrick(pixm1, pixm1, 7, 7);
1012 if (pixadb) pixaAddPix(pixadb, pixm1, L_COPY);
1013 if (pixm) {
1014 pixOr(pixm1, pixm1, pixm);
1015 if (pixadb) pixaAddPix(pixadb, pixm1, L_COPY);
1016 }
1017 pixDestroy(&pix1);
1018
1019 /* Make masks over pixels that are bluish, or greenish, or
1020 have a very large color saturation (max - min) value. */
1021 pixm2 = pixConvertRGBToBinaryArb(pixs, -1.0, 0.0, 1.0, mindiff,
1022 L_SELECT_IF_GTE); /* b - r */
1023 if (pixadb) pixaAddPix(pixadb, pixm2, L_COPY);
1024 pix1 = pixConvertRGBToBinaryArb(pixs, -1.0, 1.0, 0.0, mindiff,
1025 L_SELECT_IF_GTE); /* g - r */
1026 if (pixadb) pixaAddPix(pixadb, pix1, L_COPY);
1027 pixOr(pixm2, pixm2, pix1);
1028 pixDestroy(&pix1);
1029 pix1 = pixConvertRGBToGrayMinMax(pixs, L_CHOOSE_MAXDIFF);
1030 pix2 = pixThresholdToBinary(pix1, colordiff);
1031 pixInvert(pix2, pix2);
1032 if (pixadb) pixaAddPix(pixadb, pix2, L_COPY);
1033 pixOr(pixm2, pixm2, pix2);
1034 if (pixadb) pixaAddPix(pixadb, pixm2, L_COPY);
1035 pixDestroy(&pix1);
1036 pixDestroy(&pix2);
1037
1038 /* Subtract the dark pixels represented by pixm1.
1039 * pixm2 now holds all the color pixels of interest */
1040 pixSubtract(pixm2, pixm2, pixm1);
1041 pixDestroy(&pixm1);
1042 if (pixadb) pixaAddPix(pixadb, pixm2, L_COPY);
1043
1044 /* But we're not quite finished. Remove pixels from any component
1045 * that is touching the image border. False color pixels can
1046 * sometimes be found there if the image is much darker near
1047 * the border, due to oxidation or reduced illumination. Also
1048 * remove any pixels within the normalized fraction %distfract
1049 * of the image border. */
1050 pixm3 = pixRemoveBorderConnComps(pixm2, 8);
1051 pixGetDimensions(pixm3, &w, &h, NULL);
1052 pixDestroy(&pixm2);
1053 if (edgefract > 0.0) {
1054 pix2 = pixMakeSymmetricMask(w, h, edgefract, edgefract, L_USE_INNER);
1055 pixAnd(pixm3, pixm3, pix2);
1056 pixDestroy(&pix2);
1057 }
1058 if (pixadb) pixaAddPix(pixadb, pixm3, L_COPY);
1059
1060 /* Get the fraction of light color pixels */
1061 pixCountPixels(pixm3, &count, NULL);
1062 *pcolorfract = (l_float32)count / ((l_float32)(w) * h);
1063 if (pixadb) {
1064 if (count == 0)
1065 L_INFO("no light color pixels found\n", __func__);
1066 else
1067 L_INFO("fraction of light color pixels = %5.3f\n", __func__,
1068 *pcolorfract);
1069 }
1070
1071 /* Debug: extract the color pixels from pixs */
1072 if (pixadb && count > 0) {
1073 /* Use pixm3 to extract the color pixels */
1074 pix3 = pixCreateTemplate(pixs);
1075 pixSetAll(pix3);
1076 pixCombineMasked(pix3, pixs, pixm3);
1077 pixaAddPix(pixadb, pix3, L_INSERT);
1078
1079 /* Use additional filtering to extract the color pixels */
1080 pix3 = pixCloseSafeBrick(NULL, pixm3, 15, 15);
1081 pixaAddPix(pixadb, pix3, L_INSERT);
1082 pix5 = pixCreateTemplate(pixs);
1083 pixSetAll(pix5);
1084 pixCombineMasked(pix5, pixs, pix3);
1085 pixaAddPix(pixadb, pix5, L_INSERT);
1086
1087 /* Get the combined bounding boxes of the mask components
1088 * in pix3, and extract those pixels from pixs. */
1089 boxa1 = pixConnCompBB(pix3, 8);
1090 boxa2 = boxaCombineOverlaps(boxa1, NULL);
1091 pix4 = pixCreateTemplate(pix3);
1092 pixMaskBoxa(pix4, pix4, boxa2, L_SET_PIXELS);
1093 pixaAddPix(pixadb, pix4, L_INSERT);
1094 pix5 = pixCreateTemplate(pixs);
1095 pixSetAll(pix5);
1096 pixCombineMasked(pix5, pixs, pix4);
1097 pixaAddPix(pixadb, pix5, L_INSERT);
1098 boxaDestroy(&boxa1);
1099 boxaDestroy(&boxa2);
1100 }
1101 pixaAddPix(pixadb, pixs, L_COPY);
1102
1103 /* Optional colormask returns */
1104 if (pcolormask2 && count > 0)
1105 *pcolormask2 = pixCloseSafeBrick(NULL, pixm3, 15, 15);
1106 if (pcolormask1 && count > 0)
1107 *pcolormask1 = pixm3;
1108 else
1109 pixDestroy(&pixm3);
1110 return 0;
1111}
1112
1113
1114/* ----------------------------------------------------------------------- *
1115 * Find the number of perceptually significant gray intensities *
1116 * in a grayscale image. *
1117 * ----------------------------------------------------------------------- */
1149l_ok
1151 l_int32 darkthresh,
1152 l_int32 lightthresh,
1153 l_float32 minfract,
1154 l_int32 factor,
1155 l_int32 *pncolors)
1156{
1157l_int32 i, w, h, count, mincount, ncolors;
1158NUMA *na;
1159
1160 if (!pncolors)
1161 return ERROR_INT("&ncolors not defined", __func__, 1);
1162 *pncolors = 0;
1163 if (!pixs || pixGetDepth(pixs) != 8)
1164 return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
1165 if (darkthresh < 0) darkthresh = 20; /* defaults */
1166 if (lightthresh < 0) lightthresh = 236;
1167 if (minfract < 0.0)
1168 minfract = 0.0001f;
1169 if (minfract > 1.0)
1170 return ERROR_INT("minfract > 1.0", __func__, 1);
1171 if (minfract >= 0.001)
1172 L_WARNING("minfract too big; likely to underestimate ncolors\n",
1173 __func__);
1174 if (lightthresh > 255 || darkthresh >= lightthresh)
1175 return ERROR_INT("invalid thresholds", __func__, 1);
1176 if (factor < 1) factor = 1;
1177
1178 pixGetDimensions(pixs, &w, &h, NULL);
1179 mincount = (l_int32)(minfract * w * h * factor * factor);
1180 if ((na = pixGetGrayHistogram(pixs, factor)) == NULL)
1181 return ERROR_INT("na not made", __func__, 1);
1182 ncolors = 2; /* add in black and white */
1183 for (i = darkthresh; i <= lightthresh; i++) {
1184 numaGetIValue(na, i, &count);
1185 if (count >= mincount)
1186 ncolors++;
1187 }
1188
1189 *pncolors = ncolors;
1190 numaDestroy(&na);
1191 return 0;
1192}
1193
1194
1195/* ----------------------------------------------------------------------- *
1196 * Identifies images where color quantization will cause posterization *
1197 * due to the existence of many colors in low-gradient regions. *
1198 * ----------------------------------------------------------------------- */
1271l_ok
1273 l_int32 thresh,
1274 l_int32 *pncolors,
1275 l_int32 *piscolor,
1276 l_int32 debug)
1277{
1278l_int32 w, h, d, minside, factor;
1279l_float32 pixfract, colorfract;
1280PIX *pixt, *pixsc, *pixg, *pixe, *pixb, *pixm;
1281PIXCMAP *cmap;
1282
1283 if (piscolor) *piscolor = 0;
1284 if (!pncolors)
1285 return ERROR_INT("&ncolors not defined", __func__, 1);
1286 *pncolors = 0;
1287 if (!pixs)
1288 return ERROR_INT("pixs not defined", __func__, 1);
1289 if ((cmap = pixGetColormap(pixs)) != NULL) {
1290 *pncolors = pixcmapGetCount(cmap);
1291 if (piscolor)
1292 pixcmapHasColor(cmap, piscolor);
1293 return 0;
1294 }
1295
1296 pixGetDimensions(pixs, &w, &h, &d);
1297 if (d != 8 && d != 32)
1298 return ERROR_INT("pixs not 8 or 32 bpp", __func__, 1);
1299 if (thresh <= 0)
1300 thresh = 15;
1301
1302 /* First test if 32 bpp has any significant color; if not,
1303 * convert it to gray. Colors whose average values are within
1304 * 20 of black or 8 of white are ignored because they're not
1305 * very 'colorful'. If less than 2.5/10000 of the pixels have
1306 * significant color, consider the image to be gray. */
1307 minside = L_MIN(w, h);
1308 if (d == 8) {
1309 pixt = pixClone(pixs);
1310 } else { /* d == 32 */
1311 factor = L_MAX(1, minside / 400);
1312 pixColorFraction(pixs, 20, 248, 30, factor, &pixfract, &colorfract);
1313 if (pixfract * colorfract < 0.00025) {
1314 pixt = pixGetRGBComponent(pixs, COLOR_RED);
1315 d = 8;
1316 } else { /* d == 32 */
1317 pixt = pixClone(pixs);
1318 if (piscolor)
1319 *piscolor = 1;
1320 }
1321 }
1322
1323 /* If the smallest side is less than 1000, do not downscale.
1324 * If it is in [1000 ... 2000), downscale by 2x. If it is >= 2000,
1325 * downscale by 4x. Factors of 2 are chosen for speed. The
1326 * actual resolution at which subsequent calculations take place
1327 * is not strongly dependent on downscaling. */
1328 factor = L_MAX(1, minside / 500);
1329 if (factor == 1)
1330 pixsc = pixCopy(NULL, pixt); /* to be sure pixs is unchanged */
1331 else if (factor == 2 || factor == 3)
1332 pixsc = pixScaleAreaMap2(pixt);
1333 else
1334 pixsc = pixScaleAreaMap(pixt, 0.25, 0.25);
1335
1336 /* Basic edge mask generation procedure:
1337 * ~ work on a grayscale image
1338 * ~ get a 1 bpp edge mask by using an edge filter and
1339 * thresholding to get fg pixels at the edges
1340 * ~ for gray, dilate with a 3x3 brick Sel to get mask over
1341 * all pixels within a distance of 1 pixel from the nearest
1342 * edge pixel
1343 * ~ for color, dilate with a 7x7 brick Sel to get mask over
1344 * all pixels within a distance of 3 pixels from the nearest
1345 * edge pixel */
1346 if (d == 8)
1347 pixg = pixClone(pixsc);
1348 else /* d == 32 */
1349 pixg = pixConvertRGBToLuminance(pixsc);
1350 pixe = pixSobelEdgeFilter(pixg, L_ALL_EDGES);
1351 pixb = pixThresholdToBinary(pixe, thresh);
1352 pixInvert(pixb, pixb);
1353 if (d == 8)
1354 pixm = pixMorphSequence(pixb, "d3.3", 0);
1355 else
1356 pixm = pixMorphSequence(pixb, "d7.7", 0);
1357
1358 /* Mask the near-edge pixels to white, and count the colors.
1359 * If grayscale, don't count colors within 20 levels of
1360 * black or white, and only count colors with a fraction
1361 * of at least 1/10000 of the image pixels.
1362 * If color, count the number of level 4 octcubes that
1363 * contain at least 20 pixels. These magic numbers are guesses
1364 * as to what might work, based on a small data set. Results
1365 * should not be overly sensitive to their actual values. */
1366 if (d == 8) {
1367 pixSetMasked(pixg, pixm, 0xff);
1368 if (debug) pixWrite("junkpix8.png", pixg, IFF_PNG);
1369 pixNumSignificantGrayColors(pixg, 20, 236, 0.0001f, 1, pncolors);
1370 } else { /* d == 32 */
1371 pixSetMasked(pixsc, pixm, 0xffffffff);
1372 if (debug) pixWrite("junkpix32.png", pixsc, IFF_PNG);
1373 pixNumberOccupiedOctcubes(pixsc, 4, 20, -1, pncolors);
1374 }
1375
1376 pixDestroy(&pixt);
1377 pixDestroy(&pixsc);
1378 pixDestroy(&pixg);
1379 pixDestroy(&pixe);
1380 pixDestroy(&pixb);
1381 pixDestroy(&pixm);
1382 return 0;
1383}
1384
1385
1386/* ----------------------------------------------------------------------- *
1387 * Find the number of unique colors in an image *
1388 * ----------------------------------------------------------------------- */
1411l_ok
1413 l_int32 factor,
1414 l_int32 *pncolors)
1415{
1416l_int32 w, h, d, i, j, wpl, hashsize, sum, count, manycolors;
1417l_int32 rval, gval, bval, val;
1418l_int32 *inta;
1419l_uint32 pixel;
1420l_uint32 *data, *line;
1421PIXCMAP *cmap;
1422
1423 if (!pncolors)
1424 return ERROR_INT("&ncolors not defined", __func__, 1);
1425 *pncolors = 0;
1426 if (!pixs)
1427 return ERROR_INT("pixs not defined", __func__, 1);
1428 pixGetDimensions(pixs, &w, &h, &d);
1429 if (d != 2 && d != 4 && d != 8 && d != 32)
1430 return ERROR_INT("d not in {2, 4, 8, 32}", __func__, 1);
1431 if (factor < 1) factor = 1;
1432
1433 data = pixGetData(pixs);
1434 wpl = pixGetWpl(pixs);
1435 sum = 0;
1436 if (d != 32) { /* grayscale */
1437 inta = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
1438 for (i = 0; i < h; i += factor) {
1439 line = data + i * wpl;
1440 for (j = 0; j < w; j += factor) {
1441 if (d == 8)
1442 val = GET_DATA_BYTE(line, j);
1443 else if (d == 4)
1444 val = GET_DATA_QBIT(line, j);
1445 else /* d == 2 */
1446 val = GET_DATA_DIBIT(line, j);
1447 inta[val] = 1;
1448 }
1449 }
1450 for (i = 0; i < 256; i++)
1451 if (inta[i]) sum++;
1452 *pncolors = sum;
1453 LEPT_FREE(inta);
1454
1455 cmap = pixGetColormap(pixs);
1456 if (cmap && factor == 1) {
1457 count = pixcmapGetCount(cmap);
1458 if (sum != count)
1459 L_WARNING("colormap size %d differs from actual colors\n",
1460 __func__, count);
1461 }
1462 return 0;
1463 }
1464
1465 /* 32 bpp rgb; quit if we get above 256 colors */
1466 hashsize = 5507; /* big and prime; collisions are not likely */
1467 inta = (l_int32 *)LEPT_CALLOC(hashsize, sizeof(l_int32));
1468 manycolors = 0;
1469 for (i = 0; i < h && manycolors == 0; i += factor) {
1470 line = data + i * wpl;
1471 for (j = 0; j < w; j += factor) {
1472 pixel = line[j];
1473 extractRGBValues(pixel, &rval, &gval, &bval);
1474 val = (137 * rval + 269 * gval + 353 * bval) % hashsize;
1475 if (inta[val] == 0) {
1476 inta[val] = 1;
1477 sum++;
1478 if (sum > 256) {
1479 manycolors = 1;
1480 break;
1481 }
1482 }
1483 }
1484 }
1485 LEPT_FREE(inta);
1486
1487 if (manycolors == 0) {
1488 *pncolors = sum;
1489 return 0;
1490 }
1491
1492 /* More than 256 colors in RGB image; count all the pixels */
1493 return pixCountRGBColorsByHash(pixs, pncolors);
1494}
1495
1496
1497/* ----------------------------------------------------------------------- *
1498 * Lossless conversion of RGB image to colormapped *
1499 * ----------------------------------------------------------------------- */
1512PIX *
1514{
1515l_int32 w, h, d, i, j, wpls, wpld, hashsize, hashval, ncolors, index;
1516l_int32 rval, gval, bval, val;
1517l_int32 *hasha1, *hasha2;
1518l_uint32 pixel;
1519l_uint32 *datas, *lines, *datad, *lined;
1520PIX *pixd;
1521PIXCMAP *cmap;
1522
1523 if (!pixs || pixGetDepth(pixs) != 32)
1524 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", __func__, NULL);
1525
1526 pixNumColors(pixs, 1, &ncolors);
1527 if (ncolors > 256) {
1528 L_ERROR("too many colors found: %d\n", __func__, ncolors);
1529 return NULL;
1530 }
1531
1532 pixGetDimensions(pixs, &w, &h, NULL);
1533 if (ncolors <= 2)
1534 d = 1;
1535 else if (ncolors <= 4)
1536 d = 2;
1537 else if (ncolors <= 16)
1538 d = 4;
1539 else /* ncolors <= 256 */
1540 d = 8;
1541
1542 if ((pixd = pixCreate(w, h, d)) == NULL)
1543 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1544 cmap = pixcmapCreate(d);
1545 datas = pixGetData(pixs);
1546 wpls = pixGetWpl(pixs);
1547 datad = pixGetData(pixd);
1548 wpld = pixGetWpl(pixd);
1549
1550 /* hasha1 is a 1/0 indicator array for colors seen.
1551 hasha2 holds the index into the colormap that will be
1552 generated from the colors in the order seen. This is
1553 the value inserted into pixd. */
1554 hashsize = 5507; /* big and prime; collisions are not likely */
1555 hasha1 = (l_int32 *)LEPT_CALLOC(hashsize, sizeof(l_int32));
1556 hasha2 = (l_int32 *)LEPT_CALLOC(hashsize, sizeof(l_int32));
1557 index = -1;
1558 for (i = 0; i < h; i++) {
1559 lines = datas + i * wpls;
1560 lined = datad + i * wpld;
1561 for (j = 0; j < w; j++) {
1562 pixel = lines[j];
1563 extractRGBValues(pixel, &rval, &gval, &bval);
1564 hashval = (137 * rval + 269 * gval + 353 * bval) % hashsize;
1565 if (hasha1[hashval] == 0) { /* new color */
1566 hasha1[hashval] = 1;
1567 index++;
1568 hasha2[hashval] = index;
1569 pixcmapAddColor(cmap, rval, gval, bval);
1570 }
1571 val = hasha2[hashval];
1572 setLineDataVal(lined, j, d, val);
1573 }
1574 }
1575 pixSetColormap(pixd, cmap);
1576
1577 LEPT_FREE(hasha1);
1578 LEPT_FREE(hasha2);
1579 return pixd;
1580}
1581
1582
1583/* ----------------------------------------------------------------------- *
1584 * Find the most "populated" colors in the image (and quantize) *
1585 * ----------------------------------------------------------------------- */
1607l_ok
1609 l_int32 sigbits,
1610 l_int32 factor,
1611 l_int32 ncolors,
1612 l_uint32 **parray,
1613 PIXCMAP **pcmap)
1614{
1615l_int32 n, i, rgbindex, rval, gval, bval;
1616NUMA *nahisto, *naindex;
1617
1618 if (!parray && !pcmap)
1619 return ERROR_INT("no return val requested", __func__, 1);
1620 if (parray) *parray = NULL;
1621 if (pcmap) *pcmap = NULL;
1622 if (!pixs || pixGetDepth(pixs) != 32)
1623 return ERROR_INT("pixs not defined", __func__, 1);
1624 if (sigbits < 2 || sigbits > 6)
1625 return ERROR_INT("sigbits not in [2 ... 6]", __func__, 1);
1626 if (factor < 1 || ncolors < 1)
1627 return ERROR_INT("factor < 1 or ncolors < 1", __func__, 1);
1628
1629 if ((nahisto = pixGetRGBHistogram(pixs, sigbits, factor)) == NULL)
1630 return ERROR_INT("nahisto not made", __func__, 1);
1631
1632 /* naindex contains the index into nahisto, which is the rgbindex */
1633 naindex = numaSortIndexAutoSelect(nahisto, L_SORT_DECREASING);
1634 numaDestroy(&nahisto);
1635 if (!naindex)
1636 return ERROR_INT("naindex not made", __func__, 1);
1637
1638 n = numaGetCount(naindex);
1639 ncolors = L_MIN(n, ncolors);
1640 if (parray) *parray = (l_uint32 *)LEPT_CALLOC(ncolors, sizeof(l_uint32));
1641 if (pcmap) *pcmap = pixcmapCreate(8);
1642 for (i = 0; i < ncolors; i++) {
1643 numaGetIValue(naindex, i, &rgbindex); /* rgb index */
1644 getRGBFromIndex(rgbindex, sigbits, &rval, &gval, &bval);
1645 if (parray) composeRGBPixel(rval, gval, bval, *parray + i);
1646 if (pcmap) pixcmapAddColor(*pcmap, rval, gval, bval);
1647 }
1648
1649 numaDestroy(&naindex);
1650 return 0;
1651}
1652
1653
1682PIX *
1684 l_int32 sigbits,
1685 l_int32 factor,
1686 l_int32 ncolors)
1687{
1688l_int32 w, h;
1689PIX *pixd;
1690PIXCMAP *cmap;
1691
1692 if (!pixs || pixGetDepth(pixs) != 32)
1693 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1694 if (sigbits < 2 || sigbits > 4)
1695 return (PIX *)ERROR_PTR("sigbits not in {2,3,4}", __func__, NULL);
1696
1697 pixGetMostPopulatedColors(pixs, sigbits, factor, ncolors, NULL, &cmap);
1698 pixGetDimensions(pixs, &w, &h, NULL);
1699 pixd = pixCreate(w, h, 8);
1700 pixSetColormap(pixd, cmap);
1701 pixAssignToNearestColor(pixd, pixs, NULL, 4, NULL);
1702 return pixd;
1703}
1704
1705
1706/* ----------------------------------------------------------------------- *
1707 * Constructs a color histogram based on rgb indices *
1708 * ----------------------------------------------------------------------- */
1726NUMA *
1728 l_int32 sigbits,
1729 l_int32 factor)
1730{
1731l_int32 w, h, i, j, size, wpl, rval, gval, bval, npts;
1732l_uint32 val32, rgbindex;
1733l_float32 *array;
1734l_uint32 *data, *line, *rtab, *gtab, *btab;
1735NUMA *na;
1736
1737 if (!pixs || pixGetDepth(pixs) != 32)
1738 return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
1739 if (sigbits < 2 || sigbits > 6)
1740 return (NUMA *)ERROR_PTR("sigbits not in [2 ... 6]", __func__, NULL);
1741 if (factor < 1)
1742 return (NUMA *)ERROR_PTR("factor < 1", __func__, NULL);
1743
1744 /* Get histogram size: 2^(3 * sigbits) */
1745 size = 1 << (3 * sigbits); /* 64, 512, 4096, 32768, 262144 */
1746 na = numaMakeConstant(0, size); /* init to all 0 */
1747 array = numaGetFArray(na, L_NOCOPY);
1748
1749 makeRGBIndexTables(&rtab, &gtab, &btab, sigbits);
1750
1751 /* Check the number of sampled pixels */
1752 pixGetDimensions(pixs, &w, &h, NULL);
1753 npts = ((w + factor - 1) / factor) * ((h + factor - 1) / factor);
1754 if (npts < 1000)
1755 L_WARNING("only sampling %d pixels\n", __func__, npts);
1756 wpl = pixGetWpl(pixs);
1757 data = pixGetData(pixs);
1758 for (i = 0; i < h; i += factor) {
1759 line = data + i * wpl;
1760 for (j = 0; j < w; j += factor) {
1761 val32 = *(line + j);
1762 extractRGBValues(val32, &rval, &gval, &bval);
1763 rgbindex = rtab[rval] | gtab[gval] | btab[bval];
1764 array[rgbindex]++;
1765 }
1766 }
1767
1768 LEPT_FREE(rtab);
1769 LEPT_FREE(gtab);
1770 LEPT_FREE(btab);
1771 return na;
1772}
1773
1774
1792l_ok
1793makeRGBIndexTables(l_uint32 **prtab,
1794 l_uint32 **pgtab,
1795 l_uint32 **pbtab,
1796 l_int32 sigbits)
1797{
1798l_int32 i;
1799l_uint32 *rtab, *gtab, *btab;
1800
1801 if (prtab) *prtab = NULL;
1802 if (pgtab) *pgtab = NULL;
1803 if (pbtab) *pbtab = NULL;
1804 if (!prtab || !pgtab || !pbtab)
1805 return ERROR_INT("not all table ptrs defined", __func__, 1);
1806 if (sigbits < 2 || sigbits > 6)
1807 return ERROR_INT("sigbits not in [2 ... 6]", __func__, 1);
1808
1809 rtab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32));
1810 gtab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32));
1811 btab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32));
1812 if (!rtab || !gtab || !btab) {
1813 LEPT_FREE(rtab);
1814 LEPT_FREE(gtab);
1815 LEPT_FREE(btab);
1816 return ERROR_INT("calloc fail for tab", __func__, 1);
1817 }
1818 *prtab = rtab;
1819 *pgtab = gtab;
1820 *pbtab = btab;
1821 switch (sigbits) {
1822 case 2:
1823 for (i = 0; i < 256; i++) {
1824 rtab[i] = (i & 0xc0) >> 2;
1825 gtab[i] = (i & 0xc0) >> 4;
1826 btab[i] = (i & 0xc0) >> 6;
1827 }
1828 break;
1829 case 3:
1830 for (i = 0; i < 256; i++) {
1831 rtab[i] = (i & 0xe0) << 1;
1832 gtab[i] = (i & 0xe0) >> 2;
1833 btab[i] = (i & 0xe0) >> 5;
1834 }
1835 break;
1836 case 4:
1837 for (i = 0; i < 256; i++) {
1838 rtab[i] = (i & 0xf0) << 4;
1839 gtab[i] = (i & 0xf0);
1840 btab[i] = (i & 0xf0) >> 4;
1841 }
1842 break;
1843 case 5:
1844 for (i = 0; i < 256; i++) {
1845 rtab[i] = (i & 0xf8) << 7;
1846 gtab[i] = (i & 0xf8) << 2;
1847 btab[i] = (i & 0xf8) >> 3;
1848 }
1849 break;
1850 case 6:
1851 for (i = 0; i < 256; i++) {
1852 rtab[i] = (i & 0xfc) << 10;
1853 gtab[i] = (i & 0xfc) << 4;
1854 btab[i] = (i & 0xfc) >> 2;
1855 }
1856 break;
1857 default:
1858 L_ERROR("Illegal sigbits = %d\n", __func__, sigbits);
1859 return ERROR_INT("sigbits not in [2 ... 6]", __func__, 1);
1860 }
1861
1862 return 0;
1863}
1864
1865
1884l_ok
1885getRGBFromIndex(l_uint32 index,
1886 l_int32 sigbits,
1887 l_int32 *prval,
1888 l_int32 *pgval,
1889 l_int32 *pbval)
1890{
1891 if (prval) *prval = 0;
1892 if (pgval) *pgval = 0;
1893 if (pbval) *pbval = 0;
1894 if (!prval || !pgval || !pbval)
1895 return ERROR_INT("not all component ptrs defined", __func__, 1);
1896 if (sigbits < 2 || sigbits > 6)
1897 return ERROR_INT("sigbits not in [2 ... 6]", __func__, 1);
1898
1899 switch (sigbits) {
1900 case 2:
1901 *prval = ((index << 2) & 0xc0) | 0x20;
1902 *pgval = ((index << 4) & 0xc0) | 0x20;
1903 *pbval = ((index << 6) & 0xc0) | 0x20;
1904 break;
1905 case 3:
1906 *prval = ((index >> 1) & 0xe0) | 0x10;
1907 *pgval = ((index << 2) & 0xe0) | 0x10;
1908 *pbval = ((index << 5) & 0xe0) | 0x10;
1909 break;
1910 case 4:
1911 *prval = ((index >> 4) & 0xf0) | 0x08;
1912 *pgval = (index & 0xf0) | 0x08;
1913 *pbval = ((index << 4) & 0xf0) | 0x08;
1914 break;
1915 case 5:
1916 *prval = ((index >> 7) & 0xf8) | 0x04;
1917 *pgval = ((index >> 2) & 0xf8) | 0x04;
1918 *pbval = ((index << 3) & 0xf8) | 0x04;
1919 break;
1920 case 6:
1921 *prval = ((index >> 10) & 0xfc) | 0x02;
1922 *pgval = ((index >> 4) & 0xfc) | 0x02;
1923 *pbval = ((index << 2) & 0xfc) | 0x02;
1924 break;
1925 default:
1926 L_ERROR("Illegal sigbits = %d\n", __func__, sigbits);
1927 return ERROR_INT("sigbits not in [2 ... 6]", __func__, 1);
1928 }
1929
1930 return 0;
1931}
1932
1933
1934/* ----------------------------------------------------------------------- *
1935 * Identify images that have highlight (red) color *
1936 * ----------------------------------------------------------------------- */
1965l_ok
1967 l_int32 factor,
1968 l_float32 minfract,
1969 l_float32 fthresh,
1970 l_int32 *phasred,
1971 l_float32 *pratio,
1972 PIX **ppixdb)
1973{
1974l_float32 fract, ratio;
1975PIX *pix1, *pix2, *pix3, *pix4;
1976FPIX *fpix;
1977
1978 if (pratio) *pratio = 0.0;
1979 if (ppixdb) *ppixdb = NULL;
1980 if (phasred) *phasred = 0;
1981 if (!pratio && !ppixdb)
1982 return ERROR_INT("no return val requested", __func__, 1);
1983 if (!phasred)
1984 return ERROR_INT("&hasred not defined", __func__, 1);
1985 if (!pixs || pixGetDepth(pixs) != 32)
1986 return ERROR_INT("pixs not defined or not 32 bpp", __func__, 1);
1987 if (minfract <= 0.0)
1988 return ERROR_INT("minfract must be > 0.0", __func__, 1);
1989 if (fthresh < 1.5 || fthresh > 3.5)
1990 L_WARNING("fthresh = %f is out of normal bounds\n", __func__, fthresh);
1991
1992 if (factor > 1)
1993 pix1 = pixScaleByIntSampling(pixs, factor);
1994 else
1995 pix1 = pixClone(pixs);
1996
1997 /* Identify pixels that are either red or dark foreground */
1998 fpix = pixComponentFunction(pix1, 1.0, 0.0, -1.0, 0.0, 0.0, 1.0);
1999 pix2 = fpixThresholdToPix(fpix, fthresh);
2000 pixInvert(pix2, pix2);
2001
2002 /* Identify pixels that are either red or light background */
2003 pix3 = pixGetRGBComponent(pix1, COLOR_RED);
2004 pix4 = pixThresholdToBinary(pix3, 130);
2005 pixInvert(pix4, pix4);
2006
2007 pixAnd(pix4, pix4, pix2);
2008 pixForegroundFraction(pix4, &fract);
2009 ratio = fract / minfract;
2010 L_INFO("fract = %7.5f, ratio = %7.3f\n", __func__, fract, ratio);
2011 if (pratio) *pratio = ratio;
2012 if (ratio >= 1.0)
2013 *phasred = 1;
2014 if (ppixdb)
2015 *ppixdb = pix4;
2016 else
2017 pixDestroy(&pix4);
2018 pixDestroy(&pix1);
2019 pixDestroy(&pix2);
2020 pixDestroy(&pix3);
2021 fpixDestroy(&fpix);
2022 return 0;
2023}
#define GET_DATA_QBIT(pdata, n)
#define SET_DATA_BIT(pdata, n)
#define GET_DATA_BYTE(pdata, n)
#define GET_DATA_DIBIT(pdata, n)
#define SET_DATA_BYTE(pdata, n, val)
l_ok pixNumSignificantGrayColors(PIX *pixs, l_int32 darkthresh, l_int32 lightthresh, l_float32 minfract, l_int32 factor, l_int32 *pncolors)
pixNumSignificantGrayColors()
l_ok pixColorsForQuantization(PIX *pixs, l_int32 thresh, l_int32 *pncolors, l_int32 *piscolor, l_int32 debug)
pixColorsForQuantization()
l_ok pixColorFraction(PIX *pixs, l_int32 darkthresh, l_int32 lightthresh, l_int32 diffthresh, l_int32 factor, l_float32 *ppixfract, l_float32 *pcolorfract)
pixColorFraction()
l_ok pixHasHighlightRed(PIX *pixs, l_int32 factor, l_float32 minfract, l_float32 fthresh, l_int32 *phasred, l_float32 *pratio, PIX **ppixdb)
pixHasHighlightRed()
l_ok pixColorContent(PIX *pixs, l_int32 rref, l_int32 gref, l_int32 bref, l_int32 mingray, PIX **ppixr, PIX **ppixg, PIX **ppixb)
pixColorContent()
l_ok pixGetMostPopulatedColors(PIX *pixs, l_int32 sigbits, l_int32 factor, l_int32 ncolors, l_uint32 **parray, PIXCMAP **pcmap)
pixGetMostPopulatedColors()
PIX * pixSimpleColorQuantize(PIX *pixs, l_int32 sigbits, l_int32 factor, l_int32 ncolors)
pixSimpleColorQuantize()
PIX * pixConvertRGBToCmapLossless(PIX *pixs)
pixConvertRGBToCmapLossless()
PIX * pixMaskOverGrayPixels(PIX *pixs, l_int32 maxlimit, l_int32 satlimit)
pixMaskOverGrayPixels()
PIX * pixMaskOverColorPixels(PIX *pixs, l_int32 threshdiff, l_int32 mindist)
pixMaskOverColorPixels()
PIX * pixColorShiftWhitePoint(PIX *pixs, l_int32 rref, l_int32 gref, l_int32 bref)
pixColorShiftWhitePoint()
l_ok getRGBFromIndex(l_uint32 index, l_int32 sigbits, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
getRGBFromIndex()
NUMA * pixGetRGBHistogram(PIX *pixs, l_int32 sigbits, l_int32 factor)
pixGetRGBHistogram()
l_ok 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)
pixFindColorRegions()
l_ok makeRGBIndexTables(l_uint32 **prtab, l_uint32 **pgtab, l_uint32 **pbtab, l_int32 sigbits)
makeRGBIndexTables()
PIX * pixMaskOverColorRange(PIX *pixs, l_int32 rmin, l_int32 rmax, l_int32 gmin, l_int32 gmax, l_int32 bmin, l_int32 bmax)
pixMaskOverColorRange()
l_ok pixNumColors(PIX *pixs, l_int32 factor, l_int32 *pncolors)
pixNumColors()
PIX * pixColorMagnitude(PIX *pixs, l_int32 rref, l_int32 gref, l_int32 bref, l_int32 type)
pixColorMagnitude()
@ COLOR_RED
Definition pix.h:328
@ L_SELECT_AVERAGE
Definition pix.h:620
@ L_SET_PIXELS
Definition pix.h:565
@ L_MAX_DIFF
Definition pix.h:633
@ L_INTERMED_DIFF
Definition pix.h:631
@ L_AVE_MAX_DIFF_2
Definition pix.h:632
@ L_SELECT_IF_GTE
Definition pix.h:578
@ L_USE_INNER
Definition pix.h:1030
@ REMOVE_CMAP_TO_FULL_COLOR
Definition pix.h:382
@ L_COPY
Definition pix.h:505
@ L_NOCOPY
Definition pix.h:503
@ L_INSERT
Definition pix.h:504
@ L_SORT_DECREASING
Definition pix.h:523
@ L_ALL_EDGES
Definition pix.h:798