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