Leptonica 1.84.1
Image processing and image analysis suite
Loading...
Searching...
No Matches
adaptmap.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
140#ifdef HAVE_CONFIG_H
141#include <config_auto.h>
142#endif /* HAVE_CONFIG_H */
143
144#include "allheaders.h"
145
146 /* Default input parameters for pixBackgroundNormSimple()
147 * Notes:
148 * (1) mincount must never exceed the tile area (width * height)
149 * (2) bgval must be sufficiently below 255 to avoid accidental
150 * saturation; otherwise it should be large to avoid
151 * shrinking the dynamic range
152 * (3) results should otherwise not be sensitive to these values
153 */
154static const l_int32 DefaultTileWidth = 10;
155static const l_int32 DefaultTileHeight = 15;
156static const l_int32 DefaultFgThreshold = 60;
157static const l_int32 DefaultMinCount = 40;
158static const l_int32 DefaultBgVal = 200;
159static const l_int32 DefaultXSmoothSize = 2;
160static const l_int32 DefaultYSmoothSize = 1;
162static l_int32 pixMinMaxTiles(PIX *pixs, l_int32 sx, l_int32 sy,
163 l_int32 mindiff, l_int32 smoothx, l_int32 smoothy,
164 PIX **ppixmin, PIX **ppixmax);
165static l_int32 pixSetLowContrast(PIX *pixs1, PIX *pixs2, l_int32 mindiff);
166static PIX *pixLinearTRCTiled(PIX *pixd, PIX *pixs, l_int32 sx, l_int32 sy,
167 PIX *pixmin, PIX *pixmax);
168static l_int32 *iaaGetLinearTRC(l_int32 **iaa, l_int32 diff);
169
170static l_ok pixSelectiveContrastMod(PIX *pixs, l_int32 contrast);
171
172#ifndef NO_CONSOLE_IO
173#define DEBUG_GLOBAL 0
174#endif /* ~NO_CONSOLE_IO */
175
176/*------------------------------------------------------------------*
177 * Clean background to white using background normalization *
178 *------------------------------------------------------------------*/
203PIX *
205 PIX *pixim,
206 PIX *pixg,
207 l_float32 gamma,
208 l_int32 blackval,
209 l_int32 whiteval)
210{
211l_int32 d;
212PIX *pixd;
213
214 if (!pixs)
215 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
216 d = pixGetDepth(pixs);
217 if (d != 8 && d != 32)
218 return (PIX *)ERROR_PTR("depth not 8 or 32", __func__, NULL);
219 if (whiteval > 200) {
220 L_WARNING("white value %d must not exceed 200; reset to 190",
221 __func__, whiteval);
222 whiteval = 190;
223 }
224
225 pixd = pixBackgroundNormSimple(pixs, pixim, pixg);
226 if (!pixd)
227 return (PIX *)ERROR_PTR("background norm failedd", __func__, NULL);
228 pixGammaTRC(pixd, pixd, gamma, blackval, whiteval);
229 return pixd;
230}
231
232
233/*------------------------------------------------------------------*
234 * Adaptive background normalization *
235 *------------------------------------------------------------------*/
252PIX *
263
264
327PIX *
329 PIX *pixim,
330 PIX *pixg,
331 l_int32 sx,
332 l_int32 sy,
333 l_int32 thresh,
334 l_int32 mincount,
335 l_int32 bgval,
336 l_int32 smoothx,
337 l_int32 smoothy)
338{
339l_int32 d, allfg;
340PIX *pixm, *pixmi, *pixd;
341PIX *pixmr, *pixmg, *pixmb, *pixmri, *pixmgi, *pixmbi;
342
343 if (!pixs)
344 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
345 d = pixGetDepth(pixs);
346 if (d != 8 && d != 32)
347 return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", __func__, NULL);
348 if (sx < 4 || sy < 4)
349 return (PIX *)ERROR_PTR("sx and sy must be >= 4", __func__, NULL);
350 if (mincount > sx * sy) {
351 L_WARNING("mincount too large for tile size\n", __func__);
352 mincount = (sx * sy) / 3;
353 }
354
355 /* If pixim exists, verify that it is not all foreground. */
356 if (pixim) {
357 pixInvert(pixim, pixim);
358 pixZero(pixim, &allfg);
359 pixInvert(pixim, pixim);
360 if (allfg)
361 return (PIX *)ERROR_PTR("pixim all foreground", __func__, NULL);
362 }
363
364 pixd = NULL;
365 if (d == 8) {
366 pixm = NULL;
367 pixGetBackgroundGrayMap(pixs, pixim, sx, sy, thresh, mincount, &pixm);
368 if (!pixm) {
369 L_WARNING("map not made; return a copy of the source\n", __func__);
370 return pixCopy(NULL, pixs);
371 }
372
373 pixmi = pixGetInvBackgroundMap(pixm, bgval, smoothx, smoothy);
374 if (!pixmi) {
375 L_WARNING("pixmi not made; return a copy of source\n", __func__);
376 pixDestroy(&pixm);
377 return pixCopy(NULL, pixs);
378 } else {
379 pixd = pixApplyInvBackgroundGrayMap(pixs, pixmi, sx, sy);
380 }
381
382 pixDestroy(&pixm);
383 pixDestroy(&pixmi);
384 }
385 else {
386 pixmr = pixmg = pixmb = NULL;
387 pixGetBackgroundRGBMap(pixs, pixim, pixg, sx, sy, thresh,
388 mincount, &pixmr, &pixmg, &pixmb);
389 if (!pixmr || !pixmg || !pixmb) {
390 pixDestroy(&pixmr);
391 pixDestroy(&pixmg);
392 pixDestroy(&pixmb);
393 L_WARNING("map not made; return a copy of the source\n", __func__);
394 return pixCopy(NULL, pixs);
395 }
396
397 pixmri = pixGetInvBackgroundMap(pixmr, bgval, smoothx, smoothy);
398 pixmgi = pixGetInvBackgroundMap(pixmg, bgval, smoothx, smoothy);
399 pixmbi = pixGetInvBackgroundMap(pixmb, bgval, smoothx, smoothy);
400 if (!pixmri || !pixmgi || !pixmbi) {
401 L_WARNING("not all pixm*i are made; return src copy\n", __func__);
402 pixd = pixCopy(NULL, pixs);
403 } else {
404 pixd = pixApplyInvBackgroundRGBMap(pixs, pixmri, pixmgi, pixmbi,
405 sx, sy);
406 }
407
408 pixDestroy(&pixmr);
409 pixDestroy(&pixmg);
410 pixDestroy(&pixmb);
411 pixDestroy(&pixmri);
412 pixDestroy(&pixmgi);
413 pixDestroy(&pixmbi);
414 }
415
416 if (!pixd)
417 ERROR_PTR("pixd not made", __func__, NULL);
418 pixCopyResolution(pixd, pixs);
419 return pixd;
420}
421
422
462PIX *
464 PIX *pixim,
465 l_int32 reduction,
466 l_int32 size,
467 l_int32 bgval)
468{
469l_int32 d, allfg;
470PIX *pixm, *pixmi, *pixd;
471PIX *pixmr, *pixmg, *pixmb, *pixmri, *pixmgi, *pixmbi;
472
473 if (!pixs)
474 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
475 d = pixGetDepth(pixs);
476 if (d != 8 && d != 32)
477 return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", __func__, NULL);
478 if (reduction < 2 || reduction > 16)
479 return (PIX *)ERROR_PTR("reduction must be between 2 and 16",
480 __func__, NULL);
481
482 /* If pixim exists, verify that it is not all foreground. */
483 if (pixim) {
484 pixInvert(pixim, pixim);
485 pixZero(pixim, &allfg);
486 pixInvert(pixim, pixim);
487 if (allfg)
488 return (PIX *)ERROR_PTR("pixim all foreground", __func__, NULL);
489 }
490
491 pixd = NULL;
492 if (d == 8) {
493 pixGetBackgroundGrayMapMorph(pixs, pixim, reduction, size, &pixm);
494 if (!pixm)
495 return (PIX *)ERROR_PTR("pixm not made", __func__, NULL);
496 pixmi = pixGetInvBackgroundMap(pixm, bgval, 0, 0);
497 if (!pixmi)
498 ERROR_PTR("pixmi not made", __func__, NULL);
499 else
500 pixd = pixApplyInvBackgroundGrayMap(pixs, pixmi,
501 reduction, reduction);
502 pixDestroy(&pixm);
503 pixDestroy(&pixmi);
504 }
505 else { /* d == 32 */
506 pixmr = pixmg = pixmb = NULL;
507 pixGetBackgroundRGBMapMorph(pixs, pixim, reduction, size,
508 &pixmr, &pixmg, &pixmb);
509 if (!pixmr || !pixmg || !pixmb) {
510 pixDestroy(&pixmr);
511 pixDestroy(&pixmg);
512 pixDestroy(&pixmb);
513 return (PIX *)ERROR_PTR("not all pixm*", __func__, NULL);
514 }
515
516 pixmri = pixGetInvBackgroundMap(pixmr, bgval, 0, 0);
517 pixmgi = pixGetInvBackgroundMap(pixmg, bgval, 0, 0);
518 pixmbi = pixGetInvBackgroundMap(pixmb, bgval, 0, 0);
519 if (!pixmri || !pixmgi || !pixmbi)
520 ERROR_PTR("not all pixm*i are made", __func__, NULL);
521 else
522 pixd = pixApplyInvBackgroundRGBMap(pixs, pixmri, pixmgi, pixmbi,
523 reduction, reduction);
524
525 pixDestroy(&pixmr);
526 pixDestroy(&pixmg);
527 pixDestroy(&pixmb);
528 pixDestroy(&pixmri);
529 pixDestroy(&pixmgi);
530 pixDestroy(&pixmbi);
531 }
532
533 if (!pixd)
534 ERROR_PTR("pixd not made", __func__, NULL);
535 pixCopyResolution(pixd, pixs);
536 return pixd;
537}
538
539
540/*-------------------------------------------------------------------------*
541 * Arrays of inverted background values for normalization *
542 *-------------------------------------------------------------------------*
543 * Notes for these four functions: *
544 * (1) They are useful if you need to save the actual mapping array. *
545 * (2) They could be used in the top-level functions but are *
546 * not because their use makes those functions less clear. *
547 * (3) Each component in the input pixs generates a 16 bpp pix array. *
548 *-------------------------------------------------------------------------*/
571l_ok
573 PIX *pixim,
574 l_int32 sx,
575 l_int32 sy,
576 l_int32 thresh,
577 l_int32 mincount,
578 l_int32 bgval,
579 l_int32 smoothx,
580 l_int32 smoothy,
581 PIX **ppixd)
582{
583l_int32 allfg;
584PIX *pixm;
585
586 if (!ppixd)
587 return ERROR_INT("&pixd not defined", __func__, 1);
588 *ppixd = NULL;
589 if (!pixs || pixGetDepth(pixs) != 8)
590 return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
591 if (pixGetColormap(pixs))
592 return ERROR_INT("pixs is colormapped", __func__, 1);
593 if (pixim && pixGetDepth(pixim) != 1)
594 return ERROR_INT("pixim not 1 bpp", __func__, 1);
595 if (sx < 4 || sy < 4)
596 return ERROR_INT("sx and sy must be >= 4", __func__, 1);
597 if (mincount > sx * sy) {
598 L_WARNING("mincount too large for tile size\n", __func__);
599 mincount = (sx * sy) / 3;
600 }
601
602 /* If pixim exists, verify that it is not all foreground. */
603 if (pixim) {
604 pixInvert(pixim, pixim);
605 pixZero(pixim, &allfg);
606 pixInvert(pixim, pixim);
607 if (allfg)
608 return ERROR_INT("pixim all foreground", __func__, 1);
609 }
610
611 pixGetBackgroundGrayMap(pixs, pixim, sx, sy, thresh, mincount, &pixm);
612 if (!pixm)
613 return ERROR_INT("pixm not made", __func__, 1);
614 *ppixd = pixGetInvBackgroundMap(pixm, bgval, smoothx, smoothy);
615 pixCopyResolution(*ppixd, pixs);
616 pixDestroy(&pixm);
617 return 0;
618}
619
620
646l_ok
648 PIX *pixim,
649 PIX *pixg,
650 l_int32 sx,
651 l_int32 sy,
652 l_int32 thresh,
653 l_int32 mincount,
654 l_int32 bgval,
655 l_int32 smoothx,
656 l_int32 smoothy,
657 PIX **ppixr,
658 PIX **ppixg,
659 PIX **ppixb)
660{
661l_int32 allfg;
662PIX *pixmr, *pixmg, *pixmb;
663
664 if (!ppixr || !ppixg || !ppixb)
665 return ERROR_INT("&pixr, &pixg, &pixb not all defined", __func__, 1);
666 *ppixr = *ppixg = *ppixb = NULL;
667 if (!pixs)
668 return ERROR_INT("pixs not defined", __func__, 1);
669 if (pixGetDepth(pixs) != 32)
670 return ERROR_INT("pixs not 32 bpp", __func__, 1);
671 if (pixim && pixGetDepth(pixim) != 1)
672 return ERROR_INT("pixim not 1 bpp", __func__, 1);
673 if (sx < 4 || sy < 4)
674 return ERROR_INT("sx and sy must be >= 4", __func__, 1);
675 if (mincount > sx * sy) {
676 L_WARNING("mincount too large for tile size\n", __func__);
677 mincount = (sx * sy) / 3;
678 }
679
680 /* If pixim exists, verify that it is not all foreground. */
681 if (pixim) {
682 pixInvert(pixim, pixim);
683 pixZero(pixim, &allfg);
684 pixInvert(pixim, pixim);
685 if (allfg)
686 return ERROR_INT("pixim all foreground", __func__, 1);
687 }
688
689 pixGetBackgroundRGBMap(pixs, pixim, pixg, sx, sy, thresh, mincount,
690 &pixmr, &pixmg, &pixmb);
691 if (!pixmr || !pixmg || !pixmb) {
692 pixDestroy(&pixmr);
693 pixDestroy(&pixmg);
694 pixDestroy(&pixmb);
695 return ERROR_INT("not all pixm* made", __func__, 1);
696 }
697
698 *ppixr = pixGetInvBackgroundMap(pixmr, bgval, smoothx, smoothy);
699 *ppixg = pixGetInvBackgroundMap(pixmg, bgval, smoothx, smoothy);
700 *ppixb = pixGetInvBackgroundMap(pixmb, bgval, smoothx, smoothy);
701 pixDestroy(&pixmr);
702 pixDestroy(&pixmg);
703 pixDestroy(&pixmb);
704 return 0;
705}
706
707
727l_ok
729 PIX *pixim,
730 l_int32 reduction,
731 l_int32 size,
732 l_int32 bgval,
733 PIX **ppixd)
734{
735l_int32 allfg;
736PIX *pixm;
737
738 if (!ppixd)
739 return ERROR_INT("&pixd not defined", __func__, 1);
740 *ppixd = NULL;
741 if (!pixs)
742 return ERROR_INT("pixs not defined", __func__, 1);
743 if (pixGetDepth(pixs) != 8)
744 return ERROR_INT("pixs not 8 bpp", __func__, 1);
745 if (pixim && pixGetDepth(pixim) != 1)
746 return ERROR_INT("pixim not 1 bpp", __func__, 1);
747 if (reduction < 2 || reduction > 16)
748 return ERROR_INT("reduction must be between 2 and 16", __func__, 1);
749
750 /* If pixim exists, verify that it is not all foreground. */
751 if (pixim) {
752 pixInvert(pixim, pixim);
753 pixZero(pixim, &allfg);
754 pixInvert(pixim, pixim);
755 if (allfg)
756 return ERROR_INT("pixim all foreground", __func__, 1);
757 }
758
759 pixGetBackgroundGrayMapMorph(pixs, pixim, reduction, size, &pixm);
760 if (!pixm)
761 return ERROR_INT("pixm not made", __func__, 1);
762 *ppixd = pixGetInvBackgroundMap(pixm, bgval, 0, 0);
763 pixCopyResolution(*ppixd, pixs);
764 pixDestroy(&pixm);
765 return 0;
766}
767
768
790l_ok
792 PIX *pixim,
793 l_int32 reduction,
794 l_int32 size,
795 l_int32 bgval,
796 PIX **ppixr,
797 PIX **ppixg,
798 PIX **ppixb)
799{
800l_int32 allfg;
801PIX *pixmr, *pixmg, *pixmb;
802
803 if (!ppixr || !ppixg || !ppixb)
804 return ERROR_INT("&pixr, &pixg, &pixb not all defined", __func__, 1);
805 *ppixr = *ppixg = *ppixb = NULL;
806 if (!pixs)
807 return ERROR_INT("pixs not defined", __func__, 1);
808 if (pixGetDepth(pixs) != 32)
809 return ERROR_INT("pixs not 32 bpp", __func__, 1);
810 if (pixim && pixGetDepth(pixim) != 1)
811 return ERROR_INT("pixim not 1 bpp", __func__, 1);
812 if (reduction < 2 || reduction > 16)
813 return ERROR_INT("reduction must be between 2 and 16", __func__, 1);
814
815 /* If pixim exists, verify that it is not all foreground. */
816 if (pixim) {
817 pixInvert(pixim, pixim);
818 pixZero(pixim, &allfg);
819 pixInvert(pixim, pixim);
820 if (allfg)
821 return ERROR_INT("pixim all foreground", __func__, 1);
822 }
823
824 pixGetBackgroundRGBMapMorph(pixs, pixim, reduction, size,
825 &pixmr, &pixmg, &pixmb);
826 if (!pixmr || !pixmg || !pixmb) {
827 pixDestroy(&pixmr);
828 pixDestroy(&pixmg);
829 pixDestroy(&pixmb);
830 return ERROR_INT("not all pixm* made", __func__, 1);
831 }
832
833 *ppixr = pixGetInvBackgroundMap(pixmr, bgval, 0, 0);
834 *ppixg = pixGetInvBackgroundMap(pixmg, bgval, 0, 0);
835 *ppixb = pixGetInvBackgroundMap(pixmb, bgval, 0, 0);
836 pixDestroy(&pixmr);
837 pixDestroy(&pixmg);
838 pixDestroy(&pixmb);
839 return 0;
840}
841
842
843/*------------------------------------------------------------------*
844 * Measurement of local background *
845 *------------------------------------------------------------------*/
865l_ok
867 PIX *pixim,
868 l_int32 sx,
869 l_int32 sy,
870 l_int32 thresh,
871 l_int32 mincount,
872 PIX **ppixd)
873{
874l_int32 w, h, wd, hd, wim, him, wpls, wplim, wpld, wplf;
875l_int32 xim, yim, delx, nx, ny, i, j, k, m;
876l_int32 count, sum, val8;
877l_int32 empty, fgpixels;
878l_uint32 *datas, *dataim, *datad, *dataf, *lines, *lineim, *lined, *linef;
879l_float32 scalex, scaley;
880PIX *pixd, *piximi, *pixb, *pixf, *pixims;
881
882 if (!ppixd)
883 return ERROR_INT("&pixd not defined", __func__, 1);
884 *ppixd = NULL;
885 if (!pixs || pixGetDepth(pixs) != 8)
886 return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
887 if (pixGetColormap(pixs))
888 return ERROR_INT("pixs is colormapped", __func__, 1);
889 if (pixim && pixGetDepth(pixim) != 1)
890 return ERROR_INT("pixim not 1 bpp", __func__, 1);
891 if (sx < 4 || sy < 4)
892 return ERROR_INT("sx and sy must be >= 4", __func__, 1);
893 if (mincount > sx * sy) {
894 L_WARNING("mincount too large for tile size\n", __func__);
895 mincount = (sx * sy) / 3;
896 }
897
898 /* Evaluate the 'image' mask, pixim, and make sure
899 * it is not all fg. */
900 fgpixels = 0; /* boolean for existence of fg pixels in the image mask. */
901 if (pixim) {
902 piximi = pixInvert(NULL, pixim); /* set non-'image' pixels to 1 */
903 pixZero(piximi, &empty);
904 pixDestroy(&piximi);
905 if (empty)
906 return ERROR_INT("pixim all fg; no background", __func__, 1);
907 pixZero(pixim, &empty);
908 if (!empty) /* there are fg pixels in pixim */
909 fgpixels = 1;
910 }
911
912 /* Generate the foreground mask, pixf, which is at
913 * full resolution. These pixels will be ignored when
914 * computing the background values. */
915 pixb = pixThresholdToBinary(pixs, thresh);
916 pixf = pixMorphSequence(pixb, "d7.1 + d1.7", 0);
917 pixDestroy(&pixb);
918 if (!pixf)
919 return ERROR_INT("pixf not made", __func__, 1);
920
921
922 /* ------------- Set up the output map pixd --------------- */
923 /* Generate pixd, which is reduced by the factors (sx, sy). */
924 w = pixGetWidth(pixs);
925 h = pixGetHeight(pixs);
926 wd = (w + sx - 1) / sx;
927 hd = (h + sy - 1) / sy;
928 pixd = pixCreate(wd, hd, 8);
929
930 /* Note: we only compute map values in tiles that are complete.
931 * In general, tiles at right and bottom edges will not be
932 * complete, and we must fill them in later. */
933 nx = w / sx;
934 ny = h / sy;
935 wpls = pixGetWpl(pixs);
936 datas = pixGetData(pixs);
937 wpld = pixGetWpl(pixd);
938 datad = pixGetData(pixd);
939 wplf = pixGetWpl(pixf);
940 dataf = pixGetData(pixf);
941 for (i = 0; i < ny; i++) {
942 lines = datas + sy * i * wpls;
943 linef = dataf + sy * i * wplf;
944 lined = datad + i * wpld;
945 for (j = 0; j < nx; j++) {
946 delx = j * sx;
947 sum = 0;
948 count = 0;
949 for (k = 0; k < sy; k++) {
950 for (m = 0; m < sx; m++) {
951 if (GET_DATA_BIT(linef + k * wplf, delx + m) == 0) {
952 sum += GET_DATA_BYTE(lines + k * wpls, delx + m);
953 count++;
954 }
955 }
956 }
957 if (count >= mincount) {
958 val8 = sum / count;
959 SET_DATA_BYTE(lined, j, val8);
960 }
961 }
962 }
963 pixDestroy(&pixf);
964
965 /* If there is an optional mask with fg pixels, erase the previous
966 * calculation for the corresponding map pixels, setting the
967 * map values to 0. Then, when all the map holes are filled,
968 * these erased pixels will be set by the surrounding map values.
969 *
970 * The calculation here is relatively efficient: for each pixel
971 * in pixd (which corresponds to a tile of mask pixels in pixim)
972 * we look only at the pixel in pixim that is at the center
973 * of the tile. If the mask pixel is ON, we reset the map
974 * pixel in pixd to 0, so that it can later be filled in. */
975 pixims = NULL;
976 if (pixim && fgpixels) {
977 wim = pixGetWidth(pixim);
978 him = pixGetHeight(pixim);
979 dataim = pixGetData(pixim);
980 wplim = pixGetWpl(pixim);
981 for (i = 0; i < ny; i++) {
982 yim = i * sy + sy / 2;
983 if (yim >= him)
984 break;
985 lineim = dataim + yim * wplim;
986 for (j = 0; j < nx; j++) {
987 xim = j * sx + sx / 2;
988 if (xim >= wim)
989 break;
990 if (GET_DATA_BIT(lineim, xim))
991 pixSetPixel(pixd, j, i, 0);
992 }
993 }
994 }
995
996 /* Fill all the holes in the map. */
997 if (pixFillMapHoles(pixd, nx, ny, L_FILL_BLACK)) {
998 pixDestroy(&pixd);
999 L_WARNING("can't make the map\n", __func__);
1000 return 1;
1001 }
1002
1003 /* Finally, for each connected region corresponding to the
1004 * 'image' mask, reset all pixels to their average value.
1005 * Each of these components represents an image (or part of one)
1006 * in the input, and this smooths the background values
1007 * in each of these regions. */
1008 if (pixim && fgpixels) {
1009 scalex = 1. / (l_float32)sx;
1010 scaley = 1. / (l_float32)sy;
1011 pixims = pixScaleBySampling(pixim, scalex, scaley);
1012 pixSmoothConnectedRegions(pixd, pixims, 2);
1013 pixDestroy(&pixims);
1014 }
1015
1016 *ppixd = pixd;
1017 pixCopyResolution(*ppixd, pixs);
1018 return 0;
1019}
1020
1021
1045l_ok
1047 PIX *pixim,
1048 PIX *pixg,
1049 l_int32 sx,
1050 l_int32 sy,
1051 l_int32 thresh,
1052 l_int32 mincount,
1053 PIX **ppixmr,
1054 PIX **ppixmg,
1055 PIX **ppixmb)
1056{
1057l_int32 w, h, wm, hm, wim, him, wpls, wplim, wplf;
1058l_int32 xim, yim, delx, nx, ny, i, j, k, m;
1059l_int32 count, rsum, gsum, bsum, rval, gval, bval;
1060l_int32 empty, fgpixels;
1061l_uint32 pixel;
1062l_uint32 *datas, *dataim, *dataf, *lines, *lineim, *linef;
1063l_float32 scalex, scaley;
1064PIX *piximi, *pixgc, *pixb, *pixf, *pixims;
1065PIX *pixmr, *pixmg, *pixmb;
1066
1067 if (!ppixmr || !ppixmg || !ppixmb)
1068 return ERROR_INT("&pixm* not all defined", __func__, 1);
1069 *ppixmr = *ppixmg = *ppixmb = NULL;
1070 if (!pixs)
1071 return ERROR_INT("pixs not defined", __func__, 1);
1072 if (pixGetDepth(pixs) != 32)
1073 return ERROR_INT("pixs not 32 bpp", __func__, 1);
1074 if (pixim && pixGetDepth(pixim) != 1)
1075 return ERROR_INT("pixim not 1 bpp", __func__, 1);
1076 if (sx < 4 || sy < 4)
1077 return ERROR_INT("sx and sy must be >= 4", __func__, 1);
1078 if (mincount > sx * sy) {
1079 L_WARNING("mincount too large for tile size\n", __func__);
1080 mincount = (sx * sy) / 3;
1081 }
1082
1083 /* Evaluate the mask pixim and make sure it is not all foreground */
1084 fgpixels = 0; /* boolean for existence of fg mask pixels */
1085 if (pixim) {
1086 piximi = pixInvert(NULL, pixim); /* set non-'image' pixels to 1 */
1087 pixZero(piximi, &empty);
1088 pixDestroy(&piximi);
1089 if (empty)
1090 return ERROR_INT("pixim all fg; no background", __func__, 1);
1091 pixZero(pixim, &empty);
1092 if (!empty) /* there are fg pixels in pixim */
1093 fgpixels = 1;
1094 }
1095
1096 /* Generate the foreground mask. These pixels will be
1097 * ignored when computing the background values. */
1098 if (pixg) /* use the input grayscale version if it is provided */
1099 pixgc = pixClone(pixg);
1100 else
1101 pixgc = pixConvertRGBToGrayFast(pixs);
1102 pixb = pixThresholdToBinary(pixgc, thresh);
1103 pixf = pixMorphSequence(pixb, "d7.1 + d1.7", 0);
1104 pixDestroy(&pixgc);
1105 pixDestroy(&pixb);
1106
1107 /* Generate the output mask images */
1108 w = pixGetWidth(pixs);
1109 h = pixGetHeight(pixs);
1110 wm = (w + sx - 1) / sx;
1111 hm = (h + sy - 1) / sy;
1112 pixmr = pixCreate(wm, hm, 8);
1113 pixmg = pixCreate(wm, hm, 8);
1114 pixmb = pixCreate(wm, hm, 8);
1115
1116 /* ------------- Set up the mapping images --------------- */
1117 /* Note: we only compute map values in tiles that are complete.
1118 * In general, tiles at right and bottom edges will not be
1119 * complete, and we must fill them in later. */
1120 nx = w / sx;
1121 ny = h / sy;
1122 wpls = pixGetWpl(pixs);
1123 datas = pixGetData(pixs);
1124 wplf = pixGetWpl(pixf);
1125 dataf = pixGetData(pixf);
1126 for (i = 0; i < ny; i++) {
1127 lines = datas + sy * i * wpls;
1128 linef = dataf + sy * i * wplf;
1129 for (j = 0; j < nx; j++) {
1130 delx = j * sx;
1131 rsum = gsum = bsum = 0;
1132 count = 0;
1133 for (k = 0; k < sy; k++) {
1134 for (m = 0; m < sx; m++) {
1135 if (GET_DATA_BIT(linef + k * wplf, delx + m) == 0) {
1136 pixel = *(lines + k * wpls + delx + m);
1137 rsum += (pixel >> 24);
1138 gsum += ((pixel >> 16) & 0xff);
1139 bsum += ((pixel >> 8) & 0xff);
1140 count++;
1141 }
1142 }
1143 }
1144 if (count >= mincount) {
1145 rval = rsum / count;
1146 gval = gsum / count;
1147 bval = bsum / count;
1148 pixSetPixel(pixmr, j, i, rval);
1149 pixSetPixel(pixmg, j, i, gval);
1150 pixSetPixel(pixmb, j, i, bval);
1151 }
1152 }
1153 }
1154 pixDestroy(&pixf);
1155
1156 /* If there is an optional mask with fg pixels, erase the previous
1157 * calculation for the corresponding map pixels, setting the
1158 * map values in each of the 3 color maps to 0. Then, when
1159 * all the map holes are filled, these erased pixels will
1160 * be set by the surrounding map values. */
1161 if (pixim) {
1162 wim = pixGetWidth(pixim);
1163 him = pixGetHeight(pixim);
1164 dataim = pixGetData(pixim);
1165 wplim = pixGetWpl(pixim);
1166 for (i = 0; i < ny; i++) {
1167 yim = i * sy + sy / 2;
1168 if (yim >= him)
1169 break;
1170 lineim = dataim + yim * wplim;
1171 for (j = 0; j < nx; j++) {
1172 xim = j * sx + sx / 2;
1173 if (xim >= wim)
1174 break;
1175 if (GET_DATA_BIT(lineim, xim)) {
1176 pixSetPixel(pixmr, j, i, 0);
1177 pixSetPixel(pixmg, j, i, 0);
1178 pixSetPixel(pixmb, j, i, 0);
1179 }
1180 }
1181 }
1182 }
1183
1184 /* ----------------- Now fill in the holes ----------------------- */
1185 if (pixFillMapHoles(pixmr, nx, ny, L_FILL_BLACK) ||
1186 pixFillMapHoles(pixmg, nx, ny, L_FILL_BLACK) ||
1187 pixFillMapHoles(pixmb, nx, ny, L_FILL_BLACK)) {
1188 pixDestroy(&pixmr);
1189 pixDestroy(&pixmg);
1190 pixDestroy(&pixmb);
1191 L_WARNING("can't make the maps\n", __func__);
1192 return 1;
1193 }
1194
1195 /* Finally, for each connected region corresponding to the
1196 * fg mask, reset all pixels to their average value. */
1197 if (pixim && fgpixels) {
1198 scalex = 1. / (l_float32)sx;
1199 scaley = 1. / (l_float32)sy;
1200 pixims = pixScaleBySampling(pixim, scalex, scaley);
1201 pixSmoothConnectedRegions(pixmr, pixims, 2);
1202 pixSmoothConnectedRegions(pixmg, pixims, 2);
1203 pixSmoothConnectedRegions(pixmb, pixims, 2);
1204 pixDestroy(&pixims);
1205 }
1206
1207 *ppixmr = pixmr;
1208 *ppixmg = pixmg;
1209 *ppixmb = pixmb;
1210 pixCopyResolution(*ppixmr, pixs);
1211 pixCopyResolution(*ppixmg, pixs);
1212 pixCopyResolution(*ppixmb, pixs);
1213 return 0;
1214}
1215
1216
1228l_ok
1230 PIX *pixim,
1231 l_int32 reduction,
1232 l_int32 size,
1233 PIX **ppixm)
1234{
1235l_int32 nx, ny, empty, fgpixels;
1236l_float32 scale;
1237PIX *pixm, *pix1, *pix2, *pix3, *pixims;
1238
1239 if (!ppixm)
1240 return ERROR_INT("&pixm not defined", __func__, 1);
1241 *ppixm = NULL;
1242 if (!pixs || pixGetDepth(pixs) != 8)
1243 return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
1244 if (pixGetColormap(pixs))
1245 return ERROR_INT("pixs is colormapped", __func__, 1);
1246 if (pixim && pixGetDepth(pixim) != 1)
1247 return ERROR_INT("pixim not 1 bpp", __func__, 1);
1248
1249 /* Evaluate the mask pixim and make sure it is not all foreground. */
1250 fgpixels = 0; /* boolean for existence of fg mask pixels */
1251 if (pixim) {
1252 pixInvert(pixim, pixim); /* set background pixels to 1 */
1253 pixZero(pixim, &empty);
1254 if (empty)
1255 return ERROR_INT("pixim all fg; no background", __func__, 1);
1256 pixInvert(pixim, pixim); /* revert to original mask */
1257 pixZero(pixim, &empty);
1258 if (!empty) /* there are fg pixels in pixim */
1259 fgpixels = 1;
1260 }
1261
1262 /* Downscale as requested and do the closing to get the background. */
1263 scale = 1. / (l_float32)reduction;
1264 pix1 = pixScaleBySampling(pixs, scale, scale);
1265 pix2 = pixCloseGray(pix1, size, size);
1266 pix3 = pixExtendByReplication(pix2, 1, 1);
1267 pixDestroy(&pix1);
1268 pixDestroy(&pix2);
1269
1270 /* Downscale the image mask, if any, and remove it from the
1271 * background. These pixels will be filled in (twice). */
1272 pixims = NULL;
1273 if (pixim) {
1274 pixims = pixScale(pixim, scale, scale);
1275 pixm = pixConvertTo8(pixims, FALSE);
1276 pixAnd(pixm, pixm, pix3);
1277 }
1278 else
1279 pixm = pixClone(pix3);
1280 pixDestroy(&pix3);
1281
1282 /* Fill all the holes in the map. */
1283 nx = pixGetWidth(pixs) / reduction;
1284 ny = pixGetHeight(pixs) / reduction;
1285 if (pixFillMapHoles(pixm, nx, ny, L_FILL_BLACK)) {
1286 pixDestroy(&pixm);
1287 pixDestroy(&pixims);
1288 L_WARNING("can't make the map\n", __func__);
1289 return 1;
1290 }
1291
1292 /* Finally, for each connected region corresponding to the
1293 * fg mask, reset all pixels to their average value. */
1294 if (pixim && fgpixels)
1295 pixSmoothConnectedRegions(pixm, pixims, 2);
1296 pixDestroy(&pixims);
1297
1298 *ppixm = pixm;
1299 pixCopyResolution(*ppixm, pixs);
1300 return 0;
1301}
1302
1303
1317l_ok
1319 PIX *pixim,
1320 l_int32 reduction,
1321 l_int32 size,
1322 PIX **ppixmr,
1323 PIX **ppixmg,
1324 PIX **ppixmb)
1325{
1326l_int32 nx, ny, empty, fgpixels;
1327l_float32 scale;
1328PIX *pixm, *pixmr, *pixmg, *pixmb, *pix1, *pix2, *pix3, *pixims;
1329
1330 if (!ppixmr || !ppixmg || !ppixmb)
1331 return ERROR_INT("&pixm* not all defined", __func__, 1);
1332 *ppixmr = *ppixmg = *ppixmb = NULL;
1333 if (!pixs)
1334 return ERROR_INT("pixs not defined", __func__, 1);
1335 if (pixGetDepth(pixs) != 32)
1336 return ERROR_INT("pixs not 32 bpp", __func__, 1);
1337 if (pixim && pixGetDepth(pixim) != 1)
1338 return ERROR_INT("pixim not 1 bpp", __func__, 1);
1339
1340 /* Evaluate the mask pixim and make sure it is not all foreground. */
1341 fgpixels = 0; /* boolean for existence of fg mask pixels */
1342 if (pixim) {
1343 pixInvert(pixim, pixim); /* set background pixels to 1 */
1344 pixZero(pixim, &empty);
1345 if (empty)
1346 return ERROR_INT("pixim all fg; no background", __func__, 1);
1347 pixInvert(pixim, pixim); /* revert to original mask */
1348 pixZero(pixim, &empty);
1349 if (!empty) /* there are fg pixels in pixim */
1350 fgpixels = 1;
1351 }
1352
1353 /* Generate an 8 bpp version of the image mask, if it exists */
1354 scale = 1. / (l_float32)reduction;
1355 pixims = NULL;
1356 pixm = NULL;
1357 if (pixim) {
1358 pixims = pixScale(pixim, scale, scale);
1359 pixm = pixConvertTo8(pixims, FALSE);
1360 }
1361
1362 /* Downscale as requested and do the closing to get the background.
1363 * Then remove the image mask pixels from the background. They
1364 * will be filled in (twice) later. Do this for all 3 components. */
1365 pix1 = pixScaleRGBToGrayFast(pixs, reduction, COLOR_RED);
1366 pix2 = pixCloseGray(pix1, size, size);
1367 pix3 = pixExtendByReplication(pix2, 1, 1);
1368 if (pixim)
1369 pixmr = pixAnd(NULL, pixm, pix3);
1370 else
1371 pixmr = pixClone(pix3);
1372 pixDestroy(&pix1);
1373 pixDestroy(&pix2);
1374 pixDestroy(&pix3);
1375
1376 pix1 = pixScaleRGBToGrayFast(pixs, reduction, COLOR_GREEN);
1377 pix2 = pixCloseGray(pix1, size, size);
1378 pix3 = pixExtendByReplication(pix2, 1, 1);
1379 if (pixim)
1380 pixmg = pixAnd(NULL, pixm, pix3);
1381 else
1382 pixmg = pixClone(pix3);
1383 pixDestroy(&pix1);
1384 pixDestroy(&pix2);
1385 pixDestroy(&pix3);
1386
1387 pix1 = pixScaleRGBToGrayFast(pixs, reduction, COLOR_BLUE);
1388 pix2 = pixCloseGray(pix1, size, size);
1389 pix3 = pixExtendByReplication(pix2, 1, 1);
1390 if (pixim)
1391 pixmb = pixAnd(NULL, pixm, pix3);
1392 else
1393 pixmb = pixClone(pix3);
1394 pixDestroy(&pixm);
1395 pixDestroy(&pix1);
1396 pixDestroy(&pix2);
1397 pixDestroy(&pix3);
1398
1399 /* Fill all the holes in the three maps. */
1400 nx = pixGetWidth(pixs) / reduction;
1401 ny = pixGetHeight(pixs) / reduction;
1402 if (pixFillMapHoles(pixmr, nx, ny, L_FILL_BLACK) ||
1403 pixFillMapHoles(pixmg, nx, ny, L_FILL_BLACK) ||
1404 pixFillMapHoles(pixmb, nx, ny, L_FILL_BLACK)) {
1405 pixDestroy(&pixmr);
1406 pixDestroy(&pixmg);
1407 pixDestroy(&pixmb);
1408 pixDestroy(&pixims);
1409 L_WARNING("can't make the maps\n", __func__);
1410 return 1;
1411 }
1412
1413 /* Finally, for each connected region corresponding to the
1414 * fg mask in each component, reset all pixels to their
1415 * average value. */
1416 if (pixim && fgpixels) {
1417 pixSmoothConnectedRegions(pixmr, pixims, 2);
1418 pixSmoothConnectedRegions(pixmg, pixims, 2);
1419 pixSmoothConnectedRegions(pixmb, pixims, 2);
1420 pixDestroy(&pixims);
1421 }
1422
1423 *ppixmr = pixmr;
1424 *ppixmg = pixmg;
1425 *ppixmb = pixmb;
1426 pixCopyResolution(*ppixmr, pixs);
1427 pixCopyResolution(*ppixmg, pixs);
1428 pixCopyResolution(*ppixmb, pixs);
1429 return 0;
1430}
1431
1432
1469l_ok
1471 l_int32 nx,
1472 l_int32 ny,
1473 l_int32 filltype)
1474{
1475l_int32 w, h, y, nmiss, goodcol, i, j, found, ival, valtest;
1476l_uint32 val, lastval;
1477NUMA *na; /* indicates if there is any data in the column */
1478
1479 if (!pix || pixGetDepth(pix) != 8)
1480 return ERROR_INT("pix not defined or not 8 bpp", __func__, 1);
1481 if (pixGetColormap(pix))
1482 return ERROR_INT("pix is colormapped", __func__, 1);
1483
1484 /* ------------- Fill holes in the mapping image columns ----------- */
1485 pixGetDimensions(pix, &w, &h, NULL);
1486 na = numaCreate(0); /* holds flag for which columns have data */
1487 nmiss = 0;
1488 valtest = (filltype == L_FILL_WHITE) ? 255 : 0;
1489 for (j = 0; j < nx; j++) { /* do it by columns */
1490 found = FALSE;
1491 for (i = 0; i < ny; i++) {
1492 pixGetPixel(pix, j, i, &val);
1493 if (val != valtest) {
1494 y = i;
1495 found = TRUE;
1496 break;
1497 }
1498 }
1499 if (found == FALSE) {
1500 numaAddNumber(na, 0); /* no data in the column */
1501 nmiss++;
1502 }
1503 else {
1504 numaAddNumber(na, 1); /* data in the column */
1505 for (i = y - 1; i >= 0; i--) /* replicate upwards to top */
1506 pixSetPixel(pix, j, i, val);
1507 pixGetPixel(pix, j, 0, &lastval);
1508 for (i = 1; i < h; i++) { /* set going down to bottom */
1509 pixGetPixel(pix, j, i, &val);
1510 if (val == valtest)
1511 pixSetPixel(pix, j, i, lastval);
1512 else
1513 lastval = val;
1514 }
1515 }
1516 }
1517
1518 if (nmiss == nx) { /* no data in any column! */
1519 numaDestroy(&na);
1520 L_WARNING("no bg found; no data in any column\n", __func__);
1521 return 1;
1522 }
1523
1524 /* ---------- Fill in missing columns by replication ----------- */
1525 if (nmiss > 0) { /* replicate columns */
1526 /* Find the first good column */
1527 goodcol = 0;
1528 for (j = 0; j < w; j++) {
1529 numaGetIValue(na, j, &ival);
1530 if (ival == 1) {
1531 goodcol = j;
1532 break;
1533 }
1534 }
1535 if (goodcol > 0) { /* copy cols backward */
1536 for (j = goodcol - 1; j >= 0; j--)
1537 pixRasterop(pix, j, 0, 1, h, PIX_SRC, pix, j + 1, 0);
1538 }
1539 for (j = goodcol + 1; j < w; j++) { /* copy cols forward */
1540 numaGetIValue(na, j, &ival);
1541 if (ival == 0) {
1542 /* Copy the column to the left of j */
1543 pixRasterop(pix, j, 0, 1, h, PIX_SRC, pix, j - 1, 0);
1544 }
1545 }
1546 }
1547 if (w > nx) { /* replicate the last column */
1548 pixRasterop(pix, w - 1, 0, 1, h, PIX_SRC, pix, w - 2, 0);
1549 }
1550
1551 numaDestroy(&na);
1552 return 0;
1553}
1554
1555
1569PIX *
1571 l_int32 addw,
1572 l_int32 addh)
1573{
1574l_int32 w, h, i, j;
1575l_uint32 val;
1576PIX *pixd;
1577
1578 if (!pixs || pixGetDepth(pixs) != 8)
1579 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, NULL);
1580
1581 if (addw == 0 && addh == 0)
1582 return pixCopy(NULL, pixs);
1583
1584 pixGetDimensions(pixs, &w, &h, NULL);
1585 if ((pixd = pixCreate(w + addw, h + addh, 8)) == NULL)
1586 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1587 pixRasterop(pixd, 0, 0, w, h, PIX_SRC, pixs, 0, 0);
1588
1589 if (addw > 0) {
1590 for (i = 0; i < h; i++) {
1591 pixGetPixel(pixd, w - 1, i, &val);
1592 for (j = 0; j < addw; j++)
1593 pixSetPixel(pixd, w + j, i, val);
1594 }
1595 }
1596
1597 if (addh > 0) {
1598 for (j = 0; j < w + addw; j++) {
1599 pixGetPixel(pixd, j, h - 1, &val);
1600 for (i = 0; i < addh; i++)
1601 pixSetPixel(pixd, j, h + i, val);
1602 }
1603 }
1604
1605 pixCopyResolution(pixd, pixs);
1606 return pixd;
1607}
1608
1609
1630l_ok
1632 PIX *pixm,
1633 l_int32 factor)
1634{
1635l_int32 empty, i, n, x, y;
1636l_float32 aveval;
1637BOXA *boxa;
1638PIX *pixmc;
1639PIXA *pixa;
1640
1641 if (!pixs || pixGetDepth(pixs) != 8)
1642 return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
1643 if (pixGetColormap(pixs))
1644 return ERROR_INT("pixs has colormap", __func__, 1);
1645 if (!pixm) {
1646 L_INFO("pixm not defined\n", __func__);
1647 return 0;
1648 }
1649 if (pixGetDepth(pixm) != 1)
1650 return ERROR_INT("pixm not 1 bpp", __func__, 1);
1651 pixZero(pixm, &empty);
1652 if (empty) {
1653 L_INFO("pixm has no fg pixels; nothing to do\n", __func__);
1654 return 0;
1655 }
1656
1657 boxa = pixConnComp(pixm, &pixa, 8);
1658 n = boxaGetCount(boxa);
1659 for (i = 0; i < n; i++) {
1660 if ((pixmc = pixaGetPix(pixa, i, L_CLONE)) == NULL) {
1661 L_WARNING("missing pixmc!\n", __func__);
1662 continue;
1663 }
1664 boxaGetBoxGeometry(boxa, i, &x, &y, NULL, NULL);
1665 pixGetAverageMasked(pixs, pixmc, x, y, factor, L_MEAN_ABSVAL, &aveval);
1666 pixPaintThroughMask(pixs, pixmc, x, y, (l_int32)aveval);
1667 pixDestroy(&pixmc);
1668 }
1669
1670 boxaDestroy(&boxa);
1671 pixaDestroy(&pixa);
1672 return 0;
1673}
1674
1675
1676/*------------------------------------------------------------------*
1677 * Measurement of local foreground *
1678 *------------------------------------------------------------------*/
1679#if 0 /* Not working properly: do not use */
1680
1717l_ok
1718pixGetForegroundGrayMap(PIX *pixs,
1719 PIX *pixim,
1720 l_int32 sx,
1721 l_int32 sy,
1722 l_int32 thresh,
1723 PIX **ppixd)
1724{
1725l_int32 w, h, d, wd, hd;
1726l_int32 empty, fgpixels;
1727PIX *pixd, *piximi, *pixim2, *pixims, *pixs2, *pixb, *pixt1, *pixt2, *pixt3;
1728
1729 if (!ppixd)
1730 return ERROR_INT("&pixd not defined", __func__, 1);
1731 *ppixd = NULL;
1732 if (!pixs)
1733 return ERROR_INT("pixs not defined", __func__, 1);
1734 pixGetDimensions(pixs, &w, &h, &d);
1735 if (d != 8)
1736 return ERROR_INT("pixs not 8 bpp", __func__, 1);
1737 if (pixim && pixGetDepth(pixim) != 1)
1738 return ERROR_INT("pixim not 1 bpp", __func__, 1);
1739 if (sx < 2 || sy < 2)
1740 return ERROR_INT("sx and sy must be >= 2", __func__, 1);
1741
1742 /* Generate pixd, which is reduced by the factors (sx, sy). */
1743 wd = (w + sx - 1) / sx;
1744 hd = (h + sy - 1) / sy;
1745 pixd = pixCreate(wd, hd, 8);
1746 *ppixd = pixd;
1747
1748 /* Evaluate the 'image' mask, pixim. If it is all fg,
1749 * the output pixd has all pixels with value 0. */
1750 fgpixels = 0; /* boolean for existence of fg pixels in the image mask. */
1751 if (pixim) {
1752 piximi = pixInvert(NULL, pixim); /* set non-image pixels to 1 */
1753 pixZero(piximi, &empty);
1754 pixDestroy(&piximi);
1755 if (empty) /* all 'image'; return with all pixels set to 0 */
1756 return 0;
1757 pixZero(pixim, &empty);
1758 if (!empty) /* there are fg pixels in pixim */
1759 fgpixels = 1;
1760 }
1761
1762 /* 2x subsampling; paint white through 'image' mask. */
1763 pixs2 = pixScaleBySampling(pixs, 0.5, 0.5);
1764 if (pixim && fgpixels) {
1765 pixim2 = pixReduceBinary2(pixim, NULL);
1766 pixPaintThroughMask(pixs2, pixim2, 0, 0, 255);
1767 pixDestroy(&pixim2);
1768 }
1769
1770 /* Min (erosion) downscaling; total reduction (4 sx, 4 sy). */
1771 pixt1 = pixScaleGrayMinMax(pixs2, sx, sy, L_CHOOSE_MIN);
1772
1773/* pixDisplay(pixt1, 300, 200); */
1774
1775 /* Threshold to identify fg; paint bg pixels to white. */
1776 pixb = pixThresholdToBinary(pixt1, thresh); /* fg pixels */
1777 pixInvert(pixb, pixb);
1778 pixPaintThroughMask(pixt1, pixb, 0, 0, 255);
1779 pixDestroy(&pixb);
1780
1781 /* Replicative expansion by 2x to (sx, sy). */
1782 pixt2 = pixExpandReplicate(pixt1, 2);
1783
1784/* pixDisplay(pixt2, 500, 200); */
1785
1786 /* Fill holes in the fg by propagation */
1787 pixFillMapHoles(pixt2, w / sx, h / sy, L_FILL_WHITE);
1788
1789/* pixDisplay(pixt2, 700, 200); */
1790
1791 /* Smooth with 17x17 kernel. */
1792 pixt3 = pixBlockconv(pixt2, 8, 8);
1793 pixRasterop(pixd, 0, 0, wd, hd, PIX_SRC, pixt3, 0, 0);
1794
1795 /* Paint the image parts black. */
1796 pixims = pixScaleBySampling(pixim, 1. / sx, 1. / sy);
1797 pixPaintThroughMask(pixd, pixims, 0, 0, 0);
1798
1799 pixDestroy(&pixs2);
1800 pixDestroy(&pixt1);
1801 pixDestroy(&pixt2);
1802 pixDestroy(&pixt3);
1803 return 0;
1804}
1805#endif /* Not working properly: do not use */
1806
1807
1808/*------------------------------------------------------------------*
1809 * Generate inverted background map *
1810 *------------------------------------------------------------------*/
1827PIX *
1829 l_int32 bgval,
1830 l_int32 smoothx,
1831 l_int32 smoothy)
1832{
1833l_int32 w, h, wplsm, wpld, i, j;
1834l_int32 val, val16;
1835l_uint32 *datasm, *datad, *linesm, *lined;
1836PIX *pixsm, *pixd;
1837
1838 if (!pixs || pixGetDepth(pixs) != 8)
1839 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, NULL);
1840 if (pixGetColormap(pixs))
1841 return (PIX *)ERROR_PTR("pixs has colormap", __func__, NULL);
1842 pixGetDimensions(pixs, &w, &h, NULL);
1843 if (w < 5 || h < 5)
1844 return (PIX *)ERROR_PTR("w and h must be >= 5", __func__, NULL);
1845
1846 /* smooth the map image */
1847 pixsm = pixBlockconv(pixs, smoothx, smoothy);
1848 datasm = pixGetData(pixsm);
1849 wplsm = pixGetWpl(pixsm);
1850
1851 /* invert the map image, scaling up to preserve dynamic range */
1852 pixd = pixCreate(w, h, 16);
1853 datad = pixGetData(pixd);
1854 wpld = pixGetWpl(pixd);
1855 for (i = 0; i < h; i++) {
1856 linesm = datasm + i * wplsm;
1857 lined = datad + i * wpld;
1858 for (j = 0; j < w; j++) {
1859 val = GET_DATA_BYTE(linesm, j);
1860 if (val > 0)
1861 val16 = (256 * bgval) / val;
1862 else { /* shouldn't happen */
1863 L_WARNING("smoothed bg has 0 pixel!\n", __func__);
1864 val16 = bgval / 2;
1865 }
1866 SET_DATA_TWO_BYTES(lined, j, val16);
1867 }
1868 }
1869
1870 pixDestroy(&pixsm);
1871 pixCopyResolution(pixd, pixs);
1872 return pixd;
1873}
1874
1875
1876/*------------------------------------------------------------------*
1877 * Apply background map to image *
1878 *------------------------------------------------------------------*/
1888PIX *
1890 PIX *pixm,
1891 l_int32 sx,
1892 l_int32 sy)
1893{
1894l_int32 w, h, wm, hm, wpls, wpld, i, j, k, m, xoff, yoff;
1895l_int32 vals, vald;
1896l_uint32 val16;
1897l_uint32 *datas, *datad, *lines, *lined, *flines, *flined;
1898PIX *pixd;
1899
1900 if (!pixs || pixGetDepth(pixs) != 8)
1901 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, NULL);
1902 if (pixGetColormap(pixs))
1903 return (PIX *)ERROR_PTR("pixs has colormap", __func__, NULL);
1904 if (!pixm || pixGetDepth(pixm) != 16)
1905 return (PIX *)ERROR_PTR("pixm undefined or not 16 bpp", __func__, NULL);
1906 if (sx == 0 || sy == 0)
1907 return (PIX *)ERROR_PTR("invalid sx and/or sy", __func__, NULL);
1908
1909 datas = pixGetData(pixs);
1910 wpls = pixGetWpl(pixs);
1911 pixGetDimensions(pixs, &w, &h, NULL);
1912 pixGetDimensions(pixm, &wm, &hm, NULL);
1913 if ((pixd = pixCreateTemplate(pixs)) == NULL)
1914 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1915 datad = pixGetData(pixd);
1916 wpld = pixGetWpl(pixd);
1917 for (i = 0; i < hm; i++) {
1918 lines = datas + sy * i * wpls;
1919 lined = datad + sy * i * wpld;
1920 yoff = sy * i;
1921 for (j = 0; j < wm; j++) {
1922 pixGetPixel(pixm, j, i, &val16);
1923 xoff = sx * j;
1924 for (k = 0; k < sy && yoff + k < h; k++) {
1925 flines = lines + k * wpls;
1926 flined = lined + k * wpld;
1927 for (m = 0; m < sx && xoff + m < w; m++) {
1928 vals = GET_DATA_BYTE(flines, xoff + m);
1929 vald = (vals * val16) / 256;
1930 vald = L_MIN(vald, 255);
1931 SET_DATA_BYTE(flined, xoff + m, vald);
1932 }
1933 }
1934 }
1935 }
1936
1937 return pixd;
1938}
1939
1940
1952PIX *
1954 PIX *pixmr,
1955 PIX *pixmg,
1956 PIX *pixmb,
1957 l_int32 sx,
1958 l_int32 sy)
1959{
1960l_int32 w, h, wm, hm, wpls, wpld, i, j, k, m, xoff, yoff;
1961l_int32 rvald, gvald, bvald;
1962l_uint32 vals;
1963l_uint32 rval16, gval16, bval16;
1964l_uint32 *datas, *datad, *lines, *lined, *flines, *flined;
1965PIX *pixd;
1966
1967 if (!pixs)
1968 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1969 if (pixGetDepth(pixs) != 32)
1970 return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, NULL);
1971 if (!pixmr || !pixmg || !pixmb)
1972 return (PIX *)ERROR_PTR("pix maps not all defined", __func__, NULL);
1973 if (pixGetDepth(pixmr) != 16 || pixGetDepth(pixmg) != 16 ||
1974 pixGetDepth(pixmb) != 16)
1975 return (PIX *)ERROR_PTR("pix maps not all 16 bpp", __func__, NULL);
1976 if (sx == 0 || sy == 0)
1977 return (PIX *)ERROR_PTR("invalid sx and/or sy", __func__, NULL);
1978
1979 datas = pixGetData(pixs);
1980 wpls = pixGetWpl(pixs);
1981 w = pixGetWidth(pixs);
1982 h = pixGetHeight(pixs);
1983 wm = pixGetWidth(pixmr);
1984 hm = pixGetHeight(pixmr);
1985 if ((pixd = pixCreateTemplate(pixs)) == NULL)
1986 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1987 datad = pixGetData(pixd);
1988 wpld = pixGetWpl(pixd);
1989 for (i = 0; i < hm; i++) {
1990 lines = datas + sy * i * wpls;
1991 lined = datad + sy * i * wpld;
1992 yoff = sy * i;
1993 for (j = 0; j < wm; j++) {
1994 pixGetPixel(pixmr, j, i, &rval16);
1995 pixGetPixel(pixmg, j, i, &gval16);
1996 pixGetPixel(pixmb, j, i, &bval16);
1997 xoff = sx * j;
1998 for (k = 0; k < sy && yoff + k < h; k++) {
1999 flines = lines + k * wpls;
2000 flined = lined + k * wpld;
2001 for (m = 0; m < sx && xoff + m < w; m++) {
2002 vals = *(flines + xoff + m);
2003 rvald = ((vals >> 24) * rval16) / 256;
2004 rvald = L_MIN(rvald, 255);
2005 gvald = (((vals >> 16) & 0xff) * gval16) / 256;
2006 gvald = L_MIN(gvald, 255);
2007 bvald = (((vals >> 8) & 0xff) * bval16) / 256;
2008 bvald = L_MIN(bvald, 255);
2009 composeRGBPixel(rvald, gvald, bvald, flined + xoff + m);
2010 }
2011 }
2012 }
2013 }
2014
2015 return pixd;
2016}
2017
2018
2019/*------------------------------------------------------------------*
2020 * Apply variable map *
2021 *------------------------------------------------------------------*/
2048PIX *
2050 PIX *pixg,
2051 l_int32 target)
2052{
2053l_int32 i, j, w, h, d, wpls, wplg, wpld, vals, valg, vald;
2054l_uint8 *lut;
2055l_uint32 *datas, *datag, *datad, *lines, *lineg, *lined;
2056l_float32 fval;
2057PIX *pixd;
2058
2059 if (!pixs)
2060 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
2061 if (!pixg)
2062 return (PIX *)ERROR_PTR("pixg not defined", __func__, NULL);
2063 if (!pixSizesEqual(pixs, pixg))
2064 return (PIX *)ERROR_PTR("pix sizes not equal", __func__, NULL);
2065 pixGetDimensions(pixs, &w, &h, &d);
2066 if (d != 8)
2067 return (PIX *)ERROR_PTR("depth not 8 bpp", __func__, NULL);
2068
2069 /* Generate a LUT for the mapping if the image is large enough
2070 * to warrant the overhead. The LUT is of size 2^16. For the
2071 * index to the table, get the MSB from pixs and the LSB from pixg.
2072 * Note: this LUT is bigger than the typical 32K L1 cache, so
2073 * we expect cache misses. L2 latencies are about 5ns. But
2074 * division is slooooow. For large images, this function is about
2075 * 4x faster when using the LUT. C'est la vie. */
2076 lut = NULL;
2077 if (w * h > 100000) { /* more pixels than 2^16 */
2078 lut = (l_uint8 *)LEPT_CALLOC(0x10000, sizeof(l_uint8));
2079 for (i = 0; i < 256; i++) {
2080 for (j = 0; j < 256; j++) {
2081 fval = (l_float32)(i * target) / (j + 0.5);
2082 lut[(i << 8) + j] = L_MIN(255, (l_int32)(fval + 0.5));
2083 }
2084 }
2085 }
2086
2087 if ((pixd = pixCreate(w, h, 8)) == NULL) {
2088 LEPT_FREE(lut);
2089 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
2090 }
2091 pixCopyResolution(pixd, pixs);
2092 datad = pixGetData(pixd);
2093 wpld = pixGetWpl(pixd);
2094 datas = pixGetData(pixs);
2095 wpls = pixGetWpl(pixs);
2096 datag = pixGetData(pixg);
2097 wplg = pixGetWpl(pixg);
2098 for (i = 0; i < h; i++) {
2099 lines = datas + i * wpls;
2100 lineg = datag + i * wplg;
2101 lined = datad + i * wpld;
2102 if (lut) {
2103 for (j = 0; j < w; j++) {
2104 vals = GET_DATA_BYTE(lines, j);
2105 valg = GET_DATA_BYTE(lineg, j);
2106 vald = lut[(vals << 8) + valg];
2107 SET_DATA_BYTE(lined, j, vald);
2108 }
2109 }
2110 else {
2111 for (j = 0; j < w; j++) {
2112 vals = GET_DATA_BYTE(lines, j);
2113 valg = GET_DATA_BYTE(lineg, j);
2114 fval = (l_float32)(vals * target) / (valg + 0.5);
2115 vald = L_MIN(255, (l_int32)(fval + 0.5));
2116 SET_DATA_BYTE(lined, j, vald);
2117 }
2118 }
2119 }
2120
2121 LEPT_FREE(lut);
2122 return pixd;
2123}
2124
2125
2126/*------------------------------------------------------------------*
2127 * Non-adaptive (global) mapping *
2128 *------------------------------------------------------------------*/
2163PIX *
2165 PIX *pixs,
2166 l_int32 rval,
2167 l_int32 gval,
2168 l_int32 bval,
2169 l_int32 mapval)
2170{
2171l_int32 w, h, d, i, j, ncolors, rv, gv, bv, wpl;
2172l_int32 *rarray, *garray, *barray;
2173l_uint32 *data, *line;
2174NUMA *nar, *nag, *nab;
2175PIXCMAP *cmap;
2176
2177 if (!pixs)
2178 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
2179 cmap = pixGetColormap(pixs);
2180 pixGetDimensions(pixs, &w, &h, &d);
2181 if (!cmap && d != 32)
2182 return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", __func__, NULL);
2183 if (mapval <= 0) {
2184 L_WARNING("mapval must be > 0; setting to 255\n", __func__);
2185 mapval = 255;
2186 }
2187
2188 /* Prepare pixd to be a copy of pixs */
2189 if ((pixd = pixCopy(pixd, pixs)) == NULL)
2190 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
2191
2192 /* Generate the TRC maps for each component. Make sure the
2193 * upper range for each color is greater than zero. */
2194 nar = numaGammaTRC(1.0, 0, L_MAX(1, 255 * rval / mapval));
2195 nag = numaGammaTRC(1.0, 0, L_MAX(1, 255 * gval / mapval));
2196 nab = numaGammaTRC(1.0, 0, L_MAX(1, 255 * bval / mapval));
2197
2198 /* Extract copies of the internal arrays */
2199 rarray = numaGetIArray(nar);
2200 garray = numaGetIArray(nag);
2201 barray = numaGetIArray(nab);
2202 if (!nar || !nag || !nab || !rarray || !garray || !barray) {
2203 L_ERROR("allocation failure in arrays\n", __func__);
2204 goto cleanup_arrays;
2205 }
2206
2207 if (cmap) {
2208 ncolors = pixcmapGetCount(cmap);
2209 for (i = 0; i < ncolors; i++) {
2210 pixcmapGetColor(cmap, i, &rv, &gv, &bv);
2211 pixcmapResetColor(cmap, i, rarray[rv], garray[gv], barray[bv]);
2212 }
2213 }
2214 else {
2215 data = pixGetData(pixd);
2216 wpl = pixGetWpl(pixd);
2217 for (i = 0; i < h; i++) {
2218 line = data + i * wpl;
2219 for (j = 0; j < w; j++) {
2220 extractRGBValues(line[j], &rv, &gv, &bv);
2221 composeRGBPixel(rarray[rv], garray[gv], barray[bv], line + j);
2222 }
2223 }
2224 }
2225
2226cleanup_arrays:
2227 numaDestroy(&nar);
2228 numaDestroy(&nag);
2229 numaDestroy(&nab);
2230 LEPT_FREE(rarray);
2231 LEPT_FREE(garray);
2232 LEPT_FREE(barray);
2233 return pixd;
2234}
2235
2236
2270PIX *
2272 PIX *pixs,
2273 l_int32 rval,
2274 l_int32 gval,
2275 l_int32 bval,
2276 l_int32 factor,
2277 l_float32 rank)
2278{
2279l_int32 mapval;
2280l_float32 rankrval, rankgval, rankbval;
2281l_float32 rfract, gfract, bfract, maxfract;
2282
2283 if (!pixs)
2284 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
2285 if (pixGetDepth(pixs) != 32)
2286 return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, NULL);
2287 if (factor < 1)
2288 return (PIX *)ERROR_PTR("sampling factor < 1", __func__, NULL);
2289 if (rank < 0.0 || rank > 1.0)
2290 return (PIX *)ERROR_PTR("rank not in [0.0 ... 1.0]", __func__, NULL);
2291 if (rval <= 0 || gval <= 0 || bval <= 0)
2292 return (PIX *)ERROR_PTR("invalid estim. color values", __func__, NULL);
2293
2294 /* The max value for each component may be larger than the
2295 * input estimated background value. In that case, mapping
2296 * for those pixels would saturate. To prevent saturation,
2297 * we compute the fraction for each component by which we
2298 * would oversaturate. Then take the max of these, and
2299 * reduce, uniformly over all components, the output intensity
2300 * by this value. Then no component will saturate.
2301 * In practice, if rank < 1.0, a fraction of pixels
2302 * may have a component saturate. By keeping rank close to 1.0,
2303 * that fraction can be made arbitrarily small. */
2304 pixGetRankValueMaskedRGB(pixs, NULL, 0, 0, factor, rank, &rankrval,
2305 &rankgval, &rankbval);
2306 rfract = rankrval / (l_float32)rval;
2307 gfract = rankgval / (l_float32)gval;
2308 bfract = rankbval / (l_float32)bval;
2309 maxfract = L_MAX(rfract, gfract);
2310 maxfract = L_MAX(maxfract, bfract);
2311#if DEBUG_GLOBAL
2312 lept_stderr("rankrval = %7.2f, rankgval = %7.2f, rankbval = %7.2f\n",
2313 rankrval, rankgval, rankbval);
2314 lept_stderr("rfract = %7.4f, gfract = %7.4f, bfract = %7.4f\n",
2315 rfract, gfract, bfract);
2316#endif /* DEBUG_GLOBAL */
2317
2318 mapval = (l_int32)(255. / maxfract);
2319 pixd = pixGlobalNormRGB(pixd, pixs, rval, gval, bval, mapval);
2320 return pixd;
2321}
2322
2323
2324/*------------------------------------------------------------------*
2325 * Adaptive threshold spread normalization *
2326 *------------------------------------------------------------------*/
2370l_ok
2372 l_int32 filtertype,
2373 l_int32 edgethresh,
2374 l_int32 smoothx,
2375 l_int32 smoothy,
2376 l_float32 gamma,
2377 l_int32 minval,
2378 l_int32 maxval,
2379 l_int32 targetthresh,
2380 PIX **ppixth,
2381 PIX **ppixb,
2382 PIX **ppixd)
2383{
2384PIX *pixe, *pixet, *pixsd, *pixg1, *pixg2, *pixth;
2385
2386 if (ppixth) *ppixth = NULL;
2387 if (ppixb) *ppixb = NULL;
2388 if (ppixd) *ppixd = NULL;
2389 if (!pixs || pixGetDepth(pixs) != 8)
2390 return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
2391 if (pixGetColormap(pixs))
2392 return ERROR_INT("pixs is colormapped", __func__, 1);
2393 if (!ppixth && !ppixb && !ppixd)
2394 return ERROR_INT("no output requested", __func__, 1);
2395 if (filtertype != L_SOBEL_EDGE && filtertype != L_TWO_SIDED_EDGE)
2396 return ERROR_INT("invalid filter type", __func__, 1);
2397
2398 /* Get the thresholded edge pixels. These are the ones
2399 * that have values in pixs near the local optimal fg/bg threshold. */
2400 if (filtertype == L_SOBEL_EDGE)
2401 pixe = pixSobelEdgeFilter(pixs, L_VERTICAL_EDGES);
2402 else /* L_TWO_SIDED_EDGE */
2403 pixe = pixTwoSidedEdgeFilter(pixs, L_VERTICAL_EDGES);
2404 pixet = pixThresholdToBinary(pixe, edgethresh);
2405 pixInvert(pixet, pixet);
2406
2407 /* Build a seed image whose only nonzero values are those
2408 * values of pixs corresponding to pixels in the fg of pixet. */
2409 pixsd = pixCreateTemplate(pixs);
2410 pixCombineMasked(pixsd, pixs, pixet);
2411
2412 /* Spread the seed and optionally smooth to reduce noise */
2413 pixg1 = pixSeedspread(pixsd, 4);
2414 pixg2 = pixBlockconv(pixg1, smoothx, smoothy);
2415
2416 /* Optionally do a gamma enhancement */
2417 pixth = pixGammaTRC(NULL, pixg2, gamma, minval, maxval);
2418
2419 /* Do the mapping and thresholding */
2420 if (ppixd) {
2421 *ppixd = pixApplyVariableGrayMap(pixs, pixth, targetthresh);
2422 if (ppixb)
2423 *ppixb = pixThresholdToBinary(*ppixd, targetthresh);
2424 }
2425 else if (ppixb)
2426 *ppixb = pixVarThresholdToBinary(pixs, pixth);
2427
2428 if (ppixth)
2429 *ppixth = pixth;
2430 else
2431 pixDestroy(&pixth);
2432
2433 pixDestroy(&pixe);
2434 pixDestroy(&pixet);
2435 pixDestroy(&pixsd);
2436 pixDestroy(&pixg1);
2437 pixDestroy(&pixg2);
2438 return 0;
2439}
2440
2441
2442/*------------------------------------------------------------------*
2443 * Adaptive background normalization (flexible adaptaption) *
2444 *------------------------------------------------------------------*/
2474PIX *
2476 l_int32 sx,
2477 l_int32 sy,
2478 l_int32 smoothx,
2479 l_int32 smoothy,
2480 l_int32 delta)
2481{
2482l_float32 scalex, scaley;
2483PIX *pixt, *pixsd, *pixmin, *pixbg, *pixbgi, *pixd;
2484
2485 if (!pixs || pixGetDepth(pixs) != 8)
2486 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, NULL);
2487 if (pixGetColormap(pixs))
2488 return (PIX *)ERROR_PTR("pixs is colormapped", __func__, NULL);
2489 if (sx < 3 || sy < 3)
2490 return (PIX *)ERROR_PTR("sx and/or sy less than 3", __func__, NULL);
2491 if (sx > 10 || sy > 10)
2492 return (PIX *)ERROR_PTR("sx and/or sy exceed 10", __func__, NULL);
2493 if (smoothx < 1 || smoothy < 1)
2494 return (PIX *)ERROR_PTR("smooth params less than 1", __func__, NULL);
2495 if (smoothx > 3 || smoothy > 3)
2496 return (PIX *)ERROR_PTR("smooth params exceed 3", __func__, NULL);
2497
2498 /* Generate the bg estimate using smoothed average with subsampling */
2499 scalex = 1. / (l_float32)sx;
2500 scaley = 1. / (l_float32)sy;
2501 pixt = pixScaleSmooth(pixs, scalex, scaley);
2502
2503 /* Do basin filling on the bg estimate if requested */
2504 if (delta <= 0)
2505 pixsd = pixClone(pixt);
2506 else {
2507 pixLocalExtrema(pixt, 0, 0, &pixmin, NULL);
2508 pixsd = pixSeedfillGrayBasin(pixmin, pixt, delta, 4);
2509 pixDestroy(&pixmin);
2510 }
2511 pixbg = pixExtendByReplication(pixsd, 1, 1);
2512
2513 /* Map the bg to 200 */
2514 pixbgi = pixGetInvBackgroundMap(pixbg, 200, smoothx, smoothy);
2515 pixd = pixApplyInvBackgroundGrayMap(pixs, pixbgi, sx, sy);
2516
2517 pixDestroy(&pixt);
2518 pixDestroy(&pixsd);
2519 pixDestroy(&pixbg);
2520 pixDestroy(&pixbgi);
2521 return pixd;
2522}
2523
2524
2525/*------------------------------------------------------------------*
2526 * Adaptive contrast normalization *
2527 *------------------------------------------------------------------*/
2567PIX *
2569 PIX *pixs,
2570 l_int32 sx,
2571 l_int32 sy,
2572 l_int32 mindiff,
2573 l_int32 smoothx,
2574 l_int32 smoothy)
2575{
2576PIX *pixmin, *pixmax;
2577
2578 if (!pixs || pixGetDepth(pixs) != 8)
2579 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, pixd);
2580 if (pixd && pixd != pixs)
2581 return (PIX *)ERROR_PTR("pixd not null or == pixs", __func__, pixd);
2582 if (pixGetColormap(pixs))
2583 return (PIX *)ERROR_PTR("pixs is colormapped", __func__, pixd);
2584 if (sx < 5 || sy < 5)
2585 return (PIX *)ERROR_PTR("sx and/or sy less than 5", __func__, pixd);
2586 if (smoothx < 0 || smoothy < 0)
2587 return (PIX *)ERROR_PTR("smooth params less than 0", __func__, pixd);
2588 if (smoothx > 8 || smoothy > 8)
2589 return (PIX *)ERROR_PTR("smooth params exceed 8", __func__, pixd);
2590
2591 /* Get the min and max pixel values in each tile, and represent
2592 * each value as a pixel in pixmin and pixmax, respectively. */
2593 pixMinMaxTiles(pixs, sx, sy, mindiff, smoothx, smoothy, &pixmin, &pixmax);
2594
2595 /* For each tile, do a linear expansion of the dynamic range
2596 * of pixels so that the min value is mapped to 0 and the
2597 * max value is mapped to 255. */
2598 pixd = pixLinearTRCTiled(pixd, pixs, sx, sy, pixmin, pixmax);
2599
2600 pixDestroy(&pixmin);
2601 pixDestroy(&pixmax);
2602 return pixd;
2603}
2604
2605
2625static l_ok
2627 l_int32 sx,
2628 l_int32 sy,
2629 l_int32 mindiff,
2630 l_int32 smoothx,
2631 l_int32 smoothy,
2632 PIX **ppixmin,
2633 PIX **ppixmax)
2634{
2635l_int32 w, h;
2636PIX *pixmin1, *pixmax1, *pixmin2, *pixmax2;
2637
2638 if (ppixmin) *ppixmin = NULL;
2639 if (ppixmax) *ppixmax = NULL;
2640 if (!ppixmin || !ppixmax)
2641 return ERROR_INT("&pixmin or &pixmax undefined", __func__, 1);
2642 if (!pixs || pixGetDepth(pixs) != 8)
2643 return ERROR_INT("pixs undefined or not 8 bpp", __func__, 1);
2644 if (pixGetColormap(pixs))
2645 return ERROR_INT("pixs is colormapped", __func__, 1);
2646 if (sx < 5 || sy < 5)
2647 return ERROR_INT("sx and/or sy less than 3", __func__, 1);
2648 if (smoothx < 0 || smoothy < 0)
2649 return ERROR_INT("smooth params less than 0", __func__, 1);
2650 if (smoothx > 5 || smoothy > 5)
2651 return ERROR_INT("smooth params exceed 5", __func__, 1);
2652
2653 /* Get the min and max values in each tile */
2654 pixmin1 = pixScaleGrayMinMax(pixs, sx, sy, L_CHOOSE_MIN);
2655 pixmax1 = pixScaleGrayMinMax(pixs, sx, sy, L_CHOOSE_MAX);
2656
2657 pixmin2 = pixExtendByReplication(pixmin1, 1, 1);
2658 pixmax2 = pixExtendByReplication(pixmax1, 1, 1);
2659 pixDestroy(&pixmin1);
2660 pixDestroy(&pixmax1);
2661
2662 /* Make sure no value is 0 */
2663 pixAddConstantGray(pixmin2, 1);
2664 pixAddConstantGray(pixmax2, 1);
2665
2666 /* Generate holes where the contrast is too small */
2667 pixSetLowContrast(pixmin2, pixmax2, mindiff);
2668
2669 /* Fill the holes (0 values) */
2670 pixGetDimensions(pixmin2, &w, &h, NULL);
2671 pixFillMapHoles(pixmin2, w, h, L_FILL_BLACK);
2672 pixFillMapHoles(pixmax2, w, h, L_FILL_BLACK);
2673
2674 /* Smooth if requested */
2675 if (smoothx > 0 || smoothy > 0) {
2676 smoothx = L_MIN(smoothx, (w - 1) / 2);
2677 smoothy = L_MIN(smoothy, (h - 1) / 2);
2678 *ppixmin = pixBlockconv(pixmin2, smoothx, smoothy);
2679 *ppixmax = pixBlockconv(pixmax2, smoothx, smoothy);
2680 }
2681 else {
2682 *ppixmin = pixClone(pixmin2);
2683 *ppixmax = pixClone(pixmax2);
2684 }
2685 pixCopyResolution(*ppixmin, pixs);
2686 pixCopyResolution(*ppixmax, pixs);
2687 pixDestroy(&pixmin2);
2688 pixDestroy(&pixmax2);
2689
2690 return 0;
2691}
2692
2693
2714static l_ok
2716 PIX *pixs2,
2717 l_int32 mindiff)
2718{
2719l_int32 i, j, w, h, d, wpl, val1, val2, found;
2720l_uint32 *data1, *data2, *line1, *line2;
2721
2722 if (!pixs1 || !pixs2)
2723 return ERROR_INT("pixs1 and pixs2 not both defined", __func__, 1);
2724 if (pixSizesEqual(pixs1, pixs2) == 0)
2725 return ERROR_INT("pixs1 and pixs2 not equal size", __func__, 1);
2726 pixGetDimensions(pixs1, &w, &h, &d);
2727 if (d != 8)
2728 return ERROR_INT("depth not 8 bpp", __func__, 1);
2729 if (mindiff > 254) return 0;
2730
2731 data1 = pixGetData(pixs1);
2732 data2 = pixGetData(pixs2);
2733 wpl = pixGetWpl(pixs1);
2734 found = 0; /* init to not finding any diffs >= mindiff */
2735 for (i = 0; i < h; i++) {
2736 line1 = data1 + i * wpl;
2737 line2 = data2 + i * wpl;
2738 for (j = 0; j < w; j++) {
2739 val1 = GET_DATA_BYTE(line1, j);
2740 val2 = GET_DATA_BYTE(line2, j);
2741 if (L_ABS(val1 - val2) >= mindiff) {
2742 found = 1;
2743 break;
2744 }
2745 }
2746 if (found) break;
2747 }
2748 if (!found) {
2749 L_WARNING("no pixel pair diffs as large as mindiff\n", __func__);
2750 pixClearAll(pixs1);
2751 pixClearAll(pixs2);
2752 return 1;
2753 }
2754
2755 for (i = 0; i < h; i++) {
2756 line1 = data1 + i * wpl;
2757 line2 = data2 + i * wpl;
2758 for (j = 0; j < w; j++) {
2759 val1 = GET_DATA_BYTE(line1, j);
2760 val2 = GET_DATA_BYTE(line2, j);
2761 if (L_ABS(val1 - val2) < mindiff) {
2762 SET_DATA_BYTE(line1, j, 0);
2763 SET_DATA_BYTE(line2, j, 0);
2764 }
2765 }
2766 }
2767
2768 return 0;
2769}
2770
2771
2795static PIX *
2797 PIX *pixs,
2798 l_int32 sx,
2799 l_int32 sy,
2800 PIX *pixmin,
2801 PIX *pixmax)
2802{
2803l_int32 i, j, k, m, w, h, wt, ht, wpl, wplt, xoff, yoff;
2804l_int32 minval, maxval, val, sval;
2805l_int32 *ia;
2806l_int32 **iaa;
2807l_uint32 *data, *datamin, *datamax, *line, *tline, *linemin, *linemax;
2808
2809 if (!pixs || pixGetDepth(pixs) != 8)
2810 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, pixd);
2811 if (pixd && pixd != pixs)
2812 return (PIX *)ERROR_PTR("pixd not null or == pixs", __func__, pixd);
2813 if (pixGetColormap(pixs))
2814 return (PIX *)ERROR_PTR("pixs is colormapped", __func__, pixd);
2815 if (!pixmin || !pixmax)
2816 return (PIX *)ERROR_PTR("pixmin & pixmax not defined", __func__, pixd);
2817 if (sx < 5 || sy < 5)
2818 return (PIX *)ERROR_PTR("sx and/or sy less than 5", __func__, pixd);
2819
2820 iaa = (l_int32 **)LEPT_CALLOC(256, sizeof(l_int32 *));
2821 if ((pixd = pixCopy(pixd, pixs)) == NULL) {
2822 LEPT_FREE(iaa);
2823 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
2824 }
2825 pixGetDimensions(pixd, &w, &h, NULL);
2826
2827 data = pixGetData(pixd);
2828 wpl = pixGetWpl(pixd);
2829 datamin = pixGetData(pixmin);
2830 datamax = pixGetData(pixmax);
2831 wplt = pixGetWpl(pixmin);
2832 pixGetDimensions(pixmin, &wt, &ht, NULL);
2833 for (i = 0; i < ht; i++) {
2834 line = data + sy * i * wpl;
2835 linemin = datamin + i * wplt;
2836 linemax = datamax + i * wplt;
2837 yoff = sy * i;
2838 for (j = 0; j < wt; j++) {
2839 xoff = sx * j;
2840 minval = GET_DATA_BYTE(linemin, j);
2841 maxval = GET_DATA_BYTE(linemax, j);
2842 if (maxval == minval) {
2843 L_ERROR("shouldn't happen! i,j = %d,%d, minval = %d\n",
2844 __func__, i, j, minval);
2845 continue;
2846 }
2847 if ((ia = iaaGetLinearTRC(iaa, maxval - minval)) == NULL) {
2848 L_ERROR("failure to make ia for j = %d!\n", __func__, j);
2849 continue;
2850 }
2851 for (k = 0; k < sy && yoff + k < h; k++) {
2852 tline = line + k * wpl;
2853 for (m = 0; m < sx && xoff + m < w; m++) {
2854 val = GET_DATA_BYTE(tline, xoff + m);
2855 sval = val - minval;
2856 sval = L_MAX(0, sval);
2857 SET_DATA_BYTE(tline, xoff + m, ia[sval]);
2858 }
2859 }
2860 }
2861 }
2862
2863 for (i = 0; i < 256; i++)
2864 LEPT_FREE(iaa[i]);
2865 LEPT_FREE(iaa);
2866 return pixd;
2867}
2868
2869
2879static l_int32 *
2880iaaGetLinearTRC(l_int32 **iaa,
2881 l_int32 diff)
2882{
2883l_int32 i;
2884l_int32 *ia;
2885l_float32 factor;
2886
2887 if (!iaa)
2888 return (l_int32 *)ERROR_PTR("iaa not defined", __func__, NULL);
2889
2890 if (iaa[diff] != NULL) /* already have it */
2891 return iaa[diff];
2892
2893 ia = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
2894 iaa[diff] = ia;
2895 if (diff == 0) { /* shouldn't happen */
2896 for (i = 0; i < 256; i++)
2897 ia[i] = 128;
2898 }
2899 else {
2900 factor = 255. / (l_float32)diff;
2901 for (i = 0; i < diff + 1; i++)
2902 ia[i] = (l_int32)(factor * i + 0.5);
2903 for (i = diff + 1; i < 256; i++)
2904 ia[i] = 255;
2905 }
2906
2907 return ia;
2908}
2909
2910
2911/*------------------------------------------------------------------*
2912 * Adaptive normalization with MinMax conversion of RGB to gray, *
2913 * contrast enhancement and optional 2x upscale binarization *
2914 *------------------------------------------------------------------*/
2938PIX *
2940 l_int32 contrast,
2941 l_int32 scalefactor)
2942{
2943PIX *pix1, *pix2, *pixd;
2944
2945 if (!pixs)
2946 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
2947 if (contrast < 1 || contrast > 10)
2948 return (PIX *)ERROR_PTR("contrast not in [1 ... 10]", __func__, NULL);
2949 if (scalefactor != 1 && scalefactor != 2)
2950 return (PIX *)ERROR_PTR("scalefactor not 1 or 2", __func__, NULL);
2951
2952 if (pixGetDepth(pixs) == 1) {
2953 pixd = pixCopy(NULL, pixs);
2954 } else {
2955 pix1 = pixConvertTo8MinMax(pixs);
2956 pix2 = pixBackgroundNormSimple(pix1, NULL, NULL);
2957 pixSelectiveContrastMod(pix2, contrast);
2958 if (scalefactor == 1)
2959 pixd = pixThresholdToBinary(pix2, 180);
2960 else /* scalefactor == 2 */
2961 pixd = pixScaleGray2xLIThresh(pix2, 180);
2962 pixDestroy(&pix1);
2963 pixDestroy(&pix2);
2964 }
2965 return pixd;
2966}
2967
2968
2982PIX *
2984{
2985 if (!pixs)
2986 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
2987
2988 l_int32 d = pixGetDepth(pixs);
2989 if (d == 1) {
2990 return pixConvert1To8(NULL, pixs, 255, 0);
2991 } else if (d == 2) {
2992 return pixConvert2To8(pixs, 0, 85, 170, 255, FALSE);
2993 } else if (d == 4) {
2994 return pixConvert4To8(pixs, FALSE);
2995 } else if (d == 8) {
2996 if (pixGetColormap(pixs) != NULL)
2997 return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
2998 else
2999 return pixCopy(NULL, pixs);
3000 } else if (d == 16) {
3001 return pixConvert16To8(pixs, L_MS_BYTE);
3002 } else if (d == 32) {
3003 return pixConvertRGBToGrayMinMax(pixs, L_CHOOSE_MIN);
3004 }
3005
3006 L_ERROR("Invalid depth d = %d\n", __func__, d);
3007 return NULL;
3008}
3009
3010
3033static l_ok
3035 l_int32 contrast)
3036{
3037 if (!pixs || pixGetDepth(pixs) != 8)
3038 return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
3039
3040 if (contrast == 1)
3041 pixGammaTRC(pixs, pixs, 2.0, 50, 200);
3042 else if (contrast == 2)
3043 pixGammaTRC(pixs, pixs, 1.8, 60, 200);
3044 else if (contrast == 3)
3045 pixGammaTRC(pixs, pixs, 1.6, 70, 200);
3046 else if (contrast == 4)
3047 pixGammaTRC(pixs, pixs, 1.4, 80, 200);
3048 else if (contrast == 5)
3049 pixGammaTRC(pixs, pixs, 1.2, 90, 200);
3050 else if (contrast == 6)
3051 pixGammaTRC(pixs, pixs, 1.0, 100, 200);
3052 else if (contrast == 7)
3053 pixGammaTRC(pixs, pixs, 0.85, 110, 200);
3054 else if (contrast == 8)
3055 pixGammaTRC(pixs, pixs, 0.7, 120, 200);
3056 else if (contrast == 9)
3057 pixGammaTRC(pixs, pixs, 0.6, 130, 200);
3058 else /* contrast == 10 */
3059 pixGammaTRC(pixs, pixs, 0.5, 140, 200);
3060
3061 return 0;
3062}
3063
l_ok pixGetBackgroundRGBMap(PIX *pixs, PIX *pixim, PIX *pixg, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, PIX **ppixmr, PIX **ppixmg, PIX **ppixmb)
pixGetBackgroundRGBMap()
Definition adaptmap.c:1046
PIX * pixApplyVariableGrayMap(PIX *pixs, PIX *pixg, l_int32 target)
pixApplyVariableGrayMap()
Definition adaptmap.c:2049
PIX * pixBackgroundNormMorph(PIX *pixs, PIX *pixim, l_int32 reduction, l_int32 size, l_int32 bgval)
pixBackgroundNormMorph()
Definition adaptmap.c:463
static l_ok pixSelectiveContrastMod(PIX *pixs, l_int32 contrast)
pixSelectiveContrastMod()
Definition adaptmap.c:3034
l_ok pixBackgroundNormRGBArraysMorph(PIX *pixs, PIX *pixim, l_int32 reduction, l_int32 size, l_int32 bgval, PIX **ppixr, PIX **ppixg, PIX **ppixb)
pixBackgroundNormRGBArraysMorph()
Definition adaptmap.c:791
static const l_int32 DefaultFgThreshold
Definition adaptmap.c:156
static const l_int32 DefaultTileHeight
Definition adaptmap.c:155
static l_int32 * iaaGetLinearTRC(l_int32 **iaa, l_int32 diff)
iaaGetLinearTRC()
Definition adaptmap.c:2880
PIX * pixBackgroundNormFlex(PIX *pixs, l_int32 sx, l_int32 sy, l_int32 smoothx, l_int32 smoothy, l_int32 delta)
pixBackgroundNormFlex()
Definition adaptmap.c:2475
l_ok pixBackgroundNormRGBArrays(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, PIX **ppixr, PIX **ppixg, PIX **ppixb)
pixBackgroundNormRGBArrays()
Definition adaptmap.c:647
l_ok pixSmoothConnectedRegions(PIX *pixs, PIX *pixm, l_int32 factor)
pixSmoothConnectedRegions()
Definition adaptmap.c:1631
PIX * pixApplyInvBackgroundGrayMap(PIX *pixs, PIX *pixm, l_int32 sx, l_int32 sy)
pixApplyInvBackgroundGrayMap()
Definition adaptmap.c:1889
PIX * pixGlobalNormNoSatRGB(PIX *pixd, PIX *pixs, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 factor, l_float32 rank)
pixGlobalNormNoSatRGB()
Definition adaptmap.c:2271
l_ok pixBackgroundNormGrayArrayMorph(PIX *pixs, PIX *pixim, l_int32 reduction, l_int32 size, l_int32 bgval, PIX **ppixd)
pixBackgroundNormGrayArrayMorph()
Definition adaptmap.c:728
l_ok pixGetBackgroundGrayMap(PIX *pixs, PIX *pixim, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, PIX **ppixd)
pixGetBackgroundGrayMap()
Definition adaptmap.c:866
PIX * pixApplyInvBackgroundRGBMap(PIX *pixs, PIX *pixmr, PIX *pixmg, PIX *pixmb, l_int32 sx, l_int32 sy)
pixApplyInvBackgroundRGBMap()
Definition adaptmap.c:1953
l_ok pixGetBackgroundRGBMapMorph(PIX *pixs, PIX *pixim, l_int32 reduction, l_int32 size, PIX **ppixmr, PIX **ppixmg, PIX **ppixmb)
pixGetBackgroundRGBMapMorph()
Definition adaptmap.c:1318
PIX * pixBackgroundNormSimple(PIX *pixs, PIX *pixim, PIX *pixg)
pixBackgroundNormSimple()
Definition adaptmap.c:253
static l_int32 pixSetLowContrast(PIX *pixs1, PIX *pixs2, l_int32 mindiff)
pixSetLowContrast()
Definition adaptmap.c:2715
PIX * pixExtendByReplication(PIX *pixs, l_int32 addw, l_int32 addh)
pixExtendByReplication()
Definition adaptmap.c:1570
PIX * pixGlobalNormRGB(PIX *pixd, PIX *pixs, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 mapval)
pixGlobalNormRGB()
Definition adaptmap.c:2164
static const l_int32 DefaultBgVal
Definition adaptmap.c:158
l_ok pixBackgroundNormGrayArray(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, PIX **ppixd)
pixBackgroundNormGrayArray()
Definition adaptmap.c:572
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:2568
static const l_int32 DefaultTileWidth
Definition adaptmap.c:154
static const l_int32 DefaultMinCount
Definition adaptmap.c:157
PIX * pixConvertTo8MinMax(PIX *pixs)
pixConvertTo8MinMax()
Definition adaptmap.c:2983
l_ok pixFillMapHoles(PIX *pix, l_int32 nx, l_int32 ny, l_int32 filltype)
pixFillMapHoles()
Definition adaptmap.c:1470
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:328
l_ok pixGetBackgroundGrayMapMorph(PIX *pixs, PIX *pixim, l_int32 reduction, l_int32 size, PIX **ppixm)
pixGetBackgroundGrayMapMorph()
Definition adaptmap.c:1229
PIX * pixGetInvBackgroundMap(PIX *pixs, l_int32 bgval, l_int32 smoothx, l_int32 smoothy)
pixGetInvBackgroundMap()
Definition adaptmap.c:1828
static l_int32 pixMinMaxTiles(PIX *pixs, l_int32 sx, l_int32 sy, l_int32 mindiff, l_int32 smoothx, l_int32 smoothy, PIX **ppixmin, PIX **ppixmax)
pixMinMaxTiles()
Definition adaptmap.c:2626
static const l_int32 DefaultXSmoothSize
Definition adaptmap.c:159
PIX * pixBackgroundNormTo1MinMax(PIX *pixs, l_int32 contrast, l_int32 scalefactor)
pixBackgroundNormTo1MinMax()
Definition adaptmap.c:2939
static PIX * pixLinearTRCTiled(PIX *pixd, PIX *pixs, l_int32 sx, l_int32 sy, PIX *pixmin, PIX *pixmax)
pixLinearTRCTiled()
Definition adaptmap.c:2796
PIX * pixCleanBackgroundToWhite(PIX *pixs, PIX *pixim, PIX *pixg, l_float32 gamma, l_int32 blackval, l_int32 whiteval)
pixCleanBackgroundToWhite()
Definition adaptmap.c:204
static const l_int32 DefaultYSmoothSize
Definition adaptmap.c:160
l_ok pixThresholdSpreadNorm(PIX *pixs, l_int32 filtertype, l_int32 edgethresh, l_int32 smoothx, l_int32 smoothy, l_float32 gamma, l_int32 minval, l_int32 maxval, l_int32 targetthresh, PIX **ppixth, PIX **ppixb, PIX **ppixd)
pixThresholdSpreadNorm()
Definition adaptmap.c:2371
#define SET_DATA_TWO_BYTES(pdata, n, val)
#define GET_DATA_BYTE(pdata, n)
#define SET_DATA_BYTE(pdata, n, val)
#define GET_DATA_BIT(pdata, n)
@ COLOR_BLUE
Definition pix.h:330
@ COLOR_RED
Definition pix.h:328
@ COLOR_GREEN
Definition pix.h:329
@ REMOVE_CMAP_TO_GRAYSCALE
Definition pix.h:381
@ L_CLONE
Definition pix.h:506
@ L_TWO_SIDED_EDGE
Definition pix.h:964
@ L_SOBEL_EDGE
Definition pix.h:963
@ L_MS_BYTE
Definition pix.h:642
#define PIX_SRC
Definition pix.h:444
@ L_MEAN_ABSVAL
Definition pix.h:761
@ L_VERTICAL_EDGES
Definition pix.h:797
@ L_FILL_WHITE
Definition pix.h:690
@ L_FILL_BLACK
Definition pix.h:691