Leptonica 1.85.0
Image processing and image analysis suite
Loading...
Searching...
No Matches
pix5.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
106#ifdef HAVE_CONFIG_H
107#include <config_auto.h>
108#endif /* HAVE_CONFIG_H */
109
110#include <string.h>
111#include <math.h>
112#include "allheaders.h"
113
114static const l_uint32 rmask32[] = {0x0,
115 0x00000001, 0x00000003, 0x00000007, 0x0000000f,
116 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
117 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff,
118 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
119 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff,
120 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff,
121 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff,
122 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff};
123
124#ifndef NO_CONSOLE_IO
125#define DEBUG_EDGES 0
126#endif /* ~NO_CONSOLE_IO */
127
128
129/*-------------------------------------------------------------*
130 * Measurement of properties *
131 *-------------------------------------------------------------*/
140l_ok
142 NUMA **pnaw,
143 NUMA **pnah)
144{
145l_int32 i, n, w, h;
146PIX *pixt;
147
148 if (pnaw) *pnaw = NULL;
149 if (pnah) *pnah = NULL;
150 if (!pnaw && !pnah)
151 return ERROR_INT("no output requested", __func__, 1);
152 if (!pixa)
153 return ERROR_INT("pixa not defined", __func__, 1);
154
155 n = pixaGetCount(pixa);
156 if (pnaw) *pnaw = numaCreate(n);
157 if (pnah) *pnah = numaCreate(n);
158 for (i = 0; i < n; i++) {
159 pixt = pixaGetPix(pixa, i, L_CLONE);
160 pixGetDimensions(pixt, &w, &h, NULL);
161 if (pnaw)
162 numaAddNumber(*pnaw, w);
163 if (pnah)
164 numaAddNumber(*pnah, h);
165 pixDestroy(&pixt);
166 }
167 return 0;
168}
169
170
188l_ok
190 l_int32 *tab,
191 l_float32 *pfract)
192{
193l_int32 *tab8;
194l_int32 nfg, nbound;
195PIX *pixt;
196
197 if (!pfract)
198 return ERROR_INT("&fract not defined", __func__, 1);
199 *pfract = 0.0;
200 if (!pixs || pixGetDepth(pixs) != 1)
201 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
202
203 if (!tab)
204 tab8 = makePixelSumTab8();
205 else
206 tab8 = tab;
207
208 pixt = pixErodeBrick(NULL, pixs, 3, 3);
209 pixCountPixels(pixt, &nfg, tab8);
210 if (nfg == 0) {
211 pixDestroy(&pixt);
212 if (!tab) LEPT_FREE(tab8);
213 return 0;
214 }
215 pixXor(pixt, pixt, pixs);
216 pixCountPixels(pixt, &nbound, tab8);
217 *pfract = (l_float32)nfg / (l_float32)nbound;
218 pixDestroy(&pixt);
219
220 if (!tab) LEPT_FREE(tab8);
221 return 0;
222}
223
224
237NUMA *
239{
240l_int32 i, n;
241l_int32 *tab;
242l_float32 fract;
243NUMA *na;
244PIX *pixt;
245
246 if (!pixa)
247 return (NUMA *)ERROR_PTR("pixa not defined", __func__, NULL);
248
249 n = pixaGetCount(pixa);
250 na = numaCreate(n);
251 tab = makePixelSumTab8();
252 for (i = 0; i < n; i++) {
253 pixt = pixaGetPix(pixa, i, L_CLONE);
254 pixFindPerimToAreaRatio(pixt, tab, &fract);
255 numaAddNumber(na, fract);
256 pixDestroy(&pixt);
257 }
258 LEPT_FREE(tab);
259 return na;
260}
261
262
285l_ok
287 l_int32 *tab,
288 l_float32 *pfract)
289{
290l_int32 *tab8;
291l_int32 nfg, nbound;
292PIX *pixt;
293
294 if (!pfract)
295 return ERROR_INT("&fract not defined", __func__, 1);
296 *pfract = 0.0;
297 if (!pixs || pixGetDepth(pixs) != 1)
298 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
299
300 if (!tab)
301 tab8 = makePixelSumTab8();
302 else
303 tab8 = tab;
304
305 pixCountPixels(pixs, &nfg, tab8);
306 if (nfg == 0) {
307 if (!tab) LEPT_FREE(tab8);
308 return 0;
309 }
310 pixt = pixErodeBrick(NULL, pixs, 3, 3);
311 pixXor(pixt, pixt, pixs);
312 pixCountPixels(pixt, &nbound, tab8);
313 *pfract = (l_float32)nbound / (l_float32)nfg;
314 pixDestroy(&pixt);
315
316 if (!tab) LEPT_FREE(tab8);
317 return 0;
318}
319
320
337NUMA *
339{
340l_int32 i, n;
341l_int32 *tab;
342l_float32 ratio;
343NUMA *na;
344PIX *pixt;
345
346 if (!pixa)
347 return (NUMA *)ERROR_PTR("pixa not defined", __func__, NULL);
348
349 n = pixaGetCount(pixa);
350 na = numaCreate(n);
351 tab = makePixelSumTab8();
352 for (i = 0; i < n; i++) {
353 pixt = pixaGetPix(pixa, i, L_CLONE);
354 pixFindPerimSizeRatio(pixt, tab, &ratio);
355 numaAddNumber(na, ratio);
356 pixDestroy(&pixt);
357 }
358 LEPT_FREE(tab);
359 return na;
360}
361
362
385l_ok
387 l_int32 *tab,
388 l_float32 *pratio)
389{
390l_int32 *tab8;
391l_int32 w, h, nbound;
392PIX *pixt;
393
394 if (!pratio)
395 return ERROR_INT("&ratio not defined", __func__, 1);
396 *pratio = 0.0;
397 if (!pixs || pixGetDepth(pixs) != 1)
398 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
399
400 if (!tab)
401 tab8 = makePixelSumTab8();
402 else
403 tab8 = tab;
404
405 pixt = pixErodeBrick(NULL, pixs, 3, 3);
406 pixXor(pixt, pixt, pixs);
407 pixCountPixels(pixt, &nbound, tab8);
408 pixGetDimensions(pixs, &w, &h, NULL);
409 *pratio = (0.5f * nbound) / (l_float32)(w + h);
410 pixDestroy(&pixt);
411
412 if (!tab) LEPT_FREE(tab8);
413 return 0;
414}
415
416
429NUMA *
431{
432l_int32 i, n;
433l_int32 *tab;
434l_float32 fract;
435NUMA *na;
436PIX *pixt;
437
438 if (!pixa)
439 return (NUMA *)ERROR_PTR("pixa not defined", __func__, NULL);
440
441 n = pixaGetCount(pixa);
442 na = numaCreate(n);
443 tab = makePixelSumTab8();
444 for (i = 0; i < n; i++) {
445 pixt = pixaGetPix(pixa, i, L_CLONE);
446 pixFindAreaFraction(pixt, tab, &fract);
447 numaAddNumber(na, fract);
448 pixDestroy(&pixt);
449 }
450 LEPT_FREE(tab);
451 return na;
452}
453
454
470l_ok
472 l_int32 *tab,
473 l_float32 *pfract)
474{
475l_int32 w, h, sum;
476l_int32 *tab8;
477
478 if (!pfract)
479 return ERROR_INT("&fract not defined", __func__, 1);
480 *pfract = 0.0;
481 if (!pixs || pixGetDepth(pixs) != 1)
482 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
483
484 if (!tab)
485 tab8 = makePixelSumTab8();
486 else
487 tab8 = tab;
488 pixGetDimensions(pixs, &w, &h, NULL);
489 pixCountPixels(pixs, &sum, tab8);
490 *pfract = (l_float32)sum / (l_float32)(w * h);
491
492 if (!tab) LEPT_FREE(tab8);
493 return 0;
494}
495
496
516NUMA *
518 PIX *pixm,
519 l_int32 debug)
520{
521l_int32 i, n, full;
522l_int32 *tab;
523l_float32 fract;
524BOX *box;
525NUMA *na;
526PIX *pix;
527
528 if (!pixa)
529 return (NUMA *)ERROR_PTR("pixa not defined", __func__, NULL);
530 if (!pixm || pixGetDepth(pixm) != 1)
531 return (NUMA *)ERROR_PTR("pixm undefined or not 1 bpp", __func__, NULL);
532
533 n = pixaGetCount(pixa);
534 na = numaCreate(n);
535 tab = makePixelSumTab8();
536 pixaIsFull(pixa, NULL, &full); /* check boxa */
537 box = NULL;
538 for (i = 0; i < n; i++) {
539 pix = pixaGetPix(pixa, i, L_CLONE);
540 if (full)
541 box = pixaGetBox(pixa, i, L_CLONE);
542 pixFindAreaFractionMasked(pix, box, pixm, tab, &fract);
543 numaAddNumber(na, fract);
544 boxDestroy(&box);
545 pixDestroy(&pix);
546 }
547 LEPT_FREE(tab);
548
549 if (debug) {
550 l_int32 w, h;
551 PIX *pix1, *pix2;
552 pixGetDimensions(pixm, &w, &h, NULL);
553 pix1 = pixaDisplay(pixa, w, h); /* recover original image */
554 pix2 = pixCreate(w, h, 8); /* make an 8 bpp white image ... */
555 pixSetColormap(pix2, pixcmapCreate(8)); /* that's cmapped ... */
556 pixSetBlackOrWhite(pix2, L_SET_WHITE); /* and init to white */
557 pixSetMaskedCmap(pix2, pix1, 0, 0, 255, 0, 0); /* color all fg red */
558 pixRasterop(pix1, 0, 0, w, h, PIX_MASK, pixm, 0, 0);
559 pixSetMaskedCmap(pix2, pix1, 0, 0, 0, 255, 0); /* turn masked green */
560 pixDisplay(pix2, 100, 100);
561 pixDestroy(&pix1);
562 pixDestroy(&pix2);
563 }
564
565 return na;
566}
567
568
593l_ok
595 BOX *box,
596 PIX *pixm,
597 l_int32 *tab,
598 l_float32 *pfract)
599{
600l_int32 x, y, w, h, sum, masksum;
601l_int32 *tab8;
602PIX *pix1;
603
604 if (!pfract)
605 return ERROR_INT("&fract not defined", __func__, 1);
606 *pfract = 0.0;
607 if (!pixs || pixGetDepth(pixs) != 1)
608 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
609 if (!pixm || pixGetDepth(pixm) != 1)
610 return ERROR_INT("pixm not defined or not 1 bpp", __func__, 1);
611
612 if (!tab)
613 tab8 = makePixelSumTab8();
614 else
615 tab8 = tab;
616 x = y = 0;
617 if (box)
618 boxGetGeometry(box, &x, &y, NULL, NULL);
619 pixGetDimensions(pixs, &w, &h, NULL);
620
621 pix1 = pixCopy(NULL, pixs);
622 pixRasterop(pix1, 0, 0, w, h, PIX_MASK, pixm, x, y);
623 pixCountPixels(pixs, &sum, tab8);
624 if (sum == 0) {
625 pixDestroy(&pix1);
626 if (!tab) LEPT_FREE(tab8);
627 return 0;
628 }
629 pixCountPixels(pix1, &masksum, tab8);
630 *pfract = (l_float32)masksum / (l_float32)sum;
631
632 if (!tab) LEPT_FREE(tab8);
633 pixDestroy(&pix1);
634 return 0;
635}
636
637
650NUMA *
652{
653l_int32 i, n, w, h;
654NUMA *na;
655PIX *pixt;
656
657 if (!pixa)
658 return (NUMA *)ERROR_PTR("pixa not defined", __func__, NULL);
659
660 n = pixaGetCount(pixa);
661 na = numaCreate(n);
662 for (i = 0; i < n; i++) {
663 pixt = pixaGetPix(pixa, i, L_CLONE);
664 pixGetDimensions(pixt, &w, &h, NULL);
665 numaAddNumber(na, (l_float32)w / (l_float32)h);
666 pixDestroy(&pixt);
667 }
668 return na;
669}
670
671
684NUMA *
686{
687l_int32 i, n, w, h;
688NUMA *na;
689PIX *pixt;
690
691 if (!pixa)
692 return (NUMA *)ERROR_PTR("pixa not defined", __func__, NULL);
693
694 n = pixaGetCount(pixa);
695 na = numaCreate(n);
696 for (i = 0; i < n; i++) {
697 pixt = pixaGetPix(pixa, i, L_CLONE);
698 pixGetDimensions(pixt, &w, &h, NULL);
699 numaAddNumber(na, w * h);
700 pixDestroy(&pixt);
701 }
702 return na;
703}
704
705
722l_ok
724 PIX *pixs2,
725 l_int32 x2,
726 l_int32 y2,
727 l_int32 *tab,
728 l_float32 *pratio,
729 l_int32 *pnoverlap)
730{
731l_int32 *tab8;
732l_int32 w, h, nintersect, nunion;
733PIX *pixt;
734
735 if (pnoverlap) *pnoverlap = 0;
736 if (!pratio)
737 return ERROR_INT("&ratio not defined", __func__, 1);
738 *pratio = 0.0;
739 if (!pixs1 || pixGetDepth(pixs1) != 1)
740 return ERROR_INT("pixs1 not defined or not 1 bpp", __func__, 1);
741 if (!pixs2 || pixGetDepth(pixs2) != 1)
742 return ERROR_INT("pixs2 not defined or not 1 bpp", __func__, 1);
743
744 if (!tab)
745 tab8 = makePixelSumTab8();
746 else
747 tab8 = tab;
748
749 pixGetDimensions(pixs2, &w, &h, NULL);
750 pixt = pixCopy(NULL, pixs1);
751 pixRasterop(pixt, x2, y2, w, h, PIX_MASK, pixs2, 0, 0); /* AND */
752 pixCountPixels(pixt, &nintersect, tab8);
753 if (pnoverlap)
754 *pnoverlap = nintersect;
755 pixCopy(pixt, pixs1);
756 pixRasterop(pixt, x2, y2, w, h, PIX_PAINT, pixs2, 0, 0); /* OR */
757 pixCountPixels(pixt, &nunion, tab8);
758 if (!tab) LEPT_FREE(tab8);
759 pixDestroy(&pixt);
760
761 if (nunion > 0)
762 *pratio = (l_float32)nintersect / (l_float32)nunion;
763 return 0;
764}
765
766
787BOXA *
789 l_int32 dist,
790 l_int32 minw,
791 l_int32 minh)
792{
793l_int32 w, h, i, n, conforms;
794BOX *box;
795BOXA *boxa, *boxad;
796PIX *pix;
797PIXA *pixa;
798
799 if (!pixs || pixGetDepth(pixs) != 1)
800 return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
801 if (dist < 0)
802 return (BOXA *)ERROR_PTR("dist must be >= 0", __func__, NULL);
803 if (minw <= 2 * dist && minh <= 2 * dist)
804 return (BOXA *)ERROR_PTR("invalid parameters", __func__, NULL);
805
806 boxa = pixConnComp(pixs, &pixa, 8);
807 boxad = boxaCreate(0);
808 n = pixaGetCount(pixa);
809 for (i = 0; i < n; i++) {
810 pix = pixaGetPix(pixa, i, L_CLONE);
811 pixGetDimensions(pix, &w, &h, NULL);
812 if (w < minw || h < minh) {
813 pixDestroy(&pix);
814 continue;
815 }
816 pixConformsToRectangle(pix, NULL, dist, &conforms);
817 if (conforms) {
818 box = boxaGetBox(boxa, i, L_COPY);
819 boxaAddBox(boxad, box, L_INSERT);
820 }
821 pixDestroy(&pix);
822 }
823 boxaDestroy(&boxa);
824 pixaDestroy(&pixa);
825 return boxad;
826}
827
828
865l_ok
867 BOX *box,
868 l_int32 dist,
869 l_int32 *pconforms)
870{
871l_int32 w, h, empty;
872PIX *pix1, *pix2;
873
874 if (!pconforms)
875 return ERROR_INT("&conforms not defined", __func__, 1);
876 *pconforms = 0;
877 if (!pixs || pixGetDepth(pixs) != 1)
878 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
879 if (dist < 0)
880 return ERROR_INT("dist must be >= 0", __func__, 1);
881 pixGetDimensions(pixs, &w, &h, NULL);
882 if (w <= 2 * dist || h <= 2 * dist) {
883 L_WARNING("automatic conformation: distance too large\n", __func__);
884 *pconforms = 1;
885 return 0;
886 }
887
888 /* Extract the region, if necessary */
889 if (box)
890 pix1 = pixClipRectangle(pixs, box, NULL);
891 else
892 pix1 = pixCopy(NULL, pixs);
893
894 /* Invert and fill from the boundary into the interior.
895 * Because we're considering the connected component in an
896 * 8-connected sense, we do the background filling as 4 c.c. */
897 pixInvert(pix1, pix1);
898 pix2 = pixExtractBorderConnComps(pix1, 4);
899
900 /* Mask out all pixels within a distance %dist from the box
901 * boundary. Any remaining pixels are from filling that goes
902 * more than %dist from the boundary. If no pixels remain,
903 * the component conforms to the bounding rectangle within
904 * a distance %dist. */
905 pixSetOrClearBorder(pix2, dist, dist, dist, dist, PIX_CLR);
906 pixZero(pix2, &empty);
907 pixDestroy(&pix1);
908 pixDestroy(&pix2);
909 *pconforms = (empty) ? 1 : 0;
910 return 0;
911}
912
913
914/*-----------------------------------------------------------------------*
915 * Extract rectangular regions *
916 *-----------------------------------------------------------------------*/
935PIX *
937 BOXA *boxa)
938{
939l_int32 w, h;
940PIX *pix1;
941PIXA *pixa1;
942
943 if (!pixs)
944 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
945 if (!boxa)
946 return (PIX *)ERROR_PTR("boxa not defined", __func__, NULL);
947
948 if ((pixa1 = pixClipRectangles(pixs, boxa)) == NULL)
949 return (PIX *)ERROR_PTR("pixa1 not made", __func__, NULL);
950 pixGetDimensions(pixs, &w, &h, NULL);
951 pix1 = pixaDisplay(pixa1, w, h);
952 pixaDestroy(&pixa1);
953 return pix1;
954}
955
956
970PIXA *
972 BOXA *boxa)
973{
974l_int32 i, n;
975BOX *box, *boxc;
976PIX *pix;
977PIXA *pixa;
978
979 if (!pixs)
980 return (PIXA *)ERROR_PTR("pixs not defined", __func__, NULL);
981 if (!boxa)
982 return (PIXA *)ERROR_PTR("boxa not defined", __func__, NULL);
983
984 n = boxaGetCount(boxa);
985 pixa = pixaCreate(n);
986 for (i = 0; i < n; i++) {
987 box = boxaGetBox(boxa, i, L_CLONE);
988 pix = pixClipRectangle(pixs, box, &boxc);
989 pixaAddPix(pixa, pix, L_INSERT);
990 pixaAddBox(pixa, boxc, L_INSERT);
991 boxDestroy(&box);
992 }
993
994 return pixa;
995}
996
997
1034PIX *
1036 BOX *box,
1037 BOX **pboxc)
1038{
1039l_int32 w, h, d, bx, by, bw, bh;
1040BOX *boxc;
1041PIX *pixd;
1042
1043 if (pboxc) *pboxc = NULL;
1044 if (!pixs)
1045 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1046 if (!box)
1047 return (PIX *)ERROR_PTR("box not defined", __func__, NULL);
1048
1049 /* Clip the input box to the pix */
1050 pixGetDimensions(pixs, &w, &h, &d);
1051 if ((boxc = boxClipToRectangle(box, w, h)) == NULL) {
1052 L_WARNING("box doesn't overlap pix\n", __func__);
1053 return NULL;
1054 }
1055 boxGetGeometry(boxc, &bx, &by, &bw, &bh);
1056
1057 /* Extract the block */
1058 if ((pixd = pixCreate(bw, bh, d)) == NULL) {
1059 boxDestroy(&boxc);
1060 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1061 }
1062 pixCopyResolution(pixd, pixs);
1063 pixCopyColormap(pixd, pixs);
1064 pixCopyText(pixd, pixs);
1065 pixRasterop(pixd, 0, 0, bw, bh, PIX_SRC, pixs, bx, by);
1066
1067 if (pboxc)
1068 *pboxc = boxc;
1069 else
1070 boxDestroy(&boxc);
1071
1072 return pixd;
1073}
1074
1075
1095PIX *
1097 BOX *box,
1098 l_int32 maxbord,
1099 BOX **pboxn)
1100{
1101l_int32 w, h, bx, by, bw, bh, bord;
1102BOX *box1;
1103PIX *pix1;
1104
1105 if (!pboxn)
1106 return (PIX *)ERROR_PTR("&boxn not defined", __func__, NULL);
1107 *pboxn = NULL;
1108 if (!pixs)
1109 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1110 if (!box)
1111 return (PIX *)ERROR_PTR("box not defined", __func__, NULL);
1112
1113 /* Determine the border width */
1114 pixGetDimensions(pixs, &w, &h, NULL);
1115 boxGetGeometry(box, &bx, &by, &bw, &bh);
1116 bord = L_MIN(bx, by);
1117 bord = L_MIN(bord, w - bx - bw);
1118 bord = L_MIN(bord, h - by - bh);
1119 bord = L_MIN(bord, maxbord);
1120
1121 if (bord <= 0) { /* standard clipping */
1122 pix1 = pixClipRectangle(pixs, box, NULL);
1123 pixGetDimensions(pix1, &w, &h, NULL);
1124 *pboxn = boxCreate(0, 0, w, h);
1125 return pix1;
1126 }
1127
1128 /* There is a positive border */
1129 box1 = boxAdjustSides(NULL, box, -bord, bord, -bord, bord);
1130 pix1 = pixClipRectangle(pixs, box1, NULL);
1131 boxDestroy(&box1);
1132 *pboxn = boxCreate(bord, bord, bw, bh);
1133 return pix1;
1134}
1135
1136
1166PIX *
1168 PIX *pixm,
1169 l_int32 x,
1170 l_int32 y,
1171 l_uint32 outval)
1172{
1173l_int32 wm, hm, index, rval, gval, bval;
1174l_uint32 pixel;
1175BOX *box;
1176PIX *pixmi, *pixd;
1177PIXCMAP *cmap;
1178
1179 if (!pixs)
1180 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1181 if (!pixm || pixGetDepth(pixm) != 1)
1182 return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", __func__, NULL);
1183
1184 /* Clip out the region specified by pixm and (x,y) */
1185 pixGetDimensions(pixm, &wm, &hm, NULL);
1186 box = boxCreate(x, y, wm, hm);
1187 pixd = pixClipRectangle(pixs, box, NULL);
1188
1189 /* Paint 'outval' (or something close to it if cmapped) through
1190 * the pixels not masked by pixm */
1191 cmap = pixGetColormap(pixd);
1192 pixmi = pixInvert(NULL, pixm);
1193 if (cmap) {
1194 extractRGBValues(outval, &rval, &gval, &bval);
1195 pixcmapGetNearestIndex(cmap, rval, gval, bval, &index);
1196 pixcmapGetColor(cmap, index, &rval, &gval, &bval);
1197 composeRGBPixel(rval, gval, bval, &pixel);
1198 pixPaintThroughMask(pixd, pixmi, 0, 0, pixel);
1199 } else {
1200 pixPaintThroughMask(pixd, pixmi, 0, 0, outval);
1201 }
1202
1203 boxDestroy(&box);
1204 pixDestroy(&pixmi);
1205 return pixd;
1206}
1207
1208
1226l_ok
1228 PIX *pixs2,
1229 PIX **ppixd1,
1230 PIX **ppixd2)
1231{
1232l_int32 w1, h1, w2, h2, w, h;
1233
1234 if (!ppixd1 || !ppixd2)
1235 return ERROR_INT("&pixd1 and &pixd2 not both defined", __func__, 1);
1236 *ppixd1 = *ppixd2 = NULL;
1237 if (!pixs1 || !pixs2)
1238 return ERROR_INT("pixs1 and pixs2 not defined", __func__, 1);
1239
1240 pixGetDimensions(pixs1, &w1, &h1, NULL);
1241 pixGetDimensions(pixs2, &w2, &h2, NULL);
1242 w = L_MIN(w1, w2);
1243 h = L_MIN(h1, h2);
1244
1245 *ppixd1 = pixCropToSize(pixs1, w, h);
1246 *ppixd2 = pixCropToSize(pixs2, w, h);
1247 if (*ppixd1 == NULL || *ppixd2 == NULL)
1248 return ERROR_INT("cropped image failure", __func__, 1);
1249 return 0;
1250}
1251
1252
1267PIX *
1269 l_int32 w,
1270 l_int32 h)
1271{
1272l_int32 ws, hs, wd, hd, d;
1273PIX *pixd;
1274
1275 if (!pixs)
1276 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1277
1278 pixGetDimensions(pixs, &ws, &hs, &d);
1279 if (ws <= w && hs <= h) /* no cropping necessary */
1280 return pixClone(pixs);
1281
1282 wd = L_MIN(ws, w);
1283 hd = L_MIN(hs, h);
1284 if ((pixd = pixCreate(wd, hd, d)) == NULL)
1285 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1286 pixCopyResolution(pixd, pixs);
1287 pixCopyColormap(pixd, pixs);
1288 pixCopyText(pixd, pixs);
1289 pixCopyInputFormat(pixd, pixs);
1290 pixRasterop(pixd, 0, 0, wd, hd, PIX_SRC, pixs, 0, 0);
1291 return pixd;
1292}
1293
1294
1319PIX *
1321 PIX *pixt,
1322 l_int32 w,
1323 l_int32 h)
1324{
1325l_int32 i, j, ws, hs, d;
1326PIX *pixd;
1327
1328 if (!pixs)
1329 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1330 if (!pixt && (w <= 0 || h <= 0))
1331 return (PIX *)ERROR_PTR("both w and h not > 0", __func__, NULL);
1332
1333 if (pixt) /* redefine w, h */
1334 pixGetDimensions(pixt, &w, &h, NULL);
1335 pixGetDimensions(pixs, &ws, &hs, &d);
1336 if (ws == w && hs == h)
1337 return pixCopy(NULL, pixs);
1338
1339 if ((pixd = pixCreate(w, h, d)) == NULL)
1340 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1341 pixCopyResolution(pixd, pixs);
1342 pixCopyColormap(pixd, pixs);
1343 pixCopyText(pixd, pixs);
1344 pixCopyInputFormat(pixd, pixs);
1345 pixRasterop(pixd, 0, 0, ws, hs, PIX_SRC, pixs, 0, 0);
1346 if (ws >= w && hs >= h)
1347 return pixd;
1348
1349 /* Replicate the last column and then the last row */
1350 if (ws < w) {
1351 for (j = ws; j < w; j++)
1352 pixRasterop(pixd, j, 0, 1, h, PIX_SRC, pixd, ws - 1, 0);
1353 }
1354 if (hs < h) {
1355 for (i = hs; i < h; i++)
1356 pixRasterop(pixd, 0, i, w, 1, PIX_SRC, pixd, 0, hs - 1);
1357 }
1358
1359 return pixd;
1360}
1361
1362
1363/*---------------------------------------------------------------------*
1364 * Select a connected component by size *
1365 *---------------------------------------------------------------------*/
1386PIX *
1387pixSelectComponentBySize(PIX *pixs,
1388 l_int32 rankorder,
1389 l_int32 type,
1390 l_int32 connectivity,
1391 BOX **pbox)
1392{
1393l_int32 n, empty, sorttype, index;
1394BOXA *boxa1;
1395NUMA *naindex;
1396PIX *pixd;
1397PIXA *pixa1, *pixa2;
1398
1399 if (pbox) *pbox = NULL;
1400 if (!pixs || pixGetDepth(pixs) != 1)
1401 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
1402 if (type == L_SELECT_BY_WIDTH)
1403 sorttype = L_SORT_BY_WIDTH;
1404 else if (type == L_SELECT_BY_HEIGHT)
1405 sorttype = L_SORT_BY_HEIGHT;
1406 else if (type == L_SELECT_BY_MAX_DIMENSION)
1407 sorttype = L_SORT_BY_MAX_DIMENSION;
1408 else if (type == L_SELECT_BY_AREA)
1409 sorttype = L_SORT_BY_AREA;
1410 else if (type == L_SELECT_BY_PERIMETER)
1411 sorttype = L_SORT_BY_PERIMETER;
1412 else
1413 return (PIX *)ERROR_PTR("invalid selection type", __func__, NULL);
1414 if (connectivity != 4 && connectivity != 8)
1415 return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
1416 pixZero(pixs, &empty);
1417 if (empty)
1418 return (PIX *)ERROR_PTR("no foreground pixels", __func__, NULL);
1419
1420 boxa1 = pixConnComp(pixs, &pixa1, connectivity);
1421 n = boxaGetCount(boxa1);
1422 if (rankorder < 0 || rankorder >= n)
1423 rankorder = n - 1; /* smallest */
1424 pixa2 = pixaSort(pixa1, sorttype, L_SORT_DECREASING, &naindex, L_CLONE);
1425 pixd = pixaGetPix(pixa2, rankorder, L_COPY);
1426 if (pbox) {
1427 numaGetIValue(naindex, rankorder, &index);
1428 *pbox = boxaGetBox(boxa1, index, L_COPY);
1429 }
1430
1431 numaDestroy(&naindex);
1432 boxaDestroy(&boxa1);
1433 pixaDestroy(&pixa1);
1434 pixaDestroy(&pixa2);
1435 return pixd;
1436}
1437
1438
1457PIX *
1458pixFilterComponentBySize(PIX *pixs,
1459 l_int32 rankorder,
1460 l_int32 type,
1461 l_int32 connectivity,
1462 BOX **pbox)
1463{
1464l_int32 x, y, w, h;
1465BOX *box;
1466PIX *pix1, *pix2;
1467
1468 if (!pixs || pixGetDepth(pixs) != 1)
1469 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
1470
1471 pix1 = pixSelectComponentBySize(pixs, rankorder, type, connectivity, &box);
1472 if (!pix1) {
1473 boxDestroy(&box);
1474 return (PIX *)ERROR_PTR("pix1 not made", __func__, NULL);
1475 }
1476
1477 /* Put the selected component in a new pix at the same
1478 * location as it had in %pixs */
1479 boxGetGeometry(box, &x, &y, &w, &h);
1480 pix2 = pixCreateTemplate(pixs);
1481 pixRasterop(pix2, x, y, w, h, PIX_SRC, pix1, 0, 0);
1482 if (pbox)
1483 *pbox = box;
1484 else
1485 boxDestroy(&box);
1486 pixDestroy(&pix1);
1487 return pix2;
1488}
1489
1490
1491/*---------------------------------------------------------------------*
1492 * Make special masks *
1493 *---------------------------------------------------------------------*/
1524PIX *
1526 l_int32 h,
1527 l_float32 hf,
1528 l_float32 vf,
1529 l_int32 type)
1530{
1531 if (w <= 0 || h <= 0)
1532 return (PIX *)ERROR_PTR("mask size 0", __func__, NULL);
1533 if (hf < 0.0 || hf > 1.0)
1534 return (PIX *)ERROR_PTR("invalid horiz fractions", __func__, NULL);
1535 if (vf < 0.0 || vf > 1.0)
1536 return (PIX *)ERROR_PTR("invalid vert fractions", __func__, NULL);
1537
1538 if (type == L_USE_INNER)
1539 return pixMakeFrameMask(w, h, hf, 1.0, vf, 1.0);
1540 else if (type == L_USE_OUTER)
1541 return pixMakeFrameMask(w, h, 0.0, hf, 0.0, vf);
1542 else
1543 return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
1544}
1545
1546
1579PIX *
1581 l_int32 h,
1582 l_float32 hf1,
1583 l_float32 hf2,
1584 l_float32 vf1,
1585 l_float32 vf2)
1586{
1587l_int32 h1, h2, v1, v2;
1588PIX *pixd;
1589
1590 if (w <= 0 || h <= 0)
1591 return (PIX *)ERROR_PTR("mask size 0", __func__, NULL);
1592 if (hf1 < 0.0 || hf1 > 1.0 || hf2 < 0.0 || hf2 > 1.0)
1593 return (PIX *)ERROR_PTR("invalid horiz fractions", __func__, NULL);
1594 if (vf1 < 0.0 || vf1 > 1.0 || vf2 < 0.0 || vf2 > 1.0)
1595 return (PIX *)ERROR_PTR("invalid vert fractions", __func__, NULL);
1596 if (hf1 > hf2 || vf1 > vf2)
1597 return (PIX *)ERROR_PTR("invalid relative sizes", __func__, NULL);
1598
1599 pixd = pixCreate(w, h, 1);
1600
1601 /* Special cases */
1602 if (hf1 == 0.0 && vf1 == 0.0 && hf2 == 1.0 && vf2 == 1.0) { /* full */
1603 pixSetAll(pixd);
1604 return pixd;
1605 }
1606 if (hf1 == hf2 && vf1 == vf2) { /* empty */
1607 return pixd;
1608 }
1609
1610 /* General case */
1611 h1 = 0.5f * hf1 * w;
1612 h2 = 0.5f * hf2 * w;
1613 v1 = 0.5f * vf1 * h;
1614 v2 = 0.5f * vf2 * h;
1615 pixRasterop(pixd, h1, v1, w - 2 * h1, h - 2 * v1, PIX_SET, NULL, 0, 0);
1616 if (hf2 < 1.0 && vf2 < 1.0)
1617 pixRasterop(pixd, h2, v2, w - 2 * h2, h - 2 * v2, PIX_CLR, NULL, 0, 0);
1618 return pixd;
1619}
1620
1621
1622/*---------------------------------------------------------------------*
1623 * Generate a covering of rectangles over connected components *
1624 *---------------------------------------------------------------------*/
1643PIX *
1645 l_int32 maxiters)
1646{
1647l_int32 empty, same, niters;
1648BOXA *boxa;
1649PIX *pix1, *pix2;
1650
1651 if (!pixs || pixGetDepth(pixs) != 1)
1652 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
1653 if (maxiters < 0)
1654 return (PIX *)ERROR_PTR("maxiters must be >= 0", __func__, NULL);
1655 if (maxiters == 0) maxiters = 50; /* ridiculously large number */
1656
1657 pixZero(pixs, &empty);
1658 pix1 = pixCreateTemplate(pixs);
1659 if (empty) return pix1;
1660
1661 /* Do first iteration */
1662 boxa = pixConnCompBB(pixs, 8);
1663 pixMaskBoxa(pix1, pix1, boxa, L_SET_PIXELS);
1664 boxaDestroy(&boxa);
1665 if (maxiters == 1) return pix1;
1666
1667 niters = 1;
1668 while (niters < maxiters) { /* continue to add pixels to pix1 */
1669 niters++;
1670 boxa = pixConnCompBB(pix1, 8);
1671 pix2 = pixCopy(NULL, pix1);
1672 pixMaskBoxa(pix1, pix1, boxa, L_SET_PIXELS);
1673 boxaDestroy(&boxa);
1674 pixEqual(pix1, pix2, &same);
1675 pixDestroy(&pix2);
1676 if (same) {
1677 L_INFO("%d iterations\n", __func__, niters - 1);
1678 return pix1;
1679 }
1680 }
1681 L_INFO("maxiters = %d reached\n", __func__, niters);
1682 return pix1;
1683}
1684
1685
1686/*---------------------------------------------------------------------*
1687 * Fraction of Fg pixels under a mask *
1688 *---------------------------------------------------------------------*/
1714l_ok
1716 PIX *pix2,
1717 l_float32 *pfract)
1718{
1719l_int32 w1, h1, w2, h2, empty, count1, count3;
1720PIX *pix3;
1721
1722 if (!pfract)
1723 return ERROR_INT("&fract not defined", __func__, 1);
1724 *pfract = 0.0;
1725 if (!pix1 || pixGetDepth(pix1) != 1)
1726 return ERROR_INT("pix1 not defined or not 1 bpp", __func__, 1);
1727 if (!pix2 || pixGetDepth(pix2) != 1)
1728 return ERROR_INT("pix2 not defined or not 1 bpp", __func__, 1);
1729
1730 pixGetDimensions(pix1, &w1, &h1, NULL);
1731 pixGetDimensions(pix2, &w2, &h2, NULL);
1732 if (w1 != w2 || h1 != h2) {
1733 L_INFO("sizes unequal: (w1,w2) = (%d,%d), (h1,h2) = (%d,%d)\n",
1734 __func__, w1, w2, h1, h2);
1735 }
1736 pixZero(pix1, &empty);
1737 if (empty) return 0;
1738 pixZero(pix2, &empty);
1739 if (empty) return 0;
1740
1741 pix3 = pixCopy(NULL, pix1);
1742 pixAnd(pix3, pix3, pix2);
1743 pixCountPixels(pix1, &count1, NULL); /* |1| */
1744 pixCountPixels(pix3, &count3, NULL); /* |1 & 2| */
1745 *pfract = (l_float32)count3 / (l_float32)count1;
1746 pixDestroy(&pix3);
1747 return 0;
1748}
1749
1750
1751/*---------------------------------------------------------------------*
1752 * Clip to Foreground *
1753 *---------------------------------------------------------------------*/
1768l_ok
1770 PIX **ppixd,
1771 BOX **pbox)
1772{
1773l_int32 w, h, wpl, nfullwords, extra, i, j;
1774l_int32 minx, miny, maxx, maxy;
1775l_uint32 result, mask;
1776l_uint32 *data, *line;
1777BOX *box;
1778
1779 if (ppixd) *ppixd = NULL;
1780 if (pbox) *pbox = NULL;
1781 if (!ppixd && !pbox)
1782 return ERROR_INT("no output requested", __func__, 1);
1783 if (!pixs || (pixGetDepth(pixs) != 1))
1784 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
1785
1786 pixGetDimensions(pixs, &w, &h, NULL);
1787 nfullwords = w / 32;
1788 extra = w & 31;
1789 mask = ~rmask32[32 - extra];
1790 wpl = pixGetWpl(pixs);
1791 data = pixGetData(pixs);
1792
1793 result = 0;
1794 for (i = 0, miny = 0; i < h; i++, miny++) {
1795 line = data + i * wpl;
1796 for (j = 0; j < nfullwords; j++)
1797 result |= line[j];
1798 if (extra)
1799 result |= (line[j] & mask);
1800 if (result)
1801 break;
1802 }
1803 if (miny == h) /* no ON pixels */
1804 return 1;
1805
1806 result = 0;
1807 for (i = h - 1, maxy = h - 1; i >= 0; i--, maxy--) {
1808 line = data + i * wpl;
1809 for (j = 0; j < nfullwords; j++)
1810 result |= line[j];
1811 if (extra)
1812 result |= (line[j] & mask);
1813 if (result)
1814 break;
1815 }
1816
1817 minx = 0;
1818 for (j = 0, minx = 0; j < w; j++, minx++) {
1819 for (i = 0; i < h; i++) {
1820 line = data + i * wpl;
1821 if (GET_DATA_BIT(line, j))
1822 goto minx_found;
1823 }
1824 }
1825
1826minx_found:
1827 for (j = w - 1, maxx = w - 1; j >= 0; j--, maxx--) {
1828 for (i = 0; i < h; i++) {
1829 line = data + i * wpl;
1830 if (GET_DATA_BIT(line, j))
1831 goto maxx_found;
1832 }
1833 }
1834
1835maxx_found:
1836 box = boxCreate(minx, miny, maxx - minx + 1, maxy - miny + 1);
1837
1838 if (ppixd)
1839 *ppixd = pixClipRectangle(pixs, box, NULL);
1840 if (pbox)
1841 *pbox = box;
1842 else
1843 boxDestroy(&box);
1844
1845 return 0;
1846}
1847
1848
1866l_ok
1868 l_int32 *pcanclip)
1869{
1870l_int32 i, j, w, h, wpl, found;
1871l_uint32 *data, *line;
1872
1873 if (!pcanclip)
1874 return ERROR_INT("&canclip not defined", __func__, 1);
1875 *pcanclip = 0;
1876 if (!pixs || (pixGetDepth(pixs) != 1))
1877 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
1878
1879 /* Check top and bottom raster lines */
1880 pixGetDimensions(pixs, &w, &h, NULL);
1881 data = pixGetData(pixs);
1882 wpl = pixGetWpl(pixs);
1883 found = FALSE;
1884 for (j = 0; found == FALSE && j < w; j++)
1885 found = GET_DATA_BIT(data, j);
1886 if (!found) {
1887 *pcanclip = 1;
1888 return 0;
1889 }
1890
1891 line = data + (h - 1) * wpl;
1892 found = FALSE;
1893 for (j = 0; found == FALSE && j < w; j++)
1894 found = GET_DATA_BIT(data, j);
1895 if (!found) {
1896 *pcanclip = 1;
1897 return 0;
1898 }
1899
1900 /* Check left and right edges */
1901 found = FALSE;
1902 for (i = 0, line = data; found == FALSE && i < h; line += wpl, i++)
1903 found = GET_DATA_BIT(line, 0);
1904 if (!found) {
1905 *pcanclip = 1;
1906 return 0;
1907 }
1908
1909 found = FALSE;
1910 for (i = 0, line = data; found == FALSE && i < h; line += wpl, i++)
1911 found = GET_DATA_BIT(line, w - 1);
1912 if (!found)
1913 *pcanclip = 1;
1914
1915 return 0; /* fg pixels found on all edges */
1916}
1917
1918
1936l_ok
1938 BOX *boxs,
1939 PIX **ppixd,
1940 BOX **pboxd)
1941{
1942l_int32 w, h, bx, by, bw, bh, cbw, cbh, left, right, top, bottom;
1943BOX *boxt, *boxd;
1944
1945 if (ppixd) *ppixd = NULL;
1946 if (pboxd) *pboxd = NULL;
1947 if (!ppixd && !pboxd)
1948 return ERROR_INT("no output requested", __func__, 1);
1949 if (!pixs || (pixGetDepth(pixs) != 1))
1950 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
1951
1952 if (!boxs)
1953 return pixClipToForeground(pixs, ppixd, pboxd);
1954
1955 pixGetDimensions(pixs, &w, &h, NULL);
1956 boxGetGeometry(boxs, &bx, &by, &bw, &bh);
1957 cbw = L_MIN(bw, w - bx);
1958 cbh = L_MIN(bh, h - by);
1959 if (cbw < 0 || cbh < 0)
1960 return ERROR_INT("box not within image", __func__, 1);
1961 boxt = boxCreate(bx, by, cbw, cbh);
1962
1963 if (pixScanForForeground(pixs, boxt, L_FROM_LEFT, &left)) {
1964 boxDestroy(&boxt);
1965 return 1;
1966 }
1967 pixScanForForeground(pixs, boxt, L_FROM_RIGHT, &right);
1968 pixScanForForeground(pixs, boxt, L_FROM_TOP, &top);
1969 pixScanForForeground(pixs, boxt, L_FROM_BOT, &bottom);
1970
1971 boxd = boxCreate(left, top, right - left + 1, bottom - top + 1);
1972 if (ppixd)
1973 *ppixd = pixClipRectangle(pixs, boxd, NULL);
1974 if (pboxd)
1975 *pboxd = boxd;
1976 else
1977 boxDestroy(&boxd);
1978
1979 boxDestroy(&boxt);
1980 return 0;
1981}
1982
1983
2000l_ok
2002 BOX *box,
2003 l_int32 scanflag,
2004 l_int32 *ploc)
2005{
2006l_int32 bx, by, bw, bh, x, xstart, xend, y, ystart, yend, wpl;
2007l_uint32 *data, *line;
2008BOX *boxt;
2009
2010 if (!ploc)
2011 return ERROR_INT("&loc not defined", __func__, 1);
2012 *ploc = 0;
2013 if (!pixs || (pixGetDepth(pixs) != 1))
2014 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
2015
2016 /* Clip box to pixs if it exists */
2017 pixGetDimensions(pixs, &bw, &bh, NULL);
2018 if (box) {
2019 if ((boxt = boxClipToRectangle(box, bw, bh)) == NULL)
2020 return ERROR_INT("invalid box", __func__, 1);
2021 boxGetGeometry(boxt, &bx, &by, &bw, &bh);
2022 boxDestroy(&boxt);
2023 } else {
2024 bx = by = 0;
2025 }
2026 xstart = bx;
2027 ystart = by;
2028 xend = bx + bw - 1;
2029 yend = by + bh - 1;
2030
2031 data = pixGetData(pixs);
2032 wpl = pixGetWpl(pixs);
2033 if (scanflag == L_FROM_LEFT) {
2034 for (x = xstart; x <= xend; x++) {
2035 for (y = ystart; y <= yend; y++) {
2036 line = data + y * wpl;
2037 if (GET_DATA_BIT(line, x)) {
2038 *ploc = x;
2039 return 0;
2040 }
2041 }
2042 }
2043 } else if (scanflag == L_FROM_RIGHT) {
2044 for (x = xend; x >= xstart; x--) {
2045 for (y = ystart; y <= yend; y++) {
2046 line = data + y * wpl;
2047 if (GET_DATA_BIT(line, x)) {
2048 *ploc = x;
2049 return 0;
2050 }
2051 }
2052 }
2053 } else if (scanflag == L_FROM_TOP) {
2054 for (y = ystart; y <= yend; y++) {
2055 line = data + y * wpl;
2056 for (x = xstart; x <= xend; x++) {
2057 if (GET_DATA_BIT(line, x)) {
2058 *ploc = y;
2059 return 0;
2060 }
2061 }
2062 }
2063 } else if (scanflag == L_FROM_BOT) {
2064 for (y = yend; y >= ystart; y--) {
2065 line = data + y * wpl;
2066 for (x = xstart; x <= xend; x++) {
2067 if (GET_DATA_BIT(line, x)) {
2068 *ploc = y;
2069 return 0;
2070 }
2071 }
2072 }
2073 } else {
2074 return ERROR_INT("invalid scanflag", __func__, 1);
2075 }
2076
2077 return 1; /* no fg found */
2078}
2079
2080
2114l_ok
2116 BOX *boxs,
2117 l_int32 lowthresh,
2118 l_int32 highthresh,
2119 l_int32 maxwidth,
2120 l_int32 factor,
2121 PIX **ppixd,
2122 BOX **pboxd)
2123{
2124l_int32 w, h, bx, by, bw, bh, cbw, cbh, left, right, top, bottom;
2125l_int32 lfound, rfound, tfound, bfound, change;
2126BOX *boxt, *boxd;
2127
2128 if (ppixd) *ppixd = NULL;
2129 if (pboxd) *pboxd = NULL;
2130 if (!ppixd && !pboxd)
2131 return ERROR_INT("no output requested", __func__, 1);
2132 if (!pixs || (pixGetDepth(pixs) != 1))
2133 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
2134 if (lowthresh < 1 || highthresh < 1 ||
2135 lowthresh > highthresh || maxwidth < 1)
2136 return ERROR_INT("invalid thresholds", __func__, 1);
2137 factor = L_MIN(1, factor);
2138
2139 if (lowthresh == 1 && highthresh == 1)
2140 return pixClipBoxToForeground(pixs, boxs, ppixd, pboxd);
2141
2142 pixGetDimensions(pixs, &w, &h, NULL);
2143 if (boxs) {
2144 boxGetGeometry(boxs, &bx, &by, &bw, &bh);
2145 cbw = L_MIN(bw, w - bx);
2146 cbh = L_MIN(bh, h - by);
2147 if (cbw < 0 || cbh < 0)
2148 return ERROR_INT("box not within image", __func__, 1);
2149 boxt = boxCreate(bx, by, cbw, cbh);
2150 } else {
2151 boxt = boxCreate(0, 0, w, h);
2152 }
2153
2154 lfound = rfound = tfound = bfound = 0;
2155 while (!lfound || !rfound || !tfound || !bfound) {
2156 change = 0;
2157 if (!lfound) {
2158 if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth,
2159 factor, L_FROM_LEFT, &left)) {
2160 lfound = 1;
2161 change = 1;
2162 boxRelocateOneSide(boxt, boxt, left, L_FROM_LEFT);
2163 }
2164 }
2165 if (!rfound) {
2166 if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth,
2167 factor, L_FROM_RIGHT, &right)) {
2168 rfound = 1;
2169 change = 1;
2170 boxRelocateOneSide(boxt, boxt, right, L_FROM_RIGHT);
2171 }
2172 }
2173 if (!tfound) {
2174 if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth,
2175 factor, L_FROM_TOP, &top)) {
2176 tfound = 1;
2177 change = 1;
2178 boxRelocateOneSide(boxt, boxt, top, L_FROM_TOP);
2179 }
2180 }
2181 if (!bfound) {
2182 if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth,
2183 factor, L_FROM_BOT, &bottom)) {
2184 bfound = 1;
2185 change = 1;
2186 boxRelocateOneSide(boxt, boxt, bottom, L_FROM_BOT);
2187 }
2188 }
2189
2190#if DEBUG_EDGES
2191 lept_stderr("iter: %d %d %d %d\n", lfound, rfound, tfound, bfound);
2192#endif /* DEBUG_EDGES */
2193
2194 if (change == 0) break;
2195 }
2196 boxDestroy(&boxt);
2197
2198 if (change == 0)
2199 return ERROR_INT("not all edges found", __func__, 1);
2200
2201 boxd = boxCreate(left, top, right - left + 1, bottom - top + 1);
2202 if (ppixd)
2203 *ppixd = pixClipRectangle(pixs, boxd, NULL);
2204 if (pboxd)
2205 *pboxd = boxd;
2206 else
2207 boxDestroy(&boxd);
2208
2209 return 0;
2210}
2211
2212
2242l_ok
2244 BOX *box,
2245 l_int32 lowthresh,
2246 l_int32 highthresh,
2247 l_int32 maxwidth,
2248 l_int32 factor,
2249 l_int32 scanflag,
2250 l_int32 *ploc)
2251{
2252l_int32 bx, by, bw, bh, foundmin, loc, sum, wpl;
2253l_int32 x, xstart, xend, y, ystart, yend;
2254l_uint32 *data, *line;
2255BOX *boxt;
2256
2257 if (!ploc)
2258 return ERROR_INT("&ploc not defined", __func__, 1);
2259 *ploc = 0;
2260 if (!pixs || (pixGetDepth(pixs) != 1))
2261 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
2262 if (lowthresh < 1 || highthresh < 1 ||
2263 lowthresh > highthresh || maxwidth < 1)
2264 return ERROR_INT("invalid thresholds", __func__, 1);
2265 factor = L_MIN(1, factor);
2266
2267 /* Clip box to pixs if it exists */
2268 pixGetDimensions(pixs, &bw, &bh, NULL);
2269 if (box) {
2270 if ((boxt = boxClipToRectangle(box, bw, bh)) == NULL)
2271 return ERROR_INT("invalid box", __func__, 1);
2272 boxGetGeometry(boxt, &bx, &by, &bw, &bh);
2273 boxDestroy(&boxt);
2274 } else {
2275 bx = by = 0;
2276 }
2277 xstart = bx;
2278 ystart = by;
2279 xend = bx + bw - 1;
2280 yend = by + bh - 1;
2281
2282 data = pixGetData(pixs);
2283 wpl = pixGetWpl(pixs);
2284 foundmin = 0;
2285 if (scanflag == L_FROM_LEFT) {
2286 for (x = xstart; x <= xend; x++) {
2287 sum = 0;
2288 for (y = ystart; y <= yend; y += factor) {
2289 line = data + y * wpl;
2290 if (GET_DATA_BIT(line, x))
2291 sum++;
2292 }
2293 if (!foundmin && sum < lowthresh)
2294 continue;
2295 if (!foundmin) { /* save the loc of the beginning of the edge */
2296 foundmin = 1;
2297 loc = x;
2298 }
2299 if (sum >= highthresh) {
2300#if DEBUG_EDGES
2301 lept_stderr("Left: x = %d, loc = %d\n", x, loc);
2302#endif /* DEBUG_EDGES */
2303 if (x - loc < maxwidth) {
2304 *ploc = loc;
2305 return 0;
2306 } else {
2307 return 1;
2308 }
2309 }
2310 }
2311 } else if (scanflag == L_FROM_RIGHT) {
2312 for (x = xend; x >= xstart; x--) {
2313 sum = 0;
2314 for (y = ystart; y <= yend; y += factor) {
2315 line = data + y * wpl;
2316 if (GET_DATA_BIT(line, x))
2317 sum++;
2318 }
2319 if (!foundmin && sum < lowthresh)
2320 continue;
2321 if (!foundmin) {
2322 foundmin = 1;
2323 loc = x;
2324 }
2325 if (sum >= highthresh) {
2326#if DEBUG_EDGES
2327 lept_stderr("Right: x = %d, loc = %d\n", x, loc);
2328#endif /* DEBUG_EDGES */
2329 if (loc - x < maxwidth) {
2330 *ploc = loc;
2331 return 0;
2332 } else {
2333 return 1;
2334 }
2335 }
2336 }
2337 } else if (scanflag == L_FROM_TOP) {
2338 for (y = ystart; y <= yend; y++) {
2339 sum = 0;
2340 line = data + y * wpl;
2341 for (x = xstart; x <= xend; x += factor) {
2342 if (GET_DATA_BIT(line, x))
2343 sum++;
2344 }
2345 if (!foundmin && sum < lowthresh)
2346 continue;
2347 if (!foundmin) {
2348 foundmin = 1;
2349 loc = y;
2350 }
2351 if (sum >= highthresh) {
2352#if DEBUG_EDGES
2353 lept_stderr("Top: y = %d, loc = %d\n", y, loc);
2354#endif /* DEBUG_EDGES */
2355 if (y - loc < maxwidth) {
2356 *ploc = loc;
2357 return 0;
2358 } else {
2359 return 1;
2360 }
2361 }
2362 }
2363 } else if (scanflag == L_FROM_BOT) {
2364 for (y = yend; y >= ystart; y--) {
2365 sum = 0;
2366 line = data + y * wpl;
2367 for (x = xstart; x <= xend; x += factor) {
2368 if (GET_DATA_BIT(line, x))
2369 sum++;
2370 }
2371 if (!foundmin && sum < lowthresh)
2372 continue;
2373 if (!foundmin) {
2374 foundmin = 1;
2375 loc = y;
2376 }
2377 if (sum >= highthresh) {
2378#if DEBUG_EDGES
2379 lept_stderr("Bottom: y = %d, loc = %d\n", y, loc);
2380#endif /* DEBUG_EDGES */
2381 if (loc - y < maxwidth) {
2382 *ploc = loc;
2383 return 0;
2384 } else {
2385 return 1;
2386 }
2387 }
2388 }
2389 } else {
2390 return ERROR_INT("invalid scanflag", __func__, 1);
2391 }
2392
2393 return 1; /* edge not found */
2394}
2395
2396
2397/*---------------------------------------------------------------------*
2398 * Extract pixel averages and reversals along lines *
2399 *---------------------------------------------------------------------*/
2421NUMA *
2423 l_int32 x1,
2424 l_int32 y1,
2425 l_int32 x2,
2426 l_int32 y2,
2427 l_int32 factor)
2428{
2429l_int32 i, w, h, d, xmin, ymin, xmax, ymax, npts, direction;
2430l_uint32 val;
2431l_float32 x, y;
2432l_float64 slope;
2433NUMA *na;
2434PTA *pta;
2435
2436 if (!pixs)
2437 return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
2438 pixGetDimensions(pixs, &w, &h, &d);
2439 if (d != 1 && d != 8)
2440 return (NUMA *)ERROR_PTR("d not 1 or 8 bpp", __func__, NULL);
2441 if (pixGetColormap(pixs))
2442 return (NUMA *)ERROR_PTR("pixs has a colormap", __func__, NULL);
2443 if (factor < 1) {
2444 L_WARNING("factor must be >= 1; setting to 1\n", __func__);
2445 factor = 1;
2446 }
2447
2448 /* Clip line to the image */
2449 x1 = L_MAX(0, L_MIN(x1, w - 1));
2450 x2 = L_MAX(0, L_MIN(x2, w - 1));
2451 y1 = L_MAX(0, L_MIN(y1, h - 1));
2452 y2 = L_MAX(0, L_MIN(y2, h - 1));
2453
2454 if (x1 == x2 && y1 == y2) {
2455 pixGetPixel(pixs, x1, y1, &val);
2456 na = numaCreate(1);
2457 numaAddNumber(na, val);
2458 return na;
2459 }
2460
2461 if (y1 == y2)
2462 direction = L_HORIZONTAL_LINE;
2463 else if (x1 == x2)
2464 direction = L_VERTICAL_LINE;
2465 else
2466 direction = L_OBLIQUE_LINE;
2467
2468 na = numaCreate(0);
2469 if (direction == L_HORIZONTAL_LINE) { /* plot against x */
2470 xmin = L_MIN(x1, x2);
2471 xmax = L_MAX(x1, x2);
2472 numaSetParameters(na, xmin, factor);
2473 for (i = xmin; i <= xmax; i += factor) {
2474 pixGetPixel(pixs, i, y1, &val);
2475 numaAddNumber(na, val);
2476 }
2477 } else if (direction == L_VERTICAL_LINE) { /* plot against y */
2478 ymin = L_MIN(y1, y2);
2479 ymax = L_MAX(y1, y2);
2480 numaSetParameters(na, ymin, factor);
2481 for (i = ymin; i <= ymax; i += factor) {
2482 pixGetPixel(pixs, x1, i, &val);
2483 numaAddNumber(na, val);
2484 }
2485 } else { /* direction == L_OBLIQUE_LINE */
2486 slope = (l_float64)((y2 - y1) / (x2 - x1));
2487 if (L_ABS(slope) < 1.0) { /* quasi-horizontal */
2488 xmin = L_MIN(x1, x2);
2489 xmax = L_MAX(x1, x2);
2490 ymin = (xmin == x1) ? y1 : y2; /* pt that goes with xmin */
2491 ymax = (ymin == y1) ? y2 : y1; /* pt that goes with xmax */
2492 pta = generatePtaLine(xmin, ymin, xmax, ymax);
2493 numaSetParameters(na, xmin, (l_float32)factor);
2494 } else { /* quasi-vertical */
2495 ymin = L_MIN(y1, y2);
2496 ymax = L_MAX(y1, y2);
2497 xmin = (ymin == y1) ? x1 : x2; /* pt that goes with ymin */
2498 xmax = (xmin == x1) ? x2 : x1; /* pt that goes with ymax */
2499 pta = generatePtaLine(xmin, ymin, xmax, ymax);
2500 numaSetParameters(na, ymin, (l_float32)factor);
2501 }
2502 npts = ptaGetCount(pta);
2503 for (i = 0; i < npts; i += factor) {
2504 ptaGetPt(pta, i, &x, &y);
2505 pixGetPixel(pixs, (l_int32)x, (l_int32)y, &val);
2506 numaAddNumber(na, val);
2507 }
2508
2509#if 0 /* debugging */
2510 pixPlotAlongPta(pixs, pta, GPLOT_PNG, NULL);
2511#endif
2512
2513 ptaDestroy(&pta);
2514 }
2515
2516 return na;
2517}
2518
2519
2539l_float32
2541 l_int32 x1,
2542 l_int32 y1,
2543 l_int32 x2,
2544 l_int32 y2,
2545 l_int32 factor)
2546{
2547l_int32 i, j, w, h, d, direction, count, wpl;
2548l_uint32 *data, *line;
2549l_float32 sum;
2550
2551 if (!pixs)
2552 return ERROR_INT("pixs not defined", __func__, 1);
2553 pixGetDimensions(pixs, &w, &h, &d);
2554 if (d != 1 && d != 8)
2555 return ERROR_INT("d not 1 or 8 bpp", __func__, 1);
2556 if (pixGetColormap(pixs))
2557 return ERROR_INT("pixs has a colormap", __func__, 1);
2558 if (x1 > x2 || y1 > y2)
2559 return ERROR_INT("x1 > x2 or y1 > y2", __func__, 1);
2560
2561 if (y1 == y2) {
2562 x1 = L_MAX(0, x1);
2563 x2 = L_MIN(w - 1, x2);
2564 y1 = L_MAX(0, L_MIN(y1, h - 1));
2565 direction = L_HORIZONTAL_LINE;
2566 } else if (x1 == x2) {
2567 y1 = L_MAX(0, y1);
2568 y2 = L_MIN(h - 1, y2);
2569 x1 = L_MAX(0, L_MIN(x1, w - 1));
2570 direction = L_VERTICAL_LINE;
2571 } else {
2572 return ERROR_INT("line neither horiz nor vert", __func__, 1);
2573 }
2574
2575 if (factor < 1) {
2576 L_WARNING("factor must be >= 1; setting to 1\n", __func__);
2577 factor = 1;
2578 }
2579
2580 data = pixGetData(pixs);
2581 wpl = pixGetWpl(pixs);
2582 sum = 0;
2583 count = 0;
2584 if (direction == L_HORIZONTAL_LINE) {
2585 line = data + y1 * wpl;
2586 for (j = x1, count = 0; j <= x2; count++, j += factor) {
2587 if (d == 1)
2588 sum += GET_DATA_BIT(line, j);
2589 else /* d == 8 */
2590 sum += GET_DATA_BYTE(line, j);
2591 }
2592 } else if (direction == L_VERTICAL_LINE) {
2593 for (i = y1, count = 0; i <= y2; count++, i += factor) {
2594 line = data + i * wpl;
2595 if (d == 1)
2596 sum += GET_DATA_BIT(line, x1);
2597 else /* d == 8 */
2598 sum += GET_DATA_BYTE(line, x1);
2599 }
2600 }
2601
2602 return sum / (l_float32)count;
2603}
2604
2605
2636NUMA *
2638 l_float32 fract,
2639 l_int32 dir,
2640 l_int32 first,
2641 l_int32 last,
2642 l_int32 factor1,
2643 l_int32 factor2)
2644{
2645l_int32 i, j, w, h, d, start, end;
2646l_float32 ave;
2647NUMA *nad;
2648PIX *pixr, *pixg;
2649
2650 if (!pixs)
2651 return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
2652 if (fract < 0.0 || fract > 1.0)
2653 return (NUMA *)ERROR_PTR("fract < 0.0 or > 1.0", __func__, NULL);
2654 if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE)
2655 return (NUMA *)ERROR_PTR("invalid direction", __func__, NULL);
2656 if (first < 0) first = 0;
2657 if (last < first)
2658 return (NUMA *)ERROR_PTR("last must be >= first", __func__, NULL);
2659 if (factor1 < 1) {
2660 L_WARNING("factor1 must be >= 1; setting to 1\n", __func__);
2661 factor1 = 1;
2662 }
2663 if (factor2 < 1) {
2664 L_WARNING("factor2 must be >= 1; setting to 1\n", __func__);
2665 factor2 = 1;
2666 }
2667
2668 /* Use 1 or 8 bpp, without colormap */
2669 if (pixGetColormap(pixs))
2670 pixr = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
2671 else
2672 pixr = pixClone(pixs);
2673 pixGetDimensions(pixr, &w, &h, &d);
2674 if (d == 1)
2675 pixg = pixClone(pixr);
2676 else
2677 pixg = pixConvertTo8(pixr, 0);
2678
2679 nad = numaCreate(0); /* output: samples in slow scan direction */
2680 numaSetParameters(nad, 0, factor2);
2681 if (dir == L_HORIZONTAL_LINE) {
2682 start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)w);
2683 end = w - start;
2684 if (last > h - 1) {
2685 L_WARNING("last > h - 1; clipping\n", __func__);
2686 last = h - 1;
2687 }
2688 for (i = first; i <= last; i += factor2) {
2689 ave = pixAverageOnLine(pixg, start, i, end, i, factor1);
2690 numaAddNumber(nad, ave);
2691 }
2692 } else if (dir == L_VERTICAL_LINE) {
2693 start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)h);
2694 end = h - start;
2695 if (last > w - 1) {
2696 L_WARNING("last > w - 1; clipping\n", __func__);
2697 last = w - 1;
2698 }
2699 for (j = first; j <= last; j += factor2) {
2700 ave = pixAverageOnLine(pixg, j, start, j, end, factor1);
2701 numaAddNumber(nad, ave);
2702 }
2703 }
2704
2705 pixDestroy(&pixr);
2706 pixDestroy(&pixg);
2707 return nad;
2708}
2709
2710
2749NUMA *
2751 l_float32 fract,
2752 l_int32 dir,
2753 l_int32 first,
2754 l_int32 last,
2755 l_int32 minreversal,
2756 l_int32 factor1,
2757 l_int32 factor2)
2758{
2759l_int32 i, j, w, h, d, start, end, nr;
2760NUMA *naline, *nad;
2761PIX *pixr, *pixg;
2762
2763 if (!pixs)
2764 return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
2765 if (fract < 0.0 || fract > 1.0)
2766 return (NUMA *)ERROR_PTR("fract < 0.0 or > 1.0", __func__, NULL);
2767 if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE)
2768 return (NUMA *)ERROR_PTR("invalid direction", __func__, NULL);
2769 if (first < 0) first = 0;
2770 if (last < first)
2771 return (NUMA *)ERROR_PTR("last must be >= first", __func__, NULL);
2772 if (factor1 < 1) {
2773 L_WARNING("factor1 must be >= 1; setting to 1\n", __func__);
2774 factor1 = 1;
2775 }
2776 if (factor2 < 1) {
2777 L_WARNING("factor2 must be >= 1; setting to 1\n", __func__);
2778 factor2 = 1;
2779 }
2780
2781 /* Use 1 or 8 bpp, without colormap */
2782 if (pixGetColormap(pixs))
2783 pixr = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
2784 else
2785 pixr = pixClone(pixs);
2786 pixGetDimensions(pixr, &w, &h, &d);
2787 if (d == 1) {
2788 pixg = pixClone(pixr);
2789 minreversal = 1; /* enforce this */
2790 } else {
2791 pixg = pixConvertTo8(pixr, 0);
2792 }
2793
2794 nad = numaCreate(0); /* output: samples in slow scan direction */
2795 numaSetParameters(nad, 0, factor2);
2796 if (dir == L_HORIZONTAL_LINE) {
2797 start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)w);
2798 end = w - start;
2799 if (last > h - 1) {
2800 L_WARNING("last > h - 1; clipping\n", __func__);
2801 last = h - 1;
2802 }
2803 for (i = first; i <= last; i += factor2) {
2804 naline = pixExtractOnLine(pixg, start, i, end, i, factor1);
2805 numaCountReversals(naline, minreversal, &nr, NULL);
2806 numaAddNumber(nad, nr);
2807 numaDestroy(&naline);
2808 }
2809 } else if (dir == L_VERTICAL_LINE) {
2810 start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)h);
2811 end = h - start;
2812 if (last > w - 1) {
2813 L_WARNING("last > w - 1; clipping\n", __func__);
2814 last = w - 1;
2815 }
2816 for (j = first; j <= last; j += factor2) {
2817 naline = pixExtractOnLine(pixg, j, start, j, end, factor1);
2818 numaCountReversals(naline, minreversal, &nr, NULL);
2819 numaAddNumber(nad, nr);
2820 numaDestroy(&naline);
2821 }
2822 }
2823
2824 pixDestroy(&pixr);
2825 pixDestroy(&pixg);
2826 return nad;
2827}
2828
2829
2830/*---------------------------------------------------------------------*
2831 * Extract windowed variance along a line *
2832 *---------------------------------------------------------------------*/
2856l_ok
2858 l_int32 dir,
2859 l_int32 loc,
2860 l_int32 c1,
2861 l_int32 c2,
2862 l_int32 size,
2863 NUMA **pnad)
2864{
2865l_int32 i, j, w, h, cmin, cmax, maxloc, n, x, y;
2866l_uint32 val;
2867l_float32 norm, rootvar;
2868l_float32 *array;
2869l_float64 sum1, sum2, ave, var;
2870NUMA *na1, *nad;
2871PTA *pta;
2872
2873 if (!pnad)
2874 return ERROR_INT("&nad not defined", __func__, 1);
2875 *pnad = NULL;
2876 if (!pixs || pixGetDepth(pixs) != 8)
2877 return ERROR_INT("pixs not defined or not 8bpp", __func__, 1);
2878 if (size < 2)
2879 return ERROR_INT("window size must be > 1", __func__, 1);
2880 if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE)
2881 return ERROR_INT("invalid direction", __func__, 1);
2882 pixGetDimensions(pixs, &w, &h, NULL);
2883 maxloc = (dir == L_HORIZONTAL_LINE) ? h - 1 : w - 1;
2884 if (loc < 0 || loc > maxloc)
2885 return ERROR_INT("invalid line position", __func__, 1);
2886
2887 /* Clip line to the image */
2888 cmin = L_MIN(c1, c2);
2889 cmax = L_MAX(c1, c2);
2890 maxloc = (dir == L_HORIZONTAL_LINE) ? w - 1 : h - 1;
2891 cmin = L_MAX(0, L_MIN(cmin, maxloc));
2892 cmax = L_MAX(0, L_MIN(cmax, maxloc));
2893 n = cmax - cmin + 1;
2894
2895 /* Generate pta along the line */
2896 pta = ptaCreate(n);
2897 if (dir == L_HORIZONTAL_LINE) {
2898 for (i = cmin; i <= cmax; i++)
2899 ptaAddPt(pta, i, loc);
2900 } else { /* vertical line */
2901 for (i = cmin; i <= cmax; i++)
2902 ptaAddPt(pta, loc, i);
2903 }
2904
2905 /* Get numa of pixel values on the line */
2906 na1 = numaCreate(n);
2907 numaSetParameters(na1, cmin, 1);
2908 for (i = 0; i < n; i++) {
2909 ptaGetIPt(pta, i, &x, &y);
2910 pixGetPixel(pixs, x, y, &val);
2911 numaAddNumber(na1, val);
2912 }
2913 array = numaGetFArray(na1, L_NOCOPY);
2914 ptaDestroy(&pta);
2915
2916 /* Compute root variance on overlapping windows */
2917 nad = numaCreate(n);
2918 *pnad = nad;
2919 numaSetParameters(nad, cmin + size / 2, 1);
2920 norm = 1.0f / (l_float32)size;
2921 for (i = 0; i < n - size; i++) { /* along the line */
2922 sum1 = sum2 = 0;
2923 for (j = 0; j < size; j++) { /* over the window */
2924 val = array[i + j];
2925 sum1 += val;
2926 sum2 += (l_float64)(val) * val;
2927 }
2928 ave = norm * sum1;
2929 var = norm * sum2 - ave * ave;
2930 if (var < 0) /* avoid small negative values from rounding effects */
2931 var = 0.0;
2932 rootvar = (l_float32)sqrt(var);
2933 numaAddNumber(nad, rootvar);
2934 }
2935
2936 numaDestroy(&na1);
2937 return 0;
2938}
2939
2940
2941/*---------------------------------------------------------------------*
2942 * Extract min/max of pixel values near lines *
2943 *---------------------------------------------------------------------*/
2974l_ok
2976 l_int32 x1,
2977 l_int32 y1,
2978 l_int32 x2,
2979 l_int32 y2,
2980 l_int32 dist,
2981 l_int32 direction,
2982 NUMA **pnamin,
2983 NUMA **pnamax,
2984 l_float32 *pminave,
2985 l_float32 *pmaxave)
2986{
2987l_int32 i, j, w, h, d, x, y, n, dir, found, minval, maxval, negloc, posloc;
2988l_uint32 val;
2989l_float32 sum;
2990NUMA *namin, *namax;
2991PTA *pta;
2992
2993 if (pnamin) *pnamin = NULL;
2994 if (pnamax) *pnamax = NULL;
2995 if (pminave) *pminave = UNDEF;
2996 if (pmaxave) *pmaxave = UNDEF;
2997 if (!pnamin && !pnamax && !pminave && !pmaxave)
2998 return ERROR_INT("no output requested", __func__, 1);
2999 if (!pixs)
3000 return ERROR_INT("pixs not defined", __func__, 1);
3001 pixGetDimensions(pixs, &w, &h, &d);
3002 if (d != 8 || pixGetColormap(pixs))
3003 return ERROR_INT("pixs not 8 bpp or has colormap", __func__, 1);
3004 dist = L_ABS(dist);
3005 if (direction != L_SCAN_NEGATIVE && direction != L_SCAN_POSITIVE &&
3006 direction != L_SCAN_BOTH)
3007 return ERROR_INT("invalid direction", __func__, 1);
3008
3009 pta = generatePtaLine(x1, y1, x2, y2);
3010 n = ptaGetCount(pta);
3011 dir = (L_ABS(x1 - x2) == n - 1) ? L_HORIZ : L_VERT;
3012 namin = numaCreate(n);
3013 namax = numaCreate(n);
3014 negloc = -dist;
3015 posloc = dist;
3016 if (direction == L_SCAN_NEGATIVE)
3017 posloc = 0;
3018 else if (direction == L_SCAN_POSITIVE)
3019 negloc = 0;
3020 for (i = 0; i < n; i++) {
3021 ptaGetIPt(pta, i, &x, &y);
3022 minval = 255;
3023 maxval = 0;
3024 found = FALSE;
3025 if (dir == L_HORIZ) {
3026 if (x < 0 || x >= w) continue;
3027 for (j = negloc; j <= posloc; j++) {
3028 if (y + j < 0 || y + j >= h) continue;
3029 pixGetPixel(pixs, x, y + j, &val);
3030 found = TRUE;
3031 if (val < minval) minval = val;
3032 if (val > maxval) maxval = val;
3033 }
3034 } else { /* dir == L_VERT */
3035 if (y < 0 || y >= h) continue;
3036 for (j = negloc; j <= posloc; j++) {
3037 if (x + j < 0 || x + j >= w) continue;
3038 pixGetPixel(pixs, x + j, y, &val);
3039 found = TRUE;
3040 if (val < minval) minval = val;
3041 if (val > maxval) maxval = val;
3042 }
3043 }
3044 if (found) {
3045 numaAddNumber(namin, minval);
3046 numaAddNumber(namax, maxval);
3047 }
3048 }
3049
3050 n = numaGetCount(namin);
3051 if (n == 0) {
3052 numaDestroy(&namin);
3053 numaDestroy(&namax);
3054 ptaDestroy(&pta);
3055 return ERROR_INT("no output from this line", __func__, 1);
3056 }
3057
3058 if (pminave) {
3059 numaGetSum(namin, &sum);
3060 *pminave = sum / n;
3061 }
3062 if (pmaxave) {
3063 numaGetSum(namax, &sum);
3064 *pmaxave = sum / n;
3065 }
3066 if (pnamin)
3067 *pnamin = namin;
3068 else
3069 numaDestroy(&namin);
3070 if (pnamax)
3071 *pnamax = namax;
3072 else
3073 numaDestroy(&namax);
3074 ptaDestroy(&pta);
3075 return 0;
3076}
3077
3078
3079/*---------------------------------------------------------------------*
3080 * Rank row and column transforms *
3081 *---------------------------------------------------------------------*/
3095PIX *
3097{
3098l_int32 i, j, k, m, w, h, wpl, val;
3099l_int32 histo[256];
3100l_uint32 *datas, *datad, *lines, *lined;
3101PIX *pixd;
3102
3103 if (!pixs)
3104 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
3105 if (pixGetDepth(pixs) != 8)
3106 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL);
3107 if (pixGetColormap(pixs))
3108 return (PIX *)ERROR_PTR("pixs has a colormap", __func__, NULL);
3109
3110 pixGetDimensions(pixs, &w, &h, NULL);
3111 pixd = pixCreateTemplate(pixs);
3112 datas = pixGetData(pixs);
3113 datad = pixGetData(pixd);
3114 wpl = pixGetWpl(pixs);
3115 for (i = 0; i < h; i++) {
3116 memset(histo, 0, 1024);
3117 lines = datas + i * wpl;
3118 lined = datad + i * wpl;
3119 for (j = 0; j < w; j++) {
3120 val = GET_DATA_BYTE(lines, j);
3121 histo[val]++;
3122 }
3123 for (m = 0, j = 0; m < 256; m++) {
3124 for (k = 0; k < histo[m]; k++, j++)
3125 SET_DATA_BYTE(lined, j, m);
3126 }
3127 }
3128
3129 return pixd;
3130}
3131
3132
3146PIX *
3148{
3149l_int32 i, j, k, m, w, h, val;
3150l_int32 histo[256];
3151void **lines8, **lined8;
3152PIX *pixd;
3153
3154 if (!pixs)
3155 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
3156 if (pixGetDepth(pixs) != 8)
3157 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL);
3158 if (pixGetColormap(pixs))
3159 return (PIX *)ERROR_PTR("pixs has a colormap", __func__, NULL);
3160
3161 pixGetDimensions(pixs, &w, &h, NULL);
3162 pixd = pixCreateTemplate(pixs);
3163 lines8 = pixGetLinePtrs(pixs, NULL);
3164 lined8 = pixGetLinePtrs(pixd, NULL);
3165 for (j = 0; j < w; j++) {
3166 memset(histo, 0, 1024);
3167 for (i = 0; i < h; i++) {
3168 val = GET_DATA_BYTE(lines8[i], j);
3169 histo[val]++;
3170 }
3171 for (m = 0, i = 0; m < 256; m++) {
3172 for (k = 0; k < histo[m]; k++, i++)
3173 SET_DATA_BYTE(lined8[i], j, m);
3174 }
3175 }
3176
3177 LEPT_FREE(lines8);
3178 LEPT_FREE(lined8);
3179 return pixd;
3180}
#define GET_DATA_BYTE(pdata, n)
#define SET_DATA_BYTE(pdata, n, val)
#define GET_DATA_BIT(pdata, n)
l_ok pixWindowedVarianceOnLine(PIX *pixs, l_int32 dir, l_int32 loc, l_int32 c1, l_int32 c2, l_int32 size, NUMA **pnad)
pixWindowedVarianceOnLine()
Definition pix5.c:2857
PIX * pixClipRectangleWithBorder(PIX *pixs, BOX *box, l_int32 maxbord, BOX **pboxn)
pixClipRectangleWithBorder()
Definition pix5.c:1096
l_ok pixFractionFgInMask(PIX *pix1, PIX *pix2, l_float32 *pfract)
pixFractionFgInMask()
Definition pix5.c:1715
l_ok pixFindAreaPerimRatio(PIX *pixs, l_int32 *tab, l_float32 *pfract)
pixFindAreaPerimRatio()
Definition pix5.c:189
l_ok pixFindPerimToAreaRatio(PIX *pixs, l_int32 *tab, l_float32 *pfract)
pixFindPerimToAreaRatio()
Definition pix5.c:286
NUMA * pixaFindWidthHeightRatio(PIXA *pixa)
pixaFindWidthHeightRatio()
Definition pix5.c:651
PIX * pixClipRectangle(PIX *pixs, BOX *box, BOX **pboxc)
pixClipRectangle()
Definition pix5.c:1035
PIX * pixClipMasked(PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_uint32 outval)
pixClipMasked()
Definition pix5.c:1167
NUMA * pixaFindPerimToAreaRatio(PIXA *pixa)
pixaFindPerimToAreaRatio()
Definition pix5.c:238
PIX * pixRankColumnTransform(PIX *pixs)
pixRankColumnTransform()
Definition pix5.c:3147
l_ok pixFindPerimSizeRatio(PIX *pixs, l_int32 *tab, l_float32 *pratio)
pixFindPerimSizeRatio()
Definition pix5.c:386
PIX * pixMakeFrameMask(l_int32 w, l_int32 h, l_float32 hf1, l_float32 hf2, l_float32 vf1, l_float32 vf2)
pixMakeFrameMask()
Definition pix5.c:1580
NUMA * pixExtractOnLine(PIX *pixs, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 factor)
pixExtractOnLine()
Definition pix5.c:2422
l_ok pixFindAreaFraction(PIX *pixs, l_int32 *tab, l_float32 *pfract)
pixFindAreaFraction()
Definition pix5.c:471
l_ok pixScanForForeground(PIX *pixs, BOX *box, l_int32 scanflag, l_int32 *ploc)
pixScanForForeground()
Definition pix5.c:2001
NUMA * pixaFindAreaFraction(PIXA *pixa)
pixaFindAreaFraction()
Definition pix5.c:430
PIX * pixExtractRectangularRegions(PIX *pixs, BOXA *boxa)
pixExtractRectangularRegions()
Definition pix5.c:936
PIXA * pixClipRectangles(PIX *pixs, BOXA *boxa)
pixClipRectangles()
Definition pix5.c:971
l_ok pixClipBoxToEdges(PIX *pixs, BOX *boxs, l_int32 lowthresh, l_int32 highthresh, l_int32 maxwidth, l_int32 factor, PIX **ppixd, BOX **pboxd)
pixClipBoxToEdges()
Definition pix5.c:2115
PIX * pixResizeToMatch(PIX *pixs, PIX *pixt, l_int32 w, l_int32 h)
pixResizeToMatch()
Definition pix5.c:1320
l_float32 pixAverageOnLine(PIX *pixs, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 factor)
pixAverageOnLine()
Definition pix5.c:2540
l_ok pixTestClipToForeground(PIX *pixs, l_int32 *pcanclip)
pixTestClipToForeground()
Definition pix5.c:1867
l_ok pixClipBoxToForeground(PIX *pixs, BOX *boxs, PIX **ppixd, BOX **pboxd)
pixClipBoxToForeground()
Definition pix5.c:1937
l_ok pixFindAreaFractionMasked(PIX *pixs, BOX *box, PIX *pixm, l_int32 *tab, l_float32 *pfract)
pixFindAreaFractionMasked()
Definition pix5.c:594
PIX * pixMakeCoveringOfRectangles(PIX *pixs, l_int32 maxiters)
pixMakeCoveringOfRectangles()
Definition pix5.c:1644
l_ok pixScanForEdge(PIX *pixs, BOX *box, l_int32 lowthresh, l_int32 highthresh, l_int32 maxwidth, l_int32 factor, l_int32 scanflag, l_int32 *ploc)
pixScanForEdge()
Definition pix5.c:2243
NUMA * pixAverageIntensityProfile(PIX *pixs, l_float32 fract, l_int32 dir, l_int32 first, l_int32 last, l_int32 factor1, l_int32 factor2)
pixAverageIntensityProfile()
Definition pix5.c:2637
l_ok pixMinMaxNearLine(PIX *pixs, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 dist, l_int32 direction, NUMA **pnamin, NUMA **pnamax, l_float32 *pminave, l_float32 *pmaxave)
pixMinMaxNearLine()
Definition pix5.c:2975
BOXA * pixFindRectangleComps(PIX *pixs, l_int32 dist, l_int32 minw, l_int32 minh)
pixFindRectangleComps()
Definition pix5.c:788
PIX * pixCropToSize(PIX *pixs, l_int32 w, l_int32 h)
pixCropToSize()
Definition pix5.c:1268
l_ok pixClipToForeground(PIX *pixs, PIX **ppixd, BOX **pbox)
pixClipToForeground()
Definition pix5.c:1769
l_ok pixaFindDimensions(PIXA *pixa, NUMA **pnaw, NUMA **pnah)
pixaFindDimensions()
Definition pix5.c:141
NUMA * pixaFindAreaFractionMasked(PIXA *pixa, PIX *pixm, l_int32 debug)
pixaFindAreaFractionMasked()
Definition pix5.c:517
PIX * pixRankRowTransform(PIX *pixs)
pixRankRowTransform()
Definition pix5.c:3096
l_ok pixConformsToRectangle(PIX *pixs, BOX *box, l_int32 dist, l_int32 *pconforms)
pixConformsToRectangle()
Definition pix5.c:866
l_ok pixFindOverlapFraction(PIX *pixs1, PIX *pixs2, l_int32 x2, l_int32 y2, l_int32 *tab, l_float32 *pratio, l_int32 *pnoverlap)
pixFindOverlapFraction()
Definition pix5.c:723
l_ok pixCropToMatch(PIX *pixs1, PIX *pixs2, PIX **ppixd1, PIX **ppixd2)
pixCropToMatch()
Definition pix5.c:1227
PIX * pixMakeSymmetricMask(l_int32 w, l_int32 h, l_float32 hf, l_float32 vf, l_int32 type)
pixSelectComponentBySize()
Definition pix5.c:1525
NUMA * pixaFindPerimSizeRatio(PIXA *pixa)
pixaFindPerimSizeRatio()
Definition pix5.c:338
NUMA * pixaFindWidthHeightProduct(PIXA *pixa)
pixaFindWidthHeightProduct()
Definition pix5.c:685
NUMA * pixReversalProfile(PIX *pixs, l_float32 fract, l_int32 dir, l_int32 first, l_int32 last, l_int32 minreversal, l_int32 factor1, l_int32 factor2)
pixReversalProfile()
Definition pix5.c:2750
#define PIX_MASK
Definition pix.h:451
@ L_SET_PIXELS
Definition pix.h:565
@ L_HORIZONTAL_LINE
Definition pix.h:806
@ L_OBLIQUE_LINE
Definition pix.h:810
@ L_VERTICAL_LINE
Definition pix.h:808
@ L_USE_INNER
Definition pix.h:1030
@ L_USE_OUTER
Definition pix.h:1031
@ L_SORT_BY_AREA
Definition pix.h:537
@ L_SORT_BY_PERIMETER
Definition pix.h:536
@ L_SORT_BY_WIDTH
Definition pix.h:532
@ L_SORT_BY_HEIGHT
Definition pix.h:533
@ L_SORT_BY_MAX_DIMENSION
Definition pix.h:535
@ REMOVE_CMAP_TO_GRAYSCALE
Definition pix.h:381
@ L_COPY
Definition pix.h:505
@ L_CLONE
Definition pix.h:506
@ L_NOCOPY
Definition pix.h:503
@ L_INSERT
Definition pix.h:504
#define PIX_PAINT
Definition pix.h:450
@ L_SET_WHITE
Definition pix.h:699
@ L_FROM_BOT
Definition pix.h:830
@ L_FROM_LEFT
Definition pix.h:827
@ L_SCAN_NEGATIVE
Definition pix.h:831
@ L_SCAN_BOTH
Definition pix.h:833
@ L_FROM_RIGHT
Definition pix.h:828
@ L_SCAN_POSITIVE
Definition pix.h:832
@ L_FROM_TOP
Definition pix.h:829
#define PIX_SRC
Definition pix.h:444
#define PIX_CLR
Definition pix.h:447
@ L_SORT_DECREASING
Definition pix.h:523
#define PIX_SET
Definition pix.h:448
@ L_SELECT_BY_HEIGHT
Definition pix.h:584
@ L_SELECT_BY_AREA
Definition pix.h:587
@ L_SELECT_BY_PERIMETER
Definition pix.h:588
@ L_SELECT_BY_WIDTH
Definition pix.h:583
@ L_SELECT_BY_MAX_DIMENSION
Definition pix.h:585