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
94static PIX *pixSauvolaGetThreshold(PIX *pixm, PIX *pixms, l_float32 factor,
95 PIX **ppixsd);
96static PIX *pixApplyLocalThreshold(PIX *pixs, PIX *pixth);
97
98/*------------------------------------------------------------------*
99 * Adaptive Otsu-based thresholding *
100 *------------------------------------------------------------------*/
156l_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{
166l_int32 w, h, nx, ny, i, j, thresh;
167l_uint32 val;
168PIX *pixt, *pixb, *pixthresh, *pixth, *pixd;
169PIXTILING *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 *------------------------------------------------------------------*/
272PIX *
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{
285l_int32 w, h;
286l_uint32 val;
287PIX *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 *----------------------------------------------------------------------*/
370PIX *
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{
382l_int32 w, h, highthresh;
383l_uint32 val;
384PIX *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 *----------------------------------------------------------------------*/
483l_ok
485 l_int32 whsize,
486 l_float32 factor,
487 l_int32 nx,
488 l_int32 ny,
489 PIX **ppixth,
490 PIX **ppixd)
491{
492l_int32 i, j, w, h, xrat, yrat;
493PIX *pixth, *pixd, *tileth, *tiled, *pixt;
494PIX **ptileth, **ptiled;
495PIXTILING *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
610l_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{
620l_int32 w, h;
621PIX *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
720static PIX *
722 PIX *pixms,
723 l_float32 factor,
724 PIX **ppixsd)
725{
726l_int32 i, j, w, h, tabsize, wplm, wplms, wplsd, wpld, usetab;
727l_int32 mv, ms, var, thresh;
728l_uint32 *datam, *datams, *datasd, *datad;
729l_uint32 *linem, *linems, *linesd, *lined;
730l_float32 sd;
731l_float32 *tab; /* of 2^16 square roots */
732PIX *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
802static PIX *
804 PIX *pixth)
805{
806l_int32 i, j, w, h, wpls, wplt, wpld, vals, valt;
807l_uint32 *datas, *datat, *datad, *lines, *linet, *lined;
808PIX *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 *----------------------------------------------------------------------*/
863PIX *
865 l_int32 mindiff,
866 PIX **ppixn,
867 PIX **ppixth)
868{
869l_int32 w, h, d, nx, ny;
870PIX *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 *----------------------------------------------------------------------*/
919PIX *
921 l_int32 mindiff)
922{
923l_int32 d, ival;
924l_uint32 val;
925PIX *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 *----------------------------------------------------------------------*/
1012l_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{
1024l_int32 i, thresh, n, n4, n8, mincounts, found, globthresh;
1025l_float32 count4, count8, firstcount4, prevcount4, diff48, diff4;
1026GPLOT *gplot;
1027NUMA *na4, *na8;
1028PIX *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 *----------------------------------------------------------------------*/
1168l_ok
1170 l_int32 factor,
1171 l_int32 halfw,
1172 l_float32 delta,
1173 l_int32 *pthresh,
1174 PIX **ppixd,
1175 PIX **ppixhisto)
1176{
1177l_float32 maxval, fract;
1178NUMA *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
PIX * pixBackgroundNormFlex(PIX *pixs, l_int32 sx, l_int32 sy, l_int32 smoothx, l_int32 smoothy, l_int32 delta)
pixBackgroundNormFlex()
Definition: adaptmap.c:2511
PIX * pixBackgroundNormSimple(PIX *pixs, PIX *pixim, PIX *pixg)
pixBackgroundNormSimple()
Definition: adaptmap.c:247
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 * 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
#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
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
static PIX * pixApplyLocalThreshold(PIX *pixs, PIX *pixth)
pixApplyLocalThreshold()
Definition: binarize.c:803
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
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
static PIX * pixSauvolaGetThreshold(PIX *pixm, PIX *pixms, l_float32 factor, PIX **ppixsd)
pixSauvolaGetThreshold()
Definition: binarize.c:721
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
PIX * pixSauvolaOnContrastNorm(PIX *pixs, l_int32 mindiff, PIX **ppixn, PIX **ppixth)
pixSauvolaOnContrastNorm()
Definition: binarize.c:864
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 * pixThreshOnDoubleNorm(PIX *pixs, l_int32 mindiff)
pixTheshOnDoubleNorm()
Definition: binarize.c:920
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
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 pixCountConnComp(PIX *pixs, l_int32 connectivity, l_int32 *pcount)
pixCountConnComp()
Definition: conncomp.c:394
PIX * pixBlockconv(PIX *pix, l_int32 wc, l_int32 hc)
pixBlockconv()
Definition: convolve.c:132
PIX * pixWindowedMeanSquare(PIX *pixs, l_int32 wc, l_int32 hc, l_int32 hasborder)
pixWindowedMeanSquare()
Definition: convolve.c:1190
PIX * pixWindowedMean(PIX *pixs, l_int32 wc, l_int32 hc, l_int32 hasborder, l_int32 normflag)
pixWindowedMean()
Definition: convolve.c:1073
l_ok gplotAddPlot(GPLOT *gplot, NUMA *nax, NUMA *nay, l_int32 plotstyle, const char *plotlabel)
gplotAddPlot()
Definition: gplot.c:320
l_ok gplotMakeOutput(GPLOT *gplot)
gplotMakeOutput()
Definition: gplot.c:466
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:255
l_ok gplotSimple1(NUMA *na, l_int32 outformat, const char *outroot, const char *title)
gplotSimple1()
Definition: gplot.c:665
PIX * pixThresholdToBinary(PIX *pixs, l_int32 thresh)
pixThresholdToBinary()
Definition: grayquant.c:447
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:478
l_ok numaGetFValue(NUMA *na, l_int32 index, l_float32 *pval)
numaGetFValue()
Definition: numabasic.c:719
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:366
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:658
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:194
l_ok numaSetParameters(NUMA *na, l_float32 startx, l_float32 delx)
numaSetParameters()
Definition: numabasic.c:993
l_ok numaGetMax(NUMA *na, l_float32 *pmaxval, l_int32 *pimaxloc)
numaGetMax()
Definition: numafunc1.c:496
NUMA * numaTransform(NUMA *nas, l_float32 shift, l_float32 scale)
numaTransform()
Definition: numafunc2.c:415
NUMA * numaWindowedMean(NUMA *nas, l_int32 wc)
numaWindowedMean()
Definition: numafunc2.c:588
l_ok numaFindLocForThreshold(NUMA *na, l_int32 skip, l_int32 *pthresh, l_float32 *pfract)
numaFindLocForThreshold()
Definition: numafunc2.c:2657
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:621
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1113
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:593
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1763
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:190
PIX * pixRemoveBorder(PIX *pixs, l_int32 npix)
pixRemoveBorder()
Definition: pix2.c:1972
PIX * pixAddMirroredBorder(PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot)
pixAddMirroredBorder()
Definition: pix2.c:2101
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:382
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1509
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
NUMA * pixGetGrayHistogram(PIX *pixs, l_int32 factor)
pixGetGrayHistogram()
Definition: pix4.c:115
@ REMOVE_CMAP_BASED_ON_SRC
Definition: pix.h:260
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3133
PIX * pixConvertRGBToGrayMinMax(PIX *pixs, l_int32 type)
pixConvertRGBToGrayMinMax()
Definition: pixconv.c:963
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:328
PIX * pixConvertRGBToGray(PIX *pixs, l_float32 rwt, l_float32 gwt, l_float32 bwt)
pixConvertRGBToGray()
Definition: pixconv.c:827
PIX * pixConvertTo1(PIX *pixs, l_int32 threshold)
pixConvertTo1()
Definition: pixconv.c:3026
l_ok pixTilingNoStripOnPaint(PIXTILING *pt)
pixTilingNoStripOnPaint()
Definition: pixtiling.c:368
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 pixTilingPaintTile(PIX *pixd, l_int32 i, l_int32 j, PIX *pixs, PIXTILING *pt)
pixTilingPaintTile()
Definition: pixtiling.c:390
PIX * pixTilingGetTile(PIXTILING *pt, l_int32 i, l_int32 j)
pixTilingGetTile()
Definition: pixtiling.c:255
PIX * pixRead(const char *filename)
pixRead()
Definition: readfile.c:193
Definition: gplot.h:77
Definition: array.h:71
Definition: pix.h:559
Definition: pix.h:139
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
l_int32 lept_mkdir(const char *subdir)
lept_mkdir()
Definition: utils2.c:2218