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  * ----------------------------------------------------------------------- */
201 l_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 {
211 l_int32 w, h, i, j, wpl1, wplr, wplg, wplb;
212 l_int32 rval, gval, bval, rgdiff, rbdiff, gbdiff, maxval, colorval;
213 l_uint32 pixel;
214 l_uint32 *data1, *datar, *datag, *datab, *line1, *liner, *lineg, *lineb;
215 PIX *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  * ----------------------------------------------------------------------- */
361 PIX *
363  l_int32 rref,
364  l_int32 gref,
365  l_int32 bref,
366  l_int32 type)
367 {
368 l_int32 w, h, i, j, wpl1, wpld;
369 l_int32 rval, gval, bval, rdist, gdist, bdist, colorval;
370 l_int32 rgdist, rbdist, gbdist, mindist, maxdist, minval, maxval;
371 l_uint32 pixel;
372 l_uint32 *data1, *datad, *line1, *lined;
373 PIX *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  * ----------------------------------------------------------------------- */
493 l_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 {
502 l_int32 i, j, w, h, wpl, rval, gval, bval, minval, maxval;
503 l_int32 total, npix, ncolor;
504 l_uint32 pixel;
505 l_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  * ----------------------------------------------------------------------- */
582 PIX *
584  l_int32 rref,
585  l_int32 gref,
586  l_int32 bref)
587 {
588 l_int32 w, h, i, j, wpl1, wpl2, rval, gval, bval;
589 l_int32 *rtab, *gtab, *btab;
590 l_uint32 pixel;
591 l_uint32 *data1, *data2, *line1, *line2;
592 NUMA *nar, *nag, *nab;
593 PIX *pix1, *pix2;
594 PIXCMAP *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  * ----------------------------------------------------------------------- */
687 PIX *
689  l_int32 threshdiff,
690  l_int32 mindist)
691 {
692 l_int32 w, h, d, i, j, wpls, wpld, size;
693 l_int32 rval, gval, bval, minval, maxval;
694 l_uint32 *datas, *datad, *lines, *lined;
695 PIX *pixc, *pixd;
696 PIXCMAP *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  * ----------------------------------------------------------------------- */
769 PIX *
771  l_int32 maxlimit,
772  l_int32 satlimit)
773 {
774 l_int32 w, h, i, j, wpls, wpld;
775 l_int32 rval, gval, bval, minrg, min, maxrg, max, sat;
776 l_uint32 *datas, *datad, *lines, *lined;
777 PIX *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  * ----------------------------------------------------------------------- */
827 PIX *
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 {
836 l_int32 w, h, d, i, j, wpls, wpld;
837 l_int32 rval, gval, bval;
838 l_uint32 *datas, *datad, *lines, *lined;
839 PIX *pixc, *pixd;
840 PIXCMAP *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  * ----------------------------------------------------------------------- */
946 l_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 {
960 l_int32 w, h, count, rval, gval, bval, aveval, proceed;
961 l_float32 ratio;
962 l_uint32 *carray;
963 BOXA *boxa1, *boxa2;
964 PIX *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  * ----------------------------------------------------------------------- */
1162 l_ok
1164  l_int32 darkthresh,
1165  l_int32 lightthresh,
1166  l_float32 minfract,
1167  l_int32 factor,
1168  l_int32 *pncolors)
1169 {
1170 l_int32 i, w, h, count, mincount, ncolors;
1171 NUMA *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  * ----------------------------------------------------------------------- */
1285 l_ok
1287  l_int32 thresh,
1288  l_int32 *pncolors,
1289  l_int32 *piscolor,
1290  l_int32 debug)
1291 {
1292 l_int32 w, h, d, minside, factor;
1293 l_float32 pixfract, colorfract;
1294 PIX *pixt, *pixsc, *pixg, *pixe, *pixb, *pixm;
1295 PIXCMAP *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  * ----------------------------------------------------------------------- */
1427 l_ok
1429  l_int32 factor,
1430  l_int32 *pncolors)
1431 {
1432 l_int32 w, h, d, i, j, wpl, hashsize, sum, count, manycolors;
1433 l_int32 rval, gval, bval, val;
1434 l_int32 *inta;
1435 l_uint32 pixel;
1436 l_uint32 *data, *line;
1437 PIXCMAP *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  * ----------------------------------------------------------------------- */
1530 PIX *
1532 {
1533 l_int32 w, h, d, i, j, wpls, wpld, hashsize, hashval, ncolors, index;
1534 l_int32 rval, gval, bval, val;
1535 l_int32 *hasha1, *hasha2;
1536 l_uint32 pixel;
1537 l_uint32 *datas, *lines, *datad, *lined;
1538 PIX *pixd;
1539 PIXCMAP *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  * ----------------------------------------------------------------------- */
1627 l_ok
1629  l_int32 sigbits,
1630  l_int32 factor,
1631  l_int32 ncolors,
1632  l_uint32 **parray,
1633  PIXCMAP **pcmap)
1634 {
1635 l_int32 n, i, rgbindex, rval, gval, bval;
1636 NUMA *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 
1704 PIX *
1706  l_int32 sigbits,
1707  l_int32 factor,
1708  l_int32 ncolors)
1709 {
1710 l_int32 w, h;
1711 PIX *pixd;
1712 PIXCMAP *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  * ----------------------------------------------------------------------- */
1750 NUMA *
1752  l_int32 sigbits,
1753  l_int32 factor)
1754 {
1755 l_int32 w, h, i, j, size, wpl, rval, gval, bval, npts;
1756 l_uint32 val32, rgbindex;
1757 l_float32 *array;
1758 l_uint32 *data, *line, *rtab, *gtab, *btab;
1759 NUMA *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 
1818 l_ok
1819 makeRGBIndexTables(l_uint32 **prtab,
1820  l_uint32 **pgtab,
1821  l_uint32 **pbtab,
1822  l_int32 sigbits)
1823 {
1824 l_int32 i;
1825 l_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 
1908 l_ok
1909 getRGBFromIndex(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  * ----------------------------------------------------------------------- */
1991 l_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 {
2000 l_float32 fract, ratio;
2001 PIX *pix1, *pix2, *pix3, *pix4;
2002 FPIX *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 }
PIX * pixMaskOverColorPixels(PIX *pixs, l_int32 threshdiff, l_int32 mindist)
pixMaskOverColorPixels()
Definition: colorcontent.c:688
PIX * pixConvertRGBToLuminance(PIX *pixs)
pixConvertRGBToLuminance()
Definition: pixconv.c:742
NUMA * pixGetGrayHistogram(PIX *pixs, l_int32 factor)
pixGetGrayHistogram()
Definition: pix4.c:115
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:328
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 * fpixThresholdToPix(FPIX *fpix, l_float32 thresh)
fpixThresholdToPix()
Definition: fpix2.c:2309
l_ok pixSetMasked(PIX *pixd, PIX *pixm, l_uint32 val)
pixSetMasked()
Definition: pix3.c:163
PIX * pixCreateTemplate(const PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:383
Definition: pix.h:204
l_int32 n
Definition: ccbord.h:113
NUMA * numaGammaTRC(l_float32 gamma, l_int32 minval, l_int32 maxval)
numaGammaTRC()
Definition: enhance.c:369
Definition: pix.h:712
l_ok pixCountRGBColorsByHash(PIX *pixs, l_int32 *pncolors)
pixCountRGBColorsByHash()
Definition: pix4.c:844
PIX * pixCopy(PIX *pixd, const PIX *pixs)
pixCopy()
Definition: pix1.c:705
NUMA * numaMakeConstant(l_float32 val, l_int32 size)
numaMakeConstant()
Definition: numafunc1.c:851
Definition: pix.h:710
PIX * pixDilateBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateBrick()
Definition: morph.c:688
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
l_ok pixNumberOccupiedOctcubes(PIX *pix, l_int32 level, l_int32 mincount, l_float32 minfract, l_int32 *pncolors)
pixNumberOccupiedOctcubes()
Definition: colorquant1.c:4082
l_ok pixSetAll(PIX *pix)
pixSetAll()
Definition: pix2.c:817
PIX * pixScaleAreaMap(PIX *pix, l_float32 scalex, l_float32 scaley)
pixScaleAreaMap()
Definition: scale1.c:1914
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1509
void boxaDestroy(BOXA **pboxa)
boxaDestroy()
Definition: boxbasic.c:583
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1763
PIX * pixThresholdToBinary(PIX *pixs, l_int32 thresh)
pixThresholdToBinary()
Definition: grayquant.c:447
l_ok pixNumColors(PIX *pixs, l_int32 factor, l_int32 *pncolors)
pixNumColors()
l_ok setLineDataVal(l_uint32 *line, l_int32 j, l_int32 d, l_uint32 val)
setLineDataVal()
Definition: pix2.c:2952
Definition: pix.h:491
l_ok pixAssignToNearestColor(PIX *pixd, PIX *pixs, PIX *pixm, l_int32 level, l_int32 *countarray)
pixAssignToNearestColor()
Definition: colorseg.c:413
l_ok pixSetColormap(PIX *pix, PIXCMAP *colormap)
pixSetColormap()
Definition: pix1.c:1699
PIX * pixGetRGBComponent(PIX *pixs, l_int32 comp)
pixGetRGBComponent()
Definition: pix2.c:2479
l_ok pixColorsForQuantization(PIX *pixs, l_int32 thresh, l_int32 *pncolors, l_int32 *piscolor, l_int32 debug)
pixColorsForQuantization()
PIXCMAP * pixcmapCreate(l_int32 depth)
pixcmapCreate()
Definition: colormap.c:125
l_ok pixCombineMasked(PIX *pixd, PIX *pixs, PIX *pixm)
pixCombineMasked()
Definition: pix3.c:382
l_int32 * numaGetIArray(NUMA *na)
numaGetIArray()
Definition: numabasic.c:847
NUMA * pixGetRGBHistogram(PIX *pixs, l_int32 sigbits, l_int32 factor)
pixGetRGBHistogram()
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
l_ok numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:754
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:506
Definition: array.h:70
PIX * pixCloseSafeBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseSafeBrick()
Definition: morph.c:977
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:658
l_ok pixForegroundFraction(PIX *pix, l_float32 *pfract)
pixForegroundFraction()
Definition: pix3.c:1865
BOXA * pixConnCompBB(PIX *pixs, l_int32 connectivity)
pixConnCompBB()
Definition: conncomp.c:310
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 * pixAnd(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixAnd()
Definition: pix3.c:1624
PIX * pixMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequence()
Definition: morphseq.c:137
PIX * pixMakeSymmetricMask(l_int32 w, l_int32 h, l_float32 hf, l_float32 vf, l_int32 type)
pixSelectComponentBySize()
Definition: pix5.c:1532
l_ok pixCountPixels(PIX *pixs, l_int32 *pcount, l_int32 *tab8)
pixCountPixels()
Definition: pix3.c:1937
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
#define GET_DATA_QBIT(pdata, n)
Definition: arrayaccess.h:164
l_int32 h
Definition: ccbord.h:112
PIX * pixConvertRGBToGrayMinMax(PIX *pixs, l_int32 type)
pixConvertRGBToGrayMinMax()
Definition: pixconv.c:963
l_int32 w
Definition: ccbord.h:111
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
PIX * pixRemoveBorderConnComps(PIX *pixs, l_int32 connectivity)
pixRemoveBorderConnComps()
Definition: seedfill.c:737
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:593
PIX * pixSubtract(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixSubtract()
Definition: pix3.c:1753
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:621
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
PIX * pixMaskBoxa(PIX *pixd, PIX *pixs, BOXA *boxa, l_int32 op)
pixMaskBoxa()
Definition: boxfunc3.c:151
Definition: pix.h:711
PIX * pixScaleByIntSampling(PIX *pixs, l_int32 factor)
pixScaleByIntSampling()
Definition: scale1.c:1444
l_ok pixcmapHasColor(PIXCMAP *cmap, l_int32 *pcolor)
pixcmapHasColor()
Definition: colormap.c:1075
Definition: pix.h:455
PIX * pixScaleAreaMap2(PIX *pix)
pixScaleAreaMap2()
Definition: scale1.c:2047
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:366
l_ok pixNumSignificantGrayColors(PIX *pixs, l_int32 darkthresh, l_int32 lightthresh, l_float32 minfract, l_int32 factor, l_int32 *pncolors)
pixNumSignificantGrayColors()
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1113
PIX * pixConvertRGBToGray(PIX *pixs, l_float32 rwt, l_float32 gwt, l_float32 bwt)
pixConvertRGBToGray()
Definition: pixconv.c:827
PIX * pixOr(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixOr()
Definition: pix3.c:1560
l_ok makeRGBIndexTables(l_uint32 **prtab, l_uint32 **pgtab, l_uint32 **pbtab, l_int32 sigbits)
makeRGBIndexTables()
PIX * pixConvertRGBToCmapLossless(PIX *pixs)
pixConvertRGBToCmapLossless()
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 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_float32 * numaGetFArray(NUMA *na, l_int32 copyflag)
numaGetFArray()
Definition: numabasic.c:892
PIX * pixColorShiftWhitePoint(PIX *pixs, l_int32 rref, l_int32 gref, l_int32 bref)
pixColorShiftWhitePoint()
Definition: colorcontent.c:583
#define GET_DATA_DIBIT(pdata, n)
Definition: arrayaccess.h:145
BOXA * boxaCombineOverlaps(BOXA *boxas, PIXA *pixadb)
boxaCombineOverlaps()
Definition: boxfunc1.c:478
PIX * pixSimpleColorQuantize(PIX *pixs, l_int32 sigbits, l_int32 factor, l_int32 ncolors)
pixSimpleColorQuantize()
NUMA * numaSortIndexAutoSelect(NUMA *nas, l_int32 sortorder)
numaSortIndexAutoSelect()
Definition: numafunc1.c:2562
Definition: pix.h:138
PIX * pixSobelEdgeFilter(PIX *pixs, l_int32 orientflag)
pixSobelEdgeFilter()
Definition: edge.c:94
PIX * pixColorMagnitude(PIX *pixs, l_int32 rref, l_int32 gref, l_int32 bref, l_int32 type)
pixColorMagnitude()
Definition: colorcontent.c:362
l_int32 pixcmapGetCount(const PIXCMAP *cmap)
pixcmapGetCount()
Definition: colormap.c:708
PIX * pixDisplayColorArray(l_uint32 *carray, l_int32 ncolors, l_int32 side, l_int32 ncols, l_int32 fontsize)
pixDisplayColorArray()
Definition: pix4.c:2877
PIX * pixErodeBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixErodeBrick()
Definition: morph.c:758
l_ok composeRGBPixel(l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 *ppixel)
composeRGBPixel()
Definition: pix2.c:2751
l_ok pixGetMostPopulatedColors(PIX *pixs, l_int32 sigbits, l_int32 factor, l_int32 ncolors, l_uint32 **parray, PIXCMAP **pcmap)
pixGetMostPopulatedColors()
PIX * pixMaskOverGrayPixels(PIX *pixs, l_int32 maxlimit, l_int32 satlimit)
pixMaskOverGrayPixels()
Definition: colorcontent.c:770
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 pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:414
void fpixDestroy(FPIX **pfpix)
fpixDestroy()
Definition: fpix1.c:292
l_ok pixHasHighlightRed(PIX *pixs, l_int32 factor, l_float32 minfract, l_float32 fthresh, l_int32 *phasred, l_float32 *pratio, PIX **ppixdb)
pixHasHighlightRed()
void extractRGBValues(l_uint32 pixel, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
extractRGBValues()
Definition: pix2.c:2820
l_ok getRGBFromIndex(l_uint32 index, l_int32 sigbits, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
getRGBFromIndex()
#define SET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:127
Definition: pix.h:578