Leptonica 1.82.0
Image processing and image analysis suite
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 PROCNAME("pixColorContent");
218
219 if (!ppixr && !ppixg && !ppixb)
220 return ERROR_INT("no return val requested", procName, 1);
221 if (ppixr) *ppixr = NULL;
222 if (ppixg) *ppixg = NULL;
223 if (ppixb) *ppixb = NULL;
224 if (!pixs)
225 return ERROR_INT("pixs not defined", procName, 1);
226 if (mingray < 0) mingray = 0;
227 if (mingray > 255)
228 return ERROR_INT("mingray > 255", procName, 1);
229
230 /* Do the optional linear color map; this checks the ref vals */
231 if ((pix1 = pixColorShiftWhitePoint(pixs, rref, gref, bref)) == NULL)
232 return ERROR_INT("pix1 not returned", procName, 1);
233
234 pixr = pixg = pixb = NULL;
235 pixGetDimensions(pix1, &w, &h, NULL);
236 if (ppixr) {
237 pixr = pixCreate(w, h, 8);
238 datar = pixGetData(pixr);
239 wplr = pixGetWpl(pixr);
240 *ppixr = pixr;
241 }
242 if (ppixg) {
243 pixg = pixCreate(w, h, 8);
244 datag = pixGetData(pixg);
245 wplg = pixGetWpl(pixg);
246 *ppixg = pixg;
247 }
248 if (ppixb) {
249 pixb = pixCreate(w, h, 8);
250 datab = pixGetData(pixb);
251 wplb = pixGetWpl(pixb);
252 *ppixb = pixb;
253 }
254
255 data1 = pixGetData(pix1);
256 wpl1 = pixGetWpl(pix1);
257 for (i = 0; i < h; i++) {
258 line1 = data1 + i * wpl1;
259 if (pixr)
260 liner = datar + i * wplr;
261 if (pixg)
262 lineg = datag + i * wplg;
263 if (pixb)
264 lineb = datab + i * wplb;
265 for (j = 0; j < w; j++) {
266 pixel = line1[j];
267 extractRGBValues(pixel, &rval, &gval, &bval);
268 if (mingray > 0) { /* dark pixels have no color value */
269 maxval = L_MAX(rval, gval);
270 maxval = L_MAX(maxval, bval);
271 if (maxval < mingray)
272 continue; /* colorval = 0 for each component */
273 }
274 rgdiff = L_ABS(rval - gval);
275 rbdiff = L_ABS(rval - bval);
276 gbdiff = L_ABS(gval - bval);
277 if (pixr) {
278 colorval = (rgdiff + rbdiff) / 2;
279 SET_DATA_BYTE(liner, j, colorval);
280 }
281 if (pixg) {
282 colorval = (rgdiff + gbdiff) / 2;
283 SET_DATA_BYTE(lineg, j, colorval);
284 }
285 if (pixb) {
286 colorval = (rbdiff + gbdiff) / 2;
287 SET_DATA_BYTE(lineb, j, colorval);
288 }
289 }
290 }
291
292 pixDestroy(&pix1);
293 return 0;
294}
295
296
297/* ----------------------------------------------------------------------- *
298 * Find the 'amount' of color in an image, on a per-pixel basis, *
299 * as a measure of the difference of the pixel color from gray. *
300 * ----------------------------------------------------------------------- */
361PIX *
363 l_int32 rref,
364 l_int32 gref,
365 l_int32 bref,
366 l_int32 type)
367{
368l_int32 w, h, i, j, wpl1, wpld;
369l_int32 rval, gval, bval, rdist, gdist, bdist, colorval;
370l_int32 rgdist, rbdist, gbdist, mindist, maxdist, minval, maxval;
371l_uint32 pixel;
372l_uint32 *data1, *datad, *line1, *lined;
373PIX *pix1, *pixd;
374
375 PROCNAME("pixColorMagnitude");
376
377 if (!pixs)
378 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
379 if (type != L_INTERMED_DIFF && type != L_AVE_MAX_DIFF_2 &&
380 type != L_MAX_DIFF)
381 return (PIX *)ERROR_PTR("invalid type", procName, NULL);
382
383 /* Do the optional linear color map; this checks the ref vals */
384 if ((pix1 = pixColorShiftWhitePoint(pixs, rref, gref, bref)) == NULL)
385 return (PIX *)ERROR_PTR("pix1 not returned", procName, NULL);
386
387 pixGetDimensions(pix1, &w, &h, NULL);
388 pixd = pixCreate(w, h, 8);
389 datad = pixGetData(pixd);
390 wpld = pixGetWpl(pixd);
391 data1 = pixGetData(pix1);
392 wpl1 = pixGetWpl(pix1);
393 for (i = 0; i < h; i++) {
394 line1 = data1 + i * wpl1;
395 lined = datad + i * wpld;
396 for (j = 0; j < w; j++) {
397 pixel = line1[j];
398 extractRGBValues(pixel, &rval, &gval, &bval);
399 if (type == L_INTERMED_DIFF) {
400 rgdist = L_ABS(rval - gval);
401 rbdist = L_ABS(rval - bval);
402 gbdist = L_ABS(gval - bval);
403 maxdist = L_MAX(rgdist, rbdist);
404 if (gbdist >= maxdist) {
405 colorval = maxdist;
406 } else { /* gbdist is smallest or intermediate */
407 mindist = L_MIN(rgdist, rbdist);
408 colorval = L_MAX(mindist, gbdist);
409 }
410 } else if (type == L_AVE_MAX_DIFF_2) {
411 rdist = ((gval + bval ) / 2 - rval);
412 rdist = L_ABS(rdist);
413 gdist = ((rval + bval ) / 2 - gval);
414 gdist = L_ABS(gdist);
415 bdist = ((rval + gval ) / 2 - bval);
416 bdist = L_ABS(bdist);
417 colorval = L_MAX(rdist, gdist);
418 colorval = L_MAX(colorval, bdist);
419 } else { /* type == L_MAX_DIFF */
420 minval = L_MIN(rval, gval);
421 minval = L_MIN(minval, bval);
422 maxval = L_MAX(rval, gval);
423 maxval = L_MAX(maxval, bval);
424 colorval = maxval - minval;
425 }
426 SET_DATA_BYTE(lined, j, colorval);
427 }
428 }
429
430 pixDestroy(&pix1);
431 return pixd;
432}
433
434
435/* ----------------------------------------------------------------------- *
436 * Find the fraction of pixels with "color" that are not close to black *
437 * ----------------------------------------------------------------------- */
493l_ok
495 l_int32 darkthresh,
496 l_int32 lightthresh,
497 l_int32 diffthresh,
498 l_int32 factor,
499 l_float32 *ppixfract,
500 l_float32 *pcolorfract)
501{
502l_int32 i, j, w, h, wpl, rval, gval, bval, minval, maxval;
503l_int32 total, npix, ncolor;
504l_uint32 pixel;
505l_uint32 *data, *line;
506
507 PROCNAME("pixColorFraction");
508
509 if (ppixfract) *ppixfract = 0.0;
510 if (pcolorfract) *pcolorfract = 0.0;
511 if (!ppixfract || !pcolorfract)
512 return ERROR_INT("&pixfract and &colorfract not defined",
513 procName, 1);
514 if (!pixs || pixGetDepth(pixs) != 32)
515 return ERROR_INT("pixs not defined or not 32 bpp", procName, 1);
516
517 pixGetDimensions(pixs, &w, &h, NULL);
518 data = pixGetData(pixs);
519 wpl = pixGetWpl(pixs);
520 npix = ncolor = total = 0;
521 for (i = 0; i < h; i += factor) {
522 line = data + i * wpl;
523 for (j = 0; j < w; j += factor) {
524 total++;
525 pixel = line[j];
526 extractRGBValues(pixel, &rval, &gval, &bval);
527 minval = L_MIN(rval, gval);
528 minval = L_MIN(minval, bval);
529 if (minval > lightthresh) /* near white */
530 continue;
531 maxval = L_MAX(rval, gval);
532 maxval = L_MAX(maxval, bval);
533 if (maxval < darkthresh) /* near black */
534 continue;
535
536 npix++;
537 if (maxval - minval >= diffthresh)
538 ncolor++;
539 }
540 }
541
542 if (npix == 0) {
543 L_WARNING("No pixels found for consideration\n", procName);
544 return 0;
545 }
546 *ppixfract = (l_float32)npix / (l_float32)total;
547 *pcolorfract = (l_float32)ncolor / (l_float32)npix;
548 return 0;
549}
550
551
552/* ----------------------------------------------------------------------- *
553 * Do a linear TRC to map colors so that the three input reference *
554 * values go to white. These three numbers are typically the median *
555 * or average background values. *
556 * ----------------------------------------------------------------------- */
582PIX *
584 l_int32 rref,
585 l_int32 gref,
586 l_int32 bref)
587{
588l_int32 w, h, i, j, wpl1, wpl2, rval, gval, bval;
589l_int32 *rtab, *gtab, *btab;
590l_uint32 pixel;
591l_uint32 *data1, *data2, *line1, *line2;
592NUMA *nar, *nag, *nab;
593PIX *pix1, *pix2;
594PIXCMAP *cmap;
595
596 PROCNAME("pixColorShiftWhitePoint");
597
598 if (!pixs)
599 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
600
601 cmap = pixGetColormap(pixs);
602 if (!cmap && pixGetDepth(pixs) != 32)
603 return (PIX *)ERROR_PTR("pixs neither cmapped nor 32 bpp",
604 procName, NULL);
605 if (cmap)
607 else
608 pix1 = pixClone(pixs);
609
610 if (!rref && !gref && !bref) /* all 0; no transform requested */
611 return pix1;
612
613 /* Some ref values are < 0, or some (but not all) are 0 */
614 if ((rref < 0 || gref < 0 || bref < 0) || (rref * gref * bref == 0)) {
615 L_WARNING("invalid set of ref values\n", procName);
616 return pix1;
617 }
618
619 /* All white point ref values > 0; do transformation */
620 pixGetDimensions(pix1, &w, &h, NULL);
621 pix2 = pixCreate(w, h, 32);
622 data1 = pixGetData(pix1);
623 wpl1 = pixGetWpl(pix1);
624 data2 = pixGetData(pix2);
625 wpl2 = pixGetWpl(pix2);
626 nar = numaGammaTRC(1.0, 0, rref);
627 rtab = numaGetIArray(nar);
628 nag = numaGammaTRC(1.0, 0, gref);
629 gtab = numaGetIArray(nag);
630 nab = numaGammaTRC(1.0, 0, bref);
631 btab = numaGetIArray(nab);
632 for (i = 0; i < h; i++) {
633 line1 = data1 + i * wpl1;
634 line2 = data2 + i * wpl2;
635 for (j = 0; j < w; j++) {
636 pixel = line1[j];
637 extractRGBValues(pixel, &rval, &gval, &bval);
638 rval = rtab[rval];
639 gval = gtab[gval];
640 bval = btab[bval];
641 composeRGBPixel(rval, gval, bval, line2 + j);
642 }
643 }
644 numaDestroy(&nar);
645 numaDestroy(&nag);
646 numaDestroy(&nab);
647 LEPT_FREE(rtab);
648 LEPT_FREE(gtab);
649 LEPT_FREE(btab);
650 pixDestroy(&pix1);
651 return pix2;
652}
653
654
655/* ----------------------------------------------------------------------- *
656 * Generate a mask over pixels that have sufficient color and *
657 * are not too close to gray pixels. *
658 * ----------------------------------------------------------------------- */
687PIX *
689 l_int32 threshdiff,
690 l_int32 mindist)
691{
692l_int32 w, h, d, i, j, wpls, wpld, size;
693l_int32 rval, gval, bval, minval, maxval;
694l_uint32 *datas, *datad, *lines, *lined;
695PIX *pixc, *pixd;
696PIXCMAP *cmap;
697
698 PROCNAME("pixMaskOverColorPixels");
699
700 if (!pixs)
701 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
702 pixGetDimensions(pixs, &w, &h, &d);
703
704 cmap = pixGetColormap(pixs);
705 if (!cmap && d != 32)
706 return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL);
707 if (cmap)
709 else
710 pixc = pixClone(pixs);
711 if (!pixc || pixGetDepth(pixc) != 32) {
712 pixDestroy(&pixc);
713 return (PIX *)ERROR_PTR("rgb pix not made", procName, NULL);
714 }
715
716 pixd = pixCreate(w, h, 1);
717 datad = pixGetData(pixd);
718 wpld = pixGetWpl(pixd);
719 datas = pixGetData(pixc);
720 wpls = pixGetWpl(pixc);
721 for (i = 0; i < h; i++) {
722 lines = datas + i * wpls;
723 lined = datad + i * wpld;
724 for (j = 0; j < w; j++) {
725 extractRGBValues(lines[j], &rval, &gval, &bval);
726 minval = L_MIN(rval, gval);
727 minval = L_MIN(minval, bval);
728 maxval = L_MAX(rval, gval);
729 maxval = L_MAX(maxval, bval);
730 if (maxval - minval >= threshdiff)
731 SET_DATA_BIT(lined, j);
732 }
733 }
734
735 if (mindist > 1) {
736 size = 2 * (mindist - 1) + 1;
737 pixErodeBrick(pixd, pixd, size, size);
738 }
739
740 pixDestroy(&pixc);
741 return pixd;
742}
743
744
745/* ----------------------------------------------------------------------- *
746 * Generate a mask over dark pixels with little color *
747 * ----------------------------------------------------------------------- */
769PIX *
771 l_int32 maxlimit,
772 l_int32 satlimit)
773{
774l_int32 w, h, i, j, wpls, wpld;
775l_int32 rval, gval, bval, minrg, min, maxrg, max, sat;
776l_uint32 *datas, *datad, *lines, *lined;
777PIX *pixd;
778
779 PROCNAME("pixMaskOverGrayPixels");
780
781 if (!pixs || pixGetDepth(pixs) != 32)
782 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
783 if (maxlimit < 0 || maxlimit > 255)
784 return (PIX *)ERROR_PTR("invalid maxlimit", procName, NULL);
785 if (satlimit < 1)
786 return (PIX *)ERROR_PTR("invalid satlimit", procName, NULL);
787
788 pixGetDimensions(pixs, &w, &h, NULL);
789 datas = pixGetData(pixs);
790 wpls = pixGetWpl(pixs);
791 if ((pixd = pixCreate(w, h, 1)) == NULL)
792 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
793 datad = pixGetData(pixd);
794 wpld = pixGetWpl(pixd);
795
796 for (i = 0; i < h; i++) {
797 lines = datas + i * wpls;
798 lined = datad + i * wpld;
799 for (j = 0; j < w; j++) {
800 extractRGBValues(lines[j], &rval, &gval, &bval);
801 minrg = L_MIN(rval, gval);
802 min = L_MIN(minrg, bval);
803 maxrg = L_MAX(rval, gval);
804 max = L_MAX(maxrg, bval);
805 sat = max - min;
806 if (max <= maxlimit && sat <= satlimit)
807 SET_DATA_BIT(lined, j);
808 }
809 }
810 return pixd;
811}
812
813
814/* ----------------------------------------------------------------------- *
815 * Generate a mask over pixels that have RGB color components *
816 * within the prescribed range (a cube in RGB color space) *
817 * ----------------------------------------------------------------------- */
827PIX *
829 l_int32 rmin,
830 l_int32 rmax,
831 l_int32 gmin,
832 l_int32 gmax,
833 l_int32 bmin,
834 l_int32 bmax)
835{
836l_int32 w, h, d, i, j, wpls, wpld;
837l_int32 rval, gval, bval;
838l_uint32 *datas, *datad, *lines, *lined;
839PIX *pixc, *pixd;
840PIXCMAP *cmap;
841
842 PROCNAME("pixMaskOverColorRange");
843
844 if (!pixs)
845 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
846 pixGetDimensions(pixs, &w, &h, &d);
847
848 cmap = pixGetColormap(pixs);
849 if (!cmap && d != 32)
850 return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL);
851 if (cmap)
853 else
854 pixc = pixClone(pixs);
855
856 pixd = pixCreate(w, h, 1);
857 datad = pixGetData(pixd);
858 wpld = pixGetWpl(pixd);
859 datas = pixGetData(pixc);
860 wpls = pixGetWpl(pixc);
861 for (i = 0; i < h; i++) {
862 lines = datas + i * wpls;
863 lined = datad + i * wpld;
864 for (j = 0; j < w; j++) {
865 extractRGBValues(lines[j], &rval, &gval, &bval);
866 if (rval < rmin || rval > rmax) continue;
867 if (gval < gmin || gval > gmax) continue;
868 if (bval < bmin || bval > bmax) continue;
869 SET_DATA_BIT(lined, j);
870 }
871 }
872
873 pixDestroy(&pixc);
874 return pixd;
875}
876
877
878/* ----------------------------------------------------------------------- *
879 * Determine if there are significant color regions in a page image *
880 * ----------------------------------------------------------------------- */
946l_ok
948 PIX *pixm,
949 l_int32 factor,
950 l_int32 lightthresh,
951 l_int32 darkthresh,
952 l_int32 mindiff,
953 l_int32 colordiff,
954 l_float32 edgefract,
955 l_float32 *pcolorfract,
956 PIX **pcolormask1,
957 PIX **pcolormask2,
958 PIXA *pixadb)
959{
960l_int32 w, h, count, rval, gval, bval, aveval, proceed;
961l_float32 ratio;
962l_uint32 *carray;
963BOXA *boxa1, *boxa2;
964PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pixm1, *pixm2, *pixm3;
965
966 PROCNAME("pixFindColorRegions");
967
968 if (pcolormask1) *pcolormask1 = NULL;
969 if (pcolormask2) *pcolormask2 = NULL;
970 if (!pcolorfract)
971 return ERROR_INT("&colorfract not defined", procName, 1);
972 *pcolorfract = 0.0;
973 if (!pixs || pixGetDepth(pixs) != 32)
974 return ERROR_INT("pixs not defined or not 32 bpp", procName, 1);
975 if (factor < 1) factor = 1;
976 if (lightthresh < 0) lightthresh = 210; /* defaults */
977 if (darkthresh < 0) darkthresh = 70;
978 if (mindiff < 0) mindiff = 10;
979 if (colordiff < 0) colordiff = 90;
980 if (edgefract < 0.0 || edgefract > 1.0) edgefract = 0.05;
981
982 /* Check if pixm covers most of the image. If so, just return. */
983 if (pixm) {
984 pixForegroundFraction(pixm, &ratio);
985 if (ratio > 0.7) {
986 if (pixadb) L_INFO("pixm has big fg: %f5.2\n", procName, ratio);
987 return 0;
988 }
989 }
990
991 /* Get the light background color. Use the average component value
992 * and select the lightest of 10 buckets. Require that it is
993 * reddish and, using lightthresh, not too dark. */
994 pixGetRankColorArray(pixs, 10, L_SELECT_AVERAGE, factor, &carray, NULL, 0);
995 if (!carray)
996 return ERROR_INT("rank color array not made", procName, 1);
997 extractRGBValues(carray[9], &rval, &gval, &bval);
998 if (pixadb) L_INFO("lightest background color: (r,g,b) = (%d,%d,%d)\n",
999 procName, rval, gval, bval);
1000 proceed = TRUE;
1001 if ((rval < bval - 2) || (rval < gval - 2)) {
1002 if (pixadb) L_INFO("background not reddish\n", procName);
1003 proceed = FALSE;
1004 }
1005 aveval = (rval + gval + bval) / 3;
1006 if (aveval < lightthresh) {
1007 if (pixadb) L_INFO("background too dark\n", procName);
1008 proceed = FALSE;
1009 }
1010 if (pixadb) {
1011 pix1 = pixDisplayColorArray(carray, 10, 120, 3, 6);
1012 pixaAddPix(pixadb, pix1, L_INSERT);
1013 }
1014 LEPT_FREE(carray);
1015 if (proceed == FALSE) return 0;
1016
1017 /* Make a mask pixm1 over the dark pixels in the image:
1018 * convert to gray using the average of the components;
1019 * threshold using darkthresh; do a small dilation;
1020 * combine with pixm. */
1021 pix1 = pixConvertRGBToGray(pixs, 0.33, 0.34, 0.33);
1022 if (pixadb) pixaAddPix(pixadb, pix1, L_COPY);
1023 pixm1 = pixThresholdToBinary(pix1, darkthresh);
1024 pixDilateBrick(pixm1, pixm1, 7, 7);
1025 if (pixadb) pixaAddPix(pixadb, pixm1, L_COPY);
1026 if (pixm) {
1027 pixOr(pixm1, pixm1, pixm);
1028 if (pixadb) pixaAddPix(pixadb, pixm1, L_COPY);
1029 }
1030 pixDestroy(&pix1);
1031
1032 /* Make masks over pixels that are bluish, or greenish, or
1033 have a very large color saturation (max - min) value. */
1034 pixm2 = pixConvertRGBToBinaryArb(pixs, -1.0, 0.0, 1.0, mindiff,
1035 L_SELECT_IF_GTE); /* b - r */
1036 if (pixadb) pixaAddPix(pixadb, pixm2, L_COPY);
1037 pix1 = pixConvertRGBToBinaryArb(pixs, -1.0, 1.0, 0.0, mindiff,
1038 L_SELECT_IF_GTE); /* g - r */
1039 if (pixadb) pixaAddPix(pixadb, pix1, L_COPY);
1040 pixOr(pixm2, pixm2, pix1);
1041 pixDestroy(&pix1);
1042 pix1 = pixConvertRGBToGrayMinMax(pixs, L_CHOOSE_MAXDIFF);
1043 pix2 = pixThresholdToBinary(pix1, colordiff);
1044 pixInvert(pix2, pix2);
1045 if (pixadb) pixaAddPix(pixadb, pix2, L_COPY);
1046 pixOr(pixm2, pixm2, pix2);
1047 if (pixadb) pixaAddPix(pixadb, pixm2, L_COPY);
1048 pixDestroy(&pix1);
1049 pixDestroy(&pix2);
1050
1051 /* Subtract the dark pixels represented by pixm1.
1052 * pixm2 now holds all the color pixels of interest */
1053 pixSubtract(pixm2, pixm2, pixm1);
1054 pixDestroy(&pixm1);
1055 if (pixadb) pixaAddPix(pixadb, pixm2, L_COPY);
1056
1057 /* But we're not quite finished. Remove pixels from any component
1058 * that is touching the image border. False color pixels can
1059 * sometimes be found there if the image is much darker near
1060 * the border, due to oxidation or reduced illumination. Also
1061 * remove any pixels within the normalized fraction %distfract
1062 * of the image border. */
1063 pixm3 = pixRemoveBorderConnComps(pixm2, 8);
1064 pixGetDimensions(pixm3, &w, &h, NULL);
1065 pixDestroy(&pixm2);
1066 if (edgefract > 0.0) {
1067 pix2 = pixMakeSymmetricMask(w, h, edgefract, edgefract, L_USE_INNER);
1068 pixAnd(pixm3, pixm3, pix2);
1069 pixDestroy(&pix2);
1070 }
1071 if (pixadb) pixaAddPix(pixadb, pixm3, L_COPY);
1072
1073 /* Get the fraction of light color pixels */
1074 pixCountPixels(pixm3, &count, NULL);
1075 *pcolorfract = (l_float32)count / ((l_float32)(w) * h);
1076 if (pixadb) {
1077 if (count == 0)
1078 L_INFO("no light color pixels found\n", procName);
1079 else
1080 L_INFO("fraction of light color pixels = %5.3f\n", procName,
1081 *pcolorfract);
1082 }
1083
1084 /* Debug: extract the color pixels from pixs */
1085 if (pixadb && count > 0) {
1086 /* Use pixm3 to extract the color pixels */
1087 pix3 = pixCreateTemplate(pixs);
1088 pixSetAll(pix3);
1089 pixCombineMasked(pix3, pixs, pixm3);
1090 pixaAddPix(pixadb, pix3, L_INSERT);
1091
1092 /* Use additional filtering to extract the color pixels */
1093 pix3 = pixCloseSafeBrick(NULL, pixm3, 15, 15);
1094 pixaAddPix(pixadb, pix3, L_INSERT);
1095 pix5 = pixCreateTemplate(pixs);
1096 pixSetAll(pix5);
1097 pixCombineMasked(pix5, pixs, pix3);
1098 pixaAddPix(pixadb, pix5, L_INSERT);
1099
1100 /* Get the combined bounding boxes of the mask components
1101 * in pix3, and extract those pixels from pixs. */
1102 boxa1 = pixConnCompBB(pix3, 8);
1103 boxa2 = boxaCombineOverlaps(boxa1, NULL);
1104 pix4 = pixCreateTemplate(pix3);
1105 pixMaskBoxa(pix4, pix4, boxa2, L_SET_PIXELS);
1106 pixaAddPix(pixadb, pix4, L_INSERT);
1107 pix5 = pixCreateTemplate(pixs);
1108 pixSetAll(pix5);
1109 pixCombineMasked(pix5, pixs, pix4);
1110 pixaAddPix(pixadb, pix5, L_INSERT);
1111 boxaDestroy(&boxa1);
1112 boxaDestroy(&boxa2);
1113 }
1114 pixaAddPix(pixadb, pixs, L_COPY);
1115
1116 /* Optional colormask returns */
1117 if (pcolormask2 && count > 0)
1118 *pcolormask2 = pixCloseSafeBrick(NULL, pixm3, 15, 15);
1119 if (pcolormask1 && count > 0)
1120 *pcolormask1 = pixm3;
1121 else
1122 pixDestroy(&pixm3);
1123 return 0;
1124}
1125
1126
1127/* ----------------------------------------------------------------------- *
1128 * Find the number of perceptually significant gray intensities *
1129 * in a grayscale image. *
1130 * ----------------------------------------------------------------------- */
1162l_ok
1164 l_int32 darkthresh,
1165 l_int32 lightthresh,
1166 l_float32 minfract,
1167 l_int32 factor,
1168 l_int32 *pncolors)
1169{
1170l_int32 i, w, h, count, mincount, ncolors;
1171NUMA *na;
1172
1173 PROCNAME("pixNumSignificantGrayColors");
1174
1175 if (!pncolors)
1176 return ERROR_INT("&ncolors not defined", procName, 1);
1177 *pncolors = 0;
1178 if (!pixs || pixGetDepth(pixs) != 8)
1179 return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
1180 if (darkthresh < 0) darkthresh = 20; /* defaults */
1181 if (lightthresh < 0) lightthresh = 236;
1182 if (minfract < 0.0) minfract = 0.0001;
1183 if (minfract > 1.0)
1184 return ERROR_INT("minfract > 1.0", procName, 1);
1185 if (minfract >= 0.001)
1186 L_WARNING("minfract too big; likely to underestimate ncolors\n",
1187 procName);
1188 if (lightthresh > 255 || darkthresh >= lightthresh)
1189 return ERROR_INT("invalid thresholds", procName, 1);
1190 if (factor < 1) factor = 1;
1191
1192 pixGetDimensions(pixs, &w, &h, NULL);
1193 mincount = (l_int32)(minfract * w * h * factor * factor);
1194 if ((na = pixGetGrayHistogram(pixs, factor)) == NULL)
1195 return ERROR_INT("na not made", procName, 1);
1196 ncolors = 2; /* add in black and white */
1197 for (i = darkthresh; i <= lightthresh; i++) {
1198 numaGetIValue(na, i, &count);
1199 if (count >= mincount)
1200 ncolors++;
1201 }
1202
1203 *pncolors = ncolors;
1204 numaDestroy(&na);
1205 return 0;
1206}
1207
1208
1209/* ----------------------------------------------------------------------- *
1210 * Identifies images where color quantization will cause posterization *
1211 * due to the existence of many colors in low-gradient regions. *
1212 * ----------------------------------------------------------------------- */
1285l_ok
1287 l_int32 thresh,
1288 l_int32 *pncolors,
1289 l_int32 *piscolor,
1290 l_int32 debug)
1291{
1292l_int32 w, h, d, minside, factor;
1293l_float32 pixfract, colorfract;
1294PIX *pixt, *pixsc, *pixg, *pixe, *pixb, *pixm;
1295PIXCMAP *cmap;
1296
1297 PROCNAME("pixColorsForQuantization");
1298
1299 if (piscolor) *piscolor = 0;
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 if ((cmap = pixGetColormap(pixs)) != NULL) {
1306 *pncolors = pixcmapGetCount(cmap);
1307 if (piscolor)
1308 pixcmapHasColor(cmap, piscolor);
1309 return 0;
1310 }
1311
1312 pixGetDimensions(pixs, &w, &h, &d);
1313 if (d != 8 && d != 32)
1314 return ERROR_INT("pixs not 8 or 32 bpp", procName, 1);
1315 if (thresh <= 0)
1316 thresh = 15;
1317
1318 /* First test if 32 bpp has any significant color; if not,
1319 * convert it to gray. Colors whose average values are within
1320 * 20 of black or 8 of white are ignored because they're not
1321 * very 'colorful'. If less than 2.5/10000 of the pixels have
1322 * significant color, consider the image to be gray. */
1323 minside = L_MIN(w, h);
1324 if (d == 8) {
1325 pixt = pixClone(pixs);
1326 } else { /* d == 32 */
1327 factor = L_MAX(1, minside / 400);
1328 pixColorFraction(pixs, 20, 248, 30, factor, &pixfract, &colorfract);
1329 if (pixfract * colorfract < 0.00025) {
1330 pixt = pixGetRGBComponent(pixs, COLOR_RED);
1331 d = 8;
1332 } else { /* d == 32 */
1333 pixt = pixClone(pixs);
1334 if (piscolor)
1335 *piscolor = 1;
1336 }
1337 }
1338
1339 /* If the smallest side is less than 1000, do not downscale.
1340 * If it is in [1000 ... 2000), downscale by 2x. If it is >= 2000,
1341 * downscale by 4x. Factors of 2 are chosen for speed. The
1342 * actual resolution at which subsequent calculations take place
1343 * is not strongly dependent on downscaling. */
1344 factor = L_MAX(1, minside / 500);
1345 if (factor == 1)
1346 pixsc = pixCopy(NULL, pixt); /* to be sure pixs is unchanged */
1347 else if (factor == 2 || factor == 3)
1348 pixsc = pixScaleAreaMap2(pixt);
1349 else
1350 pixsc = pixScaleAreaMap(pixt, 0.25, 0.25);
1351
1352 /* Basic edge mask generation procedure:
1353 * ~ work on a grayscale image
1354 * ~ get a 1 bpp edge mask by using an edge filter and
1355 * thresholding to get fg pixels at the edges
1356 * ~ for gray, dilate with a 3x3 brick Sel to get mask over
1357 * all pixels within a distance of 1 pixel from the nearest
1358 * edge pixel
1359 * ~ for color, dilate with a 7x7 brick Sel to get mask over
1360 * all pixels within a distance of 3 pixels from the nearest
1361 * edge pixel */
1362 if (d == 8)
1363 pixg = pixClone(pixsc);
1364 else /* d == 32 */
1365 pixg = pixConvertRGBToLuminance(pixsc);
1366 pixe = pixSobelEdgeFilter(pixg, L_ALL_EDGES);
1367 pixb = pixThresholdToBinary(pixe, thresh);
1368 pixInvert(pixb, pixb);
1369 if (d == 8)
1370 pixm = pixMorphSequence(pixb, "d3.3", 0);
1371 else
1372 pixm = pixMorphSequence(pixb, "d7.7", 0);
1373
1374 /* Mask the near-edge pixels to white, and count the colors.
1375 * If grayscale, don't count colors within 20 levels of
1376 * black or white, and only count colors with a fraction
1377 * of at least 1/10000 of the image pixels.
1378 * If color, count the number of level 4 octcubes that
1379 * contain at least 20 pixels. These magic numbers are guesses
1380 * as to what might work, based on a small data set. Results
1381 * should not be overly sensitive to their actual values. */
1382 if (d == 8) {
1383 pixSetMasked(pixg, pixm, 0xff);
1384 if (debug) pixWrite("junkpix8.png", pixg, IFF_PNG);
1385 pixNumSignificantGrayColors(pixg, 20, 236, 0.0001, 1, pncolors);
1386 } else { /* d == 32 */
1387 pixSetMasked(pixsc, pixm, 0xffffffff);
1388 if (debug) pixWrite("junkpix32.png", pixsc, IFF_PNG);
1389 pixNumberOccupiedOctcubes(pixsc, 4, 20, -1, pncolors);
1390 }
1391
1392 pixDestroy(&pixt);
1393 pixDestroy(&pixsc);
1394 pixDestroy(&pixg);
1395 pixDestroy(&pixe);
1396 pixDestroy(&pixb);
1397 pixDestroy(&pixm);
1398 return 0;
1399}
1400
1401
1402/* ----------------------------------------------------------------------- *
1403 * Find the number of unique colors in an image *
1404 * ----------------------------------------------------------------------- */
1427l_ok
1429 l_int32 factor,
1430 l_int32 *pncolors)
1431{
1432l_int32 w, h, d, i, j, wpl, hashsize, sum, count, manycolors;
1433l_int32 rval, gval, bval, val;
1434l_int32 *inta;
1435l_uint32 pixel;
1436l_uint32 *data, *line;
1437PIXCMAP *cmap;
1438
1439 PROCNAME("pixNumColors");
1440
1441 if (!pncolors)
1442 return ERROR_INT("&ncolors not defined", procName, 1);
1443 *pncolors = 0;
1444 if (!pixs)
1445 return ERROR_INT("pixs not defined", procName, 1);
1446 pixGetDimensions(pixs, &w, &h, &d);
1447 if (d != 2 && d != 4 && d != 8 && d != 32)
1448 return ERROR_INT("d not in {2, 4, 8, 32}", procName, 1);
1449 if (factor < 1) factor = 1;
1450
1451 data = pixGetData(pixs);
1452 wpl = pixGetWpl(pixs);
1453 sum = 0;
1454 if (d != 32) { /* grayscale */
1455 inta = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
1456 for (i = 0; i < h; i += factor) {
1457 line = data + i * wpl;
1458 for (j = 0; j < w; j += factor) {
1459 if (d == 8)
1460 val = GET_DATA_BYTE(line, j);
1461 else if (d == 4)
1462 val = GET_DATA_QBIT(line, j);
1463 else /* d == 2 */
1464 val = GET_DATA_DIBIT(line, j);
1465 inta[val] = 1;
1466 }
1467 }
1468 for (i = 0; i < 256; i++)
1469 if (inta[i]) sum++;
1470 *pncolors = sum;
1471 LEPT_FREE(inta);
1472
1473 cmap = pixGetColormap(pixs);
1474 if (cmap && factor == 1) {
1475 count = pixcmapGetCount(cmap);
1476 if (sum != count)
1477 L_WARNING("colormap size %d differs from actual colors\n",
1478 procName, count);
1479 }
1480 return 0;
1481 }
1482
1483 /* 32 bpp rgb; quit if we get above 256 colors */
1484 hashsize = 5507; /* big and prime; collisions are not likely */
1485 inta = (l_int32 *)LEPT_CALLOC(hashsize, sizeof(l_int32));
1486 manycolors = 0;
1487 for (i = 0; i < h && manycolors == 0; i += factor) {
1488 line = data + i * wpl;
1489 for (j = 0; j < w; j += factor) {
1490 pixel = line[j];
1491 extractRGBValues(pixel, &rval, &gval, &bval);
1492 val = (137 * rval + 269 * gval + 353 * bval) % hashsize;
1493 if (inta[val] == 0) {
1494 inta[val] = 1;
1495 sum++;
1496 if (sum > 256) {
1497 manycolors = 1;
1498 break;
1499 }
1500 }
1501 }
1502 }
1503 LEPT_FREE(inta);
1504
1505 if (manycolors == 0) {
1506 *pncolors = sum;
1507 return 0;
1508 }
1509
1510 /* More than 256 colors in RGB image; count all the pixels */
1511 return pixCountRGBColorsByHash(pixs, pncolors);
1512}
1513
1514
1515/* ----------------------------------------------------------------------- *
1516 * Lossless conversion of RGB image to colormapped *
1517 * ----------------------------------------------------------------------- */
1530PIX *
1532{
1533l_int32 w, h, d, i, j, wpls, wpld, hashsize, hashval, ncolors, index;
1534l_int32 rval, gval, bval, val;
1535l_int32 *hasha1, *hasha2;
1536l_uint32 pixel;
1537l_uint32 *datas, *lines, *datad, *lined;
1538PIX *pixd;
1539PIXCMAP *cmap;
1540
1541 PROCNAME("pixConvertRGBToCmapLossless");
1542
1543 if (!pixs || pixGetDepth(pixs) != 32)
1544 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
1545
1546 pixNumColors(pixs, 1, &ncolors);
1547 if (ncolors > 256) {
1548 L_ERROR("too many colors found: %d\n", procName, ncolors);
1549 return NULL;
1550 }
1551
1552 pixGetDimensions(pixs, &w, &h, NULL);
1553 if (ncolors <= 2)
1554 d = 1;
1555 else if (ncolors <= 4)
1556 d = 2;
1557 else if (ncolors <= 16)
1558 d = 4;
1559 else /* ncolors <= 256 */
1560 d = 8;
1561
1562 if ((pixd = pixCreate(w, h, d)) == NULL)
1563 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1564 cmap = pixcmapCreate(d);
1565 datas = pixGetData(pixs);
1566 wpls = pixGetWpl(pixs);
1567 datad = pixGetData(pixd);
1568 wpld = pixGetWpl(pixd);
1569
1570 /* hasha1 is a 1/0 indicator array for colors seen.
1571 hasha2 holds the index into the colormap that will be
1572 generated from the colors in the order seen. This is
1573 the value inserted into pixd. */
1574 hashsize = 5507; /* big and prime; collisions are not likely */
1575 hasha1 = (l_int32 *)LEPT_CALLOC(hashsize, sizeof(l_int32));
1576 hasha2 = (l_int32 *)LEPT_CALLOC(hashsize, sizeof(l_int32));
1577 index = -1;
1578 for (i = 0; i < h; i++) {
1579 lines = datas + i * wpls;
1580 lined = datad + i * wpld;
1581 for (j = 0; j < w; j++) {
1582 pixel = lines[j];
1583 extractRGBValues(pixel, &rval, &gval, &bval);
1584 hashval = (137 * rval + 269 * gval + 353 * bval) % hashsize;
1585 if (hasha1[hashval] == 0) { /* new color */
1586 hasha1[hashval] = 1;
1587 index++;
1588 hasha2[hashval] = index;
1589 pixcmapAddColor(cmap, rval, gval, bval);
1590 }
1591 val = hasha2[hashval];
1592 setLineDataVal(lined, j, d, val);
1593 }
1594 }
1595 pixSetColormap(pixd, cmap);
1596
1597 LEPT_FREE(hasha1);
1598 LEPT_FREE(hasha2);
1599 return pixd;
1600}
1601
1602
1603/* ----------------------------------------------------------------------- *
1604 * Find the most "populated" colors in the image (and quantize) *
1605 * ----------------------------------------------------------------------- */
1627l_ok
1629 l_int32 sigbits,
1630 l_int32 factor,
1631 l_int32 ncolors,
1632 l_uint32 **parray,
1633 PIXCMAP **pcmap)
1634{
1635l_int32 n, i, rgbindex, rval, gval, bval;
1636NUMA *nahisto, *naindex;
1637
1638 PROCNAME("pixGetMostPopulatedColors");
1639
1640 if (!parray && !pcmap)
1641 return ERROR_INT("no return val requested", procName, 1);
1642 if (parray) *parray = NULL;
1643 if (pcmap) *pcmap = NULL;
1644 if (!pixs || pixGetDepth(pixs) != 32)
1645 return ERROR_INT("pixs not defined", procName, 1);
1646 if (sigbits < 2 || sigbits > 6)
1647 return ERROR_INT("sigbits not in [2 ... 6]", procName, 1);
1648 if (factor < 1 || ncolors < 1)
1649 return ERROR_INT("factor < 1 or ncolors < 1", procName, 1);
1650
1651 if ((nahisto = pixGetRGBHistogram(pixs, sigbits, factor)) == NULL)
1652 return ERROR_INT("nahisto not made", procName, 1);
1653
1654 /* naindex contains the index into nahisto, which is the rgbindex */
1655 naindex = numaSortIndexAutoSelect(nahisto, L_SORT_DECREASING);
1656 numaDestroy(&nahisto);
1657 if (!naindex)
1658 return ERROR_INT("naindex not made", procName, 1);
1659
1660 n = numaGetCount(naindex);
1661 ncolors = L_MIN(n, ncolors);
1662 if (parray) *parray = (l_uint32 *)LEPT_CALLOC(ncolors, sizeof(l_uint32));
1663 if (pcmap) *pcmap = pixcmapCreate(8);
1664 for (i = 0; i < ncolors; i++) {
1665 numaGetIValue(naindex, i, &rgbindex); /* rgb index */
1666 getRGBFromIndex(rgbindex, sigbits, &rval, &gval, &bval);
1667 if (parray) composeRGBPixel(rval, gval, bval, *parray + i);
1668 if (pcmap) pixcmapAddColor(*pcmap, rval, gval, bval);
1669 }
1670
1671 numaDestroy(&naindex);
1672 return 0;
1673}
1674
1675
1704PIX *
1706 l_int32 sigbits,
1707 l_int32 factor,
1708 l_int32 ncolors)
1709{
1710l_int32 w, h;
1711PIX *pixd;
1712PIXCMAP *cmap;
1713
1714 PROCNAME("pixSimpleColorQuantize");
1715
1716 if (!pixs || pixGetDepth(pixs) != 32)
1717 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1718 if (sigbits < 2 || sigbits > 4)
1719 return (PIX *)ERROR_PTR("sigbits not in {2,3,4}", procName, NULL);
1720
1721 pixGetMostPopulatedColors(pixs, sigbits, factor, ncolors, NULL, &cmap);
1722 pixGetDimensions(pixs, &w, &h, NULL);
1723 pixd = pixCreate(w, h, 8);
1724 pixSetColormap(pixd, cmap);
1725 pixAssignToNearestColor(pixd, pixs, NULL, 4, NULL);
1726 return pixd;
1727}
1728
1729
1730/* ----------------------------------------------------------------------- *
1731 * Constructs a color histogram based on rgb indices *
1732 * ----------------------------------------------------------------------- */
1750NUMA *
1752 l_int32 sigbits,
1753 l_int32 factor)
1754{
1755l_int32 w, h, i, j, size, wpl, rval, gval, bval, npts;
1756l_uint32 val32, rgbindex;
1757l_float32 *array;
1758l_uint32 *data, *line, *rtab, *gtab, *btab;
1759NUMA *na;
1760
1761 PROCNAME("pixGetRGBHistogram");
1762
1763 if (!pixs || pixGetDepth(pixs) != 32)
1764 return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL);
1765 if (sigbits < 2 || sigbits > 6)
1766 return (NUMA *)ERROR_PTR("sigbits not in [2 ... 6]", procName, NULL);
1767 if (factor < 1)
1768 return (NUMA *)ERROR_PTR("factor < 1", procName, NULL);
1769
1770 /* Get histogram size: 2^(3 * sigbits) */
1771 size = 1 << (3 * sigbits); /* 64, 512, 4096, 32768, 262144 */
1772 na = numaMakeConstant(0, size); /* init to all 0 */
1773 array = numaGetFArray(na, L_NOCOPY);
1774
1775 makeRGBIndexTables(&rtab, &gtab, &btab, sigbits);
1776
1777 /* Check the number of sampled pixels */
1778 pixGetDimensions(pixs, &w, &h, NULL);
1779 npts = ((w + factor - 1) / factor) * ((h + factor - 1) / factor);
1780 if (npts < 1000)
1781 L_WARNING("only sampling %d pixels\n", procName, npts);
1782 wpl = pixGetWpl(pixs);
1783 data = pixGetData(pixs);
1784 for (i = 0; i < h; i += factor) {
1785 line = data + i * wpl;
1786 for (j = 0; j < w; j += factor) {
1787 val32 = *(line + j);
1788 extractRGBValues(val32, &rval, &gval, &bval);
1789 rgbindex = rtab[rval] | gtab[gval] | btab[bval];
1790 array[rgbindex]++;
1791 }
1792 }
1793
1794 LEPT_FREE(rtab);
1795 LEPT_FREE(gtab);
1796 LEPT_FREE(btab);
1797 return na;
1798}
1799
1800
1818l_ok
1819makeRGBIndexTables(l_uint32 **prtab,
1820 l_uint32 **pgtab,
1821 l_uint32 **pbtab,
1822 l_int32 sigbits)
1823{
1824l_int32 i;
1825l_uint32 *rtab, *gtab, *btab;
1826
1827 PROCNAME("makeRGBIndexTables");
1828
1829 if (prtab) *prtab = NULL;
1830 if (pgtab) *pgtab = NULL;
1831 if (pbtab) *pbtab = NULL;
1832 if (!prtab || !pgtab || !pbtab)
1833 return ERROR_INT("not all table ptrs defined", procName, 1);
1834 if (sigbits < 2 || sigbits > 6)
1835 return ERROR_INT("sigbits not in [2 ... 6]", procName, 1);
1836
1837 rtab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32));
1838 gtab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32));
1839 btab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32));
1840 if (!rtab || !gtab || !btab)
1841 return ERROR_INT("calloc fail for tab", procName, 1);
1842 *prtab = rtab;
1843 *pgtab = gtab;
1844 *pbtab = btab;
1845 switch (sigbits) {
1846 case 2:
1847 for (i = 0; i < 256; i++) {
1848 rtab[i] = (i & 0xc0) >> 2;
1849 gtab[i] = (i & 0xc0) >> 4;
1850 btab[i] = (i & 0xc0) >> 6;
1851 }
1852 break;
1853 case 3:
1854 for (i = 0; i < 256; i++) {
1855 rtab[i] = (i & 0xe0) << 1;
1856 gtab[i] = (i & 0xe0) >> 2;
1857 btab[i] = (i & 0xe0) >> 5;
1858 }
1859 break;
1860 case 4:
1861 for (i = 0; i < 256; i++) {
1862 rtab[i] = (i & 0xf0) << 4;
1863 gtab[i] = (i & 0xf0);
1864 btab[i] = (i & 0xf0) >> 4;
1865 }
1866 break;
1867 case 5:
1868 for (i = 0; i < 256; i++) {
1869 rtab[i] = (i & 0xf8) << 7;
1870 gtab[i] = (i & 0xf8) << 2;
1871 btab[i] = (i & 0xf8) >> 3;
1872 }
1873 break;
1874 case 6:
1875 for (i = 0; i < 256; i++) {
1876 rtab[i] = (i & 0xfc) << 10;
1877 gtab[i] = (i & 0xfc) << 4;
1878 btab[i] = (i & 0xfc) >> 2;
1879 }
1880 break;
1881 default:
1882 L_ERROR("Illegal sigbits = %d\n", procName, sigbits);
1883 return ERROR_INT("sigbits not in [2 ... 6]", procName, 1);
1884 }
1885
1886 return 0;
1887}
1888
1889
1908l_ok
1909getRGBFromIndex(l_uint32 index,
1910 l_int32 sigbits,
1911 l_int32 *prval,
1912 l_int32 *pgval,
1913 l_int32 *pbval)
1914{
1915 PROCNAME("getRGBFromIndex");
1916
1917 if (prval) *prval = 0;
1918 if (pgval) *pgval = 0;
1919 if (pbval) *pbval = 0;
1920 if (!prval || !pgval || !pbval)
1921 return ERROR_INT("not all component ptrs defined", procName, 1);
1922 if (sigbits < 2 || sigbits > 6)
1923 return ERROR_INT("sigbits not in [2 ... 6]", procName, 1);
1924
1925 switch (sigbits) {
1926 case 2:
1927 *prval = ((index << 2) & 0xc0) | 0x20;
1928 *pgval = ((index << 4) & 0xc0) | 0x20;
1929 *pbval = ((index << 6) & 0xc0) | 0x20;
1930 break;
1931 case 3:
1932 *prval = ((index >> 1) & 0xe0) | 0x10;
1933 *pgval = ((index << 2) & 0xe0) | 0x10;
1934 *pbval = ((index << 5) & 0xe0) | 0x10;
1935 break;
1936 case 4:
1937 *prval = ((index >> 4) & 0xf0) | 0x08;
1938 *pgval = (index & 0xf0) | 0x08;
1939 *pbval = ((index << 4) & 0xf0) | 0x08;
1940 break;
1941 case 5:
1942 *prval = ((index >> 7) & 0xf8) | 0x04;
1943 *pgval = ((index >> 2) & 0xf8) | 0x04;
1944 *pbval = ((index << 3) & 0xf8) | 0x04;
1945 break;
1946 case 6:
1947 *prval = ((index >> 10) & 0xfc) | 0x02;
1948 *pgval = ((index >> 4) & 0xfc) | 0x02;
1949 *pbval = ((index << 2) & 0xfc) | 0x02;
1950 break;
1951 default:
1952 L_ERROR("Illegal sigbits = %d\n", procName, sigbits);
1953 return ERROR_INT("sigbits not in [2 ... 6]", procName, 1);
1954 }
1955
1956 return 0;
1957}
1958
1959
1960/* ----------------------------------------------------------------------- *
1961 * Identify images that have highlight (red) color *
1962 * ----------------------------------------------------------------------- */
1991l_ok
1993 l_int32 factor,
1994 l_float32 minfract,
1995 l_float32 fthresh,
1996 l_int32 *phasred,
1997 l_float32 *pratio,
1998 PIX **ppixdb)
1999{
2000l_float32 fract, ratio;
2001PIX *pix1, *pix2, *pix3, *pix4;
2002FPIX *fpix;
2003
2004 PROCNAME("pixHasHighlightRed");
2005
2006 if (pratio) *pratio = 0.0;
2007 if (ppixdb) *ppixdb = NULL;
2008 if (phasred) *phasred = 0;
2009 if (!pratio && !ppixdb)
2010 return ERROR_INT("no return val requested", procName, 1);
2011 if (!phasred)
2012 return ERROR_INT("&hasred not defined", procName, 1);
2013 if (!pixs || pixGetDepth(pixs) != 32)
2014 return ERROR_INT("pixs not defined or not 32 bpp", procName, 1);
2015 if (minfract <= 0.0)
2016 return ERROR_INT("minfract must be > 0.0", procName, 1);
2017 if (fthresh < 1.5 || fthresh > 3.5)
2018 L_WARNING("fthresh = %f is out of normal bounds\n", procName, fthresh);
2019
2020 if (factor > 1)
2021 pix1 = pixScaleByIntSampling(pixs, factor);
2022 else
2023 pix1 = pixClone(pixs);
2024
2025 /* Identify pixels that are either red or dark foreground */
2026 fpix = pixComponentFunction(pix1, 1.0, 0.0, -1.0, 0.0, 0.0, 1.0);
2027 pix2 = fpixThresholdToPix(fpix, fthresh);
2028 pixInvert(pix2, pix2);
2029
2030 /* Identify pixels that are either red or light background */
2031 pix3 = pixGetRGBComponent(pix1, COLOR_RED);
2032 pix4 = pixThresholdToBinary(pix3, 130);
2033 pixInvert(pix4, pix4);
2034
2035 pixAnd(pix4, pix4, pix2);
2036 pixForegroundFraction(pix4, &fract);
2037 ratio = fract / minfract;
2038 L_INFO("fract = %7.5f, ratio = %7.3f\n", procName, fract, ratio);
2039 if (pratio) *pratio = ratio;
2040 if (ratio >= 1.0)
2041 *phasred = 1;
2042 if (ppixdb)
2043 *ppixdb = pix4;
2044 else
2045 pixDestroy(&pix4);
2046 pixDestroy(&pix1);
2047 pixDestroy(&pix2);
2048 pixDestroy(&pix3);
2049 fpixDestroy(&fpix);
2050 return 0;
2051}
#define GET_DATA_QBIT(pdata, n)
Definition: arrayaccess.h:164
#define SET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:127
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
#define GET_DATA_DIBIT(pdata, n)
Definition: arrayaccess.h:145
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
void boxaDestroy(BOXA **pboxa)
boxaDestroy()
Definition: boxbasic.c:583
BOXA * boxaCombineOverlaps(BOXA *boxas, PIXA *pixadb)
boxaCombineOverlaps()
Definition: boxfunc1.c:478
PIX * pixMaskBoxa(PIX *pixd, PIX *pixs, BOXA *boxa, l_int32 op)
pixMaskBoxa()
Definition: boxfunc3.c:151
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()
Definition: colorcontent.c:494
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()
Definition: colorcontent.c:202
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()
Definition: colorcontent.c:770
PIX * pixMaskOverColorPixels(PIX *pixs, l_int32 threshdiff, l_int32 mindist)
pixMaskOverColorPixels()
Definition: colorcontent.c:688
PIX * pixColorShiftWhitePoint(PIX *pixs, l_int32 rref, l_int32 gref, l_int32 bref)
pixColorShiftWhitePoint()
Definition: colorcontent.c:583
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()
Definition: colorcontent.c:947
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()
Definition: colorcontent.c:828
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()
Definition: colorcontent.c:362
l_ok pixcmapHasColor(PIXCMAP *cmap, l_int32 *pcolor)
pixcmapHasColor()
Definition: colormap.c:1075
l_int32 pixcmapGetCount(const PIXCMAP *cmap)
pixcmapGetCount()
Definition: colormap.c:708
PIXCMAP * pixcmapCreate(l_int32 depth)
pixcmapCreate()
Definition: colormap.c:125
l_ok pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:414
l_ok pixNumberOccupiedOctcubes(PIX *pix, l_int32 level, l_int32 mincount, l_float32 minfract, l_int32 *pncolors)
pixNumberOccupiedOctcubes()
Definition: colorquant1.c:4082
l_ok pixAssignToNearestColor(PIX *pixd, PIX *pixs, PIX *pixm, l_int32 level, l_int32 *countarray)
pixAssignToNearestColor()
Definition: colorseg.c:413
BOXA * pixConnCompBB(PIX *pixs, l_int32 connectivity)
pixConnCompBB()
Definition: conncomp.c:310
PIX * pixSobelEdgeFilter(PIX *pixs, l_int32 orientflag)
pixSobelEdgeFilter()
Definition: edge.c:94
NUMA * numaGammaTRC(l_float32 gamma, l_int32 minval, l_int32 maxval)
numaGammaTRC()
Definition: enhance.c:369
void fpixDestroy(FPIX **pfpix)
fpixDestroy()
Definition: fpix1.c:292
FPIX * pixComponentFunction(PIX *pix, l_float32 rnum, l_float32 gnum, l_float32 bnum, l_float32 rdenom, l_float32 gdenom, l_float32 bdenom)
pixComponentFunction()
Definition: fpix2.c:2367
PIX * fpixThresholdToPix(FPIX *fpix, l_float32 thresh)
fpixThresholdToPix()
Definition: fpix2.c:2309
PIX * pixThresholdToBinary(PIX *pixs, l_int32 thresh)
pixThresholdToBinary()
Definition: grayquant.c:447
PIX * pixDilateBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateBrick()
Definition: morph.c:688
PIX * pixCloseSafeBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseSafeBrick()
Definition: morph.c:977
PIX * pixErodeBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixErodeBrick()
Definition: morph.c:758
PIX * pixMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequence()
Definition: morphseq.c:137
l_int32 * numaGetIArray(NUMA *na)
numaGetIArray()
Definition: numabasic.c:847
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:366
l_float32 * numaGetFArray(NUMA *na, l_int32 copyflag)
numaGetFArray()
Definition: numabasic.c:892
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:658
l_ok numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:754
NUMA * numaSortIndexAutoSelect(NUMA *nas, l_int32 sortorder)
numaSortIndexAutoSelect()
Definition: numafunc1.c:2562
NUMA * numaMakeConstant(l_float32 val, l_int32 size)
numaMakeConstant()
Definition: numafunc1.c:851
l_ok pixSetColormap(PIX *pix, PIXCMAP *colormap)
pixSetColormap()
Definition: pix1.c:1699
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:621
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1113
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:593
PIX * pixCreateTemplate(const PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:383
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
PIX * pixCopy(PIX *pixd, const PIX *pixs)
pixCopy()
Definition: pix1.c:705
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1763
l_ok pixSetAll(PIX *pix)
pixSetAll()
Definition: pix2.c:817
l_ok composeRGBPixel(l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 *ppixel)
composeRGBPixel()
Definition: pix2.c:2751
void extractRGBValues(l_uint32 pixel, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
extractRGBValues()
Definition: pix2.c:2820
l_ok setLineDataVal(l_uint32 *line, l_int32 j, l_int32 d, l_uint32 val)
setLineDataVal()
Definition: pix2.c:2952
PIX * pixGetRGBComponent(PIX *pixs, l_int32 comp)
pixGetRGBComponent()
Definition: pix2.c:2479
PIX * pixOr(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixOr()
Definition: pix3.c:1560
l_ok pixCountPixels(PIX *pixs, l_int32 *pcount, l_int32 *tab8)
pixCountPixels()
Definition: pix3.c:1937
PIX * pixAnd(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixAnd()
Definition: pix3.c:1624
PIX * pixSubtract(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixSubtract()
Definition: pix3.c:1753
l_ok pixSetMasked(PIX *pixd, PIX *pixm, l_uint32 val)
pixSetMasked()
Definition: pix3.c:163
l_ok pixForegroundFraction(PIX *pix, l_float32 *pfract)
pixForegroundFraction()
Definition: pix3.c:1865
l_ok pixCombineMasked(PIX *pixd, PIX *pixs, PIX *pixm)
pixCombineMasked()
Definition: pix3.c:382
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1509
l_ok pixCountRGBColorsByHash(PIX *pixs, l_int32 *pncolors)
pixCountRGBColorsByHash()
Definition: pix4.c:844
l_ok pixGetRankColorArray(PIX *pixs, l_int32 nbins, l_int32 type, l_int32 factor, l_uint32 **pcarray, PIXA *pixadb, l_int32 fontsize)
pixGetRankColorArray()
Definition: pix4.c:2600
PIX * pixDisplayColorArray(l_uint32 *carray, l_int32 ncolors, l_int32 side, l_int32 ncols, l_int32 fontsize)
pixDisplayColorArray()
Definition: pix4.c:2877
NUMA * pixGetGrayHistogram(PIX *pixs, l_int32 factor)
pixGetGrayHistogram()
Definition: pix4.c:115
PIX * pixMakeSymmetricMask(l_int32 w, l_int32 h, l_float32 hf, l_float32 vf, l_int32 type)
pixSelectComponentBySize()
Definition: pix5.c:1532
@ COLOR_RED
Definition: pix.h:204
@ L_SELECT_AVERAGE
Definition: pix.h:827
@ L_SET_PIXELS
Definition: pix.h:772
@ L_MAX_DIFF
Definition: pix.h:840
@ L_INTERMED_DIFF
Definition: pix.h:838
@ L_AVE_MAX_DIFF_2
Definition: pix.h:839
@ L_SELECT_IF_GTE
Definition: pix.h:785
@ L_USE_INNER
Definition: pix.h:1237
@ REMOVE_CMAP_TO_FULL_COLOR
Definition: pix.h:258
@ L_COPY
Definition: pix.h:712
@ L_NOCOPY
Definition: pix.h:710
@ L_INSERT
Definition: pix.h:711
@ L_SORT_DECREASING
Definition: pix.h:730
@ L_ALL_EDGES
Definition: pix.h:1005
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:506
PIX * pixConvertRGBToBinaryArb(PIX *pixs, l_float32 rc, l_float32 gc, l_float32 bc, l_int32 thresh, l_int32 relation)
pixConvertRGBToBinaryArb()
Definition: pixconv.c:1190
PIX * pixConvertRGBToLuminance(PIX *pixs)
pixConvertRGBToLuminance()
Definition: pixconv.c:742
PIX * pixConvertRGBToGrayMinMax(PIX *pixs, l_int32 type)
pixConvertRGBToGrayMinMax()
Definition: pixconv.c:963
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:328
PIX * pixConvertRGBToGray(PIX *pixs, l_float32 rwt, l_float32 gwt, l_float32 bwt)
pixConvertRGBToGray()
Definition: pixconv.c:827
PIX * pixScaleAreaMap2(PIX *pix)
pixScaleAreaMap2()
Definition: scale1.c:2047
PIX * pixScaleByIntSampling(PIX *pixs, l_int32 factor)
pixScaleByIntSampling()
Definition: scale1.c:1444
PIX * pixScaleAreaMap(PIX *pix, l_float32 scalex, l_float32 scaley)
pixScaleAreaMap()
Definition: scale1.c:1914
PIX * pixRemoveBorderConnComps(PIX *pixs, l_int32 connectivity)
pixRemoveBorderConnComps()
Definition: seedfill.c:737
Definition: pix.h:492
Definition: pix.h:579
Definition: array.h:71
Definition: pix.h:139
Definition: pix.h:456