Leptonica  1.77.0
Image processing and image analysis suite
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 #include <math.h>
166 #include "allheaders.h"
167 
168  /* Global constant; initialized here; must be declared extern
169  * in other files to access it directly. However, in most
170  * cases that is not necessary, because it can be reset
171  * using resetMorphBoundaryCondition(). */
172 LEPT_DLL l_int32 MORPH_BC = ASYMMETRIC_MORPH_BC;
173 
174  /* We accept this cost in extra rasterops for decomposing exactly. */
175 static const l_int32 ACCEPTABLE_COST = 5;
176 
177  /* Static helpers for arg processing */
178 static PIX * processMorphArgs1(PIX *pixd, PIX *pixs, SEL *sel, PIX **ppixt);
179 static PIX * processMorphArgs2(PIX *pixd, PIX *pixs, SEL *sel);
180 
181 
182 /*-----------------------------------------------------------------*
183  * Generic binary morphological ops implemented with rasterop *
184  *-----------------------------------------------------------------*/
208 PIX *
210  PIX *pixs,
211  SEL *sel)
212 {
213 l_int32 i, j, w, h, sx, sy, cx, cy, seldata;
214 PIX *pixt;
215 
216  PROCNAME("pixDilate");
217 
218  if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL)
219  return (PIX *)ERROR_PTR("processMorphArgs1 failed", procName, pixd);
220 
221  pixGetDimensions(pixs, &w, &h, NULL);
222  selGetParameters(sel, &sy, &sx, &cy, &cx);
223  pixClearAll(pixd);
224  for (i = 0; i < sy; i++) {
225  for (j = 0; j < sx; j++) {
226  seldata = sel->data[i][j];
227  if (seldata == 1) { /* src | dst */
228  pixRasterop(pixd, j - cx, i - cy, w, h, PIX_SRC | PIX_DST,
229  pixt, 0, 0);
230  }
231  }
232  }
233 
234  pixDestroy(&pixt);
235  return pixd;
236 }
237 
238 
262 PIX *
263 pixErode(PIX *pixd,
264  PIX *pixs,
265  SEL *sel)
266 {
267 l_int32 i, j, w, h, sx, sy, cx, cy, seldata;
268 l_int32 xp, yp, xn, yn;
269 PIX *pixt;
270 
271  PROCNAME("pixErode");
272 
273  if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL)
274  return (PIX *)ERROR_PTR("processMorphArgs1 failed", procName, 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 
337 PIX *
338 pixHMT(PIX *pixd,
339  PIX *pixs,
340  SEL *sel)
341 {
342 l_int32 i, j, w, h, sx, sy, cx, cy, firstrasterop, seldata;
343 l_int32 xp, yp, xn, yn;
344 PIX *pixt;
345 
346  PROCNAME("pixHMT");
347 
348  if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL)
349  return (PIX *)ERROR_PTR("processMorphArgs1 failed", procName, pixd);
350 
351  pixGetDimensions(pixs, &w, &h, NULL);
352  selGetParameters(sel, &sy, &sx, &cy, &cx);
353  firstrasterop = TRUE;
354  for (i = 0; i < sy; i++) {
355  for (j = 0; j < sx; j++) {
356  seldata = sel->data[i][j];
357  if (seldata == 1) { /* hit */
358  if (firstrasterop == TRUE) { /* src only */
359  pixClearAll(pixd);
360  pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC,
361  pixt, 0, 0);
362  firstrasterop = FALSE;
363  } else { /* src & dst */
364  pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC & PIX_DST,
365  pixt, 0, 0);
366  }
367  } else if (seldata == 2) { /* miss */
368  if (firstrasterop == TRUE) { /* ~src only */
369  pixSetAll(pixd);
370  pixRasterop(pixd, cx - j, cy - i, w, h, PIX_NOT(PIX_SRC),
371  pixt, 0, 0);
372  firstrasterop = FALSE;
373  } else { /* ~src & dst */
374  pixRasterop(pixd, cx - j, cy - i, w, h,
376  pixt, 0, 0);
377  }
378  }
379  }
380  }
381 
382  /* Clear near edges */
383  selFindMaxTranslations(sel, &xp, &yp, &xn, &yn);
384  if (xp > 0)
385  pixRasterop(pixd, 0, 0, xp, h, PIX_CLR, NULL, 0, 0);
386  if (xn > 0)
387  pixRasterop(pixd, w - xn, 0, xn, h, PIX_CLR, NULL, 0, 0);
388  if (yp > 0)
389  pixRasterop(pixd, 0, 0, w, yp, PIX_CLR, NULL, 0, 0);
390  if (yn > 0)
391  pixRasterop(pixd, 0, h - yn, w, yn, PIX_CLR, NULL, 0, 0);
392 
393  pixDestroy(&pixt);
394  return pixd;
395 }
396 
397 
421 PIX *
422 pixOpen(PIX *pixd,
423  PIX *pixs,
424  SEL *sel)
425 {
426 PIX *pixt;
427 
428  PROCNAME("pixOpen");
429 
430  if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL)
431  return (PIX *)ERROR_PTR("pixd not returned", procName, pixd);
432 
433  if ((pixt = pixErode(NULL, pixs, sel)) == NULL)
434  return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
435  pixDilate(pixd, pixt, sel);
436  pixDestroy(&pixt);
437 
438  return pixd;
439 }
440 
441 
468 PIX *
469 pixClose(PIX *pixd,
470  PIX *pixs,
471  SEL *sel)
472 {
473 PIX *pixt;
474 
475  PROCNAME("pixClose");
476 
477  if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL)
478  return (PIX *)ERROR_PTR("pixd not returned", procName, pixd);
479 
480  if ((pixt = pixDilate(NULL, pixs, sel)) == NULL)
481  return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
482  pixErode(pixd, pixt, sel);
483  pixDestroy(&pixt);
484 
485  return pixd;
486 }
487 
488 
519 PIX *
521  PIX *pixs,
522  SEL *sel)
523 {
524 l_int32 xp, yp, xn, yn, xmax, xbord;
525 PIX *pixt1, *pixt2;
526 
527  PROCNAME("pixCloseSafe");
528 
529  if (!pixs)
530  return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
531  if (!sel)
532  return (PIX *)ERROR_PTR("sel not defined", procName, pixd);
533  if (pixGetDepth(pixs) != 1)
534  return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
535 
536  /* Symmetric b.c. handles correctly without added pixels */
537  if (MORPH_BC == SYMMETRIC_MORPH_BC)
538  return pixClose(pixd, pixs, sel);
539 
540  selFindMaxTranslations(sel, &xp, &yp, &xn, &yn);
541  xmax = L_MAX(xp, xn);
542  xbord = 32 * ((xmax + 31) / 32); /* full 32 bit words */
543 
544  if ((pixt1 = pixAddBorderGeneral(pixs, xbord, xbord, yp, yn, 0)) == NULL)
545  return (PIX *)ERROR_PTR("pixt1 not made", procName, pixd);
546  pixClose(pixt1, pixt1, sel);
547  if ((pixt2 = pixRemoveBorderGeneral(pixt1, xbord, xbord, yp, yn)) == NULL)
548  return (PIX *)ERROR_PTR("pixt2 not made", procName, pixd);
549  pixDestroy(&pixt1);
550 
551  if (!pixd)
552  return pixt2;
553 
554  pixCopy(pixd, pixt2);
555  pixDestroy(&pixt2);
556  return pixd;
557 }
558 
559 
586 PIX *
588  PIX *pixs,
589  SEL *sel)
590 {
591 PIX *pixt;
592 
593  PROCNAME("pixOpenGeneralized");
594 
595  if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL)
596  return (PIX *)ERROR_PTR("pixd not returned", procName, pixd);
597 
598  if ((pixt = pixHMT(NULL, pixs, sel)) == NULL)
599  return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
600  pixDilate(pixd, pixt, sel);
601  pixDestroy(&pixt);
602  return pixd;
603 }
604 
605 
633 PIX *
635  PIX *pixs,
636  SEL *sel)
637 {
638 PIX *pixt;
639 
640  PROCNAME("pixCloseGeneralized");
641 
642  if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL)
643  return (PIX *)ERROR_PTR("pixd not returned", procName, pixd);
644 
645  if ((pixt = pixDilate(NULL, pixs, sel)) == NULL)
646  return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
647  pixHMT(pixd, pixt, sel);
648  pixDestroy(&pixt);
649 
650  return pixd;
651 }
652 
653 
654 /*-----------------------------------------------------------------*
655  * Binary morphological (raster) ops with brick Sels *
656  *-----------------------------------------------------------------*/
683 PIX *
685  PIX *pixs,
686  l_int32 hsize,
687  l_int32 vsize)
688 {
689 PIX *pixt;
690 SEL *sel, *selh, *selv;
691 
692  PROCNAME("pixDilateBrick");
693 
694  if (!pixs)
695  return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
696  if (pixGetDepth(pixs) != 1)
697  return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
698  if (hsize < 1 || vsize < 1)
699  return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
700 
701  if (hsize == 1 && vsize == 1)
702  return pixCopy(pixd, pixs);
703  if (hsize == 1 || vsize == 1) { /* no intermediate result */
704  sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
705  pixd = pixDilate(pixd, pixs, sel);
706  selDestroy(&sel);
707  } else {
708  selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT);
709  selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT);
710  pixt = pixDilate(NULL, pixs, selh);
711  pixd = pixDilate(pixd, pixt, selv);
712  pixDestroy(&pixt);
713  selDestroy(&selh);
714  selDestroy(&selv);
715  }
716 
717  return pixd;
718 }
719 
720 
747 PIX *
749  PIX *pixs,
750  l_int32 hsize,
751  l_int32 vsize)
752 {
753 PIX *pixt;
754 SEL *sel, *selh, *selv;
755 
756  PROCNAME("pixErodeBrick");
757 
758  if (!pixs)
759  return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
760  if (pixGetDepth(pixs) != 1)
761  return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
762  if (hsize < 1 || vsize < 1)
763  return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
764 
765  if (hsize == 1 && vsize == 1)
766  return pixCopy(pixd, pixs);
767  if (hsize == 1 || vsize == 1) { /* no intermediate result */
768  sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
769  pixd = pixErode(pixd, pixs, sel);
770  selDestroy(&sel);
771  } else {
772  selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT);
773  selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT);
774  pixt = pixErode(NULL, pixs, selh);
775  pixd = pixErode(pixd, pixt, selv);
776  pixDestroy(&pixt);
777  selDestroy(&selh);
778  selDestroy(&selv);
779  }
780 
781  return pixd;
782 }
783 
784 
811 PIX *
813  PIX *pixs,
814  l_int32 hsize,
815  l_int32 vsize)
816 {
817 PIX *pixt;
818 SEL *sel, *selh, *selv;
819 
820  PROCNAME("pixOpenBrick");
821 
822  if (!pixs)
823  return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
824  if (pixGetDepth(pixs) != 1)
825  return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
826  if (hsize < 1 || vsize < 1)
827  return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
828 
829  if (hsize == 1 && vsize == 1)
830  return pixCopy(pixd, pixs);
831  if (hsize == 1 || vsize == 1) { /* no intermediate result */
832  sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
833  pixd = pixOpen(pixd, pixs, sel);
834  selDestroy(&sel);
835  } else { /* do separably */
836  selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT);
837  selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT);
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 
877 PIX *
879  PIX *pixs,
880  l_int32 hsize,
881  l_int32 vsize)
882 {
883 PIX *pixt;
884 SEL *sel, *selh, *selv;
885 
886  PROCNAME("pixCloseBrick");
887 
888  if (!pixs)
889  return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
890  if (pixGetDepth(pixs) != 1)
891  return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
892  if (hsize < 1 || vsize < 1)
893  return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
894 
895  if (hsize == 1 && vsize == 1)
896  return pixCopy(pixd, pixs);
897  if (hsize == 1 || vsize == 1) { /* no intermediate result */
898  sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
899  pixd = pixClose(pixd, pixs, sel);
900  selDestroy(&sel);
901  } else { /* do separably */
902  selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT);
903  selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT);
904  pixt = pixDilate(NULL, pixs, selh);
905  pixd = pixDilate(pixd, pixt, selv);
906  pixErode(pixt, pixd, selh);
907  pixErode(pixd, pixt, selv);
908  pixDestroy(&pixt);
909  selDestroy(&selh);
910  selDestroy(&selv);
911  }
912 
913  return pixd;
914 }
915 
916 
948 PIX *
950  PIX *pixs,
951  l_int32 hsize,
952  l_int32 vsize)
953 {
954 l_int32 maxtrans, bordsize;
955 PIX *pixsb, *pixt, *pixdb;
956 SEL *sel, *selh, *selv;
957 
958  PROCNAME("pixCloseSafeBrick");
959 
960  if (!pixs)
961  return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
962  if (pixGetDepth(pixs) != 1)
963  return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
964  if (hsize < 1 || vsize < 1)
965  return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
966 
967  if (hsize == 1 && vsize == 1)
968  return pixCopy(pixd, pixs);
969 
970  /* Symmetric b.c. handles correctly without added pixels */
971  if (MORPH_BC == SYMMETRIC_MORPH_BC)
972  return pixCloseBrick(pixd, pixs, hsize, vsize);
973 
974  maxtrans = L_MAX(hsize / 2, vsize / 2);
975  bordsize = 32 * ((maxtrans + 31) / 32); /* full 32 bit words */
976  pixsb = pixAddBorder(pixs, bordsize, 0);
977 
978  if (hsize == 1 || vsize == 1) { /* no intermediate result */
979  sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
980  pixdb = pixClose(NULL, pixsb, sel);
981  selDestroy(&sel);
982  } else { /* do separably */
983  selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT);
984  selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT);
985  pixt = pixDilate(NULL, pixsb, selh);
986  pixdb = pixDilate(NULL, pixt, selv);
987  pixErode(pixt, pixdb, selh);
988  pixErode(pixdb, pixt, selv);
989  pixDestroy(&pixt);
990  selDestroy(&selh);
991  selDestroy(&selv);
992  }
993 
994  pixt = pixRemoveBorder(pixdb, bordsize);
995  pixDestroy(&pixsb);
996  pixDestroy(&pixdb);
997 
998  if (!pixd) {
999  pixd = pixt;
1000  } else {
1001  pixCopy(pixd, pixt);
1002  pixDestroy(&pixt);
1003  }
1004 
1005  return pixd;
1006 }
1007 
1008 
1009 /*-----------------------------------------------------------------*
1010  * Binary composed morphological (raster) ops with brick Sels *
1011  *-----------------------------------------------------------------*/
1012 /* selectComposableSels()
1013  *
1014  * Input: size (of composed sel)
1015  * direction (L_HORIZ, L_VERT)
1016  * &sel1 (<optional return> contiguous sel; can be null)
1017  * &sel2 (<optional return> comb sel; can be null)
1018  * Return: 0 if OK, 1 on error
1019  *
1020  * Notes:
1021  * (1) When using composable Sels, where the original Sel is
1022  * decomposed into two, the best you can do in terms
1023  * of reducing the computation is by a factor:
1024  *
1025  * 2 * sqrt(size) / size
1026  *
1027  * In practice, you get quite close to this. E.g.,
1028  *
1029  * Sel size | Optimum reduction factor
1030  * -------- ------------------------
1031  * 36 | 1/3
1032  * 64 | 1/4
1033  * 144 | 1/6
1034  * 256 | 1/8
1035  */
1036 l_int32
1037 selectComposableSels(l_int32 size,
1038  l_int32 direction,
1039  SEL **psel1,
1040  SEL **psel2)
1041 {
1042 l_int32 factor1, factor2;
1043 
1044  PROCNAME("selectComposableSels");
1045 
1046  if (!psel1 && !psel2)
1047  return ERROR_INT("neither &sel1 nor &sel2 are defined", procName, 1);
1048  if (psel1) *psel1 = NULL;
1049  if (psel2) *psel2 = NULL;
1050  if (size < 1 || size > 250 * 250)
1051  return ERROR_INT("size < 1", procName, 1);
1052  if (direction != L_HORIZ && direction != L_VERT)
1053  return ERROR_INT("invalid direction", procName, 1);
1054 
1055  if (selectComposableSizes(size, &factor1, &factor2))
1056  return ERROR_INT("factors not found", procName, 1);
1057 
1058  if (psel1) {
1059  if (direction == L_HORIZ)
1060  *psel1 = selCreateBrick(1, factor1, 0, factor1 / 2, SEL_HIT);
1061  else
1062  *psel1 = selCreateBrick(factor1, 1, factor1 / 2 , 0, SEL_HIT);
1063  }
1064  if (psel2)
1065  *psel2 = selCreateComb(factor1, factor2, direction);
1066  return 0;
1067 }
1068 
1069 
1091 l_ok
1093  l_int32 *pfactor1,
1094  l_int32 *pfactor2)
1095 {
1096 l_int32 i, midval, val1, val2m, val2p;
1097 l_int32 index, prodm, prodp;
1098 l_int32 mincost, totcost, rastcostm, rastcostp, diffm, diffp;
1099 l_int32 lowval[256];
1100 l_int32 hival[256];
1101 l_int32 rastcost[256]; /* excess in sum of sizes (extra rasterops) */
1102 l_int32 diff[256]; /* diff between product (sel size) and input size */
1103 
1104  PROCNAME("selectComposableSizes");
1105 
1106  if (size < 1 || size > 250 * 250)
1107  return ERROR_INT("size < 1", procName, 1);
1108  if (!pfactor1 || !pfactor2)
1109  return ERROR_INT("&factor1 or &factor2 not defined", procName, 1);
1110 
1111  midval = (l_int32)(sqrt((l_float64)size) + 0.001);
1112  if (midval * midval == size) {
1113  *pfactor1 = *pfactor2 = midval;
1114  return 0;
1115  }
1116 
1117  /* Set up arrays. For each val1, optimize for lowest diff,
1118  * and save the rastcost, the diff, and the two factors. */
1119  for (val1 = midval + 1, i = 0; val1 > 0; val1--, i++) {
1120  val2m = size / val1;
1121  val2p = val2m + 1;
1122  prodm = val1 * val2m;
1123  prodp = val1 * val2p;
1124  rastcostm = val1 + val2m - 2 * midval;
1125  rastcostp = val1 + val2p - 2 * midval;
1126  diffm = L_ABS(size - prodm);
1127  diffp = L_ABS(size - prodp);
1128  if (diffm <= diffp) {
1129  lowval[i] = L_MIN(val1, val2m);
1130  hival[i] = L_MAX(val1, val2m);
1131  rastcost[i] = rastcostm;
1132  diff[i] = diffm;
1133  } else {
1134  lowval[i] = L_MIN(val1, val2p);
1135  hival[i] = L_MAX(val1, val2p);
1136  rastcost[i] = rastcostp;
1137  diff[i] = diffp;
1138  }
1139  }
1140 
1141  /* Choose the optimum factors; use cost ratio 4 on diff */
1142  mincost = 10000;
1143  index = 1; /* unimportant initial value */
1144  for (i = 0; i < midval + 1; i++) {
1145  if (diff[i] == 0 && rastcost[i] < ACCEPTABLE_COST) {
1146  *pfactor1 = hival[i];
1147  *pfactor2 = lowval[i];
1148  return 0;
1149  }
1150  totcost = 4 * diff[i] + rastcost[i];
1151  if (totcost < mincost) {
1152  mincost = totcost;
1153  index = i;
1154  }
1155  }
1156  *pfactor1 = hival[index];
1157  *pfactor2 = lowval[index];
1158 
1159  return 0;
1160 }
1161 
1162 
1203 PIX *
1205  PIX *pixs,
1206  l_int32 hsize,
1207  l_int32 vsize)
1208 {
1209 PIX *pix1, *pix2, *pix3;
1210 SEL *selh1, *selh2, *selv1, *selv2;
1211 
1212  PROCNAME("pixDilateCompBrick");
1213 
1214  if (!pixs)
1215  return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
1216  if (pixGetDepth(pixs) != 1)
1217  return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
1218  if (hsize < 1 || vsize < 1)
1219  return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
1220 
1221  if (hsize == 1 && vsize == 1)
1222  return pixCopy(pixd, pixs);
1223  if (hsize > 1)
1224  selectComposableSels(hsize, L_HORIZ, &selh1, &selh2);
1225  if (vsize > 1)
1226  selectComposableSels(vsize, L_VERT, &selv1, &selv2);
1227 
1228  pix1 = pixAddBorder(pixs, 32, 0);
1229  if (vsize == 1) {
1230  pix2 = pixDilate(NULL, pix1, selh1);
1231  pix3 = pixDilate(NULL, pix2, selh2);
1232  } else if (hsize == 1) {
1233  pix2 = pixDilate(NULL, pix1, selv1);
1234  pix3 = pixDilate(NULL, pix2, selv2);
1235  } else {
1236  pix2 = pixDilate(NULL, pix1, selh1);
1237  pix3 = pixDilate(NULL, pix2, selh2);
1238  pixDilate(pix2, pix3, selv1);
1239  pixDilate(pix3, pix2, selv2);
1240  }
1241  pixDestroy(&pix1);
1242  pixDestroy(&pix2);
1243 
1244  if (hsize > 1) {
1245  selDestroy(&selh1);
1246  selDestroy(&selh2);
1247  }
1248  if (vsize > 1) {
1249  selDestroy(&selv1);
1250  selDestroy(&selv2);
1251  }
1252 
1253  pix1 = pixRemoveBorder(pix3, 32);
1254  pixDestroy(&pix3);
1255  if (!pixd)
1256  return pix1;
1257  pixCopy(pixd, pix1);
1258  pixDestroy(&pix1);
1259  return pixd;
1260 }
1261 
1262 
1303 PIX *
1305  PIX *pixs,
1306  l_int32 hsize,
1307  l_int32 vsize)
1308 {
1309 PIX *pixt;
1310 SEL *selh1, *selh2, *selv1, *selv2;
1311 
1312  PROCNAME("pixErodeCompBrick");
1313 
1314  if (!pixs)
1315  return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
1316  if (pixGetDepth(pixs) != 1)
1317  return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
1318  if (hsize < 1 || vsize < 1)
1319  return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
1320 
1321  if (hsize == 1 && vsize == 1)
1322  return pixCopy(pixd, pixs);
1323  if (hsize > 1)
1324  selectComposableSels(hsize, L_HORIZ, &selh1, &selh2);
1325  if (vsize > 1)
1326  selectComposableSels(vsize, L_VERT, &selv1, &selv2);
1327  if (vsize == 1) {
1328  pixt = pixErode(NULL, pixs, selh1);
1329  pixd = pixErode(pixd, pixt, selh2);
1330  } else if (hsize == 1) {
1331  pixt = pixErode(NULL, pixs, selv1);
1332  pixd = pixErode(pixd, pixt, selv2);
1333  } else {
1334  pixt = pixErode(NULL, pixs, selh1);
1335  pixd = pixErode(pixd, pixt, selh2);
1336  pixErode(pixt, pixd, selv1);
1337  pixErode(pixd, pixt, selv2);
1338  }
1339  pixDestroy(&pixt);
1340 
1341  if (hsize > 1) {
1342  selDestroy(&selh1);
1343  selDestroy(&selh2);
1344  }
1345  if (vsize > 1) {
1346  selDestroy(&selv1);
1347  selDestroy(&selv2);
1348  }
1349 
1350  return pixd;
1351 }
1352 
1353 
1394 PIX *
1396  PIX *pixs,
1397  l_int32 hsize,
1398  l_int32 vsize)
1399 {
1400 PIX *pixt;
1401 SEL *selh1, *selh2, *selv1, *selv2;
1402 
1403  PROCNAME("pixOpenCompBrick");
1404 
1405  if (!pixs)
1406  return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
1407  if (pixGetDepth(pixs) != 1)
1408  return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
1409  if (hsize < 1 || vsize < 1)
1410  return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
1411 
1412  if (hsize == 1 && vsize == 1)
1413  return pixCopy(pixd, pixs);
1414  if (hsize > 1)
1415  selectComposableSels(hsize, L_HORIZ, &selh1, &selh2);
1416  if (vsize > 1)
1417  selectComposableSels(vsize, L_VERT, &selv1, &selv2);
1418  if (vsize == 1) {
1419  pixt = pixErode(NULL, pixs, selh1);
1420  pixd = pixErode(pixd, pixt, selh2);
1421  pixDilate(pixt, pixd, selh1);
1422  pixDilate(pixd, pixt, selh2);
1423  } else if (hsize == 1) {
1424  pixt = pixErode(NULL, pixs, selv1);
1425  pixd = pixErode(pixd, pixt, selv2);
1426  pixDilate(pixt, pixd, selv1);
1427  pixDilate(pixd, pixt, selv2);
1428  } else { /* do separably */
1429  pixt = pixErode(NULL, pixs, selh1);
1430  pixd = pixErode(pixd, pixt, selh2);
1431  pixErode(pixt, pixd, selv1);
1432  pixErode(pixd, pixt, selv2);
1433  pixDilate(pixt, pixd, selh1);
1434  pixDilate(pixd, pixt, selh2);
1435  pixDilate(pixt, pixd, selv1);
1436  pixDilate(pixd, pixt, selv2);
1437  }
1438  pixDestroy(&pixt);
1439 
1440  if (hsize > 1) {
1441  selDestroy(&selh1);
1442  selDestroy(&selh2);
1443  }
1444  if (vsize > 1) {
1445  selDestroy(&selv1);
1446  selDestroy(&selv2);
1447  }
1448 
1449  return pixd;
1450 }
1451 
1452 
1493 PIX *
1495  PIX *pixs,
1496  l_int32 hsize,
1497  l_int32 vsize)
1498 {
1499 PIX *pixt;
1500 SEL *selh1, *selh2, *selv1, *selv2;
1501 
1502  PROCNAME("pixCloseCompBrick");
1503 
1504  if (!pixs)
1505  return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
1506  if (pixGetDepth(pixs) != 1)
1507  return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
1508  if (hsize < 1 || vsize < 1)
1509  return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
1510 
1511  if (hsize == 1 && vsize == 1)
1512  return pixCopy(pixd, pixs);
1513  if (hsize > 1)
1514  selectComposableSels(hsize, L_HORIZ, &selh1, &selh2);
1515  if (vsize > 1)
1516  selectComposableSels(vsize, L_VERT, &selv1, &selv2);
1517  if (vsize == 1) {
1518  pixt = pixDilate(NULL, pixs, selh1);
1519  pixd = pixDilate(pixd, pixt, selh2);
1520  pixErode(pixt, pixd, selh1);
1521  pixErode(pixd, pixt, selh2);
1522  } else if (hsize == 1) {
1523  pixt = pixDilate(NULL, pixs, selv1);
1524  pixd = pixDilate(pixd, pixt, selv2);
1525  pixErode(pixt, pixd, selv1);
1526  pixErode(pixd, pixt, selv2);
1527  } else { /* do separably */
1528  pixt = pixDilate(NULL, pixs, selh1);
1529  pixd = pixDilate(pixd, pixt, selh2);
1530  pixDilate(pixt, pixd, selv1);
1531  pixDilate(pixd, pixt, selv2);
1532  pixErode(pixt, pixd, selh1);
1533  pixErode(pixd, pixt, selh2);
1534  pixErode(pixt, pixd, selv1);
1535  pixErode(pixd, pixt, selv2);
1536  }
1537  pixDestroy(&pixt);
1538 
1539  if (hsize > 1) {
1540  selDestroy(&selh1);
1541  selDestroy(&selh2);
1542  }
1543  if (vsize > 1) {
1544  selDestroy(&selv1);
1545  selDestroy(&selv2);
1546  }
1547 
1548  return pixd;
1549 }
1550 
1551 
1597 PIX *
1599  PIX *pixs,
1600  l_int32 hsize,
1601  l_int32 vsize)
1602 {
1603 l_int32 maxtrans, bordsize;
1604 PIX *pixsb, *pixt, *pixdb;
1605 SEL *selh1, *selh2, *selv1, *selv2;
1606 
1607  PROCNAME("pixCloseSafeCompBrick");
1608 
1609  if (!pixs)
1610  return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
1611  if (pixGetDepth(pixs) != 1)
1612  return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
1613  if (hsize < 1 || vsize < 1)
1614  return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
1615 
1616  if (hsize == 1 && vsize == 1)
1617  return pixCopy(pixd, pixs);
1618 
1619  /* Symmetric b.c. handles correctly without added pixels */
1620  if (MORPH_BC == SYMMETRIC_MORPH_BC)
1621  return pixCloseCompBrick(pixd, pixs, hsize, vsize);
1622 
1623  maxtrans = L_MAX(hsize / 2, vsize / 2);
1624  bordsize = 32 * ((maxtrans + 31) / 32); /* full 32 bit words */
1625  pixsb = pixAddBorder(pixs, bordsize, 0);
1626 
1627  if (hsize > 1)
1628  selectComposableSels(hsize, L_HORIZ, &selh1, &selh2);
1629  if (vsize > 1)
1630  selectComposableSels(vsize, L_VERT, &selv1, &selv2);
1631  if (vsize == 1) {
1632  pixt = pixDilate(NULL, pixsb, selh1);
1633  pixdb = pixDilate(NULL, pixt, selh2);
1634  pixErode(pixt, pixdb, selh1);
1635  pixErode(pixdb, pixt, selh2);
1636  } else if (hsize == 1) {
1637  pixt = pixDilate(NULL, pixsb, selv1);
1638  pixdb = pixDilate(NULL, pixt, selv2);
1639  pixErode(pixt, pixdb, selv1);
1640  pixErode(pixdb, pixt, selv2);
1641  } else { /* do separably */
1642  pixt = pixDilate(NULL, pixsb, selh1);
1643  pixdb = pixDilate(NULL, pixt, selh2);
1644  pixDilate(pixt, pixdb, selv1);
1645  pixDilate(pixdb, pixt, selv2);
1646  pixErode(pixt, pixdb, selh1);
1647  pixErode(pixdb, pixt, selh2);
1648  pixErode(pixt, pixdb, selv1);
1649  pixErode(pixdb, pixt, selv2);
1650  }
1651  pixDestroy(&pixt);
1652 
1653  pixt = pixRemoveBorder(pixdb, bordsize);
1654  pixDestroy(&pixsb);
1655  pixDestroy(&pixdb);
1656 
1657  if (!pixd) {
1658  pixd = pixt;
1659  } else {
1660  pixCopy(pixd, pixt);
1661  pixDestroy(&pixt);
1662  }
1663 
1664  if (hsize > 1) {
1665  selDestroy(&selh1);
1666  selDestroy(&selh2);
1667  }
1668  if (vsize > 1) {
1669  selDestroy(&selv1);
1670  selDestroy(&selv2);
1671  }
1672 
1673  return pixd;
1674 }
1675 
1676 
1677 /*-----------------------------------------------------------------*
1678  * Functions associated with boundary conditions *
1679  *-----------------------------------------------------------------*/
1686 void
1688 {
1689  PROCNAME("resetMorphBoundaryCondition");
1690 
1691  if (bc != SYMMETRIC_MORPH_BC && bc != ASYMMETRIC_MORPH_BC) {
1692  L_WARNING("invalid bc; using asymmetric\n", procName);
1693  bc = ASYMMETRIC_MORPH_BC;
1694  }
1695  MORPH_BC = bc;
1696  return;
1697 }
1698 
1699 
1707 l_uint32
1709  l_int32 depth)
1710 {
1711  PROCNAME("getMorphBorderPixelColor");
1712 
1713  if (type != L_MORPH_DILATE && type != L_MORPH_ERODE)
1714  return ERROR_INT("invalid type", procName, 0);
1715  if (depth != 1 && depth != 2 && depth != 4 && depth != 8 &&
1716  depth != 16 && depth != 32)
1717  return ERROR_INT("invalid depth", procName, 0);
1718 
1719  if (MORPH_BC == ASYMMETRIC_MORPH_BC || type == L_MORPH_DILATE)
1720  return 0;
1721 
1722  /* Symmetric & erosion */
1723  if (depth < 32)
1724  return ((1 << depth) - 1);
1725  else /* depth == 32 */
1726  return 0xffffff00;
1727 }
1728 
1729 
1730 /*-----------------------------------------------------------------*
1731  * Static helpers for arg processing *
1732  *-----------------------------------------------------------------*/
1748 static PIX *
1750  PIX *pixs,
1751  SEL *sel,
1752  PIX **ppixt)
1753 {
1754 l_int32 sx, sy;
1755 
1756  PROCNAME("processMorphArgs1");
1757 
1758  if (!ppixt)
1759  return (PIX *)ERROR_PTR("&pixt not defined", procName, pixd);
1760  *ppixt = NULL;
1761  if (!pixs)
1762  return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
1763  if (!sel)
1764  return (PIX *)ERROR_PTR("sel not defined", procName, pixd);
1765  if (pixGetDepth(pixs) != 1)
1766  return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
1767 
1768  selGetParameters(sel, &sx, &sy, NULL, NULL);
1769  if (sx == 0 || sy == 0)
1770  return (PIX *)ERROR_PTR("sel of size 0", procName, pixd);
1771 
1772  /* We require pixd to exist and to be the same size as pixs.
1773  * Further, pixt must be a copy (or clone) of pixs. */
1774  if (!pixd) {
1775  if ((pixd = pixCreateTemplate(pixs)) == NULL)
1776  return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1777  *ppixt = pixClone(pixs);
1778  } else {
1779  pixResizeImageData(pixd, pixs);
1780  if (pixd == pixs) { /* in-place; must make a copy of pixs */
1781  if ((*ppixt = pixCopy(NULL, pixs)) == NULL)
1782  return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
1783  } else {
1784  *ppixt = pixClone(pixs);
1785  }
1786  }
1787  return pixd;
1788 }
1789 
1790 
1796 static PIX *
1798  PIX *pixs,
1799  SEL *sel)
1800 {
1801 l_int32 sx, sy;
1802 
1803  PROCNAME("processMorphArgs2");
1804 
1805  if (!pixs)
1806  return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
1807  if (!sel)
1808  return (PIX *)ERROR_PTR("sel not defined", procName, pixd);
1809  if (pixGetDepth(pixs) != 1)
1810  return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
1811 
1812  selGetParameters(sel, &sx, &sy, NULL, NULL);
1813  if (sx == 0 || sy == 0)
1814  return (PIX *)ERROR_PTR("sel of size 0", procName, pixd);
1815 
1816  if (!pixd)
1817  return pixCreateTemplate(pixs);
1818  pixResizeImageData(pixd, pixs);
1819  return pixd;
1820 }
PIX * pixCloseGeneralized(PIX *pixd, PIX *pixs, SEL *sel)
pixCloseGeneralized()
Definition: morph.c:634
l_ok selectComposableSizes(l_int32 size, l_int32 *pfactor1, l_int32 *pfactor2)
selectComposableSizes()
Definition: morph.c:1092
l_ok pixResizeImageData(PIX *pixd, const PIX *pixs)
pixResizeImageData()
Definition: pix1.c:696
#define PIX_CLR
Definition: pix.h:330
PIX * pixRemoveBorderGeneral(PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot)
pixRemoveBorderGeneral()
Definition: pix2.c:1918
l_uint32 getMorphBorderPixelColor(l_int32 type, l_int32 depth)
getMorphBorderPixelColor()
Definition: morph.c:1708
PIX * pixCloseBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseBrick()
Definition: morph.c:878
PIX * pixOpen(PIX *pixd, PIX *pixs, SEL *sel)
pixOpen()
Definition: morph.c:422
l_ok pixRasterop(PIX *pixd, l_int32 dx, l_int32 dy, l_int32 dw, l_int32 dh, l_int32 op, PIX *pixs, l_int32 sx, l_int32 sy)
pixRasterop()
Definition: rop.c:193
PIX * pixCloseSafeCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseSafeCompBrick()
Definition: morph.c:1598
PIX * pixDilateBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateBrick()
Definition: morph.c:684
l_ok selGetParameters(SEL *sel, l_int32 *psy, l_int32 *psx, l_int32 *pcy, l_int32 *pcx)
selGetParameters()
Definition: sel1.c:850
void resetMorphBoundaryCondition(l_int32 bc)
resetMorphBoundaryCondition()
Definition: morph.c:1687
static PIX * processMorphArgs2(PIX *pixd, PIX *pixs, SEL *sel)
processMorphArgs2()
Definition: morph.c:1797
l_ok pixSetAll(PIX *pix)
pixSetAll()
Definition: pix2.c:741
l_ok selFindMaxTranslations(SEL *sel, l_int32 *pxp, l_int32 *pyp, l_int32 *pxn, l_int32 *pyn)
selFindMaxTranslations()
Definition: sel1.c:1193
l_int32 ** data
Definition: morph.h:68
PIX * pixCreateTemplate(PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:367
PIX * pixAddBorder(PIX *pixs, l_int32 npix, l_uint32 val)
pixAddBorder()
Definition: pix2.c:1748
PIX * pixErode(PIX *pixd, PIX *pixs, SEL *sel)
pixErode()
Definition: morph.c:263
PIX * pixRemoveBorder(PIX *pixs, l_int32 npix)
pixRemoveBorder()
Definition: pix2.c:1897
PIX * pixCloseSafeBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseSafeBrick()
Definition: morph.c:949
l_ok pixClearAll(PIX *pix)
pixClearAll()
Definition: pix2.c:712
void selDestroy(SEL **psel)
selDestroy()
Definition: sel1.c:337
SEL * selCreateComb(l_int32 factor1, l_int32 factor2, l_int32 direction)
selCreateComb()
Definition: sel1.c:462
SEL * selCreateBrick(l_int32 h, l_int32 w, l_int32 cy, l_int32 cx, l_int32 type)
selCreateBrick()
Definition: sel1.c:418
PIX * pixCloseSafe(PIX *pixd, PIX *pixs, SEL *sel)
pixCloseSafe()
Definition: morph.c:520
PIX * pixDilateCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateCompBrick()
Definition: morph.c:1204
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:515
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:543
PIX * pixOpenGeneralized(PIX *pixd, PIX *pixs, SEL *sel)
pixOpenGeneralized()
Definition: morph.c:587
PIX * pixOpenBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixOpenBrick()
Definition: morph.c:812
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1065
PIX * pixCloseCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseCompBrick()
Definition: morph.c:1494
PIX * pixHMT(PIX *pixd, PIX *pixs, SEL *sel)
pixHMT()
Definition: morph.c:338
#define PIX_NOT(op)
Definition: pix.h:329
PIX * pixClose(PIX *pixd, PIX *pixs, SEL *sel)
pixClose()
Definition: morph.c:469
PIX * pixErodeCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixErodeCompBrick()
Definition: morph.c:1304
Definition: pix.h:134
static PIX * processMorphArgs1(PIX *pixd, PIX *pixs, SEL *sel, PIX **ppixt)
processMorphArgs1()
Definition: morph.c:1749
#define PIX_SRC
Definition: pix.h:327
PIX * pixCopy(PIX *pixd, PIX *pixs)
pixCopy()
Definition: pix1.c:628
PIX * pixDilate(PIX *pixd, PIX *pixs, SEL *sel)
pixDilate()
Definition: morph.c:209
PIX * pixErodeBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixErodeBrick()
Definition: morph.c:748
PIX * pixOpenCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixOpenCompBrick()
Definition: morph.c:1395
PIX * pixAddBorderGeneral(PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot, l_uint32 val)
pixAddBorderGeneral()
Definition: pix2.c:1842
#define PIX_DST
Definition: pix.h:328