[ VIGRA Homepage | Function Index | Class Index | Namespaces | File List | Main Page ]

multi_watersheds.hxx VIGRA

1 /************************************************************************/
2 /* */
3 /* Copyright 1998-2013 by Ullrich Koethe */
4 /* */
5 /* This file is part of the VIGRA computer vision library. */
6 /* The VIGRA Website is */
7 /* http://hci.iwr.uni-heidelberg.de/vigra/ */
8 /* Please direct questions, bug reports, and contributions to */
9 /* ullrich.koethe@iwr.uni-heidelberg.de or */
10 /* vigra@informatik.uni-hamburg.de */
11 /* */
12 /* Permission is hereby granted, free of charge, to any person */
13 /* obtaining a copy of this software and associated documentation */
14 /* files (the "Software"), to deal in the Software without */
15 /* restriction, including without limitation the rights to use, */
16 /* copy, modify, merge, publish, distribute, sublicense, and/or */
17 /* sell copies of the Software, and to permit persons to whom the */
18 /* Software is furnished to do so, subject to the following */
19 /* conditions: */
20 /* */
21 /* The above copyright notice and this permission notice shall be */
22 /* included in all copies or substantial portions of the */
23 /* Software. */
24 /* */
25 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
26 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
27 /* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
28 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
29 /* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
30 /* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
31 /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
32 /* OTHER DEALINGS IN THE SOFTWARE. */
33 /* */
34 /************************************************************************/
35 
36 #ifndef VIGRA_MULTI_WATERSHEDS_HXX
37 #define VIGRA_MULTI_WATERSHEDS_HXX
38 
39 #include <functional>
40 #include <limits>
41 #include "mathutil.hxx"
42 #include "multi_array.hxx"
43 #include "multi_math.hxx"
44 #include "multi_gridgraph.hxx"
45 #include "multi_localminmax.hxx"
46 #include "multi_labeling.hxx"
47 #include "watersheds.hxx"
48 #include "bucket_queue.hxx"
49 #include "union_find.hxx"
50 
51 namespace vigra {
52 
53 /** \addtogroup Superpixels
54 */
55 //@{
56 namespace lemon_graph {
57 
58 namespace graph_detail {
59 
60  // select the neighbor ID for union-find watersheds
61  // standard behavior: use global node ID
62 template <class Graph>
63 struct NeighborIndexFunctor
64 {
65  typedef typename Graph::index_type index_type;
66 
67  template <class NodeIter, class ArcIter>
68  static index_type get(Graph const & g, NodeIter const &, ArcIter const & a)
69  {
70  return g.id(g.target(*a));
71  }
72 
73  template <class NodeIter, class ArcIter>
74  static index_type getOpposite(Graph const & g, NodeIter const & n, ArcIter const &)
75  {
76  return g.id(*n);
77  }
78 
79  static index_type invalidIndex(Graph const &)
80  {
81  return std::numeric_limits<index_type>::max();
82  }
83 };
84 
85  // select the neighbor ID for union-find watersheds
86  // GridGraph optimization: use local neighbor index (needs only 1/4 of the memory)
87 template<unsigned int N, class DirectedTag>
88 struct NeighborIndexFunctor<GridGraph<N, DirectedTag> >
89 {
90  typedef GridGraph<N, DirectedTag> Graph;
91  typedef UInt16 index_type;
92 
93  template <class NodeIter, class ArcIter>
94  static index_type get(Graph const &, NodeIter const &, ArcIter const & a)
95  {
96  return a.neighborIndex();
97  }
98 
99  template <class NodeIter, class ArcIter>
100  static index_type getOpposite(Graph const & g, NodeIter const &, ArcIter const & a)
101  {
102  return g.oppositeIndex(a.neighborIndex());
103  }
104  static index_type invalidIndex(Graph const &)
105  {
106  return std::numeric_limits<index_type>::max();
107  }
108 };
109 
110 template <class Graph, class T1Map, class T2Map>
111 void
112 prepareWatersheds(Graph const & g,
113  T1Map const & data,
114  T2Map & lowestNeighborIndex)
115 {
116  typedef typename Graph::NodeIt graph_scanner;
117  typedef typename Graph::OutArcIt neighbor_iterator;
118  typedef NeighborIndexFunctor<Graph> IndexFunctor;
119 
120  for (graph_scanner node(g); node != INVALID; ++node)
121  {
122  typename T1Map::value_type lowestValue = data[*node];
123  typename T2Map::value_type lowestIndex = IndexFunctor::invalidIndex(g);
124 
125  for(neighbor_iterator arc(g, *node); arc != INVALID; ++arc)
126  {
127  if(data[g.target(*arc)] < lowestValue)
128  {
129  lowestValue = data[g.target(*arc)];
130  lowestIndex = IndexFunctor::get(g, node, arc);
131  }
132  }
133  lowestNeighborIndex[*node] = lowestIndex;
134  }
135 }
136 
137 
138 template <class Graph, class T1Map, class T2Map, class T3Map>
139 typename T2Map::value_type
140 unionFindWatersheds(Graph const & g,
141  T1Map const &,
142  T2Map const & lowestNeighborIndex,
143  T3Map & labels)
144 {
145  typedef typename Graph::NodeIt graph_scanner;
146  typedef typename Graph::OutBackArcIt neighbor_iterator;
147  typedef typename T3Map::value_type LabelType;
148  typedef NeighborIndexFunctor<Graph> IndexFunctor;
149 
150  vigra::UnionFindArray<LabelType> regions;
151 
152  // pass 1: find connected components
153  for (graph_scanner node(g); node != INVALID; ++node)
154  {
155  // define tentative label for current node
156  LabelType currentIndex = regions.nextFreeIndex();
157 
158  for (neighbor_iterator arc(g, node); arc != INVALID; ++arc)
159  {
160  // merge regions if current target is center's lowest neighbor or vice versa
161  if((lowestNeighborIndex[*node] == IndexFunctor::invalidIndex(g) &&
162  lowestNeighborIndex[g.target(*arc)] == IndexFunctor::invalidIndex(g)) ||
163  (lowestNeighborIndex[*node] == IndexFunctor::get(g, node, arc)) ||
164  (lowestNeighborIndex[g.target(*arc)] == IndexFunctor::getOpposite(g, node, arc)))
165  {
166  currentIndex = regions.makeUnion(labels[g.target(*arc)], currentIndex);
167  }
168  }
169 
170  // set label of current node
171  labels[*node] = regions.finalizeIndex(currentIndex);
172  }
173 
174  LabelType count = regions.makeContiguous();
175 
176  // pass 2: make component labels contiguous
177  for (graph_scanner node(g); node != INVALID; ++node)
178  {
179  labels[*node] = regions.findLabel(labels[*node]);
180  }
181  return count;
182 }
183 
184 template <class Graph, class T1Map, class T2Map>
185 typename T2Map::value_type
186 generateWatershedSeeds(Graph const & g,
187  T1Map const & data,
188  T2Map & seeds,
189  SeedOptions const & options = SeedOptions())
190 {
191  typedef typename T1Map::value_type DataType;
192  typedef unsigned char MarkerType;
193 
194  typename Graph::template NodeMap<MarkerType> minima(g);
195 
196  if(options.mini == SeedOptions::LevelSets)
197  {
198  vigra_precondition(options.thresholdIsValid<DataType>(),
199  "generateWatershedSeeds(): SeedOptions.levelSets() must be specified with threshold.");
200 
201  using namespace multi_math;
202  for(typename Graph::NodeIt iter(g);iter!=lemon::INVALID;++iter){
203  minima[*iter]= data[*iter] <= DataType(options.thresh);
204  }
205  }
206  else
207  {
208  DataType threshold = options.thresholdIsValid<DataType>()
209  ? options.thresh
210  : NumericTraits<DataType>::max();
211 
212  if(options.mini == SeedOptions::ExtendedMinima)
213  extendedLocalMinMaxGraph(g, data, minima, MarkerType(1), threshold,
214  std::less<DataType>(), std::equal_to<DataType>(), true);
215  else
216  lemon_graph::localMinMaxGraph(g, data, minima, MarkerType(1), threshold,
217  std::less<DataType>(), true);
218  }
219  return labelGraphWithBackground(g, minima, seeds, MarkerType(0), std::equal_to<MarkerType>());
220 }
221 
222 #ifdef __GNUC__
223 #pragma GCC diagnostic push
224 #pragma GCC diagnostic ignored "-Wsign-compare"
225 #endif
226 
227 template <class Graph, class T1Map, class T2Map>
228 typename T2Map::value_type
229 seededWatersheds(Graph const & g,
230  T1Map const & data,
231  T2Map & labels,
232  WatershedOptions const & options)
233 {
234  typedef typename Graph::Node Node;
235  typedef typename Graph::NodeIt graph_scanner;
236  typedef typename Graph::OutArcIt neighbor_iterator;
237  typedef typename T1Map::value_type CostType;
238  typedef typename T2Map::value_type LabelType;
239 
240  PriorityQueue<Node, CostType, true> pqueue;
241 
242  bool keepContours = ((options.terminate & KeepContours) != 0);
243  LabelType maxRegionLabel = 0;
244 
245  for (graph_scanner node(g); node != INVALID; ++node)
246  {
247  LabelType label = labels[*node];
248  if(label != 0)
249  {
250  if(maxRegionLabel < label)
251  maxRegionLabel = label;
252 
253  for (neighbor_iterator arc(g, *node); arc != INVALID; ++arc)
254  {
255  if(labels[g.target(*arc)] == 0)
256  {
257  // register all seeds that have an unlabeled neighbor
258  if(label == options.biased_label)
259  pqueue.push(*node, data[*node] * options.bias);
260  else
261  pqueue.push(*node, data[*node]);
262  break;
263  }
264  }
265  }
266  }
267 
268  LabelType contourLabel = maxRegionLabel + 1; // temporary contour label
269 
270  // perform region growing
271  while(!pqueue.empty())
272  {
273  Node node = pqueue.top();
274  CostType cost = pqueue.topPriority();
275  pqueue.pop();
276 
277  if((options.terminate & StopAtThreshold) && (cost > options.max_cost))
278  break;
279 
280  LabelType label = labels[node];
281 
282  if(label == contourLabel)
283  continue;
284 
285  // Put the unlabeled neighbors in the priority queue.
286  for (neighbor_iterator arc(g, node); arc != INVALID; ++arc)
287  {
288  LabelType neighborLabel = labels[g.target(*arc)];
289  if(neighborLabel == 0)
290  {
291  labels[g.target(*arc)] = label;
292  CostType priority = (label == options.biased_label)
293  ? data[g.target(*arc)] * options.bias
294  : data[g.target(*arc)];
295  if(priority < cost)
296  priority = cost;
297  pqueue.push(g.target(*arc), priority);
298  }
299  else if(keepContours && (label != neighborLabel) && (neighborLabel != contourLabel))
300  {
301  // The present neighbor is adjacent to more than one region
302  // => mark it as contour.
303  CostType priority = (neighborLabel == options.biased_label)
304  ? data[g.target(*arc)] * options.bias
305  : data[g.target(*arc)];
306  if(cost < priority) // neighbor not yet processed
307  labels[g.target(*arc)] = contourLabel;
308  }
309  }
310  }
311 
312  if(keepContours)
313  {
314  // Replace the temporary contour label with label 0.
315  ///typename T2Map::iterator k = labels.begin(),
316  /// end = labels.end();
317  ///for(; k != end; ++k)
318  /// if(*k == contourLabel)
319  /// *k = 0;
320 
321  for(typename Graph::NodeIt iter(g);iter!=lemon::INVALID;++iter){
322  if(labels[*iter]==contourLabel)
323  labels[*iter]=0;
324  }
325  }
326 
327  return maxRegionLabel;
328 }
329 
330 #ifdef __GNUC__
331 #pragma GCC diagnostic pop
332 #endif
333 
334 } // namespace graph_detail
335 
336 template <class Graph, class T1Map, class T2Map>
337 typename T2Map::value_type
338 watershedsGraph(Graph const & g,
339  T1Map const & data,
340  T2Map & labels,
341  WatershedOptions const & options)
342 {
343  if(options.method == WatershedOptions::UnionFind)
344  {
345  typedef typename graph_detail::NeighborIndexFunctor<Graph>::index_type index_type;
346 
347  vigra_precondition((index_type)g.maxDegree() <= NumericTraits<index_type>::max(),
348  "watershedsGraph(): cannot handle nodes with degree > 65535.");
349 
350  typename Graph::template NodeMap<index_type> lowestNeighborIndex(g);
351 
352  graph_detail::prepareWatersheds(g, data, lowestNeighborIndex);
353  return graph_detail::unionFindWatersheds(g, data, lowestNeighborIndex, labels);
354  }
355  else if(options.method == WatershedOptions::RegionGrowing)
356  {
357  SeedOptions seed_options;
358 
359  // check if the user has explicitly requested seed computation
360  if(options.seed_options.mini != SeedOptions::Unspecified)
361  {
362  seed_options = options.seed_options;
363  }
364  else
365  {
366  // otherwise, don't compute seeds if 'labels' already contains them
367  if(labels.any())
368  seed_options.mini = SeedOptions::Unspecified;
369  }
370 
371  if(seed_options.mini != SeedOptions::Unspecified)
372  {
373  graph_detail::generateWatershedSeeds(g, data, labels, seed_options);
374  }
375 
376  return graph_detail::seededWatersheds(g, data, labels, options);
377  }
378  else
379  {
380  vigra_precondition(false,
381  "watershedsGraph(): invalid method in watershed options.");
382  return 0;
383  }
384 }
385 
386 
387 } // namespace lemon_graph
388 
389  // documentation is in watersheds.hxx
390 template <unsigned int N, class T, class S1,
391  class Label, class S2>
392 inline Label
393 generateWatershedSeeds(MultiArrayView<N, T, S1> const & data,
394  MultiArrayView<N, Label, S2> seeds,
395  NeighborhoodType neighborhood = IndirectNeighborhood,
396  SeedOptions const & options = SeedOptions())
397 {
398  vigra_precondition(data.shape() == seeds.shape(),
399  "generateWatershedSeeds(): Shape mismatch between input and output.");
400 
401  GridGraph<N, undirected_tag> graph(data.shape(), neighborhood);
402  return lemon_graph::graph_detail::generateWatershedSeeds(graph, data, seeds, options);
403 }
404 
405 
406 /** \brief Watershed segmentation of an arbitrary-dimensional array.
407 
408  See also \ref unionFindWatershedsBlockwise() for a parallel version of the
409  watershed algorithm.
410 
411  This function implements variants of the watershed algorithms
412  described in
413 
414  [1] L. Vincent and P. Soille: <em>"Watersheds in digital spaces: An efficient algorithm
415  based on immersion simulations"</em>, IEEE Trans. Patt. Analysis Mach. Intell. 13(6):583-598, 1991
416 
417  [2] J. Roerdink, R. Meijster: <em>"The watershed transform: definitions, algorithms,
418  and parallelization strategies"</em>, Fundamenta Informaticae, 41:187-228, 2000
419 
420  The source array \a data is a boundary indicator such as the gaussianGradientMagnitude()
421  or the trace of the \ref boundaryTensor(), and the destination \a labels is a label array
422  designating membership of each point in one of the regions found. Plateaus in the boundary
423  indicator are handled via simple tie breaking strategies. Argument \a neighborhood
424  specifies the connectivity between points and can be <tt>DirectNeighborhood</tt> (meaning
425  4-neighborhood in 2D and 6-neighborhood in 3D, default) or <tt>IndirectNeighborhood</tt>
426  (meaning 8-neighborhood in 2D and 26-neighborhood in 3D).
427 
428  The watershed variant to be applied can be selected in the \ref WatershedOptions
429  object: When you call <tt>WatershedOptions::regionGrowing()</tt> (default), the flooding
430  algorithm from [1] is used. Alternatively, <tt>WatershedOptions::unionFind()</tt> uses
431  the scan-line algorithm 4.7 from [2]. The latter is faster, but does not support any options
432  (if you pass options nonetheless, they are silently ignored).
433 
434  The region growing algorithm needs a seed for each region. Seeds can either be provided in
435  the destination array \a labels (which will then be overwritten with the result) or computed
436  automatically by an internal call to generateWatershedSeeds(). In the former case you have
437  full control over seed placement, while the latter is more convenient. Automatic seed
438  computation is performed when you provide seeding options via <tt>WatershedOptions::seedOptions()</tt>
439  or when the array \a labels is empty (all zeros), in which case default seeding options
440  are chosen. The destination image should be zero-initialized for automatic seed computation.
441 
442  Further options to be specified via \ref WatershedOptions are:
443 
444  <ul>
445  <li> <tt>keepContours()</tt>: Whether to keep a 1-pixel-wide contour (with label 0) between
446  regions (otherwise, a complete grow is performed, i.e. all pixels are assigned to a region).
447  <li> <tt>stopAtThreshold()</tt>: Whether to stop growing when the boundaryness exceeds a threshold
448  (remaining pixels keep label 0).
449  <li> <tt>biasLabel()</tt>: Whether one region (label) is to be preferred or discouraged by biasing its cost
450  with a given factor (smaller than 1 for preference, larger than 1 for discouragement).
451  </ul>
452 
453  The option <tt>turboAlgorithm()</tt> is implied by method <tt>regionGrowing()</tt> (this is
454  in contrast to watershedsRegionGrowing(), which supports an additional algorithm in 2D only).
455 
456  watershedsMultiArray() returns the number of regions found (= the highest region label, because
457  labels start at 1).
458 
459  <b> Declaration:</b>
460 
461  \code
462  namespace vigra {
463  template <unsigned int N, class T, class S1,
464  class Label, class S2>
465  Label
466  watershedsMultiArray(MultiArrayView<N, T, S1> const & data,
467  MultiArrayView<N, Label, S2> labels, // may also hold input seeds
468  NeighborhoodType neighborhood = DirectNeighborhood,
469  WatershedOptions const & options = WatershedOptions());
470  }
471  \endcode
472 
473  <b> Usage:</b>
474 
475  <b>\#include</b> <vigra/multi_watersheds.hxx><br>
476  Namespace: vigra
477 
478  Example: watersheds of the gradient magnitude (the example works likewise for higher dimensions).
479 
480  \code
481  MultiArray<2, unsigned char> src(Shape2(w, h));
482  ... // read input data
483 
484  // compute gradient magnitude at scale 1.0 as a boundary indicator
485  MultiArray<2, float> gradMag(src.shape());
486  gaussianGradientMagnitude(srcImageRange(src), destImage(gradMag), 1.0);
487 
488  // example 1
489  {
490  // the pixel type of the destination image must be large enough to hold
491  // numbers up to 'max_region_label' to prevent overflow
492  MultiArray<2, unsigned int> labeling(src.shape());
493 
494  // call region-growing algorithm for 4-neighborhood, leave a 1-pixel boundary between
495  // regions, and autogenerate seeds from all gradient minima where the magnitude is
496  // less than 2.0.
497  unsigned int max_region_label =
498  watershedsMultiArray(gradMag, labeling, DirectNeighborhood,
499  WatershedOptions().keepContours()
500  .seedOptions(SeedOptions().minima().threshold(2.0)));
501  }
502 
503  // example 2
504  {
505  MultiArray<2, unsigned int> labeling(src.shape());
506 
507  // compute seeds beforehand (use connected components of all pixels
508  // where the gradient is below 4.0)
509  unsigned int max_region_label = generateWatershedSeeds(gradMag, labeling,
510  SeedOptions().levelSets(4.0));
511 
512  // quantize the gradient image to 256 gray levels
513  float m, M;
514  gradMag.minmax(&m, &M);
515 
516  using namespace multi_math;
517  MultiArray<2, unsigned char> gradMag256(255.0 / (M - m) * (gradMag - m));
518 
519  // call region-growing algorithm with 8-neighborhood,
520  // since the data are 8-bit, a faster priority queue will be used
521  watershedsMultiArray(gradMag256, labeling, IndirectNeighborhood);
522  }
523 
524  // example 3
525  {
526  MultiArray<2, unsigned int> labeling(src.shape());
527 
528  .. // put seeds in 'labeling', e.g. from an interactive labeling program,
529  // make sure that label 1 corresponds to the background
530 
531  // bias the watershed algorithm so that the background is preferred
532  // by reducing the cost for label 1 to 90%
533  watershedsMultiArray(gradMag, labeling,
534  WatershedOptions().biasLabel(1, 0.9));
535  }
536 
537  // example 4
538  {
539  MultiArray<2, unsigned int> labeling(src.shape());
540 
541  // use the fast union-find algorithm with 4-neighborhood
542  watershedsMultiArray(gradMag, labeling, WatershedOptions().unionFind());
543  }
544  \endcode
545 */
547 
548 template <unsigned int N, class T, class S1,
549  class Label, class S2>
550 inline Label
551 watershedsMultiArray(MultiArrayView<N, T, S1> const & data,
552  MultiArrayView<N, Label, S2> labels, // may also hold input seeds
553  NeighborhoodType neighborhood = DirectNeighborhood,
554  WatershedOptions const & options = WatershedOptions())
555 {
556  vigra_precondition(data.shape() == labels.shape(),
557  "watershedsMultiArray(): Shape mismatch between input and output.");
558 
559  GridGraph<N, undirected_tag> graph(data.shape(), neighborhood);
560  return lemon_graph::watershedsGraph(graph, data, labels, options);
561 }
562 
563 //@}
564 
565 } // namespace vigra
566 
567 #endif // VIGRA_MULTI_WATERSHEDS_HXX
CoupledHandleCast< TARGET_INDEX, Handle >::reference get(Handle &handle)
Definition: multi_handle.hxx:927
Label watershedsMultiArray(...)
Watershed segmentation of an arbitrary-dimensional array.
detail::SelectIntegerType< 16, detail::UnsignedIntTypes >::type UInt16
16-bit unsigned int
Definition: sized_int.hxx:181
doxygen_overloaded_function(template<...> void separableConvolveBlockwise) template< unsigned int N
Separated convolution on ChunkedArrays.
use direct and indirect neighbors
Definition: multi_fwd.hxx:188
use only direct neighbors
Definition: multi_fwd.hxx:187
NeighborhoodType
Choose the neighborhood system in a dimension-independent way.
Definition: multi_fwd.hxx:186

© Ullrich Köthe (ullrich.koethe@iwr.uni-heidelberg.de)
Heidelberg Collaboratory for Image Processing, University of Heidelberg, Germany

html generated using doxygen and Python
vigra 1.11.1 (Fri May 19 2017)