comparison mupdf-source/thirdparty/leptonica/src/watershed.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
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
27 /*!
28 * \file watershed.c
29 * <pre>
30 *
31 * Top-level
32 * L_WSHED *wshedCreate()
33 * void wshedDestroy()
34 * l_int32 wshedApply()
35 *
36 * Helpers
37 * static l_int32 identifyWatershedBasin()
38 * static l_int32 mergeLookup()
39 * static l_int32 wshedGetHeight()
40 * static void pushNewPixel()
41 * static void popNewPixel()
42 * static void pushWSPixel()
43 * static void popWSPixel()
44 * static void debugPrintLUT()
45 * static void debugWshedMerge()
46 *
47 * Output
48 * l_int32 wshedBasins()
49 * PIX *wshedRenderFill()
50 * PIX *wshedRenderColors()
51 *
52 * The watershed function identifies the "catch basins" of the input
53 * 8 bpp image, with respect to the specified seeds or "markers".
54 * The use is in segmentation, but the selection of the markers is
55 * critical to getting meaningful results.
56 *
57 * How are the markers selected? You can't simply use the local
58 * minima, because a typical image has sufficient noise so that
59 * a useful catch basin can easily have multiple local minima. However
60 * they are selected, the question for the watershed function is
61 * how to handle local minima that are not markers. The reason
62 * this is important is because of the algorithm used to find the
63 * watersheds, which is roughly like this:
64 *
65 * (1) Identify the markers and the local minima, and enter them
66 * into a priority queue based on the pixel value. Each marker
67 * is shrunk to a single pixel, if necessary, before the
68 * operation starts.
69 * (2) Feed the priority queue with neighbors of pixels that are
70 * popped off the queue. Each of these queue pixels is labeled
71 * with the index value of its parent.
72 * (3) Each pixel is also labeled, in a 32-bit image, with the marker
73 * or local minimum index, from which it was originally derived.
74 * (4) There are actually 3 classes of labels: seeds, minima, and
75 * fillers. The fillers are labels of regions that have already
76 * been identified as watersheds and are continuing to fill, for
77 * the purpose of finding higher watersheds.
78 * (5) When a pixel is popped that has already been labeled in the
79 * 32-bit image and that label differs from the label of its
80 * parent (stored in the queue pixel), a boundary has been crossed.
81 * There are several cases:
82 * (a) Both parents are derived from markers but at least one
83 * is not deep enough to become a watershed. Absorb the
84 * shallower basin into the deeper one, fixing the LUT to
85 * redirect the shallower index to the deeper one.
86 * (b) Both parents are derived from markers and both are deep
87 * enough. Identify and save the watershed for each marker.
88 * (c) One parent was derived from a marker and the other from
89 * a minima: absorb the minima basin into the marker basin.
90 * (d) One parent was derived from a marker and the other is
91 * a filler: identify and save the watershed for the marker.
92 * (e) Both parents are derived from minima: merge them.
93 * (f) One parent is a filler and the other is derived from a
94 * minima: merge the minima into the filler.
95 * (6) The output of the watershed operation consists of:
96 * ~ a pixa of the basins
97 * ~ a pta of the markers
98 * ~ a numa of the watershed levels
99 *
100 * Typical usage:
101 * L_WShed *wshed = wshedCreate(pixs, pixseed, mindepth, 0);
102 * wshedApply(wshed);
103 *
104 * wshedBasins(wshed, &pixa, &nalevels);
105 * ... do something with pixa, nalevels ...
106 * pixaDestroy(&pixa);
107 * numaDestroy(&nalevels);
108 *
109 * Pix *pixd = wshedRenderFill(wshed);
110 *
111 * wshedDestroy(&wshed);
112 * </pre>
113 */
114
115 #ifdef HAVE_CONFIG_H
116 #include <config_auto.h>
117 #endif /* HAVE_CONFIG_H */
118
119 #include "allheaders.h"
120
121 #ifndef NO_CONSOLE_IO
122 #define DEBUG_WATERSHED 0
123 #endif /* ~NO_CONSOLE_IO */
124
125 static const l_uint32 MAX_LABEL_VALUE = 0x7fffffff; /* largest l_int32 */
126
127 /*! New pixel coordinates */
128 struct L_NewPixel
129 {
130 l_int32 x; /*!< x coordinate */
131 l_int32 y; /*!< y coordinate */
132 };
133 typedef struct L_NewPixel L_NEWPIXEL;
134
135 /*! Wartshed pixel */
136 struct L_WSPixel
137 {
138 l_float32 val; /*!< pixel value */
139 l_int32 x; /*!< x coordinate */
140 l_int32 y; /*!< y coordinate */
141 l_int32 index; /*!< label for set to which pixel belongs */
142 };
143 typedef struct L_WSPixel L_WSPIXEL;
144
145
146 /* Static functions for obtaining bitmap of watersheds */
147 static void wshedSaveBasin(L_WSHED *wshed, l_int32 index, l_int32 level);
148
149 static l_int32 identifyWatershedBasin(L_WSHED *wshed,
150 l_int32 index, l_int32 level,
151 BOX **pbox, PIX **ppixd);
152
153 /* Static function for merging lut and backlink arrays */
154 static l_int32 mergeLookup(L_WSHED *wshed, l_int32 sindex, l_int32 dindex);
155
156 /* Static function for finding the height of the current pixel
157 above its seed or minima in the watershed. */
158 static l_int32 wshedGetHeight(L_WSHED *wshed, l_int32 val, l_int32 label,
159 l_int32 *pheight);
160
161 /* Static accessors for NewPixel on a queue */
162 static void pushNewPixel(L_QUEUE *lq, l_int32 x, l_int32 y,
163 l_int32 *pminx, l_int32 *pmaxx,
164 l_int32 *pminy, l_int32 *pmaxy);
165 static void popNewPixel(L_QUEUE *lq, l_int32 *px, l_int32 *py);
166
167 /* Static accessors for WSPixel on a heap */
168 static void pushWSPixel(L_HEAP *lh, L_STACK *stack, l_int32 val,
169 l_int32 x, l_int32 y, l_int32 index);
170 static void popWSPixel(L_HEAP *lh, L_STACK *stack, l_int32 *pval,
171 l_int32 *px, l_int32 *py, l_int32 *pindex);
172
173 /* Static debug print output */
174 static void debugPrintLUT(l_int32 *lut, l_int32 size, l_int32 debug);
175
176 static void debugWshedMerge(L_WSHED *wshed, char *descr, l_int32 x,
177 l_int32 y, l_int32 label, l_int32 index);
178
179
180 /*-----------------------------------------------------------------------*
181 * Top-level watershed *
182 *-----------------------------------------------------------------------*/
183 /*!
184 * \brief wshedCreate()
185 *
186 * \param[in] pixs 8 bpp source
187 * \param[in] pixm 1 bpp 'marker' seed
188 * \param[in] mindepth minimum depth; anything less is not saved
189 * \param[in] debugflag 1 for debug output
190 * \return WShed, or NULL on error
191 *
192 * <pre>
193 * Notes:
194 * (1) It is not necessary for the fg pixels in the seed image
195 * be at minima, or that they be isolated. We extract a
196 * single pixel from each connected component, and a seed
197 * anywhere in a watershed will eventually label the watershed
198 * when the filling level reaches it.
199 * (2) Set mindepth to some value to ignore noise in pixs that
200 * can create small local minima. Any watershed shallower
201 * than mindepth, even if it has a seed, will not be saved;
202 * It will either be incorporated in another watershed or
203 * eliminated.
204 * </pre>
205 */
206 L_WSHED *
207 wshedCreate(PIX *pixs,
208 PIX *pixm,
209 l_int32 mindepth,
210 l_int32 debugflag)
211 {
212 l_int32 w, h;
213 L_WSHED *wshed;
214
215 if (!pixs)
216 return (L_WSHED *)ERROR_PTR("pixs is not defined", __func__, NULL);
217 if (pixGetDepth(pixs) != 8)
218 return (L_WSHED *)ERROR_PTR("pixs is not 8 bpp", __func__, NULL);
219 if (!pixm)
220 return (L_WSHED *)ERROR_PTR("pixm is not defined", __func__, NULL);
221 if (pixGetDepth(pixm) != 1)
222 return (L_WSHED *)ERROR_PTR("pixm is not 1 bpp", __func__, NULL);
223 pixGetDimensions(pixs, &w, &h, NULL);
224 if (pixGetWidth(pixm) != w || pixGetHeight(pixm) != h)
225 return (L_WSHED *)ERROR_PTR("pixs/m sizes are unequal", __func__, NULL);
226
227 if ((wshed = (L_WSHED *)LEPT_CALLOC(1, sizeof(L_WSHED))) == NULL)
228 return (L_WSHED *)ERROR_PTR("wshed not made", __func__, NULL);
229
230 wshed->pixs = pixClone(pixs);
231 wshed->pixm = pixClone(pixm);
232 wshed->mindepth = L_MAX(1, mindepth);
233 wshed->pixlab = pixCreate(w, h, 32);
234 pixSetAllArbitrary(wshed->pixlab, MAX_LABEL_VALUE);
235 wshed->pixt = pixCreate(w, h, 1);
236 wshed->lines8 = pixGetLinePtrs(pixs, NULL);
237 wshed->linem1 = pixGetLinePtrs(pixm, NULL);
238 wshed->linelab32 = pixGetLinePtrs(wshed->pixlab, NULL);
239 wshed->linet1 = pixGetLinePtrs(wshed->pixt, NULL);
240 wshed->debug = debugflag;
241 return wshed;
242 }
243
244
245 /*!
246 * \brief wshedDestroy()
247 *
248 * \param[in,out] pwshed will be set to null before returning
249 * \return void
250 */
251 void
252 wshedDestroy(L_WSHED **pwshed)
253 {
254 l_int32 i;
255 L_WSHED *wshed;
256
257 if (pwshed == NULL) {
258 L_WARNING("ptr address is null!\n", __func__);
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 }
289
290
291 /*!
292 * \brief wshedApply()
293 *
294 * \param[in] wshed generated from wshedCreate()
295 * \return 0 if OK, 1 on error
296 *
297 * <pre>
298 * Notes:
299 * (1) N.B. This is buggy! It seems to locate watersheds that are
300 * duplicates. The watershed extraction after complete fill
301 * grabs some regions belonging to existing watersheds.
302 * See prog/watershedtest.c for testing.
303 * </pre>
304 */
305 l_ok
306 wshedApply(L_WSHED *wshed)
307 {
308 char two_new_watersheds[] = "Two new watersheds";
309 char seed_absorbed_into_seeded_basin[] = "Seed absorbed into seeded basin";
310 char one_new_watershed_label[] = "One new watershed (label)";
311 char one_new_watershed_index[] = "One new watershed (index)";
312 char minima_absorbed_into_seeded_basin[] =
313 "Minima absorbed into seeded basin";
314 char minima_absorbed_by_filler_or_another[] =
315 "Minima absorbed by filler or another";
316 l_int32 nseeds, nother, nboth, arraysize;
317 l_int32 i, j, val, x, y, w, h, index, mindepth;
318 l_int32 imin, imax, jmin, jmax, cindex, clabel, nindex;
319 l_int32 hindex, hlabel, hmin, hmax, minhindex, maxhindex;
320 l_int32 *lut;
321 l_uint32 ulabel, uval;
322 void **lines8, **linelab32;
323 NUMA *nalut, *nalevels, *nash, *namh, *nasi;
324 NUMA **links;
325 L_HEAP *lh;
326 PIX *pixmin, *pixsd;
327 PIXA *pixad;
328 L_STACK *rstack;
329 PTA *ptas, *ptao;
330
331 if (!wshed)
332 return ERROR_INT("wshed not defined", __func__, 1);
333
334 /* ------------------------------------------------------------ *
335 * Initialize priority queue and pixlab with seeds and minima *
336 * ------------------------------------------------------------ */
337
338 lh = lheapCreate(0, L_SORT_INCREASING); /* remove lowest values first */
339 rstack = lstackCreate(0); /* for reusing the WSPixels */
340 pixGetDimensions(wshed->pixs, &w, &h, NULL);
341 lines8 = wshed->lines8; /* wshed owns this */
342 linelab32 = wshed->linelab32; /* ditto */
343
344 /* Identify seed (marker) pixels, 1 for each c.c. in pixm */
345 pixSelectMinInConnComp(wshed->pixs, wshed->pixm, &ptas, &nash);
346 pixsd = pixGenerateFromPta(ptas, w, h);
347 nseeds = ptaGetCount(ptas);
348 for (i = 0; i < nseeds; i++) {
349 ptaGetIPt(ptas, i, &x, &y);
350 uval = GET_DATA_BYTE(lines8[y], x);
351 pushWSPixel(lh, rstack, (l_int32)uval, x, y, i);
352 }
353 wshed->ptas = ptas;
354 nasi = numaMakeConstant(1, nseeds); /* indicator array */
355 wshed->nasi = nasi;
356 wshed->nash = nash;
357 wshed->nseeds = nseeds;
358
359 /* Identify minima that are not seeds. Use these 4 steps:
360 * (1) Get the local minima, which can have components
361 * of arbitrary size. This will be a clipping mask.
362 * (2) Get the image of the actual seeds (pixsd)
363 * (3) Remove all elements of the clipping mask that have a seed.
364 * (4) Shrink each of the remaining elements of the minima mask
365 * to a single pixel. */
366 pixLocalExtrema(wshed->pixs, 200, 0, &pixmin, NULL);
367 pixRemoveSeededComponents(pixmin, pixsd, pixmin, 8, 2);
368 pixSelectMinInConnComp(wshed->pixs, pixmin, &ptao, &namh);
369 nother = ptaGetCount(ptao);
370 for (i = 0; i < nother; i++) {
371 ptaGetIPt(ptao, i, &x, &y);
372 uval = GET_DATA_BYTE(lines8[y], x);
373 pushWSPixel(lh, rstack, (l_int32)uval, x, y, nseeds + i);
374 }
375 wshed->namh = namh;
376
377 /* ------------------------------------------------------------ *
378 * Initialize merging lookup tables *
379 * ------------------------------------------------------------ */
380
381 /* nalut should always give the current after-merging index.
382 * links are effectively backpointers: they are numas associated with
383 * a dest index of all indices in nalut that point to that index. */
384 mindepth = wshed->mindepth;
385 nboth = nseeds + nother;
386 arraysize = 2 * nboth;
387 wshed->arraysize = arraysize;
388 nalut = numaMakeSequence(0, 1, arraysize);
389 lut = numaGetIArray(nalut);
390 wshed->lut = lut; /* wshed owns this */
391 links = (NUMA **)LEPT_CALLOC(arraysize, sizeof(NUMA *));
392 wshed->links = links; /* wshed owns this */
393 nindex = nseeds + nother; /* the next unused index value */
394
395 /* ------------------------------------------------------------ *
396 * Fill the basins, using the priority queue *
397 * ------------------------------------------------------------ */
398
399 pixad = pixaCreate(nseeds);
400 wshed->pixad = pixad; /* wshed owns this */
401 nalevels = numaCreate(nseeds);
402 wshed->nalevels = nalevels; /* wshed owns this */
403 L_INFO("nseeds = %d, nother = %d\n", __func__, nseeds, nother);
404 while (lheapGetCount(lh) > 0) {
405 popWSPixel(lh, rstack, &val, &x, &y, &index);
406 /* lept_stderr("x = %d, y = %d, index = %d\n", x, y, index); */
407 ulabel = GET_DATA_FOUR_BYTES(linelab32[y], x);
408 if (ulabel == MAX_LABEL_VALUE)
409 clabel = ulabel;
410 else
411 clabel = lut[ulabel];
412 cindex = lut[index];
413 if (clabel == cindex) continue; /* have already seen this one */
414 if (clabel == MAX_LABEL_VALUE) { /* new one; assign index and try to
415 * propagate to all neighbors */
416 SET_DATA_FOUR_BYTES(linelab32[y], x, cindex);
417 imin = L_MAX(0, y - 1);
418 imax = L_MIN(h - 1, y + 1);
419 jmin = L_MAX(0, x - 1);
420 jmax = L_MIN(w - 1, x + 1);
421 for (i = imin; i <= imax; i++) {
422 for (j = jmin; j <= jmax; j++) {
423 if (i == y && j == x) continue;
424 uval = GET_DATA_BYTE(lines8[i], j);
425 pushWSPixel(lh, rstack, (l_int32)uval, j, i, cindex);
426 }
427 }
428 } else { /* pixel is already labeled (differently); must resolve */
429
430 /* If both indices are seeds, check if the min height is
431 * greater than mindepth. If so, we have two new watersheds;
432 * locate them and assign to both regions a new index
433 * for further waterfill. If not, absorb the shallower
434 * watershed into the deeper one and continue filling it. */
435 pixGetPixel(pixsd, x, y, &uval);
436 if (clabel < nseeds && cindex < nseeds) {
437 wshedGetHeight(wshed, val, clabel, &hlabel);
438 wshedGetHeight(wshed, val, cindex, &hindex);
439 hmin = L_MIN(hlabel, hindex);
440 hmax = L_MAX(hlabel, hindex);
441 if (hmin == hmax) {
442 hmin = hlabel;
443 hmax = hindex;
444 }
445 if (wshed->debug) {
446 lept_stderr("clabel,hlabel = %d,%d\n", clabel, hlabel);
447 lept_stderr("hmin = %d, hmax = %d\n", hmin, hmax);
448 lept_stderr("cindex,hindex = %d,%d\n", cindex, hindex);
449 if (hmin < mindepth)
450 lept_stderr("Too shallow!\n");
451 }
452
453 if (hmin >= mindepth) {
454 debugWshedMerge(wshed, two_new_watersheds,
455 x, y, clabel, cindex);
456 wshedSaveBasin(wshed, cindex, val - 1);
457 wshedSaveBasin(wshed, clabel, val - 1);
458 numaSetValue(nasi, cindex, 0);
459 numaSetValue(nasi, clabel, 0);
460
461 if (wshed->debug) lept_stderr("nindex = %d\n", nindex);
462 debugPrintLUT(lut, nindex, wshed->debug);
463 mergeLookup(wshed, clabel, nindex);
464 debugPrintLUT(lut, nindex, wshed->debug);
465 mergeLookup(wshed, cindex, nindex);
466 debugPrintLUT(lut, nindex, wshed->debug);
467 nindex++;
468 } else /* extraneous seed within seeded basin; absorb */ {
469 debugWshedMerge(wshed, seed_absorbed_into_seeded_basin,
470 x, y, clabel, cindex);
471 }
472 maxhindex = clabel; /* TODO: is this part of above 'else'? */
473 minhindex = cindex;
474 if (hindex > hlabel) {
475 maxhindex = cindex;
476 minhindex = clabel;
477 }
478 mergeLookup(wshed, minhindex, maxhindex);
479 } else if (clabel < nseeds && cindex >= nboth) {
480 /* If one index is a seed and the other is a merge of
481 * 2 watersheds, generate a single watershed. */
482 debugWshedMerge(wshed, one_new_watershed_label,
483 x, y, clabel, cindex);
484 wshedSaveBasin(wshed, clabel, val - 1);
485 numaSetValue(nasi, clabel, 0);
486 mergeLookup(wshed, clabel, cindex);
487 } else if (cindex < nseeds && clabel >= nboth) {
488 debugWshedMerge(wshed, one_new_watershed_index,
489 x, y, clabel, cindex);
490 wshedSaveBasin(wshed, cindex, val - 1);
491 numaSetValue(nasi, cindex, 0);
492 mergeLookup(wshed, cindex, clabel);
493 } else if (clabel < nseeds) { /* cindex from minima; absorb */
494 /* If one index is a seed and the other is from a minimum,
495 * merge the minimum wshed into the seed wshed. */
496 debugWshedMerge(wshed, minima_absorbed_into_seeded_basin,
497 x, y, clabel, cindex);
498 mergeLookup(wshed, cindex, clabel);
499 } else if (cindex < nseeds) { /* clabel from minima; absorb */
500 debugWshedMerge(wshed, minima_absorbed_into_seeded_basin,
501 x, y, clabel, cindex);
502 mergeLookup(wshed, clabel, cindex);
503 } else { /* If neither index is a seed, just merge */
504 debugWshedMerge(wshed, minima_absorbed_by_filler_or_another,
505 x, y, clabel, cindex);
506 mergeLookup(wshed, clabel, cindex);
507 }
508 }
509 }
510
511 #if 0
512 /* Use the indicator array to save any watersheds that fill
513 * to the maximum value. This seems to screw things up! */
514 for (i = 0; i < nseeds; i++) {
515 numaGetIValue(nasi, i, &ival);
516 if (ival == 1) {
517 wshedSaveBasin(wshed, lut[i], val - 1);
518 numaSetValue(nasi, i, 0);
519 }
520 }
521 #endif
522
523 numaDestroy(&nalut);
524 pixDestroy(&pixmin);
525 pixDestroy(&pixsd);
526 ptaDestroy(&ptao);
527 lheapDestroy(&lh, TRUE);
528 lstackDestroy(&rstack, TRUE);
529 return 0;
530 }
531
532
533 /*-----------------------------------------------------------------------*
534 * Helpers *
535 *-----------------------------------------------------------------------*/
536 /*!
537 * \brief wshedSaveBasin()
538 *
539 * \param[in] wshed
540 * \param[in] index index of basin to be located
541 * \param[in] level filling level reached at the time this function
542 * is called
543 * \return 0 if OK, 1 on error
544 *
545 * <pre>
546 * Notes:
547 * (1) This identifies a single watershed. It does not change
548 * the LUT, which must be done subsequently.
549 * (2) The fill level of a basin is taken to be %level - 1.
550 * </pre>
551 */
552 static void
553 wshedSaveBasin(L_WSHED *wshed,
554 l_int32 index,
555 l_int32 level)
556 {
557 BOX *box;
558 PIX *pix;
559
560 if (!wshed) {
561 L_ERROR("wshed not defined\n", __func__);
562 return;
563 }
564
565 if (identifyWatershedBasin(wshed, index, level, &box, &pix) == 0) {
566 pixaAddPix(wshed->pixad, pix, L_INSERT);
567 pixaAddBox(wshed->pixad, box, L_INSERT);
568 numaAddNumber(wshed->nalevels, level - 1);
569 }
570 }
571
572
573 /*!
574 * \brief identifyWatershedBasin()
575 *
576 * \param[in] wshed
577 * \param[in] index index of basin to be located
578 * \param[in] level of basin at point at which the two basins met
579 * \param[out] pbox bounding box of basin
580 * \param[out] ppixd pix of basin, cropped to its bounding box
581 * \return 0 if OK, 1 on error
582 *
583 * <pre>
584 * Notes:
585 * (1) This is a static function, so we assume pixlab, pixs and pixt
586 * exist and are the same size.
587 * (2) It selects all pixels that have the label %index in pixlab
588 * and that have a value in pixs that is less than %level.
589 * (3) It is used whenever two seeded basins meet (typically at a saddle),
590 * or when one seeded basin meets a 'filler'. All identified
591 * basins are saved as a watershed.
592 * </pre>
593 */
594 static l_int32
595 identifyWatershedBasin(L_WSHED *wshed,
596 l_int32 index,
597 l_int32 level,
598 BOX **pbox,
599 PIX **ppixd)
600 {
601 l_int32 imin, imax, jmin, jmax, minx, miny, maxx, maxy;
602 l_int32 bw, bh, i, j, w, h, x, y;
603 l_int32 *lut;
604 l_uint32 label, bval, lval;
605 void **lines8, **linelab32, **linet1;
606 BOX *box;
607 PIX *pixs, *pixt, *pixd;
608 L_QUEUE *lq;
609
610 if (!pbox)
611 return ERROR_INT("&box not defined", __func__, 1);
612 *pbox = NULL;
613 if (!ppixd)
614 return ERROR_INT("&pixd not defined", __func__, 1);
615 *ppixd = NULL;
616 if (!wshed)
617 return ERROR_INT("wshed not defined", __func__, 1);
618
619 /* Make a queue and an auxiliary stack */
620 lq = lqueueCreate(0);
621 lq->stack = lstackCreate(0);
622
623 pixs = wshed->pixs;
624 pixt = wshed->pixt;
625 lines8 = wshed->lines8;
626 linelab32 = wshed->linelab32;
627 linet1 = wshed->linet1;
628 lut = wshed->lut;
629 pixGetDimensions(pixs, &w, &h, NULL);
630
631 /* Prime the queue with the seed pixel for this watershed. */
632 minx = miny = 1000000;
633 maxx = maxy = 0;
634 ptaGetIPt(wshed->ptas, index, &x, &y);
635 pixSetPixel(pixt, x, y, 1);
636 pushNewPixel(lq, x, y, &minx, &maxx, &miny, &maxy);
637 if (wshed->debug) lept_stderr("prime: (x,y) = (%d, %d)\n", x, y);
638
639 /* Each pixel in a spreading breadth-first search is inspected.
640 * It is accepted as part of this watershed, and pushed on
641 * the search queue, if:
642 * (1) It has a label value equal to %index
643 * (2) The pixel value is less than %level, the overflow
644 * height at which the two basins join.
645 * (3) It has not yet been seen in this search. */
646 while (lqueueGetCount(lq) > 0) {
647 popNewPixel(lq, &x, &y);
648 imin = L_MAX(0, y - 1);
649 imax = L_MIN(h - 1, y + 1);
650 jmin = L_MAX(0, x - 1);
651 jmax = L_MIN(w - 1, x + 1);
652 for (i = imin; i <= imax; i++) {
653 for (j = jmin; j <= jmax; j++) {
654 if (j == x && i == y) continue; /* parent */
655 label = GET_DATA_FOUR_BYTES(linelab32[i], j);
656 if (label == MAX_LABEL_VALUE || lut[label] != index) continue;
657 bval = GET_DATA_BIT(linet1[i], j);
658 if (bval == 1) continue; /* already seen */
659 lval = GET_DATA_BYTE(lines8[i], j);
660 if (lval >= level) continue; /* too high */
661 SET_DATA_BIT(linet1[i], j);
662 pushNewPixel(lq, j, i, &minx, &maxx, &miny, &maxy);
663 }
664 }
665 }
666
667 /* Extract the box and pix, and clear pixt */
668 bw = maxx - minx + 1;
669 bh = maxy - miny + 1;
670 box = boxCreate(minx, miny, bw, bh);
671 pixd = pixClipRectangle(pixt, box, NULL);
672 pixRasterop(pixt, minx, miny, bw, bh, PIX_SRC ^ PIX_DST, pixd, 0, 0);
673 *pbox = box;
674 *ppixd = pixd;
675
676 lqueueDestroy(&lq, 1);
677 return 0;
678 }
679
680
681 /*!
682 * \brief mergeLookup()
683 *
684 * \param[in] wshed
685 * \param[in] sindex primary index being changed in the merge
686 * \param[in] dindex index that %sindex will point to after the merge
687 * \return 0 if OK, 1 on error
688 *
689 * <pre>
690 * Notes:
691 * (1) The links are a sparse array of Numas showing current back-links.
692 * The lut gives the current index (of the seed or the minima
693 * for the wshed in which it is located.
694 * (2) Think of each entry in the lut. There are two types:
695 * owner: lut[index] = index
696 * redirect: lut[index] != index
697 * (3) This is called each time a merge occurs. It puts the lut
698 * and backlinks in a canonical form after the merge, where
699 * all entries in the lut point to the current "owner", which
700 * has all backlinks. That is, every "redirect" in the lut
701 * points to an "owner". The lut always gives the index of
702 * the current owner.
703 * </pre>
704 */
705 static l_int32
706 mergeLookup(L_WSHED *wshed,
707 l_int32 sindex,
708 l_int32 dindex)
709 {
710 l_int32 i, n, size, index;
711 l_int32 *lut;
712 NUMA *na;
713 NUMA **links;
714
715 if (!wshed)
716 return ERROR_INT("wshed not defined", __func__, 1);
717 size = wshed->arraysize;
718 if (sindex < 0 || sindex >= size)
719 return ERROR_INT("invalid sindex", __func__, 1);
720 if (dindex < 0 || dindex >= size)
721 return ERROR_INT("invalid dindex", __func__, 1);
722
723 /* Redirect links in the lut */
724 n = 0;
725 links = wshed->links;
726 lut = wshed->lut;
727 if ((na = links[sindex]) != NULL) {
728 n = numaGetCount(na);
729 for (i = 0; i < n; i++) {
730 numaGetIValue(na, i, &index);
731 lut[index] = dindex;
732 }
733 }
734 lut[sindex] = dindex;
735
736 /* Shift the backlink arrays from sindex to dindex.
737 * sindex should have no backlinks because all entries in the
738 * lut that were previously pointing to it have been redirected
739 * to dindex. */
740 if (!links[dindex])
741 links[dindex] = numaCreate(n);
742 numaJoin(links[dindex], links[sindex], 0, -1);
743 numaAddNumber(links[dindex], sindex);
744 numaDestroy(&links[sindex]);
745
746 return 0;
747 }
748
749
750 /*!
751 * \brief wshedGetHeight()
752 *
753 * \param[in] wshed array of current indices
754 * \param[in] val value of current pixel popped off queue
755 * \param[in] label of pixel or 32 bpp label image
756 * \param[out] pheight height of current value from seed
757 * or minimum of watershed
758 * \return 0 if OK, 1 on error
759 *
760 * <pre>
761 * Notes:
762 * (1) It is only necessary to find the height for a watershed
763 * that is indexed by a seed or a minima. This function should
764 * not be called on a finished watershed (that continues to fill).
765 * </pre>
766 */
767 static l_int32
768 wshedGetHeight(L_WSHED *wshed,
769 l_int32 val,
770 l_int32 label,
771 l_int32 *pheight)
772 {
773 l_int32 minval;
774
775 if (!pheight)
776 return ERROR_INT("&height not defined", __func__, 1);
777 *pheight = 0;
778 if (!wshed)
779 return ERROR_INT("wshed not defined", __func__, 1);
780
781 if (label < wshed->nseeds)
782 numaGetIValue(wshed->nash, label, &minval);
783 else if (label < wshed->nseeds + wshed->nother)
784 numaGetIValue(wshed->namh, label, &minval);
785 else
786 return ERROR_INT("finished watershed; should not call", __func__, 1);
787
788 *pheight = val - minval;
789 return 0;
790 }
791
792
793 /*
794 * \brief pushNewPixel()
795 *
796 * \param[in] lqueue
797 * \param[in] x, y pixel coordinates
798 * \param[out] pminx, pmaxx, pminy, pmaxy bounding box update
799 * \return void
800 *
801 * <pre>
802 * Notes:
803 * (1) This is a wrapper for adding a NewPixel to a queue, which
804 * updates the bounding box for all pixels on that queue and
805 * uses the storage stack to retrieve a NewPixel.
806 * </pre>
807 */
808 static void
809 pushNewPixel(L_QUEUE *lq,
810 l_int32 x,
811 l_int32 y,
812 l_int32 *pminx,
813 l_int32 *pmaxx,
814 l_int32 *pminy,
815 l_int32 *pmaxy)
816 {
817 L_NEWPIXEL *np;
818
819 if (!lq) {
820 L_ERROR("queue not defined\n", __func__);
821 return;
822 }
823
824 /* Adjust bounding box */
825 *pminx = L_MIN(*pminx, x);
826 *pmaxx = L_MAX(*pmaxx, x);
827 *pminy = L_MIN(*pminy, y);
828 *pmaxy = L_MAX(*pmaxy, y);
829
830 /* Get a newpixel to use */
831 if (lstackGetCount(lq->stack) > 0)
832 np = (L_NEWPIXEL *)lstackRemove(lq->stack);
833 else
834 np = (L_NEWPIXEL *)LEPT_CALLOC(1, sizeof(L_NEWPIXEL));
835
836 np->x = x;
837 np->y = y;
838 lqueueAdd(lq, np);
839 }
840
841
842 /*
843 * \brief popNewPixel()
844 *
845 * \param[in] lqueue
846 * \param[out] px, py pixel coordinates
847 * \return void
848 *
849 * <pre>
850 * Notes:
851 * (1) This is a wrapper for removing a NewPixel from a queue,
852 * which returns the pixel coordinates and saves the NewPixel
853 * on the storage stack.
854 * </pre>
855 */
856 static void
857 popNewPixel(L_QUEUE *lq,
858 l_int32 *px,
859 l_int32 *py)
860 {
861 L_NEWPIXEL *np;
862
863 if (!lq) {
864 L_ERROR("lqueue not defined\n", __func__);
865 return;
866 }
867
868 if ((np = (L_NEWPIXEL *)lqueueRemove(lq)) == NULL)
869 return;
870 *px = np->x;
871 *py = np->y;
872 lstackAdd(lq->stack, np); /* save for re-use */
873 }
874
875
876 /*
877 * \brief pushWSPixel()
878 *
879 * \param[in] lh priority queue
880 * \param[in] stack of reusable WSPixels
881 * \param[in] val pixel value: used for ordering the heap
882 * \param[in] x, y pixel coordinates
883 * \param[in] index label for set to which pixel belongs
884 * \return void
885 *
886 * <pre>
887 * Notes:
888 * (1) This is a wrapper for adding a WSPixel to a heap. It
889 * uses the storage stack to retrieve a WSPixel.
890 * </pre>
891 */
892 static void
893 pushWSPixel(L_HEAP *lh,
894 L_STACK *stack,
895 l_int32 val,
896 l_int32 x,
897 l_int32 y,
898 l_int32 index)
899 {
900 L_WSPIXEL *wsp;
901
902 if (!lh) {
903 L_ERROR("heap not defined\n", __func__);
904 return;
905 }
906 if (!stack) {
907 L_ERROR("stack not defined\n", __func__);
908 return;
909 }
910
911 /* Get a wspixel to use */
912 if (lstackGetCount(stack) > 0)
913 wsp = (L_WSPIXEL *)lstackRemove(stack);
914 else
915 wsp = (L_WSPIXEL *)LEPT_CALLOC(1, sizeof(L_WSPIXEL));
916
917 wsp->val = (l_float32)val;
918 wsp->x = x;
919 wsp->y = y;
920 wsp->index = index;
921 lheapAdd(lh, wsp);
922 }
923
924
925 /*
926 * \brief popWSPixel()
927 *
928 * \param[in] lh priority queue
929 * \param[in] stack of reusable WSPixels
930 * \param[out] pval pixel value
931 * \param[out] px, py pixel coordinates
932 * \param[out] pindex label for set to which pixel belongs
933 * \return void
934 *
935 * <pre>
936 * Notes:
937 * (1) This is a wrapper for removing a WSPixel from a heap,
938 * which returns the WSPixel data and saves the WSPixel
939 * on the storage stack.
940 * </pre>
941 */
942 static void
943 popWSPixel(L_HEAP *lh,
944 L_STACK *stack,
945 l_int32 *pval,
946 l_int32 *px,
947 l_int32 *py,
948 l_int32 *pindex)
949 {
950 L_WSPIXEL *wsp;
951
952 if (!lh) {
953 L_ERROR("lheap not defined\n", __func__);
954 return;
955 }
956 if (!stack) {
957 L_ERROR("stack not defined\n", __func__);
958 return;
959 }
960 if (!pval || !px || !py || !pindex) {
961 L_ERROR("data can't be returned\n", __func__);
962 return;
963 }
964
965 if ((wsp = (L_WSPIXEL *)lheapRemove(lh)) == NULL)
966 return;
967 *pval = (l_int32)wsp->val;
968 *px = wsp->x;
969 *py = wsp->y;
970 *pindex = wsp->index;
971 lstackAdd(stack, wsp); /* save for re-use */
972 }
973
974
975 static void
976 debugPrintLUT(l_int32 *lut,
977 l_int32 size,
978 l_int32 debug)
979 {
980 l_int32 i;
981
982 if (!debug) return;
983 lept_stderr("lut: ");
984 for (i = 0; i < size; i++)
985 lept_stderr( "%d ", lut[i]);
986 lept_stderr("\n");
987 }
988
989
990 static void
991 debugWshedMerge(L_WSHED *wshed,
992 char *descr,
993 l_int32 x,
994 l_int32 y,
995 l_int32 label,
996 l_int32 index)
997 {
998 if (!wshed || (wshed->debug == 0))
999 return;
1000 lept_stderr("%s:\n", descr);
1001 lept_stderr(" (x, y) = (%d, %d)\n", x, y);
1002 lept_stderr(" clabel = %d, cindex = %d\n", label, index);
1003 }
1004
1005
1006 /*-----------------------------------------------------------------------*
1007 * Output *
1008 *-----------------------------------------------------------------------*/
1009 /*!
1010 * \brief wshedBasins()
1011 *
1012 * \param[in] wshed
1013 * \param[out] ppixa [optional] mask of watershed basins
1014 * \param[out] pnalevels [optional] watershed levels
1015 * \return 0 if OK, 1 on error
1016 */
1017 l_ok
1018 wshedBasins(L_WSHED *wshed,
1019 PIXA **ppixa,
1020 NUMA **pnalevels)
1021 {
1022 if (!wshed)
1023 return ERROR_INT("wshed not defined", __func__, 1);
1024
1025 if (ppixa)
1026 *ppixa = pixaCopy(wshed->pixad, L_CLONE);
1027 if (pnalevels)
1028 *pnalevels = numaClone(wshed->nalevels);
1029 return 0;
1030 }
1031
1032
1033 /*!
1034 * \brief wshedRenderFill()
1035 *
1036 * \param[in] wshed
1037 * \return pixd initial image with all basins filled, or NULL on error
1038 */
1039 PIX *
1040 wshedRenderFill(L_WSHED *wshed)
1041 {
1042 l_int32 i, n, level, bx, by;
1043 NUMA *na;
1044 PIX *pix, *pixd;
1045 PIXA *pixa;
1046
1047 if (!wshed)
1048 return (PIX *)ERROR_PTR("wshed not defined", __func__, NULL);
1049
1050 wshedBasins(wshed, &pixa, &na);
1051 pixd = pixCopy(NULL, wshed->pixs);
1052 n = pixaGetCount(pixa);
1053 for (i = 0; i < n; i++) {
1054 pix = pixaGetPix(pixa, i, L_CLONE);
1055 pixaGetBoxGeometry(pixa, i, &bx, &by, NULL, NULL);
1056 numaGetIValue(na, i, &level);
1057 pixPaintThroughMask(pixd, pix, bx, by, level);
1058 pixDestroy(&pix);
1059 }
1060
1061 pixaDestroy(&pixa);
1062 numaDestroy(&na);
1063 return pixd;
1064 }
1065
1066
1067 /*!
1068 * \brief wshedRenderColors()
1069 *
1070 * \param[in] wshed
1071 * \return pixd initial image with all basins filled, or null on error
1072 */
1073 PIX *
1074 wshedRenderColors(L_WSHED *wshed)
1075 {
1076 l_int32 w, h;
1077 PIX *pixg, *pixt, *pixc, *pixm, *pixd;
1078 PIXA *pixa;
1079
1080 if (!wshed)
1081 return (PIX *)ERROR_PTR("wshed not defined", __func__, NULL);
1082
1083 wshedBasins(wshed, &pixa, NULL);
1084 pixg = pixCopy(NULL, wshed->pixs);
1085 pixGetDimensions(wshed->pixs, &w, &h, NULL);
1086 pixd = pixConvertTo32(pixg);
1087 pixt = pixaDisplayRandomCmap(pixa, w, h);
1088 pixc = pixConvertTo32(pixt);
1089 pixm = pixaDisplay(pixa, w, h);
1090 pixCombineMasked(pixd, pixc, pixm);
1091
1092 pixDestroy(&pixg);
1093 pixDestroy(&pixt);
1094 pixDestroy(&pixc);
1095 pixDestroy(&pixm);
1096 pixaDestroy(&pixa);
1097 return pixd;
1098 }