Leptonica  1.83.1
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  if (!ppixth && !ppixd)
172  return ERROR_INT("neither &pixth nor &pixd defined", __func__, 1);
173  if (ppixth) *ppixth = NULL;
174  if (ppixd) *ppixd = NULL;
175  if (!pixs || pixGetDepth(pixs) != 8)
176  return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
177  if (sx < 16 || sy < 16)
178  return ERROR_INT("sx and sy must be >= 16", __func__, 1);
179 
180  /* Compute the threshold array for the tiles */
181  pixGetDimensions(pixs, &w, &h, NULL);
182  nx = L_MAX(1, w / sx);
183  ny = L_MAX(1, h / sy);
184  smoothx = L_MIN(smoothx, (nx - 1) / 2);
185  smoothy = L_MIN(smoothy, (ny - 1) / 2);
186  pt = pixTilingCreate(pixs, nx, ny, 0, 0, 0, 0);
187  pixthresh = pixCreate(nx, ny, 8);
188  for (i = 0; i < ny; i++) {
189  for (j = 0; j < nx; j++) {
190  pixt = pixTilingGetTile(pt, i, j);
191  pixSplitDistributionFgBg(pixt, scorefract, 1, &thresh,
192  NULL, NULL, NULL);
193  pixSetPixel(pixthresh, j, i, thresh); /* see note (4) */
194  pixDestroy(&pixt);
195  }
196  }
197 
198  /* Optionally smooth the threshold array */
199  if (smoothx > 0 || smoothy > 0)
200  pixth = pixBlockconv(pixthresh, smoothx, smoothy);
201  else
202  pixth = pixClone(pixthresh);
203  pixDestroy(&pixthresh);
204 
205  /* Optionally apply the threshold array to binarize pixs */
206  if (ppixd) {
207  pixd = pixCreate(w, h, 1);
208  pixCopyResolution(pixd, pixs);
209  for (i = 0; i < ny; i++) {
210  for (j = 0; j < nx; j++) {
211  pixt = pixTilingGetTile(pt, i, j);
212  pixGetPixel(pixth, j, i, &val);
213  pixb = pixThresholdToBinary(pixt, val);
214  pixTilingPaintTile(pixd, i, j, pixb, pt);
215  pixDestroy(&pixt);
216  pixDestroy(&pixb);
217  }
218  }
219  *ppixd = pixd;
220  }
221 
222  if (ppixth)
223  *ppixth = pixth;
224  else
225  pixDestroy(&pixth);
226 
227  pixTilingDestroy(&pt);
228  return 0;
229 }
230 
231 
232 /*------------------------------------------------------------------*
233  * Otsu thresholding on adaptive background normalization *
234  *------------------------------------------------------------------*/
270 PIX *
272  PIX *pixim,
273  l_int32 sx,
274  l_int32 sy,
275  l_int32 thresh,
276  l_int32 mincount,
277  l_int32 bgval,
278  l_int32 smoothx,
279  l_int32 smoothy,
280  l_float32 scorefract,
281  l_int32 *pthresh)
282 {
283 l_int32 w, h;
284 l_uint32 val;
285 PIX *pixn, *pixt, *pixd;
286 
287  if (pthresh) *pthresh = 0;
288  if (!pixs || pixGetDepth(pixs) != 8)
289  return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, NULL);
290  if (pixGetColormap(pixs))
291  return (PIX *)ERROR_PTR("pixs is colormapped", __func__, NULL);
292  if (sx < 4 || sy < 4)
293  return (PIX *)ERROR_PTR("sx and sy must be >= 4", __func__, NULL);
294  if (mincount > sx * sy) {
295  L_WARNING("mincount too large for tile size\n", __func__);
296  mincount = (sx * sy) / 3;
297  }
298 
299  pixn = pixBackgroundNorm(pixs, pixim, NULL, sx, sy, thresh,
300  mincount, bgval, smoothx, smoothy);
301  if (!pixn)
302  return (PIX *)ERROR_PTR("pixn not made", __func__, NULL);
303 
304  /* Just use 1 tile for a global threshold, which is stored
305  * as a single pixel in pixt. */
306  pixGetDimensions(pixn, &w, &h, NULL);
307  pixOtsuAdaptiveThreshold(pixn, w, h, 0, 0, scorefract, &pixt, &pixd);
308  pixDestroy(&pixn);
309 
310  if (pixt && pthresh) {
311  pixGetPixel(pixt, 0, 0, &val);
312  *pthresh = val;
313  }
314  pixDestroy(&pixt);
315 
316  if (!pixd)
317  return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
318  else
319  return pixd;
320 }
321 
322 
323 
324 /*----------------------------------------------------------------------*
325  * Masking and Otsu estimate on adaptive background normalization *
326  *----------------------------------------------------------------------*/
366 PIX *
368  PIX *pixim,
369  l_int32 sx,
370  l_int32 sy,
371  l_int32 thresh,
372  l_int32 mincount,
373  l_int32 smoothx,
374  l_int32 smoothy,
375  l_float32 scorefract,
376  l_int32 *pthresh)
377 {
378 l_int32 w, h, highthresh;
379 l_uint32 val;
380 PIX *pixn, *pixm, *pixd, *pix1, *pix2, *pix3, *pix4;
381 
382  if (pthresh) *pthresh = 0;
383  if (!pixs || pixGetDepth(pixs) != 8)
384  return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, NULL);
385  if (pixGetColormap(pixs))
386  return (PIX *)ERROR_PTR("pixs is colormapped", __func__, NULL);
387  if (sx < 4 || sy < 4)
388  return (PIX *)ERROR_PTR("sx and sy must be >= 4", __func__, NULL);
389  if (mincount > sx * sy) {
390  L_WARNING("mincount too large for tile size\n", __func__);
391  mincount = (sx * sy) / 3;
392  }
393 
394  /* Standard background normalization */
395  pixn = pixBackgroundNorm(pixs, pixim, NULL, sx, sy, thresh,
396  mincount, 255, smoothx, smoothy);
397  if (!pixn)
398  return (PIX *)ERROR_PTR("pixn not made", __func__, NULL);
399 
400  /* Special background normalization for adaptation to quickly
401  * varying background. Threshold on the very light parts,
402  * which tend to be near significant edges, and dilate to
403  * form a mask over regions that are typically text. The
404  * dilation size is chosen to cover the text completely,
405  * except for very thick fonts. */
406  pix1 = pixBackgroundNormFlex(pixs, 7, 7, 1, 1, 20);
407  pix2 = pixThresholdToBinary(pix1, 240);
408  pixInvert(pix2, pix2);
409  pixm = pixMorphSequence(pix2, "d21.21", 0);
410  pixDestroy(&pix1);
411  pixDestroy(&pix2);
412 
413  /* Use Otsu to get a global threshold estimate for the image,
414  * which is stored as a single pixel in pix3. */
415  pixGetDimensions(pixs, &w, &h, NULL);
416  pixOtsuAdaptiveThreshold(pixs, w, h, 0, 0, scorefract, &pix3, NULL);
417  pixGetPixel(pix3, 0, 0, &val);
418  if (pthresh) *pthresh = val;
419  pixDestroy(&pix3);
420 
421  /* Threshold the background normalized images differentially,
422  * using a high value correlated with the background normalization
423  * for the part of the image under the mask (i.e., near the
424  * darker, thicker foreground), and a value that depends on the Otsu
425  * threshold for the rest of the image. This gives a solid
426  * (high) thresholding for the foreground parts of the image,
427  * while allowing the background and light foreground to be
428  * reasonably well cleaned using a threshold adapted to the
429  * input image. */
430  highthresh = L_MIN(256, val + 30);
431  pixd = pixThresholdToBinary(pixn, highthresh); /* for bg and light fg */
432  pix4 = pixThresholdToBinary(pixn, 190); /* for heavier fg */
433  pixCombineMasked(pixd, pix4, pixm);
434  pixDestroy(&pix4);
435  pixDestroy(&pixm);
436  pixDestroy(&pixn);
437 
438  if (!pixd)
439  return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
440  else
441  return pixd;
442 }
443 
444 
445 /*----------------------------------------------------------------------*
446  * Sauvola binarization *
447  *----------------------------------------------------------------------*/
477 l_ok
479  l_int32 whsize,
480  l_float32 factor,
481  l_int32 nx,
482  l_int32 ny,
483  PIX **ppixth,
484  PIX **ppixd)
485 {
486 l_int32 i, j, w, h, xrat, yrat;
487 PIX *pixth, *pixd, *tileth, *tiled, *pixt;
488 PIX **ptileth, **ptiled;
489 PIXTILING *pt;
490 
491  if (!ppixth && !ppixd)
492  return ERROR_INT("no outputs", __func__, 1);
493  if (ppixth) *ppixth = NULL;
494  if (ppixd) *ppixd = NULL;
495  if (!pixs || pixGetDepth(pixs) != 8)
496  return ERROR_INT("pixs undefined or not 8 bpp", __func__, 1);
497  if (pixGetColormap(pixs))
498  return ERROR_INT("pixs is cmapped", __func__, 1);
499  pixGetDimensions(pixs, &w, &h, NULL);
500  if (whsize < 2)
501  return ERROR_INT("whsize must be >= 2", __func__, 1);
502  if (w < 2 * whsize + 3 || h < 2 * whsize + 3)
503  return ERROR_INT("whsize too large for image", __func__, 1);
504  if (factor < 0.0)
505  return ERROR_INT("factor must be >= 0", __func__, 1);
506 
507  if (nx <= 1 && ny <= 1)
508  return pixSauvolaBinarize(pixs, whsize, factor, 1, NULL, NULL,
509  ppixth, ppixd);
510 
511  /* Test to see if the tiles are too small. The required
512  * condition is that the tile dimensions must be at least
513  * (whsize + 2) x (whsize + 2). */
514  xrat = w / nx;
515  yrat = h / ny;
516  if (xrat < whsize + 2) {
517  nx = w / (whsize + 2);
518  L_WARNING("tile width too small; nx reduced to %d\n", __func__, nx);
519  }
520  if (yrat < whsize + 2) {
521  ny = h / (whsize + 2);
522  L_WARNING("tile height too small; ny reduced to %d\n", __func__, ny);
523  }
524  if (nx <= 1 && ny <= 1)
525  return pixSauvolaBinarize(pixs, whsize, factor, 1, NULL, NULL,
526  ppixth, ppixd);
527 
528  /* We can use pixtiling for painting both outputs, if requested */
529  if (ppixth) {
530  pixth = pixCreate(w, h, 8);
531  *ppixth = pixth;
532  }
533  if (ppixd) {
534  pixd = pixCreate(w, h, 1);
535  *ppixd = pixd;
536  }
537  pt = pixTilingCreate(pixs, nx, ny, 0, 0, whsize + 1, whsize + 1);
538  pixTilingNoStripOnPaint(pt); /* pixSauvolaBinarize() does the stripping */
539 
540  for (i = 0; i < ny; i++) {
541  for (j = 0; j < nx; j++) {
542  pixt = pixTilingGetTile(pt, i, j);
543  ptileth = (ppixth) ? &tileth : NULL;
544  ptiled = (ppixd) ? &tiled : NULL;
545  pixSauvolaBinarize(pixt, whsize, factor, 0, NULL, NULL,
546  ptileth, ptiled);
547  if (ppixth) { /* do not strip */
548  pixTilingPaintTile(pixth, i, j, tileth, pt);
549  pixDestroy(&tileth);
550  }
551  if (ppixd) {
552  pixTilingPaintTile(pixd, i, j, tiled, pt);
553  pixDestroy(&tiled);
554  }
555  pixDestroy(&pixt);
556  }
557  }
558 
559  pixTilingDestroy(&pt);
560  return 0;
561 }
562 
563 
602 l_ok
604  l_int32 whsize,
605  l_float32 factor,
606  l_int32 addborder,
607  PIX **ppixm,
608  PIX **ppixsd,
609  PIX **ppixth,
610  PIX **ppixd)
611 {
612 l_int32 w, h;
613 PIX *pixg, *pixsc, *pixm, *pixms, *pixth, *pixd;
614 
615  if (ppixm) *ppixm = NULL;
616  if (ppixsd) *ppixsd = NULL;
617  if (ppixth) *ppixth = NULL;
618  if (ppixd) *ppixd = NULL;
619  if (!ppixm && !ppixsd && !ppixth && !ppixd)
620  return ERROR_INT("no outputs", __func__, 1);
621  if (!pixs || pixGetDepth(pixs) != 8)
622  return ERROR_INT("pixs undefined or not 8 bpp", __func__, 1);
623  if (pixGetColormap(pixs))
624  return ERROR_INT("pixs is cmapped", __func__, 1);
625  pixGetDimensions(pixs, &w, &h, NULL);
626  if (whsize < 2)
627  return ERROR_INT("whsize must be >= 2", __func__, 1);
628  if (w < 2 * whsize + 3 || h < 2 * whsize + 3)
629  return ERROR_INT("whsize too large for image", __func__, 1);
630  if (factor < 0.0)
631  return ERROR_INT("factor must be >= 0", __func__, 1);
632 
633  if (addborder) {
634  pixg = pixAddMirroredBorder(pixs, whsize + 1, whsize + 1,
635  whsize + 1, whsize + 1);
636  pixsc = pixClone(pixs);
637  } else {
638  pixg = pixClone(pixs);
639  pixsc = pixRemoveBorder(pixs, whsize + 1);
640  }
641  if (!pixg || !pixsc)
642  return ERROR_INT("pixg and pixsc not made", __func__, 1);
643 
644  /* All these functions strip off the border pixels. */
645  if (ppixm || ppixth || ppixd)
646  pixm = pixWindowedMean(pixg, whsize, whsize, 1, 1);
647  if (ppixsd || ppixth || ppixd)
648  pixms = pixWindowedMeanSquare(pixg, whsize, whsize, 1);
649  if (ppixth || ppixd)
650  pixth = pixSauvolaGetThreshold(pixm, pixms, factor, ppixsd);
651  if (ppixd) {
652  pixd = pixApplyLocalThreshold(pixsc, pixth);
653  pixCopyResolution(pixd, pixs);
654  }
655 
656  if (ppixm)
657  *ppixm = pixm;
658  else
659  pixDestroy(&pixm);
660  pixDestroy(&pixms);
661  if (ppixth)
662  *ppixth = pixth;
663  else
664  pixDestroy(&pixth);
665  if (ppixd)
666  *ppixd = pixd;
667  pixDestroy(&pixg);
668  pixDestroy(&pixsc);
669  return 0;
670 }
671 
672 
710 static PIX *
712  PIX *pixms,
713  l_float32 factor,
714  PIX **ppixsd)
715 {
716 l_int32 i, j, w, h, tabsize, wplm, wplms, wplsd, wpld, usetab;
717 l_int32 mv, ms, var, thresh;
718 l_uint32 *datam, *datams, *datasd, *datad;
719 l_uint32 *linem, *linems, *linesd, *lined;
720 l_float32 sd;
721 l_float32 *tab; /* of 2^16 square roots */
722 PIX *pixsd, *pixd;
723 
724  if (ppixsd) *ppixsd = NULL;
725  if (!pixm || pixGetDepth(pixm) != 8)
726  return (PIX *)ERROR_PTR("pixm undefined or not 8 bpp", __func__, NULL);
727  if (pixGetColormap(pixm))
728  return (PIX *)ERROR_PTR("pixm is colormapped", __func__, NULL);
729  if (!pixms || pixGetDepth(pixms) != 32)
730  return (PIX *)ERROR_PTR("pixms undefined or not 32 bpp",
731  __func__, NULL);
732  if (factor < 0.0)
733  return (PIX *)ERROR_PTR("factor must be >= 0", __func__, NULL);
734 
735  /* Only make a table of 2^16 square roots if there
736  * are enough pixels to justify it. */
737  pixGetDimensions(pixm, &w, &h, NULL);
738  usetab = (w * h > 100000) ? 1 : 0;
739  if (usetab) {
740  tabsize = 1 << 16;
741  tab = (l_float32 *)LEPT_CALLOC(tabsize, sizeof(l_float32));
742  for (i = 0; i < tabsize; i++)
743  tab[i] = sqrtf((l_float32)i);
744  }
745 
746  pixd = pixCreate(w, h, 8);
747  if (ppixsd) {
748  pixsd = pixCreate(w, h, 8);
749  *ppixsd = pixsd;
750  }
751  datam = pixGetData(pixm);
752  datams = pixGetData(pixms);
753  if (ppixsd) datasd = pixGetData(pixsd);
754  datad = pixGetData(pixd);
755  wplm = pixGetWpl(pixm);
756  wplms = pixGetWpl(pixms);
757  if (ppixsd) wplsd = pixGetWpl(pixsd);
758  wpld = pixGetWpl(pixd);
759  for (i = 0; i < h; i++) {
760  linem = datam + i * wplm;
761  linems = datams + i * wplms;
762  if (ppixsd) linesd = datasd + i * wplsd;
763  lined = datad + i * wpld;
764  for (j = 0; j < w; j++) {
765  mv = GET_DATA_BYTE(linem, j);
766  ms = linems[j];
767  var = ms - mv * mv;
768  if (usetab)
769  sd = tab[var];
770  else
771  sd = sqrtf((l_float32)var);
772  if (ppixsd) SET_DATA_BYTE(linesd, j, (l_int32)sd);
773  thresh = (l_int32)(mv * (1.0 - factor * (1.0 - sd / 128.)));
774  SET_DATA_BYTE(lined, j, thresh);
775  }
776  }
777 
778  if (usetab) LEPT_FREE(tab);
779  return pixd;
780 }
781 
782 
790 static PIX *
792  PIX *pixth)
793 {
794 l_int32 i, j, w, h, wpls, wplt, wpld, vals, valt;
795 l_uint32 *datas, *datat, *datad, *lines, *linet, *lined;
796 PIX *pixd;
797 
798  if (!pixs || pixGetDepth(pixs) != 8)
799  return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, NULL);
800  if (pixGetColormap(pixs))
801  return (PIX *)ERROR_PTR("pixs is colormapped", __func__, NULL);
802  if (!pixth || pixGetDepth(pixth) != 8)
803  return (PIX *)ERROR_PTR("pixth undefined or not 8 bpp", __func__, NULL);
804 
805  pixGetDimensions(pixs, &w, &h, NULL);
806  pixd = pixCreate(w, h, 1);
807  datas = pixGetData(pixs);
808  datat = pixGetData(pixth);
809  datad = pixGetData(pixd);
810  wpls = pixGetWpl(pixs);
811  wplt = pixGetWpl(pixth);
812  wpld = pixGetWpl(pixd);
813  for (i = 0; i < h; i++) {
814  lines = datas + i * wpls;
815  linet = datat + i * wplt;
816  lined = datad + i * wpld;
817  for (j = 0; j < w; j++) {
818  vals = GET_DATA_BYTE(lines, j);
819  valt = GET_DATA_BYTE(linet, j);
820  if (vals < valt)
821  SET_DATA_BIT(lined, j);
822  }
823  }
824 
825  return pixd;
826 }
827 
828 
829 /*----------------------------------------------------------------------*
830  * Contrast normalization followed by Sauvola binarization *
831  *----------------------------------------------------------------------*/
849 PIX *
851  l_int32 mindiff,
852  PIX **ppixn,
853  PIX **ppixth)
854 {
855 l_int32 w, h, d, nx, ny;
856 PIX *pixg, *pix1, *pixd;
857 
858  if (ppixn) *ppixn = NULL;
859  if (ppixth) *ppixth = NULL;
860  if (!pixs || (d = pixGetDepth(pixs)) < 8)
861  return (PIX *)ERROR_PTR("pixs undefined or d < 8 bpp", __func__, NULL);
862  if (d == 32)
863  pixg = pixConvertRGBToGray(pixs, 0.3, 0.4, 0.3);
864  else
865  pixg = pixConvertTo8(pixs, 0);
866 
867  pix1 = pixContrastNorm(NULL, pixg, 50, 50, mindiff, 2, 2);
868 
869  /* Use tiles of size approximately 250 x 250 */
870  pixGetDimensions(pix1, &w, &h, NULL);
871  nx = L_MAX(1, (w + 125) / 250);
872  ny = L_MAX(1, (h + 125) / 250);
873  pixSauvolaBinarizeTiled(pix1, 25, 0.40, nx, ny, ppixth, &pixd);
874  pixDestroy(&pixg);
875  if (ppixn)
876  *ppixn = pix1;
877  else
878  pixDestroy(&pix1);
879  return pixd;
880 }
881 
882 
883 /*----------------------------------------------------------------------*
884  * Contrast normalization followed by background normalization *
885  * and thresholding *
886  *----------------------------------------------------------------------*/
903 PIX *
905  l_int32 mindiff)
906 {
907 l_int32 d, ival;
908 l_uint32 val;
909 PIX *pixg, *pix1, *pixd;
910 
911  if (!pixs || (d = pixGetDepth(pixs)) < 8)
912  return (PIX *)ERROR_PTR("pixs undefined or d < 8 bpp", __func__, NULL);
913  if (d == 32)
914  pixg = pixConvertRGBToGray(pixs, 0.3, 0.4, 0.3);
915  else
916  pixg = pixConvertTo8(pixs, 0);
917 
918  /* Use the entire image for the estimate; pix1 is 1x1 */
919  pixOtsuAdaptiveThreshold(pixg, 5000, 5000, 0, 0, 0.1, &pix1, NULL);
920  pixGetPixel(pix1, 0, 0, &val);
921  ival = (l_int32)val;
922  ival = L_MIN(ival, 110);
923  pixDestroy(&pix1);
924 
925  /* Double normalization */
926  pixContrastNorm(pixg, pixg, 50, 50, mindiff, 2, 2);
927  pix1 = pixBackgroundNormSimple(pixg, NULL, NULL);
928  pixDestroy(&pixg);
929 
930 /* lept_stderr("ival = %d\n", ival); */
931  pixd = pixThresholdToBinary(pix1, ival);
932  pixDestroy(&pix1);
933  return pixd;
934 }
935 
936 
937 /*----------------------------------------------------------------------*
938  * Global thresholding using connected components *
939  *----------------------------------------------------------------------*/
994 l_ok
996  PIX *pixm,
997  l_int32 start,
998  l_int32 end,
999  l_int32 incr,
1000  l_float32 thresh48,
1001  l_float32 threshdiff,
1002  l_int32 *pglobthresh,
1003  PIX **ppixd,
1004  l_int32 debugflag)
1005 {
1006 l_int32 i, thresh, n, n4, n8, mincounts, found, globthresh;
1007 l_float32 count4, count8, firstcount4, prevcount4, diff48, diff4;
1008 GPLOT *gplot;
1009 NUMA *na4, *na8;
1010 PIX *pix1, *pix2, *pix3;
1011 
1012  if (pglobthresh) *pglobthresh = 0;
1013  if (ppixd) *ppixd = NULL;
1014  if (!pixs || pixGetDepth(pixs) == 1)
1015  return ERROR_INT("pixs undefined or 1 bpp", __func__, 1);
1016  if (pixm && pixGetDepth(pixm) != 1)
1017  return ERROR_INT("pixm must be 1 bpp", __func__, 1);
1018 
1019  /* Assign default values if requested */
1020  if (start <= 0) start = 80;
1021  if (end <= 0) end = 200;
1022  if (incr <= 0) incr = 10;
1023  if (thresh48 <= 0.0) thresh48 = 0.01;
1024  if (threshdiff <= 0.0) threshdiff = 0.01;
1025  if (start > end)
1026  return ERROR_INT("invalid start,end", __func__, 1);
1027 
1028  /* Make 8 bpp, using the max component if color. */
1029  if (pixGetColormap(pixs))
1031  else
1032  pix1 = pixClone(pixs);
1033  if (pixGetDepth(pix1) == 32)
1034  pix2 = pixConvertRGBToGrayMinMax(pix1, L_CHOOSE_MAX);
1035  else
1036  pix2 = pixConvertTo8(pix1, 0);
1037  pixDestroy(&pix1);
1038 
1039  /* Mask out any non-text regions. Do this in-place, because pix2
1040  * can never be the same pix as pixs. */
1041  if (pixm)
1042  pixSetMasked(pix2, pixm, 255);
1043 
1044  /* Make sure there are enough components to get a valid signal */
1045  pix3 = pixConvertTo1(pix2, start);
1046  pixCountConnComp(pix3, 4, &n4);
1047  pixDestroy(&pix3);
1048  mincounts = 500;
1049  if (n4 < mincounts) {
1050  L_INFO("Insufficient component count: %d\n", __func__, n4);
1051  pixDestroy(&pix2);
1052  return 1;
1053  }
1054 
1055  /* Compute the c.c. data */
1056  na4 = numaCreate(0);
1057  na8 = numaCreate(0);
1058  numaSetParameters(na4, start, incr);
1059  numaSetParameters(na8, start, incr);
1060  for (thresh = start, i = 0; thresh <= end; thresh += incr, i++) {
1061  pix3 = pixConvertTo1(pix2, thresh);
1062  pixCountConnComp(pix3, 4, &n4);
1063  pixCountConnComp(pix3, 8, &n8);
1064  numaAddNumber(na4, n4);
1065  numaAddNumber(na8, n8);
1066  pixDestroy(&pix3);
1067  }
1068  if (debugflag) {
1069  lept_mkdir("lept/binarize");
1070  gplot = gplotCreate("/tmp/lept/binarize", GPLOT_PNG,
1071  "number of cc vs. threshold",
1072  "threshold", "number of cc");
1073  gplotAddPlot(gplot, NULL, na4, GPLOT_LINES, "plot 4cc");
1074  gplotAddPlot(gplot, NULL, na8, GPLOT_LINES, "plot 8cc");
1075  gplotMakeOutput(gplot);
1076  gplotDestroy(&gplot);
1077  }
1078 
1079  n = numaGetCount(na4);
1080  found = FALSE;
1081  for (i = 0; i < n; i++) {
1082  if (i == 0) {
1083  numaGetFValue(na4, i, &firstcount4);
1084  prevcount4 = firstcount4;
1085  } else {
1086  numaGetFValue(na4, i, &count4);
1087  numaGetFValue(na8, i, &count8);
1088  diff48 = (count4 - count8) / firstcount4;
1089  diff4 = L_ABS(prevcount4 - count4) / firstcount4;
1090  if (debugflag) {
1091  lept_stderr("diff48 = %7.3f, diff4 = %7.3f\n",
1092  diff48, diff4);
1093  }
1094  if (diff48 < thresh48 && diff4 < threshdiff) {
1095  found = TRUE;
1096  break;
1097  }
1098  prevcount4 = count4;
1099  }
1100  }
1101  numaDestroy(&na4);
1102  numaDestroy(&na8);
1103 
1104  if (found) {
1105  globthresh = start + i * incr;
1106  if (pglobthresh) *pglobthresh = globthresh;
1107  if (ppixd) {
1108  *ppixd = pixConvertTo1(pix2, globthresh);
1109  pixCopyResolution(*ppixd, pixs);
1110  }
1111  if (debugflag) lept_stderr("global threshold = %d\n", globthresh);
1112  pixDestroy(&pix2);
1113  return 0;
1114  }
1115 
1116  if (debugflag) lept_stderr("no global threshold found\n");
1117  pixDestroy(&pix2);
1118  return 1;
1119 }
1120 
1121 /*----------------------------------------------------------------------*
1122  * Global thresholding by histogram *
1123  *----------------------------------------------------------------------*/
1150 l_ok
1152  l_int32 factor,
1153  l_int32 halfw,
1154  l_int32 skip,
1155  l_int32 *pthresh,
1156  PIX **ppixd,
1157  NUMA **pnahisto,
1158  PIX **ppixhisto)
1159 {
1160 l_float32 maxval, fract;
1161 NUMA *na1, *na2, *na3;
1162 
1163  if (ppixd) *ppixd = NULL;
1164  if (pnahisto) *pnahisto = NULL;
1165  if (ppixhisto) *ppixhisto = NULL;
1166  if (!pthresh)
1167  return ERROR_INT("&thresh not defined", __func__, 1);
1168  *pthresh = 0;
1169  if (!pixs || pixGetDepth(pixs) != 8)
1170  return ERROR_INT("pixs undefined or not 8 bpp", __func__, 1);
1171  if (pixGetColormap(pixs))
1172  return ERROR_INT("pixs has colormap", __func__, 1);
1173  if (factor < 1)
1174  return ERROR_INT("sampling must be >= 1", __func__, 1);
1175  if (halfw <= 0) halfw = 20;
1176  if (skip <= 0) skip = 20;
1177 
1178  /* Make a histogram of pixel values where the largest peak
1179  * is normalized to a value of 1.0. */
1180  na1 = pixGetGrayHistogram(pixs, factor);
1181  na2 = numaWindowedMean(na1, halfw); /* smoothing */
1182  numaGetMax(na2, &maxval, NULL);
1183  na3 = numaTransform(na2, 0.0, 1.0 / maxval); /* rescale to max of 1.0 */
1184  numaDestroy(&na1);
1185  numaDestroy(&na2);
1186 
1187  if (numaFindLocForThreshold(na3, skip, pthresh, &fract) == 1) {
1188  numaDestroy(&na3);
1189  return ERROR_INT("failure to find threshold", __func__, 1);
1190  }
1191  L_INFO("fractional area under first peak: %5.3f\n", __func__, fract);
1192 
1193  if (ppixhisto) {
1194  lept_mkdir("lept/histo");
1195  gplotSimple1(na3, GPLOT_PNG, "/tmp/lept/histo/histo", NULL);
1196  *ppixhisto = pixRead("/tmp/lept/histo/histo.png");
1197  }
1198  if (pnahisto)
1199  *pnahisto = na3;
1200  else
1201  numaDestroy(&na3);
1202  if (*pthresh > 0 && ppixd)
1203  *ppixd = pixThresholdToBinary(pixs, *pthresh);
1204  return 0;
1205 }
1206 
PIX * pixBackgroundNormSimple(PIX *pixs, PIX *pixim, PIX *pixg)
pixBackgroundNormSimple()
Definition: adaptmap.c:245
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:2560
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:320
PIX * pixBackgroundNormFlex(PIX *pixs, l_int32 sx, l_int32 sy, l_int32 smoothx, l_int32 smoothy, l_int32 delta)
pixBackgroundNormFlex()
Definition: adaptmap.c:2467
#define SET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:127
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
static PIX * pixApplyLocalThreshold(PIX *pixs, PIX *pixth)
pixApplyLocalThreshold()
Definition: binarize.c:791
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:478
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:995
static PIX * pixSauvolaGetThreshold(PIX *pixm, PIX *pixms, l_float32 factor, PIX **ppixsd)
pixSauvolaGetThreshold()
Definition: binarize.c:711
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:367
PIX * pixSauvolaOnContrastNorm(PIX *pixs, l_int32 mindiff, PIX **ppixn, PIX **ppixth)
pixSauvolaOnContrastNorm()
Definition: binarize.c:850
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:603
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:271
PIX * pixThreshOnDoubleNorm(PIX *pixs, l_int32 mindiff)
pixThreshOnDoubleNorm()
Definition: binarize.c:904
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
l_ok pixThresholdByHisto(PIX *pixs, l_int32 factor, l_int32 halfw, l_int32 skip, l_int32 *pthresh, PIX **ppixd, NUMA **pnahisto, PIX **ppixhisto)
pixThresholdByHisto()
Definition: binarize.c:1151
l_ok pixCountConnComp(PIX *pixs, l_int32 connectivity, l_int32 *pcount)
pixCountConnComp()
Definition: conncomp.c:389
PIX * pixWindowedMeanSquare(PIX *pixs, l_int32 wc, l_int32 hc, l_int32 hasborder)
pixWindowedMeanSquare()
Definition: convolve.c:1170
PIX * pixBlockconv(PIX *pix, l_int32 wc, l_int32 hc)
pixBlockconv()
Definition: convolve.c:132
PIX * pixWindowedMean(PIX *pixs, l_int32 wc, l_int32 hc, l_int32 hasborder, l_int32 normflag)
pixWindowedMean()
Definition: convolve.c:1055
l_ok gplotAddPlot(GPLOT *gplot, NUMA *nax, NUMA *nay, l_int32 plotstyle, const char *plotlabel)
gplotAddPlot()
Definition: gplot.c:316
l_ok gplotMakeOutput(GPLOT *gplot)
gplotMakeOutput()
Definition: gplot.c:456
GPLOT * gplotCreate(const char *rootname, l_int32 outformat, const char *title, const char *xlabel, const char *ylabel)
gplotCreate()
Definition: gplot.c:187
void gplotDestroy(GPLOT **pgplot)
gplotDestroy()
Definition: gplot.c:253
l_ok gplotSimple1(NUMA *na, l_int32 outformat, const char *outroot, const char *title)
gplotSimple1()
Definition: gplot.c:649
PIX * pixThresholdToBinary(PIX *pixs, l_int32 thresh)
pixThresholdToBinary()
Definition: grayquant.c:443
PIX * pixMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequence()
Definition: morphseq.c:137
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:460
l_ok numaGetFValue(NUMA *na, l_int32 index, l_float32 *pval)
numaGetFValue()
Definition: numabasic.c:687
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:193
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:357
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:630
l_ok numaSetParameters(NUMA *na, l_float32 startx, l_float32 delx)
numaSetParameters()
Definition: numabasic.c:910
l_ok numaGetMax(NUMA *na, l_float32 *pmaxval, l_int32 *pimaxloc)
numaGetMax()
Definition: numafunc1.c:485
NUMA * numaWindowedMean(NUMA *nas, l_int32 wc)
numaWindowedMean()
Definition: numafunc2.c:574
NUMA * numaTransform(NUMA *nas, l_float32 shift, l_float32 scale)
numaTransform()
Definition: numafunc2.c:407
l_ok numaFindLocForThreshold(NUMA *na, l_int32 skip, l_int32 *pthresh, l_float32 *pfract)
numaFindLocForThreshold()
Definition: numafunc2.c:2589
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1642
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:608
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1074
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:582
l_ok pixSetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 val)
pixSetPixel()
Definition: pix2.c:263
l_ok pixGetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 *pval)
pixGetPixel()
Definition: pix2.c:192
PIX * pixRemoveBorder(PIX *pixs, l_int32 npix)
pixRemoveBorder()
Definition: pix2.c:1977
PIX * pixAddMirroredBorder(PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot)
pixAddMirroredBorder()
Definition: pix2.c:2100
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1481
l_ok pixSetMasked(PIX *pixd, PIX *pixm, l_uint32 val)
pixSetMasked()
Definition: pix3.c:163
l_ok pixCombineMasked(PIX *pixd, PIX *pixs, PIX *pixm)
pixCombineMasked()
Definition: pix3.c:378
NUMA * pixGetGrayHistogram(PIX *pixs, l_int32 factor)
pixGetGrayHistogram()
Definition: pix4.c:115
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:3424
@ REMOVE_CMAP_BASED_ON_SRC
Definition: pix.h:384
PIX * pixConvertRGBToGrayMinMax(PIX *pixs, l_int32 type)
pixConvertRGBToGrayMinMax()
Definition: pixconv.c:947
PIX * pixConvertRGBToGray(PIX *pixs, l_float32 rwt, l_float32 gwt, l_float32 bwt)
pixConvertRGBToGray()
Definition: pixconv.c:815
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:324
PIX * pixConvertTo1(PIX *pixs, l_int32 threshold)
pixConvertTo1()
Definition: pixconv.c:2952
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3055
l_ok pixTilingNoStripOnPaint(PIXTILING *pt)
pixTilingNoStripOnPaint()
Definition: pixtiling.c:359
PIX * pixTilingGetTile(PIXTILING *pt, l_int32 i, l_int32 j)
pixTilingGetTile()
Definition: pixtiling.c:248
void pixTilingDestroy(PIXTILING **ppt)
pixTilingDestroy()
Definition: pixtiling.c:178
l_ok pixTilingPaintTile(PIX *pixd, l_int32 i, l_int32 j, PIX *pixs, PIXTILING *pt)
pixTilingPaintTile()
Definition: pixtiling.c:379
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:124
PIX * pixRead(const char *filename)
pixRead()
Definition: readfile.c:189
Definition: gplot.h:77
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
l_int32 lept_mkdir(const char *subdir)
lept_mkdir()
Definition: utils2.c:2138