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

multi_distance.hxx VIGRA

1 /************************************************************************/
2 /* */
3 /* Copyright 2003-2007 by Kasim Terzic, Christian-Dennis Rahn, */
4 /* Philipp Schubert and Ullrich Koethe */
5 /* */
6 /* This file is part of the VIGRA computer vision library. */
7 /* The VIGRA Website is */
8 /* http://hci.iwr.uni-heidelberg.de/vigra/ */
9 /* Please direct questions, bug reports, and contributions to */
10 /* ullrich.koethe@iwr.uni-heidelberg.de or */
11 /* vigra@informatik.uni-hamburg.de */
12 /* */
13 /* Permission is hereby granted, free of charge, to any person */
14 /* obtaining a copy of this software and associated documentation */
15 /* files (the "Software"), to deal in the Software without */
16 /* restriction, including without limitation the rights to use, */
17 /* copy, modify, merge, publish, distribute, sublicense, and/or */
18 /* sell copies of the Software, and to permit persons to whom the */
19 /* Software is furnished to do so, subject to the following */
20 /* conditions: */
21 /* */
22 /* The above copyright notice and this permission notice shall be */
23 /* included in all copies or substantial portions of the */
24 /* Software. */
25 /* */
26 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
27 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
28 /* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
29 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
30 /* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
31 /* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
32 /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
33 /* OTHER DEALINGS IN THE SOFTWARE. */
34 /* */
35 /************************************************************************/
36 
37 #ifndef VIGRA_MULTI_DISTANCE_HXX
38 #define VIGRA_MULTI_DISTANCE_HXX
39 
40 #include <vector>
41 #include <functional>
42 #include "array_vector.hxx"
43 #include "multi_array.hxx"
44 #include "accessor.hxx"
45 #include "numerictraits.hxx"
46 #include "navigator.hxx"
47 #include "metaprogramming.hxx"
48 #include "multi_pointoperators.hxx"
49 #include "functorexpression.hxx"
50 
51 #include "multi_gridgraph.hxx" //for boundaryGraph & boundaryMultiDistance
52 #include "union_find.hxx" //for boundaryGraph & boundaryMultiDistance
53 
54 namespace vigra
55 {
56 
57 namespace detail
58 {
59 
60 template <class Value>
61 struct DistParabolaStackEntry
62 {
63  double left, center, right;
64  Value apex_height;
65 
66  DistParabolaStackEntry(Value const & p, double l, double c, double r)
67  : left(l), center(c), right(r), apex_height(p)
68  {}
69 };
70 
71 /********************************************************/
72 /* */
73 /* distParabola */
74 /* */
75 /* Version with sigma (parabola spread) for morphology */
76 /* */
77 /********************************************************/
78 
79 template <class SrcIterator, class SrcAccessor,
80  class DestIterator, class DestAccessor >
81 void distParabola(SrcIterator is, SrcIterator iend, SrcAccessor sa,
82  DestIterator id, DestAccessor da, double sigma )
83 {
84  // We assume that the data in the input is distance squared and treat it as such
85  double w = iend - is;
86  if(w <= 0)
87  return;
88 
89  double sigma2 = sigma * sigma;
90  double sigma22 = 2.0 * sigma2;
91 
92  typedef typename SrcAccessor::value_type SrcType;
93  typedef DistParabolaStackEntry<SrcType> Influence;
94  std::vector<Influence> _stack;
95  _stack.push_back(Influence(sa(is), 0.0, 0.0, w));
96 
97  ++is;
98  double current = 1.0;
99  for(;current < w; ++is, ++current)
100  {
101  double intersection;
102 
103  while(true)
104  {
105  Influence & s = _stack.back();
106  double diff = current - s.center;
107  intersection = current + (sa(is) - s.apex_height - sigma2*sq(diff)) / (sigma22 * diff);
108 
109  if( intersection < s.left) // previous point has no influence
110  {
111  _stack.pop_back();
112  if(!_stack.empty())
113  continue; // try new top of stack without advancing current
114  else
115  intersection = 0.0;
116  }
117  else if(intersection < s.right)
118  {
119  s.right = intersection;
120  }
121  break;
122  }
123  _stack.push_back(Influence(sa(is), intersection, current, w));
124  }
125 
126  // Now we have the stack indicating which rows are influenced by (and therefore
127  // closest to) which row. We can go through the stack and calculate the
128  // distance squared for each element of the column.
129  typename std::vector<Influence>::iterator it = _stack.begin();
130  for(current = 0.0; current < w; ++current, ++id)
131  {
132  while( current >= it->right)
133  ++it;
134  da.set(sigma2 * sq(current - it->center) + it->apex_height, id);
135  }
136 }
137 
138 template <class SrcIterator, class SrcAccessor,
139  class DestIterator, class DestAccessor>
140 inline void distParabola(triple<SrcIterator, SrcIterator, SrcAccessor> src,
141  pair<DestIterator, DestAccessor> dest, double sigma)
142 {
143  distParabola(src.first, src.second, src.third,
144  dest.first, dest.second, sigma);
145 }
146 
147 /********************************************************/
148 /* */
149 /* internalSeparableMultiArrayDistTmp */
150 /* */
151 /********************************************************/
152 
153 template <class SrcIterator, class SrcShape, class SrcAccessor,
154  class DestIterator, class DestAccessor, class Array>
155 void internalSeparableMultiArrayDistTmp(
156  SrcIterator si, SrcShape const & shape, SrcAccessor src,
157  DestIterator di, DestAccessor dest, Array const & sigmas, bool invert)
158 {
159  // Sigma is the spread of the parabolas. It determines the structuring element size
160  // for ND morphology. When calculating the distance transforms, sigma is usually set to 1,
161  // unless one wants to account for anisotropic pixel pitch
162  enum { N = SrcShape::static_size};
163 
164  // we need the Promote type here if we want to invert the image (dilation)
165  typedef typename NumericTraits<typename DestAccessor::value_type>::RealPromote TmpType;
166 
167  // temporary array to hold the current line to enable in-place operation
168  ArrayVector<TmpType> tmp( shape[0] );
169 
170  typedef MultiArrayNavigator<SrcIterator, N> SNavigator;
171  typedef MultiArrayNavigator<DestIterator, N> DNavigator;
172 
173 
174  // only operate on first dimension here
175  SNavigator snav( si, shape, 0 );
176  DNavigator dnav( di, shape, 0 );
177 
178  using namespace vigra::functor;
179 
180  for( ; snav.hasMore(); snav++, dnav++ )
181  {
182  // first copy source to temp for maximum cache efficiency
183  // Invert the values if necessary. Only needed for grayscale morphology
184  if(invert)
185  transformLine( snav.begin(), snav.end(), src, tmp.begin(),
186  typename AccessorTraits<TmpType>::default_accessor(),
187  Param(NumericTraits<TmpType>::zero())-Arg1());
188  else
189  copyLine( snav.begin(), snav.end(), src, tmp.begin(),
190  typename AccessorTraits<TmpType>::default_accessor() );
191 
192  detail::distParabola( srcIterRange(tmp.begin(), tmp.end(),
193  typename AccessorTraits<TmpType>::default_const_accessor()),
194  destIter( dnav.begin(), dest ), sigmas[0] );
195  }
196 
197  // operate on further dimensions
198  for( int d = 1; d < N; ++d )
199  {
200  DNavigator dnav( di, shape, d );
201 
202  tmp.resize( shape[d] );
203 
204 
205  for( ; dnav.hasMore(); dnav++ )
206  {
207  // first copy source to temp for maximum cache efficiency
208  copyLine( dnav.begin(), dnav.end(), dest,
209  tmp.begin(), typename AccessorTraits<TmpType>::default_accessor() );
210 
211  detail::distParabola( srcIterRange(tmp.begin(), tmp.end(),
212  typename AccessorTraits<TmpType>::default_const_accessor()),
213  destIter( dnav.begin(), dest ), sigmas[d] );
214  }
215  }
216  if(invert) transformMultiArray( di, shape, dest, di, dest, -Arg1());
217 }
218 
219 template <class SrcIterator, class SrcShape, class SrcAccessor,
220  class DestIterator, class DestAccessor, class Array>
221 inline void internalSeparableMultiArrayDistTmp( SrcIterator si, SrcShape const & shape, SrcAccessor src,
222  DestIterator di, DestAccessor dest, Array const & sigmas)
223 {
224  internalSeparableMultiArrayDistTmp( si, shape, src, di, dest, sigmas, false );
225 }
226 
227 template <class SrcIterator, class SrcShape, class SrcAccessor,
228  class DestIterator, class DestAccessor>
229 inline void internalSeparableMultiArrayDistTmp( SrcIterator si, SrcShape const & shape, SrcAccessor src,
230  DestIterator di, DestAccessor dest)
231 {
232  ArrayVector<double> sigmas(shape.size(), 1.0);
233  internalSeparableMultiArrayDistTmp( si, shape, src, di, dest, sigmas, false );
234 }
235 
236 } // namespace detail
237 
238 /** \addtogroup DistanceTransform
239 */
240 //@{
241 
242 /********************************************************/
243 /* */
244 /* separableMultiDistSquared */
245 /* */
246 /********************************************************/
247 
248 /** \brief Euclidean distance squared on multi-dimensional arrays.
249 
250  The algorithm is taken from Donald Bailey: "An Efficient Euclidean Distance Transform",
251  Proc. IWCIA'04, Springer LNCS 3322, 2004.
252 
253  <b> Declarations:</b>
254 
255  pass arbitrary-dimensional array views:
256  \code
257  namespace vigra {
258  // explicitly specify pixel pitch for each coordinate
259  template <unsigned int N, class T1, class S1,
260  class T2, class S2,
261  class Array>
262  void
263  separableMultiDistSquared(MultiArrayView<N, T1, S1> const & source,
264  MultiArrayView<N, T2, S2> dest,
265  bool background,
266  Array const & pixelPitch);
267 
268  // use default pixel pitch = 1.0 for each coordinate
269  template <unsigned int N, class T1, class S1,
270  class T2, class S2>
271  void
272  separableMultiDistSquared(MultiArrayView<N, T1, S1> const & source,
273  MultiArrayView<N, T2, S2> dest,
274  bool background);
275  }
276  \endcode
277 
278  \deprecatedAPI{separableMultiDistSquared}
279  pass \ref MultiIteratorPage "MultiIterators" and \ref DataAccessors :
280  \code
281  namespace vigra {
282  // explicitly specify pixel pitch for each coordinate
283  template <class SrcIterator, class SrcShape, class SrcAccessor,
284  class DestIterator, class DestAccessor, class Array>
285  void
286  separableMultiDistSquared( SrcIterator s, SrcShape const & shape, SrcAccessor src,
287  DestIterator d, DestAccessor dest,
288  bool background,
289  Array const & pixelPitch);
290 
291  // use default pixel pitch = 1.0 for each coordinate
292  template <class SrcIterator, class SrcShape, class SrcAccessor,
293  class DestIterator, class DestAccessor>
294  void
295  separableMultiDistSquared(SrcIterator siter, SrcShape const & shape, SrcAccessor src,
296  DestIterator diter, DestAccessor dest,
297  bool background);
298 
299  }
300  \endcode
301  use argument objects in conjunction with \ref ArgumentObjectFactories :
302  \code
303  namespace vigra {
304  // explicitly specify pixel pitch for each coordinate
305  template <class SrcIterator, class SrcShape, class SrcAccessor,
306  class DestIterator, class DestAccessor, class Array>
307  void
308  separableMultiDistSquared( triple<SrcIterator, SrcShape, SrcAccessor> const & source,
309  pair<DestIterator, DestAccessor> const & dest,
310  bool background,
311  Array const & pixelPitch);
312 
313  // use default pixel pitch = 1.0 for each coordinate
314  template <class SrcIterator, class SrcShape, class SrcAccessor,
315  class DestIterator, class DestAccessor>
316  void
317  separableMultiDistSquared(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
318  pair<DestIterator, DestAccessor> const & dest,
319  bool background);
320 
321  }
322  \endcode
323  \deprecatedEnd
324 
325  This function performs a squared Euclidean squared distance transform on the given
326  multi-dimensional array. Both source and destination
327  arrays are represented by iterators, shape objects and accessors.
328  The destination array is required to already have the correct size.
329 
330  This function expects a mask as its source, where background pixels are
331  marked as zero, and non-background pixels as non-zero. If the parameter
332  <i>background</i> is true, then the squared distance of all background
333  pixels to the nearest object is calculated. Otherwise, the distance of all
334  object pixels to the nearest background pixel is calculated.
335 
336  Optionally, one can pass an array that specifies the pixel pitch in each direction.
337  This is necessary when the data have non-uniform resolution (as is common in confocal
338  microscopy, for example).
339 
340  This function may work in-place, which means that <tt>siter == diter</tt> is allowed.
341  A full-sized internal array is only allocated if working on the destination
342  array directly would cause overflow errors (i.e. if
343  <tt> NumericTraits<typename DestAccessor::value_type>::max() < N * M*M</tt>, where M is the
344  size of the largest dimension of the array.
345 
346  <b> Usage:</b>
347 
348  <b>\#include</b> <vigra/multi_distance.hxx><br/>
349  Namespace: vigra
350 
351  \code
352  Shape3 shape(width, height, depth);
353  MultiArray<3, unsigned char> source(shape);
354  MultiArray<3, unsigned int> dest(shape);
355  ...
356 
357  // Calculate Euclidean distance squared for all background pixels
358  separableMultiDistSquared(source, dest, true);
359  \endcode
360 
361  \see vigra::distanceTransform(), vigra::separableMultiDistance()
362 */
364 
365 template <class SrcIterator, class SrcShape, class SrcAccessor,
366  class DestIterator, class DestAccessor, class Array>
367 void separableMultiDistSquared( SrcIterator s, SrcShape const & shape, SrcAccessor src,
368  DestIterator d, DestAccessor dest, bool background,
369  Array const & pixelPitch)
370 {
371  int N = shape.size();
372 
373  typedef typename SrcAccessor::value_type SrcType;
374  typedef typename DestAccessor::value_type DestType;
375  typedef typename NumericTraits<DestType>::RealPromote Real;
376 
377  SrcType zero = NumericTraits<SrcType>::zero();
378 
379  double dmax = 0.0;
380  bool pixelPitchIsReal = false;
381  for( int k=0; k<N; ++k)
382  {
383  if(int(pixelPitch[k]) != pixelPitch[k])
384  pixelPitchIsReal = true;
385  dmax += sq(pixelPitch[k]*shape[k]);
386  }
387 
388  using namespace vigra::functor;
389 
390  if(dmax > NumericTraits<DestType>::toRealPromote(NumericTraits<DestType>::max())
391  || pixelPitchIsReal) // need a temporary array to avoid overflows
392  {
393  // Threshold the values so all objects have infinity value in the beginning
394  Real maxDist = (Real)dmax, rzero = (Real)0.0;
395  MultiArray<SrcShape::static_size, Real> tmpArray(shape);
396  if(background == true)
397  transformMultiArray( s, shape, src,
398  tmpArray.traverser_begin(), typename AccessorTraits<Real>::default_accessor(),
399  ifThenElse( Arg1() == Param(zero), Param(maxDist), Param(rzero) ));
400  else
401  transformMultiArray( s, shape, src,
402  tmpArray.traverser_begin(), typename AccessorTraits<Real>::default_accessor(),
403  ifThenElse( Arg1() != Param(zero), Param(maxDist), Param(rzero) ));
404 
405  detail::internalSeparableMultiArrayDistTmp( tmpArray.traverser_begin(),
406  shape, typename AccessorTraits<Real>::default_accessor(),
407  tmpArray.traverser_begin(),
408  typename AccessorTraits<Real>::default_accessor(), pixelPitch);
409 
410  copyMultiArray(srcMultiArrayRange(tmpArray), destIter(d, dest));
411  }
412  else // work directly on the destination array
413  {
414  // Threshold the values so all objects have infinity value in the beginning
415  DestType maxDist = DestType(std::ceil(dmax)), rzero = (DestType)0;
416  if(background == true)
417  transformMultiArray( s, shape, src, d, dest,
418  ifThenElse( Arg1() == Param(zero), Param(maxDist), Param(rzero) ));
419  else
420  transformMultiArray( s, shape, src, d, dest,
421  ifThenElse( Arg1() != Param(zero), Param(maxDist), Param(rzero) ));
422 
423  detail::internalSeparableMultiArrayDistTmp( d, shape, dest, d, dest, pixelPitch);
424  }
425 }
426 
427 template <class SrcIterator, class SrcShape, class SrcAccessor,
428  class DestIterator, class DestAccessor>
429 inline
430 void separableMultiDistSquared( SrcIterator s, SrcShape const & shape, SrcAccessor src,
431  DestIterator d, DestAccessor dest, bool background)
432 {
433  ArrayVector<double> pixelPitch(shape.size(), 1.0);
434  separableMultiDistSquared( s, shape, src, d, dest, background, pixelPitch );
435 }
436 
437 template <class SrcIterator, class SrcShape, class SrcAccessor,
438  class DestIterator, class DestAccessor, class Array>
439 inline void separableMultiDistSquared( triple<SrcIterator, SrcShape, SrcAccessor> const & source,
440  pair<DestIterator, DestAccessor> const & dest, bool background,
441  Array const & pixelPitch)
442 {
443  separableMultiDistSquared( source.first, source.second, source.third,
444  dest.first, dest.second, background, pixelPitch );
445 }
446 
447 template <class SrcIterator, class SrcShape, class SrcAccessor,
448  class DestIterator, class DestAccessor>
449 inline void separableMultiDistSquared( triple<SrcIterator, SrcShape, SrcAccessor> const & source,
450  pair<DestIterator, DestAccessor> const & dest, bool background)
451 {
452  separableMultiDistSquared( source.first, source.second, source.third,
453  dest.first, dest.second, background );
454 }
455 
456 template <unsigned int N, class T1, class S1,
457  class T2, class S2,
458  class Array>
459 inline void
460 separableMultiDistSquared(MultiArrayView<N, T1, S1> const & source,
461  MultiArrayView<N, T2, S2> dest, bool background,
462  Array const & pixelPitch)
463 {
464  vigra_precondition(source.shape() == dest.shape(),
465  "separableMultiDistSquared(): shape mismatch between input and output.");
466  separableMultiDistSquared( srcMultiArrayRange(source),
467  destMultiArray(dest), background, pixelPitch );
468 }
469 
470 template <unsigned int N, class T1, class S1,
471  class T2, class S2>
472 inline void
473 separableMultiDistSquared(MultiArrayView<N, T1, S1> const & source,
474  MultiArrayView<N, T2, S2> dest, bool background)
475 {
476  vigra_precondition(source.shape() == dest.shape(),
477  "separableMultiDistSquared(): shape mismatch between input and output.");
478  separableMultiDistSquared( srcMultiArrayRange(source),
479  destMultiArray(dest), background );
480 }
481 
482 /********************************************************/
483 /* */
484 /* separableMultiDistance */
485 /* */
486 /********************************************************/
487 
488 /** \brief Euclidean distance on multi-dimensional arrays.
489 
490  <b> Declarations:</b>
491 
492  pass arbitrary-dimensional array views:
493  \code
494  namespace vigra {
495  // explicitly specify pixel pitch for each coordinate
496  template <unsigned int N, class T1, class S1,
497  class T2, class S2, class Array>
498  void
499  separableMultiDistance(MultiArrayView<N, T1, S1> const & source,
500  MultiArrayView<N, T2, S2> dest,
501  bool background,
502  Array const & pixelPitch);
503 
504  // use default pixel pitch = 1.0 for each coordinate
505  template <unsigned int N, class T1, class S1,
506  class T2, class S2>
507  void
508  separableMultiDistance(MultiArrayView<N, T1, S1> const & source,
509  MultiArrayView<N, T2, S2> dest,
510  bool background);
511  }
512  \endcode
513 
514  \deprecatedAPI{separableMultiDistance}
515  pass \ref MultiIteratorPage "MultiIterators" and \ref DataAccessors :
516  \code
517  namespace vigra {
518  // explicitly specify pixel pitch for each coordinate
519  template <class SrcIterator, class SrcShape, class SrcAccessor,
520  class DestIterator, class DestAccessor, class Array>
521  void
522  separableMultiDistance( SrcIterator s, SrcShape const & shape, SrcAccessor src,
523  DestIterator d, DestAccessor dest,
524  bool background,
525  Array const & pixelPitch);
526 
527  // use default pixel pitch = 1.0 for each coordinate
528  template <class SrcIterator, class SrcShape, class SrcAccessor,
529  class DestIterator, class DestAccessor>
530  void
531  separableMultiDistance(SrcIterator siter, SrcShape const & shape, SrcAccessor src,
532  DestIterator diter, DestAccessor dest,
533  bool background);
534 
535  }
536  \endcode
537  use argument objects in conjunction with \ref ArgumentObjectFactories :
538  \code
539  namespace vigra {
540  // explicitly specify pixel pitch for each coordinate
541  template <class SrcIterator, class SrcShape, class SrcAccessor,
542  class DestIterator, class DestAccessor, class Array>
543  void
544  separableMultiDistance( triple<SrcIterator, SrcShape, SrcAccessor> const & source,
545  pair<DestIterator, DestAccessor> const & dest,
546  bool background,
547  Array const & pixelPitch);
548 
549  // use default pixel pitch = 1.0 for each coordinate
550  template <class SrcIterator, class SrcShape, class SrcAccessor,
551  class DestIterator, class DestAccessor>
552  void
553  separableMultiDistance(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
554  pair<DestIterator, DestAccessor> const & dest,
555  bool background);
556 
557  }
558  \endcode
559  \deprecatedEnd
560 
561  This function performs a Euclidean distance transform on the given
562  multi-dimensional array. It simply calls \ref separableMultiDistSquared()
563  and takes the pixel-wise square root of the result. See \ref separableMultiDistSquared()
564  for more documentation.
565 
566  <b> Usage:</b>
567 
568  <b>\#include</b> <vigra/multi_distance.hxx><br/>
569  Namespace: vigra
570 
571  \code
572  Shape3 shape(width, height, depth);
573  MultiArray<3, unsigned char> source(shape);
574  MultiArray<3, float> dest(shape);
575  ...
576 
577  // Calculate Euclidean distance for all background pixels
578  separableMultiDistance(source, dest, true);
579  \endcode
580 
581  \see vigra::distanceTransform(), vigra::separableMultiDistSquared()
582 */
584 
585 template <class SrcIterator, class SrcShape, class SrcAccessor,
586  class DestIterator, class DestAccessor, class Array>
587 void separableMultiDistance( SrcIterator s, SrcShape const & shape, SrcAccessor src,
588  DestIterator d, DestAccessor dest, bool background,
589  Array const & pixelPitch)
590 {
591  separableMultiDistSquared( s, shape, src, d, dest, background, pixelPitch);
592 
593  // Finally, calculate the square root of the distances
594  using namespace vigra::functor;
595 
596  transformMultiArray( d, shape, dest, d, dest, sqrt(Arg1()) );
597 }
598 
599 template <class SrcIterator, class SrcShape, class SrcAccessor,
600  class DestIterator, class DestAccessor>
601 void separableMultiDistance( SrcIterator s, SrcShape const & shape, SrcAccessor src,
602  DestIterator d, DestAccessor dest, bool background)
603 {
604  separableMultiDistSquared( s, shape, src, d, dest, background);
605 
606  // Finally, calculate the square root of the distances
607  using namespace vigra::functor;
608 
609  transformMultiArray( d, shape, dest, d, dest, sqrt(Arg1()) );
610 }
611 
612 template <class SrcIterator, class SrcShape, class SrcAccessor,
613  class DestIterator, class DestAccessor, class Array>
614 inline void separableMultiDistance( triple<SrcIterator, SrcShape, SrcAccessor> const & source,
615  pair<DestIterator, DestAccessor> const & dest, bool background,
616  Array const & pixelPitch)
617 {
618  separableMultiDistance( source.first, source.second, source.third,
619  dest.first, dest.second, background, pixelPitch );
620 }
621 
622 template <class SrcIterator, class SrcShape, class SrcAccessor,
623  class DestIterator, class DestAccessor>
624 inline void separableMultiDistance( triple<SrcIterator, SrcShape, SrcAccessor> const & source,
625  pair<DestIterator, DestAccessor> const & dest, bool background)
626 {
627  separableMultiDistance( source.first, source.second, source.third,
628  dest.first, dest.second, background );
629 }
630 
631 template <unsigned int N, class T1, class S1,
632  class T2, class S2, class Array>
633 inline void
634 separableMultiDistance(MultiArrayView<N, T1, S1> const & source,
635  MultiArrayView<N, T2, S2> dest,
636  bool background,
637  Array const & pixelPitch)
638 {
639  vigra_precondition(source.shape() == dest.shape(),
640  "separableMultiDistance(): shape mismatch between input and output.");
641  separableMultiDistance( srcMultiArrayRange(source),
642  destMultiArray(dest), background, pixelPitch );
643 }
644 
645 template <unsigned int N, class T1, class S1,
646  class T2, class S2>
647 inline void
648 separableMultiDistance(MultiArrayView<N, T1, S1> const & source,
649  MultiArrayView<N, T2, S2> dest,
650  bool background)
651 {
652  vigra_precondition(source.shape() == dest.shape(),
653  "separableMultiDistance(): shape mismatch between input and output.");
654  separableMultiDistance( srcMultiArrayRange(source),
655  destMultiArray(dest), background );
656 }
657 
658 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% BoundaryDistanceTransform %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
659 
660 //rewrite labeled data and work with separableMultiDist
661 namespace lemon_graph {
662 
663 template <class Graph, class T1Map, class T2Map>
664 void
665 markRegionBoundaries(Graph const & g,
666  T1Map const & labels,
667  T2Map & out)
668 {
669  typedef typename Graph::NodeIt graph_scanner;
670  typedef typename Graph::OutBackArcIt neighbor_iterator;
671 
672  //find faces
673  for (graph_scanner node(g); node != INVALID; ++node)
674  {
675  typename T1Map::value_type center = labels[*node];
676 
677  for (neighbor_iterator arc(g, node); arc != INVALID; ++arc)
678  {
679  // set adjacent nodes with different labels to 1
680  if(center != labels[g.target(*arc)])
681  {
682  out[*node] = 1;
683  out[g.target(*arc)] = 1;
684  }
685  }
686  }
687 }
688 
689 } //-- namspace lemon_graph
690 
691 doxygen_overloaded_function(template <...> unsigned int markRegionBoundaries)
692 
693 template <unsigned int N, class T1, class S1,
694  class T2, class S2>
695 inline void
696 markRegionBoundaries(MultiArrayView<N, T1, S1> const & labels,
697  MultiArrayView<N, T2, S2> out,
699 {
700  vigra_precondition(labels.shape() == out.shape(),
701  "markRegionBoundaries(): shape mismatch between input and output.");
702 
703  GridGraph<N, undirected_tag> graph(labels.shape(), neighborhood);
704 
705  lemon_graph::markRegionBoundaries(graph, labels, out);
706 }
707 
708 //MultiDistance which works directly on labeled data
709 
710 namespace detail
711 {
712 
713 /********************************************************/
714 /* */
715 /* boundaryDistParabola */
716 /* */
717 /********************************************************/
718 
719 template <class DestIterator, class LabelIterator>
720 void
721 boundaryDistParabola(DestIterator is, DestIterator iend,
722  LabelIterator ilabels,
723  double dmax,
724  bool array_border_is_active=false)
725 {
726  // We assume that the data in the input is distance squared and treat it as such
727  double w = iend - is;
728  if(w <= 0)
729  return;
730 
731  DestIterator id = is;
732  typedef typename LabelIterator::value_type LabelType;
733  typedef typename DestIterator::value_type DestType;
734  typedef detail::DistParabolaStackEntry<DestType> Influence;
735  typedef std::vector<Influence> Stack;
736 
737  double apex_height = array_border_is_active
738  ? 0.0
739  : dmax;
740  Stack _stack(1, Influence(apex_height, 0.0, -1.0, w));
741  LabelType current_label = *ilabels;
742  for(double begin = 0.0, current = 0.0; current <= w; ++ilabels, ++is, ++current)
743  {
744  apex_height = (current < w)
745  ? (current_label == *ilabels)
746  ? *is
747  : 0.0
748  : array_border_is_active
749  ? 0.0
750  : dmax;
751  while(true)
752  {
753  Influence & s = _stack.back();
754  double diff = current - s.center;
755  double intersection = current + (apex_height - s.apex_height - sq(diff)) / (2.0 * diff);
756 
757  if(intersection < s.left) // previous parabola has no influence
758  {
759  _stack.pop_back();
760  if(_stack.empty())
761  intersection = begin; // new parabola is valid for entire present segment
762  else
763  continue; // try new top of stack without advancing to next pixel
764  }
765  else if(intersection < s.right)
766  {
767  s.right = intersection;
768  }
769  if(intersection < w)
770  _stack.push_back(Influence(apex_height, intersection, current, w));
771  if(current < w && current_label == *ilabels)
772  break; // finished present pixel, advance to next one
773 
774  // label changed => finalize the current segment
775  typename Stack::iterator it = _stack.begin();
776  for(double c = begin; c < current; ++c, ++id)
777  {
778  while(c >= it->right)
779  ++it;
780  *id = sq(c - it->center) + it->apex_height;
781  }
782  if(current == w)
783  break; // stop when this was the last segment
784 
785  // initialize the new segment
786  begin = current;
787  current_label = *ilabels;
788  apex_height = *is;
789  Stack(1, Influence(0.0, begin-1.0, begin-1.0, w)).swap(_stack);
790  // don't advance to next pixel here, because the present pixel must also
791  // be analysed in the context of the new segment
792  }
793  }
794 }
795 
796 /********************************************************/
797 /* */
798 /* internalBoundaryMultiArrayDist */
799 /* */
800 /********************************************************/
801 
802 template <unsigned int N, class T1, class S1,
803  class T2, class S2>
804 void
805 internalBoundaryMultiArrayDist(
806  MultiArrayView<N, T1, S1> const & labels,
807  MultiArrayView<N, T2, S2> dest,
808  double dmax, bool array_border_is_active=false)
809 {
810  typedef typename MultiArrayView<N, T1, S1>::const_traverser LabelIterator;
811  typedef typename MultiArrayView<N, T2, S2>::traverser DestIterator;
812  typedef MultiArrayNavigator<LabelIterator, N> LabelNavigator;
813  typedef MultiArrayNavigator<DestIterator, N> DNavigator;
814 
815  dest = dmax;
816  for( unsigned d = 0; d < N; ++d )
817  {
818  LabelNavigator lnav( labels.traverser_begin(), labels.shape(), d );
819  DNavigator dnav( dest.traverser_begin(), dest.shape(), d );
820 
821  for( ; dnav.hasMore(); dnav++, lnav++ )
822  {
823  boundaryDistParabola(dnav.begin(), dnav.end(),
824  lnav.begin(),
825  dmax, array_border_is_active);
826  }
827  }
828 }
829 
830 } // namespace detail
831 
832  /** \brief Specify which boundary is used for boundaryMultiDistance().
833 
834  */
836  OuterBoundary, ///< Pixels just outside of each region
837  InterpixelBoundary, ///< Half-integer points between pixels of different labels
838  InnerBoundary ///< Pixels just inside of each region
839 };
840 
841 /********************************************************/
842 /* */
843 /* boundaryMultiDistance */
844 /* */
845 /********************************************************/
846 
847 /** \brief Euclidean distance to the implicit boundaries of a multi-dimensional label array.
848 
849 
850  <b> Declarations:</b>
851 
852  pass arbitrary-dimensional array views:
853  \code
854  namespace vigra {
855  template <unsigned int N, class T1, class S1,
856  class T2, class S2>
857  void
858  boundaryMultiDistance(MultiArrayView<N, T1, S1> const & labels,
859  MultiArrayView<N, T2, S2> dest,
860  bool array_border_is_active=false,
861  BoundaryDistanceTag boundary=InterpixelBoundary);
862  }
863  \endcode
864 
865  This function computes the distance transform of a labeled image <i>simultaneously</i>
866  for all regions. Depending on the requested type of \a boundary, three modes
867  are supported:
868  <ul>
869  <li><tt>OuterBoundary</tt>: In each region, compute the distance to the nearest pixel not
870  belonging to that regions. This is the same as if a normal distance transform
871  where applied to a binary image containing just this region.</li>
872  <li><tt>InterpixelBoundary</tt> (default): Like <tt>OuterBoundary</tt>, but shift the distance
873  to the interpixel boundary by subtractiong 1/2. This make the distences consistent
874  accross boundaries.</li>
875  <li><tt>InnerBoundary</tt>: In each region, compute the distance to the nearest pixel in the
876  region which is adjacent to the boundary. </li>
877  </ul>
878  If <tt>array_border_is_active=true</tt>, the
879  outer border of the array (i.e. the interpixel boundary between the array
880  and the infinite region) is also used. Otherwise (the default), regions
881  touching the array border are treated as if they extended to infinity.
882 
883  <b> Usage:</b>
884 
885  <b>\#include</b> <vigra/multi_distance.hxx><br/>
886  Namespace: vigra
887 
888  \code
889  Shape3 shape(width, height, depth);
890  MultiArray<3, unsigned char> source(shape);
891  MultiArray<3, UInt32> labels(shape);
892  MultiArray<3, float> dest(shape);
893  ...
894 
895  // find regions (interpixel boundaries are implied)
896  labelMultiArray(source, labels);
897 
898  // Calculate Euclidean distance to interpixel boundary for all pixels
899  boundaryMultiDistance(labels, dest);
900  \endcode
901 
902  \see vigra::distanceTransform(), vigra::separableMultiDistance()
903 */
905 
906 template <unsigned int N, class T1, class S1,
907  class T2, class S2>
908 void
909 boundaryMultiDistance(MultiArrayView<N, T1, S1> const & labels,
910  MultiArrayView<N, T2, S2> dest,
911  bool array_border_is_active=false,
913 {
914  vigra_precondition(labels.shape() == dest.shape(),
915  "boundaryMultiDistance(): shape mismatch between input and output.");
916 
917  using namespace vigra::functor;
918 
919  if(boundary == InnerBoundary)
920  {
921  MultiArray<N, unsigned char> boundaries(labels.shape());
922 
923  markRegionBoundaries(labels, boundaries, IndirectNeighborhood);
924  if(array_border_is_active)
925  initMultiArrayBorder(boundaries, 1, 1);
926  separableMultiDistance(boundaries, dest, true);
927  }
928  else
929  {
930  T2 offset = 0.0;
931 
932  if(boundary == InterpixelBoundary)
933  {
934  vigra_precondition(!NumericTraits<T2>::isIntegral::value,
935  "boundaryMultiDistance(..., InterpixelBoundary): output pixel type must be float or double.");
936  offset = T2(0.5);
937  }
938  double dmax = squaredNorm(labels.shape()) + N;
939  if(dmax > double(NumericTraits<T2>::max()))
940  {
941  // need a temporary array to avoid overflows
942  typedef typename NumericTraits<T2>::RealPromote Real;
943  MultiArray<N, Real> tmpArray(labels.shape());
944  detail::internalBoundaryMultiArrayDist(labels, tmpArray,
945  dmax, array_border_is_active);
946  transformMultiArray(tmpArray, dest, sqrt(Arg1()) - Param(offset) );
947  }
948  else
949  {
950  // can work directly on the destination array
951  detail::internalBoundaryMultiArrayDist(labels, dest, dmax, array_border_is_active);
952  transformMultiArray(dest, dest, sqrt(Arg1()) - Param(offset) );
953  }
954  }
955 }
956 
957 //@}
958 
959 } //-- namespace vigra
960 
961 
962 #endif //-- VIGRA_MULTI_DISTANCE_HXX
void initMultiArrayBorder(...)
Write values to the specified border values in the array.
void boundaryMultiDistance(...)
Euclidean distance to the implicit boundaries of a multi-dimensional label array. ...
BoundaryDistanceTag
Specify which boundary is used for boundaryMultiDistance().
Definition: multi_distance.hxx:835
FFTWComplex< R >::SquaredNormType squaredNorm(const FFTWComplex< R > &a)
squared norm (= squared magnitude)
Definition: fftw3.hxx:1044
void separableMultiDistance(...)
Euclidean distance on multi-dimensional arrays.
Pixels just inside of each region.
Definition: multi_distance.hxx:838
Pixels just outside of each region.
Definition: multi_distance.hxx:836
NumericTraits< T >::Promote sq(T t)
The square function.
Definition: mathutil.hxx:382
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
doxygen_overloaded_function(template<...> void separableConvolveBlockwise) template< unsigned int N
Separated convolution on ChunkedArrays.
void copyMultiArray(...)
Copy a multi-dimensional array.
use direct and indirect neighbors
Definition: multi_fwd.hxx:188
Half-integer points between pixels of different labels.
Definition: multi_distance.hxx:837
void separableMultiDistSquared(...)
Euclidean distance squared on multi-dimensional arrays.
void transformMultiArray(...)
Transform a multi-dimensional array with a unary function or functor.
int ceil(FixedPoint< IntBits, FracBits > v)
rounding up.
Definition: fixedpoint.hxx:675
use only direct neighbors
Definition: multi_fwd.hxx:187
NeighborhoodType
Choose the neighborhood system in a dimension-independent way.
Definition: multi_fwd.hxx:186
SquareRootTraits< FixedPoint< IntBits, FracBits > >::SquareRootResult sqrt(FixedPoint< IntBits, FracBits > v)
square root.
Definition: fixedpoint.hxx:616

© 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)