Leptonica  1.77.0
Image processing and image analysis suite
watershed.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 
115 #include "allheaders.h"
116 
117 #ifndef NO_CONSOLE_IO
118 #define DEBUG_WATERSHED 0
119 #endif /* ~NO_CONSOLE_IO */
120 
121 static const l_uint32 MAX_LABEL_VALUE = 0x7fffffff; /* largest l_int32 */
122 
125 {
126  l_int32 x;
127  l_int32 y;
128 };
129 typedef struct L_NewPixel L_NEWPIXEL;
130 
132 struct L_WSPixel
133 {
134  l_float32 val;
135  l_int32 x;
136  l_int32 y;
137  l_int32 index;
138 };
139 typedef struct L_WSPixel L_WSPIXEL;
140 
141 
142  /* Static functions for obtaining bitmap of watersheds */
143 static void wshedSaveBasin(L_WSHED *wshed, l_int32 index, l_int32 level);
144 
145 static l_int32 identifyWatershedBasin(L_WSHED *wshed,
146  l_int32 index, l_int32 level,
147  BOX **pbox, PIX **ppixd);
148 
149  /* Static function for merging lut and backlink arrays */
150 static l_int32 mergeLookup(L_WSHED *wshed, l_int32 sindex, l_int32 dindex);
151 
152  /* Static function for finding the height of the current pixel
153  above its seed or minima in the watershed. */
154 static l_int32 wshedGetHeight(L_WSHED *wshed, l_int32 val, l_int32 label,
155  l_int32 *pheight);
156 
157  /* Static accessors for NewPixel on a queue */
158 static void pushNewPixel(L_QUEUE *lq, l_int32 x, l_int32 y,
159  l_int32 *pminx, l_int32 *pmaxx,
160  l_int32 *pminy, l_int32 *pmaxy);
161 static void popNewPixel(L_QUEUE *lq, l_int32 *px, l_int32 *py);
162 
163  /* Static accessors for WSPixel on a heap */
164 static void pushWSPixel(L_HEAP *lh, L_STACK *stack, l_int32 val,
165  l_int32 x, l_int32 y, l_int32 index);
166 static void popWSPixel(L_HEAP *lh, L_STACK *stack, l_int32 *pval,
167  l_int32 *px, l_int32 *py, l_int32 *pindex);
168 
169  /* Static debug print output */
170 static void debugPrintLUT(l_int32 *lut, l_int32 size, l_int32 debug);
171 
172 static void debugWshedMerge(L_WSHED *wshed, char *descr, l_int32 x,
173  l_int32 y, l_int32 label, l_int32 index);
174 
175 
176 /*-----------------------------------------------------------------------*
177  * Top-level watershed *
178  *-----------------------------------------------------------------------*/
202 L_WSHED *
204  PIX *pixm,
205  l_int32 mindepth,
206  l_int32 debugflag)
207 {
208 l_int32 w, h;
209 L_WSHED *wshed;
210 
211  PROCNAME("wshedCreate");
212 
213  if (!pixs)
214  return (L_WSHED *)ERROR_PTR("pixs is not defined", procName, NULL);
215  if (pixGetDepth(pixs) != 8)
216  return (L_WSHED *)ERROR_PTR("pixs is not 8 bpp", procName, NULL);
217  if (!pixm)
218  return (L_WSHED *)ERROR_PTR("pixm is not defined", procName, NULL);
219  if (pixGetDepth(pixm) != 1)
220  return (L_WSHED *)ERROR_PTR("pixm is not 1 bpp", procName, NULL);
221  pixGetDimensions(pixs, &w, &h, NULL);
222  if (pixGetWidth(pixm) != w || pixGetHeight(pixm) != h)
223  return (L_WSHED *)ERROR_PTR("pixs/m sizes are unequal", procName, NULL);
224 
225  if ((wshed = (L_WSHED *)LEPT_CALLOC(1, sizeof(L_WSHED))) == NULL)
226  return (L_WSHED *)ERROR_PTR("wshed not made", procName, NULL);
227 
228  wshed->pixs = pixClone(pixs);
229  wshed->pixm = pixClone(pixm);
230  wshed->mindepth = L_MAX(1, mindepth);
231  wshed->pixlab = pixCreate(w, h, 32);
232  pixSetAllArbitrary(wshed->pixlab, MAX_LABEL_VALUE);
233  wshed->pixt = pixCreate(w, h, 1);
234  wshed->lines8 = pixGetLinePtrs(pixs, NULL);
235  wshed->linem1 = pixGetLinePtrs(pixm, NULL);
236  wshed->linelab32 = pixGetLinePtrs(wshed->pixlab, NULL);
237  wshed->linet1 = pixGetLinePtrs(wshed->pixt, NULL);
238  wshed->debug = debugflag;
239  return wshed;
240 }
241 
242 
249 void
251 {
252 l_int32 i;
253 L_WSHED *wshed;
254 
255  PROCNAME("wshedDestroy");
256 
257  if (pwshed == NULL) {
258  L_WARNING("ptr address is null!\n", procName);
259  return;
260  }
261 
262  if ((wshed = *pwshed) == NULL)
263  return;
264 
265  pixDestroy(&wshed->pixs);
266  pixDestroy(&wshed->pixm);
267  pixDestroy(&wshed->pixlab);
268  pixDestroy(&wshed->pixt);
269  if (wshed->lines8) LEPT_FREE(wshed->lines8);
270  if (wshed->linem1) LEPT_FREE(wshed->linem1);
271  if (wshed->linelab32) LEPT_FREE(wshed->linelab32);
272  if (wshed->linet1) LEPT_FREE(wshed->linet1);
273  pixaDestroy(&wshed->pixad);
274  ptaDestroy(&wshed->ptas);
275  numaDestroy(&wshed->nash);
276  numaDestroy(&wshed->nasi);
277  numaDestroy(&wshed->namh);
278  numaDestroy(&wshed->nalevels);
279  if (wshed->lut)
280  LEPT_FREE(wshed->lut);
281  if (wshed->links) {
282  for (i = 0; i < wshed->arraysize; i++)
283  numaDestroy(&wshed->links[i]);
284  LEPT_FREE(wshed->links);
285  }
286  LEPT_FREE(wshed);
287  *pwshed = NULL;
288  return;
289 }
290 
291 
304 l_ok
306 {
307 char two_new_watersheds[] = "Two new watersheds";
308 char seed_absorbed_into_seeded_basin[] = "Seed absorbed into seeded basin";
309 char one_new_watershed_label[] = "One new watershed (label)";
310 char one_new_watershed_index[] = "One new watershed (index)";
311 char minima_absorbed_into_seeded_basin[] =
312  "Minima absorbed into seeded basin";
313 char minima_absorbed_by_filler_or_another[] =
314  "Minima absorbed by filler or another";
315 l_int32 nseeds, nother, nboth, arraysize;
316 l_int32 i, j, val, x, y, w, h, index, mindepth;
317 l_int32 imin, imax, jmin, jmax, cindex, clabel, nindex;
318 l_int32 hindex, hlabel, hmin, hmax, minhindex, maxhindex;
319 l_int32 *lut;
320 l_uint32 ulabel, uval;
321 void **lines8, **linelab32;
322 NUMA *nalut, *nalevels, *nash, *namh, *nasi;
323 NUMA **links;
324 L_HEAP *lh;
325 PIX *pixmin, *pixsd;
326 PIXA *pixad;
327 L_STACK *rstack;
328 PTA *ptas, *ptao;
329 
330  PROCNAME("wshedApply");
331 
332  if (!wshed)
333  return ERROR_INT("wshed not defined", procName, 1);
334 
335  /* ------------------------------------------------------------ *
336  * Initialize priority queue and pixlab with seeds and minima *
337  * ------------------------------------------------------------ */
338 
339  lh = lheapCreate(0, L_SORT_INCREASING); /* remove lowest values first */
340  rstack = lstackCreate(0); /* for reusing the WSPixels */
341  pixGetDimensions(wshed->pixs, &w, &h, NULL);
342  lines8 = wshed->lines8; /* wshed owns this */
343  linelab32 = wshed->linelab32; /* ditto */
344 
345  /* Identify seed (marker) pixels, 1 for each c.c. in pixm */
346  pixSelectMinInConnComp(wshed->pixs, wshed->pixm, &ptas, &nash);
347  pixsd = pixGenerateFromPta(ptas, w, h);
348  nseeds = ptaGetCount(ptas);
349  for (i = 0; i < nseeds; i++) {
350  ptaGetIPt(ptas, i, &x, &y);
351  uval = GET_DATA_BYTE(lines8[y], x);
352  pushWSPixel(lh, rstack, (l_int32)uval, x, y, i);
353  }
354  wshed->ptas = ptas;
355  nasi = numaMakeConstant(1, nseeds); /* indicator array */
356  wshed->nasi = nasi;
357  wshed->nash = nash;
358  wshed->nseeds = nseeds;
359 
360  /* Identify minima that are not seeds. Use these 4 steps:
361  * (1) Get the local minima, which can have components
362  * of arbitrary size. This will be a clipping mask.
363  * (2) Get the image of the actual seeds (pixsd)
364  * (3) Remove all elements of the clipping mask that have a seed.
365  * (4) Shrink each of the remaining elements of the minima mask
366  * to a single pixel. */
367  pixLocalExtrema(wshed->pixs, 200, 0, &pixmin, NULL);
368  pixRemoveSeededComponents(pixmin, pixsd, pixmin, 8, 2);
369  pixSelectMinInConnComp(wshed->pixs, pixmin, &ptao, &namh);
370  nother = ptaGetCount(ptao);
371  for (i = 0; i < nother; i++) {
372  ptaGetIPt(ptao, i, &x, &y);
373  uval = GET_DATA_BYTE(lines8[y], x);
374  pushWSPixel(lh, rstack, (l_int32)uval, x, y, nseeds + i);
375  }
376  wshed->namh = namh;
377 
378  /* ------------------------------------------------------------ *
379  * Initialize merging lookup tables *
380  * ------------------------------------------------------------ */
381 
382  /* nalut should always give the current after-merging index.
383  * links are effectively backpointers: they are numas associated with
384  * a dest index of all indices in nalut that point to that index. */
385  mindepth = wshed->mindepth;
386  nboth = nseeds + nother;
387  arraysize = 2 * nboth;
388  wshed->arraysize = arraysize;
389  nalut = numaMakeSequence(0, 1, arraysize);
390  lut = numaGetIArray(nalut);
391  wshed->lut = lut; /* wshed owns this */
392  links = (NUMA **)LEPT_CALLOC(arraysize, sizeof(NUMA *));
393  wshed->links = links; /* wshed owns this */
394  nindex = nseeds + nother; /* the next unused index value */
395 
396  /* ------------------------------------------------------------ *
397  * Fill the basins, using the priority queue *
398  * ------------------------------------------------------------ */
399 
400  pixad = pixaCreate(nseeds);
401  wshed->pixad = pixad; /* wshed owns this */
402  nalevels = numaCreate(nseeds);
403  wshed->nalevels = nalevels; /* wshed owns this */
404  L_INFO("nseeds = %d, nother = %d\n", procName, nseeds, nother);
405  while (lheapGetCount(lh) > 0) {
406  popWSPixel(lh, rstack, &val, &x, &y, &index);
407 /* fprintf(stderr, "x = %d, y = %d, index = %d\n", x, y, index); */
408  ulabel = GET_DATA_FOUR_BYTES(linelab32[y], x);
409  if (ulabel == MAX_LABEL_VALUE)
410  clabel = ulabel;
411  else
412  clabel = lut[ulabel];
413  cindex = lut[index];
414  if (clabel == cindex) continue; /* have already seen this one */
415  if (clabel == MAX_LABEL_VALUE) { /* new one; assign index and try to
416  * propagate to all neighbors */
417  SET_DATA_FOUR_BYTES(linelab32[y], x, cindex);
418  imin = L_MAX(0, y - 1);
419  imax = L_MIN(h - 1, y + 1);
420  jmin = L_MAX(0, x - 1);
421  jmax = L_MIN(w - 1, x + 1);
422  for (i = imin; i <= imax; i++) {
423  for (j = jmin; j <= jmax; j++) {
424  if (i == y && j == x) continue;
425  uval = GET_DATA_BYTE(lines8[i], j);
426  pushWSPixel(lh, rstack, (l_int32)uval, j, i, cindex);
427  }
428  }
429  } else { /* pixel is already labeled (differently); must resolve */
430 
431  /* If both indices are seeds, check if the min height is
432  * greater than mindepth. If so, we have two new watersheds;
433  * locate them and assign to both regions a new index
434  * for further waterfill. If not, absorb the shallower
435  * watershed into the deeper one and continue filling it. */
436  pixGetPixel(pixsd, x, y, &uval);
437  if (clabel < nseeds && cindex < nseeds) {
438  wshedGetHeight(wshed, val, clabel, &hlabel);
439  wshedGetHeight(wshed, val, cindex, &hindex);
440  hmin = L_MIN(hlabel, hindex);
441  hmax = L_MAX(hlabel, hindex);
442  if (hmin == hmax) {
443  hmin = hlabel;
444  hmax = hindex;
445  }
446  if (wshed->debug) {
447  fprintf(stderr, "clabel,hlabel = %d,%d\n", clabel, hlabel);
448  fprintf(stderr, "hmin = %d, hmax = %d\n", hmin, hmax);
449  fprintf(stderr, "cindex,hindex = %d,%d\n", cindex, hindex);
450  if (hmin < mindepth)
451  fprintf(stderr, "Too shallow!\n");
452  }
453 
454  if (hmin >= mindepth) {
455  debugWshedMerge(wshed, two_new_watersheds,
456  x, y, clabel, cindex);
457  wshedSaveBasin(wshed, cindex, val - 1);
458  wshedSaveBasin(wshed, clabel, val - 1);
459  numaSetValue(nasi, cindex, 0);
460  numaSetValue(nasi, clabel, 0);
461 
462  if (wshed->debug) fprintf(stderr, "nindex = %d\n", nindex);
463  debugPrintLUT(lut, nindex, wshed->debug);
464  mergeLookup(wshed, clabel, nindex);
465  debugPrintLUT(lut, nindex, wshed->debug);
466  mergeLookup(wshed, cindex, nindex);
467  debugPrintLUT(lut, nindex, wshed->debug);
468  nindex++;
469  } else /* extraneous seed within seeded basin; absorb */ {
470  debugWshedMerge(wshed, seed_absorbed_into_seeded_basin,
471  x, y, clabel, cindex);
472  }
473  maxhindex = clabel; /* TODO: is this part of above 'else'? */
474  minhindex = cindex;
475  if (hindex > hlabel) {
476  maxhindex = cindex;
477  minhindex = clabel;
478  }
479  mergeLookup(wshed, minhindex, maxhindex);
480  } else if (clabel < nseeds && cindex >= nboth) {
481  /* If one index is a seed and the other is a merge of
482  * 2 watersheds, generate a single watershed. */
483  debugWshedMerge(wshed, one_new_watershed_label,
484  x, y, clabel, cindex);
485  wshedSaveBasin(wshed, clabel, val - 1);
486  numaSetValue(nasi, clabel, 0);
487  mergeLookup(wshed, clabel, cindex);
488  } else if (cindex < nseeds && clabel >= nboth) {
489  debugWshedMerge(wshed, one_new_watershed_index,
490  x, y, clabel, cindex);
491  wshedSaveBasin(wshed, cindex, val - 1);
492  numaSetValue(nasi, cindex, 0);
493  mergeLookup(wshed, cindex, clabel);
494  } else if (clabel < nseeds) { /* cindex from minima; absorb */
495  /* If one index is a seed and the other is from a minimum,
496  * merge the minimum wshed into the seed wshed. */
497  debugWshedMerge(wshed, minima_absorbed_into_seeded_basin,
498  x, y, clabel, cindex);
499  mergeLookup(wshed, cindex, clabel);
500  } else if (cindex < nseeds) { /* clabel from minima; absorb */
501  debugWshedMerge(wshed, minima_absorbed_into_seeded_basin,
502  x, y, clabel, cindex);
503  mergeLookup(wshed, clabel, cindex);
504  } else { /* If neither index is a seed, just merge */
505  debugWshedMerge(wshed, minima_absorbed_by_filler_or_another,
506  x, y, clabel, cindex);
507  mergeLookup(wshed, clabel, cindex);
508  }
509  }
510  }
511 
512 #if 0
513  /* Use the indicator array to save any watersheds that fill
514  * to the maximum value. This seems to screw things up! */
515  for (i = 0; i < nseeds; i++) {
516  numaGetIValue(nasi, i, &ival);
517  if (ival == 1) {
518  wshedSaveBasin(wshed, lut[i], val - 1);
519  numaSetValue(nasi, i, 0);
520  }
521  }
522 #endif
523 
524  numaDestroy(&nalut);
525  pixDestroy(&pixmin);
526  pixDestroy(&pixsd);
527  ptaDestroy(&ptao);
528  lheapDestroy(&lh, TRUE);
529  lstackDestroy(&rstack, TRUE);
530  return 0;
531 }
532 
533 
534 /*-----------------------------------------------------------------------*
535  * Helpers *
536  *-----------------------------------------------------------------------*/
553 static void
555  l_int32 index,
556  l_int32 level)
557 {
558 BOX *box;
559 PIX *pix;
560 
561  PROCNAME("wshedSaveBasin");
562 
563  if (!wshed) {
564  L_ERROR("wshed not defined\n", procName);
565  return;
566  }
567 
568  if (identifyWatershedBasin(wshed, index, level, &box, &pix) == 0) {
569  pixaAddPix(wshed->pixad, pix, L_INSERT);
570  pixaAddBox(wshed->pixad, box, L_INSERT);
571  numaAddNumber(wshed->nalevels, level - 1);
572  }
573  return;
574 }
575 
576 
598 static l_int32
600  l_int32 index,
601  l_int32 level,
602  BOX **pbox,
603  PIX **ppixd)
604 {
605 l_int32 imin, imax, jmin, jmax, minx, miny, maxx, maxy;
606 l_int32 bw, bh, i, j, w, h, x, y;
607 l_int32 *lut;
608 l_uint32 label, bval, lval;
609 void **lines8, **linelab32, **linet1;
610 BOX *box;
611 PIX *pixs, *pixt, *pixd;
612 L_QUEUE *lq;
613 
614  PROCNAME("identifyWatershedBasin");
615 
616  if (!pbox)
617  return ERROR_INT("&box not defined", procName, 1);
618  *pbox = NULL;
619  if (!ppixd)
620  return ERROR_INT("&pixd not defined", procName, 1);
621  *ppixd = NULL;
622  if (!wshed)
623  return ERROR_INT("wshed not defined", procName, 1);
624 
625  /* Make a queue and an auxiliary stack */
626  lq = lqueueCreate(0);
627  lq->stack = lstackCreate(0);
628 
629  pixs = wshed->pixs;
630  pixt = wshed->pixt;
631  lines8 = wshed->lines8;
632  linelab32 = wshed->linelab32;
633  linet1 = wshed->linet1;
634  lut = wshed->lut;
635  pixGetDimensions(pixs, &w, &h, NULL);
636 
637  /* Prime the queue with the seed pixel for this watershed. */
638  minx = miny = 1000000;
639  maxx = maxy = 0;
640  ptaGetIPt(wshed->ptas, index, &x, &y);
641  pixSetPixel(pixt, x, y, 1);
642  pushNewPixel(lq, x, y, &minx, &maxx, &miny, &maxy);
643  if (wshed->debug) fprintf(stderr, "prime: (x,y) = (%d, %d)\n", x, y);
644 
645  /* Each pixel in a spreading breadth-first search is inspected.
646  * It is accepted as part of this watershed, and pushed on
647  * the search queue, if:
648  * (1) It has a label value equal to %index
649  * (2) The pixel value is less than %level, the overflow
650  * height at which the two basins join.
651  * (3) It has not yet been seen in this search. */
652  while (lqueueGetCount(lq) > 0) {
653  popNewPixel(lq, &x, &y);
654  imin = L_MAX(0, y - 1);
655  imax = L_MIN(h - 1, y + 1);
656  jmin = L_MAX(0, x - 1);
657  jmax = L_MIN(w - 1, x + 1);
658  for (i = imin; i <= imax; i++) {
659  for (j = jmin; j <= jmax; j++) {
660  if (j == x && i == y) continue; /* parent */
661  label = GET_DATA_FOUR_BYTES(linelab32[i], j);
662  if (label == MAX_LABEL_VALUE || lut[label] != index) continue;
663  bval = GET_DATA_BIT(linet1[i], j);
664  if (bval == 1) continue; /* already seen */
665  lval = GET_DATA_BYTE(lines8[i], j);
666  if (lval >= level) continue; /* too high */
667  SET_DATA_BIT(linet1[i], j);
668  pushNewPixel(lq, j, i, &minx, &maxx, &miny, &maxy);
669  }
670  }
671  }
672 
673  /* Extract the box and pix, and clear pixt */
674  bw = maxx - minx + 1;
675  bh = maxy - miny + 1;
676  box = boxCreate(minx, miny, bw, bh);
677  pixd = pixClipRectangle(pixt, box, NULL);
678  pixRasterop(pixt, minx, miny, bw, bh, PIX_SRC ^ PIX_DST, pixd, 0, 0);
679  *pbox = box;
680  *ppixd = pixd;
681 
682  lqueueDestroy(&lq, 1);
683  return 0;
684 }
685 
686 
711 static l_int32
713  l_int32 sindex,
714  l_int32 dindex)
715 {
716 l_int32 i, n, size, index;
717 l_int32 *lut;
718 NUMA *na;
719 NUMA **links;
720 
721  PROCNAME("mergeLookup");
722 
723  if (!wshed)
724  return ERROR_INT("wshed not defined", procName, 1);
725  size = wshed->arraysize;
726  if (sindex < 0 || sindex >= size)
727  return ERROR_INT("invalid sindex", procName, 1);
728  if (dindex < 0 || dindex >= size)
729  return ERROR_INT("invalid dindex", procName, 1);
730 
731  /* Redirect links in the lut */
732  n = 0;
733  links = wshed->links;
734  lut = wshed->lut;
735  if ((na = links[sindex]) != NULL) {
736  n = numaGetCount(na);
737  for (i = 0; i < n; i++) {
738  numaGetIValue(na, i, &index);
739  lut[index] = dindex;
740  }
741  }
742  lut[sindex] = dindex;
743 
744  /* Shift the backlink arrays from sindex to dindex.
745  * sindex should have no backlinks because all entries in the
746  * lut that were previously pointing to it have been redirected
747  * to dindex. */
748  if (!links[dindex])
749  links[dindex] = numaCreate(n);
750  numaJoin(links[dindex], links[sindex], 0, -1);
751  numaAddNumber(links[dindex], sindex);
752  numaDestroy(&links[sindex]);
753 
754  return 0;
755 }
756 
757 
775 static l_int32
777  l_int32 val,
778  l_int32 label,
779  l_int32 *pheight)
780 {
781 l_int32 minval;
782 
783  PROCNAME("wshedGetHeight");
784 
785  if (!pheight)
786  return ERROR_INT("&height not defined", procName, 1);
787  *pheight = 0;
788  if (!wshed)
789  return ERROR_INT("wshed not defined", procName, 1);
790 
791  if (label < wshed->nseeds)
792  numaGetIValue(wshed->nash, label, &minval);
793  else if (label < wshed->nseeds + wshed->nother)
794  numaGetIValue(wshed->namh, label, &minval);
795  else
796  return ERROR_INT("finished watershed; should not call", procName, 1);
797 
798  *pheight = val - minval;
799  return 0;
800 }
801 
802 
803 /*
804  * pushNewPixel()
805  *
806  * Input: lqueue
807  * x, y (pixel coordinates)
808  * &minx, &maxx, &miny, &maxy (<return> bounding box update)
809  * Return: void
810  *
811  * Notes:
812  * (1) This is a wrapper for adding a NewPixel to a queue, which
813  * updates the bounding box for all pixels on that queue and
814  * uses the storage stack to retrieve a NewPixel.
815  */
816 static void
817 pushNewPixel(L_QUEUE *lq,
818  l_int32 x,
819  l_int32 y,
820  l_int32 *pminx,
821  l_int32 *pmaxx,
822  l_int32 *pminy,
823  l_int32 *pmaxy)
824 {
825 L_NEWPIXEL *np;
826 
827  PROCNAME("pushNewPixel");
828 
829  if (!lq) {
830  L_ERROR("queue not defined\n", procName);
831  return;
832  }
833 
834  /* Adjust bounding box */
835  *pminx = L_MIN(*pminx, x);
836  *pmaxx = L_MAX(*pmaxx, x);
837  *pminy = L_MIN(*pminy, y);
838  *pmaxy = L_MAX(*pmaxy, y);
839 
840  /* Get a newpixel to use */
841  if (lstackGetCount(lq->stack) > 0)
842  np = (L_NEWPIXEL *)lstackRemove(lq->stack);
843  else
844  np = (L_NEWPIXEL *)LEPT_CALLOC(1, sizeof(L_NEWPIXEL));
845 
846  np->x = x;
847  np->y = y;
848  lqueueAdd(lq, np);
849  return;
850 }
851 
852 
853 /*
854  * popNewPixel()
855  *
856  * Input: lqueue
857  * &x, &y (<return> pixel coordinates)
858  * Return: void
859  *
860  * Notes:
861  * (1) This is a wrapper for removing a NewPixel from a queue,
862  * which returns the pixel coordinates and saves the NewPixel
863  * on the storage stack.
864  */
865 static void
866 popNewPixel(L_QUEUE *lq,
867  l_int32 *px,
868  l_int32 *py)
869 {
870 L_NEWPIXEL *np;
871 
872  PROCNAME("popNewPixel");
873 
874  if (!lq) {
875  L_ERROR("lqueue not defined\n", procName);
876  return;
877  }
878 
879  if ((np = (L_NEWPIXEL *)lqueueRemove(lq)) == NULL)
880  return;
881  *px = np->x;
882  *py = np->y;
883  lstackAdd(lq->stack, np); /* save for re-use */
884  return;
885 }
886 
887 
888 /*
889  * pushWSPixel()
890  *
891  * Input: lh (priority queue)
892  * stack (of reusable WSPixels)
893  * val (pixel value: used for ordering the heap)
894  * x, y (pixel coordinates)
895  * index (label for set to which pixel belongs)
896  * Return: void
897  *
898  * Notes:
899  * (1) This is a wrapper for adding a WSPixel to a heap. It
900  * uses the storage stack to retrieve a WSPixel.
901  */
902 static void
903 pushWSPixel(L_HEAP *lh,
904  L_STACK *stack,
905  l_int32 val,
906  l_int32 x,
907  l_int32 y,
908  l_int32 index)
909 {
910 L_WSPIXEL *wsp;
911 
912  PROCNAME("pushWSPixel");
913 
914  if (!lh) {
915  L_ERROR("heap not defined\n", procName);
916  return;
917  }
918  if (!stack) {
919  L_ERROR("stack not defined\n", procName);
920  return;
921  }
922 
923  /* Get a wspixel to use */
924  if (lstackGetCount(stack) > 0)
925  wsp = (L_WSPIXEL *)lstackRemove(stack);
926  else
927  wsp = (L_WSPIXEL *)LEPT_CALLOC(1, sizeof(L_WSPIXEL));
928 
929  wsp->val = (l_float32)val;
930  wsp->x = x;
931  wsp->y = y;
932  wsp->index = index;
933  lheapAdd(lh, wsp);
934  return;
935 }
936 
937 
938 /*
939  * popWSPixel()
940  *
941  * Input: lh (priority queue)
942  * stack (of reusable WSPixels)
943  * &val (<return> pixel value)
944  * &x, &y (<return> pixel coordinates)
945  * &index (<return> label for set to which pixel belongs)
946  * Return: void
947  *
948  * Notes:
949  * (1) This is a wrapper for removing a WSPixel from a heap,
950  * which returns the WSPixel data and saves the WSPixel
951  * on the storage stack.
952  */
953 static void
954 popWSPixel(L_HEAP *lh,
955  L_STACK *stack,
956  l_int32 *pval,
957  l_int32 *px,
958  l_int32 *py,
959  l_int32 *pindex)
960 {
961 L_WSPIXEL *wsp;
962 
963  PROCNAME("popWSPixel");
964 
965  if (!lh) {
966  L_ERROR("lheap not defined\n", procName);
967  return;
968  }
969  if (!stack) {
970  L_ERROR("stack not defined\n", procName);
971  return;
972  }
973  if (!pval || !px || !py || !pindex) {
974  L_ERROR("data can't be returned\n", procName);
975  return;
976  }
977 
978  if ((wsp = (L_WSPIXEL *)lheapRemove(lh)) == NULL)
979  return;
980  *pval = (l_int32)wsp->val;
981  *px = wsp->x;
982  *py = wsp->y;
983  *pindex = wsp->index;
984  lstackAdd(stack, wsp); /* save for re-use */
985  return;
986 }
987 
988 
989 static void
990 debugPrintLUT(l_int32 *lut,
991  l_int32 size,
992  l_int32 debug)
993 {
994 l_int32 i;
995 
996  if (!debug) return;
997  fprintf(stderr, "lut: ");
998  for (i = 0; i < size; i++)
999  fprintf(stderr, "%d ", lut[i]);
1000  fprintf(stderr, "\n");
1001  return;
1002 }
1003 
1004 
1005 static void
1006 debugWshedMerge(L_WSHED *wshed,
1007  char *descr,
1008  l_int32 x,
1009  l_int32 y,
1010  l_int32 label,
1011  l_int32 index)
1012 {
1013  if (!wshed || (wshed->debug == 0))
1014  return;
1015  fprintf(stderr, "%s:\n", descr);
1016  fprintf(stderr, " (x, y) = (%d, %d)\n", x, y);
1017  fprintf(stderr, " clabel = %d, cindex = %d\n", label, index);
1018  return;
1019 }
1020 
1021 
1022 /*-----------------------------------------------------------------------*
1023  * Output *
1024  *-----------------------------------------------------------------------*/
1033 l_ok
1035  PIXA **ppixa,
1036  NUMA **pnalevels)
1037 {
1038  PROCNAME("wshedBasins");
1039 
1040  if (!wshed)
1041  return ERROR_INT("wshed not defined", procName, 1);
1042 
1043  if (ppixa)
1044  *ppixa = pixaCopy(wshed->pixad, L_CLONE);
1045  if (pnalevels)
1046  *pnalevels = numaClone(wshed->nalevels);
1047  return 0;
1048 }
1049 
1050 
1057 PIX *
1059 {
1060 l_int32 i, n, level, bx, by;
1061 NUMA *na;
1062 PIX *pix, *pixd;
1063 PIXA *pixa;
1064 
1065  PROCNAME("wshedRenderFill");
1066 
1067  if (!wshed)
1068  return (PIX *)ERROR_PTR("wshed not defined", procName, NULL);
1069 
1070  wshedBasins(wshed, &pixa, &na);
1071  pixd = pixCopy(NULL, wshed->pixs);
1072  n = pixaGetCount(pixa);
1073  for (i = 0; i < n; i++) {
1074  pix = pixaGetPix(pixa, i, L_CLONE);
1075  pixaGetBoxGeometry(pixa, i, &bx, &by, NULL, NULL);
1076  numaGetIValue(na, i, &level);
1077  pixPaintThroughMask(pixd, pix, bx, by, level);
1078  pixDestroy(&pix);
1079  }
1080 
1081  pixaDestroy(&pixa);
1082  numaDestroy(&na);
1083  return pixd;
1084 }
1085 
1086 
1093 PIX *
1095 {
1096 l_int32 w, h;
1097 PIX *pixg, *pixt, *pixc, *pixm, *pixd;
1098 PIXA *pixa;
1099 
1100  PROCNAME("wshedRenderColors");
1101 
1102  if (!wshed)
1103  return (PIX *)ERROR_PTR("wshed not defined", procName, NULL);
1104 
1105  wshedBasins(wshed, &pixa, NULL);
1106  pixg = pixCopy(NULL, wshed->pixs);
1107  pixGetDimensions(wshed->pixs, &w, &h, NULL);
1108  pixd = pixConvertTo32(pixg);
1109  pixt = pixaDisplayRandomCmap(pixa, w, h);
1110  pixc = pixConvertTo32(pixt);
1111  pixm = pixaDisplay(pixa, w, h);
1112  pixCombineMasked(pixd, pixc, pixm);
1113 
1114  pixDestroy(&pixg);
1115  pixDestroy(&pixt);
1116  pixDestroy(&pixc);
1117  pixDestroy(&pixm);
1118  pixaDestroy(&pixa);
1119  return pixd;
1120 }
void ** lines8
Definition: watershed.h:45
l_int32 y
Definition: watershed.c:127
l_int32 * lut
Definition: watershed.h:57
l_int32 debug
Definition: watershed.h:60
Definition: pix.h:717
l_ok lheapAdd(L_HEAP *lh, void *item)
lheapAdd()
Definition: heap.c:186
PIX * pixConvertTo32(PIX *pixs)
pixConvertTo32()
Definition: pixconv.c:3233
void lstackDestroy(L_STACK **plstack, l_int32 freeflag)
lstackDestroy()
Definition: stack.c:121
PIX * pixGenerateFromPta(PTA *pta, l_int32 w, l_int32 h)
pixGenerateFromPta()
Definition: ptafunc1.c:1961
struct Numa * nasi
Definition: watershed.h:51
PIX * pixRemoveSeededComponents(PIX *pixd, PIX *pixs, PIX *pixm, l_int32 connectivity, l_int32 bordersize)
pixRemoveSeededComponents()
Definition: seedfill.c:3430
l_int32 nother
Definition: watershed.h:56
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:473
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:163
L_QUEUE * lqueueCreate(l_int32 nalloc)
lqueueCreate()
Definition: queue.c:90
l_int32 lstackGetCount(L_STACK *lstack)
lstackGetCount()
Definition: stack.c:247
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
struct Pix * pixt
Definition: watershed.h:44
void wshedDestroy(L_WSHED **pwshed)
wshedDestroy()
Definition: watershed.c:250
void ** pixGetLinePtrs(PIX *pix, l_int32 *psize)
pixGetLinePtrs()
Definition: pix1.c:1810
NUMA * numaMakeConstant(l_float32 val, l_int32 size)
numaMakeConstant()
Definition: numafunc1.c:781
void ** linem1
Definition: watershed.h:46
#define GET_DATA_FOUR_BYTES(pdata, n)
Definition: arrayaccess.h:231
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
struct L_Stack * stack
Definition: queue.h:71
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:187
l_int32 ptaGetCount(PTA *pta)
ptaGetCount()
Definition: ptabasic.c:504
void lheapDestroy(L_HEAP **plh, l_int32 freeflag)
lheapDestroy()
Definition: heap.c:145
l_int32 x
Definition: watershed.c:135
PIX * pixaDisplay(PIXA *pixa, l_int32 w, l_int32 h)
pixaDisplay()
Definition: pixafunc2.c:184
struct Numa * namh
Definition: watershed.h:53
l_int32 index
Definition: watershed.c:137
#define GET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:123
struct Numa * nalevels
Definition: watershed.h:54
PIX * pixClipRectangle(PIX *pixs, BOX *box, BOX **pboxc)
pixClipRectangle()
Definition: pix5.c:1020
struct Pix * pixm
Definition: watershed.h:41
l_ok numaSetValue(NUMA *na, l_int32 index, l_float32 val)
numaSetValue()
Definition: numabasic.c:759
l_int32 lheapGetCount(L_HEAP *lh)
lheapGetCount()
Definition: heap.c:271
l_ok pixPaintThroughMask(PIX *pixd, PIX *pixm, l_int32 x, l_int32 y, l_uint32 val)
pixPaintThroughMask()
Definition: pix3.c:618
void * lqueueRemove(L_QUEUE *lq)
lqueueRemove()
Definition: queue.c:254
l_ok lstackAdd(L_STACK *lstack, void *item)
lstackAdd()
Definition: stack.c:167
l_ok pixSetAllArbitrary(PIX *pix, l_uint32 val)
pixSetAllArbitrary()
Definition: pix2.c:876
struct Pta * ptas
Definition: watershed.h:50
l_ok pixCombineMasked(PIX *pixd, PIX *pixs, PIX *pixm)
pixCombineMasked()
Definition: pix3.c:374
PIXA * pixaCopy(PIXA *pixa, l_int32 copyflag)
pixaCopy()
Definition: pixabasic.c:450
l_int32 * numaGetIArray(NUMA *na)
numaGetIArray()
Definition: numabasic.c:820
struct Pix * pixlab
Definition: watershed.h:43
PIX * pixaDisplayRandomCmap(PIXA *pixa, l_int32 w, l_int32 h)
pixaDisplayRandomCmap()
Definition: pixafunc2.c:359
l_ok numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:727
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:503
Definition: array.h:59
PIX * wshedRenderColors(L_WSHED *wshed)
wshedRenderColors()
Definition: watershed.c:1094
void * lstackRemove(L_STACK *lstack)
lstackRemove()
Definition: stack.c:197
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:631
l_int32 mindepth
Definition: watershed.h:42
l_ok pixSetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 val)
pixSetPixel()
Definition: pix2.c:253
l_ok lqueueAdd(L_QUEUE *lq, void *item)
lqueueAdd()
Definition: queue.c:187
Definition: queue.h:64
void ** linet1
Definition: watershed.h:48
void ** linelab32
Definition: watershed.h:47
struct Numa ** links
Definition: watershed.h:58
static l_int32 mergeLookup(L_WSHED *wshed, l_int32 sindex, l_int32 dindex)
mergeLookup()
Definition: watershed.c:712
L_HEAP * lheapCreate(l_int32 nalloc, l_int32 direction)
lheapCreate()
Definition: heap.c:102
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
void * lheapRemove(L_HEAP *lh)
lheapRemove()
Definition: heap.c:242
L_STACK * lstackCreate(l_int32 nalloc)
lstackCreate()
Definition: stack.c:78
l_ok pixaGetBoxGeometry(PIXA *pixa, l_int32 index, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
pixaGetBoxGeometry()
Definition: pixabasic.c:835
l_ok wshedApply(L_WSHED *wshed)
wshedApply()
Definition: watershed.c:305
PIX * wshedRenderFill(L_WSHED *wshed)
wshedRenderFill()
Definition: watershed.c:1058
static void wshedSaveBasin(L_WSHED *wshed, l_int32 index, l_int32 level)
wshedSaveBasin()
Definition: watershed.c:554
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:515
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:543
NUMA * numaMakeSequence(l_float32 startval, l_float32 increment, l_int32 size)
numaMakeSequence()
Definition: numafunc1.c:750
l_int32 lqueueGetCount(L_QUEUE *lq)
lqueueGetCount()
Definition: queue.c:283
l_int32 arraysize
Definition: watershed.h:59
struct Pix * pixs
Definition: watershed.h:40
l_int32 nseeds
Definition: watershed.h:55
Definition: heap.h:77
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
struct Pixa * pixad
Definition: watershed.h:49
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1065
L_WSHED * wshedCreate(PIX *pixs, PIX *pixm, l_int32 mindepth, l_int32 debugflag)
wshedCreate()
Definition: watershed.c:203
l_ok numaJoin(NUMA *nad, NUMA *nas, l_int32 istart, l_int32 iend)
numaJoin()
Definition: numafunc1.c:3370
l_int32 x
Definition: watershed.c:126
#define SET_DATA_FOUR_BYTES(pdata, n, val)
Definition: arrayaccess.h:235
static l_int32 identifyWatershedBasin(L_WSHED *wshed, l_int32 index, l_int32 level, BOX **pbox, PIX **ppixd)
identifyWatershedBasin()
Definition: watershed.c:599
l_ok wshedBasins(L_WSHED *wshed, PIXA **ppixa, NUMA **pnalevels)
wshedBasins()
Definition: watershed.c:1034
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:672
void lqueueDestroy(L_QUEUE **plq, l_int32 freeflag)
lqueueDestroy()
Definition: queue.c:131
Definition: pix.h:134
l_ok pixSelectMinInConnComp(PIX *pixs, PIX *pixm, PTA **ppta, NUMA **pnav)
pixSelectMinInConnComp()
Definition: seedfill.c:3317
Definition: pix.h:719
void ptaDestroy(PTA **ppta)
ptaDestroy()
Definition: ptabasic.c:192
#define PIX_SRC
Definition: pix.h:327
PIX * pixCopy(PIX *pixd, PIX *pixs)
pixCopy()
Definition: pix1.c:628
NUMA * numaClone(NUMA *na)
numaClone()
Definition: numabasic.c:423
l_ok ptaGetIPt(PTA *pta, l_int32 index, l_int32 *px, l_int32 *py)
ptaGetIPt()
Definition: ptabasic.c:555
static l_int32 wshedGetHeight(L_WSHED *wshed, l_int32 val, l_int32 label, l_int32 *pheight)
wshedGetHeight()
Definition: watershed.c:776
struct Numa * nash
Definition: watershed.h:52
l_float32 val
Definition: watershed.c:134
l_ok pixaAddBox(PIXA *pixa, BOX *box, l_int32 copyflag)
pixaAddBox()
Definition: pixabasic.c:547
l_int32 y
Definition: watershed.c:136
Definition: pix.h:480
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:408
BOX * boxCreate(l_int32 x, l_int32 y, l_int32 w, l_int32 h)
boxCreate()
Definition: boxbasic.c:165
l_int32 pixaGetCount(PIXA *pixa)
pixaGetCount()
Definition: pixabasic.c:631
#define SET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:127
Definition: stack.h:59
Definition: pix.h:517
#define PIX_DST
Definition: pix.h:328