Leptonica  1.77.0
Image processing and image analysis suite
seedfill.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 
167 #include <math.h>
168 #include "allheaders.h"
169 
170 struct L_Pixel
171 {
172  l_int32 x;
173  l_int32 y;
174 };
175 typedef struct L_Pixel L_PIXEL;
176 
177 static void seedfillBinaryLow(l_uint32 *datas, l_int32 hs, l_int32 wpls,
178  l_uint32 *datam, l_int32 hm, l_int32 wplm,
179  l_int32 connectivity);
180 static void seedfillGrayLow(l_uint32 *datas, l_int32 w, l_int32 h,
181  l_int32 wpls, l_uint32 *datam, l_int32 wplm,
182  l_int32 connectivity);
183 static void seedfillGrayInvLow(l_uint32 *datas, l_int32 w, l_int32 h,
184  l_int32 wpls, l_uint32 *datam, l_int32 wplm,
185  l_int32 connectivity);
186 static void seedfillGrayLowSimple(l_uint32 *datas, l_int32 w, l_int32 h,
187  l_int32 wpls, l_uint32 *datam, l_int32 wplm,
188  l_int32 connectivity);
189 static void seedfillGrayInvLowSimple(l_uint32 *datas, l_int32 w, l_int32 h,
190  l_int32 wpls, l_uint32 *datam,
191  l_int32 wplm, l_int32 connectivity);
192 static void distanceFunctionLow(l_uint32 *datad, l_int32 w, l_int32 h,
193  l_int32 d, l_int32 wpld, l_int32 connectivity);
194 static void seedspreadLow(l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpld,
195  l_uint32 *datat, l_int32 wplt, l_int32 connectivity);
196 
197 
198 static l_int32 pixQualifyLocalMinima(PIX *pixs, PIX *pixm, l_int32 maxval);
199 
200 #ifndef NO_CONSOLE_IO
201 #define DEBUG_PRINT_ITERS 0
202 #endif /* ~NO_CONSOLE_IO */
203 
204  /* Two-way (UL --> LR, LR --> UL) sweep iterations; typically need only 4 */
205 static const l_int32 MAX_ITERS = 40;
206 
207 
208 /*-----------------------------------------------------------------------*
209  * Vincent's Iterative Binary Seedfill method *
210  *-----------------------------------------------------------------------*/
242 PIX *
244  PIX *pixs,
245  PIX *pixm,
246  l_int32 connectivity)
247 {
248 l_int32 i, boolval;
249 l_int32 hd, hm, wpld, wplm;
250 l_uint32 *datad, *datam;
251 PIX *pixt;
252 
253  PROCNAME("pixSeedfillBinary");
254 
255  if (!pixs || pixGetDepth(pixs) != 1)
256  return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, pixd);
257  if (!pixm || pixGetDepth(pixm) != 1)
258  return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", procName, pixd);
259  if (connectivity != 4 && connectivity != 8)
260  return (PIX *)ERROR_PTR("connectivity not in {4,8}", procName, pixd);
261 
262  /* Prepare pixd as a copy of pixs if not identical */
263  if ((pixd = pixCopy(pixd, pixs)) == NULL)
264  return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
265 
266  /* pixt is used to test for completion */
267  if ((pixt = pixCreateTemplate(pixs)) == NULL)
268  return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
269 
270  hd = pixGetHeight(pixd);
271  hm = pixGetHeight(pixm); /* included so seedfillBinaryLow() can clip */
272  datad = pixGetData(pixd);
273  datam = pixGetData(pixm);
274  wpld = pixGetWpl(pixd);
275  wplm = pixGetWpl(pixm);
276 
277  pixSetPadBits(pixm, 0);
278 
279  for (i = 0; i < MAX_ITERS; i++) {
280  pixCopy(pixt, pixd);
281  seedfillBinaryLow(datad, hd, wpld, datam, hm, wplm, connectivity);
282  pixEqual(pixd, pixt, &boolval);
283  if (boolval == 1) {
284 #if DEBUG_PRINT_ITERS
285  fprintf(stderr, "Binary seed fill converged: %d iters\n", i + 1);
286 #endif /* DEBUG_PRINT_ITERS */
287  break;
288  }
289  }
290 
291  pixDestroy(&pixt);
292  return pixd;
293 }
294 
295 
329 PIX *
331  PIX *pixs,
332  PIX *pixm,
333  l_int32 connectivity,
334  l_int32 xmax,
335  l_int32 ymax)
336 {
337 l_int32 w, h;
338 PIX *pix1, *pix2;
339 
340  PROCNAME("pixSeedfillBinaryRestricted");
341 
342  if (!pixs || pixGetDepth(pixs) != 1)
343  return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, pixd);
344  if (!pixm || pixGetDepth(pixm) != 1)
345  return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", procName, pixd);
346  if (connectivity != 4 && connectivity != 8)
347  return (PIX *)ERROR_PTR("connectivity not in {4,8}", procName, pixd);
348  if (xmax == 0 && ymax == 0) /* no filling permitted */
349  return pixClone(pixs);
350  if (xmax < 0 || ymax < 0) {
351  L_ERROR("xmax and ymax must be non-negative", procName);
352  return pixClone(pixs);
353  }
354 
355  /* Full fill from the seed into the mask. */
356  if ((pix1 = pixSeedfillBinary(NULL, pixs, pixm, connectivity)) == NULL)
357  return (PIX *)ERROR_PTR("pix1 not made", procName, pixd);
358 
359  /* Dilate the seed. This gives the maximal region where changes
360  * are permitted. Invert to get the region where pixs is
361  * not allowed to change. */
362  pix2 = pixDilateCompBrick(NULL, pixs, 2 * xmax + 1, 2 * ymax + 1);
363  pixInvert(pix2, pix2);
364 
365  /* Blank the region of pix1 specified by the fg of pix2.
366  * This is not yet the final result, because it may have fg pixels
367  * that are not accessible from the seed in the restricted distance.
368  * For example, such pixels may be connected to the original seed,
369  * but through a path that goes outside the permitted region. */
370  pixGetDimensions(pixs, &w, &h, NULL);
371  pixRasterop(pix1, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC), pix2, 0, 0);
372 
373  /* To get the accessible pixels in the restricted region, do
374  * a second seedfill from the original seed, using pix1 as
375  * a mask. The result, in pixd, will not have any bad fg
376  * pixels that were in pix1. */
377  pixd = pixSeedfillBinary(pixd, pixs, pix1, connectivity);
378 
379  pixDestroy(&pix1);
380  pixDestroy(&pix2);
381  return pixd;
382 }
383 
384 
397 static void
398 seedfillBinaryLow(l_uint32 *datas,
399  l_int32 hs,
400  l_int32 wpls,
401  l_uint32 *datam,
402  l_int32 hm,
403  l_int32 wplm,
404  l_int32 connectivity)
405 {
406 l_int32 i, j, h, wpl;
407 l_uint32 word, mask;
408 l_uint32 wordabove, wordleft, wordbelow, wordright;
409 l_uint32 wordprev; /* test against this in previous iteration */
410 l_uint32 *lines, *linem;
411 
412  PROCNAME("seedfillBinaryLow");
413 
414  h = L_MIN(hs, hm);
415  wpl = L_MIN(wpls, wplm);
416 
417  switch (connectivity)
418  {
419  case 4:
420  /* UL --> LR scan */
421  for (i = 0; i < h; i++) {
422  lines = datas + i * wpls;
423  linem = datam + i * wplm;
424  for (j = 0; j < wpl; j++) {
425  word = *(lines + j);
426  mask = *(linem + j);
427 
428  /* OR from word above and from word to left; mask */
429  if (i > 0) {
430  wordabove = *(lines - wpls + j);
431  word |= wordabove;
432  }
433  if (j > 0) {
434  wordleft = *(lines + j - 1);
435  word |= wordleft << 31;
436  }
437  word &= mask;
438 
439  /* No need to fill horizontally? */
440  if (!word || !(~word)) {
441  *(lines + j) = word;
442  continue;
443  }
444 
445  while (1) {
446  wordprev = word;
447  word = (word | (word >> 1) | (word << 1)) & mask;
448  if ((word ^ wordprev) == 0) {
449  *(lines + j) = word;
450  break;
451  }
452  }
453  }
454  }
455 
456  /* LR --> UL scan */
457  for (i = h - 1; i >= 0; i--) {
458  lines = datas + i * wpls;
459  linem = datam + i * wplm;
460  for (j = wpl - 1; j >= 0; j--) {
461  word = *(lines + j);
462  mask = *(linem + j);
463 
464  /* OR from word below and from word to right; mask */
465  if (i < h - 1) {
466  wordbelow = *(lines + wpls + j);
467  word |= wordbelow;
468  }
469  if (j < wpl - 1) {
470  wordright = *(lines + j + 1);
471  word |= wordright >> 31;
472  }
473  word &= mask;
474 
475  /* No need to fill horizontally? */
476  if (!word || !(~word)) {
477  *(lines + j) = word;
478  continue;
479  }
480 
481  while (1) {
482  wordprev = word;
483  word = (word | (word >> 1) | (word << 1)) & mask;
484  if ((word ^ wordprev) == 0) {
485  *(lines + j) = word;
486  break;
487  }
488  }
489  }
490  }
491  break;
492 
493  case 8:
494  /* UL --> LR scan */
495  for (i = 0; i < h; i++) {
496  lines = datas + i * wpls;
497  linem = datam + i * wplm;
498  for (j = 0; j < wpl; j++) {
499  word = *(lines + j);
500  mask = *(linem + j);
501 
502  /* OR from words above and from word to left; mask */
503  if (i > 0) {
504  wordabove = *(lines - wpls + j);
505  word |= (wordabove | (wordabove << 1) | (wordabove >> 1));
506  if (j > 0)
507  word |= (*(lines - wpls + j - 1)) << 31;
508  if (j < wpl - 1)
509  word |= (*(lines - wpls + j + 1)) >> 31;
510  }
511  if (j > 0) {
512  wordleft = *(lines + j - 1);
513  word |= wordleft << 31;
514  }
515  word &= mask;
516 
517  /* No need to fill horizontally? */
518  if (!word || !(~word)) {
519  *(lines + j) = word;
520  continue;
521  }
522 
523  while (1) {
524  wordprev = word;
525  word = (word | (word >> 1) | (word << 1)) & mask;
526  if ((word ^ wordprev) == 0) {
527  *(lines + j) = word;
528  break;
529  }
530  }
531  }
532  }
533 
534  /* LR --> UL scan */
535  for (i = h - 1; i >= 0; i--) {
536  lines = datas + i * wpls;
537  linem = datam + i * wplm;
538  for (j = wpl - 1; j >= 0; j--) {
539  word = *(lines + j);
540  mask = *(linem + j);
541 
542  /* OR from words below and from word to right; mask */
543  if (i < h - 1) {
544  wordbelow = *(lines + wpls + j);
545  word |= (wordbelow | (wordbelow << 1) | (wordbelow >> 1));
546  if (j > 0)
547  word |= (*(lines + wpls + j - 1)) << 31;
548  if (j < wpl - 1)
549  word |= (*(lines + wpls + j + 1)) >> 31;
550  }
551  if (j < wpl - 1) {
552  wordright = *(lines + j + 1);
553  word |= wordright >> 31;
554  }
555  word &= mask;
556 
557  /* No need to fill horizontally? */
558  if (!word || !(~word)) {
559  *(lines + j) = word;
560  continue;
561  }
562 
563  while (1) {
564  wordprev = word;
565  word = (word | (word >> 1) | (word << 1)) & mask;
566  if ((word ^ wordprev) == 0) {
567  *(lines + j) = word;
568  break;
569  }
570  }
571  }
572  }
573  break;
574 
575  default:
576  L_ERROR("connectivity must be 4 or 8\n", procName);
577  return;
578  }
579 }
580 
581 
604 PIX *
606  l_int32 connectivity)
607 {
608 PIX *pixsi, *pixd;
609 
610  PROCNAME("pixHolesByFilling");
611 
612  if (!pixs || pixGetDepth(pixs) != 1)
613  return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
614  if (connectivity != 4 && connectivity != 8)
615  return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
616 
617  if ((pixd = pixCreateTemplate(pixs)) == NULL)
618  return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
619  if ((pixsi = pixInvert(NULL, pixs)) == NULL) {
620  pixDestroy(&pixd);
621  return (PIX *)ERROR_PTR("pixsi not made", procName, NULL);
622  }
623 
624  pixSetOrClearBorder(pixd, 1, 1, 1, 1, PIX_SET);
625  pixSeedfillBinary(pixd, pixd, pixsi, connectivity);
626  pixOr(pixd, pixd, pixs);
627  pixInvert(pixd, pixd);
628  pixDestroy(&pixsi);
629  return pixd;
630 }
631 
632 
655 PIX *
657  l_int32 connectivity)
658 {
659 PIX *pixsi, *pixd;
660 
661  PROCNAME("pixFillClosedBorders");
662 
663  if (!pixs || pixGetDepth(pixs) != 1)
664  return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
665  if (connectivity != 4 && connectivity != 8)
666  return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
667 
668  if ((pixd = pixCreateTemplate(pixs)) == NULL)
669  return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
670  pixSetOrClearBorder(pixd, 1, 1, 1, 1, PIX_SET);
671  pixSubtract(pixd, pixd, pixs);
672  if ((pixsi = pixInvert(NULL, pixs)) == NULL) {
673  pixDestroy(&pixd);
674  return (PIX *)ERROR_PTR("pixsi not made", procName, NULL);
675  }
676 
677  pixSeedfillBinary(pixd, pixd, pixsi, connectivity);
678  pixInvert(pixd, pixd);
679  pixDestroy(&pixsi);
680 
681  return pixd;
682 }
683 
684 
693 PIX *
695  l_int32 connectivity)
696 {
697 PIX *pixd;
698 
699  PROCNAME("pixExtractBorderConnComps");
700 
701  if (!pixs || pixGetDepth(pixs) != 1)
702  return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
703  if (connectivity != 4 && connectivity != 8)
704  return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
705 
706  /* Start with 1 pixel wide black border as seed in pixd */
707  if ((pixd = pixCreateTemplate(pixs)) == NULL)
708  return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
709  pixSetOrClearBorder(pixd, 1, 1, 1, 1, PIX_SET);
710 
711  /* Fill in pixd from the seed, using pixs as the filling mask.
712  * This fills all components from pixs that are touching the border. */
713  pixSeedfillBinary(pixd, pixd, pixs, connectivity);
714 
715  return pixd;
716 }
717 
718 
732 PIX *
734  l_int32 connectivity)
735 {
736 PIX *pixd;
737 
738  PROCNAME("pixRemoveBorderConnComps");
739 
740  if (!pixs || pixGetDepth(pixs) != 1)
741  return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
742  if (connectivity != 4 && connectivity != 8)
743  return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
744 
745  /* Fill from a 1 pixel wide seed at the border into all components
746  * in pixs (the filling mask) that are touching the border */
747  pixd = pixExtractBorderConnComps(pixs, connectivity);
748 
749  /* Save in pixd only those components in pixs not touching the border */
750  pixXor(pixd, pixd, pixs);
751  return pixd;
752 }
753 
754 
782 PIX *
784  l_int32 connectivity)
785 {
786 PIX *pixd;
787 
788  PROCNAME("pixFillBgFromBorder");
789 
790  if (!pixs || pixGetDepth(pixs) != 1)
791  return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
792  if (connectivity != 4 && connectivity != 8)
793  return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
794 
795  /* Invert to turn bg touching the border to a fg component.
796  * Extract this by filling from a 1 pixel wide seed at the border. */
797  pixInvert(pixs, pixs);
798  pixd = pixExtractBorderConnComps(pixs, connectivity);
799  pixInvert(pixs, pixs); /* restore pixs */
800 
801  /* Bit-or the filled bg component with pixs */
802  pixOr(pixd, pixd, pixs);
803  return pixd;
804 }
805 
806 
807 /*-----------------------------------------------------------------------*
808  * Hole-filling of components to bounding rectangle *
809  *-----------------------------------------------------------------------*/
841 PIX *
843  l_int32 minsize,
844  l_float32 maxhfract,
845  l_float32 minfgfract)
846 {
847 l_int32 i, x, y, w, h, n, nfg, nh, ntot, area;
848 l_int32 *tab;
849 l_float32 hfract; /* measured hole fraction */
850 l_float32 fgfract; /* measured fg fraction */
851 BOXA *boxa;
852 PIX *pixd, *pixfg, *pixh;
853 PIXA *pixa;
854 
855  PROCNAME("pixFillHolesToBoundingRect");
856 
857  if (!pixs || pixGetDepth(pixs) != 1)
858  return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
859 
860  pixd = pixCopy(NULL, pixs);
861  boxa = pixConnComp(pixd, &pixa, 8);
862  n = boxaGetCount(boxa);
863  tab = makePixelSumTab8();
864  for (i = 0; i < n; i++) {
865  boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h);
866  area = w * h;
867  if (area < minsize)
868  continue;
869  pixfg = pixaGetPix(pixa, i, L_COPY);
870  pixh = pixHolesByFilling(pixfg, 4); /* holes only */
871  pixCountPixels(pixfg, &nfg, tab);
872  pixCountPixels(pixh, &nh, tab);
873  hfract = (l_float32)nh / (l_float32)nfg;
874  ntot = nfg;
875  if (hfract <= maxhfract) /* we will fill the holes (at least) */
876  ntot = nfg + nh;
877  fgfract = (l_float32)ntot / (l_float32)area;
878  if (fgfract >= minfgfract) { /* fill to bounding rect */
879  pixSetAll(pixfg);
880  pixRasterop(pixd, x, y, w, h, PIX_SRC, pixfg, 0, 0);
881  } else if (hfract <= maxhfract) { /* fill just the holes */
882  pixRasterop(pixd, x, y, w, h, PIX_DST | PIX_SRC , pixh, 0, 0);
883  }
884  pixDestroy(&pixfg);
885  pixDestroy(&pixh);
886  }
887  boxaDestroy(&boxa);
888  pixaDestroy(&pixa);
889  LEPT_FREE(tab);
890 
891  return pixd;
892 }
893 
894 
895 /*-----------------------------------------------------------------------*
896  * Vincent's hybrid Grayscale Seedfill method *
897  *-----------------------------------------------------------------------*/
922 l_ok
924  PIX *pixm,
925  l_int32 connectivity)
926 {
927 l_int32 h, w, wpls, wplm;
928 l_uint32 *datas, *datam;
929 
930  PROCNAME("pixSeedfillGray");
931 
932  if (!pixs || pixGetDepth(pixs) != 8)
933  return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
934  if (!pixm || pixGetDepth(pixm) != 8)
935  return ERROR_INT("pixm not defined or not 8 bpp", procName, 1);
936  if (connectivity != 4 && connectivity != 8)
937  return ERROR_INT("connectivity not in {4,8}", procName, 1);
938 
939  /* Make sure the sizes of seed and mask images are the same */
940  if (pixSizesEqual(pixs, pixm) == 0)
941  return ERROR_INT("pixs and pixm sizes differ", procName, 1);
942 
943  datas = pixGetData(pixs);
944  datam = pixGetData(pixm);
945  wpls = pixGetWpl(pixs);
946  wplm = pixGetWpl(pixm);
947  pixGetDimensions(pixs, &w, &h, NULL);
948  seedfillGrayLow(datas, w, h, wpls, datam, wplm, connectivity);
949 
950  return 0;
951 }
952 
953 
981 l_ok
983  PIX *pixm,
984  l_int32 connectivity)
985 {
986 l_int32 h, w, wpls, wplm;
987 l_uint32 *datas, *datam;
988 
989  PROCNAME("pixSeedfillGrayInv");
990 
991  if (!pixs || pixGetDepth(pixs) != 8)
992  return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
993  if (!pixm || pixGetDepth(pixm) != 8)
994  return ERROR_INT("pixm not defined or not 8 bpp", procName, 1);
995  if (connectivity != 4 && connectivity != 8)
996  return ERROR_INT("connectivity not in {4,8}", procName, 1);
997 
998  /* Make sure the sizes of seed and mask images are the same */
999  if (pixSizesEqual(pixs, pixm) == 0)
1000  return ERROR_INT("pixs and pixm sizes differ", procName, 1);
1001 
1002  datas = pixGetData(pixs);
1003  datam = pixGetData(pixm);
1004  wpls = pixGetWpl(pixs);
1005  wplm = pixGetWpl(pixm);
1006  pixGetDimensions(pixs, &w, &h, NULL);
1007  seedfillGrayInvLow(datas, w, h, wpls, datam, wplm, connectivity);
1008 
1009  return 0;
1010 }
1011 
1012 
1058 static void
1059 seedfillGrayLow(l_uint32 *datas,
1060  l_int32 w,
1061  l_int32 h,
1062  l_int32 wpls,
1063  l_uint32 *datam,
1064  l_int32 wplm,
1065  l_int32 connectivity)
1066 {
1067 l_uint8 val1, val2, val3, val4, val5, val6, val7, val8;
1068 l_uint8 val, maxval, maskval, boolval;
1069 l_int32 i, j, imax, jmax, queue_size;
1070 l_uint32 *lines, *linem;
1071 L_PIXEL *pixel;
1072 L_QUEUE *lq_pixel;
1073 
1074  PROCNAME("seedfillGrayLow");
1075 
1076  if (connectivity != 4 && connectivity != 8) {
1077  L_ERROR("connectivity must be 4 or 8\n", procName);
1078  return;
1079  }
1080 
1081  imax = h - 1;
1082  jmax = w - 1;
1083 
1084  /* In the worst case, most of the pixels could be pushed
1085  * onto the FIFO queue during anti-raster scan. However this
1086  * will rarely happen, and we initialize the queue ptr size to
1087  * the image perimeter. */
1088  lq_pixel = lqueueCreate(2 * (w + h));
1089 
1090  switch (connectivity)
1091  {
1092  case 4:
1093  /* UL --> LR scan (Raster Order)
1094  * If I : mask image
1095  * J : marker image
1096  * Let p be the currect pixel;
1097  * J(p) <- (max{J(p) union J(p) neighbors in raster order})
1098  * intersection I(p) */
1099  for (i = 0; i < h; i++) {
1100  lines = datas + i * wpls;
1101  linem = datam + i * wplm;
1102  for (j = 0; j < w; j++) {
1103  if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
1104  maxval = 0;
1105  if (i > 0)
1106  maxval = GET_DATA_BYTE(lines - wpls, j);
1107  if (j > 0) {
1108  val4 = GET_DATA_BYTE(lines, j - 1);
1109  maxval = L_MAX(maxval, val4);
1110  }
1111  val = GET_DATA_BYTE(lines, j);
1112  maxval = L_MAX(maxval, val);
1113  val = L_MIN(maxval, maskval);
1114  SET_DATA_BYTE(lines, j, val);
1115  }
1116  }
1117  }
1118 
1119  /* LR --> UL scan (anti-raster order)
1120  * Let p be the currect pixel;
1121  * J(p) <- (max{J(p) union J(p) neighbors in anti-raster order})
1122  * intersection I(p) */
1123  for (i = imax; i >= 0; i--) {
1124  lines = datas + i * wpls;
1125  linem = datam + i * wplm;
1126  for (j = jmax; j >= 0; j--) {
1127  boolval = FALSE;
1128  if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
1129  maxval = 0;
1130  if (i < imax)
1131  maxval = GET_DATA_BYTE(lines + wpls, j);
1132  if (j < jmax) {
1133  val5 = GET_DATA_BYTE(lines, j + 1);
1134  maxval = L_MAX(maxval, val5);
1135  }
1136  val = GET_DATA_BYTE(lines, j);
1137  maxval = L_MAX(maxval, val);
1138  val = L_MIN(maxval, maskval);
1139  SET_DATA_BYTE(lines, j, val);
1140 
1141  /*
1142  * If there exists a point (q) which belongs to J(p)
1143  * neighbors in anti-raster order such that J(q) < J(p)
1144  * and J(q) < I(q) then
1145  * fifo_add(p) */
1146  if (i < imax) {
1147  val7 = GET_DATA_BYTE(lines + wpls, j);
1148  if ((val7 < val) &&
1149  (val7 < GET_DATA_BYTE(linem + wplm, j))) {
1150  boolval = TRUE;
1151  }
1152  }
1153  if (j < jmax) {
1154  val5 = GET_DATA_BYTE(lines, j + 1);
1155  if (!boolval && (val5 < val) &&
1156  (val5 < GET_DATA_BYTE(linem, j + 1))) {
1157  boolval = TRUE;
1158  }
1159  }
1160  if (boolval) {
1161  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1162  pixel->x = i;
1163  pixel->y = j;
1164  lqueueAdd(lq_pixel, pixel);
1165  }
1166  }
1167  }
1168  }
1169 
1170  /* Propagation step:
1171  * while fifo_empty = false
1172  * p <- fifo_first()
1173  * for every pixel (q) belong to neighbors of (p)
1174  * if J(q) < J(p) and I(q) != J(q)
1175  * J(q) <- min(J(p), I(q));
1176  * fifo_add(q);
1177  * end
1178  * end
1179  * end */
1180  queue_size = lqueueGetCount(lq_pixel);
1181  while (queue_size) {
1182  pixel = (L_PIXEL *)lqueueRemove(lq_pixel);
1183  i = pixel->x;
1184  j = pixel->y;
1185  LEPT_FREE(pixel);
1186  lines = datas + i * wpls;
1187  linem = datam + i * wplm;
1188 
1189  if ((val = GET_DATA_BYTE(lines, j)) > 0) {
1190  if (i > 0) {
1191  val2 = GET_DATA_BYTE(lines - wpls, j);
1192  maskval = GET_DATA_BYTE(linem - wplm, j);
1193  if (val > val2 && val2 != maskval) {
1194  SET_DATA_BYTE(lines - wpls, j, L_MIN(val, maskval));
1195  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1196  pixel->x = i - 1;
1197  pixel->y = j;
1198  lqueueAdd(lq_pixel, pixel);
1199  }
1200 
1201  }
1202  if (j > 0) {
1203  val4 = GET_DATA_BYTE(lines, j - 1);
1204  maskval = GET_DATA_BYTE(linem, j - 1);
1205  if (val > val4 && val4 != maskval) {
1206  SET_DATA_BYTE(lines, j - 1, L_MIN(val, maskval));
1207  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1208  pixel->x = i;
1209  pixel->y = j - 1;
1210  lqueueAdd(lq_pixel, pixel);
1211  }
1212  }
1213  if (i < imax) {
1214  val7 = GET_DATA_BYTE(lines + wpls, j);
1215  maskval = GET_DATA_BYTE(linem + wplm, j);
1216  if (val > val7 && val7 != maskval) {
1217  SET_DATA_BYTE(lines + wpls, j, L_MIN(val, maskval));
1218  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1219  pixel->x = i + 1;
1220  pixel->y = j;
1221  lqueueAdd(lq_pixel, pixel);
1222  }
1223  }
1224  if (j < jmax) {
1225  val5 = GET_DATA_BYTE(lines, j + 1);
1226  maskval = GET_DATA_BYTE(linem, j + 1);
1227  if (val > val5 && val5 != maskval) {
1228  SET_DATA_BYTE(lines, j + 1, L_MIN(val, maskval));
1229  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1230  pixel->x = i;
1231  pixel->y = j + 1;
1232  lqueueAdd(lq_pixel, pixel);
1233  }
1234  }
1235  }
1236 
1237  queue_size = lqueueGetCount(lq_pixel);
1238  }
1239 
1240  break;
1241 
1242  case 8:
1243  /* UL --> LR scan (Raster Order)
1244  * If I : mask image
1245  * J : marker image
1246  * Let p be the currect pixel;
1247  * J(p) <- (max{J(p) union J(p) neighbors in raster order})
1248  * intersection I(p) */
1249  for (i = 0; i < h; i++) {
1250  lines = datas + i * wpls;
1251  linem = datam + i * wplm;
1252  for (j = 0; j < w; j++) {
1253  if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
1254  maxval = 0;
1255  if (i > 0) {
1256  if (j > 0)
1257  maxval = GET_DATA_BYTE(lines - wpls, j - 1);
1258  if (j < jmax) {
1259  val3 = GET_DATA_BYTE(lines - wpls, j + 1);
1260  maxval = L_MAX(maxval, val3);
1261  }
1262  val2 = GET_DATA_BYTE(lines - wpls, j);
1263  maxval = L_MAX(maxval, val2);
1264  }
1265  if (j > 0) {
1266  val4 = GET_DATA_BYTE(lines, j - 1);
1267  maxval = L_MAX(maxval, val4);
1268  }
1269  val = GET_DATA_BYTE(lines, j);
1270  maxval = L_MAX(maxval, val);
1271  val = L_MIN(maxval, maskval);
1272  SET_DATA_BYTE(lines, j, val);
1273  }
1274  }
1275  }
1276 
1277  /* LR --> UL scan (anti-raster order)
1278  * Let p be the currect pixel;
1279  * J(p) <- (max{J(p) union J(p) neighbors in anti-raster order})
1280  * intersection I(p) */
1281  for (i = imax; i >= 0; i--) {
1282  lines = datas + i * wpls;
1283  linem = datam + i * wplm;
1284  for (j = jmax; j >= 0; j--) {
1285  boolval = FALSE;
1286  if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
1287  maxval = 0;
1288  if (i < imax) {
1289  if (j > 0) {
1290  maxval = GET_DATA_BYTE(lines + wpls, j - 1);
1291  }
1292  if (j < jmax) {
1293  val8 = GET_DATA_BYTE(lines + wpls, j + 1);
1294  maxval = L_MAX(maxval, val8);
1295  }
1296  val7 = GET_DATA_BYTE(lines + wpls, j);
1297  maxval = L_MAX(maxval, val7);
1298  }
1299  if (j < jmax) {
1300  val5 = GET_DATA_BYTE(lines, j + 1);
1301  maxval = L_MAX(maxval, val5);
1302  }
1303  val = GET_DATA_BYTE(lines, j);
1304  maxval = L_MAX(maxval, val);
1305  val = L_MIN(maxval, maskval);
1306  SET_DATA_BYTE(lines, j, val);
1307 
1308  /* If there exists a point (q) which belongs to J(p)
1309  * neighbors in anti-raster order such that J(q) < J(p)
1310  * and J(q) < I(q) then
1311  * fifo_add(p) */
1312  if (i < imax) {
1313  if (j > 0) {
1314  val6 = GET_DATA_BYTE(lines + wpls, j - 1);
1315  if ((val6 < val) &&
1316  (val6 < GET_DATA_BYTE(linem + wplm, j - 1))) {
1317  boolval = TRUE;
1318  }
1319  }
1320  if (j < jmax) {
1321  val8 = GET_DATA_BYTE(lines + wpls, j + 1);
1322  if (!boolval && (val8 < val) &&
1323  (val8 < GET_DATA_BYTE(linem + wplm, j + 1))) {
1324  boolval = TRUE;
1325  }
1326  }
1327  val7 = GET_DATA_BYTE(lines + wpls, j);
1328  if (!boolval && (val7 < val) &&
1329  (val7 < GET_DATA_BYTE(linem + wplm, j))) {
1330  boolval = TRUE;
1331  }
1332  }
1333  if (j < jmax) {
1334  val5 = GET_DATA_BYTE(lines, j + 1);
1335  if (!boolval && (val5 < val) &&
1336  (val5 < GET_DATA_BYTE(linem, j + 1))) {
1337  boolval = TRUE;
1338  }
1339  }
1340  if (boolval) {
1341  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1342  pixel->x = i;
1343  pixel->y = j;
1344  lqueueAdd(lq_pixel, pixel);
1345  }
1346  }
1347  }
1348  }
1349 
1350  /* Propagation step:
1351  * while fifo_empty = false
1352  * p <- fifo_first()
1353  * for every pixel (q) belong to neighbors of (p)
1354  * if J(q) < J(p) and I(q) != J(q)
1355  * J(q) <- min(J(p), I(q));
1356  * fifo_add(q);
1357  * end
1358  * end
1359  * end */
1360  queue_size = lqueueGetCount(lq_pixel);
1361  while (queue_size) {
1362  pixel = (L_PIXEL *)lqueueRemove(lq_pixel);
1363  i = pixel->x;
1364  j = pixel->y;
1365  LEPT_FREE(pixel);
1366  lines = datas + i * wpls;
1367  linem = datam + i * wplm;
1368 
1369  if ((val = GET_DATA_BYTE(lines, j)) > 0) {
1370  if (i > 0) {
1371  if (j > 0) {
1372  val1 = GET_DATA_BYTE(lines - wpls, j - 1);
1373  maskval = GET_DATA_BYTE(linem - wplm, j - 1);
1374  if (val > val1 && val1 != maskval) {
1375  SET_DATA_BYTE(lines - wpls, j - 1,
1376  L_MIN(val, maskval));
1377  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1378  pixel->x = i - 1;
1379  pixel->y = j - 1;
1380  lqueueAdd(lq_pixel, pixel);
1381  }
1382  }
1383  if (j < jmax) {
1384  val3 = GET_DATA_BYTE(lines - wpls, j + 1);
1385  maskval = GET_DATA_BYTE(linem - wplm, j + 1);
1386  if (val > val3 && val3 != maskval) {
1387  SET_DATA_BYTE(lines - wpls, j + 1,
1388  L_MIN(val, maskval));
1389  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1390  pixel->x = i - 1;
1391  pixel->y = j + 1;
1392  lqueueAdd(lq_pixel, pixel);
1393  }
1394  }
1395  val2 = GET_DATA_BYTE(lines - wpls, j);
1396  maskval = GET_DATA_BYTE(linem - wplm, j);
1397  if (val > val2 && val2 != maskval) {
1398  SET_DATA_BYTE(lines - wpls, j, L_MIN(val, maskval));
1399  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1400  pixel->x = i - 1;
1401  pixel->y = j;
1402  lqueueAdd(lq_pixel, pixel);
1403  }
1404 
1405  }
1406  if (j > 0) {
1407  val4 = GET_DATA_BYTE(lines, j - 1);
1408  maskval = GET_DATA_BYTE(linem, j - 1);
1409  if (val > val4 && val4 != maskval) {
1410  SET_DATA_BYTE(lines, j - 1, L_MIN(val, maskval));
1411  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1412  pixel->x = i;
1413  pixel->y = j - 1;
1414  lqueueAdd(lq_pixel, pixel);
1415  }
1416  }
1417  if (i < imax) {
1418  if (j > 0) {
1419  val6 = GET_DATA_BYTE(lines + wpls, j - 1);
1420  maskval = GET_DATA_BYTE(linem + wplm, j - 1);
1421  if (val > val6 && val6 != maskval) {
1422  SET_DATA_BYTE(lines + wpls, j - 1,
1423  L_MIN(val, maskval));
1424  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1425  pixel->x = i + 1;
1426  pixel->y = j - 1;
1427  lqueueAdd(lq_pixel, pixel);
1428  }
1429  }
1430  if (j < jmax) {
1431  val8 = GET_DATA_BYTE(lines + wpls, j + 1);
1432  maskval = GET_DATA_BYTE(linem + wplm, j + 1);
1433  if (val > val8 && val8 != maskval) {
1434  SET_DATA_BYTE(lines + wpls, j + 1,
1435  L_MIN(val, maskval));
1436  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1437  pixel->x = i + 1;
1438  pixel->y = j + 1;
1439  lqueueAdd(lq_pixel, pixel);
1440  }
1441  }
1442  val7 = GET_DATA_BYTE(lines + wpls, j);
1443  maskval = GET_DATA_BYTE(linem + wplm, j);
1444  if (val > val7 && val7 != maskval) {
1445  SET_DATA_BYTE(lines + wpls, j, L_MIN(val, maskval));
1446  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1447  pixel->x = i + 1;
1448  pixel->y = j;
1449  lqueueAdd(lq_pixel, pixel);
1450  }
1451  }
1452  if (j < jmax) {
1453  val5 = GET_DATA_BYTE(lines, j + 1);
1454  maskval = GET_DATA_BYTE(linem, j + 1);
1455  if (val > val5 && val5 != maskval) {
1456  SET_DATA_BYTE(lines, j + 1, L_MIN(val, maskval));
1457  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1458  pixel->x = i;
1459  pixel->y = j + 1;
1460  lqueueAdd(lq_pixel, pixel);
1461  }
1462  }
1463  }
1464 
1465  queue_size = lqueueGetCount(lq_pixel);
1466  }
1467  break;
1468 
1469  default:
1470  L_ERROR("shouldn't get here!\n", procName);
1471  break;
1472  }
1473 
1474  lqueueDestroy(&lq_pixel, TRUE);
1475 }
1476 
1477 
1511 static void
1512 seedfillGrayInvLow(l_uint32 *datas,
1513  l_int32 w,
1514  l_int32 h,
1515  l_int32 wpls,
1516  l_uint32 *datam,
1517  l_int32 wplm,
1518  l_int32 connectivity)
1519 {
1520 l_uint8 val1, val2, val3, val4, val5, val6, val7, val8;
1521 l_uint8 val, maxval, maskval, boolval;
1522 l_int32 i, j, imax, jmax, queue_size;
1523 l_uint32 *lines, *linem;
1524 L_PIXEL *pixel;
1525 L_QUEUE *lq_pixel;
1526 
1527  PROCNAME("seedfillGrayInvLow");
1528 
1529  if (connectivity != 4 && connectivity != 8) {
1530  L_ERROR("connectivity must be 4 or 8\n", procName);
1531  return;
1532  }
1533 
1534  imax = h - 1;
1535  jmax = w - 1;
1536 
1537  /* In the worst case, most of the pixels could be pushed
1538  * onto the FIFO queue during anti-raster scan. However this
1539  * will rarely happen, and we initialize the queue ptr size to
1540  * the image perimeter. */
1541  lq_pixel = lqueueCreate(2 * (w + h));
1542 
1543  switch (connectivity)
1544  {
1545  case 4:
1546  /* UL --> LR scan (Raster Order)
1547  * If I : mask image
1548  * J : marker image
1549  * Let p be the currect pixel;
1550  * tmp <- max{J(p) union J(p) neighbors in raster order}
1551  * if (tmp > I(p))
1552  * J(p) <- tmp
1553  * end */
1554  for (i = 0; i < h; i++) {
1555  lines = datas + i * wpls;
1556  linem = datam + i * wplm;
1557  for (j = 0; j < w; j++) {
1558  if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
1559  maxval = GET_DATA_BYTE(lines, j);
1560  if (i > 0) {
1561  val2 = GET_DATA_BYTE(lines - wpls, j);
1562  maxval = L_MAX(maxval, val2);
1563  }
1564  if (j > 0) {
1565  val4 = GET_DATA_BYTE(lines, j - 1);
1566  maxval = L_MAX(maxval, val4);
1567  }
1568  if (maxval > maskval)
1569  SET_DATA_BYTE(lines, j, maxval);
1570  }
1571  }
1572  }
1573 
1574  /* LR --> UL scan (anti-raster order)
1575  * If I : mask image
1576  * J : marker image
1577  * Let p be the currect pixel;
1578  * tmp <- max{J(p) union J(p) neighbors in anti-raster order}
1579  * if (tmp > I(p))
1580  * J(p) <- tmp
1581  * end */
1582  for (i = imax; i >= 0; i--) {
1583  lines = datas + i * wpls;
1584  linem = datam + i * wplm;
1585  for (j = jmax; j >= 0; j--) {
1586  boolval = FALSE;
1587  if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
1588  val = maxval = GET_DATA_BYTE(lines, j);
1589  if (i < imax) {
1590  val7 = GET_DATA_BYTE(lines + wpls, j);
1591  maxval = L_MAX(maxval, val7);
1592  }
1593  if (j < jmax) {
1594  val5 = GET_DATA_BYTE(lines, j + 1);
1595  maxval = L_MAX(maxval, val5);
1596  }
1597  if (maxval > maskval)
1598  SET_DATA_BYTE(lines, j, maxval);
1599  val = GET_DATA_BYTE(lines, j);
1600 
1601  /*
1602  * If there exists a point (q) which belongs to J(p)
1603  * neighbors in anti-raster order such that J(q) < J(p)
1604  * and J(p) > I(q) then
1605  * fifo_add(p) */
1606  if (i < imax) {
1607  val7 = GET_DATA_BYTE(lines + wpls, j);
1608  if ((val7 < val) &&
1609  (val > GET_DATA_BYTE(linem + wplm, j))) {
1610  boolval = TRUE;
1611  }
1612  }
1613  if (j < jmax) {
1614  val5 = GET_DATA_BYTE(lines, j + 1);
1615  if (!boolval && (val5 < val) &&
1616  (val > GET_DATA_BYTE(linem, j + 1))) {
1617  boolval = TRUE;
1618  }
1619  }
1620  if (boolval) {
1621  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1622  pixel->x = i;
1623  pixel->y = j;
1624  lqueueAdd(lq_pixel, pixel);
1625  }
1626  }
1627  }
1628  }
1629 
1630  /* Propagation step:
1631  * while fifo_empty = false
1632  * p <- fifo_first()
1633  * for every pixel (q) belong to neighbors of (p)
1634  * if J(q) < J(p) and J(p) > I(q)
1635  * J(q) <- min(J(p), I(q));
1636  * fifo_add(q);
1637  * end
1638  * end
1639  * end */
1640  queue_size = lqueueGetCount(lq_pixel);
1641  while (queue_size) {
1642  pixel = (L_PIXEL *)lqueueRemove(lq_pixel);
1643  i = pixel->x;
1644  j = pixel->y;
1645  LEPT_FREE(pixel);
1646  lines = datas + i * wpls;
1647  linem = datam + i * wplm;
1648 
1649  if ((val = GET_DATA_BYTE(lines, j)) > 0) {
1650  if (i > 0) {
1651  val2 = GET_DATA_BYTE(lines - wpls, j);
1652  maskval = GET_DATA_BYTE(linem - wplm, j);
1653  if (val > val2 && val > maskval) {
1654  SET_DATA_BYTE(lines - wpls, j, val);
1655  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1656  pixel->x = i - 1;
1657  pixel->y = j;
1658  lqueueAdd(lq_pixel, pixel);
1659  }
1660 
1661  }
1662  if (j > 0) {
1663  val4 = GET_DATA_BYTE(lines, j - 1);
1664  maskval = GET_DATA_BYTE(linem, j - 1);
1665  if (val > val4 && val > maskval) {
1666  SET_DATA_BYTE(lines, j - 1, val);
1667  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1668  pixel->x = i;
1669  pixel->y = j - 1;
1670  lqueueAdd(lq_pixel, pixel);
1671  }
1672  }
1673  if (i < imax) {
1674  val7 = GET_DATA_BYTE(lines + wpls, j);
1675  maskval = GET_DATA_BYTE(linem + wplm, j);
1676  if (val > val7 && val > maskval) {
1677  SET_DATA_BYTE(lines + wpls, j, val);
1678  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1679  pixel->x = i + 1;
1680  pixel->y = j;
1681  lqueueAdd(lq_pixel, pixel);
1682  }
1683  }
1684  if (j < jmax) {
1685  val5 = GET_DATA_BYTE(lines, j + 1);
1686  maskval = GET_DATA_BYTE(linem, j + 1);
1687  if (val > val5 && val > maskval) {
1688  SET_DATA_BYTE(lines, j + 1, val);
1689  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1690  pixel->x = i;
1691  pixel->y = j + 1;
1692  lqueueAdd(lq_pixel, pixel);
1693  }
1694  }
1695  }
1696 
1697  queue_size = lqueueGetCount(lq_pixel);
1698  }
1699 
1700  break;
1701 
1702  case 8:
1703  /* UL --> LR scan (Raster Order)
1704  * If I : mask image
1705  * J : marker image
1706  * Let p be the currect pixel;
1707  * tmp <- max{J(p) union J(p) neighbors in raster order}
1708  * if (tmp > I(p))
1709  * J(p) <- tmp
1710  * end */
1711  for (i = 0; i < h; i++) {
1712  lines = datas + i * wpls;
1713  linem = datam + i * wplm;
1714  for (j = 0; j < w; j++) {
1715  if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
1716  maxval = GET_DATA_BYTE(lines, j);
1717  if (i > 0) {
1718  if (j > 0) {
1719  val1 = GET_DATA_BYTE(lines - wpls, j - 1);
1720  maxval = L_MAX(maxval, val1);
1721  }
1722  if (j < jmax) {
1723  val3 = GET_DATA_BYTE(lines - wpls, j + 1);
1724  maxval = L_MAX(maxval, val3);
1725  }
1726  val2 = GET_DATA_BYTE(lines - wpls, j);
1727  maxval = L_MAX(maxval, val2);
1728  }
1729  if (j > 0) {
1730  val4 = GET_DATA_BYTE(lines, j - 1);
1731  maxval = L_MAX(maxval, val4);
1732  }
1733  if (maxval > maskval)
1734  SET_DATA_BYTE(lines, j, maxval);
1735  }
1736  }
1737  }
1738 
1739  /* LR --> UL scan (anti-raster order)
1740  * If I : mask image
1741  * J : marker image
1742  * Let p be the currect pixel;
1743  * tmp <- max{J(p) union J(p) neighbors in anti-raster order}
1744  * if (tmp > I(p))
1745  * J(p) <- tmp
1746  * end */
1747  for (i = imax; i >= 0; i--) {
1748  lines = datas + i * wpls;
1749  linem = datam + i * wplm;
1750  for (j = jmax; j >= 0; j--) {
1751  boolval = FALSE;
1752  if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
1753  maxval = GET_DATA_BYTE(lines, j);
1754  if (i < imax) {
1755  if (j > 0) {
1756  val6 = GET_DATA_BYTE(lines + wpls, j - 1);
1757  maxval = L_MAX(maxval, val6);
1758  }
1759  if (j < jmax) {
1760  val8 = GET_DATA_BYTE(lines + wpls, j + 1);
1761  maxval = L_MAX(maxval, val8);
1762  }
1763  val7 = GET_DATA_BYTE(lines + wpls, j);
1764  maxval = L_MAX(maxval, val7);
1765  }
1766  if (j < jmax) {
1767  val5 = GET_DATA_BYTE(lines, j + 1);
1768  maxval = L_MAX(maxval, val5);
1769  }
1770  if (maxval > maskval)
1771  SET_DATA_BYTE(lines, j, maxval);
1772  val = GET_DATA_BYTE(lines, j);
1773 
1774  /*
1775  * If there exists a point (q) which belongs to J(p)
1776  * neighbors in anti-raster order such that J(q) < J(p)
1777  * and J(p) > I(q) then
1778  * fifo_add(p) */
1779  if (i < imax) {
1780  if (j > 0) {
1781  val6 = GET_DATA_BYTE(lines + wpls, j - 1);
1782  if ((val6 < val) &&
1783  (val > GET_DATA_BYTE(linem + wplm, j - 1))) {
1784  boolval = TRUE;
1785  }
1786  }
1787  if (j < jmax) {
1788  val8 = GET_DATA_BYTE(lines + wpls, j + 1);
1789  if (!boolval && (val8 < val) &&
1790  (val > GET_DATA_BYTE(linem + wplm, j + 1))) {
1791  boolval = TRUE;
1792  }
1793  }
1794  val7 = GET_DATA_BYTE(lines + wpls, j);
1795  if (!boolval && (val7 < val) &&
1796  (val > GET_DATA_BYTE(linem + wplm, j))) {
1797  boolval = TRUE;
1798  }
1799  }
1800  if (j < jmax) {
1801  val5 = GET_DATA_BYTE(lines, j + 1);
1802  if (!boolval && (val5 < val) &&
1803  (val > GET_DATA_BYTE(linem, j + 1))) {
1804  boolval = TRUE;
1805  }
1806  }
1807  if (boolval) {
1808  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1809  pixel->x = i;
1810  pixel->y = j;
1811  lqueueAdd(lq_pixel, pixel);
1812  }
1813  }
1814  }
1815  }
1816 
1817  /* Propagation step:
1818  * while fifo_empty = false
1819  * p <- fifo_first()
1820  * for every pixel (q) belong to neighbors of (p)
1821  * if J(q) < J(p) and J(p) > I(q)
1822  * J(q) <- min(J(p), I(q));
1823  * fifo_add(q);
1824  * end
1825  * end
1826  * end */
1827  queue_size = lqueueGetCount(lq_pixel);
1828  while (queue_size) {
1829  pixel = (L_PIXEL *)lqueueRemove(lq_pixel);
1830  i = pixel->x;
1831  j = pixel->y;
1832  LEPT_FREE(pixel);
1833  lines = datas + i * wpls;
1834  linem = datam + i * wplm;
1835 
1836  if ((val = GET_DATA_BYTE(lines, j)) > 0) {
1837  if (i > 0) {
1838  if (j > 0) {
1839  val1 = GET_DATA_BYTE(lines - wpls, j - 1);
1840  maskval = GET_DATA_BYTE(linem - wplm, j - 1);
1841  if (val > val1 && val > maskval) {
1842  SET_DATA_BYTE(lines - wpls, j - 1, val);
1843  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1844  pixel->x = i - 1;
1845  pixel->y = j - 1;
1846  lqueueAdd(lq_pixel, pixel);
1847  }
1848  }
1849  if (j < jmax) {
1850  val3 = GET_DATA_BYTE(lines - wpls, j + 1);
1851  maskval = GET_DATA_BYTE(linem - wplm, j + 1);
1852  if (val > val3 && val > maskval) {
1853  SET_DATA_BYTE(lines - wpls, j + 1, val);
1854  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1855  pixel->x = i - 1;
1856  pixel->y = j + 1;
1857  lqueueAdd(lq_pixel, pixel);
1858  }
1859  }
1860  val2 = GET_DATA_BYTE(lines - wpls, j);
1861  maskval = GET_DATA_BYTE(linem - wplm, j);
1862  if (val > val2 && val > maskval) {
1863  SET_DATA_BYTE(lines - wpls, j, val);
1864  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1865  pixel->x = i - 1;
1866  pixel->y = j;
1867  lqueueAdd(lq_pixel, pixel);
1868  }
1869 
1870  }
1871  if (j > 0) {
1872  val4 = GET_DATA_BYTE(lines, j - 1);
1873  maskval = GET_DATA_BYTE(linem, j - 1);
1874  if (val > val4 && val > maskval) {
1875  SET_DATA_BYTE(lines, j - 1, val);
1876  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1877  pixel->x = i;
1878  pixel->y = j - 1;
1879  lqueueAdd(lq_pixel, pixel);
1880  }
1881  }
1882  if (i < imax) {
1883  if (j > 0) {
1884  val6 = GET_DATA_BYTE(lines + wpls, j - 1);
1885  maskval = GET_DATA_BYTE(linem + wplm, j - 1);
1886  if (val > val6 && val > maskval) {
1887  SET_DATA_BYTE(lines + wpls, j - 1, val);
1888  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1889  pixel->x = i + 1;
1890  pixel->y = j - 1;
1891  lqueueAdd(lq_pixel, pixel);
1892  }
1893  }
1894  if (j < jmax) {
1895  val8 = GET_DATA_BYTE(lines + wpls, j + 1);
1896  maskval = GET_DATA_BYTE(linem + wplm, j + 1);
1897  if (val > val8 && val > maskval) {
1898  SET_DATA_BYTE(lines + wpls, j + 1, val);
1899  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1900  pixel->x = i + 1;
1901  pixel->y = j + 1;
1902  lqueueAdd(lq_pixel, pixel);
1903  }
1904  }
1905  val7 = GET_DATA_BYTE(lines + wpls, j);
1906  maskval = GET_DATA_BYTE(linem + wplm, j);
1907  if (val > val7 && val > maskval) {
1908  SET_DATA_BYTE(lines + wpls, j, val);
1909  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1910  pixel->x = i + 1;
1911  pixel->y = j;
1912  lqueueAdd(lq_pixel, pixel);
1913  }
1914  }
1915  if (j < jmax) {
1916  val5 = GET_DATA_BYTE(lines, j + 1);
1917  maskval = GET_DATA_BYTE(linem, j + 1);
1918  if (val > val5 && val > maskval) {
1919  SET_DATA_BYTE(lines, j + 1, val);
1920  pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1921  pixel->x = i;
1922  pixel->y = j + 1;
1923  lqueueAdd(lq_pixel, pixel);
1924  }
1925  }
1926  }
1927 
1928  queue_size = lqueueGetCount(lq_pixel);
1929  }
1930  break;
1931 
1932  default:
1933  L_ERROR("shouldn't get here!\n", procName);
1934  break;
1935  }
1936 
1937  lqueueDestroy(&lq_pixel, TRUE);
1938 }
1939 
1940 
1941 /*-----------------------------------------------------------------------*
1942  * Vincent's Iterative Grayscale Seedfill method *
1943  *-----------------------------------------------------------------------*/
1968 l_ok
1970  PIX *pixm,
1971  l_int32 connectivity)
1972 {
1973 l_int32 i, h, w, wpls, wplm, boolval;
1974 l_uint32 *datas, *datam;
1975 PIX *pixt;
1976 
1977  PROCNAME("pixSeedfillGraySimple");
1978 
1979  if (!pixs || pixGetDepth(pixs) != 8)
1980  return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
1981  if (!pixm || pixGetDepth(pixm) != 8)
1982  return ERROR_INT("pixm not defined or not 8 bpp", procName, 1);
1983  if (connectivity != 4 && connectivity != 8)
1984  return ERROR_INT("connectivity not in {4,8}", procName, 1);
1985 
1986  /* Make sure the sizes of seed and mask images are the same */
1987  if (pixSizesEqual(pixs, pixm) == 0)
1988  return ERROR_INT("pixs and pixm sizes differ", procName, 1);
1989 
1990  /* This is used to test for completion */
1991  if ((pixt = pixCreateTemplate(pixs)) == NULL)
1992  return ERROR_INT("pixt not made", procName, 1);
1993 
1994  datas = pixGetData(pixs);
1995  datam = pixGetData(pixm);
1996  wpls = pixGetWpl(pixs);
1997  wplm = pixGetWpl(pixm);
1998  pixGetDimensions(pixs, &w, &h, NULL);
1999  for (i = 0; i < MAX_ITERS; i++) {
2000  pixCopy(pixt, pixs);
2001  seedfillGrayLowSimple(datas, w, h, wpls, datam, wplm, connectivity);
2002  pixEqual(pixs, pixt, &boolval);
2003  if (boolval == 1) {
2004 #if DEBUG_PRINT_ITERS
2005  L_INFO("Gray seed fill converged: %d iters\n", procName, i + 1);
2006 #endif /* DEBUG_PRINT_ITERS */
2007  break;
2008  }
2009  }
2010 
2011  pixDestroy(&pixt);
2012  return 0;
2013 }
2014 
2015 
2039 l_ok
2041  PIX *pixm,
2042  l_int32 connectivity)
2043 {
2044 l_int32 i, h, w, wpls, wplm, boolval;
2045 l_uint32 *datas, *datam;
2046 PIX *pixt;
2047 
2048  PROCNAME("pixSeedfillGrayInvSimple");
2049 
2050  if (!pixs || pixGetDepth(pixs) != 8)
2051  return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
2052  if (!pixm || pixGetDepth(pixm) != 8)
2053  return ERROR_INT("pixm not defined or not 8 bpp", procName, 1);
2054  if (connectivity != 4 && connectivity != 8)
2055  return ERROR_INT("connectivity not in {4,8}", procName, 1);
2056 
2057  /* Make sure the sizes of seed and mask images are the same */
2058  if (pixSizesEqual(pixs, pixm) == 0)
2059  return ERROR_INT("pixs and pixm sizes differ", procName, 1);
2060 
2061  /* This is used to test for completion */
2062  if ((pixt = pixCreateTemplate(pixs)) == NULL)
2063  return ERROR_INT("pixt not made", procName, 1);
2064 
2065  datas = pixGetData(pixs);
2066  datam = pixGetData(pixm);
2067  wpls = pixGetWpl(pixs);
2068  wplm = pixGetWpl(pixm);
2069  pixGetDimensions(pixs, &w, &h, NULL);
2070  for (i = 0; i < MAX_ITERS; i++) {
2071  pixCopy(pixt, pixs);
2072  seedfillGrayInvLowSimple(datas, w, h, wpls, datam, wplm, connectivity);
2073  pixEqual(pixs, pixt, &boolval);
2074  if (boolval == 1) {
2075 #if DEBUG_PRINT_ITERS
2076  L_INFO("Gray seed fill converged: %d iters\n", procName, i + 1);
2077 #endif /* DEBUG_PRINT_ITERS */
2078  break;
2079  }
2080  }
2081 
2082  pixDestroy(&pixt);
2083  return 0;
2084 }
2085 
2086 
2120 static void
2121 seedfillGrayLowSimple(l_uint32 *datas,
2122  l_int32 w,
2123  l_int32 h,
2124  l_int32 wpls,
2125  l_uint32 *datam,
2126  l_int32 wplm,
2127  l_int32 connectivity)
2128 {
2129 l_uint8 val2, val3, val4, val5, val7, val8;
2130 l_uint8 val, maxval, maskval;
2131 l_int32 i, j, imax, jmax;
2132 l_uint32 *lines, *linem;
2133 
2134  PROCNAME("seedfillGrayLowSimple");
2135 
2136  imax = h - 1;
2137  jmax = w - 1;
2138 
2139  switch (connectivity)
2140  {
2141  case 4:
2142  /* UL --> LR scan */
2143  for (i = 0; i < h; i++) {
2144  lines = datas + i * wpls;
2145  linem = datam + i * wplm;
2146  for (j = 0; j < w; j++) {
2147  if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
2148  maxval = 0;
2149  if (i > 0)
2150  maxval = GET_DATA_BYTE(lines - wpls, j);
2151  if (j > 0) {
2152  val4 = GET_DATA_BYTE(lines, j - 1);
2153  maxval = L_MAX(maxval, val4);
2154  }
2155  val = GET_DATA_BYTE(lines, j);
2156  maxval = L_MAX(maxval, val);
2157  val = L_MIN(maxval, maskval);
2158  SET_DATA_BYTE(lines, j, val);
2159  }
2160  }
2161  }
2162 
2163  /* LR --> UL scan */
2164  for (i = imax; i >= 0; i--) {
2165  lines = datas + i * wpls;
2166  linem = datam + i * wplm;
2167  for (j = jmax; j >= 0; j--) {
2168  if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
2169  maxval = 0;
2170  if (i < imax)
2171  maxval = GET_DATA_BYTE(lines + wpls, j);
2172  if (j < jmax) {
2173  val5 = GET_DATA_BYTE(lines, j + 1);
2174  maxval = L_MAX(maxval, val5);
2175  }
2176  val = GET_DATA_BYTE(lines, j);
2177  maxval = L_MAX(maxval, val);
2178  val = L_MIN(maxval, maskval);
2179  SET_DATA_BYTE(lines, j, val);
2180  }
2181  }
2182  }
2183  break;
2184 
2185  case 8:
2186  /* UL --> LR scan */
2187  for (i = 0; i < h; i++) {
2188  lines = datas + i * wpls;
2189  linem = datam + i * wplm;
2190  for (j = 0; j < w; j++) {
2191  if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
2192  maxval = 0;
2193  if (i > 0) {
2194  if (j > 0)
2195  maxval = GET_DATA_BYTE(lines - wpls, j - 1);
2196  if (j < jmax) {
2197  val2 = GET_DATA_BYTE(lines - wpls, j + 1);
2198  maxval = L_MAX(maxval, val2);
2199  }
2200  val3 = GET_DATA_BYTE(lines - wpls, j);
2201  maxval = L_MAX(maxval, val3);
2202  }
2203  if (j > 0) {
2204  val4 = GET_DATA_BYTE(lines, j - 1);
2205  maxval = L_MAX(maxval, val4);
2206  }
2207  val = GET_DATA_BYTE(lines, j);
2208  maxval = L_MAX(maxval, val);
2209  val = L_MIN(maxval, maskval);
2210  SET_DATA_BYTE(lines, j, val);
2211  }
2212  }
2213  }
2214 
2215  /* LR --> UL scan */
2216  for (i = imax; i >= 0; i--) {
2217  lines = datas + i * wpls;
2218  linem = datam + i * wplm;
2219  for (j = jmax; j >= 0; j--) {
2220  if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
2221  maxval = 0;
2222  if (i < imax) {
2223  if (j > 0)
2224  maxval = GET_DATA_BYTE(lines + wpls, j - 1);
2225  if (j < jmax) {
2226  val8 = GET_DATA_BYTE(lines + wpls, j + 1);
2227  maxval = L_MAX(maxval, val8);
2228  }
2229  val7 = GET_DATA_BYTE(lines + wpls, j);
2230  maxval = L_MAX(maxval, val7);
2231  }
2232  if (j < jmax) {
2233  val5 = GET_DATA_BYTE(lines, j + 1);
2234  maxval = L_MAX(maxval, val5);
2235  }
2236  val = GET_DATA_BYTE(lines, j);
2237  maxval = L_MAX(maxval, val);
2238  val = L_MIN(maxval, maskval);
2239  SET_DATA_BYTE(lines, j, val);
2240  }
2241  }
2242  }
2243  break;
2244 
2245  default:
2246  L_ERROR("connectivity must be 4 or 8\n", procName);
2247  }
2248 }
2249 
2250 
2276 static void
2278  l_int32 w,
2279  l_int32 h,
2280  l_int32 wpls,
2281  l_uint32 *datam,
2282  l_int32 wplm,
2283  l_int32 connectivity)
2284 {
2285 l_uint8 val1, val2, val3, val4, val5, val6, val7, val8;
2286 l_uint8 maxval, maskval;
2287 l_int32 i, j, imax, jmax;
2288 l_uint32 *lines, *linem;
2289 
2290  PROCNAME("seedfillGrayInvLowSimple");
2291 
2292  imax = h - 1;
2293  jmax = w - 1;
2294 
2295  switch (connectivity)
2296  {
2297  case 4:
2298  /* UL --> LR scan */
2299  for (i = 0; i < h; i++) {
2300  lines = datas + i * wpls;
2301  linem = datam + i * wplm;
2302  for (j = 0; j < w; j++) {
2303  if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
2304  maxval = GET_DATA_BYTE(lines, j);
2305  if (i > 0) {
2306  val2 = GET_DATA_BYTE(lines - wpls, j);
2307  maxval = L_MAX(maxval, val2);
2308  }
2309  if (j > 0) {
2310  val4 = GET_DATA_BYTE(lines, j - 1);
2311  maxval = L_MAX(maxval, val4);
2312  }
2313  if (maxval > maskval)
2314  SET_DATA_BYTE(lines, j, maxval);
2315  }
2316  }
2317  }
2318 
2319  /* LR --> UL scan */
2320  for (i = imax; i >= 0; i--) {
2321  lines = datas + i * wpls;
2322  linem = datam + i * wplm;
2323  for (j = jmax; j >= 0; j--) {
2324  if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
2325  maxval = GET_DATA_BYTE(lines, j);
2326  if (i < imax) {
2327  val7 = GET_DATA_BYTE(lines + wpls, j);
2328  maxval = L_MAX(maxval, val7);
2329  }
2330  if (j < jmax) {
2331  val5 = GET_DATA_BYTE(lines, j + 1);
2332  maxval = L_MAX(maxval, val5);
2333  }
2334  if (maxval > maskval)
2335  SET_DATA_BYTE(lines, j, maxval);
2336  }
2337  }
2338  }
2339  break;
2340 
2341  case 8:
2342  /* UL --> LR scan */
2343  for (i = 0; i < h; i++) {
2344  lines = datas + i * wpls;
2345  linem = datam + i * wplm;
2346  for (j = 0; j < w; j++) {
2347  if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
2348  maxval = GET_DATA_BYTE(lines, j);
2349  if (i > 0) {
2350  if (j > 0) {
2351  val1 = GET_DATA_BYTE(lines - wpls, j - 1);
2352  maxval = L_MAX(maxval, val1);
2353  }
2354  if (j < jmax) {
2355  val2 = GET_DATA_BYTE(lines - wpls, j + 1);
2356  maxval = L_MAX(maxval, val2);
2357  }
2358  val3 = GET_DATA_BYTE(lines - wpls, j);
2359  maxval = L_MAX(maxval, val3);
2360  }
2361  if (j > 0) {
2362  val4 = GET_DATA_BYTE(lines, j - 1);
2363  maxval = L_MAX(maxval, val4);
2364  }
2365  if (maxval > maskval)
2366  SET_DATA_BYTE(lines, j, maxval);
2367  }
2368  }
2369  }
2370 
2371  /* LR --> UL scan */
2372  for (i = imax; i >= 0; i--) {
2373  lines = datas + i * wpls;
2374  linem = datam + i * wplm;
2375  for (j = jmax; j >= 0; j--) {
2376  if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
2377  maxval = GET_DATA_BYTE(lines, j);
2378  if (i < imax) {
2379  if (j > 0) {
2380  val6 = GET_DATA_BYTE(lines + wpls, j - 1);
2381  maxval = L_MAX(maxval, val6);
2382  }
2383  if (j < jmax) {
2384  val8 = GET_DATA_BYTE(lines + wpls, j + 1);
2385  maxval = L_MAX(maxval, val8);
2386  }
2387  val7 = GET_DATA_BYTE(lines + wpls, j);
2388  maxval = L_MAX(maxval, val7);
2389  }
2390  if (j < jmax) {
2391  val5 = GET_DATA_BYTE(lines, j + 1);
2392  maxval = L_MAX(maxval, val5);
2393  }
2394  if (maxval > maskval)
2395  SET_DATA_BYTE(lines, j, maxval);
2396  }
2397  }
2398  }
2399  break;
2400 
2401  default:
2402  L_ERROR("connectivity must be 4 or 8\n", procName);
2403  }
2404 }
2405 
2406 
2407 /*-----------------------------------------------------------------------*
2408  * Gray seedfill variations *
2409  *-----------------------------------------------------------------------*/
2441 PIX *
2443  PIX *pixm,
2444  l_int32 delta,
2445  l_int32 connectivity)
2446 {
2447 PIX *pixbi, *pixmi, *pixsd;
2448 
2449  PROCNAME("pixSeedfillGrayBasin");
2450 
2451  if (!pixb || pixGetDepth(pixb) != 1)
2452  return (PIX *)ERROR_PTR("pixb undefined or not 1 bpp", procName, NULL);
2453  if (!pixm || pixGetDepth(pixm) != 8)
2454  return (PIX *)ERROR_PTR("pixm undefined or not 8 bpp", procName, NULL);
2455  if (connectivity != 4 && connectivity != 8)
2456  return (PIX *)ERROR_PTR("connectivity not in {4,8}", procName, NULL);
2457 
2458  if (delta <= 0) {
2459  L_WARNING("delta <= 0; returning a copy of pixm\n", procName);
2460  return pixCopy(NULL, pixm);
2461  }
2462 
2463  /* Add delta to every pixel in pixm */
2464  pixsd = pixCopy(NULL, pixm);
2465  pixAddConstantGray(pixsd, delta);
2466 
2467  /* Prepare the seed. Write 255 in all pixels of
2468  * ([pixm] + delta) where pixb is 0. */
2469  pixbi = pixInvert(NULL, pixb);
2470  pixSetMasked(pixsd, pixbi, 255);
2471 
2472  /* Fill the inverse seed, using the inverse clipping mask */
2473  pixmi = pixInvert(NULL, pixm);
2474  pixInvert(pixsd, pixsd);
2475  pixSeedfillGray(pixsd, pixmi, connectivity);
2476 
2477  /* Re-invert the filled seed */
2478  pixInvert(pixsd, pixsd);
2479 
2480  pixDestroy(&pixbi);
2481  pixDestroy(&pixmi);
2482  return pixsd;
2483 }
2484 
2485 
2486 /*-----------------------------------------------------------------------*
2487  * Vincent's Distance Function method *
2488  *-----------------------------------------------------------------------*/
2532 PIX *
2534  l_int32 connectivity,
2535  l_int32 outdepth,
2536  l_int32 boundcond)
2537 {
2538 l_int32 w, h, wpld;
2539 l_uint32 *datad;
2540 PIX *pixd;
2541 
2542  PROCNAME("pixDistanceFunction");
2543 
2544  if (!pixs || pixGetDepth(pixs) != 1)
2545  return (PIX *)ERROR_PTR("!pixs or pixs not 1 bpp", procName, NULL);
2546  if (connectivity != 4 && connectivity != 8)
2547  return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
2548  if (outdepth != 8 && outdepth != 16)
2549  return (PIX *)ERROR_PTR("outdepth not 8 or 16 bpp", procName, NULL);
2550  if (boundcond != L_BOUNDARY_BG && boundcond != L_BOUNDARY_FG)
2551  return (PIX *)ERROR_PTR("invalid boundcond", procName, NULL);
2552 
2553  pixGetDimensions(pixs, &w, &h, NULL);
2554  if ((pixd = pixCreate(w, h, outdepth)) == NULL)
2555  return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
2556  datad = pixGetData(pixd);
2557  wpld = pixGetWpl(pixd);
2558 
2559  /* Initialize the fg pixels to 1 and the bg pixels to 0 */
2560  pixSetMasked(pixd, pixs, 1);
2561 
2562  if (boundcond == L_BOUNDARY_BG) {
2563  distanceFunctionLow(datad, w, h, outdepth, wpld, connectivity);
2564  } else { /* L_BOUNDARY_FG: set boundary pixels to max val */
2565  pixRasterop(pixd, 0, 0, w, 1, PIX_SET, NULL, 0, 0); /* top */
2566  pixRasterop(pixd, 0, h - 1, w, 1, PIX_SET, NULL, 0, 0); /* bot */
2567  pixRasterop(pixd, 0, 0, 1, h, PIX_SET, NULL, 0, 0); /* left */
2568  pixRasterop(pixd, w - 1, 0, 1, h, PIX_SET, NULL, 0, 0); /* right */
2569 
2570  distanceFunctionLow(datad, w, h, outdepth, wpld, connectivity);
2571 
2572  /* Set each boundary pixel equal to the pixel next to it */
2573  pixSetMirroredBorder(pixd, 1, 1, 1, 1);
2574  }
2575 
2576  return pixd;
2577 }
2578 
2579 
2583 static void
2584 distanceFunctionLow(l_uint32 *datad,
2585  l_int32 w,
2586  l_int32 h,
2587  l_int32 d,
2588  l_int32 wpld,
2589  l_int32 connectivity)
2590 {
2591 l_int32 val1, val2, val3, val4, val5, val6, val7, val8, minval, val;
2592 l_int32 i, j, imax, jmax;
2593 l_uint32 *lined;
2594 
2595  PROCNAME("distanceFunctionLow");
2596 
2597  /* One raster scan followed by one anti-raster scan.
2598  * This does not re-set the 1-boundary of pixels that
2599  * were initialized to either 0 or maxval. */
2600  imax = h - 1;
2601  jmax = w - 1;
2602  switch (connectivity)
2603  {
2604  case 4:
2605  if (d == 8) {
2606  /* UL --> LR scan */
2607  for (i = 1; i < imax; i++) {
2608  lined = datad + i * wpld;
2609  for (j = 1; j < jmax; j++) {
2610  if ((val = GET_DATA_BYTE(lined, j)) > 0) {
2611  val2 = GET_DATA_BYTE(lined - wpld, j);
2612  val4 = GET_DATA_BYTE(lined, j - 1);
2613  minval = L_MIN(val2, val4);
2614  minval = L_MIN(minval, 254);
2615  SET_DATA_BYTE(lined, j, minval + 1);
2616  }
2617  }
2618  }
2619 
2620  /* LR --> UL scan */
2621  for (i = imax - 1; i > 0; i--) {
2622  lined = datad + i * wpld;
2623  for (j = jmax - 1; j > 0; j--) {
2624  if ((val = GET_DATA_BYTE(lined, j)) > 0) {
2625  val7 = GET_DATA_BYTE(lined + wpld, j);
2626  val5 = GET_DATA_BYTE(lined, j + 1);
2627  minval = L_MIN(val5, val7);
2628  minval = L_MIN(minval + 1, val);
2629  SET_DATA_BYTE(lined, j, minval);
2630  }
2631  }
2632  }
2633  } else { /* d == 16 */
2634  /* UL --> LR scan */
2635  for (i = 1; i < imax; i++) {
2636  lined = datad + i * wpld;
2637  for (j = 1; j < jmax; j++) {
2638  if ((val = GET_DATA_TWO_BYTES(lined, j)) > 0) {
2639  val2 = GET_DATA_TWO_BYTES(lined - wpld, j);
2640  val4 = GET_DATA_TWO_BYTES(lined, j - 1);
2641  minval = L_MIN(val2, val4);
2642  minval = L_MIN(minval, 0xfffe);
2643  SET_DATA_TWO_BYTES(lined, j, minval + 1);
2644  }
2645  }
2646  }
2647 
2648  /* LR --> UL scan */
2649  for (i = imax - 1; i > 0; i--) {
2650  lined = datad + i * wpld;
2651  for (j = jmax - 1; j > 0; j--) {
2652  if ((val = GET_DATA_TWO_BYTES(lined, j)) > 0) {
2653  val7 = GET_DATA_TWO_BYTES(lined + wpld, j);
2654  val5 = GET_DATA_TWO_BYTES(lined, j + 1);
2655  minval = L_MIN(val5, val7);
2656  minval = L_MIN(minval + 1, val);
2657  SET_DATA_TWO_BYTES(lined, j, minval);
2658  }
2659  }
2660  }
2661  }
2662  break;
2663 
2664  case 8:
2665  if (d == 8) {
2666  /* UL --> LR scan */
2667  for (i = 1; i < imax; i++) {
2668  lined = datad + i * wpld;
2669  for (j = 1; j < jmax; j++) {
2670  if ((val = GET_DATA_BYTE(lined, j)) > 0) {
2671  val1 = GET_DATA_BYTE(lined - wpld, j - 1);
2672  val2 = GET_DATA_BYTE(lined - wpld, j);
2673  val3 = GET_DATA_BYTE(lined - wpld, j + 1);
2674  val4 = GET_DATA_BYTE(lined, j - 1);
2675  minval = L_MIN(val1, val2);
2676  minval = L_MIN(minval, val3);
2677  minval = L_MIN(minval, val4);
2678  minval = L_MIN(minval, 254);
2679  SET_DATA_BYTE(lined, j, minval + 1);
2680  }
2681  }
2682  }
2683 
2684  /* LR --> UL scan */
2685  for (i = imax - 1; i > 0; i--) {
2686  lined = datad + i * wpld;
2687  for (j = jmax - 1; j > 0; j--) {
2688  if ((val = GET_DATA_BYTE(lined, j)) > 0) {
2689  val8 = GET_DATA_BYTE(lined + wpld, j + 1);
2690  val7 = GET_DATA_BYTE(lined + wpld, j);
2691  val6 = GET_DATA_BYTE(lined + wpld, j - 1);
2692  val5 = GET_DATA_BYTE(lined, j + 1);
2693  minval = L_MIN(val8, val7);
2694  minval = L_MIN(minval, val6);
2695  minval = L_MIN(minval, val5);
2696  minval = L_MIN(minval + 1, val);
2697  SET_DATA_BYTE(lined, j, minval);
2698  }
2699  }
2700  }
2701  } else { /* d == 16 */
2702  /* UL --> LR scan */
2703  for (i = 1; i < imax; i++) {
2704  lined = datad + i * wpld;
2705  for (j = 1; j < jmax; j++) {
2706  if ((val = GET_DATA_TWO_BYTES(lined, j)) > 0) {
2707  val1 = GET_DATA_TWO_BYTES(lined - wpld, j - 1);
2708  val2 = GET_DATA_TWO_BYTES(lined - wpld, j);
2709  val3 = GET_DATA_TWO_BYTES(lined - wpld, j + 1);
2710  val4 = GET_DATA_TWO_BYTES(lined, j - 1);
2711  minval = L_MIN(val1, val2);
2712  minval = L_MIN(minval, val3);
2713  minval = L_MIN(minval, val4);
2714  minval = L_MIN(minval, 0xfffe);
2715  SET_DATA_TWO_BYTES(lined, j, minval + 1);
2716  }
2717  }
2718  }
2719 
2720  /* LR --> UL scan */
2721  for (i = imax - 1; i > 0; i--) {
2722  lined = datad + i * wpld;
2723  for (j = jmax - 1; j > 0; j--) {
2724  if ((val = GET_DATA_TWO_BYTES(lined, j)) > 0) {
2725  val8 = GET_DATA_TWO_BYTES(lined + wpld, j + 1);
2726  val7 = GET_DATA_TWO_BYTES(lined + wpld, j);
2727  val6 = GET_DATA_TWO_BYTES(lined + wpld, j - 1);
2728  val5 = GET_DATA_TWO_BYTES(lined, j + 1);
2729  minval = L_MIN(val8, val7);
2730  minval = L_MIN(minval, val6);
2731  minval = L_MIN(minval, val5);
2732  minval = L_MIN(minval + 1, val);
2733  SET_DATA_TWO_BYTES(lined, j, minval);
2734  }
2735  }
2736  }
2737  }
2738  break;
2739 
2740  default:
2741  L_ERROR("connectivity must be 4 or 8\n", procName);
2742  break;
2743  }
2744 }
2745 
2746 
2747 /*-----------------------------------------------------------------------*
2748  * Seed spread (based on distance function) *
2749  *-----------------------------------------------------------------------*/
2790 PIX *
2792  l_int32 connectivity)
2793 {
2794 l_int32 w, h, wplt, wplg;
2795 l_uint32 *datat, *datag;
2796 PIX *pixm, *pixt, *pixg, *pixd;
2797 
2798  PROCNAME("pixSeedspread");
2799 
2800  if (!pixs || pixGetDepth(pixs) != 8)
2801  return (PIX *)ERROR_PTR("!pixs or pixs not 8 bpp", procName, NULL);
2802  if (connectivity != 4 && connectivity != 8)
2803  return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
2804 
2805  /* Add a 4 byte border to pixs. This simplifies the computation. */
2806  pixg = pixAddBorder(pixs, 4, 0);
2807  pixGetDimensions(pixg, &w, &h, NULL);
2808 
2809  /* Initialize distance function pixt. Threshold pixs to get
2810  * a 0 at the seed points where the pixs pixel is nonzero, and
2811  * a 1 at all points that need to be filled. Use this as a
2812  * mask to set a 1 in pixt at all non-seed points. Also, set all
2813  * pixt pixels in an interior boundary of width 1 to the
2814  * maximum value. For debugging, to view the distance function,
2815  * use pixConvert16To8(pixt, 0) on small images. */
2816  pixm = pixThresholdToBinary(pixg, 1);
2817  pixt = pixCreate(w, h, 16);
2818  pixSetMasked(pixt, pixm, 1);
2819  pixRasterop(pixt, 0, 0, w, 1, PIX_SET, NULL, 0, 0); /* top */
2820  pixRasterop(pixt, 0, h - 1, w, 1, PIX_SET, NULL, 0, 0); /* bot */
2821  pixRasterop(pixt, 0, 0, 1, h, PIX_SET, NULL, 0, 0); /* left */
2822  pixRasterop(pixt, w - 1, 0, 1, h, PIX_SET, NULL, 0, 0); /* right */
2823  datat = pixGetData(pixt);
2824  wplt = pixGetWpl(pixt);
2825 
2826  /* Do the interpolation and remove the border. */
2827  datag = pixGetData(pixg);
2828  wplg = pixGetWpl(pixg);
2829  seedspreadLow(datag, w, h, wplg, datat, wplt, connectivity);
2830  pixd = pixRemoveBorder(pixg, 4);
2831 
2832  pixDestroy(&pixm);
2833  pixDestroy(&pixg);
2834  pixDestroy(&pixt);
2835  return pixd;
2836 }
2837 
2838 
2844 static void
2845 seedspreadLow(l_uint32 *datad,
2846  l_int32 w,
2847  l_int32 h,
2848  l_int32 wpld,
2849  l_uint32 *datat,
2850  l_int32 wplt,
2851  l_int32 connectivity)
2852 {
2853 l_int32 val1t, val2t, val3t, val4t, val5t, val6t, val7t, val8t;
2854 l_int32 i, j, imax, jmax, minval, valt, vald;
2855 l_uint32 *linet, *lined;
2856 
2857  PROCNAME("seedspreadLow");
2858 
2859  /* One raster scan followed by one anti-raster scan.
2860  * pixt is initialized to have 0 on pixels where the
2861  * input is specified in pixd, and to have 1 on all
2862  * other pixels. We only change pixels in pixt and pixd
2863  * that are non-zero in pixt. */
2864  imax = h - 1;
2865  jmax = w - 1;
2866  switch (connectivity)
2867  {
2868  case 4:
2869  /* UL --> LR scan */
2870  for (i = 1; i < h; i++) {
2871  linet = datat + i * wplt;
2872  lined = datad + i * wpld;
2873  for (j = 1; j < jmax; j++) {
2874  if ((valt = GET_DATA_TWO_BYTES(linet, j)) > 0) {
2875  val2t = GET_DATA_TWO_BYTES(linet - wplt, j);
2876  val4t = GET_DATA_TWO_BYTES(linet, j - 1);
2877  minval = L_MIN(val2t, val4t);
2878  minval = L_MIN(minval, 0xfffe);
2879  SET_DATA_TWO_BYTES(linet, j, minval + 1);
2880  if (val2t < val4t)
2881  vald = GET_DATA_BYTE(lined - wpld, j);
2882  else
2883  vald = GET_DATA_BYTE(lined, j - 1);
2884  SET_DATA_BYTE(lined, j, vald);
2885  }
2886  }
2887  }
2888 
2889  /* LR --> UL scan */
2890  for (i = imax - 1; i > 0; i--) {
2891  linet = datat + i * wplt;
2892  lined = datad + i * wpld;
2893  for (j = jmax - 1; j > 0; j--) {
2894  if ((valt = GET_DATA_TWO_BYTES(linet, j)) > 0) {
2895  val7t = GET_DATA_TWO_BYTES(linet + wplt, j);
2896  val5t = GET_DATA_TWO_BYTES(linet, j + 1);
2897  minval = L_MIN(val5t, val7t);
2898  minval = L_MIN(minval + 1, valt);
2899  if (valt > minval) { /* replace */
2900  SET_DATA_TWO_BYTES(linet, j, minval);
2901  if (val5t < val7t)
2902  vald = GET_DATA_BYTE(lined, j + 1);
2903  else
2904  vald = GET_DATA_BYTE(lined + wplt, j);
2905  SET_DATA_BYTE(lined, j, vald);
2906  }
2907  }
2908  }
2909  }
2910  break;
2911  case 8:
2912  /* UL --> LR scan */
2913  for (i = 1; i < h; i++) {
2914  linet = datat + i * wplt;
2915  lined = datad + i * wpld;
2916  for (j = 1; j < jmax; j++) {
2917  if ((valt = GET_DATA_TWO_BYTES(linet, j)) > 0) {
2918  val1t = GET_DATA_TWO_BYTES(linet - wplt, j - 1);
2919  val2t = GET_DATA_TWO_BYTES(linet - wplt, j);
2920  val3t = GET_DATA_TWO_BYTES(linet - wplt, j + 1);
2921  val4t = GET_DATA_TWO_BYTES(linet, j - 1);
2922  minval = L_MIN(val1t, val2t);
2923  minval = L_MIN(minval, val3t);
2924  minval = L_MIN(minval, val4t);
2925  minval = L_MIN(minval, 0xfffe);
2926  SET_DATA_TWO_BYTES(linet, j, minval + 1);
2927  if (minval == val1t)
2928  vald = GET_DATA_BYTE(lined - wpld, j - 1);
2929  else if (minval == val2t)
2930  vald = GET_DATA_BYTE(lined - wpld, j);
2931  else if (minval == val3t)
2932  vald = GET_DATA_BYTE(lined - wpld, j + 1);
2933  else /* minval == val4t */
2934  vald = GET_DATA_BYTE(lined, j - 1);
2935  SET_DATA_BYTE(lined, j, vald);
2936  }
2937  }
2938  }
2939 
2940  /* LR --> UL scan */
2941  for (i = imax - 1; i > 0; i--) {
2942  linet = datat + i * wplt;
2943  lined = datad + i * wpld;
2944  for (j = jmax - 1; j > 0; j--) {
2945  if ((valt = GET_DATA_TWO_BYTES(linet, j)) > 0) {
2946  val8t = GET_DATA_TWO_BYTES(linet + wplt, j + 1);
2947  val7t = GET_DATA_TWO_BYTES(linet + wplt, j);
2948  val6t = GET_DATA_TWO_BYTES(linet + wplt, j - 1);
2949  val5t = GET_DATA_TWO_BYTES(linet, j + 1);
2950  minval = L_MIN(val8t, val7t);
2951  minval = L_MIN(minval, val6t);
2952  minval = L_MIN(minval, val5t);
2953  minval = L_MIN(minval + 1, valt);
2954  if (valt > minval) { /* replace */
2955  SET_DATA_TWO_BYTES(linet, j, minval);
2956  if (minval == val5t + 1)
2957  vald = GET_DATA_BYTE(lined, j + 1);
2958  else if (minval == val6t + 1)
2959  vald = GET_DATA_BYTE(lined + wpld, j - 1);
2960  else if (minval == val7t + 1)
2961  vald = GET_DATA_BYTE(lined + wpld, j);
2962  else /* minval == val8t + 1 */
2963  vald = GET_DATA_BYTE(lined + wpld, j + 1);
2964  SET_DATA_BYTE(lined, j, vald);
2965  }
2966  }
2967  }
2968  }
2969  break;
2970  default:
2971  L_ERROR("connectivity must be 4 or 8\n", procName);
2972  break;
2973  }
2974 }
2975 
2976 
2977 /*-----------------------------------------------------------------------*
2978  * Local extrema *
2979  *-----------------------------------------------------------------------*/
3017 l_ok
3019  l_int32 maxmin,
3020  l_int32 minmax,
3021  PIX **ppixmin,
3022  PIX **ppixmax)
3023 {
3024 PIX *pixmin, *pixmax, *pixt1, *pixt2;
3025 
3026  PROCNAME("pixLocalExtrema");
3027 
3028  if (!pixs || pixGetDepth(pixs) != 8)
3029  return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
3030  if (!ppixmin && !ppixmax)
3031  return ERROR_INT("neither &pixmin, &pixmax are defined", procName, 1);
3032  if (maxmin <= 0) maxmin = 254;
3033  if (minmax <= 0) minmax = 1;
3034 
3035  if (ppixmin) {
3036  pixt1 = pixErodeGray(pixs, 3, 3);
3037  pixmin = pixFindEqualValues(pixs, pixt1);
3038  pixDestroy(&pixt1);
3039  pixQualifyLocalMinima(pixs, pixmin, maxmin);
3040  *ppixmin = pixmin;
3041  }
3042 
3043  if (ppixmax) {
3044  pixt1 = pixInvert(NULL, pixs);
3045  pixt2 = pixErodeGray(pixt1, 3, 3);
3046  pixmax = pixFindEqualValues(pixt1, pixt2);
3047  pixDestroy(&pixt2);
3048  pixQualifyLocalMinima(pixt1, pixmax, 255 - minmax);
3049  *ppixmax = pixmax;
3050  pixDestroy(&pixt1);
3051  }
3052 
3053  return 0;
3054 }
3055 
3056 
3081 static l_int32
3083  PIX *pixm,
3084  l_int32 maxval)
3085 {
3086 l_int32 n, i, j, k, x, y, w, h, xc, yc, wc, hc, xon, yon;
3087 l_int32 vals, wpls, wplc, ismin;
3088 l_uint32 val;
3089 l_uint32 *datas, *datac, *lines, *linec;
3090 BOXA *boxa;
3091 PIX *pix1, *pix2, *pix3;
3092 PIXA *pixa;
3093 
3094  PROCNAME("pixQualifyLocalMinima");
3095 
3096  if (!pixs || pixGetDepth(pixs) != 8)
3097  return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
3098  if (!pixm || pixGetDepth(pixm) != 1)
3099  return ERROR_INT("pixm not defined or not 1 bpp", procName, 1);
3100  if (maxval <= 0) maxval = 254;
3101 
3102  pixGetDimensions(pixs, &w, &h, NULL);
3103  datas = pixGetData(pixs);
3104  wpls = pixGetWpl(pixs);
3105  boxa = pixConnComp(pixm, &pixa, 8);
3106  n = pixaGetCount(pixa);
3107  for (k = 0; k < n; k++) {
3108  boxaGetBoxGeometry(boxa, k, &xc, &yc, &wc, &hc);
3109  pix1 = pixaGetPix(pixa, k, L_COPY);
3110  pix2 = pixAddBorder(pix1, 1, 0);
3111  pix3 = pixDilateBrick(NULL, pix2, 3, 3);
3112  pixXor(pix3, pix3, pix2); /* exterior boundary pixels */
3113  datac = pixGetData(pix3);
3114  wplc = pixGetWpl(pix3);
3115  nextOnPixelInRaster(pix1, 0, 0, &xon, &yon);
3116  pixGetPixel(pixs, xc + xon, yc + yon, &val);
3117  if (val > maxval) { /* too large; erase */
3118  pixRasterop(pixm, xc, yc, wc, hc, PIX_XOR, pix1, 0, 0);
3119  pixDestroy(&pix1);
3120  pixDestroy(&pix2);
3121  pixDestroy(&pix3);
3122  continue;
3123  }
3124  ismin = TRUE;
3125 
3126  /* Check all values in pixs that correspond to the exterior
3127  * boundary pixels of the c.c. in pixm. Verify that the
3128  * value in the c.c. is always less. */
3129  for (i = 0, y = yc - 1; i < hc + 2 && y >= 0 && y < h; i++, y++) {
3130  lines = datas + y * wpls;
3131  linec = datac + i * wplc;
3132  for (j = 0, x = xc - 1; j < wc + 2 && x >= 0 && x < w; j++, x++) {
3133  if (GET_DATA_BIT(linec, j)) {
3134  vals = GET_DATA_BYTE(lines, x);
3135  if (vals <= val) { /* not a minimum! */
3136  ismin = FALSE;
3137  break;
3138  }
3139  }
3140  }
3141  if (!ismin)
3142  break;
3143  }
3144  if (!ismin) /* erase it */
3145  pixRasterop(pixm, xc, yc, wc, hc, PIX_XOR, pix1, 0, 0);
3146  pixDestroy(&pix1);
3147  pixDestroy(&pix2);
3148  pixDestroy(&pix3);
3149  }
3150 
3151  boxaDestroy(&boxa);
3152  pixaDestroy(&pixa);
3153  return 0;
3154 }
3155 
3156 
3189 l_ok
3191  l_int32 mindist,
3192  PIX **ppixmin,
3193  PIX **ppixmax)
3194 {
3195 PIX *pixmin, *pixmax, *pixt, *pixtmin, *pixtmax;
3196 
3197  PROCNAME("pixSelectedLocalExtrema");
3198 
3199  if (!pixs || pixGetDepth(pixs) != 8)
3200  return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
3201  if (!ppixmin || !ppixmax)
3202  return ERROR_INT("&pixmin and &pixmax not both defined", procName, 1);
3203 
3204  pixt = pixErodeGray(pixs, 3, 3);
3205  pixmin = pixFindEqualValues(pixs, pixt);
3206  pixDestroy(&pixt);
3207  pixt = pixDilateGray(pixs, 3, 3);
3208  pixmax = pixFindEqualValues(pixs, pixt);
3209  pixDestroy(&pixt);
3210 
3211  /* Remove all points that are within the prescribed distance
3212  * from each other. */
3213  if (mindist < 0) { /* remove no points */
3214  *ppixmin = pixmin;
3215  *ppixmax = pixmax;
3216  } else if (mindist == 0) { /* remove points belonging to both sets */
3217  pixt = pixAnd(NULL, pixmin, pixmax);
3218  *ppixmin = pixSubtract(pixmin, pixmin, pixt);
3219  *ppixmax = pixSubtract(pixmax, pixmax, pixt);
3220  pixDestroy(&pixt);
3221  } else {
3222  pixtmin = pixDilateBrick(NULL, pixmin,
3223  2 * mindist + 1, 2 * mindist + 1);
3224  pixtmax = pixDilateBrick(NULL, pixmax,
3225  2 * mindist + 1, 2 * mindist + 1);
3226  *ppixmin = pixSubtract(pixmin, pixmin, pixtmax);
3227  *ppixmax = pixSubtract(pixmax, pixmax, pixtmin);
3228  pixDestroy(&pixtmin);
3229  pixDestroy(&pixtmax);
3230  }
3231  return 0;
3232 }
3233 
3234 
3249 PIX *
3251  PIX *pixs2)
3252 {
3253 l_int32 w1, h1, w2, h2, w, h;
3254 l_int32 i, j, val1, val2, wpls1, wpls2, wpld;
3255 l_uint32 *datas1, *datas2, *datad, *lines1, *lines2, *lined;
3256 PIX *pixd;
3257 
3258  PROCNAME("pixFindEqualValues");
3259 
3260  if (!pixs1 || pixGetDepth(pixs1) != 8)
3261  return (PIX *)ERROR_PTR("pixs1 undefined or not 8 bpp", procName, NULL);
3262  if (!pixs2 || pixGetDepth(pixs2) != 8)
3263  return (PIX *)ERROR_PTR("pixs2 undefined or not 8 bpp", procName, NULL);
3264  pixGetDimensions(pixs1, &w1, &h1, NULL);
3265  pixGetDimensions(pixs2, &w2, &h2, NULL);
3266  w = L_MIN(w1, w2);
3267  h = L_MIN(h1, h2);
3268  pixd = pixCreate(w, h, 1);
3269  datas1 = pixGetData(pixs1);
3270  datas2 = pixGetData(pixs2);
3271  datad = pixGetData(pixd);
3272  wpls1 = pixGetWpl(pixs1);
3273  wpls2 = pixGetWpl(pixs2);
3274  wpld = pixGetWpl(pixd);
3275 
3276  for (i = 0; i < h; i++) {
3277  lines1 = datas1 + i * wpls1;
3278  lines2 = datas2 + i * wpls2;
3279  lined = datad + i * wpld;
3280  for (j = 0; j < w; j++) {
3281  val1 = GET_DATA_BYTE(lines1, j);
3282  val2 = GET_DATA_BYTE(lines2, j);
3283  if (val1 == val2)
3284  SET_DATA_BIT(lined, j);
3285  }
3286  }
3287 
3288  return pixd;
3289 }
3290 
3291 
3292 /*-----------------------------------------------------------------------*
3293  * Selection of minima in mask connected components *
3294  *-----------------------------------------------------------------------*/
3316 l_ok
3318  PIX *pixm,
3319  PTA **ppta,
3320  NUMA **pnav)
3321 {
3322 l_int32 bx, by, bw, bh, i, j, c, n;
3323 l_int32 xs, ys, minx, miny, wpls, wplt, val, minval;
3324 l_uint32 *datas, *datat, *lines, *linet;
3325 BOXA *boxa;
3326 NUMA *nav;
3327 PIX *pixt, *pixs2, *pixm2;
3328 PIXA *pixa;
3329 PTA *pta;
3330 
3331  PROCNAME("pixSelectMinInConnComp");
3332 
3333  if (!ppta)
3334  return ERROR_INT("&pta not defined", procName, 1);
3335  *ppta = NULL;
3336  if (pnav) *pnav = NULL;
3337  if (!pixs || pixGetDepth(pixs) != 8)
3338  return ERROR_INT("pixs undefined or not 8 bpp", procName, 1);
3339  if (!pixm || pixGetDepth(pixm) != 1)
3340  return ERROR_INT("pixm undefined or not 1 bpp", procName, 1);
3341 
3342  /* Crop to the min size if necessary */
3343  if (pixCropToMatch(pixs, pixm, &pixs2, &pixm2)) {
3344  pixDestroy(&pixs2);
3345  pixDestroy(&pixm2);
3346  return ERROR_INT("cropping failure", procName, 1);
3347  }
3348 
3349  /* Find value and location of min value pixel in each component */
3350  boxa = pixConnComp(pixm2, &pixa, 8);
3351  n = boxaGetCount(boxa);
3352  pta = ptaCreate(n);
3353  *ppta = pta;
3354  nav = numaCreate(n);
3355  datas = pixGetData(pixs2);
3356  wpls = pixGetWpl(pixs2);
3357  for (c = 0; c < n; c++) {
3358  pixt = pixaGetPix(pixa, c, L_CLONE);
3359  boxaGetBoxGeometry(boxa, c, &bx, &by, &bw, &bh);
3360  if (bw == 1 && bh == 1) {
3361  ptaAddPt(pta, bx, by);
3362  numaAddNumber(nav, GET_DATA_BYTE(datas + by * wpls, bx));
3363  pixDestroy(&pixt);
3364  continue;
3365  }
3366  datat = pixGetData(pixt);
3367  wplt = pixGetWpl(pixt);
3368  minx = miny = 1000000;
3369  minval = 256;
3370  for (i = 0; i < bh; i++) {
3371  ys = by + i;
3372  lines = datas + ys * wpls;
3373  linet = datat + i * wplt;
3374  for (j = 0; j < bw; j++) {
3375  xs = bx + j;
3376  if (GET_DATA_BIT(linet, j)) {
3377  val = GET_DATA_BYTE(lines, xs);
3378  if (val < minval) {
3379  minval = val;
3380  minx = xs;
3381  miny = ys;
3382  }
3383  }
3384  }
3385  }
3386  ptaAddPt(pta, minx, miny);
3387  numaAddNumber(nav, GET_DATA_BYTE(datas + miny * wpls, minx));
3388  pixDestroy(&pixt);
3389  }
3390 
3391  boxaDestroy(&boxa);
3392  pixaDestroy(&pixa);
3393  if (pnav)
3394  *pnav = nav;
3395  else
3396  numaDestroy(&nav);
3397  pixDestroy(&pixs2);
3398  pixDestroy(&pixm2);
3399  return 0;
3400 }
3401 
3402 
3403 /*-----------------------------------------------------------------------*
3404  * Removal of seeded connected components from a mask *
3405  *-----------------------------------------------------------------------*/
3429 PIX *
3431  PIX *pixs,
3432  PIX *pixm,
3433  l_int32 connectivity,
3434  l_int32 bordersize)
3435 {
3436 PIX *pixt;
3437 
3438  PROCNAME("pixRemoveSeededComponents");
3439 
3440  if (!pixs || pixGetDepth(pixs) != 1)
3441  return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, pixd);
3442  if (!pixm || pixGetDepth(pixm) != 1)
3443  return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", procName, pixd);
3444  if (pixd && pixd != pixm)
3445  return (PIX *)ERROR_PTR("operation not inplace", procName, pixd);
3446 
3447  pixt = pixCopy(NULL, pixs);
3448  pixSeedfillBinary(pixt, pixt, pixm, connectivity);
3449  pixd = pixXor(pixd, pixm, pixt);
3450  if (bordersize > 0)
3451  pixSetOrClearBorder(pixd, bordersize, bordersize, bordersize,
3452  bordersize, PIX_CLR);
3453  pixDestroy(&pixt);
3454  return pixd;
3455 }
static void seedspreadLow(l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpld, l_uint32 *datat, l_int32 wplt, l_int32 connectivity)
seedspreadLow()
Definition: seedfill.c:2845
PIX * pixHolesByFilling(PIX *pixs, l_int32 connectivity)
pixHolesByFilling()
Definition: seedfill.c:605
PIX * pixDilateGray(PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateGray()
Definition: graymorph.c:274
#define PIX_CLR
Definition: pix.h:330
l_ok pixSetMasked(PIX *pixd, PIX *pixm, l_uint32 val)
pixSetMasked()
Definition: pix3.c:155
PIX * pixSeedspread(PIX *pixs, l_int32 connectivity)
pixSeedspread()
Definition: seedfill.c:2791
PIX * pixRemoveSeededComponents(PIX *pixd, PIX *pixs, PIX *pixm, l_int32 connectivity, l_int32 bordersize)
pixRemoveSeededComponents()
Definition: seedfill.c:3430
l_ok ptaAddPt(PTA *pta, l_float32 x, l_float32 y)
ptaAddPt()
Definition: ptabasic.c:342
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:473
l_int32 nextOnPixelInRaster(PIX *pixs, l_int32 xstart, l_int32 ystart, l_int32 *px, l_int32 *py)
nextOnPixelInRaster()
Definition: conncomp.c:453
L_QUEUE * lqueueCreate(l_int32 nalloc)
lqueueCreate()
Definition: queue.c:90
PIX * pixExtractBorderConnComps(PIX *pixs, l_int32 connectivity)
pixExtractBorderConnComps()
Definition: seedfill.c:694
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
PTA * ptaCreate(l_int32 n)
ptaCreate()
Definition: ptabasic.c:116
PIX * pixDilateBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateBrick()
Definition: morph.c:684
l_ok pixLocalExtrema(PIX *pixs, l_int32 maxmin, l_int32 minmax, PIX **ppixmin, PIX **ppixmax)
pixLocalExtrema()
Definition: seedfill.c:3018
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:302
l_ok pixSetAll(PIX *pix)
pixSetAll()
Definition: pix2.c:741
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1395
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:187
void boxaDestroy(BOXA **pboxa)
boxaDestroy()
Definition: boxbasic.c:580
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1624
PIX * pixThresholdToBinary(PIX *pixs, l_int32 thresh)
pixThresholdToBinary()
Definition: grayquant.c:443
#define GET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:123
PIX * pixCreateTemplate(PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:367
Definition: pix.h:492
static void seedfillGrayLowSimple(l_uint32 *datas, l_int32 w, l_int32 h, l_int32 wpls, l_uint32 *datam, l_int32 wplm, l_int32 connectivity)
seedfillGrayLowSimple()
Definition: seedfill.c:2121
l_ok pixSetOrClearBorder(PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot, l_int32 op)
pixSetOrClearBorder()
Definition: pix2.c:1439
PIX * pixAddBorder(PIX *pixs, l_int32 npix, l_uint32 val)
pixAddBorder()
Definition: pix2.c:1748
void * lqueueRemove(L_QUEUE *lq)
lqueueRemove()
Definition: queue.c:254
BOXA * pixConnComp(PIX *pixs, PIXA **ppixa, l_int32 connectivity)
pixConnComp()
Definition: conncomp.c:147
l_ok pixSelectedLocalExtrema(PIX *pixs, l_int32 mindist, PIX **ppixmin, PIX **ppixmax)
pixSelectedLocalExtrema()
Definition: seedfill.c:3190
PIX * pixRemoveBorder(PIX *pixs, l_int32 npix)
pixRemoveBorder()
Definition: pix2.c:1897
Definition: array.h:59
PIX * pixXor(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixXor()
Definition: pix3.c:1574
static void seedfillGrayInvLow(l_uint32 *datas, l_int32 w, l_int32 h, l_int32 wpls, l_uint32 *datam, l_int32 wplm, l_int32 connectivity)
seedfillGrayInvLow()
Definition: seedfill.c:1512
#define PIX_SET
Definition: pix.h:331
PIX * pixAnd(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixAnd()
Definition: pix3.c:1510
l_ok boxaGetBoxGeometry(BOXA *boxa, l_int32 index, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxaGetBoxGeometry()
Definition: boxbasic.c:863
l_ok lqueueAdd(L_QUEUE *lq, void *item)
lqueueAdd()
Definition: queue.c:187
static void distanceFunctionLow(l_uint32 *datad, l_int32 w, l_int32 h, l_int32 d, l_int32 wpld, l_int32 connectivity)
distanceFunctionLow()
Definition: seedfill.c:2584
Definition: queue.h:64
l_ok pixCountPixels(PIX *pixs, l_int32 *pcount, l_int32 *tab8)
pixCountPixels()
Definition: pix3.c:1823
l_int32 * makePixelSumTab8(void)
makePixelSumTab8()
Definition: pix3.c:2297
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
l_ok pixSetPadBits(PIX *pix, l_int32 val)
pixSetPadBits()
Definition: pix2.c:1307
l_ok pixSeedfillGrayInv(PIX *pixs, PIX *pixm, l_int32 connectivity)
pixSeedfillGrayInv()
Definition: seedfill.c:982
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
static l_int32 pixQualifyLocalMinima(PIX *pixs, PIX *pixm, l_int32 maxval)
pixQualifyLocalMinima()
Definition: seedfill.c:3082
PIX * pixRemoveBorderConnComps(PIX *pixs, l_int32 connectivity)
pixRemoveBorderConnComps()
Definition: seedfill.c:733
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
PIX * pixSubtract(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixSubtract()
Definition: pix3.c:1639
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:543
l_int32 lqueueGetCount(L_QUEUE *lq)
lqueueGetCount()
Definition: queue.c:283
PIX * pixFillClosedBorders(PIX *pixs, l_int32 connectivity)
pixFillClosedBorders()
Definition: seedfill.c:656
static void seedfillGrayLow(l_uint32 *datas, l_int32 w, l_int32 h, l_int32 wpls, l_uint32 *datam, l_int32 wplm, l_int32 connectivity)
seedfillGrayLow()
Definition: seedfill.c:1059
PIX * pixSeedfillBinaryRestricted(PIX *pixd, PIX *pixs, PIX *pixm, l_int32 connectivity, l_int32 xmax, l_int32 ymax)
pixSeedfillBinaryRestricted()
Definition: seedfill.c:330
Definition: pix.h:454
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:360
l_ok pixGetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 *pval)
pixGetPixel()
Definition: pix2.c:180
PIX * pixSeedfillBinary(PIX *pixd, PIX *pixs, PIX *pixm, l_int32 connectivity)
pixSeedfillBinary()
Definition: seedfill.c:243
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1065
PIX * pixOr(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixOr()
Definition: pix3.c:1446
l_ok pixSetMirroredBorder(PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot)
pixSetMirroredBorder()
Definition: pix2.c:1643
l_ok pixSeedfillGray(PIX *pixs, PIX *pixm, l_int32 connectivity)
pixSeedfillGray()
Definition: seedfill.c:923
#define GET_DATA_TWO_BYTES(pdata, n)
Definition: arrayaccess.h:212
PIX * pixDistanceFunction(PIX *pixs, l_int32 connectivity, l_int32 outdepth, l_int32 boundcond)
pixDistanceFunction()
Definition: seedfill.c:2533
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:672
Definition: pix.h:718
l_ok pixSeedfillGraySimple(PIX *pixs, PIX *pixm, l_int32 connectivity)
pixSeedfillGraySimple()
Definition: seedfill.c:1969
PIX * pixFillBgFromBorder(PIX *pixs, l_int32 connectivity)
pixFillBgFromBorder()
Definition: seedfill.c:783
#define PIX_NOT(op)
Definition: pix.h:329
void lqueueDestroy(L_QUEUE **plq, l_int32 freeflag)
lqueueDestroy()
Definition: queue.c:131
l_ok pixSeedfillGrayInvSimple(PIX *pixs, PIX *pixm, l_int32 connectivity)
pixSeedfillGrayInvSimple()
Definition: seedfill.c:2040
l_ok pixCropToMatch(PIX *pixs1, PIX *pixs2, PIX **ppixd1, PIX **ppixd2)
pixCropToMatch()
Definition: pix5.c:1155
Definition: pix.h:134
l_ok pixSelectMinInConnComp(PIX *pixs, PIX *pixm, PTA **ppta, NUMA **pnav)
pixSelectMinInConnComp()
Definition: seedfill.c:3317
Definition: pix.h:719
#define PIX_SRC
Definition: pix.h:327
PIX * pixCopy(PIX *pixd, PIX *pixs)
pixCopy()
Definition: pix1.c:628
static void seedfillGrayInvLowSimple(l_uint32 *datas, l_int32 w, l_int32 h, l_int32 wpls, l_uint32 *datam, l_int32 wplm, l_int32 connectivity)
seedfillGrayInvLowSimple()
Definition: seedfill.c:2277
l_ok pixEqual(PIX *pix1, PIX *pix2, l_int32 *psame)
pixEqual()
Definition: compare.c:150
l_int32 boxaGetCount(BOXA *boxa)
boxaGetCount()
Definition: boxbasic.c:718
PIX * pixFillHolesToBoundingRect(PIX *pixs, l_int32 minsize, l_float32 maxhfract, l_float32 minfgfract)
pixFillHolesToBoundingRect()
Definition: seedfill.c:842
static void seedfillBinaryLow(l_uint32 *datas, l_int32 hs, l_int32 wpls, l_uint32 *datam, l_int32 hm, l_int32 wplm, l_int32 connectivity)
seedfillBinaryLow()
Definition: seedfill.c:398
#define PIX_XOR
Definition: pix.h:337
l_int32 pixSizesEqual(const PIX *pix1, const PIX *pix2)
pixSizesEqual()
Definition: pix1.c:781
PIX * pixFindEqualValues(PIX *pixs1, PIX *pixs2)
pixFindEqualValues()
Definition: seedfill.c:3250
#define SET_DATA_TWO_BYTES(pdata, n, val)
Definition: arrayaccess.h:222
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:408
l_int32 pixaGetCount(PIXA *pixa)
pixaGetCount()
Definition: pixabasic.c:631
PIX * pixSeedfillGrayBasin(PIX *pixb, PIX *pixm, l_int32 delta, l_int32 connectivity)
pixSeedfillGrayBasin()
Definition: seedfill.c:2442
#define SET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:127
PIX * pixErodeGray(PIX *pixs, l_int32 hsize, l_int32 vsize)
pixErodeGray()
Definition: graymorph.c:158
Definition: pix.h:517
#define PIX_DST
Definition: pix.h:328
l_ok pixAddConstantGray(PIX *pixs, l_int32 val)
pixAddConstantGray()
Definition: pixarith.c:115