vigra - vigra/watersheds3d.hxx Source File

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

watersheds3d.hxx |

1 /************************************************************************/
2 /* */
3 /* Copyright 2006-2007 by F. Heinrich, B. Seppke, 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_watersheds3D_HXX
37 #define VIGRA_watersheds3D_HXX
38
39 #include "voxelneighborhood.hxx"
40 #include "multi_array.hxx"
41 #include "multi_localminmax.hxx"
42 #include "labelvolume.hxx"
43 #include "seededregiongrowing3d.hxx"
44 #include "watersheds.hxx"
45
46 namespace vigra
47 {
48
49 template <class SrcIterator, class SrcAccessor, class SrcShape,
50 class DestIterator, class DestAccessor, class Neighborhood3D>
51 int preparewatersheds3D( SrcIterator s_Iter, SrcShape srcShape, SrcAccessor sa,
52 DestIterator d_Iter, DestAccessor da, Neighborhood3D)
53 {
54 //basically needed for iteration and border-checks
55 int w = srcShape[0], h = srcShape[1], d = srcShape[2];
56 int x,y,z, local_min_count=0;
57
58 //declare and define Iterators for all three dims at src
59 SrcIterator zs = s_Iter;
60 SrcIterator ys(zs);
61 SrcIterator xs(ys);
62
63 //Declare Iterators for all three dims at dest
64 DestIterator zd = d_Iter;
65
66 for(z = 0; z != d; ++z, ++zs.dim2(), ++zd.dim2())
67 {
68 ys = zs;
69 DestIterator yd(zd);
70
71 for(y = 0; y != h; ++y, ++ys.dim1(), ++yd.dim1())
72 {
73 xs = ys;
74 DestIterator xd(yd);
75
76 for(x = 0; x != w; ++x, ++xs.dim0(), ++xd.dim0())
77 {
78 AtVolumeBorder atBorder = isAtVolumeBorder(x,y,z,w,h,d);
79 typename SrcAccessor::value_type v = sa(xs);
80 // the following choice causes minima to point
81 // to their lowest neighbor -- would this be better???
82 // typename SrcAccessor::value_type v = NumericTraits<typename SrcAccessor::value_type>::max();
83 int o = 0; // means center is minimum
84 typename SrcAccessor::value_type my_v = v;
85 if(atBorder == NotAtBorder)
86 {
87 NeighborhoodCirculator<SrcIterator, Neighborhood3D> c(xs), cend(c);
88
89 do {
90 if(sa(c) < v)
91 {
92 v = sa(c);
93 o = c.directionBit();
94 }
95 else if(sa(c) == my_v && my_v == v)
96 {
97 o = o | c.directionBit();
98 }
99 }
100 while(++c != cend);
101 }
102 else
103 {
104 RestrictedNeighborhoodCirculator<SrcIterator, Neighborhood3D> c(xs, atBorder), cend(c);
105 do {
106 if(sa(c) < v)
107 {
108 v = sa(c);
109 o = c.directionBit();
110 }
111 else if(sa(c) == my_v && my_v == v)
112 {
113 o = o | c.directionBit();
114 }
115 }
116 while(++c != cend);
117 }
118 if (o==0) local_min_count++;
119 da.set(o, xd);
120 }//end x-iteration
121 }//end y-iteration
122 }//end z-iteration
123 return local_min_count;
124 }
125
126 template <class SrcIterator, class SrcAccessor,class SrcShape,
127 class DestIterator, class DestAccessor,
128 class Neighborhood3D>
129 unsigned int watershedLabeling3D( SrcIterator s_Iter, SrcShape srcShape, SrcAccessor sa,
130 DestIterator d_Iter, DestAccessor da,
131 Neighborhood3D)
132 {
133 typedef typename DestAccessor::value_type LabelType;
134
135 //basically needed for iteration and border-checks
136 int w = srcShape[0], h = srcShape[1], d = srcShape[2];
137 int x,y,z;
138
139 //declare and define Iterators for all three dims at src
140 SrcIterator zs = s_Iter;
141 DestIterator zd = d_Iter;
142
143 // temporary image to store region labels
144 UnionFindArray<LabelType> labels;
145
146 // initialize the neighborhood traversers
147 NeighborOffsetCirculator<Neighborhood3D> nc(Neighborhood3D::CausalFirst);
148 NeighborOffsetCirculator<Neighborhood3D> nce(Neighborhood3D::CausalLast);
149 ++nce;
150 // pass 1: scan image from upper left front to lower right back
151 // to find connected components
152
153 // Each component will be represented by a tree of pixels. Each
154 // pixel contains the scan order address of its parent in the
155 // tree. In order for pass 2 to work correctly, the parent must
156 // always have a smaller scan order address than the child.
157 // Therefore, we can merge trees only at their roots, because the
158 // root of the combined tree must have the smallest scan order
159 // address among all the tree's pixels/ nodes. The root of each
160 // tree is distinguished by pointing to itself (it contains its
161 // own scan order address). This condition is enforced whenever a
162 // new region is found or two regions are merged
163 for(z = 0; z != d; ++z, ++zs.dim2(), ++zd.dim2())
164 {
165 SrcIterator ys = zs;
166 DestIterator yd = zd;
167
168 for(y = 0; y != h; ++y, ++ys.dim1(), ++yd.dim1())
169 {
170 SrcIterator xs = ys;
171 DestIterator xd = yd;
172
173 for(x = 0; x != w; ++x, ++xs.dim0(), ++xd.dim0())
174 {
175 LabelType currentIndex = labels.nextFreeIndex(); // default: new region
176
177 //check whether there is a special border treatment to be used or not
178 AtVolumeBorder atBorder = isAtVolumeBorderCausal(x,y,z,w,h,d);
179
180 //We are not at the border!
181 if(atBorder == NotAtBorder)
182 {
183
184 nc = NeighborOffsetCirculator<Neighborhood3D>(Neighborhood3D::CausalFirst);
185
186 do
187 {
188 // Direction of NTraversr Neighbor's direction bit is pointing
189 // = Direction of voxel towards us?
190 if((sa(xs) & nc.directionBit()) || (sa(xs,*nc) & nc.oppositeDirectionBit()))
191 {
192 currentIndex = labels.makeUnion(da(xd,*nc), currentIndex);
193 }
194 ++nc;
195 }while(nc!=nce);
196 }
197 //we are at a border - handle this!!
198 else
199 {
200 nc = NeighborOffsetCirculator<Neighborhood3D>(Neighborhood3D::nearBorderDirectionsCausal(atBorder,0));
201 int j=0;
202 while(nc.direction() != Neighborhood3D::Error)
203 {
204 // Direction of NTraversr Neighbor's direction bit is pointing
205 // = Direction of voxel towards us?
206 if((sa(xs) & nc.directionBit()) || (sa(xs,*nc) & nc.oppositeDirectionBit()))
207 {
208 currentIndex = labels.makeUnion(da(xd,*nc), currentIndex);
209 }
210 nc.turnTo(Neighborhood3D::nearBorderDirectionsCausal(atBorder,++j));
211 }
212 }
213 da.set(labels.finalizeIndex(currentIndex), xd);
214 }
215 }
216 }
217
218 unsigned int count = labels.makeContiguous();
219
220 // pass 2: assign one label to each region (tree)
221 // so that labels form a consecutive sequence 1, 2, ...
222 zd = d_Iter;
223 for(z=0; z != d; ++z, ++zd.dim2())
224 {
225 DestIterator yd(zd);
226
227 for(y=0; y != h; ++y, ++yd.dim1())
228 {
229 DestIterator xd(yd);
230
231 for(x = 0; x != w; ++x, ++xd.dim0())
232 {
233 da.set(labels.findLabel(da(xd)), xd);
234 }
235 }
236 }
237 return count;
238 }
239
240
241 /** \addtogroup Superpixels
242 */
243 //@{
244
245 /********************************************************/
246 /* */
247 /* watersheds3D */
248 /* */
249 /********************************************************/
250
251 /** \brief Region Segmentation by means of the watershed algorithm.
252
253 This function is deprecated, use \ref watershedsMultiArray() instead.
254
255 <b> Declarations:</b>
256
257 \deprecatedAPI{watersheds3D}
258 pass \ref MultiIteratorPage "MultiIterators" and \ref DataAccessors :
259 \code
260 namespace vigra {
261 template <class SrcIterator, class SrcAccessor,class SrcShape,
262 class DestIterator, class DestAccessor,
263 class Neighborhood3D>
264 unsigned int watersheds3D(SrcIterator s_Iter, SrcShape srcShape, SrcAccessor sa,
265 DestIterator d_Iter, DestAccessor da,
266 Neighborhood3D neighborhood3D);
267 }
268 \endcode
269 use argument objects in conjunction with \ref ArgumentObjectFactories :
270 \code
271 namespace vigra {
272 template <class SrcIterator, class SrcAccessor,class SrcShape,
273 class DestIterator, class DestAccessor,
274 class Neighborhood3D>
275 unsigned int watersheds3D(triple<SrcIterator, SrcShape, SrcAccessor> src,
276 pair<DestIterator, DestAccessor> dest,
277 Neighborhood3D neighborhood3D);
278 }
279 \endcode
280
281 use with 3D-Six-Neighborhood:
282 \code
283 namespace vigra {
284
285 template <class SrcIterator, class SrcAccessor,class SrcShape,
286 class DestIterator, class DestAccessor>
287 unsigned int watersheds3DSix(triple<SrcIterator, SrcShape, SrcAccessor> src,
288 pair<DestIterator, DestAccessor> dest);
289
290 }
291 \endcode
292
293 use with 3D-TwentySix-Neighborhood:
294 \code
295 namespace vigra {
296
297 template <class SrcIterator, class SrcAccessor,class SrcShape,
298 class DestIterator, class DestAccessor>
299 unsigned int watersheds3DTwentySix(triple<SrcIterator, SrcShape, SrcAccessor> src,
300 pair<DestIterator, DestAccessor> dest);
301
302 }
303 \endcode
304 \deprecatedEnd
305
306 This function implements the union-find version of the watershed algorithms
307 as described in
308
309 J. Roerdink, R. Meijster: <em>"The watershed transform: definitions, algorithms,
310 and parallelization strategies"</em>, Fundamenta Informaticae, 41:187-228, 2000
311
312 The source volume is a boundary indicator such as the gradient magnitude
313 of the trace of the \ref boundaryTensor(). Local minima of the boundary indicator
314 are used as region seeds, and all other voxels are recursively assigned to the same
315 region as their lowest neighbor. Pass \ref vigra::NeighborCode3DSix or
316 \ref vigra::NeighborCode3DTwentySix to determine the neighborhood where voxel values
317 are compared. The voxel type of the input volume must be <tt>LessThanComparable</tt>.
318
319 <b> Usage:</b>
320
321 <b>\#include</b> <vigra/watersheds3D.hxx><br>
322 Namespace: vigra
323
324 Example: watersheds3D of the gradient magnitude.
325
326 \code
327 Shape3 shape(w, h, d);
328
329 MultiArray<3, float> src(shape), grad(shape);
330 ...
331
332 double scale = 1;
333 gaussianGradientMagnitude(src, grad, scale);
334
335 MultiArray<3, int> labels(shape);
336
337 // find 6-connected regions
338 int max_region_label = watersheds3DSix(grad, labels);
339
340 // find 26-connected regions
341 max_region_label = watersheds3DTwentySix(grad, labels);
342 \endcode
343
344 \deprecatedUsage{watersheds3D}
345 \code
346 typedef vigra::MultiArray<3,int> IntVolume;
347 typedef vigra::MultiArray<3,double> DVolume;
348 DVolume src(DVolume::difference_type(w,h,d));
349 IntVolume dest(IntVolume::difference_type(w,h,d));
350
351 float gauss=1;
352
353 vigra::MultiArray<3, vigra::TinyVector<float,3> > temp(IntVolume::difference_type(w,h,d));
354 vigra::gaussianGradientMultiArray(srcMultiArrayRange(vol),destMultiArray(temp),gauss);
355
356 IntVolume::iterator temp_iter=temp.begin();
357 for(DVolume::iterator iter=src.begin(); iter!=src.end(); ++iter, ++temp_iter)
358 *iter = norm(*temp_iter);
359
360 // find 6-connected regions
361 int max_region_label = vigra::watersheds3DSix(srcMultiArrayRange(src), destMultiArray(dest));
362
363 // find 26-connected regions
364 max_region_label = vigra::watersheds3DTwentySix(srcMultiArrayRange(src), destMultiArray(dest));
365
366 \endcode
367 <b> Required Interface:</b>
368 \code
369 SrcIterator src_begin;
370 SrcShape src_shape;
371 DestIterator dest_begin;
372
373 SrcAccessor src_accessor;
374 DestAccessor dest_accessor;
375
376 // compare src values
377 src_accessor(src_begin) <= src_accessor(src_begin)
378
379 // set result
380 int label;
381 dest_accessor.set(label, dest_begin);
382 \endcode
383 \deprecatedEnd
384 */
385 doxygen_overloaded_function(template <...> unsigned int watersheds3D)
386
387 template <class SrcIterator, class SrcAccessor, class SrcShape,
388 class DestIterator, class DestAccessor,
389 class Neighborhood3D>
390 unsigned int watersheds3D( SrcIterator s_Iter, SrcShape srcShape, SrcAccessor sa,
391 DestIterator d_Iter, DestAccessor da, Neighborhood3D neighborhood3D)
392 {
393 //create temporary volume to store the DAG of directions to minima
394 if ((int)Neighborhood3D::DirectionCount>7){ //If we have 3D-TwentySix Neighborhood
395
396 vigra::MultiArray<3,int> orientationVolume(srcShape);
397
398 preparewatersheds3D( s_Iter, srcShape, sa,
399 destMultiArray(orientationVolume).first, destMultiArray(orientationVolume).second,
400 neighborhood3D);
401
402 return watershedLabeling3D( srcMultiArray(orientationVolume).first, srcShape, srcMultiArray(orientationVolume).second,
403 d_Iter, da,
404 neighborhood3D);
405 }
406 else{
407
408 vigra::MultiArray<3,unsigned char> orientationVolume(srcShape);
409
410 preparewatersheds3D( s_Iter, srcShape, sa,
411 destMultiArray(orientationVolume).first, destMultiArray(orientationVolume).second,
412 neighborhood3D);
413
414 return watershedLabeling3D( srcMultiArray(orientationVolume).first, srcShape, srcMultiArray(orientationVolume).second,
415 d_Iter, da,
416 neighborhood3D);
417 }
418 }
419
420 template <class SrcIterator, class SrcShape, class SrcAccessor,
421 class DestIterator, class DestAccessor>
422 inline unsigned int watersheds3DSix( triple<SrcIterator, SrcShape, SrcAccessor> src,
423 pair<DestIterator, DestAccessor> dest)
424 {
425 return watersheds3D(src.first, src.second, src.third, dest.first, dest.second, NeighborCode3DSix());
426 }
427
428 template <class SrcIterator, class SrcShape, class SrcAccessor,
429 class DestIterator, class DestAccessor>
430 inline unsigned int watersheds3DTwentySix( triple<SrcIterator, SrcShape, SrcAccessor> src,
431 pair<DestIterator, DestAccessor> dest)
432 {
433 return watersheds3D(src.first, src.second, src.third, dest.first, dest.second, NeighborCode3DTwentySix());
434 }
435
436 template <unsigned int N, class T1, class S1,
437 class T2, class S2>
438 inline unsigned int
439 watersheds3DSix(MultiArrayView<N, T1, S1> const & source,
440 MultiArrayView<N, T2, S2> dest)
441 {
442 vigra_precondition(source.shape() == dest.shape(),
443 "watersheds3DSix(): shape mismatch between input and output.");
444 return watersheds3DSix(srcMultiArrayRange(source), destMultiArray(dest));
445 }
446
447 template <unsigned int N, class T1, class S1,
448 class T2, class S2>
449 inline unsigned int
450 watersheds3DTwentySix(MultiArrayView<N, T1, S1> const & source,
451 MultiArrayView<N, T2, S2> dest)
452 {
453 vigra_precondition(source.shape() == dest.shape(),
454 "watersheds3DTwentySix(): shape mismatch between input and output.");
455 return watersheds3DTwentySix(srcMultiArrayRange(source), destMultiArray(dest));
456 }
457
458 }//namespace vigra
459
460 #endif //VIGRA_watersheds3D_HXX
vigra::AtVolumeBorder
AtImageBorder AtVolumeBorder
Encode whether a voxel is near the volume border.
Definition: voxelneighborhood.hxx:72
vigra::NeighborCode3DTwentySix
Neighborhood3DTwentySix::NeighborCode3D NeighborCode3DTwentySix
Definition: voxelneighborhood.hxx:1646
vigra::MultiArray
Main MultiArray class containing the memory management.
Definition: multi_array.hxx:2474
vigra::isAtVolumeBorderCausal
AtVolumeBorder isAtVolumeBorderCausal(int x, int y, int z, int width, int height, int)
Find out whether a voxel is at a scan-order relevant volume border. This function checks if x == 0 or...
Definition: voxelneighborhood.hxx:112
vigra::isAtVolumeBorder
AtVolumeBorder isAtVolumeBorder(int x, int y, int z, int width, int height, int depth)
Find out whether a voxel is at the volume border.
Definition: voxelneighborhood.hxx:82
vigra::boost_graph::source
vigra::GridGraph< N, DirectedTag >::vertex_descriptor source(typename vigra::GridGraph< N, DirectedTag >::edge_descriptor const &e, vigra::GridGraph< N, DirectedTag > const &g)
Get a vertex descriptor for the start vertex of edge e in graph g (API: boost).
Definition: multi_gridgraph.hxx:2943
vigra::doxygen_overloaded_function
doxygen_overloaded_function(template<...> void separableConvolveBlockwise) template< unsigned int N
Separated convolution on ChunkedArrays.
vigra::NeighborCode3DSix
Neighborhood3DSix::NeighborCode3D NeighborCode3DSix
Definition: voxelneighborhood.hxx:490
vigra::watersheds3D
unsigned int watersheds3D(...)
Region Segmentation by means of the watershed algorithm.
vigra::NotAtBorder
Definition: pixelneighborhood.hxx:70

© 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.0 (Fri May 19 2017)