Leptonica 1.84.1
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
105#ifdef HAVE_CONFIG_H
106#include <config_auto.h>
107#endif /* HAVE_CONFIG_H */
108
109#include <string.h>
110#include <math.h>
111#include "allheaders.h"
112
113static const l_uint32 rmask32[] = {0x0,
114 0x00000001, 0x00000003, 0x00000007, 0x0000000f,
115 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
116 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff,
117 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
118 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff,
119 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff,
120 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff,
121 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff};
122
123#ifndef NO_CONSOLE_IO
124#define DEBUG_EDGES 0
125#endif /* ~NO_CONSOLE_IO */
126
127
128/*-------------------------------------------------------------*
129 * Measurement of properties *
130 *-------------------------------------------------------------*/
139l_ok
141 NUMA **pnaw,
142 NUMA **pnah)
143{
144l_int32 i, n, w, h;
145PIX *pixt;
146
147 if (pnaw) *pnaw = NULL;
148 if (pnah) *pnah = NULL;
149 if (!pnaw && !pnah)
150 return ERROR_INT("no output requested", __func__, 1);
151 if (!pixa)
152 return ERROR_INT("pixa not defined", __func__, 1);
153
154 n = pixaGetCount(pixa);
155 if (pnaw) *pnaw = numaCreate(n);
156 if (pnah) *pnah = numaCreate(n);
157 for (i = 0; i < n; i++) {
158 pixt = pixaGetPix(pixa, i, L_CLONE);
159 pixGetDimensions(pixt, &w, &h, NULL);
160 if (pnaw)
161 numaAddNumber(*pnaw, w);
162 if (pnah)
163 numaAddNumber(*pnah, h);
164 pixDestroy(&pixt);
165 }
166 return 0;
167}
168
169
187l_ok
189 l_int32 *tab,
190 l_float32 *pfract)
191{
192l_int32 *tab8;
193l_int32 nfg, nbound;
194PIX *pixt;
195
196 if (!pfract)
197 return ERROR_INT("&fract not defined", __func__, 1);
198 *pfract = 0.0;
199 if (!pixs || pixGetDepth(pixs) != 1)
200 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
201
202 if (!tab)
203 tab8 = makePixelSumTab8();
204 else
205 tab8 = tab;
206
207 pixt = pixErodeBrick(NULL, pixs, 3, 3);
208 pixCountPixels(pixt, &nfg, tab8);
209 if (nfg == 0) {
210 pixDestroy(&pixt);
211 if (!tab) LEPT_FREE(tab8);
212 return 0;
213 }
214 pixXor(pixt, pixt, pixs);
215 pixCountPixels(pixt, &nbound, tab8);
216 *pfract = (l_float32)nfg / (l_float32)nbound;
217 pixDestroy(&pixt);
218
219 if (!tab) LEPT_FREE(tab8);
220 return 0;
221}
222
223
236NUMA *
238{
239l_int32 i, n;
240l_int32 *tab;
241l_float32 fract;
242NUMA *na;
243PIX *pixt;
244
245 if (!pixa)
246 return (NUMA *)ERROR_PTR("pixa not defined", __func__, NULL);
247
248 n = pixaGetCount(pixa);
249 na = numaCreate(n);
250 tab = makePixelSumTab8();
251 for (i = 0; i < n; i++) {
252 pixt = pixaGetPix(pixa, i, L_CLONE);
253 pixFindPerimToAreaRatio(pixt, tab, &fract);
254 numaAddNumber(na, fract);
255 pixDestroy(&pixt);
256 }
257 LEPT_FREE(tab);
258 return na;
259}
260
261
284l_ok
286 l_int32 *tab,
287 l_float32 *pfract)
288{
289l_int32 *tab8;
290l_int32 nfg, nbound;
291PIX *pixt;
292
293 if (!pfract)
294 return ERROR_INT("&fract not defined", __func__, 1);
295 *pfract = 0.0;
296 if (!pixs || pixGetDepth(pixs) != 1)
297 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
298
299 if (!tab)
300 tab8 = makePixelSumTab8();
301 else
302 tab8 = tab;
303
304 pixCountPixels(pixs, &nfg, tab8);
305 if (nfg == 0) {
306 if (!tab) LEPT_FREE(tab8);
307 return 0;
308 }
309 pixt = pixErodeBrick(NULL, pixs, 3, 3);
310 pixXor(pixt, pixt, pixs);
311 pixCountPixels(pixt, &nbound, tab8);
312 *pfract = (l_float32)nbound / (l_float32)nfg;
313 pixDestroy(&pixt);
314
315 if (!tab) LEPT_FREE(tab8);
316 return 0;
317}
318
319
336NUMA *
338{
339l_int32 i, n;
340l_int32 *tab;
341l_float32 ratio;
342NUMA *na;
343PIX *pixt;
344
345 if (!pixa)
346 return (NUMA *)ERROR_PTR("pixa not defined", __func__, NULL);
347
348 n = pixaGetCount(pixa);
349 na = numaCreate(n);
350 tab = makePixelSumTab8();
351 for (i = 0; i < n; i++) {
352 pixt = pixaGetPix(pixa, i, L_CLONE);
353 pixFindPerimSizeRatio(pixt, tab, &ratio);
354 numaAddNumber(na, ratio);
355 pixDestroy(&pixt);
356 }
357 LEPT_FREE(tab);
358 return na;
359}
360
361
384l_ok
386 l_int32 *tab,
387 l_float32 *pratio)
388{
389l_int32 *tab8;
390l_int32 w, h, nbound;
391PIX *pixt;
392
393 if (!pratio)
394 return ERROR_INT("&ratio not defined", __func__, 1);
395 *pratio = 0.0;
396 if (!pixs || pixGetDepth(pixs) != 1)
397 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
398
399 if (!tab)
400 tab8 = makePixelSumTab8();
401 else
402 tab8 = tab;
403
404 pixt = pixErodeBrick(NULL, pixs, 3, 3);
405 pixXor(pixt, pixt, pixs);
406 pixCountPixels(pixt, &nbound, tab8);
407 pixGetDimensions(pixs, &w, &h, NULL);
408 *pratio = (0.5f * nbound) / (l_float32)(w + h);
409 pixDestroy(&pixt);
410
411 if (!tab) LEPT_FREE(tab8);
412 return 0;
413}
414
415
428NUMA *
430{
431l_int32 i, n;
432l_int32 *tab;
433l_float32 fract;
434NUMA *na;
435PIX *pixt;
436
437 if (!pixa)
438 return (NUMA *)ERROR_PTR("pixa not defined", __func__, NULL);
439
440 n = pixaGetCount(pixa);
441 na = numaCreate(n);
442 tab = makePixelSumTab8();
443 for (i = 0; i < n; i++) {
444 pixt = pixaGetPix(pixa, i, L_CLONE);
445 pixFindAreaFraction(pixt, tab, &fract);
446 numaAddNumber(na, fract);
447 pixDestroy(&pixt);
448 }
449 LEPT_FREE(tab);
450 return na;
451}
452
453
469l_ok
471 l_int32 *tab,
472 l_float32 *pfract)
473{
474l_int32 w, h, sum;
475l_int32 *tab8;
476
477 if (!pfract)
478 return ERROR_INT("&fract not defined", __func__, 1);
479 *pfract = 0.0;
480 if (!pixs || pixGetDepth(pixs) != 1)
481 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
482
483 if (!tab)
484 tab8 = makePixelSumTab8();
485 else
486 tab8 = tab;
487 pixGetDimensions(pixs, &w, &h, NULL);
488 pixCountPixels(pixs, &sum, tab8);
489 *pfract = (l_float32)sum / (l_float32)(w * h);
490
491 if (!tab) LEPT_FREE(tab8);
492 return 0;
493}
494
495
515NUMA *
517 PIX *pixm,
518 l_int32 debug)
519{
520l_int32 i, n, full;
521l_int32 *tab;
522l_float32 fract;
523BOX *box;
524NUMA *na;
525PIX *pix;
526
527 if (!pixa)
528 return (NUMA *)ERROR_PTR("pixa not defined", __func__, NULL);
529 if (!pixm || pixGetDepth(pixm) != 1)
530 return (NUMA *)ERROR_PTR("pixm undefined or not 1 bpp", __func__, NULL);
531
532 n = pixaGetCount(pixa);
533 na = numaCreate(n);
534 tab = makePixelSumTab8();
535 pixaIsFull(pixa, NULL, &full); /* check boxa */
536 box = NULL;
537 for (i = 0; i < n; i++) {
538 pix = pixaGetPix(pixa, i, L_CLONE);
539 if (full)
540 box = pixaGetBox(pixa, i, L_CLONE);
541 pixFindAreaFractionMasked(pix, box, pixm, tab, &fract);
542 numaAddNumber(na, fract);
543 boxDestroy(&box);
544 pixDestroy(&pix);
545 }
546 LEPT_FREE(tab);
547
548 if (debug) {
549 l_int32 w, h;
550 PIX *pix1, *pix2;
551 pixGetDimensions(pixm, &w, &h, NULL);
552 pix1 = pixaDisplay(pixa, w, h); /* recover original image */
553 pix2 = pixCreate(w, h, 8); /* make an 8 bpp white image ... */
554 pixSetColormap(pix2, pixcmapCreate(8)); /* that's cmapped ... */
555 pixSetBlackOrWhite(pix2, L_SET_WHITE); /* and init to white */
556 pixSetMaskedCmap(pix2, pix1, 0, 0, 255, 0, 0); /* color all fg red */
557 pixRasterop(pix1, 0, 0, w, h, PIX_MASK, pixm, 0, 0);
558 pixSetMaskedCmap(pix2, pix1, 0, 0, 0, 255, 0); /* turn masked green */
559 pixDisplay(pix2, 100, 100);
560 pixDestroy(&pix1);
561 pixDestroy(&pix2);
562 }
563
564 return na;
565}
566
567
592l_ok
594 BOX *box,
595 PIX *pixm,
596 l_int32 *tab,
597 l_float32 *pfract)
598{
599l_int32 x, y, w, h, sum, masksum;
600l_int32 *tab8;
601PIX *pix1;
602
603 if (!pfract)
604 return ERROR_INT("&fract not defined", __func__, 1);
605 *pfract = 0.0;
606 if (!pixs || pixGetDepth(pixs) != 1)
607 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
608 if (!pixm || pixGetDepth(pixm) != 1)
609 return ERROR_INT("pixm not defined or not 1 bpp", __func__, 1);
610
611 if (!tab)
612 tab8 = makePixelSumTab8();
613 else
614 tab8 = tab;
615 x = y = 0;
616 if (box)
617 boxGetGeometry(box, &x, &y, NULL, NULL);
618 pixGetDimensions(pixs, &w, &h, NULL);
619
620 pix1 = pixCopy(NULL, pixs);
621 pixRasterop(pix1, 0, 0, w, h, PIX_MASK, pixm, x, y);
622 pixCountPixels(pixs, &sum, tab8);
623 if (sum == 0) {
624 pixDestroy(&pix1);
625 if (!tab) LEPT_FREE(tab8);
626 return 0;
627 }
628 pixCountPixels(pix1, &masksum, tab8);
629 *pfract = (l_float32)masksum / (l_float32)sum;
630
631 if (!tab) LEPT_FREE(tab8);
632 pixDestroy(&pix1);
633 return 0;
634}
635
636
649NUMA *
651{
652l_int32 i, n, w, h;
653NUMA *na;
654PIX *pixt;
655
656 if (!pixa)
657 return (NUMA *)ERROR_PTR("pixa not defined", __func__, NULL);
658
659 n = pixaGetCount(pixa);
660 na = numaCreate(n);
661 for (i = 0; i < n; i++) {
662 pixt = pixaGetPix(pixa, i, L_CLONE);
663 pixGetDimensions(pixt, &w, &h, NULL);
664 numaAddNumber(na, (l_float32)w / (l_float32)h);
665 pixDestroy(&pixt);
666 }
667 return na;
668}
669
670
683NUMA *
685{
686l_int32 i, n, w, h;
687NUMA *na;
688PIX *pixt;
689
690 if (!pixa)
691 return (NUMA *)ERROR_PTR("pixa not defined", __func__, NULL);
692
693 n = pixaGetCount(pixa);
694 na = numaCreate(n);
695 for (i = 0; i < n; i++) {
696 pixt = pixaGetPix(pixa, i, L_CLONE);
697 pixGetDimensions(pixt, &w, &h, NULL);
698 numaAddNumber(na, w * h);
699 pixDestroy(&pixt);
700 }
701 return na;
702}
703
704
721l_ok
723 PIX *pixs2,
724 l_int32 x2,
725 l_int32 y2,
726 l_int32 *tab,
727 l_float32 *pratio,
728 l_int32 *pnoverlap)
729{
730l_int32 *tab8;
731l_int32 w, h, nintersect, nunion;
732PIX *pixt;
733
734 if (pnoverlap) *pnoverlap = 0;
735 if (!pratio)
736 return ERROR_INT("&ratio not defined", __func__, 1);
737 *pratio = 0.0;
738 if (!pixs1 || pixGetDepth(pixs1) != 1)
739 return ERROR_INT("pixs1 not defined or not 1 bpp", __func__, 1);
740 if (!pixs2 || pixGetDepth(pixs2) != 1)
741 return ERROR_INT("pixs2 not defined or not 1 bpp", __func__, 1);
742
743 if (!tab)
744 tab8 = makePixelSumTab8();
745 else
746 tab8 = tab;
747
748 pixGetDimensions(pixs2, &w, &h, NULL);
749 pixt = pixCopy(NULL, pixs1);
750 pixRasterop(pixt, x2, y2, w, h, PIX_MASK, pixs2, 0, 0); /* AND */
751 pixCountPixels(pixt, &nintersect, tab8);
752 if (pnoverlap)
753 *pnoverlap = nintersect;
754 pixCopy(pixt, pixs1);
755 pixRasterop(pixt, x2, y2, w, h, PIX_PAINT, pixs2, 0, 0); /* OR */
756 pixCountPixels(pixt, &nunion, tab8);
757 if (!tab) LEPT_FREE(tab8);
758 pixDestroy(&pixt);
759
760 if (nunion > 0)
761 *pratio = (l_float32)nintersect / (l_float32)nunion;
762 return 0;
763}
764
765
786BOXA *
788 l_int32 dist,
789 l_int32 minw,
790 l_int32 minh)
791{
792l_int32 w, h, i, n, conforms;
793BOX *box;
794BOXA *boxa, *boxad;
795PIX *pix;
796PIXA *pixa;
797
798 if (!pixs || pixGetDepth(pixs) != 1)
799 return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
800 if (dist < 0)
801 return (BOXA *)ERROR_PTR("dist must be >= 0", __func__, NULL);
802 if (minw <= 2 * dist && minh <= 2 * dist)
803 return (BOXA *)ERROR_PTR("invalid parameters", __func__, NULL);
804
805 boxa = pixConnComp(pixs, &pixa, 8);
806 boxad = boxaCreate(0);
807 n = pixaGetCount(pixa);
808 for (i = 0; i < n; i++) {
809 pix = pixaGetPix(pixa, i, L_CLONE);
810 pixGetDimensions(pix, &w, &h, NULL);
811 if (w < minw || h < minh) {
812 pixDestroy(&pix);
813 continue;
814 }
815 pixConformsToRectangle(pix, NULL, dist, &conforms);
816 if (conforms) {
817 box = boxaGetBox(boxa, i, L_COPY);
818 boxaAddBox(boxad, box, L_INSERT);
819 }
820 pixDestroy(&pix);
821 }
822 boxaDestroy(&boxa);
823 pixaDestroy(&pixa);
824 return boxad;
825}
826
827
864l_ok
866 BOX *box,
867 l_int32 dist,
868 l_int32 *pconforms)
869{
870l_int32 w, h, empty;
871PIX *pix1, *pix2;
872
873 if (!pconforms)
874 return ERROR_INT("&conforms not defined", __func__, 1);
875 *pconforms = 0;
876 if (!pixs || pixGetDepth(pixs) != 1)
877 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
878 if (dist < 0)
879 return ERROR_INT("dist must be >= 0", __func__, 1);
880 pixGetDimensions(pixs, &w, &h, NULL);
881 if (w <= 2 * dist || h <= 2 * dist) {
882 L_WARNING("automatic conformation: distance too large\n", __func__);
883 *pconforms = 1;
884 return 0;
885 }
886
887 /* Extract the region, if necessary */
888 if (box)
889 pix1 = pixClipRectangle(pixs, box, NULL);
890 else
891 pix1 = pixCopy(NULL, pixs);
892
893 /* Invert and fill from the boundary into the interior.
894 * Because we're considering the connected component in an
895 * 8-connected sense, we do the background filling as 4 c.c. */
896 pixInvert(pix1, pix1);
897 pix2 = pixExtractBorderConnComps(pix1, 4);
898
899 /* Mask out all pixels within a distance %dist from the box
900 * boundary. Any remaining pixels are from filling that goes
901 * more than %dist from the boundary. If no pixels remain,
902 * the component conforms to the bounding rectangle within
903 * a distance %dist. */
904 pixSetOrClearBorder(pix2, dist, dist, dist, dist, PIX_CLR);
905 pixZero(pix2, &empty);
906 pixDestroy(&pix1);
907 pixDestroy(&pix2);
908 *pconforms = (empty) ? 1 : 0;
909 return 0;
910}
911
912
913/*-----------------------------------------------------------------------*
914 * Extract rectangular region *
915 *-----------------------------------------------------------------------*/
929PIXA *
931 BOXA *boxa)
932{
933l_int32 i, n;
934BOX *box, *boxc;
935PIX *pix;
936PIXA *pixa;
937
938 if (!pixs)
939 return (PIXA *)ERROR_PTR("pixs not defined", __func__, NULL);
940 if (!boxa)
941 return (PIXA *)ERROR_PTR("boxa not defined", __func__, NULL);
942
943 n = boxaGetCount(boxa);
944 pixa = pixaCreate(n);
945 for (i = 0; i < n; i++) {
946 box = boxaGetBox(boxa, i, L_CLONE);
947 pix = pixClipRectangle(pixs, box, &boxc);
948 pixaAddPix(pixa, pix, L_INSERT);
949 pixaAddBox(pixa, boxc, L_INSERT);
950 boxDestroy(&box);
951 }
952
953 return pixa;
954}
955
956
993PIX *
995 BOX *box,
996 BOX **pboxc)
997{
998l_int32 w, h, d, bx, by, bw, bh;
999BOX *boxc;
1000PIX *pixd;
1001
1002 if (pboxc) *pboxc = NULL;
1003 if (!pixs)
1004 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1005 if (!box)
1006 return (PIX *)ERROR_PTR("box not defined", __func__, NULL);
1007
1008 /* Clip the input box to the pix */
1009 pixGetDimensions(pixs, &w, &h, &d);
1010 if ((boxc = boxClipToRectangle(box, w, h)) == NULL) {
1011 L_WARNING("box doesn't overlap pix\n", __func__);
1012 return NULL;
1013 }
1014 boxGetGeometry(boxc, &bx, &by, &bw, &bh);
1015
1016 /* Extract the block */
1017 if ((pixd = pixCreate(bw, bh, d)) == NULL) {
1018 boxDestroy(&boxc);
1019 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1020 }
1021 pixCopyResolution(pixd, pixs);
1022 pixCopyColormap(pixd, pixs);
1023 pixCopyText(pixd, pixs);
1024 pixRasterop(pixd, 0, 0, bw, bh, PIX_SRC, pixs, bx, by);
1025
1026 if (pboxc)
1027 *pboxc = boxc;
1028 else
1029 boxDestroy(&boxc);
1030
1031 return pixd;
1032}
1033
1034
1054PIX *
1056 BOX *box,
1057 l_int32 maxbord,
1058 BOX **pboxn)
1059{
1060l_int32 w, h, bx, by, bw, bh, bord;
1061BOX *box1;
1062PIX *pix1;
1063
1064 if (!pboxn)
1065 return (PIX *)ERROR_PTR("&boxn not defined", __func__, NULL);
1066 *pboxn = NULL;
1067 if (!pixs)
1068 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1069 if (!box)
1070 return (PIX *)ERROR_PTR("box not defined", __func__, NULL);
1071
1072 /* Determine the border width */
1073 pixGetDimensions(pixs, &w, &h, NULL);
1074 boxGetGeometry(box, &bx, &by, &bw, &bh);
1075 bord = L_MIN(bx, by);
1076 bord = L_MIN(bord, w - bx - bw);
1077 bord = L_MIN(bord, h - by - bh);
1078 bord = L_MIN(bord, maxbord);
1079
1080 if (bord <= 0) { /* standard clipping */
1081 pix1 = pixClipRectangle(pixs, box, NULL);
1082 pixGetDimensions(pix1, &w, &h, NULL);
1083 *pboxn = boxCreate(0, 0, w, h);
1084 return pix1;
1085 }
1086
1087 /* There is a positive border */
1088 box1 = boxAdjustSides(NULL, box, -bord, bord, -bord, bord);
1089 pix1 = pixClipRectangle(pixs, box1, NULL);
1090 boxDestroy(&box1);
1091 *pboxn = boxCreate(bord, bord, bw, bh);
1092 return pix1;
1093}
1094
1095
1125PIX *
1127 PIX *pixm,
1128 l_int32 x,
1129 l_int32 y,
1130 l_uint32 outval)
1131{
1132l_int32 wm, hm, index, rval, gval, bval;
1133l_uint32 pixel;
1134BOX *box;
1135PIX *pixmi, *pixd;
1136PIXCMAP *cmap;
1137
1138 if (!pixs)
1139 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1140 if (!pixm || pixGetDepth(pixm) != 1)
1141 return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", __func__, NULL);
1142
1143 /* Clip out the region specified by pixm and (x,y) */
1144 pixGetDimensions(pixm, &wm, &hm, NULL);
1145 box = boxCreate(x, y, wm, hm);
1146 pixd = pixClipRectangle(pixs, box, NULL);
1147
1148 /* Paint 'outval' (or something close to it if cmapped) through
1149 * the pixels not masked by pixm */
1150 cmap = pixGetColormap(pixd);
1151 pixmi = pixInvert(NULL, pixm);
1152 if (cmap) {
1153 extractRGBValues(outval, &rval, &gval, &bval);
1154 pixcmapGetNearestIndex(cmap, rval, gval, bval, &index);
1155 pixcmapGetColor(cmap, index, &rval, &gval, &bval);
1156 composeRGBPixel(rval, gval, bval, &pixel);
1157 pixPaintThroughMask(pixd, pixmi, 0, 0, pixel);
1158 } else {
1159 pixPaintThroughMask(pixd, pixmi, 0, 0, outval);
1160 }
1161
1162 boxDestroy(&box);
1163 pixDestroy(&pixmi);
1164 return pixd;
1165}
1166
1167
1185l_ok
1187 PIX *pixs2,
1188 PIX **ppixd1,
1189 PIX **ppixd2)
1190{
1191l_int32 w1, h1, w2, h2, w, h;
1192
1193 if (!ppixd1 || !ppixd2)
1194 return ERROR_INT("&pixd1 and &pixd2 not both defined", __func__, 1);
1195 *ppixd1 = *ppixd2 = NULL;
1196 if (!pixs1 || !pixs2)
1197 return ERROR_INT("pixs1 and pixs2 not defined", __func__, 1);
1198
1199 pixGetDimensions(pixs1, &w1, &h1, NULL);
1200 pixGetDimensions(pixs2, &w2, &h2, NULL);
1201 w = L_MIN(w1, w2);
1202 h = L_MIN(h1, h2);
1203
1204 *ppixd1 = pixCropToSize(pixs1, w, h);
1205 *ppixd2 = pixCropToSize(pixs2, w, h);
1206 if (*ppixd1 == NULL || *ppixd2 == NULL)
1207 return ERROR_INT("cropped image failure", __func__, 1);
1208 return 0;
1209}
1210
1211
1226PIX *
1228 l_int32 w,
1229 l_int32 h)
1230{
1231l_int32 ws, hs, wd, hd, d;
1232PIX *pixd;
1233
1234 if (!pixs)
1235 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1236
1237 pixGetDimensions(pixs, &ws, &hs, &d);
1238 if (ws <= w && hs <= h) /* no cropping necessary */
1239 return pixClone(pixs);
1240
1241 wd = L_MIN(ws, w);
1242 hd = L_MIN(hs, h);
1243 if ((pixd = pixCreate(wd, hd, d)) == NULL)
1244 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1245 pixCopyResolution(pixd, pixs);
1246 pixCopyColormap(pixd, pixs);
1247 pixCopyText(pixd, pixs);
1248 pixCopyInputFormat(pixd, pixs);
1249 pixRasterop(pixd, 0, 0, wd, hd, PIX_SRC, pixs, 0, 0);
1250 return pixd;
1251}
1252
1253
1278PIX *
1280 PIX *pixt,
1281 l_int32 w,
1282 l_int32 h)
1283{
1284l_int32 i, j, ws, hs, d;
1285PIX *pixd;
1286
1287 if (!pixs)
1288 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1289 if (!pixt && (w <= 0 || h <= 0))
1290 return (PIX *)ERROR_PTR("both w and h not > 0", __func__, NULL);
1291
1292 if (pixt) /* redefine w, h */
1293 pixGetDimensions(pixt, &w, &h, NULL);
1294 pixGetDimensions(pixs, &ws, &hs, &d);
1295 if (ws == w && hs == h)
1296 return pixCopy(NULL, pixs);
1297
1298 if ((pixd = pixCreate(w, h, d)) == NULL)
1299 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1300 pixCopyResolution(pixd, pixs);
1301 pixCopyColormap(pixd, pixs);
1302 pixCopyText(pixd, pixs);
1303 pixCopyInputFormat(pixd, pixs);
1304 pixRasterop(pixd, 0, 0, ws, hs, PIX_SRC, pixs, 0, 0);
1305 if (ws >= w && hs >= h)
1306 return pixd;
1307
1308 /* Replicate the last column and then the last row */
1309 if (ws < w) {
1310 for (j = ws; j < w; j++)
1311 pixRasterop(pixd, j, 0, 1, h, PIX_SRC, pixd, ws - 1, 0);
1312 }
1313 if (hs < h) {
1314 for (i = hs; i < h; i++)
1315 pixRasterop(pixd, 0, i, w, 1, PIX_SRC, pixd, 0, hs - 1);
1316 }
1317
1318 return pixd;
1319}
1320
1321
1322/*---------------------------------------------------------------------*
1323 * Select a connected component by size *
1324 *---------------------------------------------------------------------*/
1345PIX *
1346pixSelectComponentBySize(PIX *pixs,
1347 l_int32 rankorder,
1348 l_int32 type,
1349 l_int32 connectivity,
1350 BOX **pbox)
1351{
1352l_int32 n, empty, sorttype, index;
1353BOXA *boxa1;
1354NUMA *naindex;
1355PIX *pixd;
1356PIXA *pixa1, *pixa2;
1357
1358 if (pbox) *pbox = NULL;
1359 if (!pixs || pixGetDepth(pixs) != 1)
1360 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
1361 if (type == L_SELECT_BY_WIDTH)
1362 sorttype = L_SORT_BY_WIDTH;
1363 else if (type == L_SELECT_BY_HEIGHT)
1364 sorttype = L_SORT_BY_HEIGHT;
1365 else if (type == L_SELECT_BY_MAX_DIMENSION)
1366 sorttype = L_SORT_BY_MAX_DIMENSION;
1367 else if (type == L_SELECT_BY_AREA)
1368 sorttype = L_SORT_BY_AREA;
1369 else if (type == L_SELECT_BY_PERIMETER)
1370 sorttype = L_SORT_BY_PERIMETER;
1371 else
1372 return (PIX *)ERROR_PTR("invalid selection type", __func__, NULL);
1373 if (connectivity != 4 && connectivity != 8)
1374 return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
1375 pixZero(pixs, &empty);
1376 if (empty)
1377 return (PIX *)ERROR_PTR("no foreground pixels", __func__, NULL);
1378
1379 boxa1 = pixConnComp(pixs, &pixa1, connectivity);
1380 n = boxaGetCount(boxa1);
1381 if (rankorder < 0 || rankorder >= n)
1382 rankorder = n - 1; /* smallest */
1383 pixa2 = pixaSort(pixa1, sorttype, L_SORT_DECREASING, &naindex, L_CLONE);
1384 pixd = pixaGetPix(pixa2, rankorder, L_COPY);
1385 if (pbox) {
1386 numaGetIValue(naindex, rankorder, &index);
1387 *pbox = boxaGetBox(boxa1, index, L_COPY);
1388 }
1389
1390 numaDestroy(&naindex);
1391 boxaDestroy(&boxa1);
1392 pixaDestroy(&pixa1);
1393 pixaDestroy(&pixa2);
1394 return pixd;
1395}
1396
1397
1416PIX *
1417pixFilterComponentBySize(PIX *pixs,
1418 l_int32 rankorder,
1419 l_int32 type,
1420 l_int32 connectivity,
1421 BOX **pbox)
1422{
1423l_int32 x, y, w, h;
1424BOX *box;
1425PIX *pix1, *pix2;
1426
1427 if (!pixs || pixGetDepth(pixs) != 1)
1428 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
1429
1430 pix1 = pixSelectComponentBySize(pixs, rankorder, type, connectivity, &box);
1431 if (!pix1) {
1432 boxDestroy(&box);
1433 return (PIX *)ERROR_PTR("pix1 not made", __func__, NULL);
1434 }
1435
1436 /* Put the selected component in a new pix at the same
1437 * location as it had in %pixs */
1438 boxGetGeometry(box, &x, &y, &w, &h);
1439 pix2 = pixCreateTemplate(pixs);
1440 pixRasterop(pix2, x, y, w, h, PIX_SRC, pix1, 0, 0);
1441 if (pbox)
1442 *pbox = box;
1443 else
1444 boxDestroy(&box);
1445 pixDestroy(&pix1);
1446 return pix2;
1447}
1448
1449
1450/*---------------------------------------------------------------------*
1451 * Make special masks *
1452 *---------------------------------------------------------------------*/
1483PIX *
1485 l_int32 h,
1486 l_float32 hf,
1487 l_float32 vf,
1488 l_int32 type)
1489{
1490 if (w <= 0 || h <= 0)
1491 return (PIX *)ERROR_PTR("mask size 0", __func__, NULL);
1492 if (hf < 0.0 || hf > 1.0)
1493 return (PIX *)ERROR_PTR("invalid horiz fractions", __func__, NULL);
1494 if (vf < 0.0 || vf > 1.0)
1495 return (PIX *)ERROR_PTR("invalid vert fractions", __func__, NULL);
1496
1497 if (type == L_USE_INNER)
1498 return pixMakeFrameMask(w, h, hf, 1.0, vf, 1.0);
1499 else if (type == L_USE_OUTER)
1500 return pixMakeFrameMask(w, h, 0.0, hf, 0.0, vf);
1501 else
1502 return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
1503}
1504
1505
1538PIX *
1540 l_int32 h,
1541 l_float32 hf1,
1542 l_float32 hf2,
1543 l_float32 vf1,
1544 l_float32 vf2)
1545{
1546l_int32 h1, h2, v1, v2;
1547PIX *pixd;
1548
1549 if (w <= 0 || h <= 0)
1550 return (PIX *)ERROR_PTR("mask size 0", __func__, NULL);
1551 if (hf1 < 0.0 || hf1 > 1.0 || hf2 < 0.0 || hf2 > 1.0)
1552 return (PIX *)ERROR_PTR("invalid horiz fractions", __func__, NULL);
1553 if (vf1 < 0.0 || vf1 > 1.0 || vf2 < 0.0 || vf2 > 1.0)
1554 return (PIX *)ERROR_PTR("invalid vert fractions", __func__, NULL);
1555 if (hf1 > hf2 || vf1 > vf2)
1556 return (PIX *)ERROR_PTR("invalid relative sizes", __func__, NULL);
1557
1558 pixd = pixCreate(w, h, 1);
1559
1560 /* Special cases */
1561 if (hf1 == 0.0 && vf1 == 0.0 && hf2 == 1.0 && vf2 == 1.0) { /* full */
1562 pixSetAll(pixd);
1563 return pixd;
1564 }
1565 if (hf1 == hf2 && vf1 == vf2) { /* empty */
1566 return pixd;
1567 }
1568
1569 /* General case */
1570 h1 = 0.5f * hf1 * w;
1571 h2 = 0.5f * hf2 * w;
1572 v1 = 0.5f * vf1 * h;
1573 v2 = 0.5f * vf2 * h;
1574 pixRasterop(pixd, h1, v1, w - 2 * h1, h - 2 * v1, PIX_SET, NULL, 0, 0);
1575 if (hf2 < 1.0 && vf2 < 1.0)
1576 pixRasterop(pixd, h2, v2, w - 2 * h2, h - 2 * v2, PIX_CLR, NULL, 0, 0);
1577 return pixd;
1578}
1579
1580
1581/*---------------------------------------------------------------------*
1582 * Generate a covering of rectangles over connected components *
1583 *---------------------------------------------------------------------*/
1602PIX *
1604 l_int32 maxiters)
1605{
1606l_int32 empty, same, niters;
1607BOXA *boxa;
1608PIX *pix1, *pix2;
1609
1610 if (!pixs || pixGetDepth(pixs) != 1)
1611 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
1612 if (maxiters < 0)
1613 return (PIX *)ERROR_PTR("maxiters must be >= 0", __func__, NULL);
1614 if (maxiters == 0) maxiters = 50; /* ridiculously large number */
1615
1616 pixZero(pixs, &empty);
1617 pix1 = pixCreateTemplate(pixs);
1618 if (empty) return pix1;
1619
1620 /* Do first iteration */
1621 boxa = pixConnCompBB(pixs, 8);
1622 pixMaskBoxa(pix1, pix1, boxa, L_SET_PIXELS);
1623 boxaDestroy(&boxa);
1624 if (maxiters == 1) return pix1;
1625
1626 niters = 1;
1627 while (niters < maxiters) { /* continue to add pixels to pix1 */
1628 niters++;
1629 boxa = pixConnCompBB(pix1, 8);
1630 pix2 = pixCopy(NULL, pix1);
1631 pixMaskBoxa(pix1, pix1, boxa, L_SET_PIXELS);
1632 boxaDestroy(&boxa);
1633 pixEqual(pix1, pix2, &same);
1634 pixDestroy(&pix2);
1635 if (same) {
1636 L_INFO("%d iterations\n", __func__, niters - 1);
1637 return pix1;
1638 }
1639 }
1640 L_INFO("maxiters = %d reached\n", __func__, niters);
1641 return pix1;
1642}
1643
1644
1645/*---------------------------------------------------------------------*
1646 * Fraction of Fg pixels under a mask *
1647 *---------------------------------------------------------------------*/
1673l_ok
1675 PIX *pix2,
1676 l_float32 *pfract)
1677{
1678l_int32 w1, h1, w2, h2, empty, count1, count3;
1679PIX *pix3;
1680
1681 if (!pfract)
1682 return ERROR_INT("&fract not defined", __func__, 1);
1683 *pfract = 0.0;
1684 if (!pix1 || pixGetDepth(pix1) != 1)
1685 return ERROR_INT("pix1 not defined or not 1 bpp", __func__, 1);
1686 if (!pix2 || pixGetDepth(pix2) != 1)
1687 return ERROR_INT("pix2 not defined or not 1 bpp", __func__, 1);
1688
1689 pixGetDimensions(pix1, &w1, &h1, NULL);
1690 pixGetDimensions(pix2, &w2, &h2, NULL);
1691 if (w1 != w2 || h1 != h2) {
1692 L_INFO("sizes unequal: (w1,w2) = (%d,%d), (h1,h2) = (%d,%d)\n",
1693 __func__, w1, w2, h1, h2);
1694 }
1695 pixZero(pix1, &empty);
1696 if (empty) return 0;
1697 pixZero(pix2, &empty);
1698 if (empty) return 0;
1699
1700 pix3 = pixCopy(NULL, pix1);
1701 pixAnd(pix3, pix3, pix2);
1702 pixCountPixels(pix1, &count1, NULL); /* |1| */
1703 pixCountPixels(pix3, &count3, NULL); /* |1 & 2| */
1704 *pfract = (l_float32)count3 / (l_float32)count1;
1705 pixDestroy(&pix3);
1706 return 0;
1707}
1708
1709
1710/*---------------------------------------------------------------------*
1711 * Clip to Foreground *
1712 *---------------------------------------------------------------------*/
1727l_ok
1729 PIX **ppixd,
1730 BOX **pbox)
1731{
1732l_int32 w, h, wpl, nfullwords, extra, i, j;
1733l_int32 minx, miny, maxx, maxy;
1734l_uint32 result, mask;
1735l_uint32 *data, *line;
1736BOX *box;
1737
1738 if (ppixd) *ppixd = NULL;
1739 if (pbox) *pbox = NULL;
1740 if (!ppixd && !pbox)
1741 return ERROR_INT("no output requested", __func__, 1);
1742 if (!pixs || (pixGetDepth(pixs) != 1))
1743 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
1744
1745 pixGetDimensions(pixs, &w, &h, NULL);
1746 nfullwords = w / 32;
1747 extra = w & 31;
1748 mask = ~rmask32[32 - extra];
1749 wpl = pixGetWpl(pixs);
1750 data = pixGetData(pixs);
1751
1752 result = 0;
1753 for (i = 0, miny = 0; i < h; i++, miny++) {
1754 line = data + i * wpl;
1755 for (j = 0; j < nfullwords; j++)
1756 result |= line[j];
1757 if (extra)
1758 result |= (line[j] & mask);
1759 if (result)
1760 break;
1761 }
1762 if (miny == h) /* no ON pixels */
1763 return 1;
1764
1765 result = 0;
1766 for (i = h - 1, maxy = h - 1; i >= 0; i--, maxy--) {
1767 line = data + i * wpl;
1768 for (j = 0; j < nfullwords; j++)
1769 result |= line[j];
1770 if (extra)
1771 result |= (line[j] & mask);
1772 if (result)
1773 break;
1774 }
1775
1776 minx = 0;
1777 for (j = 0, minx = 0; j < w; j++, minx++) {
1778 for (i = 0; i < h; i++) {
1779 line = data + i * wpl;
1780 if (GET_DATA_BIT(line, j))
1781 goto minx_found;
1782 }
1783 }
1784
1785minx_found:
1786 for (j = w - 1, maxx = w - 1; j >= 0; j--, maxx--) {
1787 for (i = 0; i < h; i++) {
1788 line = data + i * wpl;
1789 if (GET_DATA_BIT(line, j))
1790 goto maxx_found;
1791 }
1792 }
1793
1794maxx_found:
1795 box = boxCreate(minx, miny, maxx - minx + 1, maxy - miny + 1);
1796
1797 if (ppixd)
1798 *ppixd = pixClipRectangle(pixs, box, NULL);
1799 if (pbox)
1800 *pbox = box;
1801 else
1802 boxDestroy(&box);
1803
1804 return 0;
1805}
1806
1807
1825l_ok
1827 l_int32 *pcanclip)
1828{
1829l_int32 i, j, w, h, wpl, found;
1830l_uint32 *data, *line;
1831
1832 if (!pcanclip)
1833 return ERROR_INT("&canclip not defined", __func__, 1);
1834 *pcanclip = 0;
1835 if (!pixs || (pixGetDepth(pixs) != 1))
1836 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
1837
1838 /* Check top and bottom raster lines */
1839 pixGetDimensions(pixs, &w, &h, NULL);
1840 data = pixGetData(pixs);
1841 wpl = pixGetWpl(pixs);
1842 found = FALSE;
1843 for (j = 0; found == FALSE && j < w; j++)
1844 found = GET_DATA_BIT(data, j);
1845 if (!found) {
1846 *pcanclip = 1;
1847 return 0;
1848 }
1849
1850 line = data + (h - 1) * wpl;
1851 found = FALSE;
1852 for (j = 0; found == FALSE && j < w; j++)
1853 found = GET_DATA_BIT(data, j);
1854 if (!found) {
1855 *pcanclip = 1;
1856 return 0;
1857 }
1858
1859 /* Check left and right edges */
1860 found = FALSE;
1861 for (i = 0, line = data; found == FALSE && i < h; line += wpl, i++)
1862 found = GET_DATA_BIT(line, 0);
1863 if (!found) {
1864 *pcanclip = 1;
1865 return 0;
1866 }
1867
1868 found = FALSE;
1869 for (i = 0, line = data; found == FALSE && i < h; line += wpl, i++)
1870 found = GET_DATA_BIT(line, w - 1);
1871 if (!found)
1872 *pcanclip = 1;
1873
1874 return 0; /* fg pixels found on all edges */
1875}
1876
1877
1895l_ok
1897 BOX *boxs,
1898 PIX **ppixd,
1899 BOX **pboxd)
1900{
1901l_int32 w, h, bx, by, bw, bh, cbw, cbh, left, right, top, bottom;
1902BOX *boxt, *boxd;
1903
1904 if (ppixd) *ppixd = NULL;
1905 if (pboxd) *pboxd = NULL;
1906 if (!ppixd && !pboxd)
1907 return ERROR_INT("no output requested", __func__, 1);
1908 if (!pixs || (pixGetDepth(pixs) != 1))
1909 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
1910
1911 if (!boxs)
1912 return pixClipToForeground(pixs, ppixd, pboxd);
1913
1914 pixGetDimensions(pixs, &w, &h, NULL);
1915 boxGetGeometry(boxs, &bx, &by, &bw, &bh);
1916 cbw = L_MIN(bw, w - bx);
1917 cbh = L_MIN(bh, h - by);
1918 if (cbw < 0 || cbh < 0)
1919 return ERROR_INT("box not within image", __func__, 1);
1920 boxt = boxCreate(bx, by, cbw, cbh);
1921
1922 if (pixScanForForeground(pixs, boxt, L_FROM_LEFT, &left)) {
1923 boxDestroy(&boxt);
1924 return 1;
1925 }
1926 pixScanForForeground(pixs, boxt, L_FROM_RIGHT, &right);
1927 pixScanForForeground(pixs, boxt, L_FROM_TOP, &top);
1928 pixScanForForeground(pixs, boxt, L_FROM_BOT, &bottom);
1929
1930 boxd = boxCreate(left, top, right - left + 1, bottom - top + 1);
1931 if (ppixd)
1932 *ppixd = pixClipRectangle(pixs, boxd, NULL);
1933 if (pboxd)
1934 *pboxd = boxd;
1935 else
1936 boxDestroy(&boxd);
1937
1938 boxDestroy(&boxt);
1939 return 0;
1940}
1941
1942
1959l_ok
1961 BOX *box,
1962 l_int32 scanflag,
1963 l_int32 *ploc)
1964{
1965l_int32 bx, by, bw, bh, x, xstart, xend, y, ystart, yend, wpl;
1966l_uint32 *data, *line;
1967BOX *boxt;
1968
1969 if (!ploc)
1970 return ERROR_INT("&loc not defined", __func__, 1);
1971 *ploc = 0;
1972 if (!pixs || (pixGetDepth(pixs) != 1))
1973 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
1974
1975 /* Clip box to pixs if it exists */
1976 pixGetDimensions(pixs, &bw, &bh, NULL);
1977 if (box) {
1978 if ((boxt = boxClipToRectangle(box, bw, bh)) == NULL)
1979 return ERROR_INT("invalid box", __func__, 1);
1980 boxGetGeometry(boxt, &bx, &by, &bw, &bh);
1981 boxDestroy(&boxt);
1982 } else {
1983 bx = by = 0;
1984 }
1985 xstart = bx;
1986 ystart = by;
1987 xend = bx + bw - 1;
1988 yend = by + bh - 1;
1989
1990 data = pixGetData(pixs);
1991 wpl = pixGetWpl(pixs);
1992 if (scanflag == L_FROM_LEFT) {
1993 for (x = xstart; x <= xend; x++) {
1994 for (y = ystart; y <= yend; y++) {
1995 line = data + y * wpl;
1996 if (GET_DATA_BIT(line, x)) {
1997 *ploc = x;
1998 return 0;
1999 }
2000 }
2001 }
2002 } else if (scanflag == L_FROM_RIGHT) {
2003 for (x = xend; x >= xstart; x--) {
2004 for (y = ystart; y <= yend; y++) {
2005 line = data + y * wpl;
2006 if (GET_DATA_BIT(line, x)) {
2007 *ploc = x;
2008 return 0;
2009 }
2010 }
2011 }
2012 } else if (scanflag == L_FROM_TOP) {
2013 for (y = ystart; y <= yend; y++) {
2014 line = data + y * wpl;
2015 for (x = xstart; x <= xend; x++) {
2016 if (GET_DATA_BIT(line, x)) {
2017 *ploc = y;
2018 return 0;
2019 }
2020 }
2021 }
2022 } else if (scanflag == L_FROM_BOT) {
2023 for (y = yend; y >= ystart; y--) {
2024 line = data + y * wpl;
2025 for (x = xstart; x <= xend; x++) {
2026 if (GET_DATA_BIT(line, x)) {
2027 *ploc = y;
2028 return 0;
2029 }
2030 }
2031 }
2032 } else {
2033 return ERROR_INT("invalid scanflag", __func__, 1);
2034 }
2035
2036 return 1; /* no fg found */
2037}
2038
2039
2073l_ok
2075 BOX *boxs,
2076 l_int32 lowthresh,
2077 l_int32 highthresh,
2078 l_int32 maxwidth,
2079 l_int32 factor,
2080 PIX **ppixd,
2081 BOX **pboxd)
2082{
2083l_int32 w, h, bx, by, bw, bh, cbw, cbh, left, right, top, bottom;
2084l_int32 lfound, rfound, tfound, bfound, change;
2085BOX *boxt, *boxd;
2086
2087 if (ppixd) *ppixd = NULL;
2088 if (pboxd) *pboxd = NULL;
2089 if (!ppixd && !pboxd)
2090 return ERROR_INT("no output requested", __func__, 1);
2091 if (!pixs || (pixGetDepth(pixs) != 1))
2092 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
2093 if (lowthresh < 1 || highthresh < 1 ||
2094 lowthresh > highthresh || maxwidth < 1)
2095 return ERROR_INT("invalid thresholds", __func__, 1);
2096 factor = L_MIN(1, factor);
2097
2098 if (lowthresh == 1 && highthresh == 1)
2099 return pixClipBoxToForeground(pixs, boxs, ppixd, pboxd);
2100
2101 pixGetDimensions(pixs, &w, &h, NULL);
2102 if (boxs) {
2103 boxGetGeometry(boxs, &bx, &by, &bw, &bh);
2104 cbw = L_MIN(bw, w - bx);
2105 cbh = L_MIN(bh, h - by);
2106 if (cbw < 0 || cbh < 0)
2107 return ERROR_INT("box not within image", __func__, 1);
2108 boxt = boxCreate(bx, by, cbw, cbh);
2109 } else {
2110 boxt = boxCreate(0, 0, w, h);
2111 }
2112
2113 lfound = rfound = tfound = bfound = 0;
2114 while (!lfound || !rfound || !tfound || !bfound) {
2115 change = 0;
2116 if (!lfound) {
2117 if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth,
2118 factor, L_FROM_LEFT, &left)) {
2119 lfound = 1;
2120 change = 1;
2121 boxRelocateOneSide(boxt, boxt, left, L_FROM_LEFT);
2122 }
2123 }
2124 if (!rfound) {
2125 if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth,
2126 factor, L_FROM_RIGHT, &right)) {
2127 rfound = 1;
2128 change = 1;
2129 boxRelocateOneSide(boxt, boxt, right, L_FROM_RIGHT);
2130 }
2131 }
2132 if (!tfound) {
2133 if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth,
2134 factor, L_FROM_TOP, &top)) {
2135 tfound = 1;
2136 change = 1;
2137 boxRelocateOneSide(boxt, boxt, top, L_FROM_TOP);
2138 }
2139 }
2140 if (!bfound) {
2141 if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth,
2142 factor, L_FROM_BOT, &bottom)) {
2143 bfound = 1;
2144 change = 1;
2145 boxRelocateOneSide(boxt, boxt, bottom, L_FROM_BOT);
2146 }
2147 }
2148
2149#if DEBUG_EDGES
2150 lept_stderr("iter: %d %d %d %d\n", lfound, rfound, tfound, bfound);
2151#endif /* DEBUG_EDGES */
2152
2153 if (change == 0) break;
2154 }
2155 boxDestroy(&boxt);
2156
2157 if (change == 0)
2158 return ERROR_INT("not all edges found", __func__, 1);
2159
2160 boxd = boxCreate(left, top, right - left + 1, bottom - top + 1);
2161 if (ppixd)
2162 *ppixd = pixClipRectangle(pixs, boxd, NULL);
2163 if (pboxd)
2164 *pboxd = boxd;
2165 else
2166 boxDestroy(&boxd);
2167
2168 return 0;
2169}
2170
2171
2201l_ok
2203 BOX *box,
2204 l_int32 lowthresh,
2205 l_int32 highthresh,
2206 l_int32 maxwidth,
2207 l_int32 factor,
2208 l_int32 scanflag,
2209 l_int32 *ploc)
2210{
2211l_int32 bx, by, bw, bh, foundmin, loc, sum, wpl;
2212l_int32 x, xstart, xend, y, ystart, yend;
2213l_uint32 *data, *line;
2214BOX *boxt;
2215
2216 if (!ploc)
2217 return ERROR_INT("&ploc not defined", __func__, 1);
2218 *ploc = 0;
2219 if (!pixs || (pixGetDepth(pixs) != 1))
2220 return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
2221 if (lowthresh < 1 || highthresh < 1 ||
2222 lowthresh > highthresh || maxwidth < 1)
2223 return ERROR_INT("invalid thresholds", __func__, 1);
2224 factor = L_MIN(1, factor);
2225
2226 /* Clip box to pixs if it exists */
2227 pixGetDimensions(pixs, &bw, &bh, NULL);
2228 if (box) {
2229 if ((boxt = boxClipToRectangle(box, bw, bh)) == NULL)
2230 return ERROR_INT("invalid box", __func__, 1);
2231 boxGetGeometry(boxt, &bx, &by, &bw, &bh);
2232 boxDestroy(&boxt);
2233 } else {
2234 bx = by = 0;
2235 }
2236 xstart = bx;
2237 ystart = by;
2238 xend = bx + bw - 1;
2239 yend = by + bh - 1;
2240
2241 data = pixGetData(pixs);
2242 wpl = pixGetWpl(pixs);
2243 foundmin = 0;
2244 if (scanflag == L_FROM_LEFT) {
2245 for (x = xstart; x <= xend; x++) {
2246 sum = 0;
2247 for (y = ystart; y <= yend; y += factor) {
2248 line = data + y * wpl;
2249 if (GET_DATA_BIT(line, x))
2250 sum++;
2251 }
2252 if (!foundmin && sum < lowthresh)
2253 continue;
2254 if (!foundmin) { /* save the loc of the beginning of the edge */
2255 foundmin = 1;
2256 loc = x;
2257 }
2258 if (sum >= highthresh) {
2259#if DEBUG_EDGES
2260 lept_stderr("Left: x = %d, loc = %d\n", x, loc);
2261#endif /* DEBUG_EDGES */
2262 if (x - loc < maxwidth) {
2263 *ploc = loc;
2264 return 0;
2265 } else {
2266 return 1;
2267 }
2268 }
2269 }
2270 } else if (scanflag == L_FROM_RIGHT) {
2271 for (x = xend; x >= xstart; x--) {
2272 sum = 0;
2273 for (y = ystart; y <= yend; y += factor) {
2274 line = data + y * wpl;
2275 if (GET_DATA_BIT(line, x))
2276 sum++;
2277 }
2278 if (!foundmin && sum < lowthresh)
2279 continue;
2280 if (!foundmin) {
2281 foundmin = 1;
2282 loc = x;
2283 }
2284 if (sum >= highthresh) {
2285#if DEBUG_EDGES
2286 lept_stderr("Right: x = %d, loc = %d\n", x, loc);
2287#endif /* DEBUG_EDGES */
2288 if (loc - x < maxwidth) {
2289 *ploc = loc;
2290 return 0;
2291 } else {
2292 return 1;
2293 }
2294 }
2295 }
2296 } else if (scanflag == L_FROM_TOP) {
2297 for (y = ystart; y <= yend; y++) {
2298 sum = 0;
2299 line = data + y * wpl;
2300 for (x = xstart; x <= xend; x += factor) {
2301 if (GET_DATA_BIT(line, x))
2302 sum++;
2303 }
2304 if (!foundmin && sum < lowthresh)
2305 continue;
2306 if (!foundmin) {
2307 foundmin = 1;
2308 loc = y;
2309 }
2310 if (sum >= highthresh) {
2311#if DEBUG_EDGES
2312 lept_stderr("Top: y = %d, loc = %d\n", y, loc);
2313#endif /* DEBUG_EDGES */
2314 if (y - loc < maxwidth) {
2315 *ploc = loc;
2316 return 0;
2317 } else {
2318 return 1;
2319 }
2320 }
2321 }
2322 } else if (scanflag == L_FROM_BOT) {
2323 for (y = yend; y >= ystart; y--) {
2324 sum = 0;
2325 line = data + y * wpl;
2326 for (x = xstart; x <= xend; x += factor) {
2327 if (GET_DATA_BIT(line, x))
2328 sum++;
2329 }
2330 if (!foundmin && sum < lowthresh)
2331 continue;
2332 if (!foundmin) {
2333 foundmin = 1;
2334 loc = y;
2335 }
2336 if (sum >= highthresh) {
2337#if DEBUG_EDGES
2338 lept_stderr("Bottom: y = %d, loc = %d\n", y, loc);
2339#endif /* DEBUG_EDGES */
2340 if (loc - y < maxwidth) {
2341 *ploc = loc;
2342 return 0;
2343 } else {
2344 return 1;
2345 }
2346 }
2347 }
2348 } else {
2349 return ERROR_INT("invalid scanflag", __func__, 1);
2350 }
2351
2352 return 1; /* edge not found */
2353}
2354
2355
2356/*---------------------------------------------------------------------*
2357 * Extract pixel averages and reversals along lines *
2358 *---------------------------------------------------------------------*/
2380NUMA *
2382 l_int32 x1,
2383 l_int32 y1,
2384 l_int32 x2,
2385 l_int32 y2,
2386 l_int32 factor)
2387{
2388l_int32 i, w, h, d, xmin, ymin, xmax, ymax, npts, direction;
2389l_uint32 val;
2390l_float32 x, y;
2391l_float64 slope;
2392NUMA *na;
2393PTA *pta;
2394
2395 if (!pixs)
2396 return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
2397 pixGetDimensions(pixs, &w, &h, &d);
2398 if (d != 1 && d != 8)
2399 return (NUMA *)ERROR_PTR("d not 1 or 8 bpp", __func__, NULL);
2400 if (pixGetColormap(pixs))
2401 return (NUMA *)ERROR_PTR("pixs has a colormap", __func__, NULL);
2402 if (factor < 1) {
2403 L_WARNING("factor must be >= 1; setting to 1\n", __func__);
2404 factor = 1;
2405 }
2406
2407 /* Clip line to the image */
2408 x1 = L_MAX(0, L_MIN(x1, w - 1));
2409 x2 = L_MAX(0, L_MIN(x2, w - 1));
2410 y1 = L_MAX(0, L_MIN(y1, h - 1));
2411 y2 = L_MAX(0, L_MIN(y2, h - 1));
2412
2413 if (x1 == x2 && y1 == y2) {
2414 pixGetPixel(pixs, x1, y1, &val);
2415 na = numaCreate(1);
2416 numaAddNumber(na, val);
2417 return na;
2418 }
2419
2420 if (y1 == y2)
2421 direction = L_HORIZONTAL_LINE;
2422 else if (x1 == x2)
2423 direction = L_VERTICAL_LINE;
2424 else
2425 direction = L_OBLIQUE_LINE;
2426
2427 na = numaCreate(0);
2428 if (direction == L_HORIZONTAL_LINE) { /* plot against x */
2429 xmin = L_MIN(x1, x2);
2430 xmax = L_MAX(x1, x2);
2431 numaSetParameters(na, xmin, factor);
2432 for (i = xmin; i <= xmax; i += factor) {
2433 pixGetPixel(pixs, i, y1, &val);
2434 numaAddNumber(na, val);
2435 }
2436 } else if (direction == L_VERTICAL_LINE) { /* plot against y */
2437 ymin = L_MIN(y1, y2);
2438 ymax = L_MAX(y1, y2);
2439 numaSetParameters(na, ymin, factor);
2440 for (i = ymin; i <= ymax; i += factor) {
2441 pixGetPixel(pixs, x1, i, &val);
2442 numaAddNumber(na, val);
2443 }
2444 } else { /* direction == L_OBLIQUE_LINE */
2445 slope = (l_float64)((y2 - y1) / (x2 - x1));
2446 if (L_ABS(slope) < 1.0) { /* quasi-horizontal */
2447 xmin = L_MIN(x1, x2);
2448 xmax = L_MAX(x1, x2);
2449 ymin = (xmin == x1) ? y1 : y2; /* pt that goes with xmin */
2450 ymax = (ymin == y1) ? y2 : y1; /* pt that goes with xmax */
2451 pta = generatePtaLine(xmin, ymin, xmax, ymax);
2452 numaSetParameters(na, xmin, (l_float32)factor);
2453 } else { /* quasi-vertical */
2454 ymin = L_MIN(y1, y2);
2455 ymax = L_MAX(y1, y2);
2456 xmin = (ymin == y1) ? x1 : x2; /* pt that goes with ymin */
2457 xmax = (xmin == x1) ? x2 : x1; /* pt that goes with ymax */
2458 pta = generatePtaLine(xmin, ymin, xmax, ymax);
2459 numaSetParameters(na, ymin, (l_float32)factor);
2460 }
2461 npts = ptaGetCount(pta);
2462 for (i = 0; i < npts; i += factor) {
2463 ptaGetPt(pta, i, &x, &y);
2464 pixGetPixel(pixs, (l_int32)x, (l_int32)y, &val);
2465 numaAddNumber(na, val);
2466 }
2467
2468#if 0 /* debugging */
2469 pixPlotAlongPta(pixs, pta, GPLOT_PNG, NULL);
2470#endif
2471
2472 ptaDestroy(&pta);
2473 }
2474
2475 return na;
2476}
2477
2478
2498l_float32
2500 l_int32 x1,
2501 l_int32 y1,
2502 l_int32 x2,
2503 l_int32 y2,
2504 l_int32 factor)
2505{
2506l_int32 i, j, w, h, d, direction, count, wpl;
2507l_uint32 *data, *line;
2508l_float32 sum;
2509
2510 if (!pixs)
2511 return ERROR_INT("pixs not defined", __func__, 1);
2512 pixGetDimensions(pixs, &w, &h, &d);
2513 if (d != 1 && d != 8)
2514 return ERROR_INT("d not 1 or 8 bpp", __func__, 1);
2515 if (pixGetColormap(pixs))
2516 return ERROR_INT("pixs has a colormap", __func__, 1);
2517 if (x1 > x2 || y1 > y2)
2518 return ERROR_INT("x1 > x2 or y1 > y2", __func__, 1);
2519
2520 if (y1 == y2) {
2521 x1 = L_MAX(0, x1);
2522 x2 = L_MIN(w - 1, x2);
2523 y1 = L_MAX(0, L_MIN(y1, h - 1));
2524 direction = L_HORIZONTAL_LINE;
2525 } else if (x1 == x2) {
2526 y1 = L_MAX(0, y1);
2527 y2 = L_MIN(h - 1, y2);
2528 x1 = L_MAX(0, L_MIN(x1, w - 1));
2529 direction = L_VERTICAL_LINE;
2530 } else {
2531 return ERROR_INT("line neither horiz nor vert", __func__, 1);
2532 }
2533
2534 if (factor < 1) {
2535 L_WARNING("factor must be >= 1; setting to 1\n", __func__);
2536 factor = 1;
2537 }
2538
2539 data = pixGetData(pixs);
2540 wpl = pixGetWpl(pixs);
2541 sum = 0;
2542 count = 0;
2543 if (direction == L_HORIZONTAL_LINE) {
2544 line = data + y1 * wpl;
2545 for (j = x1, count = 0; j <= x2; count++, j += factor) {
2546 if (d == 1)
2547 sum += GET_DATA_BIT(line, j);
2548 else /* d == 8 */
2549 sum += GET_DATA_BYTE(line, j);
2550 }
2551 } else if (direction == L_VERTICAL_LINE) {
2552 for (i = y1, count = 0; i <= y2; count++, i += factor) {
2553 line = data + i * wpl;
2554 if (d == 1)
2555 sum += GET_DATA_BIT(line, x1);
2556 else /* d == 8 */
2557 sum += GET_DATA_BYTE(line, x1);
2558 }
2559 }
2560
2561 return sum / (l_float32)count;
2562}
2563
2564
2595NUMA *
2597 l_float32 fract,
2598 l_int32 dir,
2599 l_int32 first,
2600 l_int32 last,
2601 l_int32 factor1,
2602 l_int32 factor2)
2603{
2604l_int32 i, j, w, h, d, start, end;
2605l_float32 ave;
2606NUMA *nad;
2607PIX *pixr, *pixg;
2608
2609 if (!pixs)
2610 return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
2611 if (fract < 0.0 || fract > 1.0)
2612 return (NUMA *)ERROR_PTR("fract < 0.0 or > 1.0", __func__, NULL);
2613 if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE)
2614 return (NUMA *)ERROR_PTR("invalid direction", __func__, NULL);
2615 if (first < 0) first = 0;
2616 if (last < first)
2617 return (NUMA *)ERROR_PTR("last must be >= first", __func__, NULL);
2618 if (factor1 < 1) {
2619 L_WARNING("factor1 must be >= 1; setting to 1\n", __func__);
2620 factor1 = 1;
2621 }
2622 if (factor2 < 1) {
2623 L_WARNING("factor2 must be >= 1; setting to 1\n", __func__);
2624 factor2 = 1;
2625 }
2626
2627 /* Use 1 or 8 bpp, without colormap */
2628 if (pixGetColormap(pixs))
2629 pixr = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
2630 else
2631 pixr = pixClone(pixs);
2632 pixGetDimensions(pixr, &w, &h, &d);
2633 if (d == 1)
2634 pixg = pixClone(pixr);
2635 else
2636 pixg = pixConvertTo8(pixr, 0);
2637
2638 nad = numaCreate(0); /* output: samples in slow scan direction */
2639 numaSetParameters(nad, 0, factor2);
2640 if (dir == L_HORIZONTAL_LINE) {
2641 start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)w);
2642 end = w - start;
2643 if (last > h - 1) {
2644 L_WARNING("last > h - 1; clipping\n", __func__);
2645 last = h - 1;
2646 }
2647 for (i = first; i <= last; i += factor2) {
2648 ave = pixAverageOnLine(pixg, start, i, end, i, factor1);
2649 numaAddNumber(nad, ave);
2650 }
2651 } else if (dir == L_VERTICAL_LINE) {
2652 start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)h);
2653 end = h - start;
2654 if (last > w - 1) {
2655 L_WARNING("last > w - 1; clipping\n", __func__);
2656 last = w - 1;
2657 }
2658 for (j = first; j <= last; j += factor2) {
2659 ave = pixAverageOnLine(pixg, j, start, j, end, factor1);
2660 numaAddNumber(nad, ave);
2661 }
2662 }
2663
2664 pixDestroy(&pixr);
2665 pixDestroy(&pixg);
2666 return nad;
2667}
2668
2669
2708NUMA *
2710 l_float32 fract,
2711 l_int32 dir,
2712 l_int32 first,
2713 l_int32 last,
2714 l_int32 minreversal,
2715 l_int32 factor1,
2716 l_int32 factor2)
2717{
2718l_int32 i, j, w, h, d, start, end, nr;
2719NUMA *naline, *nad;
2720PIX *pixr, *pixg;
2721
2722 if (!pixs)
2723 return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
2724 if (fract < 0.0 || fract > 1.0)
2725 return (NUMA *)ERROR_PTR("fract < 0.0 or > 1.0", __func__, NULL);
2726 if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE)
2727 return (NUMA *)ERROR_PTR("invalid direction", __func__, NULL);
2728 if (first < 0) first = 0;
2729 if (last < first)
2730 return (NUMA *)ERROR_PTR("last must be >= first", __func__, NULL);
2731 if (factor1 < 1) {
2732 L_WARNING("factor1 must be >= 1; setting to 1\n", __func__);
2733 factor1 = 1;
2734 }
2735 if (factor2 < 1) {
2736 L_WARNING("factor2 must be >= 1; setting to 1\n", __func__);
2737 factor2 = 1;
2738 }
2739
2740 /* Use 1 or 8 bpp, without colormap */
2741 if (pixGetColormap(pixs))
2742 pixr = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
2743 else
2744 pixr = pixClone(pixs);
2745 pixGetDimensions(pixr, &w, &h, &d);
2746 if (d == 1) {
2747 pixg = pixClone(pixr);
2748 minreversal = 1; /* enforce this */
2749 } else {
2750 pixg = pixConvertTo8(pixr, 0);
2751 }
2752
2753 nad = numaCreate(0); /* output: samples in slow scan direction */
2754 numaSetParameters(nad, 0, factor2);
2755 if (dir == L_HORIZONTAL_LINE) {
2756 start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)w);
2757 end = w - start;
2758 if (last > h - 1) {
2759 L_WARNING("last > h - 1; clipping\n", __func__);
2760 last = h - 1;
2761 }
2762 for (i = first; i <= last; i += factor2) {
2763 naline = pixExtractOnLine(pixg, start, i, end, i, factor1);
2764 numaCountReversals(naline, minreversal, &nr, NULL);
2765 numaAddNumber(nad, nr);
2766 numaDestroy(&naline);
2767 }
2768 } else if (dir == L_VERTICAL_LINE) {
2769 start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)h);
2770 end = h - start;
2771 if (last > w - 1) {
2772 L_WARNING("last > w - 1; clipping\n", __func__);
2773 last = w - 1;
2774 }
2775 for (j = first; j <= last; j += factor2) {
2776 naline = pixExtractOnLine(pixg, j, start, j, end, factor1);
2777 numaCountReversals(naline, minreversal, &nr, NULL);
2778 numaAddNumber(nad, nr);
2779 numaDestroy(&naline);
2780 }
2781 }
2782
2783 pixDestroy(&pixr);
2784 pixDestroy(&pixg);
2785 return nad;
2786}
2787
2788
2789/*---------------------------------------------------------------------*
2790 * Extract windowed variance along a line *
2791 *---------------------------------------------------------------------*/
2815l_ok
2817 l_int32 dir,
2818 l_int32 loc,
2819 l_int32 c1,
2820 l_int32 c2,
2821 l_int32 size,
2822 NUMA **pnad)
2823{
2824l_int32 i, j, w, h, cmin, cmax, maxloc, n, x, y;
2825l_uint32 val;
2826l_float32 norm, rootvar;
2827l_float32 *array;
2828l_float64 sum1, sum2, ave, var;
2829NUMA *na1, *nad;
2830PTA *pta;
2831
2832 if (!pnad)
2833 return ERROR_INT("&nad not defined", __func__, 1);
2834 *pnad = NULL;
2835 if (!pixs || pixGetDepth(pixs) != 8)
2836 return ERROR_INT("pixs not defined or not 8bpp", __func__, 1);
2837 if (size < 2)
2838 return ERROR_INT("window size must be > 1", __func__, 1);
2839 if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE)
2840 return ERROR_INT("invalid direction", __func__, 1);
2841 pixGetDimensions(pixs, &w, &h, NULL);
2842 maxloc = (dir == L_HORIZONTAL_LINE) ? h - 1 : w - 1;
2843 if (loc < 0 || loc > maxloc)
2844 return ERROR_INT("invalid line position", __func__, 1);
2845
2846 /* Clip line to the image */
2847 cmin = L_MIN(c1, c2);
2848 cmax = L_MAX(c1, c2);
2849 maxloc = (dir == L_HORIZONTAL_LINE) ? w - 1 : h - 1;
2850 cmin = L_MAX(0, L_MIN(cmin, maxloc));
2851 cmax = L_MAX(0, L_MIN(cmax, maxloc));
2852 n = cmax - cmin + 1;
2853
2854 /* Generate pta along the line */
2855 pta = ptaCreate(n);
2856 if (dir == L_HORIZONTAL_LINE) {
2857 for (i = cmin; i <= cmax; i++)
2858 ptaAddPt(pta, i, loc);
2859 } else { /* vertical line */
2860 for (i = cmin; i <= cmax; i++)
2861 ptaAddPt(pta, loc, i);
2862 }
2863
2864 /* Get numa of pixel values on the line */
2865 na1 = numaCreate(n);
2866 numaSetParameters(na1, cmin, 1);
2867 for (i = 0; i < n; i++) {
2868 ptaGetIPt(pta, i, &x, &y);
2869 pixGetPixel(pixs, x, y, &val);
2870 numaAddNumber(na1, val);
2871 }
2872 array = numaGetFArray(na1, L_NOCOPY);
2873 ptaDestroy(&pta);
2874
2875 /* Compute root variance on overlapping windows */
2876 nad = numaCreate(n);
2877 *pnad = nad;
2878 numaSetParameters(nad, cmin + size / 2, 1);
2879 norm = 1.0f / (l_float32)size;
2880 for (i = 0; i < n - size; i++) { /* along the line */
2881 sum1 = sum2 = 0;
2882 for (j = 0; j < size; j++) { /* over the window */
2883 val = array[i + j];
2884 sum1 += val;
2885 sum2 += (l_float64)(val) * val;
2886 }
2887 ave = norm * sum1;
2888 var = norm * sum2 - ave * ave;
2889 if (var < 0) /* avoid small negative values from rounding effects */
2890 var = 0.0;
2891 rootvar = (l_float32)sqrt(var);
2892 numaAddNumber(nad, rootvar);
2893 }
2894
2895 numaDestroy(&na1);
2896 return 0;
2897}
2898
2899
2900/*---------------------------------------------------------------------*
2901 * Extract min/max of pixel values near lines *
2902 *---------------------------------------------------------------------*/
2933l_ok
2935 l_int32 x1,
2936 l_int32 y1,
2937 l_int32 x2,
2938 l_int32 y2,
2939 l_int32 dist,
2940 l_int32 direction,
2941 NUMA **pnamin,
2942 NUMA **pnamax,
2943 l_float32 *pminave,
2944 l_float32 *pmaxave)
2945{
2946l_int32 i, j, w, h, d, x, y, n, dir, found, minval, maxval, negloc, posloc;
2947l_uint32 val;
2948l_float32 sum;
2949NUMA *namin, *namax;
2950PTA *pta;
2951
2952 if (pnamin) *pnamin = NULL;
2953 if (pnamax) *pnamax = NULL;
2954 if (pminave) *pminave = UNDEF;
2955 if (pmaxave) *pmaxave = UNDEF;
2956 if (!pnamin && !pnamax && !pminave && !pmaxave)
2957 return ERROR_INT("no output requested", __func__, 1);
2958 if (!pixs)
2959 return ERROR_INT("pixs not defined", __func__, 1);
2960 pixGetDimensions(pixs, &w, &h, &d);
2961 if (d != 8 || pixGetColormap(pixs))
2962 return ERROR_INT("pixs not 8 bpp or has colormap", __func__, 1);
2963 dist = L_ABS(dist);
2964 if (direction != L_SCAN_NEGATIVE && direction != L_SCAN_POSITIVE &&
2965 direction != L_SCAN_BOTH)
2966 return ERROR_INT("invalid direction", __func__, 1);
2967
2968 pta = generatePtaLine(x1, y1, x2, y2);
2969 n = ptaGetCount(pta);
2970 dir = (L_ABS(x1 - x2) == n - 1) ? L_HORIZ : L_VERT;
2971 namin = numaCreate(n);
2972 namax = numaCreate(n);
2973 negloc = -dist;
2974 posloc = dist;
2975 if (direction == L_SCAN_NEGATIVE)
2976 posloc = 0;
2977 else if (direction == L_SCAN_POSITIVE)
2978 negloc = 0;
2979 for (i = 0; i < n; i++) {
2980 ptaGetIPt(pta, i, &x, &y);
2981 minval = 255;
2982 maxval = 0;
2983 found = FALSE;
2984 if (dir == L_HORIZ) {
2985 if (x < 0 || x >= w) continue;
2986 for (j = negloc; j <= posloc; j++) {
2987 if (y + j < 0 || y + j >= h) continue;
2988 pixGetPixel(pixs, x, y + j, &val);
2989 found = TRUE;
2990 if (val < minval) minval = val;
2991 if (val > maxval) maxval = val;
2992 }
2993 } else { /* dir == L_VERT */
2994 if (y < 0 || y >= h) continue;
2995 for (j = negloc; j <= posloc; j++) {
2996 if (x + j < 0 || x + j >= w) continue;
2997 pixGetPixel(pixs, x + j, y, &val);
2998 found = TRUE;
2999 if (val < minval) minval = val;
3000 if (val > maxval) maxval = val;
3001 }
3002 }
3003 if (found) {
3004 numaAddNumber(namin, minval);
3005 numaAddNumber(namax, maxval);
3006 }
3007 }
3008
3009 n = numaGetCount(namin);
3010 if (n == 0) {
3011 numaDestroy(&namin);
3012 numaDestroy(&namax);
3013 ptaDestroy(&pta);
3014 return ERROR_INT("no output from this line", __func__, 1);
3015 }
3016
3017 if (pminave) {
3018 numaGetSum(namin, &sum);
3019 *pminave = sum / n;
3020 }
3021 if (pmaxave) {
3022 numaGetSum(namax, &sum);
3023 *pmaxave = sum / n;
3024 }
3025 if (pnamin)
3026 *pnamin = namin;
3027 else
3028 numaDestroy(&namin);
3029 if (pnamax)
3030 *pnamax = namax;
3031 else
3032 numaDestroy(&namax);
3033 ptaDestroy(&pta);
3034 return 0;
3035}
3036
3037
3038/*---------------------------------------------------------------------*
3039 * Rank row and column transforms *
3040 *---------------------------------------------------------------------*/
3054PIX *
3056{
3057l_int32 i, j, k, m, w, h, wpl, val;
3058l_int32 histo[256];
3059l_uint32 *datas, *datad, *lines, *lined;
3060PIX *pixd;
3061
3062 if (!pixs)
3063 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
3064 if (pixGetDepth(pixs) != 8)
3065 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL);
3066 if (pixGetColormap(pixs))
3067 return (PIX *)ERROR_PTR("pixs has a colormap", __func__, NULL);
3068
3069 pixGetDimensions(pixs, &w, &h, NULL);
3070 pixd = pixCreateTemplate(pixs);
3071 datas = pixGetData(pixs);
3072 datad = pixGetData(pixd);
3073 wpl = pixGetWpl(pixs);
3074 for (i = 0; i < h; i++) {
3075 memset(histo, 0, 1024);
3076 lines = datas + i * wpl;
3077 lined = datad + i * wpl;
3078 for (j = 0; j < w; j++) {
3079 val = GET_DATA_BYTE(lines, j);
3080 histo[val]++;
3081 }
3082 for (m = 0, j = 0; m < 256; m++) {
3083 for (k = 0; k < histo[m]; k++, j++)
3084 SET_DATA_BYTE(lined, j, m);
3085 }
3086 }
3087
3088 return pixd;
3089}
3090
3091
3105PIX *
3107{
3108l_int32 i, j, k, m, w, h, val;
3109l_int32 histo[256];
3110void **lines8, **lined8;
3111PIX *pixd;
3112
3113 if (!pixs)
3114 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
3115 if (pixGetDepth(pixs) != 8)
3116 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL);
3117 if (pixGetColormap(pixs))
3118 return (PIX *)ERROR_PTR("pixs has a colormap", __func__, NULL);
3119
3120 pixGetDimensions(pixs, &w, &h, NULL);
3121 pixd = pixCreateTemplate(pixs);
3122 lines8 = pixGetLinePtrs(pixs, NULL);
3123 lined8 = pixGetLinePtrs(pixd, NULL);
3124 for (j = 0; j < w; j++) {
3125 memset(histo, 0, 1024);
3126 for (i = 0; i < h; i++) {
3127 val = GET_DATA_BYTE(lines8[i], j);
3128 histo[val]++;
3129 }
3130 for (m = 0, i = 0; m < 256; m++) {
3131 for (k = 0; k < histo[m]; k++, i++)
3132 SET_DATA_BYTE(lined8[i], j, m);
3133 }
3134 }
3135
3136 LEPT_FREE(lines8);
3137 LEPT_FREE(lined8);
3138 return pixd;
3139}
#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:2816
PIX * pixClipRectangleWithBorder(PIX *pixs, BOX *box, l_int32 maxbord, BOX **pboxn)
pixClipRectangleWithBorder()
Definition pix5.c:1055
l_ok pixFractionFgInMask(PIX *pix1, PIX *pix2, l_float32 *pfract)
pixFractionFgInMask()
Definition pix5.c:1674
l_ok pixFindAreaPerimRatio(PIX *pixs, l_int32 *tab, l_float32 *pfract)
pixFindAreaPerimRatio()
Definition pix5.c:188
l_ok pixFindPerimToAreaRatio(PIX *pixs, l_int32 *tab, l_float32 *pfract)
pixFindPerimToAreaRatio()
Definition pix5.c:285
NUMA * pixaFindWidthHeightRatio(PIXA *pixa)
pixaFindWidthHeightRatio()
Definition pix5.c:650
PIX * pixClipRectangle(PIX *pixs, BOX *box, BOX **pboxc)
pixClipRectangle()
Definition pix5.c:994
PIX * pixClipMasked(PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_uint32 outval)
pixClipMasked()
Definition pix5.c:1126
NUMA * pixaFindPerimToAreaRatio(PIXA *pixa)
pixaFindPerimToAreaRatio()
Definition pix5.c:237
PIX * pixRankColumnTransform(PIX *pixs)
pixRankColumnTransform()
Definition pix5.c:3106
l_ok pixFindPerimSizeRatio(PIX *pixs, l_int32 *tab, l_float32 *pratio)
pixFindPerimSizeRatio()
Definition pix5.c:385
PIX * pixMakeFrameMask(l_int32 w, l_int32 h, l_float32 hf1, l_float32 hf2, l_float32 vf1, l_float32 vf2)
pixMakeFrameMask()
Definition pix5.c:1539
NUMA * pixExtractOnLine(PIX *pixs, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 factor)
pixExtractOnLine()
Definition pix5.c:2381
l_ok pixFindAreaFraction(PIX *pixs, l_int32 *tab, l_float32 *pfract)
pixFindAreaFraction()
Definition pix5.c:470
l_ok pixScanForForeground(PIX *pixs, BOX *box, l_int32 scanflag, l_int32 *ploc)
pixScanForForeground()
Definition pix5.c:1960
NUMA * pixaFindAreaFraction(PIXA *pixa)
pixaFindAreaFraction()
Definition pix5.c:429
PIXA * pixClipRectangles(PIX *pixs, BOXA *boxa)
pixClipRectangles()
Definition pix5.c:930
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:2074
PIX * pixResizeToMatch(PIX *pixs, PIX *pixt, l_int32 w, l_int32 h)
pixResizeToMatch()
Definition pix5.c:1279
l_float32 pixAverageOnLine(PIX *pixs, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 factor)
pixAverageOnLine()
Definition pix5.c:2499
l_ok pixTestClipToForeground(PIX *pixs, l_int32 *pcanclip)
pixTestClipToForeground()
Definition pix5.c:1826
l_ok pixClipBoxToForeground(PIX *pixs, BOX *boxs, PIX **ppixd, BOX **pboxd)
pixClipBoxToForeground()
Definition pix5.c:1896
l_ok pixFindAreaFractionMasked(PIX *pixs, BOX *box, PIX *pixm, l_int32 *tab, l_float32 *pfract)
pixFindAreaFractionMasked()
Definition pix5.c:593
PIX * pixMakeCoveringOfRectangles(PIX *pixs, l_int32 maxiters)
pixMakeCoveringOfRectangles()
Definition pix5.c:1603
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:2202
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:2596
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:2934
BOXA * pixFindRectangleComps(PIX *pixs, l_int32 dist, l_int32 minw, l_int32 minh)
pixFindRectangleComps()
Definition pix5.c:787
PIX * pixCropToSize(PIX *pixs, l_int32 w, l_int32 h)
pixCropToSize()
Definition pix5.c:1227
l_ok pixClipToForeground(PIX *pixs, PIX **ppixd, BOX **pbox)
pixClipToForeground()
Definition pix5.c:1728
l_ok pixaFindDimensions(PIXA *pixa, NUMA **pnaw, NUMA **pnah)
pixaFindDimensions()
Definition pix5.c:140
NUMA * pixaFindAreaFractionMasked(PIXA *pixa, PIX *pixm, l_int32 debug)
pixaFindAreaFractionMasked()
Definition pix5.c:516
PIX * pixRankRowTransform(PIX *pixs)
pixRankRowTransform()
Definition pix5.c:3055
l_ok pixConformsToRectangle(PIX *pixs, BOX *box, l_int32 dist, l_int32 *pconforms)
pixConformsToRectangle()
Definition pix5.c:865
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:722
l_ok pixCropToMatch(PIX *pixs1, PIX *pixs2, PIX **ppixd1, PIX **ppixd2)
pixCropToMatch()
Definition pix5.c:1186
PIX * pixMakeSymmetricMask(l_int32 w, l_int32 h, l_float32 hf, l_float32 vf, l_int32 type)
pixSelectComponentBySize()
Definition pix5.c:1484
NUMA * pixaFindPerimSizeRatio(PIXA *pixa)
pixaFindPerimSizeRatio()
Definition pix5.c:337
NUMA * pixaFindWidthHeightProduct(PIXA *pixa)
pixaFindWidthHeightProduct()
Definition pix5.c:684
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:2709
#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