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