Leptonica 1.85.0
Image processing and image analysis suite
Loading...
Searching...
No Matches
morph.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
165#ifdef HAVE_CONFIG_H
166#include <config_auto.h>
167#endif /* HAVE_CONFIG_H */
168
169#include <math.h>
170#include "allheaders.h"
171
172 /* Global constant; initialized here; must be declared extern
173 * in other files to access it directly. However, in most
174 * cases that is not necessary, because it can be reset
175 * using resetMorphBoundaryCondition(). */
176LEPT_DLL l_int32 MORPH_BC = ASYMMETRIC_MORPH_BC;
177
178 /* We accept this cost in extra rasterops for decomposing exactly. */
179static const l_int32 ACCEPTABLE_COST = 5;
180
181 /* Static helpers for arg processing */
182static PIX * processMorphArgs1(PIX *pixd, PIX *pixs, SEL *sel, PIX **ppixt);
183static PIX * processMorphArgs2(PIX *pixd, PIX *pixs, SEL *sel);
184
185
186/*-----------------------------------------------------------------*
187 * Generic binary morphological ops implemented with rasterop *
188 *-----------------------------------------------------------------*/
212PIX *
214 PIX *pixs,
215 SEL *sel)
216{
217l_int32 i, j, w, h, sx, sy, cx, cy, seldata;
218PIX *pixt;
219
220 if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL)
221 return (PIX *)ERROR_PTR("processMorphArgs1 failed", __func__, pixd);
222
223 pixGetDimensions(pixs, &w, &h, NULL);
224 selGetParameters(sel, &sy, &sx, &cy, &cx);
225 pixClearAll(pixd);
226 for (i = 0; i < sy; i++) {
227 for (j = 0; j < sx; j++) {
228 seldata = sel->data[i][j];
229 if (seldata == 1) { /* src | dst */
230 pixRasterop(pixd, j - cx, i - cy, w, h, PIX_SRC | PIX_DST,
231 pixt, 0, 0);
232 }
233 }
234 }
235
236 pixDestroy(&pixt);
237 return pixd;
238}
239
240
264PIX *
266 PIX *pixs,
267 SEL *sel)
268{
269l_int32 i, j, w, h, sx, sy, cx, cy, seldata;
270l_int32 xp, yp, xn, yn;
271PIX *pixt;
272
273 if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL)
274 return (PIX *)ERROR_PTR("processMorphArgs1 failed", __func__, pixd);
275
276 pixGetDimensions(pixs, &w, &h, NULL);
277 selGetParameters(sel, &sy, &sx, &cy, &cx);
278 pixSetAll(pixd);
279 for (i = 0; i < sy; i++) {
280 for (j = 0; j < sx; j++) {
281 seldata = sel->data[i][j];
282 if (seldata == 1) { /* src & dst */
283 pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC & PIX_DST,
284 pixt, 0, 0);
285 }
286 }
287 }
288
289 /* Clear near edges. We do this for the asymmetric boundary
290 * condition convention that implements erosion assuming all
291 * pixels surrounding the image are OFF. If you use a
292 * use a symmetric b.c. convention, where the erosion is
293 * implemented assuming pixels surrounding the image
294 * are ON, these operations are omitted. */
295 if (MORPH_BC == ASYMMETRIC_MORPH_BC) {
296 selFindMaxTranslations(sel, &xp, &yp, &xn, &yn);
297 if (xp > 0)
298 pixRasterop(pixd, 0, 0, xp, h, PIX_CLR, NULL, 0, 0);
299 if (xn > 0)
300 pixRasterop(pixd, w - xn, 0, xn, h, PIX_CLR, NULL, 0, 0);
301 if (yp > 0)
302 pixRasterop(pixd, 0, 0, w, yp, PIX_CLR, NULL, 0, 0);
303 if (yn > 0)
304 pixRasterop(pixd, 0, h - yn, w, yn, PIX_CLR, NULL, 0, 0);
305 }
306
307 pixDestroy(&pixt);
308 return pixd;
309}
310
311
337PIX *
339 PIX *pixs,
340 SEL *sel)
341{
342l_int32 i, j, w, h, sx, sy, cx, cy, firstrasterop, seldata;
343l_int32 xp, yp, xn, yn;
344PIX *pixt;
345
346 if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL)
347 return (PIX *)ERROR_PTR("processMorphArgs1 failed", __func__, pixd);
348
349 pixGetDimensions(pixs, &w, &h, NULL);
350 selGetParameters(sel, &sy, &sx, &cy, &cx);
351 firstrasterop = TRUE;
352 for (i = 0; i < sy; i++) {
353 for (j = 0; j < sx; j++) {
354 seldata = sel->data[i][j];
355 if (seldata == 1) { /* hit */
356 if (firstrasterop == TRUE) { /* src only */
357 pixClearAll(pixd);
358 pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC,
359 pixt, 0, 0);
360 firstrasterop = FALSE;
361 } else { /* src & dst */
362 pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC & PIX_DST,
363 pixt, 0, 0);
364 }
365 } else if (seldata == 2) { /* miss */
366 if (firstrasterop == TRUE) { /* ~src only */
367 pixSetAll(pixd);
368 pixRasterop(pixd, cx - j, cy - i, w, h, PIX_NOT(PIX_SRC),
369 pixt, 0, 0);
370 firstrasterop = FALSE;
371 } else { /* ~src & dst */
372 pixRasterop(pixd, cx - j, cy - i, w, h,
374 pixt, 0, 0);
375 }
376 }
377 }
378 }
379
380 /* Clear near edges */
381 selFindMaxTranslations(sel, &xp, &yp, &xn, &yn);
382 if (xp > 0)
383 pixRasterop(pixd, 0, 0, xp, h, PIX_CLR, NULL, 0, 0);
384 if (xn > 0)
385 pixRasterop(pixd, w - xn, 0, xn, h, PIX_CLR, NULL, 0, 0);
386 if (yp > 0)
387 pixRasterop(pixd, 0, 0, w, yp, PIX_CLR, NULL, 0, 0);
388 if (yn > 0)
389 pixRasterop(pixd, 0, h - yn, w, yn, PIX_CLR, NULL, 0, 0);
390
391 pixDestroy(&pixt);
392 return pixd;
393}
394
395
419PIX *
421 PIX *pixs,
422 SEL *sel)
423{
424PIX *pixt;
425
426 if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL)
427 return (PIX *)ERROR_PTR("pixd not returned", __func__, pixd);
428
429 if ((pixt = pixErode(NULL, pixs, sel)) == NULL)
430 return (PIX *)ERROR_PTR("pixt not made", __func__, pixd);
431 pixDilate(pixd, pixt, sel);
432 pixDestroy(&pixt);
433
434 return pixd;
435}
436
437
464PIX *
466 PIX *pixs,
467 SEL *sel)
468{
469PIX *pixt;
470
471 if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL)
472 return (PIX *)ERROR_PTR("pixd not returned", __func__, pixd);
473
474 if ((pixt = pixDilate(NULL, pixs, sel)) == NULL)
475 return (PIX *)ERROR_PTR("pixt not made", __func__, pixd);
476 pixErode(pixd, pixt, sel);
477 pixDestroy(&pixt);
478
479 return pixd;
480}
481
482
513PIX *
515 PIX *pixs,
516 SEL *sel)
517{
518l_int32 xp, yp, xn, yn, xmax, xbord;
519PIX *pixt1, *pixt2;
520
521 if (!pixs)
522 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
523 if (!sel)
524 return (PIX *)ERROR_PTR("sel not defined", __func__, pixd);
525 if (pixGetDepth(pixs) != 1)
526 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
527
528 /* Symmetric b.c. handles correctly without added pixels */
529 if (MORPH_BC == SYMMETRIC_MORPH_BC)
530 return pixClose(pixd, pixs, sel);
531
532 selFindMaxTranslations(sel, &xp, &yp, &xn, &yn);
533 xmax = L_MAX(xp, xn);
534 xbord = 32 * ((xmax + 31) / 32); /* full 32 bit words */
535
536 if ((pixt1 = pixAddBorderGeneral(pixs, xbord, xbord, yp, yn, 0)) == NULL)
537 return (PIX *)ERROR_PTR("pixt1 not made", __func__, pixd);
538 pixClose(pixt1, pixt1, sel);
539 if ((pixt2 = pixRemoveBorderGeneral(pixt1, xbord, xbord, yp, yn)) == NULL)
540 return (PIX *)ERROR_PTR("pixt2 not made", __func__, pixd);
541 pixDestroy(&pixt1);
542
543 if (!pixd)
544 return pixt2;
545
546 pixCopy(pixd, pixt2);
547 pixDestroy(&pixt2);
548 return pixd;
549}
550
551
578PIX *
580 PIX *pixs,
581 SEL *sel)
582{
583PIX *pixt;
584
585 if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL)
586 return (PIX *)ERROR_PTR("pixd not returned", __func__, pixd);
587
588 if ((pixt = pixHMT(NULL, pixs, sel)) == NULL)
589 return (PIX *)ERROR_PTR("pixt not made", __func__, pixd);
590 pixDilate(pixd, pixt, sel);
591 pixDestroy(&pixt);
592 return pixd;
593}
594
595
623PIX *
625 PIX *pixs,
626 SEL *sel)
627{
628PIX *pixt;
629
630 if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL)
631 return (PIX *)ERROR_PTR("pixd not returned", __func__, pixd);
632
633 if ((pixt = pixDilate(NULL, pixs, sel)) == NULL)
634 return (PIX *)ERROR_PTR("pixt not made", __func__, pixd);
635 pixHMT(pixd, pixt, sel);
636 pixDestroy(&pixt);
637
638 return pixd;
639}
640
641
642/*-----------------------------------------------------------------*
643 * Binary morphological (raster) ops with brick Sels *
644 *-----------------------------------------------------------------*/
671PIX *
673 PIX *pixs,
674 l_int32 hsize,
675 l_int32 vsize)
676{
677PIX *pixt;
678SEL *sel, *selh, *selv;
679
680 if (!pixs)
681 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
682 if (pixGetDepth(pixs) != 1)
683 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
684 if (hsize < 1 || vsize < 1)
685 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
686
687 if (hsize == 1 && vsize == 1)
688 return pixCopy(pixd, pixs);
689 if (hsize == 1 || vsize == 1) { /* no intermediate result */
690 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
691 if (!sel)
692 return (PIX *)ERROR_PTR("sel not made", __func__, pixd);
693 pixd = pixDilate(pixd, pixs, sel);
694 selDestroy(&sel);
695 } else {
696 if ((selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT)) == NULL)
697 return (PIX *)ERROR_PTR("selh not made", __func__, pixd);
698 if ((selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT)) == NULL) {
699 selDestroy(&selh);
700 return (PIX *)ERROR_PTR("selv not made", __func__, pixd);
701 }
702 pixt = pixDilate(NULL, pixs, selh);
703 pixd = pixDilate(pixd, pixt, selv);
704 pixDestroy(&pixt);
705 selDestroy(&selh);
706 selDestroy(&selv);
707 }
708
709 return pixd;
710}
711
712
739PIX *
741 PIX *pixs,
742 l_int32 hsize,
743 l_int32 vsize)
744{
745PIX *pixt;
746SEL *sel, *selh, *selv;
747
748 if (!pixs)
749 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
750 if (pixGetDepth(pixs) != 1)
751 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
752 if (hsize < 1 || vsize < 1)
753 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
754
755 if (hsize == 1 && vsize == 1)
756 return pixCopy(pixd, pixs);
757 if (hsize == 1 || vsize == 1) { /* no intermediate result */
758 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
759 if (!sel)
760 return (PIX *)ERROR_PTR("sel not made", __func__, pixd);
761 pixd = pixErode(pixd, pixs, sel);
762 selDestroy(&sel);
763 } else {
764 if ((selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT)) == NULL)
765 return (PIX *)ERROR_PTR("selh not made", __func__, pixd);
766 if ((selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT)) == NULL) {
767 selDestroy(&selh);
768 return (PIX *)ERROR_PTR("selv not made", __func__, pixd);
769 }
770 pixt = pixErode(NULL, pixs, selh);
771 pixd = pixErode(pixd, pixt, selv);
772 pixDestroy(&pixt);
773 selDestroy(&selh);
774 selDestroy(&selv);
775 }
776
777 return pixd;
778}
779
780
807PIX *
809 PIX *pixs,
810 l_int32 hsize,
811 l_int32 vsize)
812{
813PIX *pixt;
814SEL *sel, *selh, *selv;
815
816 if (!pixs)
817 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
818 if (pixGetDepth(pixs) != 1)
819 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
820 if (hsize < 1 || vsize < 1)
821 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
822
823 if (hsize == 1 && vsize == 1)
824 return pixCopy(pixd, pixs);
825 if (hsize == 1 || vsize == 1) { /* no intermediate result */
826 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
827 if (!sel)
828 return (PIX *)ERROR_PTR("sel not made", __func__, pixd);
829 pixd = pixOpen(pixd, pixs, sel);
830 selDestroy(&sel);
831 } else { /* do separably */
832 if ((selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT)) == NULL)
833 return (PIX *)ERROR_PTR("selh not made", __func__, pixd);
834 if ((selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT)) == NULL) {
835 selDestroy(&selh);
836 return (PIX *)ERROR_PTR("selv not made", __func__, pixd);
837 }
838 pixt = pixErode(NULL, pixs, selh);
839 pixd = pixErode(pixd, pixt, selv);
840 pixDilate(pixt, pixd, selh);
841 pixDilate(pixd, pixt, selv);
842 pixDestroy(&pixt);
843 selDestroy(&selh);
844 selDestroy(&selv);
845 }
846
847 return pixd;
848}
849
850
877PIX *
879 PIX *pixs,
880 l_int32 hsize,
881 l_int32 vsize)
882{
883PIX *pixt;
884SEL *sel, *selh, *selv;
885
886 if (!pixs)
887 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
888 if (pixGetDepth(pixs) != 1)
889 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
890 if (hsize < 1 || vsize < 1)
891 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
892
893 if (hsize == 1 && vsize == 1)
894 return pixCopy(pixd, pixs);
895 if (hsize == 1 || vsize == 1) { /* no intermediate result */
896 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
897 if (!sel)
898 return (PIX *)ERROR_PTR("sel not made", __func__, pixd);
899 pixd = pixClose(pixd, pixs, sel);
900 selDestroy(&sel);
901 } else { /* do separably */
902 if ((selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT)) == NULL)
903 return (PIX *)ERROR_PTR("selh not made", __func__, pixd);
904 if ((selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT)) == NULL) {
905 selDestroy(&selh);
906 return (PIX *)ERROR_PTR("selv not made", __func__, pixd);
907 }
908 pixt = pixDilate(NULL, pixs, selh);
909 pixd = pixDilate(pixd, pixt, selv);
910 pixErode(pixt, pixd, selh);
911 pixErode(pixd, pixt, selv);
912 pixDestroy(&pixt);
913 selDestroy(&selh);
914 selDestroy(&selv);
915 }
916
917 return pixd;
918}
919
920
952PIX *
954 PIX *pixs,
955 l_int32 hsize,
956 l_int32 vsize)
957{
958l_int32 maxtrans, bordsize;
959PIX *pixsb, *pixt, *pixdb;
960SEL *sel, *selh, *selv;
961
962 if (!pixs)
963 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
964 if (pixGetDepth(pixs) != 1)
965 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
966 if (hsize < 1 || vsize < 1)
967 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
968
969 if (hsize == 1 && vsize == 1)
970 return pixCopy(pixd, pixs);
971
972 /* Symmetric b.c. handles correctly without added pixels */
973 if (MORPH_BC == SYMMETRIC_MORPH_BC)
974 return pixCloseBrick(pixd, pixs, hsize, vsize);
975
976 maxtrans = L_MAX(hsize / 2, vsize / 2);
977 bordsize = 32 * ((maxtrans + 31) / 32); /* full 32 bit words */
978 pixsb = pixAddBorder(pixs, bordsize, 0);
979
980 if (hsize == 1 || vsize == 1) { /* no intermediate result */
981 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
982 if (!sel) {
983 pixDestroy(&pixsb);
984 return (PIX *)ERROR_PTR("sel not made", __func__, pixd);
985 }
986 pixdb = pixClose(NULL, pixsb, sel);
987 selDestroy(&sel);
988 } else { /* do separably */
989 selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT);
990 selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT);
991 if (!selh || !selv) {
992 selDestroy(&selh);
993 selDestroy(&selv);
994 pixDestroy(&pixsb);
995 return (PIX *)ERROR_PTR("selh and selv not both made",
996 __func__, pixd);
997 }
998 pixt = pixDilate(NULL, pixsb, selh);
999 pixdb = pixDilate(NULL, pixt, selv);
1000 pixErode(pixt, pixdb, selh);
1001 pixErode(pixdb, pixt, selv);
1002 pixDestroy(&pixt);
1003 selDestroy(&selh);
1004 selDestroy(&selv);
1005 }
1006
1007 pixt = pixRemoveBorder(pixdb, bordsize);
1008 pixDestroy(&pixsb);
1009 pixDestroy(&pixdb);
1010
1011 if (!pixd) {
1012 pixd = pixt;
1013 } else {
1014 pixCopy(pixd, pixt);
1015 pixDestroy(&pixt);
1016 }
1017 return pixd;
1018}
1019
1020
1021/*-----------------------------------------------------------------*
1022 * Binary composed morphological (raster) ops with brick Sels *
1023 *-----------------------------------------------------------------*/
1024/* \brief selectComposableSels()
1025 *
1026 * \param[in] size of composed sel
1027 * \param[in] direction L_HORIZ, L_VERT
1028 * \param[out] psel1 [optional] contiguous sel; can be null
1029 * \param[out] psel2 [optional] comb sel; can be null
1030 * \return 0 if OK, 1 on error
1031 *
1032 * <pre>
1033 * Notes:
1034 * (1) When using composable Sels, where the original Sel is
1035 * decomposed into two, the best you can do in terms
1036 * of reducing the computation is by a factor:
1037 *
1038 * 2 * sqrt(size) / size
1039 *
1040 * In practice, you get quite close to this. E.g.,
1041 *
1042 * Sel size | Optimum reduction factor
1043 * -------- ------------------------
1044 * 36 | 1/3
1045 * 64 | 1/4
1046 * 144 | 1/6
1047 * 256 | 1/8
1048 * </pre>
1049 */
1050l_int32
1051selectComposableSels(l_int32 size,
1052 l_int32 direction,
1053 SEL **psel1,
1054 SEL **psel2)
1055{
1056l_int32 factor1, factor2;
1057
1058 if (!psel1 && !psel2)
1059 return ERROR_INT("neither &sel1 nor &sel2 are defined", __func__, 1);
1060 if (psel1) *psel1 = NULL;
1061 if (psel2) *psel2 = NULL;
1062 if (size < 1 || size > 10000)
1063 return ERROR_INT("size < 1 or size > 10000", __func__, 1);
1064 if (direction != L_HORIZ && direction != L_VERT)
1065 return ERROR_INT("invalid direction", __func__, 1);
1066
1067 if (selectComposableSizes(size, &factor1, &factor2))
1068 return ERROR_INT("factors not found", __func__, 1);
1069
1070 if (psel1) {
1071 if (direction == L_HORIZ)
1072 *psel1 = selCreateBrick(1, factor1, 0, factor1 / 2, SEL_HIT);
1073 else
1074 *psel1 = selCreateBrick(factor1, 1, factor1 / 2 , 0, SEL_HIT);
1075 }
1076 if (psel2)
1077 *psel2 = selCreateComb(factor1, factor2, direction);
1078 return 0;
1079}
1080
1081
1103l_ok
1105 l_int32 *pfactor1,
1106 l_int32 *pfactor2)
1107{
1108l_int32 i, midval, val1, val2m, val2p;
1109l_int32 index, prodm, prodp;
1110l_int32 mincost, totcost, rastcostm, rastcostp, diffm, diffp;
1111l_int32 lowval[256];
1112l_int32 hival[256];
1113l_int32 rastcost[256]; /* excess in sum of sizes (extra rasterops) */
1114l_int32 diff[256]; /* diff between product (sel size) and input size */
1115
1116 if (size < 1 || size > 10000)
1117 return ERROR_INT("size < 1 or size > 10000", __func__, 1);
1118 if (!pfactor1 || !pfactor2)
1119 return ERROR_INT("&factor1 or &factor2 not defined", __func__, 1);
1120
1121 midval = (l_int32)(sqrt((l_float64)size) + 0.001);
1122 if (midval * midval == size) {
1123 *pfactor1 = *pfactor2 = midval;
1124 return 0;
1125 }
1126
1127 /* Set up arrays. For each val1, optimize for lowest diff,
1128 * and save the rastcost, the diff, and the two factors. */
1129 for (val1 = midval + 1, i = 0; val1 > 0; val1--, i++) {
1130 val2m = size / val1;
1131 val2p = val2m + 1;
1132 prodm = val1 * val2m;
1133 prodp = val1 * val2p;
1134 rastcostm = val1 + val2m - 2 * midval;
1135 rastcostp = val1 + val2p - 2 * midval;
1136 diffm = L_ABS(size - prodm);
1137 diffp = L_ABS(size - prodp);
1138 if (diffm <= diffp) {
1139 lowval[i] = L_MIN(val1, val2m);
1140 hival[i] = L_MAX(val1, val2m);
1141 rastcost[i] = rastcostm;
1142 diff[i] = diffm;
1143 } else {
1144 lowval[i] = L_MIN(val1, val2p);
1145 hival[i] = L_MAX(val1, val2p);
1146 rastcost[i] = rastcostp;
1147 diff[i] = diffp;
1148 }
1149 }
1150
1151 /* Choose the optimum factors; use cost ratio 4 on diff */
1152 mincost = 10000;
1153 index = 1; /* unimportant initial value */
1154 for (i = 0; i < midval + 1; i++) {
1155 if (diff[i] == 0 && rastcost[i] < ACCEPTABLE_COST) {
1156 *pfactor1 = hival[i];
1157 *pfactor2 = lowval[i];
1158 return 0;
1159 }
1160 totcost = 4 * diff[i] + rastcost[i];
1161 if (totcost < mincost) {
1162 mincost = totcost;
1163 index = i;
1164 }
1165 }
1166 *pfactor1 = hival[index];
1167 *pfactor2 = lowval[index];
1168
1169 return 0;
1170}
1171
1172
1213PIX *
1215 PIX *pixs,
1216 l_int32 hsize,
1217 l_int32 vsize)
1218{
1219PIX *pix1, *pix2, *pix3;
1220SEL *selh1 = NULL;
1221SEL *selh2 = NULL;
1222SEL *selv1 = NULL;
1223SEL *selv2 = NULL;
1224
1225 if (!pixs)
1226 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1227 if (pixGetDepth(pixs) != 1)
1228 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1229 if (hsize < 1 || vsize < 1)
1230 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1231
1232 if (hsize == 1 && vsize == 1)
1233 return pixCopy(pixd, pixs);
1234 if (hsize > 1) {
1235 if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) {
1236 selDestroy(&selh1);
1237 selDestroy(&selh2);
1238 return (PIX *)ERROR_PTR("horiz sels not made", __func__, pixd);
1239 }
1240 }
1241 if (vsize > 1) {
1242 if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) {
1243 selDestroy(&selh1);
1244 selDestroy(&selh2);
1245 selDestroy(&selv1);
1246 selDestroy(&selv2);
1247 return (PIX *)ERROR_PTR("vert sels not made", __func__, pixd);
1248 }
1249 }
1250
1251 pix1 = pixAddBorder(pixs, 32, 0);
1252 if (vsize == 1) {
1253 pix2 = pixDilate(NULL, pix1, selh1);
1254 pix3 = pixDilate(NULL, pix2, selh2);
1255 } else if (hsize == 1) {
1256 pix2 = pixDilate(NULL, pix1, selv1);
1257 pix3 = pixDilate(NULL, pix2, selv2);
1258 } else {
1259 pix2 = pixDilate(NULL, pix1, selh1);
1260 pix3 = pixDilate(NULL, pix2, selh2);
1261 pixDilate(pix2, pix3, selv1);
1262 pixDilate(pix3, pix2, selv2);
1263 }
1264 pixDestroy(&pix1);
1265 pixDestroy(&pix2);
1266
1267 selDestroy(&selh1);
1268 selDestroy(&selh2);
1269 selDestroy(&selv1);
1270 selDestroy(&selv2);
1271
1272 pix1 = pixRemoveBorder(pix3, 32);
1273 pixDestroy(&pix3);
1274 if (!pixd)
1275 return pix1;
1276 pixCopy(pixd, pix1);
1277 pixDestroy(&pix1);
1278 return pixd;
1279}
1280
1281
1322PIX *
1324 PIX *pixs,
1325 l_int32 hsize,
1326 l_int32 vsize)
1327{
1328PIX *pixt;
1329SEL *selh1 = NULL;
1330SEL *selh2 = NULL;
1331SEL *selv1 = NULL;
1332SEL *selv2 = NULL;
1333
1334 if (!pixs)
1335 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1336 if (pixGetDepth(pixs) != 1)
1337 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1338 if (hsize < 1 || vsize < 1)
1339 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1340
1341 if (hsize == 1 && vsize == 1)
1342 return pixCopy(pixd, pixs);
1343 if (hsize > 1) {
1344 if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) {
1345 selDestroy(&selh1);
1346 selDestroy(&selh2);
1347 return (PIX *)ERROR_PTR("horiz sels not made", __func__, pixd);
1348 }
1349 }
1350 if (vsize > 1) {
1351 if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) {
1352 selDestroy(&selh1);
1353 selDestroy(&selh2);
1354 selDestroy(&selv1);
1355 selDestroy(&selv2);
1356 return (PIX *)ERROR_PTR("vert sels not made", __func__, pixd);
1357 }
1358 }
1359
1360 if (vsize == 1) {
1361 pixt = pixErode(NULL, pixs, selh1);
1362 pixd = pixErode(pixd, pixt, selh2);
1363 } else if (hsize == 1) {
1364 pixt = pixErode(NULL, pixs, selv1);
1365 pixd = pixErode(pixd, pixt, selv2);
1366 } else {
1367 pixt = pixErode(NULL, pixs, selh1);
1368 pixd = pixErode(pixd, pixt, selh2);
1369 pixErode(pixt, pixd, selv1);
1370 pixErode(pixd, pixt, selv2);
1371 }
1372 pixDestroy(&pixt);
1373
1374 selDestroy(&selh1);
1375 selDestroy(&selh2);
1376 selDestroy(&selv1);
1377 selDestroy(&selv2);
1378 return pixd;
1379}
1380
1381
1422PIX *
1424 PIX *pixs,
1425 l_int32 hsize,
1426 l_int32 vsize)
1427{
1428PIX *pixt;
1429SEL *selh1 = NULL;
1430SEL *selh2 = NULL;
1431SEL *selv1 = NULL;
1432SEL *selv2 = NULL;
1433
1434 if (!pixs)
1435 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1436 if (pixGetDepth(pixs) != 1)
1437 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1438 if (hsize < 1 || vsize < 1)
1439 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1440
1441 if (hsize == 1 && vsize == 1)
1442 return pixCopy(pixd, pixs);
1443 if (hsize > 1) {
1444 if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) {
1445 selDestroy(&selh1);
1446 selDestroy(&selh2);
1447 return (PIX *)ERROR_PTR("horiz sels not made", __func__, pixd);
1448 }
1449 }
1450 if (vsize > 1) {
1451 if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) {
1452 selDestroy(&selh1);
1453 selDestroy(&selh2);
1454 selDestroy(&selv1);
1455 selDestroy(&selv2);
1456 return (PIX *)ERROR_PTR("vert sels not made", __func__, pixd);
1457 }
1458 }
1459
1460 if (vsize == 1) {
1461 pixt = pixErode(NULL, pixs, selh1);
1462 pixd = pixErode(pixd, pixt, selh2);
1463 pixDilate(pixt, pixd, selh1);
1464 pixDilate(pixd, pixt, selh2);
1465 } else if (hsize == 1) {
1466 pixt = pixErode(NULL, pixs, selv1);
1467 pixd = pixErode(pixd, pixt, selv2);
1468 pixDilate(pixt, pixd, selv1);
1469 pixDilate(pixd, pixt, selv2);
1470 } else { /* do separably */
1471 pixt = pixErode(NULL, pixs, selh1);
1472 pixd = pixErode(pixd, pixt, selh2);
1473 pixErode(pixt, pixd, selv1);
1474 pixErode(pixd, pixt, selv2);
1475 pixDilate(pixt, pixd, selh1);
1476 pixDilate(pixd, pixt, selh2);
1477 pixDilate(pixt, pixd, selv1);
1478 pixDilate(pixd, pixt, selv2);
1479 }
1480 pixDestroy(&pixt);
1481
1482 selDestroy(&selh1);
1483 selDestroy(&selh2);
1484 selDestroy(&selv1);
1485 selDestroy(&selv2);
1486 return pixd;
1487}
1488
1489
1530PIX *
1532 PIX *pixs,
1533 l_int32 hsize,
1534 l_int32 vsize)
1535{
1536PIX *pixt;
1537SEL *selh1 = NULL;
1538SEL *selh2 = NULL;
1539SEL *selv1 = NULL;
1540SEL *selv2 = NULL;
1541
1542 if (!pixs)
1543 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1544 if (pixGetDepth(pixs) != 1)
1545 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1546 if (hsize < 1 || vsize < 1)
1547 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1548
1549 if (hsize == 1 && vsize == 1)
1550 return pixCopy(pixd, pixs);
1551 if (hsize > 1) {
1552 if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) {
1553 selDestroy(&selh1);
1554 selDestroy(&selh2);
1555 return (PIX *)ERROR_PTR("horiz sels not made", __func__, pixd);
1556 }
1557 }
1558 if (vsize > 1) {
1559 if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) {
1560 selDestroy(&selh1);
1561 selDestroy(&selh2);
1562 selDestroy(&selv1);
1563 selDestroy(&selv2);
1564 return (PIX *)ERROR_PTR("vert sels not made", __func__, pixd);
1565 }
1566 }
1567
1568 if (vsize == 1) {
1569 pixt = pixDilate(NULL, pixs, selh1);
1570 pixd = pixDilate(pixd, pixt, selh2);
1571 pixErode(pixt, pixd, selh1);
1572 pixErode(pixd, pixt, selh2);
1573 } else if (hsize == 1) {
1574 pixt = pixDilate(NULL, pixs, selv1);
1575 pixd = pixDilate(pixd, pixt, selv2);
1576 pixErode(pixt, pixd, selv1);
1577 pixErode(pixd, pixt, selv2);
1578 } else { /* do separably */
1579 pixt = pixDilate(NULL, pixs, selh1);
1580 pixd = pixDilate(pixd, pixt, selh2);
1581 pixDilate(pixt, pixd, selv1);
1582 pixDilate(pixd, pixt, selv2);
1583 pixErode(pixt, pixd, selh1);
1584 pixErode(pixd, pixt, selh2);
1585 pixErode(pixt, pixd, selv1);
1586 pixErode(pixd, pixt, selv2);
1587 }
1588 pixDestroy(&pixt);
1589
1590 selDestroy(&selh1);
1591 selDestroy(&selh2);
1592 selDestroy(&selv1);
1593 selDestroy(&selv2);
1594 return pixd;
1595}
1596
1597
1643PIX *
1645 PIX *pixs,
1646 l_int32 hsize,
1647 l_int32 vsize)
1648{
1649l_int32 maxtrans, bordsize;
1650PIX *pixsb, *pixt, *pixdb;
1651SEL *selh1 = NULL;
1652SEL *selh2 = NULL;
1653SEL *selv1 = NULL;
1654SEL *selv2 = NULL;
1655
1656 if (!pixs)
1657 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1658 if (pixGetDepth(pixs) != 1)
1659 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1660 if (hsize < 1 || vsize < 1)
1661 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1662
1663 if (hsize == 1 && vsize == 1)
1664 return pixCopy(pixd, pixs);
1665
1666 /* Symmetric b.c. handles correctly without added pixels */
1667 if (MORPH_BC == SYMMETRIC_MORPH_BC)
1668 return pixCloseCompBrick(pixd, pixs, hsize, vsize);
1669
1670 if (hsize > 1) {
1671 if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) {
1672 selDestroy(&selh1);
1673 selDestroy(&selh2);
1674 return (PIX *)ERROR_PTR("horiz sels not made", __func__, pixd);
1675 }
1676 }
1677 if (vsize > 1) {
1678 if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) {
1679 selDestroy(&selh1);
1680 selDestroy(&selh2);
1681 selDestroy(&selv1);
1682 selDestroy(&selv2);
1683 return (PIX *)ERROR_PTR("vert sels not made", __func__, pixd);
1684 }
1685 }
1686
1687 maxtrans = L_MAX(hsize / 2, vsize / 2);
1688 bordsize = 32 * ((maxtrans + 31) / 32); /* full 32 bit words */
1689 pixsb = pixAddBorder(pixs, bordsize, 0);
1690
1691 if (vsize == 1) {
1692 pixt = pixDilate(NULL, pixsb, selh1);
1693 pixdb = pixDilate(NULL, pixt, selh2);
1694 pixErode(pixt, pixdb, selh1);
1695 pixErode(pixdb, pixt, selh2);
1696 } else if (hsize == 1) {
1697 pixt = pixDilate(NULL, pixsb, selv1);
1698 pixdb = pixDilate(NULL, pixt, selv2);
1699 pixErode(pixt, pixdb, selv1);
1700 pixErode(pixdb, pixt, selv2);
1701 } else { /* do separably */
1702 pixt = pixDilate(NULL, pixsb, selh1);
1703 pixdb = pixDilate(NULL, pixt, selh2);
1704 pixDilate(pixt, pixdb, selv1);
1705 pixDilate(pixdb, pixt, selv2);
1706 pixErode(pixt, pixdb, selh1);
1707 pixErode(pixdb, pixt, selh2);
1708 pixErode(pixt, pixdb, selv1);
1709 pixErode(pixdb, pixt, selv2);
1710 }
1711 pixDestroy(&pixt);
1712
1713 pixt = pixRemoveBorder(pixdb, bordsize);
1714 pixDestroy(&pixsb);
1715 pixDestroy(&pixdb);
1716
1717 if (!pixd) {
1718 pixd = pixt;
1719 } else {
1720 pixCopy(pixd, pixt);
1721 pixDestroy(&pixt);
1722 }
1723
1724 selDestroy(&selh1);
1725 selDestroy(&selh2);
1726 selDestroy(&selv1);
1727 selDestroy(&selv2);
1728 return pixd;
1729}
1730
1731
1732/*-----------------------------------------------------------------*
1733 * Functions associated with boundary conditions *
1734 *-----------------------------------------------------------------*/
1741void
1743{
1744 if (bc != SYMMETRIC_MORPH_BC && bc != ASYMMETRIC_MORPH_BC) {
1745 L_WARNING("invalid bc; using asymmetric\n", __func__);
1746 bc = ASYMMETRIC_MORPH_BC;
1747 }
1748 MORPH_BC = bc;
1749 return;
1750}
1751
1752
1760l_uint32
1762 l_int32 depth)
1763{
1764 if (type != L_MORPH_DILATE && type != L_MORPH_ERODE)
1765 return ERROR_INT("invalid type", __func__, 0);
1766 if (depth != 1 && depth != 2 && depth != 4 && depth != 8 &&
1767 depth != 16 && depth != 32)
1768 return ERROR_INT("invalid depth", __func__, 0);
1769
1770 if (MORPH_BC == ASYMMETRIC_MORPH_BC || type == L_MORPH_DILATE)
1771 return 0;
1772
1773 /* Symmetric & erosion */
1774 if (depth < 32)
1775 return ((1 << depth) - 1);
1776 else /* depth == 32 */
1777 return 0xffffff00;
1778}
1779
1780
1781/*-----------------------------------------------------------------*
1782 * Static helpers for arg processing *
1783 *-----------------------------------------------------------------*/
1799static PIX *
1801 PIX *pixs,
1802 SEL *sel,
1803 PIX **ppixt)
1804{
1805l_int32 sx, sy;
1806
1807 if (!ppixt)
1808 return (PIX *)ERROR_PTR("&pixt not defined", __func__, pixd);
1809 *ppixt = NULL;
1810 if (!pixs)
1811 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1812 if (!sel)
1813 return (PIX *)ERROR_PTR("sel not defined", __func__, pixd);
1814 if (pixGetDepth(pixs) != 1)
1815 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1816
1817 selGetParameters(sel, &sx, &sy, NULL, NULL);
1818 if (sx == 0 || sy == 0)
1819 return (PIX *)ERROR_PTR("sel of size 0", __func__, pixd);
1820
1821 /* We require pixd to exist and to be the same size as pixs.
1822 * Further, pixt must be a copy (or clone) of pixs. */
1823 if (!pixd) {
1824 if ((pixd = pixCreateTemplate(pixs)) == NULL)
1825 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1826 *ppixt = pixClone(pixs);
1827 } else {
1828 pixResizeImageData(pixd, pixs);
1829 if (pixd == pixs) { /* in-place; must make a copy of pixs */
1830 if ((*ppixt = pixCopy(NULL, pixs)) == NULL)
1831 return (PIX *)ERROR_PTR("pixt not made", __func__, pixd);
1832 } else {
1833 *ppixt = pixClone(pixs);
1834 }
1835 }
1836 return pixd;
1837}
1838
1839
1845static PIX *
1847 PIX *pixs,
1848 SEL *sel)
1849{
1850l_int32 sx, sy;
1851
1852 if (!pixs)
1853 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1854 if (!sel)
1855 return (PIX *)ERROR_PTR("sel not defined", __func__, pixd);
1856 if (pixGetDepth(pixs) != 1)
1857 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1858
1859 selGetParameters(sel, &sx, &sy, NULL, NULL);
1860 if (sx == 0 || sy == 0)
1861 return (PIX *)ERROR_PTR("sel of size 0", __func__, pixd);
1862
1863 if (!pixd)
1864 return pixCreateTemplate(pixs);
1865 pixResizeImageData(pixd, pixs);
1866 return pixd;
1867}
PIX * pixOpenGeneralized(PIX *pixd, PIX *pixs, SEL *sel)
pixOpenGeneralized()
Definition morph.c:579
PIX * pixErodeCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixErodeCompBrick()
Definition morph.c:1323
PIX * pixDilateBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateBrick()
Definition morph.c:672
void resetMorphBoundaryCondition(l_int32 bc)
resetMorphBoundaryCondition()
Definition morph.c:1742
PIX * pixCloseBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseBrick()
Definition morph.c:878
PIX * pixDilateCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateCompBrick()
Definition morph.c:1214
PIX * pixCloseCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseCompBrick()
Definition morph.c:1531
l_ok selectComposableSizes(l_int32 size, l_int32 *pfactor1, l_int32 *pfactor2)
selectComposableSizes()
Definition morph.c:1104
PIX * pixCloseSafeBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseSafeBrick()
Definition morph.c:953
PIX * pixOpen(PIX *pixd, PIX *pixs, SEL *sel)
pixOpen()
Definition morph.c:420
PIX * pixOpenBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixOpenBrick()
Definition morph.c:808
PIX * pixDilate(PIX *pixd, PIX *pixs, SEL *sel)
pixDilate()
Definition morph.c:213
PIX * pixCloseGeneralized(PIX *pixd, PIX *pixs, SEL *sel)
pixCloseGeneralized()
Definition morph.c:624
PIX * pixOpenCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixOpenCompBrick()
Definition morph.c:1423
static PIX * processMorphArgs1(PIX *pixd, PIX *pixs, SEL *sel, PIX **ppixt)
processMorphArgs1()
Definition morph.c:1800
PIX * pixClose(PIX *pixd, PIX *pixs, SEL *sel)
pixClose()
Definition morph.c:465
PIX * pixHMT(PIX *pixd, PIX *pixs, SEL *sel)
pixHMT()
Definition morph.c:338
PIX * pixCloseSafe(PIX *pixd, PIX *pixs, SEL *sel)
pixCloseSafe()
Definition morph.c:514
PIX * pixErode(PIX *pixd, PIX *pixs, SEL *sel)
pixErode()
Definition morph.c:265
static PIX * processMorphArgs2(PIX *pixd, PIX *pixs, SEL *sel)
processMorphArgs2()
Definition morph.c:1846
PIX * pixCloseSafeCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseSafeCompBrick()
Definition morph.c:1644
l_uint32 getMorphBorderPixelColor(l_int32 type, l_int32 depth)
getMorphBorderPixelColor()
Definition morph.c:1761
PIX * pixErodeBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixErodeBrick()
Definition morph.c:740
#define PIX_DST
Definition pix.h:445
#define PIX_SRC
Definition pix.h:444
#define PIX_CLR
Definition pix.h:447
#define PIX_NOT(op)
Definition pix.h:446
l_int32 ** data
Definition morph.h:67