Leptonica  1.77.0
Image processing and image analysis suite
binarize.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 
78 #include <math.h>
79 #include "allheaders.h"
80 
81 /*------------------------------------------------------------------*
82  * Adaptive Otsu-based thresholding *
83  *------------------------------------------------------------------*/
139 l_ok
141  l_int32 sx,
142  l_int32 sy,
143  l_int32 smoothx,
144  l_int32 smoothy,
145  l_float32 scorefract,
146  PIX **ppixth,
147  PIX **ppixd)
148 {
149 l_int32 w, h, nx, ny, i, j, thresh;
150 l_uint32 val;
151 PIX *pixt, *pixb, *pixthresh, *pixth, *pixd;
152 PIXTILING *pt;
153 
154  PROCNAME("pixOtsuAdaptiveThreshold");
155 
156  if (!ppixth && !ppixd)
157  return ERROR_INT("neither &pixth nor &pixd defined", procName, 1);
158  if (ppixth) *ppixth = NULL;
159  if (ppixd) *ppixd = NULL;
160  if (!pixs || pixGetDepth(pixs) != 8)
161  return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
162  if (sx < 16 || sy < 16)
163  return ERROR_INT("sx and sy must be >= 16", procName, 1);
164 
165  /* Compute the threshold array for the tiles */
166  pixGetDimensions(pixs, &w, &h, NULL);
167  nx = L_MAX(1, w / sx);
168  ny = L_MAX(1, h / sy);
169  smoothx = L_MIN(smoothx, (nx - 1) / 2);
170  smoothy = L_MIN(smoothy, (ny - 1) / 2);
171  pt = pixTilingCreate(pixs, nx, ny, 0, 0, 0, 0);
172  pixthresh = pixCreate(nx, ny, 8);
173  for (i = 0; i < ny; i++) {
174  for (j = 0; j < nx; j++) {
175  pixt = pixTilingGetTile(pt, i, j);
176  pixSplitDistributionFgBg(pixt, scorefract, 1, &thresh,
177  NULL, NULL, NULL);
178  pixSetPixel(pixthresh, j, i, thresh); /* see note (4) */
179  pixDestroy(&pixt);
180  }
181  }
182 
183  /* Optionally smooth the threshold array */
184  if (smoothx > 0 || smoothy > 0)
185  pixth = pixBlockconv(pixthresh, smoothx, smoothy);
186  else
187  pixth = pixClone(pixthresh);
188  pixDestroy(&pixthresh);
189 
190  /* Optionally apply the threshold array to binarize pixs */
191  if (ppixd) {
192  pixd = pixCreate(w, h, 1);
193  pixCopyResolution(pixd, pixs);
194  for (i = 0; i < ny; i++) {
195  for (j = 0; j < nx; j++) {
196  pixt = pixTilingGetTile(pt, i, j);
197  pixGetPixel(pixth, j, i, &val);
198  pixb = pixThresholdToBinary(pixt, val);
199  pixTilingPaintTile(pixd, i, j, pixb, pt);
200  pixDestroy(&pixt);
201  pixDestroy(&pixb);
202  }
203  }
204  *ppixd = pixd;
205  }
206 
207  if (ppixth)
208  *ppixth = pixth;
209  else
210  pixDestroy(&pixth);
211 
212  pixTilingDestroy(&pt);
213  return 0;
214 }
215 
216 
217 /*------------------------------------------------------------------*
218  * Otsu thresholding on adaptive background normalization *
219  *------------------------------------------------------------------*/
255 PIX *
257  PIX *pixim,
258  l_int32 sx,
259  l_int32 sy,
260  l_int32 thresh,
261  l_int32 mincount,
262  l_int32 bgval,
263  l_int32 smoothx,
264  l_int32 smoothy,
265  l_float32 scorefract,
266  l_int32 *pthresh)
267 {
268 l_int32 w, h;
269 l_uint32 val;
270 PIX *pixn, *pixt, *pixd;
271 
272  PROCNAME("pixOtsuThreshOnBackgroundNorm");
273 
274  if (pthresh) *pthresh = 0;
275  if (!pixs || pixGetDepth(pixs) != 8)
276  return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
277  if (pixGetColormap(pixs))
278  return (PIX *)ERROR_PTR("pixs is colormapped", procName, NULL);
279  if (sx < 4 || sy < 4)
280  return (PIX *)ERROR_PTR("sx and sy must be >= 4", procName, NULL);
281  if (mincount > sx * sy) {
282  L_WARNING("mincount too large for tile size\n", procName);
283  mincount = (sx * sy) / 3;
284  }
285 
286  pixn = pixBackgroundNorm(pixs, pixim, NULL, sx, sy, thresh,
287  mincount, bgval, smoothx, smoothy);
288  if (!pixn)
289  return (PIX *)ERROR_PTR("pixn not made", procName, NULL);
290 
291  /* Just use 1 tile for a global threshold, which is stored
292  * as a single pixel in pixt. */
293  pixGetDimensions(pixn, &w, &h, NULL);
294  pixOtsuAdaptiveThreshold(pixn, w, h, 0, 0, scorefract, &pixt, &pixd);
295  pixDestroy(&pixn);
296 
297  if (pixt && pthresh) {
298  pixGetPixel(pixt, 0, 0, &val);
299  *pthresh = val;
300  }
301  pixDestroy(&pixt);
302 
303  if (!pixd)
304  return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
305  else
306  return pixd;
307 }
308 
309 
310 
311 /*----------------------------------------------------------------------*
312  * Masking and Otsu estimate on adaptive background normalization *
313  *----------------------------------------------------------------------*/
353 PIX *
355  PIX *pixim,
356  l_int32 sx,
357  l_int32 sy,
358  l_int32 thresh,
359  l_int32 mincount,
360  l_int32 smoothx,
361  l_int32 smoothy,
362  l_float32 scorefract,
363  l_int32 *pthresh)
364 {
365 l_int32 w, h, highthresh;
366 l_uint32 val;
367 PIX *pixn, *pixm, *pixd, *pix1, *pix2, *pix3, *pix4;
368 
369  PROCNAME("pixMaskedThreshOnBackgroundNorm");
370 
371  if (pthresh) *pthresh = 0;
372  if (!pixs || pixGetDepth(pixs) != 8)
373  return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
374  if (pixGetColormap(pixs))
375  return (PIX *)ERROR_PTR("pixs is colormapped", procName, NULL);
376  if (sx < 4 || sy < 4)
377  return (PIX *)ERROR_PTR("sx and sy must be >= 4", procName, NULL);
378  if (mincount > sx * sy) {
379  L_WARNING("mincount too large for tile size\n", procName);
380  mincount = (sx * sy) / 3;
381  }
382 
383  /* Standard background normalization */
384  pixn = pixBackgroundNorm(pixs, pixim, NULL, sx, sy, thresh,
385  mincount, 255, smoothx, smoothy);
386  if (!pixn)
387  return (PIX *)ERROR_PTR("pixn not made", procName, NULL);
388 
389  /* Special background normalization for adaptation to quickly
390  * varying background. Threshold on the very light parts,
391  * which tend to be near significant edges, and dilate to
392  * form a mask over regions that are typically text. The
393  * dilation size is chosen to cover the text completely,
394  * except for very thick fonts. */
395  pix1 = pixBackgroundNormFlex(pixs, 7, 7, 1, 1, 20);
396  pix2 = pixThresholdToBinary(pix1, 240);
397  pixInvert(pix2, pix2);
398  pixm = pixMorphSequence(pix2, "d21.21", 0);
399  pixDestroy(&pix1);
400  pixDestroy(&pix2);
401 
402  /* Use Otsu to get a global threshold estimate for the image,
403  * which is stored as a single pixel in pix3. */
404  pixGetDimensions(pixs, &w, &h, NULL);
405  pixOtsuAdaptiveThreshold(pixs, w, h, 0, 0, scorefract, &pix3, NULL);
406  pixGetPixel(pix3, 0, 0, &val);
407  if (pthresh) *pthresh = val;
408  pixDestroy(&pix3);
409 
410  /* Threshold the background normalized images differentially,
411  * using a high value correlated with the background normalization
412  * for the part of the image under the mask (i.e., near the
413  * darker, thicker foreground), and a value that depends on the Otsu
414  * threshold for the rest of the image. This gives a solid
415  * (high) thresholding for the foreground parts of the image,
416  * while allowing the background and light foreground to be
417  * reasonably well cleaned using a threshold adapted to the
418  * input image. */
419  highthresh = L_MIN(256, val + 30);
420  pixd = pixThresholdToBinary(pixn, highthresh); /* for bg and light fg */
421  pix4 = pixThresholdToBinary(pixn, 190); /* for heavier fg */
422  pixCombineMasked(pixd, pix4, pixm);
423  pixDestroy(&pix4);
424  pixDestroy(&pixm);
425  pixDestroy(&pixn);
426 
427  if (!pixd)
428  return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
429  else
430  return pixd;
431 }
432 
433 
434 /*----------------------------------------------------------------------*
435  * Sauvola binarization *
436  *----------------------------------------------------------------------*/
466 l_ok
468  l_int32 whsize,
469  l_float32 factor,
470  l_int32 nx,
471  l_int32 ny,
472  PIX **ppixth,
473  PIX **ppixd)
474 {
475 l_int32 i, j, w, h, xrat, yrat;
476 PIX *pixth, *pixd, *tileth, *tiled, *pixt;
477 PIX **ptileth, **ptiled;
478 PIXTILING *pt;
479 
480  PROCNAME("pixSauvolaBinarizeTiled");
481 
482  if (!ppixth && !ppixd)
483  return ERROR_INT("no outputs", procName, 1);
484  if (ppixth) *ppixth = NULL;
485  if (ppixd) *ppixd = NULL;
486  if (!pixs || pixGetDepth(pixs) != 8)
487  return ERROR_INT("pixs undefined or not 8 bpp", procName, 1);
488  if (pixGetColormap(pixs))
489  return ERROR_INT("pixs is cmapped", procName, 1);
490  pixGetDimensions(pixs, &w, &h, NULL);
491  if (whsize < 2)
492  return ERROR_INT("whsize must be >= 2", procName, 1);
493  if (w < 2 * whsize + 3 || h < 2 * whsize + 3)
494  return ERROR_INT("whsize too large for image", procName, 1);
495  if (factor < 0.0)
496  return ERROR_INT("factor must be >= 0", procName, 1);
497 
498  if (nx <= 1 && ny <= 1)
499  return pixSauvolaBinarize(pixs, whsize, factor, 1, NULL, NULL,
500  ppixth, ppixd);
501 
502  /* Test to see if the tiles are too small. The required
503  * condition is that the tile dimensions must be at least
504  * (whsize + 2) x (whsize + 2). */
505  xrat = w / nx;
506  yrat = h / ny;
507  if (xrat < whsize + 2) {
508  nx = w / (whsize + 2);
509  L_WARNING("tile width too small; nx reduced to %d\n", procName, nx);
510  }
511  if (yrat < whsize + 2) {
512  ny = h / (whsize + 2);
513  L_WARNING("tile height too small; ny reduced to %d\n", procName, ny);
514  }
515  if (nx <= 1 && ny <= 1)
516  return pixSauvolaBinarize(pixs, whsize, factor, 1, NULL, NULL,
517  ppixth, ppixd);
518 
519  /* We can use pixtiling for painting both outputs, if requested */
520  if (ppixth) {
521  pixth = pixCreateNoInit(w, h, 8);
522  *ppixth = pixth;
523  }
524  if (ppixd) {
525  pixd = pixCreateNoInit(w, h, 1);
526  *ppixd = pixd;
527  }
528  pt = pixTilingCreate(pixs, nx, ny, 0, 0, whsize + 1, whsize + 1);
529  pixTilingNoStripOnPaint(pt); /* pixSauvolaBinarize() does the stripping */
530 
531  for (i = 0; i < ny; i++) {
532  for (j = 0; j < nx; j++) {
533  pixt = pixTilingGetTile(pt, i, j);
534  ptileth = (ppixth) ? &tileth : NULL;
535  ptiled = (ppixd) ? &tiled : NULL;
536  pixSauvolaBinarize(pixt, whsize, factor, 0, NULL, NULL,
537  ptileth, ptiled);
538  if (ppixth) { /* do not strip */
539  pixTilingPaintTile(pixth, i, j, tileth, pt);
540  pixDestroy(&tileth);
541  }
542  if (ppixd) {
543  pixTilingPaintTile(pixd, i, j, tiled, pt);
544  pixDestroy(&tiled);
545  }
546  pixDestroy(&pixt);
547  }
548  }
549 
550  pixTilingDestroy(&pt);
551  return 0;
552 }
553 
554 
593 l_ok
595  l_int32 whsize,
596  l_float32 factor,
597  l_int32 addborder,
598  PIX **ppixm,
599  PIX **ppixsd,
600  PIX **ppixth,
601  PIX **ppixd)
602 {
603 l_int32 w, h;
604 PIX *pixg, *pixsc, *pixm, *pixms, *pixth, *pixd;
605 
606  PROCNAME("pixSauvolaBinarize");
607 
608  if (ppixm) *ppixm = NULL;
609  if (ppixsd) *ppixsd = NULL;
610  if (ppixth) *ppixth = NULL;
611  if (ppixd) *ppixd = NULL;
612  if (!ppixm && !ppixsd && !ppixth && !ppixd)
613  return ERROR_INT("no outputs", procName, 1);
614  if (!pixs || pixGetDepth(pixs) != 8)
615  return ERROR_INT("pixs undefined or not 8 bpp", procName, 1);
616  if (pixGetColormap(pixs))
617  return ERROR_INT("pixs is cmapped", procName, 1);
618  pixGetDimensions(pixs, &w, &h, NULL);
619  if (whsize < 2)
620  return ERROR_INT("whsize must be >= 2", procName, 1);
621  if (w < 2 * whsize + 3 || h < 2 * whsize + 3)
622  return ERROR_INT("whsize too large for image", procName, 1);
623  if (factor < 0.0)
624  return ERROR_INT("factor must be >= 0", procName, 1);
625 
626  if (addborder) {
627  pixg = pixAddMirroredBorder(pixs, whsize + 1, whsize + 1,
628  whsize + 1, whsize + 1);
629  pixsc = pixClone(pixs);
630  } else {
631  pixg = pixClone(pixs);
632  pixsc = pixRemoveBorder(pixs, whsize + 1);
633  }
634  if (!pixg || !pixsc)
635  return ERROR_INT("pixg and pixsc not made", procName, 1);
636 
637  /* All these functions strip off the border pixels. */
638  if (ppixm || ppixth || ppixd)
639  pixm = pixWindowedMean(pixg, whsize, whsize, 1, 1);
640  if (ppixsd || ppixth || ppixd)
641  pixms = pixWindowedMeanSquare(pixg, whsize, whsize, 1);
642  if (ppixth || ppixd)
643  pixth = pixSauvolaGetThreshold(pixm, pixms, factor, ppixsd);
644  if (ppixd) {
645  pixd = pixApplyLocalThreshold(pixsc, pixth, 1);
646  pixCopyResolution(pixd, pixs);
647  }
648 
649  if (ppixm)
650  *ppixm = pixm;
651  else
652  pixDestroy(&pixm);
653  pixDestroy(&pixms);
654  if (ppixth)
655  *ppixth = pixth;
656  else
657  pixDestroy(&pixth);
658  if (ppixd)
659  *ppixd = pixd;
660  pixDestroy(&pixg);
661  pixDestroy(&pixsc);
662  return 0;
663 }
664 
665 
703 PIX *
705  PIX *pixms,
706  l_float32 factor,
707  PIX **ppixsd)
708 {
709 l_int32 i, j, w, h, tabsize, wplm, wplms, wplsd, wpld, usetab;
710 l_int32 mv, ms, var, thresh;
711 l_uint32 *datam, *datams, *datasd, *datad;
712 l_uint32 *linem, *linems, *linesd, *lined;
713 l_float32 sd;
714 l_float32 *tab; /* of 2^16 square roots */
715 PIX *pixsd, *pixd;
716 
717  PROCNAME("pixSauvolaGetThreshold");
718 
719  if (ppixsd) *ppixsd = NULL;
720  if (!pixm || pixGetDepth(pixm) != 8)
721  return (PIX *)ERROR_PTR("pixm undefined or not 8 bpp", procName, NULL);
722  if (pixGetColormap(pixm))
723  return (PIX *)ERROR_PTR("pixm is colormapped", procName, NULL);
724  if (!pixms || pixGetDepth(pixms) != 32)
725  return (PIX *)ERROR_PTR("pixms undefined or not 32 bpp",
726  procName, NULL);
727  if (factor < 0.0)
728  return (PIX *)ERROR_PTR("factor must be >= 0", procName, NULL);
729 
730  /* Only make a table of 2^16 square roots if there
731  * are enough pixels to justify it. */
732  pixGetDimensions(pixm, &w, &h, NULL);
733  usetab = (w * h > 100000) ? 1 : 0;
734  if (usetab) {
735  tabsize = 1 << 16;
736  tab = (l_float32 *)LEPT_CALLOC(tabsize, sizeof(l_float32));
737  for (i = 0; i < tabsize; i++)
738  tab[i] = sqrtf((l_float32)i);
739  }
740 
741  pixd = pixCreate(w, h, 8);
742  if (ppixsd) {
743  pixsd = pixCreate(w, h, 8);
744  *ppixsd = pixsd;
745  }
746  datam = pixGetData(pixm);
747  datams = pixGetData(pixms);
748  if (ppixsd) datasd = pixGetData(pixsd);
749  datad = pixGetData(pixd);
750  wplm = pixGetWpl(pixm);
751  wplms = pixGetWpl(pixms);
752  if (ppixsd) wplsd = pixGetWpl(pixsd);
753  wpld = pixGetWpl(pixd);
754  for (i = 0; i < h; i++) {
755  linem = datam + i * wplm;
756  linems = datams + i * wplms;
757  if (ppixsd) linesd = datasd + i * wplsd;
758  lined = datad + i * wpld;
759  for (j = 0; j < w; j++) {
760  mv = GET_DATA_BYTE(linem, j);
761  ms = linems[j];
762  var = ms - mv * mv;
763  if (usetab)
764  sd = tab[var];
765  else
766  sd = sqrtf((l_float32)var);
767  if (ppixsd) SET_DATA_BYTE(linesd, j, (l_int32)sd);
768  thresh = (l_int32)(mv * (1.0 - factor * (1.0 - sd / 128.)));
769  SET_DATA_BYTE(lined, j, thresh);
770  }
771  }
772 
773  if (usetab) LEPT_FREE(tab);
774  return pixd;
775 }
776 
777 
786 PIX *
788  PIX *pixth,
789  l_int32 redfactor)
790 {
791 l_int32 i, j, w, h, wpls, wplt, wpld, vals, valt;
792 l_uint32 *datas, *datat, *datad, *lines, *linet, *lined;
793 PIX *pixd;
794 
795  PROCNAME("pixApplyLocalThreshold");
796 
797  if (!pixs || pixGetDepth(pixs) != 8)
798  return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
799  if (pixGetColormap(pixs))
800  return (PIX *)ERROR_PTR("pixs is colormapped", procName, NULL);
801  if (!pixth || pixGetDepth(pixth) != 8)
802  return (PIX *)ERROR_PTR("pixth undefined or not 8 bpp", procName, NULL);
803 
804  pixGetDimensions(pixs, &w, &h, NULL);
805  pixd = pixCreate(w, h, 1);
806  datas = pixGetData(pixs);
807  datat = pixGetData(pixth);
808  datad = pixGetData(pixd);
809  wpls = pixGetWpl(pixs);
810  wplt = pixGetWpl(pixth);
811  wpld = pixGetWpl(pixd);
812  for (i = 0; i < h; i++) {
813  lines = datas + i * wpls;
814  linet = datat + i * wplt;
815  lined = datad + i * wpld;
816  for (j = 0; j < w; j++) {
817  vals = GET_DATA_BYTE(lines, j);
818  valt = GET_DATA_BYTE(linet, j);
819  if (vals < valt)
820  SET_DATA_BIT(lined, j);
821  }
822  }
823 
824  return pixd;
825 }
826 
827 
828 /*----------------------------------------------------------------------*
829  * Thresholding using connected components *
830  *----------------------------------------------------------------------*/
885 l_ok
887  PIX *pixm,
888  l_int32 start,
889  l_int32 end,
890  l_int32 incr,
891  l_float32 thresh48,
892  l_float32 threshdiff,
893  l_int32 *pglobthresh,
894  PIX **ppixd,
895  l_int32 debugflag)
896 {
897 l_int32 i, thresh, n, n4, n8, mincounts, found, globthresh;
898 l_float32 count4, count8, firstcount4, prevcount4, diff48, diff4;
899 GPLOT *gplot;
900 NUMA *na4, *na8;
901 PIX *pix1, *pix2, *pix3;
902 
903  PROCNAME("pixThresholdByConnComp");
904 
905  if (pglobthresh) *pglobthresh = 0;
906  if (ppixd) *ppixd = NULL;
907  if (!pixs || pixGetDepth(pixs) == 1)
908  return ERROR_INT("pixs undefined or 1 bpp", procName, 1);
909  if (pixm && pixGetDepth(pixm) != 1)
910  return ERROR_INT("pixm must be 1 bpp", procName, 1);
911 
912  /* Assign default values if requested */
913  if (start <= 0) start = 80;
914  if (end <= 0) end = 200;
915  if (incr <= 0) incr = 10;
916  if (thresh48 <= 0.0) thresh48 = 0.01;
917  if (threshdiff <= 0.0) threshdiff = 0.01;
918  if (start > end)
919  return ERROR_INT("invalid start,end", procName, 1);
920 
921  /* Make 8 bpp, using the max component if color. */
922  if (pixGetColormap(pixs))
924  else
925  pix1 = pixClone(pixs);
926  if (pixGetDepth(pix1) == 32)
927  pix2 = pixConvertRGBToGrayMinMax(pix1, L_CHOOSE_MAX);
928  else
929  pix2 = pixConvertTo8(pix1, 0);
930  pixDestroy(&pix1);
931 
932  /* Mask out any non-text regions. Do this in-place, because pix2
933  * can never be the same pix as pixs. */
934  if (pixm)
935  pixSetMasked(pix2, pixm, 255);
936 
937  /* Make sure there are enough components to get a valid signal */
938  pix3 = pixConvertTo1(pix2, start);
939  pixCountConnComp(pix3, 4, &n4);
940  pixDestroy(&pix3);
941  mincounts = 500;
942  if (n4 < mincounts) {
943  L_INFO("Insufficient component count: %d\n", procName, n4);
944  pixDestroy(&pix2);
945  return 1;
946  }
947 
948  /* Compute the c.c. data */
949  na4 = numaCreate(0);
950  na8 = numaCreate(0);
951  numaSetParameters(na4, start, incr);
952  numaSetParameters(na8, start, incr);
953  for (thresh = start, i = 0; thresh <= end; thresh += incr, i++) {
954  pix3 = pixConvertTo1(pix2, thresh);
955  pixCountConnComp(pix3, 4, &n4);
956  pixCountConnComp(pix3, 8, &n8);
957  numaAddNumber(na4, n4);
958  numaAddNumber(na8, n8);
959  pixDestroy(&pix3);
960  }
961  if (debugflag) {
962  gplot = gplotCreate("/tmp/threshroot", GPLOT_PNG,
963  "number of cc vs. threshold",
964  "threshold", "number of cc");
965  gplotAddPlot(gplot, NULL, na4, GPLOT_LINES, "plot 4cc");
966  gplotAddPlot(gplot, NULL, na8, GPLOT_LINES, "plot 8cc");
967  gplotMakeOutput(gplot);
968  gplotDestroy(&gplot);
969  }
970 
971  n = numaGetCount(na4);
972  found = FALSE;
973  for (i = 0; i < n; i++) {
974  if (i == 0) {
975  numaGetFValue(na4, i, &firstcount4);
976  prevcount4 = firstcount4;
977  } else {
978  numaGetFValue(na4, i, &count4);
979  numaGetFValue(na8, i, &count8);
980  diff48 = (count4 - count8) / firstcount4;
981  diff4 = L_ABS(prevcount4 - count4) / firstcount4;
982  if (debugflag) {
983  fprintf(stderr, "diff48 = %7.3f, diff4 = %7.3f\n",
984  diff48, diff4);
985  }
986  if (diff48 < thresh48 && diff4 < threshdiff) {
987  found = TRUE;
988  break;
989  }
990  prevcount4 = count4;
991  }
992  }
993  numaDestroy(&na4);
994  numaDestroy(&na8);
995 
996  if (found) {
997  globthresh = start + i * incr;
998  if (pglobthresh) *pglobthresh = globthresh;
999  if (ppixd) {
1000  *ppixd = pixConvertTo1(pix2, globthresh);
1001  pixCopyResolution(*ppixd, pixs);
1002  }
1003  if (debugflag) fprintf(stderr, "global threshold = %d\n", globthresh);
1004  pixDestroy(&pix2);
1005  return 0;
1006  }
1007 
1008  if (debugflag) fprintf(stderr, "no global threshold found\n");
1009  pixDestroy(&pix2);
1010  return 1;
1011 }
struct Pix * pixsc
Definition: bilateral.h:118
void gplotDestroy(GPLOT **pgplot)
gplotDestroy()
Definition: gplot.c:197
PIX * pixConvertTo1(PIX *pixs, l_int32 threshold)
pixConvertTo1()
Definition: pixconv.c:2933
l_ok numaGetFValue(NUMA *na, l_int32 index, l_float32 *pval)
numaGetFValue()
Definition: numabasic.c:692
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:322
l_ok pixSauvolaBinarize(PIX *pixs, l_int32 whsize, l_float32 factor, l_int32 addborder, PIX **ppixm, PIX **ppixsd, PIX **ppixth, PIX **ppixd)
pixSauvolaBinarize()
Definition: binarize.c:594
l_ok gplotAddPlot(GPLOT *gplot, NUMA *nax, NUMA *nay, l_int32 plotstyle, const char *plottitle)
gplotAddPlot()
Definition: gplot.c:263
PIX * pixOtsuThreshOnBackgroundNorm(PIX *pixs, PIX *pixim, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, l_int32 bgval, l_int32 smoothx, l_int32 smoothy, l_float32 scorefract, l_int32 *pthresh)
pixOtsuThreshOnBackgroundNorm()
Definition: binarize.c:256
l_ok pixSetMasked(PIX *pixd, PIX *pixm, l_uint32 val)
pixSetMasked()
Definition: pix3.c:155
PIX * pixWindowedMean(PIX *pixs, l_int32 wc, l_int32 hc, l_int32 hasborder, l_int32 normflag)
pixWindowedMean()
Definition: convolve.c:1065
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:473
l_ok gplotMakeOutput(GPLOT *gplot)
gplotMakeOutput()
Definition: gplot.c:379
GPLOT * gplotCreate(const char *rootname, l_int32 outformat, const char *title, const char *xlabel, const char *ylabel)
gplotCreate()
Definition: gplot.c:138
PIX * pixMaskedThreshOnBackgroundNorm(PIX *pixs, PIX *pixim, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, l_int32 smoothx, l_int32 smoothy, l_float32 scorefract, l_int32 *pthresh)
pixMaskedThreshOnBackgroundNorm()
Definition: binarize.c:354
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3041
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:302
struct Pix * pixs
Definition: bilateral.h:117
PIX * pixSauvolaGetThreshold(PIX *pixm, PIX *pixms, l_float32 factor, PIX **ppixsd)
pixSauvolaGetThreshold()
Definition: binarize.c:704
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1395
PIX * pixAddMirroredBorder(PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot)
pixAddMirroredBorder()
Definition: pix2.c:2026
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:187
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1624
l_ok pixTilingPaintTile(PIX *pixd, l_int32 i, l_int32 j, PIX *pixs, PIXTILING *pt)
pixTilingPaintTile()
Definition: pixtiling.c:390
PIX * pixThresholdToBinary(PIX *pixs, l_int32 thresh)
pixThresholdToBinary()
Definition: grayquant.c:443
Definition: pix.h:562
PIXTILING * pixTilingCreate(PIX *pixs, l_int32 nx, l_int32 ny, l_int32 w, l_int32 h, l_int32 xoverlap, l_int32 yoverlap)
pixTilingCreate()
Definition: pixtiling.c:121
void pixTilingDestroy(PIXTILING **ppt)
pixTilingDestroy()
Definition: pixtiling.c:178
l_ok pixCombineMasked(PIX *pixd, PIX *pixs, PIX *pixm)
pixCombineMasked()
Definition: pix3.c:374
PIX * pixRemoveBorder(PIX *pixs, l_int32 npix)
pixRemoveBorder()
Definition: pix2.c:1897
Definition: array.h:59
l_ok pixSplitDistributionFgBg(PIX *pixs, l_float32 scorefract, l_int32 factor, l_int32 *pthresh, l_int32 *pfgval, l_int32 *pbgval, PIX **ppixdb)
pixSplitDistributionFgBg()
Definition: pix4.c:3372
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:631
PIX * pixBackgroundNormFlex(PIX *pixs, l_int32 sx, l_int32 sy, l_int32 smoothx, l_int32 smoothy, l_int32 delta)
pixBackgroundNormFlex()
Definition: adaptmap.c:2496
l_ok pixSetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 val)
pixSetPixel()
Definition: pix2.c:253
PIX * pixMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequence()
Definition: morphseq.c:133
PIX * pixWindowedMeanSquare(PIX *pixs, l_int32 wc, l_int32 hc, l_int32 hasborder)
pixWindowedMeanSquare()
Definition: convolve.c:1182
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
PIX * pixConvertRGBToGrayMinMax(PIX *pixs, l_int32 type)
pixConvertRGBToGrayMinMax()
Definition: pixconv.c:887
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
l_ok numaSetParameters(NUMA *na, l_float32 startx, l_float32 delx)
numaSetParameters()
Definition: numabasic.c:966
Definition: gplot.h:75
PIX * pixTilingGetTile(PIXTILING *pt, l_int32 i, l_int32 j)
pixTilingGetTile()
Definition: pixtiling.c:255
PIX * pixCreateNoInit(l_int32 width, l_int32 height, l_int32 depth)
pixCreateNoInit()
Definition: pix1.c:331
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:515
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:543
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:360
l_ok pixThresholdByConnComp(PIX *pixs, PIX *pixm, l_int32 start, l_int32 end, l_int32 incr, l_float32 thresh48, l_float32 threshdiff, l_int32 *pglobthresh, PIX **ppixd, l_int32 debugflag)
pixThresholdByConnComp()
Definition: binarize.c:886
l_ok pixGetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 *pval)
pixGetPixel()
Definition: pix2.c:180
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1065
l_ok pixOtsuAdaptiveThreshold(PIX *pixs, l_int32 sx, l_int32 sy, l_int32 smoothx, l_int32 smoothy, l_float32 scorefract, PIX **ppixth, PIX **ppixd)
pixOtsuAdaptiveThreshold()
Definition: binarize.c:140
l_ok pixSauvolaBinarizeTiled(PIX *pixs, l_int32 whsize, l_float32 factor, l_int32 nx, l_int32 ny, PIX **ppixth, PIX **ppixd)
pixSauvolaBinarizeTiled()
Definition: binarize.c:467
PIX * pixBackgroundNorm(PIX *pixs, PIX *pixim, PIX *pixg, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, l_int32 bgval, l_int32 smoothx, l_int32 smoothy)
pixBackgroundNorm()
Definition: adaptmap.c:302
Definition: pix.h:134
PIX * pixBlockconv(PIX *pix, l_int32 wc, l_int32 hc)
pixBlockconv()
Definition: convolve.c:127
PIX * pixApplyLocalThreshold(PIX *pixs, PIX *pixth, l_int32 redfactor)
pixApplyLocalThreshold()
Definition: binarize.c:787
l_ok pixTilingNoStripOnPaint(PIXTILING *pt)
pixTilingNoStripOnPaint()
Definition: pixtiling.c:368
l_ok pixCountConnComp(PIX *pixs, l_int32 connectivity, l_int32 *pcount)
pixCountConnComp()
Definition: conncomp.c:390
#define SET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:127