Leptonica  1.82.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 
87 #ifdef HAVE_CONFIG_H
88 #include <config_auto.h>
89 #endif /* HAVE_CONFIG_H */
90 
91 #include <math.h>
92 #include "allheaders.h"
93 
94 static PIX *pixSauvolaGetThreshold(PIX *pixm, PIX *pixms, l_float32 factor,
95  PIX **ppixsd);
96 static PIX *pixApplyLocalThreshold(PIX *pixs, PIX *pixth);
97 
98 /*------------------------------------------------------------------*
99  * Adaptive Otsu-based thresholding *
100  *------------------------------------------------------------------*/
156 l_ok
158  l_int32 sx,
159  l_int32 sy,
160  l_int32 smoothx,
161  l_int32 smoothy,
162  l_float32 scorefract,
163  PIX **ppixth,
164  PIX **ppixd)
165 {
166 l_int32 w, h, nx, ny, i, j, thresh;
167 l_uint32 val;
168 PIX *pixt, *pixb, *pixthresh, *pixth, *pixd;
169 PIXTILING *pt;
170 
171  PROCNAME("pixOtsuAdaptiveThreshold");
172 
173  if (!ppixth && !ppixd)
174  return ERROR_INT("neither &pixth nor &pixd defined", procName, 1);
175  if (ppixth) *ppixth = NULL;
176  if (ppixd) *ppixd = NULL;
177  if (!pixs || pixGetDepth(pixs) != 8)
178  return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
179  if (sx < 16 || sy < 16)
180  return ERROR_INT("sx and sy must be >= 16", procName, 1);
181 
182  /* Compute the threshold array for the tiles */
183  pixGetDimensions(pixs, &w, &h, NULL);
184  nx = L_MAX(1, w / sx);
185  ny = L_MAX(1, h / sy);
186  smoothx = L_MIN(smoothx, (nx - 1) / 2);
187  smoothy = L_MIN(smoothy, (ny - 1) / 2);
188  pt = pixTilingCreate(pixs, nx, ny, 0, 0, 0, 0);
189  pixthresh = pixCreate(nx, ny, 8);
190  for (i = 0; i < ny; i++) {
191  for (j = 0; j < nx; j++) {
192  pixt = pixTilingGetTile(pt, i, j);
193  pixSplitDistributionFgBg(pixt, scorefract, 1, &thresh,
194  NULL, NULL, NULL);
195  pixSetPixel(pixthresh, j, i, thresh); /* see note (4) */
196  pixDestroy(&pixt);
197  }
198  }
199 
200  /* Optionally smooth the threshold array */
201  if (smoothx > 0 || smoothy > 0)
202  pixth = pixBlockconv(pixthresh, smoothx, smoothy);
203  else
204  pixth = pixClone(pixthresh);
205  pixDestroy(&pixthresh);
206 
207  /* Optionally apply the threshold array to binarize pixs */
208  if (ppixd) {
209  pixd = pixCreate(w, h, 1);
210  pixCopyResolution(pixd, pixs);
211  for (i = 0; i < ny; i++) {
212  for (j = 0; j < nx; j++) {
213  pixt = pixTilingGetTile(pt, i, j);
214  pixGetPixel(pixth, j, i, &val);
215  pixb = pixThresholdToBinary(pixt, val);
216  pixTilingPaintTile(pixd, i, j, pixb, pt);
217  pixDestroy(&pixt);
218  pixDestroy(&pixb);
219  }
220  }
221  *ppixd = pixd;
222  }
223 
224  if (ppixth)
225  *ppixth = pixth;
226  else
227  pixDestroy(&pixth);
228 
229  pixTilingDestroy(&pt);
230  return 0;
231 }
232 
233 
234 /*------------------------------------------------------------------*
235  * Otsu thresholding on adaptive background normalization *
236  *------------------------------------------------------------------*/
272 PIX *
274  PIX *pixim,
275  l_int32 sx,
276  l_int32 sy,
277  l_int32 thresh,
278  l_int32 mincount,
279  l_int32 bgval,
280  l_int32 smoothx,
281  l_int32 smoothy,
282  l_float32 scorefract,
283  l_int32 *pthresh)
284 {
285 l_int32 w, h;
286 l_uint32 val;
287 PIX *pixn, *pixt, *pixd;
288 
289  PROCNAME("pixOtsuThreshOnBackgroundNorm");
290 
291  if (pthresh) *pthresh = 0;
292  if (!pixs || pixGetDepth(pixs) != 8)
293  return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
294  if (pixGetColormap(pixs))
295  return (PIX *)ERROR_PTR("pixs is colormapped", procName, NULL);
296  if (sx < 4 || sy < 4)
297  return (PIX *)ERROR_PTR("sx and sy must be >= 4", procName, NULL);
298  if (mincount > sx * sy) {
299  L_WARNING("mincount too large for tile size\n", procName);
300  mincount = (sx * sy) / 3;
301  }
302 
303  pixn = pixBackgroundNorm(pixs, pixim, NULL, sx, sy, thresh,
304  mincount, bgval, smoothx, smoothy);
305  if (!pixn)
306  return (PIX *)ERROR_PTR("pixn not made", procName, NULL);
307 
308  /* Just use 1 tile for a global threshold, which is stored
309  * as a single pixel in pixt. */
310  pixGetDimensions(pixn, &w, &h, NULL);
311  pixOtsuAdaptiveThreshold(pixn, w, h, 0, 0, scorefract, &pixt, &pixd);
312  pixDestroy(&pixn);
313 
314  if (pixt && pthresh) {
315  pixGetPixel(pixt, 0, 0, &val);
316  *pthresh = val;
317  }
318  pixDestroy(&pixt);
319 
320  if (!pixd)
321  return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
322  else
323  return pixd;
324 }
325 
326 
327 
328 /*----------------------------------------------------------------------*
329  * Masking and Otsu estimate on adaptive background normalization *
330  *----------------------------------------------------------------------*/
370 PIX *
372  PIX *pixim,
373  l_int32 sx,
374  l_int32 sy,
375  l_int32 thresh,
376  l_int32 mincount,
377  l_int32 smoothx,
378  l_int32 smoothy,
379  l_float32 scorefract,
380  l_int32 *pthresh)
381 {
382 l_int32 w, h, highthresh;
383 l_uint32 val;
384 PIX *pixn, *pixm, *pixd, *pix1, *pix2, *pix3, *pix4;
385 
386  PROCNAME("pixMaskedThreshOnBackgroundNorm");
387 
388  if (pthresh) *pthresh = 0;
389  if (!pixs || pixGetDepth(pixs) != 8)
390  return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
391  if (pixGetColormap(pixs))
392  return (PIX *)ERROR_PTR("pixs is colormapped", procName, NULL);
393  if (sx < 4 || sy < 4)
394  return (PIX *)ERROR_PTR("sx and sy must be >= 4", procName, NULL);
395  if (mincount > sx * sy) {
396  L_WARNING("mincount too large for tile size\n", procName);
397  mincount = (sx * sy) / 3;
398  }
399 
400  /* Standard background normalization */
401  pixn = pixBackgroundNorm(pixs, pixim, NULL, sx, sy, thresh,
402  mincount, 255, smoothx, smoothy);
403  if (!pixn)
404  return (PIX *)ERROR_PTR("pixn not made", procName, NULL);
405 
406  /* Special background normalization for adaptation to quickly
407  * varying background. Threshold on the very light parts,
408  * which tend to be near significant edges, and dilate to
409  * form a mask over regions that are typically text. The
410  * dilation size is chosen to cover the text completely,
411  * except for very thick fonts. */
412  pix1 = pixBackgroundNormFlex(pixs, 7, 7, 1, 1, 20);
413  pix2 = pixThresholdToBinary(pix1, 240);
414  pixInvert(pix2, pix2);
415  pixm = pixMorphSequence(pix2, "d21.21", 0);
416  pixDestroy(&pix1);
417  pixDestroy(&pix2);
418 
419  /* Use Otsu to get a global threshold estimate for the image,
420  * which is stored as a single pixel in pix3. */
421  pixGetDimensions(pixs, &w, &h, NULL);
422  pixOtsuAdaptiveThreshold(pixs, w, h, 0, 0, scorefract, &pix3, NULL);
423  pixGetPixel(pix3, 0, 0, &val);
424  if (pthresh) *pthresh = val;
425  pixDestroy(&pix3);
426 
427  /* Threshold the background normalized images differentially,
428  * using a high value correlated with the background normalization
429  * for the part of the image under the mask (i.e., near the
430  * darker, thicker foreground), and a value that depends on the Otsu
431  * threshold for the rest of the image. This gives a solid
432  * (high) thresholding for the foreground parts of the image,
433  * while allowing the background and light foreground to be
434  * reasonably well cleaned using a threshold adapted to the
435  * input image. */
436  highthresh = L_MIN(256, val + 30);
437  pixd = pixThresholdToBinary(pixn, highthresh); /* for bg and light fg */
438  pix4 = pixThresholdToBinary(pixn, 190); /* for heavier fg */
439  pixCombineMasked(pixd, pix4, pixm);
440  pixDestroy(&pix4);
441  pixDestroy(&pixm);
442  pixDestroy(&pixn);
443 
444  if (!pixd)
445  return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
446  else
447  return pixd;
448 }
449 
450 
451 /*----------------------------------------------------------------------*
452  * Sauvola binarization *
453  *----------------------------------------------------------------------*/
483 l_ok
485  l_int32 whsize,
486  l_float32 factor,
487  l_int32 nx,
488  l_int32 ny,
489  PIX **ppixth,
490  PIX **ppixd)
491 {
492 l_int32 i, j, w, h, xrat, yrat;
493 PIX *pixth, *pixd, *tileth, *tiled, *pixt;
494 PIX **ptileth, **ptiled;
495 PIXTILING *pt;
496 
497  PROCNAME("pixSauvolaBinarizeTiled");
498 
499  if (!ppixth && !ppixd)
500  return ERROR_INT("no outputs", procName, 1);
501  if (ppixth) *ppixth = NULL;
502  if (ppixd) *ppixd = NULL;
503  if (!pixs || pixGetDepth(pixs) != 8)
504  return ERROR_INT("pixs undefined or not 8 bpp", procName, 1);
505  if (pixGetColormap(pixs))
506  return ERROR_INT("pixs is cmapped", procName, 1);
507  pixGetDimensions(pixs, &w, &h, NULL);
508  if (whsize < 2)
509  return ERROR_INT("whsize must be >= 2", procName, 1);
510  if (w < 2 * whsize + 3 || h < 2 * whsize + 3)
511  return ERROR_INT("whsize too large for image", procName, 1);
512  if (factor < 0.0)
513  return ERROR_INT("factor must be >= 0", procName, 1);
514 
515  if (nx <= 1 && ny <= 1)
516  return pixSauvolaBinarize(pixs, whsize, factor, 1, NULL, NULL,
517  ppixth, ppixd);
518 
519  /* Test to see if the tiles are too small. The required
520  * condition is that the tile dimensions must be at least
521  * (whsize + 2) x (whsize + 2). */
522  xrat = w / nx;
523  yrat = h / ny;
524  if (xrat < whsize + 2) {
525  nx = w / (whsize + 2);
526  L_WARNING("tile width too small; nx reduced to %d\n", procName, nx);
527  }
528  if (yrat < whsize + 2) {
529  ny = h / (whsize + 2);
530  L_WARNING("tile height too small; ny reduced to %d\n", procName, ny);
531  }
532  if (nx <= 1 && ny <= 1)
533  return pixSauvolaBinarize(pixs, whsize, factor, 1, NULL, NULL,
534  ppixth, ppixd);
535 
536  /* We can use pixtiling for painting both outputs, if requested */
537  if (ppixth) {
538  pixth = pixCreate(w, h, 8);
539  *ppixth = pixth;
540  }
541  if (ppixd) {
542  pixd = pixCreate(w, h, 1);
543  *ppixd = pixd;
544  }
545  pt = pixTilingCreate(pixs, nx, ny, 0, 0, whsize + 1, whsize + 1);
546  pixTilingNoStripOnPaint(pt); /* pixSauvolaBinarize() does the stripping */
547 
548  for (i = 0; i < ny; i++) {
549  for (j = 0; j < nx; j++) {
550  pixt = pixTilingGetTile(pt, i, j);
551  ptileth = (ppixth) ? &tileth : NULL;
552  ptiled = (ppixd) ? &tiled : NULL;
553  pixSauvolaBinarize(pixt, whsize, factor, 0, NULL, NULL,
554  ptileth, ptiled);
555  if (ppixth) { /* do not strip */
556  pixTilingPaintTile(pixth, i, j, tileth, pt);
557  pixDestroy(&tileth);
558  }
559  if (ppixd) {
560  pixTilingPaintTile(pixd, i, j, tiled, pt);
561  pixDestroy(&tiled);
562  }
563  pixDestroy(&pixt);
564  }
565  }
566 
567  pixTilingDestroy(&pt);
568  return 0;
569 }
570 
571 
610 l_ok
612  l_int32 whsize,
613  l_float32 factor,
614  l_int32 addborder,
615  PIX **ppixm,
616  PIX **ppixsd,
617  PIX **ppixth,
618  PIX **ppixd)
619 {
620 l_int32 w, h;
621 PIX *pixg, *pixsc, *pixm, *pixms, *pixth, *pixd;
622 
623  PROCNAME("pixSauvolaBinarize");
624 
625  if (ppixm) *ppixm = NULL;
626  if (ppixsd) *ppixsd = NULL;
627  if (ppixth) *ppixth = NULL;
628  if (ppixd) *ppixd = NULL;
629  if (!ppixm && !ppixsd && !ppixth && !ppixd)
630  return ERROR_INT("no outputs", procName, 1);
631  if (!pixs || pixGetDepth(pixs) != 8)
632  return ERROR_INT("pixs undefined or not 8 bpp", procName, 1);
633  if (pixGetColormap(pixs))
634  return ERROR_INT("pixs is cmapped", procName, 1);
635  pixGetDimensions(pixs, &w, &h, NULL);
636  if (whsize < 2)
637  return ERROR_INT("whsize must be >= 2", procName, 1);
638  if (w < 2 * whsize + 3 || h < 2 * whsize + 3)
639  return ERROR_INT("whsize too large for image", procName, 1);
640  if (factor < 0.0)
641  return ERROR_INT("factor must be >= 0", procName, 1);
642 
643  if (addborder) {
644  pixg = pixAddMirroredBorder(pixs, whsize + 1, whsize + 1,
645  whsize + 1, whsize + 1);
646  pixsc = pixClone(pixs);
647  } else {
648  pixg = pixClone(pixs);
649  pixsc = pixRemoveBorder(pixs, whsize + 1);
650  }
651  if (!pixg || !pixsc)
652  return ERROR_INT("pixg and pixsc not made", procName, 1);
653 
654  /* All these functions strip off the border pixels. */
655  if (ppixm || ppixth || ppixd)
656  pixm = pixWindowedMean(pixg, whsize, whsize, 1, 1);
657  if (ppixsd || ppixth || ppixd)
658  pixms = pixWindowedMeanSquare(pixg, whsize, whsize, 1);
659  if (ppixth || ppixd)
660  pixth = pixSauvolaGetThreshold(pixm, pixms, factor, ppixsd);
661  if (ppixd) {
662  pixd = pixApplyLocalThreshold(pixsc, pixth);
663  pixCopyResolution(pixd, pixs);
664  }
665 
666  if (ppixm)
667  *ppixm = pixm;
668  else
669  pixDestroy(&pixm);
670  pixDestroy(&pixms);
671  if (ppixth)
672  *ppixth = pixth;
673  else
674  pixDestroy(&pixth);
675  if (ppixd)
676  *ppixd = pixd;
677  pixDestroy(&pixg);
678  pixDestroy(&pixsc);
679  return 0;
680 }
681 
682 
720 static PIX *
722  PIX *pixms,
723  l_float32 factor,
724  PIX **ppixsd)
725 {
726 l_int32 i, j, w, h, tabsize, wplm, wplms, wplsd, wpld, usetab;
727 l_int32 mv, ms, var, thresh;
728 l_uint32 *datam, *datams, *datasd, *datad;
729 l_uint32 *linem, *linems, *linesd, *lined;
730 l_float32 sd;
731 l_float32 *tab; /* of 2^16 square roots */
732 PIX *pixsd, *pixd;
733 
734  PROCNAME("pixSauvolaGetThreshold");
735 
736  if (ppixsd) *ppixsd = NULL;
737  if (!pixm || pixGetDepth(pixm) != 8)
738  return (PIX *)ERROR_PTR("pixm undefined or not 8 bpp", procName, NULL);
739  if (pixGetColormap(pixm))
740  return (PIX *)ERROR_PTR("pixm is colormapped", procName, NULL);
741  if (!pixms || pixGetDepth(pixms) != 32)
742  return (PIX *)ERROR_PTR("pixms undefined or not 32 bpp",
743  procName, NULL);
744  if (factor < 0.0)
745  return (PIX *)ERROR_PTR("factor must be >= 0", procName, NULL);
746 
747  /* Only make a table of 2^16 square roots if there
748  * are enough pixels to justify it. */
749  pixGetDimensions(pixm, &w, &h, NULL);
750  usetab = (w * h > 100000) ? 1 : 0;
751  if (usetab) {
752  tabsize = 1 << 16;
753  tab = (l_float32 *)LEPT_CALLOC(tabsize, sizeof(l_float32));
754  for (i = 0; i < tabsize; i++)
755  tab[i] = sqrtf((l_float32)i);
756  }
757 
758  pixd = pixCreate(w, h, 8);
759  if (ppixsd) {
760  pixsd = pixCreate(w, h, 8);
761  *ppixsd = pixsd;
762  }
763  datam = pixGetData(pixm);
764  datams = pixGetData(pixms);
765  if (ppixsd) datasd = pixGetData(pixsd);
766  datad = pixGetData(pixd);
767  wplm = pixGetWpl(pixm);
768  wplms = pixGetWpl(pixms);
769  if (ppixsd) wplsd = pixGetWpl(pixsd);
770  wpld = pixGetWpl(pixd);
771  for (i = 0; i < h; i++) {
772  linem = datam + i * wplm;
773  linems = datams + i * wplms;
774  if (ppixsd) linesd = datasd + i * wplsd;
775  lined = datad + i * wpld;
776  for (j = 0; j < w; j++) {
777  mv = GET_DATA_BYTE(linem, j);
778  ms = linems[j];
779  var = ms - mv * mv;
780  if (usetab)
781  sd = tab[var];
782  else
783  sd = sqrtf((l_float32)var);
784  if (ppixsd) SET_DATA_BYTE(linesd, j, (l_int32)sd);
785  thresh = (l_int32)(mv * (1.0 - factor * (1.0 - sd / 128.)));
786  SET_DATA_BYTE(lined, j, thresh);
787  }
788  }
789 
790  if (usetab) LEPT_FREE(tab);
791  return pixd;
792 }
793 
794 
802 static PIX *
804  PIX *pixth)
805 {
806 l_int32 i, j, w, h, wpls, wplt, wpld, vals, valt;
807 l_uint32 *datas, *datat, *datad, *lines, *linet, *lined;
808 PIX *pixd;
809 
810  PROCNAME("pixApplyLocalThreshold");
811 
812  if (!pixs || pixGetDepth(pixs) != 8)
813  return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
814  if (pixGetColormap(pixs))
815  return (PIX *)ERROR_PTR("pixs is colormapped", procName, NULL);
816  if (!pixth || pixGetDepth(pixth) != 8)
817  return (PIX *)ERROR_PTR("pixth undefined or not 8 bpp", procName, NULL);
818 
819  pixGetDimensions(pixs, &w, &h, NULL);
820  pixd = pixCreate(w, h, 1);
821  datas = pixGetData(pixs);
822  datat = pixGetData(pixth);
823  datad = pixGetData(pixd);
824  wpls = pixGetWpl(pixs);
825  wplt = pixGetWpl(pixth);
826  wpld = pixGetWpl(pixd);
827  for (i = 0; i < h; i++) {
828  lines = datas + i * wpls;
829  linet = datat + i * wplt;
830  lined = datad + i * wpld;
831  for (j = 0; j < w; j++) {
832  vals = GET_DATA_BYTE(lines, j);
833  valt = GET_DATA_BYTE(linet, j);
834  if (vals < valt)
835  SET_DATA_BIT(lined, j);
836  }
837  }
838 
839  return pixd;
840 }
841 
842 
843 /*----------------------------------------------------------------------*
844  * Contrast normalization followed by Sauvola binarization *
845  *----------------------------------------------------------------------*/
863 PIX *
865  l_int32 mindiff,
866  PIX **ppixn,
867  PIX **ppixth)
868 {
869 l_int32 w, h, d, nx, ny;
870 PIX *pixg, *pix1, *pixd;
871 
872  PROCNAME("pixSauvolaOnContrastNorm");
873 
874  if (ppixn) *ppixn = NULL;
875  if (ppixth) *ppixth = NULL;
876  if (!pixs || (d = pixGetDepth(pixs)) < 8)
877  return (PIX *)ERROR_PTR("pixs undefined or d < 8 bpp", procName, NULL);
878  if (d == 32)
879  pixg = pixConvertRGBToGray(pixs, 0.3, 0.4, 0.3);
880  else
881  pixg = pixConvertTo8(pixs, 0);
882 
883  pix1 = pixContrastNorm(NULL, pixg, 50, 50, mindiff, 2, 2);
884 
885  /* Use tiles of size approximately 250 x 250 */
886  pixGetDimensions(pix1, &w, &h, NULL);
887  nx = L_MAX(1, (w + 125) / 250);
888  ny = L_MAX(1, (h + 125) / 250);
889  pixSauvolaBinarizeTiled(pix1, 25, 0.40, nx, ny, ppixth, &pixd);
890  pixDestroy(&pixg);
891  if (ppixn)
892  *ppixn = pix1;
893  else
894  pixDestroy(&pix1);
895  return pixd;
896 }
897 
898 
899 /*----------------------------------------------------------------------*
900  * Contrast normalization followed by background normalization *
901  * and thresholding *
902  *----------------------------------------------------------------------*/
919 PIX *
921  l_int32 mindiff)
922 {
923 l_int32 d, ival;
924 l_uint32 val;
925 PIX *pixg, *pix1, *pixd;
926 
927  PROCNAME("pixThreshOnDoubleNorm");
928 
929  if (!pixs || (d = pixGetDepth(pixs)) < 8)
930  return (PIX *)ERROR_PTR("pixs undefined or d < 8 bpp", procName, NULL);
931  if (d == 32)
932  pixg = pixConvertRGBToGray(pixs, 0.3, 0.4, 0.3);
933  else
934  pixg = pixConvertTo8(pixs, 0);
935 
936  /* Use the entire image for the estimate; pix1 is 1x1 */
937  pixOtsuAdaptiveThreshold(pixg, 5000, 5000, 0, 0, 0.1, &pix1, NULL);
938  pixGetPixel(pix1, 0, 0, &val);
939  ival = (l_int32)val;
940  ival = L_MIN(ival, 110);
941  pixDestroy(&pix1);
942 
943  /* Double normalization */
944  pixContrastNorm(pixg, pixg, 50, 50, mindiff, 2, 2);
945  pix1 = pixBackgroundNormSimple(pixg, NULL, NULL);
946  pixDestroy(&pixg);
947 
948 /* lept_stderr("ival = %d\n", ival); */
949  pixd = pixThresholdToBinary(pix1, ival);
950  pixDestroy(&pix1);
951  return pixd;
952 }
953 
954 
955 /*----------------------------------------------------------------------*
956  * Global thresholding using connected components *
957  *----------------------------------------------------------------------*/
1012 l_ok
1014  PIX *pixm,
1015  l_int32 start,
1016  l_int32 end,
1017  l_int32 incr,
1018  l_float32 thresh48,
1019  l_float32 threshdiff,
1020  l_int32 *pglobthresh,
1021  PIX **ppixd,
1022  l_int32 debugflag)
1023 {
1024 l_int32 i, thresh, n, n4, n8, mincounts, found, globthresh;
1025 l_float32 count4, count8, firstcount4, prevcount4, diff48, diff4;
1026 GPLOT *gplot;
1027 NUMA *na4, *na8;
1028 PIX *pix1, *pix2, *pix3;
1029 
1030  PROCNAME("pixThresholdByConnComp");
1031 
1032  if (pglobthresh) *pglobthresh = 0;
1033  if (ppixd) *ppixd = NULL;
1034  if (!pixs || pixGetDepth(pixs) == 1)
1035  return ERROR_INT("pixs undefined or 1 bpp", procName, 1);
1036  if (pixm && pixGetDepth(pixm) != 1)
1037  return ERROR_INT("pixm must be 1 bpp", procName, 1);
1038 
1039  /* Assign default values if requested */
1040  if (start <= 0) start = 80;
1041  if (end <= 0) end = 200;
1042  if (incr <= 0) incr = 10;
1043  if (thresh48 <= 0.0) thresh48 = 0.01;
1044  if (threshdiff <= 0.0) threshdiff = 0.01;
1045  if (start > end)
1046  return ERROR_INT("invalid start,end", procName, 1);
1047 
1048  /* Make 8 bpp, using the max component if color. */
1049  if (pixGetColormap(pixs))
1051  else
1052  pix1 = pixClone(pixs);
1053  if (pixGetDepth(pix1) == 32)
1054  pix2 = pixConvertRGBToGrayMinMax(pix1, L_CHOOSE_MAX);
1055  else
1056  pix2 = pixConvertTo8(pix1, 0);
1057  pixDestroy(&pix1);
1058 
1059  /* Mask out any non-text regions. Do this in-place, because pix2
1060  * can never be the same pix as pixs. */
1061  if (pixm)
1062  pixSetMasked(pix2, pixm, 255);
1063 
1064  /* Make sure there are enough components to get a valid signal */
1065  pix3 = pixConvertTo1(pix2, start);
1066  pixCountConnComp(pix3, 4, &n4);
1067  pixDestroy(&pix3);
1068  mincounts = 500;
1069  if (n4 < mincounts) {
1070  L_INFO("Insufficient component count: %d\n", procName, n4);
1071  pixDestroy(&pix2);
1072  return 1;
1073  }
1074 
1075  /* Compute the c.c. data */
1076  na4 = numaCreate(0);
1077  na8 = numaCreate(0);
1078  numaSetParameters(na4, start, incr);
1079  numaSetParameters(na8, start, incr);
1080  for (thresh = start, i = 0; thresh <= end; thresh += incr, i++) {
1081  pix3 = pixConvertTo1(pix2, thresh);
1082  pixCountConnComp(pix3, 4, &n4);
1083  pixCountConnComp(pix3, 8, &n8);
1084  numaAddNumber(na4, n4);
1085  numaAddNumber(na8, n8);
1086  pixDestroy(&pix3);
1087  }
1088  if (debugflag) {
1089  lept_mkdir("lept/binarize");
1090  gplot = gplotCreate("/tmp/lept/binarize", GPLOT_PNG,
1091  "number of cc vs. threshold",
1092  "threshold", "number of cc");
1093  gplotAddPlot(gplot, NULL, na4, GPLOT_LINES, "plot 4cc");
1094  gplotAddPlot(gplot, NULL, na8, GPLOT_LINES, "plot 8cc");
1095  gplotMakeOutput(gplot);
1096  gplotDestroy(&gplot);
1097  }
1098 
1099  n = numaGetCount(na4);
1100  found = FALSE;
1101  for (i = 0; i < n; i++) {
1102  if (i == 0) {
1103  numaGetFValue(na4, i, &firstcount4);
1104  prevcount4 = firstcount4;
1105  } else {
1106  numaGetFValue(na4, i, &count4);
1107  numaGetFValue(na8, i, &count8);
1108  diff48 = (count4 - count8) / firstcount4;
1109  diff4 = L_ABS(prevcount4 - count4) / firstcount4;
1110  if (debugflag) {
1111  lept_stderr("diff48 = %7.3f, diff4 = %7.3f\n",
1112  diff48, diff4);
1113  }
1114  if (diff48 < thresh48 && diff4 < threshdiff) {
1115  found = TRUE;
1116  break;
1117  }
1118  prevcount4 = count4;
1119  }
1120  }
1121  numaDestroy(&na4);
1122  numaDestroy(&na8);
1123 
1124  if (found) {
1125  globthresh = start + i * incr;
1126  if (pglobthresh) *pglobthresh = globthresh;
1127  if (ppixd) {
1128  *ppixd = pixConvertTo1(pix2, globthresh);
1129  pixCopyResolution(*ppixd, pixs);
1130  }
1131  if (debugflag) lept_stderr("global threshold = %d\n", globthresh);
1132  pixDestroy(&pix2);
1133  return 0;
1134  }
1135 
1136  if (debugflag) lept_stderr("no global threshold found\n");
1137  pixDestroy(&pix2);
1138  return 1;
1139 }
1140 
1141 /*----------------------------------------------------------------------*
1142  * Global thresholding by histogram *
1143  *----------------------------------------------------------------------*/
1168 l_ok
1170  l_int32 factor,
1171  l_int32 halfw,
1172  l_float32 delta,
1173  l_int32 *pthresh,
1174  PIX **ppixd,
1175  PIX **ppixhisto)
1176 {
1177 l_float32 maxval, fract;
1178 NUMA *na1, *na2, *na3;
1179 
1180  PROCNAME("pixThresholdByHisto");
1181 
1182  if (ppixhisto) *ppixhisto = NULL;
1183  if (ppixd) *ppixd = NULL;
1184  if (!pthresh)
1185  return ERROR_INT("&thresh not defined", procName, 1);
1186  *pthresh = 0;
1187  if (!pixs || pixGetDepth(pixs) != 8)
1188  return ERROR_INT("pixs undefined or not 8 bpp", procName, 1);
1189  if (pixGetColormap(pixs))
1190  return ERROR_INT("pixs has colormap", procName, 1);
1191  if (factor < 1)
1192  return ERROR_INT("sampling must be >= 1", procName, 1);
1193  if (halfw <= 0) halfw = 20;
1194  if (delta <= 0.0) delta = 0.1;
1195 
1196  /* Make a histogram of pixel values where the largest peak
1197  * is normalized to a value of 1.0. */
1198  na1 = pixGetGrayHistogram(pixs, factor);
1199  na2 = numaWindowedMean(na1, halfw); /* smoothing */
1200  numaGetMax(na2, &maxval, NULL);
1201  na3 = numaTransform(na2, 0.0, 1.0 / maxval); /* rescale to max of 1.0 */
1202  numaDestroy(&na1);
1203  numaDestroy(&na2);
1204 
1205  numaFindLocForThreshold(na3, 0, pthresh, &fract);
1206  L_INFO("fractional area under first peak: %5.3f\n", procName, fract);
1207 
1208  if (ppixhisto) {
1209  lept_mkdir("lept/histo");
1210  gplotSimple1(na3, GPLOT_PNG, "/tmp/lept/histo/histo", NULL);
1211  *ppixhisto = pixRead("/tmp/lept/histo/histo.png");
1212  }
1213  numaDestroy(&na3);
1214 
1215  if (*pthresh > 0 && ppixd)
1216  *ppixd = pixThresholdToBinary(pixs, *pthresh);
1217  return 0;
1218 }
1219 
struct Pix * pixsc
Definition: bilateral.h:118
void gplotDestroy(GPLOT **pgplot)
gplotDestroy()
Definition: gplot.c:255
NUMA * pixGetGrayHistogram(PIX *pixs, l_int32 factor)
pixGetGrayHistogram()
Definition: pix4.c:115
PIX * pixSauvolaOnContrastNorm(PIX *pixs, l_int32 mindiff, PIX **ppixn, PIX **ppixth)
pixSauvolaOnContrastNorm()
Definition: binarize.c:864
PIX * pixConvertTo1(PIX *pixs, l_int32 threshold)
pixConvertTo1()
Definition: pixconv.c:3026
l_ok numaGetFValue(NUMA *na, l_int32 index, l_float32 *pval)
numaGetFValue()
Definition: numabasic.c:719
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:328
l_int32 lept_mkdir(const char *subdir)
lept_mkdir()
Definition: utils2.c:2218
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:611
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:273
l_ok pixSetMasked(PIX *pixd, PIX *pixm, l_uint32 val)
pixSetMasked()
Definition: pix3.c:163
PIX * pixWindowedMean(PIX *pixs, l_int32 wc, l_int32 hc, l_int32 hasborder, l_int32 normflag)
pixWindowedMean()
Definition: convolve.c:1073
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:478
l_ok gplotMakeOutput(GPLOT *gplot)
gplotMakeOutput()
Definition: gplot.c:466
static PIX * pixApplyLocalThreshold(PIX *pixs, PIX *pixth)
pixApplyLocalThreshold()
Definition: binarize.c:803
GPLOT * gplotCreate(const char *rootname, l_int32 outformat, const char *title, const char *xlabel, const char *ylabel)
gplotCreate()
Definition: gplot.c:187
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:371
l_ok numaFindLocForThreshold(NUMA *na, l_int32 skip, l_int32 *pthresh, l_float32 *pfract)
numaFindLocForThreshold()
Definition: numafunc2.c:2657
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3133
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
struct Pix * pixs
Definition: bilateral.h:117
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1509
PIX * pixAddMirroredBorder(PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot)
pixAddMirroredBorder()
Definition: pix2.c:2101
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:194
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1763
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:447
l_ok pixThresholdByHisto(PIX *pixs, l_int32 factor, l_int32 halfw, l_float32 delta, l_int32 *pthresh, PIX **ppixd, PIX **ppixhisto)
pixThresholdByHisto()
Definition: binarize.c:1169
Definition: pix.h:558
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:123
void pixTilingDestroy(PIXTILING **ppt)
pixTilingDestroy()
Definition: pixtiling.c:179
l_ok pixCombineMasked(PIX *pixd, PIX *pixs, PIX *pixm)
pixCombineMasked()
Definition: pix3.c:382
PIX * pixRemoveBorder(PIX *pixs, l_int32 npix)
pixRemoveBorder()
Definition: pix2.c:1972
Definition: array.h:70
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:3504
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:658
PIX * pixBackgroundNormFlex(PIX *pixs, l_int32 sx, l_int32 sy, l_int32 smoothx, l_int32 smoothy, l_int32 delta)
pixBackgroundNormFlex()
Definition: adaptmap.c:2511
l_ok pixSetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 val)
pixSetPixel()
Definition: pix2.c:263
l_int32 maxval
Definition: bilateral.h:125
PIX * pixMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequence()
Definition: morphseq.c:137
PIX * pixContrastNorm(PIX *pixd, PIX *pixs, l_int32 sx, l_int32 sy, l_int32 mindiff, l_int32 smoothx, l_int32 smoothy)
pixContrastNorm()
Definition: adaptmap.c:2606
PIX * pixWindowedMeanSquare(PIX *pixs, l_int32 wc, l_int32 hc, l_int32 hasborder)
pixWindowedMeanSquare()
Definition: convolve.c:1190
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
PIX * pixConvertRGBToGrayMinMax(PIX *pixs, l_int32 type)
pixConvertRGBToGrayMinMax()
Definition: pixconv.c:963
#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:993
Definition: gplot.h:76
l_ok gplotAddPlot(GPLOT *gplot, NUMA *nax, NUMA *nay, l_int32 plotstyle, const char *plotlabel)
gplotAddPlot()
Definition: gplot.c:320
PIX * pixTilingGetTile(PIXTILING *pt, l_int32 i, l_int32 j)
pixTilingGetTile()
Definition: pixtiling.c:255
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:593
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:621
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:366
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:1013
l_ok pixGetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 *pval)
pixGetPixel()
Definition: pix2.c:190
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
NUMA * numaTransform(NUMA *nas, l_float32 shift, l_float32 scale)
numaTransform()
Definition: numafunc2.c:415
PIX * pixRead(const char *filename)
pixRead()
Definition: readfile.c:193
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:157
NUMA * numaWindowedMean(NUMA *nas, l_int32 wc)
numaWindowedMean()
Definition: numafunc2.c:588
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:484
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:322
PIX * pixBackgroundNormSimple(PIX *pixs, PIX *pixim, PIX *pixg)
pixBackgroundNormSimple()
Definition: adaptmap.c:247
Definition: pix.h:138
PIX * pixBlockconv(PIX *pix, l_int32 wc, l_int32 hc)
pixBlockconv()
Definition: convolve.c:132
static PIX * pixSauvolaGetThreshold(PIX *pixm, PIX *pixms, l_float32 factor, PIX **ppixsd)
pixSauvolaGetThreshold()
Definition: binarize.c:721
PIX * pixThreshOnDoubleNorm(PIX *pixs, l_int32 mindiff)
pixTheshOnDoubleNorm()
Definition: binarize.c:920
l_ok numaGetMax(NUMA *na, l_float32 *pmaxval, l_int32 *pimaxloc)
numaGetMax()
Definition: numafunc1.c:496
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:394
#define SET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:127
l_ok gplotSimple1(NUMA *na, l_int32 outformat, const char *outroot, const char *title)
gplotSimple1()
Definition: gplot.c:665