Leptonica 1.68
C Image Processing Library
|
00001 /*====================================================================* 00002 - Copyright (C) 2001 Leptonica. All rights reserved. 00003 - This software is distributed in the hope that it will be 00004 - useful, but with NO WARRANTY OF ANY KIND. 00005 - No author or distributor accepts responsibility to anyone for the 00006 - consequences of using this software, or for whether it serves any 00007 - particular purpose or works at all, unless he or she says so in 00008 - writing. Everyone is granted permission to copy, modify and 00009 - redistribute this source code, for commercial or non-commercial 00010 - purposes, with the following restrictions: (1) the origin of this 00011 - source code must not be misrepresented; (2) modified versions must 00012 - be plainly marked as such; and (3) this notice may not be removed 00013 - or altered from any source or modified source distribution. 00014 *====================================================================*/ 00015 00016 /* 00017 * watershed.c 00018 * 00019 * Top-level 00020 * L_WSHED *wshedCreate() 00021 * void wshedDestroy() 00022 * l_int32 wshedApply() 00023 * 00024 * Helpers 00025 * static l_int32 identifyWatershedBasin() 00026 * static l_int32 mergeLookup() 00027 * static l_int32 wshedGetHeight() 00028 * static void pushNewPixel() 00029 * static void popNewPixel() 00030 * static void pushWSPixel() 00031 * static void popWSPixel() 00032 * static void debugPrintLUT() 00033 * static void debugWshedMerge() 00034 * 00035 * Output 00036 * l_int32 wshedBasins() 00037 * PIX *wshedRenderFill() 00038 * PIX *wshedRenderColors() 00039 * 00040 * The watershed function identifies the "catch basins" of the input 00041 * 8 bpp image, with respect to the specified seeds or "markers". 00042 * The use is in segmentation, but the selection of the markers is 00043 * critical to getting meaningful results. 00044 * 00045 * How are the markers selected? You can't simply use the local 00046 * minima, because a typical image has sufficient noise so that 00047 * a useful catch basin can easily have multiple local minima. However 00048 * they are selected, the question for the watershed function is 00049 * how to handle local minima that are not markers. The reason 00050 * this is important is because of the algorithm used to find the 00051 * watersheds, which is roughly like this: 00052 * 00053 * (1) Identify the markers and the local minima, and enter them 00054 * into a priority queue based on the pixel value. Each marker 00055 * is shrunk to a single pixel, if necessary, before the 00056 * operation starts. 00057 * (2) Feed the priority queue with neighbors of pixels that are 00058 * popped off the queue. Each of these queue pixels is labelled 00059 * with the index value of its parent. 00060 * (3) Each pixel is also labelled, in a 32-bit image, with the marker 00061 * or local minimum index, from which it was originally derived. 00062 * (4) There are actually 3 classes of labels: seeds, minima, and 00063 * fillers. The fillers are labels of regions that have already 00064 * been identified as watersheds and are continuing to fill, for 00065 * the purpose of finding higher watersheds. 00066 * (5) When a pixel is popped that has already been labelled in the 00067 * 32-bit image and that label differs from the label of its 00068 * parent (stored in the queue pixel), a boundary has been crossed. 00069 * There are several cases: 00070 * (a) Both parents are derived from markers but at least one 00071 * is not deep enough to become a watershed. Absorb the 00072 * shallower basin into the deeper one, fixing the LUT to 00073 * redirect the shallower index to the deeper one. 00074 * (b) Both parents are derived from markers and both are deep 00075 * enough. Identify and save the watershed for each marker. 00076 * (c) One parent was derived from a marker and the other from 00077 * a minima: absorb the minima basin into the marker basin. 00078 * (d) One parent was derived from a marker and the other is 00079 * a filler: identify and save the watershed for the marker. 00080 * (e) Both parents are derived from minima: merge them. 00081 * (f) One parent is a filler and the other is derived from a 00082 * minima: merge the minima into the filler. 00083 * (6) The output of the watershed operation consists of: 00084 * - a pixa of the basins 00085 * - a pta of the markers 00086 * - a numa of the watershed levels 00087 * 00088 * Typical usage: 00089 * L_WShed *wshed = wshedCreate(pixs, pixseed, mindepth, 0); 00090 * wshedApply(wshed); 00091 * 00092 * wshedBasins(wshed, &pixa, &nalevels); 00093 * ... do something with pixa, nalevels ... 00094 * pixaDestroy(&pixa); 00095 * numaDestroy(&nalevels); 00096 * 00097 * Pix *pixd = wshedRenderFill(wshed); 00098 * 00099 * wshedDestroy(&wshed); 00100 */ 00101 00102 #include <stdio.h> 00103 #include <stdlib.h> 00104 #include "allheaders.h" 00105 00106 #ifndef NO_CONSOLE_IO 00107 #define DEBUG_WATERSHED 0 00108 #endif /* ~NO_CONSOLE_IO */ 00109 00110 static const l_uint32 MAX_LABEL_VALUE = 0x7fffffff; /* largest l_int32 */ 00111 00112 struct L_NewPixel 00113 { 00114 l_int32 x; 00115 l_int32 y; 00116 }; 00117 typedef struct L_NewPixel L_NEWPIXEL; 00118 00119 struct L_WSPixel 00120 { 00121 l_float32 val; /* pixel value */ 00122 l_int32 x; 00123 l_int32 y; 00124 l_int32 index; /* label for set to which pixel belongs */ 00125 }; 00126 typedef struct L_WSPixel L_WSPIXEL; 00127 00128 00129 /* Static functions for obtaining bitmap of watersheds */ 00130 static void wshedSaveBasin(L_WSHED *wshed, l_int32 index, l_int32 level); 00131 00132 static l_int32 identifyWatershedBasin(L_WSHED *wshed, 00133 l_int32 index, l_int32 level, 00134 BOX **pbox, PIX **ppixd); 00135 00136 /* Static function for merging lut and backlink arrays */ 00137 static l_int32 mergeLookup(L_WSHED *wshed, l_int32 sindex, l_int32 dindex); 00138 00139 /* Static function for finding the height of the current pixel 00140 above its seed or minima in the watershed. */ 00141 static l_int32 wshedGetHeight(L_WSHED *wshed, l_int32 val, l_int32 label, 00142 l_int32 *pheight); 00143 00144 /* Static accessors for NewPixel on a queue */ 00145 static void pushNewPixel(L_QUEUE *lq, l_int32 x, l_int32 y, 00146 l_int32 *pminx, l_int32 *pmaxx, 00147 l_int32 *pminy, l_int32 *pmaxy); 00148 static void popNewPixel(L_QUEUE *lq, l_int32 *px, l_int32 *py); 00149 00150 /* Static accessors for WSPixel on a heap */ 00151 static void pushWSPixel(L_HEAP *lh, L_STACK *stack, l_int32 val, 00152 l_int32 x, l_int32 y, l_int32 index); 00153 static void popWSPixel(L_HEAP *lh, L_STACK *stack, l_int32 *pval, 00154 l_int32 *px, l_int32 *py, l_int32 *pindex); 00155 00156 /* Static debug print output */ 00157 static void debugPrintLUT(l_int32 *lut, l_int32 size, l_int32 debug); 00158 00159 static void debugWshedMerge(L_WSHED *wshed, char *descr, l_int32 x, 00160 l_int32 y, l_int32 label, l_int32 index); 00161 00162 00163 /*-----------------------------------------------------------------------* 00164 * Top-level watershed * 00165 *-----------------------------------------------------------------------*/ 00166 /*! 00167 * wshedCreate() 00168 * 00169 * Input: pixs (8 bpp source) 00170 * pixm (1 bpp 'marker' seed) 00171 * mindepth (minimum depth; anything less is not saved) 00172 * debugflag (1 for debug output) 00173 * Return: WShed, or null on error 00174 * 00175 * Notes: 00176 * (1) It is not necessary for the fg pixels in the seed image 00177 * be at minima, or that they be isolated. We extract a 00178 * single pixel from each connected component, and a seed 00179 * anywhere in a watershed will eventually label the watershed 00180 * when the filling level reaches it. 00181 * (2) Set mindepth to some value to ignore noise in pixs that 00182 * can create small local minima. Any watershed shallower 00183 * than mindepth, even if it has a seed, will not be saved; 00184 * It will either be incorporated in another watershed or 00185 * eliminated. 00186 */ 00187 L_WSHED * 00188 wshedCreate(PIX *pixs, 00189 PIX *pixm, 00190 l_int32 mindepth, 00191 l_int32 debugflag) 00192 { 00193 l_int32 w, h; 00194 L_WSHED *wshed; 00195 00196 PROCNAME("wshedCreate"); 00197 00198 if (!pixs) 00199 return (L_WSHED *)ERROR_PTR("pixs is not defined", procName, NULL); 00200 if (pixGetDepth(pixs) != 8) 00201 return (L_WSHED *)ERROR_PTR("pixs is not 8 bpp", procName, NULL); 00202 if (!pixm) 00203 return (L_WSHED *)ERROR_PTR("pixm is not defined", procName, NULL); 00204 if (pixGetDepth(pixm) != 1) 00205 return (L_WSHED *)ERROR_PTR("pixm is not 1 bpp", procName, NULL); 00206 pixGetDimensions(pixs, &w, &h, NULL); 00207 if (pixGetWidth(pixm) != w || pixGetHeight(pixm) != h) 00208 return (L_WSHED *)ERROR_PTR("pixs/m sizes are unequal", procName, NULL); 00209 00210 if ((wshed = (L_WSHED *)CALLOC(1, sizeof(L_WSHED))) == NULL) 00211 return (L_WSHED *)ERROR_PTR("wshed not made", procName, NULL); 00212 00213 wshed->pixs = pixClone(pixs); 00214 wshed->pixm = pixClone(pixm); 00215 wshed->mindepth = L_MAX(1, mindepth); 00216 wshed->pixlab = pixCreate(w, h, 32); 00217 pixSetAllArbitrary(wshed->pixlab, MAX_LABEL_VALUE); 00218 wshed->pixt = pixCreate(w, h, 1); 00219 wshed->lines8 = pixGetLinePtrs(pixs, NULL); 00220 wshed->linem1 = pixGetLinePtrs(pixm, NULL); 00221 wshed->linelab32 = pixGetLinePtrs(wshed->pixlab, NULL); 00222 wshed->linet1 = pixGetLinePtrs(wshed->pixt, NULL); 00223 wshed->debug = debugflag; 00224 return wshed; 00225 } 00226 00227 00228 /*! 00229 * wshedDestroy() 00230 * 00231 * Input: &wshed (<will be set to null before returning>) 00232 * Return: void 00233 */ 00234 void 00235 wshedDestroy(L_WSHED **pwshed) 00236 { 00237 l_int32 i; 00238 L_WSHED *wshed; 00239 00240 PROCNAME("wshedDestroy"); 00241 00242 if (pwshed == NULL) { 00243 L_WARNING("ptr address is null!", procName); 00244 return; 00245 } 00246 00247 if ((wshed = *pwshed) == NULL) 00248 return; 00249 00250 pixDestroy(&wshed->pixs); 00251 pixDestroy(&wshed->pixm); 00252 pixDestroy(&wshed->pixlab); 00253 pixDestroy(&wshed->pixt); 00254 if (wshed->lines8) FREE(wshed->lines8); 00255 if (wshed->linem1) FREE(wshed->linem1); 00256 if (wshed->linelab32) FREE(wshed->linelab32); 00257 if (wshed->linet1) FREE(wshed->linet1); 00258 pixaDestroy(&wshed->pixad); 00259 ptaDestroy(&wshed->ptas); 00260 numaDestroy(&wshed->nash); 00261 numaDestroy(&wshed->nasi); 00262 numaDestroy(&wshed->namh); 00263 numaDestroy(&wshed->nalevels); 00264 if (wshed->lut) 00265 FREE(wshed->lut); 00266 if (wshed->links) { 00267 for (i = 0; i < wshed->arraysize; i++) 00268 numaDestroy(&wshed->links[i]); 00269 FREE(wshed->links); 00270 } 00271 FREE(wshed); 00272 *pwshed = NULL; 00273 return; 00274 } 00275 00276 00277 /*! 00278 * wshedApply() 00279 * 00280 * Input: wshed (generated from wshedCreate()) 00281 * Return: 0 if OK, 1 on error 00282 * 00283 * Iportant note: 00284 * (1) This is buggy. It seems to locate watersheds that are 00285 * duplicates. The watershed extraction after complete fill 00286 * grabs some regions belonging to existing watersheds. 00287 * See prog/watershedtest.c for testing. 00288 */ 00289 l_int32 00290 wshedApply(L_WSHED *wshed) 00291 { 00292 char two_new_watersheds[] = "Two new watersheds"; 00293 char seed_absorbed_into_seeded_basin[] = "Seed absorbed into seeded basin"; 00294 char one_new_watershed_label[] = "One new watershed (label)"; 00295 char one_new_watershed_index[] = "One new watershed (index)"; 00296 char minima_absorbed_into_seeded_basin[] = 00297 "Minima absorbed into seeded basin"; 00298 char minima_absorbed_by_filler_or_another[] = 00299 "Minima absorbed by filler or another"; 00300 l_int32 nseeds, nother, nboth, arraysize; 00301 l_int32 i, j, val, x, y, w, h, index, mindepth; 00302 l_int32 imin, imax, jmin, jmax, cindex, clabel, nindex; 00303 l_int32 hindex, hlabel, hmin, hmax, minhindex, maxhindex; 00304 l_int32 *lut; 00305 l_uint32 ulabel, uval; 00306 void **lines8, **linelab32; 00307 NUMA *nalut, *nalevels, *nash, *namh, *nasi; 00308 NUMA **links; 00309 L_HEAP *lh; 00310 PIX *pixmin, *pixsd; 00311 PIXA *pixad; 00312 L_STACK *rstack; 00313 PTA *ptas, *ptao; 00314 00315 PROCNAME("wshedApply"); 00316 00317 if (!wshed) 00318 return ERROR_INT("wshed not defined", procName, 1); 00319 00320 /* ------------------------------------------------------------ * 00321 * Initialize priority queue and pixlab with seeds and minima * 00322 * ------------------------------------------------------------ */ 00323 00324 lh = lheapCreate(0, L_SORT_INCREASING); /* remove lowest values first */ 00325 rstack = lstackCreate(0); /* for reusing the WSPixels */ 00326 pixGetDimensions(wshed->pixs, &w, &h, NULL); 00327 lines8 = wshed->lines8; /* wshed owns this */ 00328 linelab32 = wshed->linelab32; /* ditto */ 00329 00330 /* Identify seed (marker) pixels, 1 for each c.c. in pixm */ 00331 ptas = pixSelectMinInConnComp(wshed->pixs, wshed->pixm, &nash); 00332 pixsd = pixGenerateFromPta(ptas, w, h); 00333 nseeds = ptaGetCount(ptas); 00334 for (i = 0; i < nseeds; i++) { 00335 ptaGetIPt(ptas, i, &x, &y); 00336 uval = GET_DATA_BYTE(lines8[y], x); 00337 pushWSPixel(lh, rstack, (l_int32)uval, x, y, i); 00338 } 00339 wshed->ptas = ptas; 00340 nasi = numaMakeConstant(1, nseeds); /* indicator array */ 00341 wshed->nasi = nasi; 00342 wshed->nash = nash; 00343 wshed->nseeds = nseeds; 00344 00345 /* Identify minima that are not seeds. Use these 4 steps: 00346 * (1) Get the local minima, which can have components 00347 * of arbitrary size. This will be a clipping mask. 00348 * (2) Get the image of the actual seeds (pixsd) 00349 * (3) Remove all elements of the clipping mask that have a seed. 00350 * (4) Shrink each of the remaining elements of the minima mask 00351 * to a single pixel. */ 00352 pixLocalExtrema(wshed->pixs, 200, 0, &pixmin, NULL); 00353 pixRemoveSeededComponents(pixmin, pixsd, pixmin, 8, 2); 00354 ptao = pixSelectMinInConnComp(wshed->pixs, pixmin, &namh); 00355 nother = ptaGetCount(ptao); 00356 for (i = 0; i < nother; i++) { 00357 ptaGetIPt(ptao, i, &x, &y); 00358 uval = GET_DATA_BYTE(lines8[y], x); 00359 pushWSPixel(lh, rstack, (l_int32)uval, x, y, nseeds + i); 00360 } 00361 wshed->namh = namh; 00362 00363 /* ------------------------------------------------------------ * 00364 * Initialize merging lookup tables * 00365 * ------------------------------------------------------------ */ 00366 00367 /* nalut should always give the current after-merging index. 00368 * links are effectively backpointers: they are numas associated with 00369 * a dest index of all indices in nalut that point to that index. */ 00370 mindepth = wshed->mindepth; 00371 nboth = nseeds + nother; 00372 arraysize = 2 * nboth; 00373 wshed->arraysize = arraysize; 00374 nalut = numaMakeSequence(0, 1, arraysize); 00375 lut = numaGetIArray(nalut); 00376 wshed->lut = lut; /* wshed owns this */ 00377 links = (NUMA **)CALLOC(arraysize, sizeof(NUMA *)); 00378 wshed->links = links; /* wshed owns this */ 00379 nindex = nseeds + nother; /* the next unused index value */ 00380 00381 /* ------------------------------------------------------------ * 00382 * Fill the basins, using the priority queue * 00383 * ------------------------------------------------------------ */ 00384 00385 pixad = pixaCreate(nseeds); 00386 wshed->pixad = pixad; /* wshed owns this */ 00387 nalevels = numaCreate(nseeds); 00388 wshed->nalevels = nalevels; /* wshed owns this */ 00389 L_INFO_INT2("nseeds = %d, nother = %d\n", procName, nseeds, nother); 00390 while (lheapGetCount(lh) > 0) { 00391 popWSPixel(lh, rstack, &val, &x, &y, &index); 00392 /* fprintf(stderr, "x = %d, y = %d, index = %d\n", x, y, index); */ 00393 ulabel = GET_DATA_FOUR_BYTES(linelab32[y], x); 00394 if (ulabel == MAX_LABEL_VALUE) 00395 clabel = ulabel; 00396 else 00397 clabel = lut[ulabel]; 00398 cindex = lut[index]; 00399 if (clabel == cindex) continue; /* have already seen this one */ 00400 if (clabel == MAX_LABEL_VALUE) { /* new one; assign index and try to 00401 * propagate to all neighbors */ 00402 SET_DATA_FOUR_BYTES(linelab32[y], x, cindex); 00403 imin = L_MAX(0, y - 1); 00404 imax = L_MIN(h - 1, y + 1); 00405 jmin = L_MAX(0, x - 1); 00406 jmax = L_MIN(w - 1, x + 1); 00407 for (i = imin; i <= imax; i++) { 00408 for (j = jmin; j <= jmax; j++) { 00409 if (i == y && j == x) continue; 00410 uval = GET_DATA_BYTE(lines8[i], j); 00411 pushWSPixel(lh, rstack, (l_int32)uval, j, i, cindex); 00412 } 00413 } 00414 } 00415 else { /* this pixel is already labeled (differently); must resolve */ 00416 00417 /* If both indices are seeds, check if the min height is 00418 * greater than mindepth. If so, we have two new watersheds; 00419 * locate them and assign to both regions a new index 00420 * for further waterfill. If not, absorb the shallower 00421 * watershed into the deeper one and continue filling it. */ 00422 pixGetPixel(pixsd, x, y, &uval); 00423 if (clabel < nseeds && cindex < nseeds) { 00424 wshedGetHeight(wshed, val, clabel, &hlabel); 00425 wshedGetHeight(wshed, val, cindex, &hindex); 00426 hmin = L_MIN(hlabel, hindex); 00427 hmax = L_MAX(hlabel, hindex); 00428 if (hmin == hmax) { 00429 hmin = hlabel; 00430 hmax = hindex; 00431 } 00432 if (wshed->debug) { 00433 fprintf(stderr, "clabel,hlabel = %d,%d\n", clabel, hlabel); 00434 fprintf(stderr, "hmin = %d, hmax = %d\n", hmin, hmax); 00435 fprintf(stderr, "cindex,hindex = %d,%d\n", cindex, hindex); 00436 if (hmin < mindepth) 00437 fprintf(stderr, "Too shallow!\n"); 00438 } 00439 00440 if (hmin >= mindepth) { 00441 debugWshedMerge(wshed, two_new_watersheds, 00442 x, y, clabel, cindex); 00443 wshedSaveBasin(wshed, cindex, val - 1); 00444 wshedSaveBasin(wshed, clabel, val - 1); 00445 numaSetValue(nasi, cindex, 0); 00446 numaSetValue(nasi, clabel, 0); 00447 00448 if (wshed->debug) fprintf(stderr, "nindex = %d\n", nindex); 00449 debugPrintLUT(lut, nindex, wshed->debug); 00450 mergeLookup(wshed, clabel, nindex); 00451 debugPrintLUT(lut, nindex, wshed->debug); 00452 mergeLookup(wshed, cindex, nindex); 00453 debugPrintLUT(lut, nindex, wshed->debug); 00454 nindex++; 00455 } 00456 else /* extraneous seed within seeded basin; absorb */ 00457 debugWshedMerge(wshed, seed_absorbed_into_seeded_basin, 00458 x, y, clabel, cindex); 00459 maxhindex = clabel; 00460 minhindex = cindex; 00461 if (hindex > hlabel) { 00462 maxhindex = cindex; 00463 minhindex = clabel; 00464 } 00465 mergeLookup(wshed, minhindex, maxhindex); 00466 } 00467 00468 /* If one index is a seed and the other is a merge of 00469 * 2 watersheds, generate a single watershed. */ 00470 else if (clabel < nseeds && cindex >= nboth) { 00471 debugWshedMerge(wshed, one_new_watershed_label, 00472 x, y, clabel, cindex); 00473 wshedSaveBasin(wshed, clabel, val - 1); 00474 numaSetValue(nasi, clabel, 0); 00475 mergeLookup(wshed, clabel, cindex); 00476 } 00477 else if (cindex < nseeds && clabel >= nboth) { 00478 debugWshedMerge(wshed, one_new_watershed_index, 00479 x, y, clabel, cindex); 00480 wshedSaveBasin(wshed, cindex, val - 1); 00481 numaSetValue(nasi, cindex, 0); 00482 mergeLookup(wshed, cindex, clabel); 00483 } 00484 /* If one index is a seed and the other is from a minimum, 00485 * merge the minimum wshed into the seed wshed. */ 00486 else if (clabel < nseeds) { /* cindex from minima; absorb */ 00487 debugWshedMerge(wshed, minima_absorbed_into_seeded_basin, 00488 x, y, clabel, cindex); 00489 mergeLookup(wshed, cindex, clabel); 00490 } 00491 else if (cindex < nseeds) { /* clabel from minima; absorb */ 00492 debugWshedMerge(wshed, minima_absorbed_into_seeded_basin, 00493 x, y, clabel, cindex); 00494 mergeLookup(wshed, clabel, cindex); 00495 } 00496 /* If neither index is a seed, just merge */ 00497 else { 00498 debugWshedMerge(wshed, minima_absorbed_by_filler_or_another, 00499 x, y, clabel, cindex); 00500 mergeLookup(wshed, clabel, cindex); 00501 } 00502 } 00503 } 00504 00505 #if 0 00506 /* Use the indicator array to save any watersheds that fill 00507 * to the maximum value. This seems to screw things up! */ 00508 for (i = 0; i < nseeds; i++) { 00509 numaGetIValue(nasi, i, &ival); 00510 if (ival == 1) { 00511 wshedSaveBasin(wshed, lut[i], val - 1); 00512 numaSetValue(nasi, i, 0); 00513 } 00514 } 00515 #endif 00516 00517 numaDestroy(&nalut); 00518 pixDestroy(&pixmin); 00519 pixDestroy(&pixsd); 00520 ptaDestroy(&ptao); 00521 lheapDestroy(&lh, TRUE); 00522 lstackDestroy(&rstack, TRUE); 00523 return 0; 00524 } 00525 00526 00527 /*-----------------------------------------------------------------------* 00528 * Helpers * 00529 *-----------------------------------------------------------------------*/ 00530 /*! 00531 * wshedSaveBasin() 00532 * 00533 * Input: wshed 00534 * index (index of basin to be located) 00535 * level (filling level reached at the time this function 00536 * is called) 00537 * Return: 0 if OK, 1 on error 00538 * 00539 * Notes: 00540 * (1) This identifies a single watershed. It does not change 00541 * the LUT, which must be done subsequently. 00542 * (2) The fill level of a basin is taken to be @level - 1. 00543 */ 00544 static void 00545 wshedSaveBasin(L_WSHED *wshed, 00546 l_int32 index, 00547 l_int32 level) 00548 { 00549 BOX *box; 00550 PIX *pix; 00551 00552 PROCNAME("wshedSaveBasin"); 00553 00554 if (!wshed) { 00555 L_ERROR("wshed not defined", procName); 00556 return; 00557 } 00558 00559 if (identifyWatershedBasin(wshed, index, level, &box, &pix) == 0) { 00560 pixaAddPix(wshed->pixad, pix, L_INSERT); 00561 pixaAddBox(wshed->pixad, box, L_INSERT); 00562 numaAddNumber(wshed->nalevels, level - 1); 00563 } 00564 return; 00565 } 00566 00567 00568 /*! 00569 * identifyWatershedBasin() 00570 * 00571 * Input: wshed 00572 * index (index of basin to be located) 00573 * level (of basin at point at which the two basins met) 00574 * &box (<return> bounding box of basin) 00575 * &pixd (<return> pix of basin, cropped to its bounding box) 00576 * Return: 0 if OK, 1 on error 00577 * 00578 * Notes: 00579 * (1) This is a static function, so we assume pixlab, pixs and pixt 00580 * exist and are the same size. 00581 * (2) It selects all pixels that have the label @index in pixlab 00582 * and that have a value in pixs that is less than @level. 00583 * (3) It is used whenever two seeded basins meet (typically at a saddle), 00584 * or when one seeded basin meets a 'filler'. All identified 00585 * basins are saved as a watershed. 00586 */ 00587 static l_int32 00588 identifyWatershedBasin(L_WSHED *wshed, 00589 l_int32 index, 00590 l_int32 level, 00591 BOX **pbox, 00592 PIX **ppixd) 00593 { 00594 l_int32 imin, imax, jmin, jmax, minx, miny, maxx, maxy; 00595 l_int32 bw, bh, i, j, w, h, x, y; 00596 l_int32 *lut; 00597 l_uint32 label, bval, lval; 00598 void **lines8, **linelab32, **linet1; 00599 BOX *box; 00600 PIX *pixs, *pixlab, *pixt, *pixd; 00601 L_QUEUE *lq; 00602 00603 PROCNAME("identifyWatershedBasin"); 00604 00605 if (!pbox) 00606 return ERROR_INT("&box not defined", procName, 1); 00607 *pbox = NULL; 00608 if (!ppixd) 00609 return ERROR_INT("&pixd not defined", procName, 1); 00610 *ppixd = NULL; 00611 if (!wshed) 00612 return ERROR_INT("wshed not defined", procName, 1); 00613 00614 /* Make a queue and an auxiliary stack */ 00615 lq = lqueueCreate(0); 00616 lq->stack = lstackCreate(0); 00617 00618 pixs = wshed->pixs; 00619 pixlab = wshed->pixlab; 00620 pixt = wshed->pixt; 00621 lines8 = wshed->lines8; 00622 linelab32 = wshed->linelab32; 00623 linet1 = wshed->linet1; 00624 lut = wshed->lut; 00625 pixGetDimensions(pixs, &w, &h, NULL); 00626 00627 /* Prime the queue with the seed pixel for this watershed. */ 00628 minx = miny = 1000000; 00629 maxx = maxy = 0; 00630 ptaGetIPt(wshed->ptas, index, &x, &y); 00631 pixSetPixel(pixt, x, y, 1); 00632 pushNewPixel(lq, x, y, &minx, &maxx, &miny, &maxy); 00633 if (wshed->debug) fprintf(stderr, "prime: (x,y) = (%d, %d)\n", x, y); 00634 00635 /* Each pixel in a spreading breadth-first search is inspected. 00636 * It is accepted as part of this watershed, and pushed on 00637 * the search queue, if: 00638 * (1) It has a label value equal to @index 00639 * (2) The pixel value is less than @level, the overflow 00640 * height at which the two basins join. 00641 * (3) It has not yet been seen in this search. */ 00642 while (lqueueGetCount(lq) > 0) { 00643 popNewPixel(lq, &x, &y); 00644 imin = L_MAX(0, y - 1); 00645 imax = L_MIN(h - 1, y + 1); 00646 jmin = L_MAX(0, x - 1); 00647 jmax = L_MIN(w - 1, x + 1); 00648 for (i = imin; i <= imax; i++) { 00649 for (j = jmin; j <= jmax; j++) { 00650 if (j == x && i == y) continue; /* parent */ 00651 label = GET_DATA_FOUR_BYTES(linelab32[i], j); 00652 if (label == MAX_LABEL_VALUE || lut[label] != index) continue; 00653 bval = GET_DATA_BIT(linet1[i], j); 00654 if (bval == 1) continue; /* already seen */ 00655 lval = GET_DATA_BYTE(lines8[i], j); 00656 if (lval >= level) continue; /* too high */ 00657 SET_DATA_BIT(linet1[i], j); 00658 pushNewPixel(lq, j, i, &minx, &maxx, &miny, &maxy); 00659 } 00660 } 00661 } 00662 00663 /* Extract the box and pix, and clear pixt */ 00664 bw = maxx - minx + 1; 00665 bh = maxy - miny + 1; 00666 box = boxCreate(minx, miny, bw, bh); 00667 pixd = pixClipRectangle(pixt, box, NULL); 00668 pixRasterop(pixt, minx, miny, bw, bh, PIX_SRC ^ PIX_DST, pixd, 0, 0); 00669 *pbox = box; 00670 *ppixd = pixd; 00671 00672 lqueueDestroy(&lq, 1); 00673 return 0; 00674 } 00675 00676 00677 /*! 00678 * mergeLookup() 00679 * 00680 * Input: wshed 00681 * sindex (primary index being changed in the merge) 00682 * dindex (index that @sindex will point to after the merge) 00683 * Return: 0 if OK, 1 on error 00684 * 00685 * Notes: 00686 * (1) The links are a sparse array of Numas showing current back-links. 00687 * The lut gives the current index (of the seed or the minima 00688 * for the wshed in which it is located. 00689 * (2) Think of each entry in the lut. There are two types: 00690 * owner: lut[index] = index 00691 * redirect: lut[index] != index 00692 * (3) This is called each time a merge occurs. It puts the lut 00693 * and backlinks in a canonical form after the merge, where 00694 * all entries in the lut point to the current "owner", which 00695 * has all backlinks. That is, every "redirect" in the lut 00696 * points to an "owner". The lut always gives the index of 00697 * the current owner. 00698 */ 00699 static l_int32 00700 mergeLookup(L_WSHED *wshed, 00701 l_int32 sindex, 00702 l_int32 dindex) 00703 { 00704 l_int32 i, n, size, index; 00705 l_int32 *lut; 00706 NUMA *na; 00707 NUMA **links; 00708 00709 PROCNAME("mergeLookup"); 00710 00711 if (!wshed) 00712 return ERROR_INT("wshed not defined", procName, 1); 00713 size = wshed->arraysize; 00714 if (sindex < 0 || sindex >= size) 00715 return ERROR_INT("invalid sindex", procName, 1); 00716 if (dindex < 0 || dindex >= size) 00717 return ERROR_INT("invalid dindex", procName, 1); 00718 00719 /* Redirect links in the lut */ 00720 n = 0; 00721 links = wshed->links; 00722 lut = wshed->lut; 00723 if ((na = links[sindex]) != NULL) { 00724 n = numaGetCount(na); 00725 for (i = 0; i < n; i++) { 00726 numaGetIValue(na, i, &index); 00727 lut[index] = dindex; 00728 } 00729 } 00730 lut[sindex] = dindex; 00731 00732 /* Shift the backlink arrays from sindex to dindex. 00733 * sindex should have no backlinks because all entries in the 00734 * lut that were previously pointing to it have been redirected 00735 * to dindex. */ 00736 if (!links[dindex]) 00737 links[dindex] = numaCreate(n); 00738 numaJoin(links[dindex], links[sindex], 0, 0); 00739 numaAddNumber(links[dindex], sindex); 00740 numaDestroy(&links[sindex]); 00741 00742 return 0; 00743 } 00744 00745 00746 /*! 00747 * wshedGetHeight() 00748 * 00749 * Input: wshed (array of current indices) 00750 * val (value of current pixel popped off queue) 00751 * label (of pixel or 32 bpp label image) 00752 * &height (<return> height of current value from seed 00753 * or minimum of watershed) 00754 * Return: 0 if OK, 1 on error 00755 * 00756 * Notes: 00757 * (1) It is only necessary to find the height for a watershed 00758 * that is indexed by a seed or a minima. This function should 00759 * not be called on a finished watershed (that continues to fill). 00760 */ 00761 static l_int32 00762 wshedGetHeight(L_WSHED *wshed, 00763 l_int32 val, 00764 l_int32 label, 00765 l_int32 *pheight) 00766 { 00767 l_int32 minval; 00768 00769 PROCNAME("wshedGetHeight"); 00770 00771 if (!pheight) 00772 return ERROR_INT("&height not defined", procName, 1); 00773 *pheight = 0; 00774 if (!wshed) 00775 return ERROR_INT("wshed not defined", procName, 1); 00776 00777 if (label < wshed->nseeds) 00778 numaGetIValue(wshed->nash, label, &minval); 00779 else if (label < wshed->nseeds + wshed->nother) 00780 numaGetIValue(wshed->namh, label, &minval); 00781 else 00782 return ERROR_INT("finished watershed; should not call", procName, 1); 00783 00784 *pheight = val - minval; 00785 return 0; 00786 } 00787 00788 00789 /* 00790 * pushNewPixel() 00791 * 00792 * Input: lqueue 00793 * x, y (pixel coordinates) 00794 * &minx, &maxx, &miny, &maxy (<return> bounding box update) 00795 * Return: void 00796 * 00797 * Notes: 00798 * (1) This is a wrapper for adding a NewPixel to a queue, which 00799 * updates the bounding box for all pixels on that queue and 00800 * uses the storage stack to retrieve a NewPixel. 00801 */ 00802 static void 00803 pushNewPixel(L_QUEUE *lq, 00804 l_int32 x, 00805 l_int32 y, 00806 l_int32 *pminx, 00807 l_int32 *pmaxx, 00808 l_int32 *pminy, 00809 l_int32 *pmaxy) 00810 { 00811 L_NEWPIXEL *np; 00812 00813 PROCNAME("pushNewPixel"); 00814 00815 if (!lq) { 00816 L_ERROR(procName, "queue not defined"); 00817 return; 00818 } 00819 00820 /* Adjust bounding box */ 00821 *pminx = L_MIN(*pminx, x); 00822 *pmaxx = L_MAX(*pmaxx, x); 00823 *pminy = L_MIN(*pminy, y); 00824 *pmaxy = L_MAX(*pmaxy, y); 00825 00826 /* Get a newpixel to use */ 00827 if (lstackGetCount(lq->stack) > 0) 00828 np = (L_NEWPIXEL *)lstackRemove(lq->stack); 00829 else 00830 np = (L_NEWPIXEL *)CALLOC(1, sizeof(L_NEWPIXEL)); 00831 00832 np->x = x; 00833 np->y = y; 00834 lqueueAdd(lq, np); 00835 return; 00836 } 00837 00838 00839 /* 00840 * popNewPixel() 00841 * 00842 * Input: lqueue 00843 * &x, &y (<return> pixel coordinates) 00844 * Return: void 00845 * 00846 * Notes: 00847 * (1) This is a wrapper for removing a NewPixel from a queue, 00848 * which returns the pixel coordinates and saves the NewPixel 00849 * on the storage stack. 00850 */ 00851 static void 00852 popNewPixel(L_QUEUE *lq, 00853 l_int32 *px, 00854 l_int32 *py) 00855 { 00856 L_NEWPIXEL *np; 00857 00858 PROCNAME("popNewPixel"); 00859 00860 if (!lq) { 00861 L_ERROR(procName, "lqueue not defined"); 00862 return; 00863 } 00864 00865 if ((np = (L_NEWPIXEL *)lqueueRemove(lq)) == NULL) 00866 return; 00867 *px = np->x; 00868 *py = np->y; 00869 lstackAdd(lq->stack, np); /* save for re-use */ 00870 return; 00871 } 00872 00873 00874 /* 00875 * pushWSPixel() 00876 * 00877 * Input: lh (priority queue) 00878 * stack (of reusable WSPixels) 00879 * val (pixel value: used for ordering the heap) 00880 * x, y (pixel coordinates) 00881 * index (label for set to which pixel belongs) 00882 * Return: void 00883 * 00884 * Notes: 00885 * (1) This is a wrapper for adding a WSPixel to a heap. It 00886 * uses the storage stack to retrieve a WSPixel. 00887 */ 00888 static void 00889 pushWSPixel(L_HEAP *lh, 00890 L_STACK *stack, 00891 l_int32 val, 00892 l_int32 x, 00893 l_int32 y, 00894 l_int32 index) 00895 { 00896 L_WSPIXEL *wsp; 00897 00898 PROCNAME("pushWSPixel"); 00899 00900 if (!lh) { 00901 L_ERROR(procName, "heap not defined"); 00902 return; 00903 } 00904 if (!stack) { 00905 L_ERROR(procName, "stack not defined"); 00906 return; 00907 } 00908 00909 /* Get a wspixel to use */ 00910 if (lstackGetCount(stack) > 0) 00911 wsp = (L_WSPIXEL *)lstackRemove(stack); 00912 else 00913 wsp = (L_WSPIXEL *)CALLOC(1, sizeof(L_WSPIXEL)); 00914 00915 wsp->val = (l_float32)val; 00916 wsp->x = x; 00917 wsp->y = y; 00918 wsp->index = index; 00919 lheapAdd(lh, wsp); 00920 return; 00921 } 00922 00923 00924 /* 00925 * popWSPixel() 00926 * 00927 * Input: lh (priority queue) 00928 * stack (of reusable WSPixels) 00929 * &val (<return> pixel value) 00930 * &x, &y (<return> pixel coordinates) 00931 * &index (<return> label for set to which pixel belongs) 00932 * Return: void 00933 * 00934 * Notes: 00935 * (1) This is a wrapper for removing a WSPixel from a heap, 00936 * which returns the WSPixel data and saves the WSPixel 00937 * on the storage stack. 00938 */ 00939 static void 00940 popWSPixel(L_HEAP *lh, 00941 L_STACK *stack, 00942 l_int32 *pval, 00943 l_int32 *px, 00944 l_int32 *py, 00945 l_int32 *pindex) 00946 { 00947 L_WSPIXEL *wsp; 00948 00949 PROCNAME("popWSPixel"); 00950 00951 if (!lh) { 00952 L_ERROR(procName, "lheap not defined"); 00953 return; 00954 } 00955 if (!stack) { 00956 L_ERROR(procName, "stack not defined"); 00957 return; 00958 } 00959 if (!pval || !px || !py || !pindex) { 00960 L_ERROR(procName, "data can't be returned"); 00961 return; 00962 } 00963 00964 if ((wsp = (L_WSPIXEL *)lheapRemove(lh)) == NULL) 00965 return; 00966 *pval = (l_int32)wsp->val; 00967 *px = wsp->x; 00968 *py = wsp->y; 00969 *pindex = wsp->index; 00970 lstackAdd(stack, wsp); /* save for re-use */ 00971 return; 00972 } 00973 00974 00975 static void 00976 debugPrintLUT(l_int32 *lut, 00977 l_int32 size, 00978 l_int32 debug) 00979 { 00980 l_int32 i; 00981 00982 if (!debug) return; 00983 fprintf(stderr, "lut: "); 00984 for (i = 0; i < size; i++) 00985 fprintf(stderr, "%d ", lut[i]); 00986 fprintf(stderr, "\n"); 00987 return; 00988 } 00989 00990 00991 static void 00992 debugWshedMerge(L_WSHED *wshed, 00993 char *descr, 00994 l_int32 x, 00995 l_int32 y, 00996 l_int32 label, 00997 l_int32 index) 00998 { 00999 if (!wshed || (wshed->debug == 0)) 01000 return; 01001 fprintf(stderr, "%s:\n", descr); 01002 fprintf(stderr, " (x, y) = (%d, %d)\n", x, y); 01003 fprintf(stderr, " clabel = %d, cindex = %d\n", label, index); 01004 return; 01005 } 01006 01007 01008 /*-----------------------------------------------------------------------* 01009 * Output * 01010 *-----------------------------------------------------------------------*/ 01011 /*! 01012 * wshedBasins() 01013 * 01014 * Input: wshed 01015 * &pixa (<optional return> mask of watershed basins) 01016 * &nalevels (<optional return> watershed levels) 01017 * Return: 0 if OK, 1 on error 01018 */ 01019 l_int32 01020 wshedBasins(L_WSHED *wshed, 01021 PIXA **ppixa, 01022 NUMA **pnalevels) 01023 { 01024 PROCNAME("wshedBasins"); 01025 01026 if (!wshed) 01027 return ERROR_INT("wshed not defined", procName, 1); 01028 01029 if (ppixa) 01030 *ppixa = pixaCopy(wshed->pixad, L_CLONE); 01031 if (pnalevels) 01032 *pnalevels = numaClone(wshed->nalevels); 01033 return 0; 01034 } 01035 01036 01037 /*! 01038 * wshedRenderFill() 01039 * 01040 * Input: wshed 01041 * Return: pixd (initial image with all basins filled), or null on error 01042 */ 01043 PIX * 01044 wshedRenderFill(L_WSHED *wshed) 01045 { 01046 l_int32 i, n, level, bx, by; 01047 NUMA *na; 01048 PIX *pix, *pixd; 01049 PIXA *pixa; 01050 01051 PROCNAME("wshedRenderFill"); 01052 01053 if (!wshed) 01054 return (PIX *)ERROR_PTR("wshed not defined", procName, NULL); 01055 01056 wshedBasins(wshed, &pixa, &na); 01057 pixd = pixCopy(NULL, wshed->pixs); 01058 n = pixaGetCount(pixa); 01059 for (i = 0; i < n; i++) { 01060 pix = pixaGetPix(pixa, i, L_CLONE); 01061 pixaGetBoxGeometry(pixa, i, &bx, &by, NULL, NULL); 01062 numaGetIValue(na, i, &level); 01063 pixPaintThroughMask(pixd, pix, bx, by, level); 01064 pixDestroy(&pix); 01065 } 01066 01067 pixaDestroy(&pixa); 01068 numaDestroy(&na); 01069 return pixd; 01070 } 01071 01072 01073 /*! 01074 * wshedRenderColors() 01075 * 01076 * Input: wshed 01077 * Return: pixd (initial image with all basins filled), or null on error 01078 */ 01079 PIX * 01080 wshedRenderColors(L_WSHED *wshed) 01081 { 01082 l_int32 w, h; 01083 PIX *pixg, *pixt, *pixc, *pixm, *pixd; 01084 PIXA *pixa; 01085 01086 PROCNAME("wshedRenderColors"); 01087 01088 if (!wshed) 01089 return (PIX *)ERROR_PTR("wshed not defined", procName, NULL); 01090 01091 wshedBasins(wshed, &pixa, NULL); 01092 pixg = pixCopy(NULL, wshed->pixs); 01093 pixGetDimensions(wshed->pixs, &w, &h, NULL); 01094 pixd = pixConvertTo32(pixg); 01095 pixt = pixaDisplayRandomCmap(pixa, w, h); 01096 pixc = pixConvertTo32(pixt); 01097 pixm = pixaDisplay(pixa, w, h); 01098 pixCombineMasked(pixd, pixc, pixm); 01099 01100 pixDestroy(&pixg); 01101 pixDestroy(&pixt); 01102 pixDestroy(&pixc); 01103 pixDestroy(&pixm); 01104 pixaDestroy(&pixa); 01105 return pixd; 01106 } 01107