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

multi_convolution.hxx VIGRA

1 //-- -*- c++ -*-
2 /************************************************************************/
3 /* */
4 /* Copyright 2003 by Christian-Dennis Rahn */
5 /* and Ullrich Koethe */
6 /* */
7 /* This file is part of the VIGRA computer vision library. */
8 /* The VIGRA Website is */
9 /* http://hci.iwr.uni-heidelberg.de/vigra/ */
10 /* Please direct questions, bug reports, and contributions to */
11 /* ullrich.koethe@iwr.uni-heidelberg.de or */
12 /* vigra@informatik.uni-hamburg.de */
13 /* */
14 /* Permission is hereby granted, free of charge, to any person */
15 /* obtaining a copy of this software and associated documentation */
16 /* files (the "Software"), to deal in the Software without */
17 /* restriction, including without limitation the rights to use, */
18 /* copy, modify, merge, publish, distribute, sublicense, and/or */
19 /* sell copies of the Software, and to permit persons to whom the */
20 /* Software is furnished to do so, subject to the following */
21 /* conditions: */
22 /* */
23 /* The above copyright notice and this permission notice shall be */
24 /* included in all copies or substantial portions of the */
25 /* Software. */
26 /* */
27 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
28 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
29 /* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
30 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
31 /* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
32 /* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
33 /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
34 /* OTHER DEALINGS IN THE SOFTWARE. */
35 /* */
36 /************************************************************************/
37 
38 #ifndef VIGRA_MULTI_CONVOLUTION_H
39 #define VIGRA_MULTI_CONVOLUTION_H
40 
41 #include "separableconvolution.hxx"
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 "multi_math.hxx"
50 #include "functorexpression.hxx"
51 #include "tinyvector.hxx"
52 #include "algorithm.hxx"
53 
54 
55 #include <iostream>
56 
57 namespace vigra
58 {
59 
60 namespace detail
61 {
62 
63 struct DoubleYielder
64 {
65  const double value;
66  DoubleYielder(double v, unsigned, const char *const) : value(v) {}
67  DoubleYielder(double v) : value(v) {}
68  void operator++() {}
69  double operator*() const { return value; }
70 };
71 
72 template <typename X>
73 struct IteratorDoubleYielder
74 {
75  X it;
76  IteratorDoubleYielder(X i, unsigned, const char *const) : it(i) {}
77  IteratorDoubleYielder(X i) : it(i) {}
78  void operator++() { ++it; }
79  double operator*() const { return *it; }
80 };
81 
82 template <typename X>
83 struct SequenceDoubleYielder
84 {
85  typename X::const_iterator it;
86  SequenceDoubleYielder(const X & seq, unsigned dim,
87  const char *const function_name = "SequenceDoubleYielder")
88  : it(seq.begin())
89  {
90  if (seq.size() == dim)
91  return;
92  std::string msg = "(): Parameter number be equal to the number of spatial dimensions.";
93  vigra_precondition(false, function_name + msg);
94  }
95  void operator++() { ++it; }
96  double operator*() const { return *it; }
97 };
98 
99 template <typename X>
100 struct WrapDoubleIterator
101 {
102  typedef
103  typename IfBool< IsConvertibleTo<X, double>::value,
104  DoubleYielder,
105  typename IfBool< IsIterator<X>::value || IsArray<X>::value,
106  IteratorDoubleYielder<X>,
107  SequenceDoubleYielder<X>
108  >::type
109  >::type type;
110 };
111 
112 template <class Param1, class Param2, class Param3>
113 struct WrapDoubleIteratorTriple
114 {
115  typename WrapDoubleIterator<Param1>::type sigma_eff_it;
116  typename WrapDoubleIterator<Param2>::type sigma_d_it;
117  typename WrapDoubleIterator<Param3>::type step_size_it;
118  WrapDoubleIteratorTriple(Param1 sigma_eff, Param2 sigma_d, Param3 step_size)
119  : sigma_eff_it(sigma_eff), sigma_d_it(sigma_d), step_size_it(step_size) {}
120  void operator++()
121  {
122  ++sigma_eff_it;
123  ++sigma_d_it;
124  ++step_size_it;
125  }
126  double sigma_eff() const { return *sigma_eff_it; }
127  double sigma_d() const { return *sigma_d_it; }
128  double step_size() const { return *step_size_it; }
129  static void sigma_precondition(double sigma, const char *const function_name)
130  {
131  if (sigma < 0.0)
132  {
133  std::string msg = "(): Scale must be positive.";
134  vigra_precondition(false, function_name + msg);
135  }
136  }
137  double sigma_scaled(const char *const function_name = "unknown function ",
138  bool allow_zero = false) const
139  {
140  sigma_precondition(sigma_eff(), function_name);
141  sigma_precondition(sigma_d(), function_name);
142  double sigma_squared = sq(sigma_eff()) - sq(sigma_d());
143  if (sigma_squared > 0.0 || (allow_zero && sigma_squared == 0.0))
144  {
145  return std::sqrt(sigma_squared) / step_size();
146  }
147  else
148  {
149  std::string msg = "(): Scale would be imaginary";
150  if(!allow_zero)
151  msg += " or zero";
152  vigra_precondition(false, function_name + msg + ".");
153  return 0;
154  }
155  }
156 };
157 
158 template <unsigned dim>
159 struct multiArrayScaleParam
160 {
161  typedef TinyVector<double, dim> p_vector;
162  typedef typename p_vector::const_iterator return_type;
163  p_vector vec;
164 
165  template <class Param>
166  multiArrayScaleParam(Param val, const char *const function_name = "multiArrayScaleParam")
167  {
168  typename WrapDoubleIterator<Param>::type in(val, dim, function_name);
169  for (unsigned i = 0; i != dim; ++i, ++in)
170  vec[i] = *in;
171  }
172  return_type operator()() const
173  {
174  return vec.begin();
175  }
176  static void precondition(unsigned n_par, const char *const function_name = "multiArrayScaleParam")
177  {
178  char n[3] = "0.";
179  n[0] += dim;
180  std::string msg = "(): dimension parameter must be ";
181  vigra_precondition(dim == n_par, function_name + msg + n);
182  }
183  multiArrayScaleParam(double v0, double v1, const char *const function_name = "multiArrayScaleParam")
184  {
185  precondition(2, function_name);
186  vec = p_vector(v0, v1);
187  }
188  multiArrayScaleParam(double v0, double v1, double v2, const char *const function_name = "multiArrayScaleParam")
189  {
190  precondition(3, function_name);
191  vec = p_vector(v0, v1, v2);
192  }
193  multiArrayScaleParam(double v0, double v1, double v2, double v3, const char *const function_name = "multiArrayScaleParam")
194  {
195  precondition(4, function_name);
196  vec = p_vector(v0, v1, v2, v3);
197  }
198  multiArrayScaleParam(double v0, double v1, double v2, double v3, double v4, const char *const function_name = "multiArrayScaleParam")
199  {
200  precondition(5, function_name);
201  vec = p_vector(v0, v1, v2, v3, v4);
202  }
203 };
204 
205 } // namespace detail
206 
207 #define VIGRA_CONVOLUTION_OPTIONS(function_name, default_value, member_name, getter_setter_name) \
208  template <class Param> \
209  ConvolutionOptions & function_name(const Param & val) \
210  { \
211  member_name = ParamVec(val, "ConvolutionOptions::" #function_name); \
212  return *this; \
213  } \
214  ConvolutionOptions & function_name() \
215  { \
216  member_name = ParamVec(default_value, "ConvolutionOptions::" #function_name); \
217  return *this; \
218  } \
219  ConvolutionOptions & function_name(double v0, double v1) \
220  { \
221  member_name = ParamVec(v0, v1, "ConvolutionOptions::" #function_name); \
222  return *this; \
223  } \
224  ConvolutionOptions & function_name(double v0, double v1, double v2) \
225  { \
226  member_name = ParamVec(v0, v1, v2, "ConvolutionOptions::" #function_name); \
227  return *this; \
228  } \
229  ConvolutionOptions & function_name(double v0, double v1, double v2, double v3) \
230  { \
231  member_name = ParamVec(v0, v1, v2, v3, "ConvolutionOptions::" #function_name); \
232  return *this; \
233  } \
234  ConvolutionOptions & function_name(double v0, double v1, double v2, double v3, double v4) \
235  { \
236  member_name = ParamVec(v0, v1, v2, v3, v4, "ConvolutionOptions::" #function_name); \
237  return *this; \
238  } \
239  typename ParamVec::p_vector get##getter_setter_name()const{ \
240  return member_name.vec; \
241  } \
242  void set##getter_setter_name(typename ParamVec::p_vector vec){ \
243  member_name.vec = vec; \
244  }
245 
246 /** \brief Options class template for convolutions.
247 
248  <b>\#include</b> <vigra/multi_convolution.hxx><br/>
249  Namespace: vigra
250 
251  This class enables the calculation of scale space convolutions
252  such as \ref gaussianGradientMultiArray() on data with anisotropic
253  discretization. For these, the result of the ordinary calculation
254  has to be multiplied by factors of \f$1/w^{n}\f$ for each dimension,
255  where \f$w\f$ is the step size of the grid in said dimension and
256  \f$n\f$ is the differential order of the convolution, e.g., 1 for
257  gaussianGradientMultiArray(), and 0 for gaussianSmoothMultiArray(),
258  respectively. Also for each dimension in turn, the convolution's scale
259  parameter \f$\sigma\f$ has to be replaced by
260  \f$\sqrt{\sigma_\mathrm{eff}^2 - \sigma_\mathrm{D}^2}\Big/w\f$,
261  where \f$\sigma_\mathrm{eff}\f$ is the resulting effective filtering
262  scale. The data is assumed to be already filtered by a
263  gaussian smoothing with the scale parameter \f$\sigma_\mathrm{D}\f$
264  (such as by measuring equipment). All of the above changes are
265  automatically employed by the convolution functions for <tt>MultiArray</tt>s
266  if a corresponding options object is provided.
267 
268  The <tt>ConvolutionOptions</tt> class must be parameterized by the dimension
269  <tt>dim</tt>
270  of the <tt>MultiArray</tt>s on which it is used. The actual per-axis
271  options are set by (overloaded) member functions explained below,
272  or else default to neutral values corresponding to the absence of the
273  particular option.
274 
275  All member functions set <tt>dim</tt> values of the respective convolution
276  option, one for each dimension. They may be set explicitly by multiple
277  arguments for up to five dimensions, or by a single argument to the same
278  value for all dimensions. For the general case, a single argument that is
279  either a C-syle array, an iterator, or a C++ standard library style
280  sequence (such as <tt>std::vector</tt>, with member functions <tt>begin()</tt>
281  and <tt>size()</tt>) supplies the option values for any number of dimensions.
282 
283  Note that the return value of all member functions is <tt>*this</tt>, which
284  provides the mechanism for concatenating member function calls as shown below.
285 
286  <b>usage with explicit parameters:</b>
287 
288  \code
289  ConvolutionOptions<2> opt = ConvolutionOptions<2>().stepSize(1, 2.3);
290  \endcode
291 
292  <b>usage with arrays:</b>
293 
294  \code
295  const double step_size[3] = { x_scale, y_scale, z_scale };
296  ConvolutionOptions<3> opt = ConvolutionOptions<3>().stepSize(step_size);
297  \endcode
298 
299  <b>usage with C++ standard library style sequences:</b>
300 
301  \code
302  TinyVector<double, 4> step_size(1, 1, 2.0, 1.5);
303  TinyVector<double, 4> r_sigmas(1, 1, 2.3, 3.2);
304  ConvolutionOptions<4> opt = ConvolutionOptions<4>().stepSize(step_size).resolutionStdDev(r_sigmas);
305  \endcode
306 
307  <b>usage with iterators:</b>
308 
309  \code
310  ArrayVector<double> step_size;
311  step_size.push_back(0);
312  step_size.push_back(3);
313  step_size.push_back(4);
314  ArrayVector<double>::iterator i = step_size.begin();
315  ++i;
316  ConvolutionOptions<2> opt = ConvolutionOptions<2>().stepSize(i);
317  \endcode
318 
319  <b>general usage in a convolution function call:</b>
320 
321  \code
322  MultiArray<3, double> test_image;
323  MultiArray<3, double> out_image;
324 
325  double scale = 5.0;
326  gaussianSmoothMultiArray(test_image, out_image, scale,
327  ConvolutionOptions<3>()
328  .stepSize (1, 1, 3.2)
329  .resolutionStdDev(1, 1, 4)
330  );
331  \endcode
332 
333 */
334 template <unsigned dim>
336 {
337  public:
338  typedef typename MultiArrayShape<dim>::type Shape;
339  typedef detail::multiArrayScaleParam<dim> ParamVec;
340  typedef typename ParamVec::return_type ParamIt;
341 
342  ParamVec sigma_eff;
343  ParamVec sigma_d;
344  ParamVec step_size;
345  ParamVec outer_scale;
346  double window_ratio;
347  Shape from_point, to_point;
348 
350  : sigma_eff(0.0),
351  sigma_d(0.0),
352  step_size(1.0),
353  outer_scale(0.0),
354  window_ratio(0.0)
355  {}
356 
357  typedef typename detail::WrapDoubleIteratorTriple<ParamIt, ParamIt, ParamIt>
358  ScaleIterator;
359  typedef typename detail::WrapDoubleIterator<ParamIt>::type
360  StepIterator;
361 
362  ScaleIterator scaleParams() const
363  {
364  return ScaleIterator(sigma_eff(), sigma_d(), step_size());
365  }
366  StepIterator stepParams() const
367  {
368  return StepIterator(step_size());
369  }
370 
371  ConvolutionOptions outerOptions() const
372  {
373  ConvolutionOptions outer = *this;
374  // backward-compatible values:
375  return outer.stdDev(outer_scale()).resolutionStdDev(0.0);
376  }
377 
378  // Step size per axis.
379  // Default: dim values of 1.0
380  VIGRA_CONVOLUTION_OPTIONS(stepSize, 1.0, step_size, StepSize)
381 #ifdef DOXYGEN
382  /** Step size(s) per axis, i.e., the distance between two
383  adjacent pixels. Required for <tt>MultiArray</tt>
384  containing anisotropic data.
385 
386  Note that a convolution containing a derivative operator
387  of order <tt>n</tt> results in a multiplication by
388  \f${\rm stepSize}^{-n}\f$ for each axis.
389  Also, the above standard deviations
390  are scaled according to the step size of each axis.
391  Default value for the options object if this member function is not
392  used: A value of 1.0 for each dimension.
393  */
395 #endif
396 
397  // Resolution standard deviation per axis.
398  // Default: dim values of 0.0
399  VIGRA_CONVOLUTION_OPTIONS(resolutionStdDev, 0.0, sigma_d, ResolutionStdDev)
400 #ifdef DOXYGEN
401  /** Resolution standard deviation(s) per axis, i.e., a supposed
402  pre-existing gaussian filtering by this value.
403 
404  The standard deviation actually used by the convolution operators
405  is \f$\sqrt{{\rm sigma}^{2} - {\rm resolutionStdDev}^{2}}\f$ for each
406  axis.
407  Default value for the options object if this member function is not
408  used: A value of 0.0 for each dimension.
409  */
411 #endif
412 
413  // Standard deviation of scale space operators.
414  // Default: dim values of 0.0
415  VIGRA_CONVOLUTION_OPTIONS(stdDev, 0.0, sigma_eff, StdDev)
416  VIGRA_CONVOLUTION_OPTIONS(innerScale, 0.0, sigma_eff, InnerScale)
417 
418 #ifdef DOXYGEN
419  /** Standard deviation(s) of scale space operators, or inner scale(s) for \ref structureTensorMultiArray().
420 
421  Usually not
422  needed, since a single value for all axes may be specified as a parameter
423  <tt>sigma</tt> to the call of
424  an convolution operator such as \ref gaussianGradientMultiArray(), and
425  anisotropic data requiring the use of the stepSize() member function.
426  Default value for the options object if this member function is not
427  used: A value of 0.0 for each dimension.
428  */
430 
431  /** Standard deviation(s) of scale space operators, or inner scale(s) for \ref structureTensorMultiArray().
432 
433  Usually not
434  needed, since a single value for all axes may be specified as a parameter
435  <tt>sigma</tt> to the call of
436  an convolution operator such as \ref gaussianGradientMultiArray(), and
437  anisotropic data requiring the use of the stepSize() member function.
438  Default value for the options object if this member function is not
439  used: A value of 0.0 for each dimension.
440  */
442 #endif
443 
444  // Outer scale, for structure tensor.
445  // Default: dim values of 0.0
446  VIGRA_CONVOLUTION_OPTIONS(outerScale, 0.0, outer_scale, OuterScale)
447 #ifdef DOXYGEN
448  /** Standard deviation(s) of the second convolution of the
449  structure tensor.
450 
451  Usually not needed, since a single value for
452  all axes may be specified as a parameter <tt>outerScale</tt> to
453  the call of \ref structureTensorMultiArray(), and
454  anisotropic data requiring the use of the stepSize() member
455  function.
456  Default value for the options object if this member function is not
457  used: A value of 0.0 for each dimension.
458  */
460 #endif
461 
462  /** Size of the filter window as a multiple of the scale parameter.
463 
464  This option is only used for Gaussian filters and their derivatives.
465  By default, the window size of a Gaussian filter is automatically
466  determined such that the error resulting from restricting the
467  infinitely large Gaussian function to a finite size is minimized.
468  In particular, the window radius is determined as
469  <tt>radius = round(3.0 * sigma + 0.5 * order)</tt>, where 'order' is the
470  desired derivative order. In some cases, it is desirable to trade off
471  accuracy for speed, and this function can be used to request a smaller
472  window radius.
473 
474  Default: <tt>0.0</tt> (i.e. determine the window size automatically)
475  */
477  {
478  vigra_precondition(ratio >= 0.0,
479  "ConvolutionOptions::filterWindowSize(): ratio must not be negative.");
480  window_ratio = ratio;
481  return *this;
482  }
483 
484  double getFilterWindowSize() const {
485  return window_ratio;
486  }
487 
488  /** Restrict the filter to a subregion of the input array.
489 
490  This is useful for speeding up computations by ignoring irrelevant
491  areas in the array. <b>Note:</b> It is assumed that the output array
492  of the convolution has the size given in this function. Negative ROI
493  boundaries are interpreted relative to the end of the respective dimension
494  (i.e. <tt>if(to[k] < 0) to[k] += source.shape(k);</tt>).
495 
496  Default: <tt>from = Shape(), to = Shape()</tt> (i.e. use entire array)
497  */
498  ConvolutionOptions<dim> & subarray(Shape const & from, Shape const & to)
499  {
500  from_point = from;
501  to_point = to;
502  return *this;
503  }
504 
505  std::pair<Shape, Shape> getSubarray()const{
506  std::pair<Shape, Shape> res;
507  res.first = from_point;
508  res.second = to_point;
509  return res;
510  }
511 };
512 
513 namespace detail
514 {
515 
516 /********************************************************/
517 /* */
518 /* internalSeparableConvolveMultiArray */
519 /* */
520 /********************************************************/
521 
522 template <class SrcIterator, class SrcShape, class SrcAccessor,
523  class DestIterator, class DestAccessor, class KernelIterator>
524 void
525 internalSeparableConvolveMultiArrayTmp(
526  SrcIterator si, SrcShape const & shape, SrcAccessor src,
527  DestIterator di, DestAccessor dest, KernelIterator kit)
528 {
529  enum { N = 1 + SrcIterator::level };
530 
531  typedef typename NumericTraits<typename DestAccessor::value_type>::RealPromote TmpType;
532  typedef typename AccessorTraits<TmpType>::default_accessor TmpAcessor;
533 
534  // temporary array to hold the current line to enable in-place operation
535  ArrayVector<TmpType> tmp( shape[0] );
536 
537  typedef MultiArrayNavigator<SrcIterator, N> SNavigator;
538  typedef MultiArrayNavigator<DestIterator, N> DNavigator;
539 
540  TmpAcessor acc;
541 
542  {
543  // only operate on first dimension here
544  SNavigator snav( si, shape, 0 );
545  DNavigator dnav( di, shape, 0 );
546 
547  for( ; snav.hasMore(); snav++, dnav++ )
548  {
549  // first copy source to tmp for maximum cache efficiency
550  copyLine(snav.begin(), snav.end(), src, tmp.begin(), acc);
551 
552  convolveLine(srcIterRange(tmp.begin(), tmp.end(), acc),
553  destIter( dnav.begin(), dest ),
554  kernel1d( *kit ) );
555  }
556  ++kit;
557  }
558 
559  // operate on further dimensions
560  for( int d = 1; d < N; ++d, ++kit )
561  {
562  DNavigator dnav( di, shape, d );
563 
564  tmp.resize( shape[d] );
565 
566  for( ; dnav.hasMore(); dnav++ )
567  {
568  // first copy source to tmp since convolveLine() cannot work in-place
569  copyLine(dnav.begin(), dnav.end(), dest, tmp.begin(), acc);
570 
571  convolveLine(srcIterRange(tmp.begin(), tmp.end(), acc),
572  destIter( dnav.begin(), dest ),
573  kernel1d( *kit ) );
574  }
575  }
576 }
577 
578 /********************************************************/
579 /* */
580 /* internalSeparableConvolveSubarray */
581 /* */
582 /********************************************************/
583 
584 template <class SrcIterator, class SrcShape, class SrcAccessor,
585  class DestIterator, class DestAccessor, class KernelIterator>
586 void
587 internalSeparableConvolveSubarray(
588  SrcIterator si, SrcShape const & shape, SrcAccessor src,
589  DestIterator di, DestAccessor dest, KernelIterator kit,
590  SrcShape const & start, SrcShape const & stop)
591 {
592  enum { N = 1 + SrcIterator::level };
593 
594  typedef typename NumericTraits<typename DestAccessor::value_type>::RealPromote TmpType;
595  typedef MultiArray<N, TmpType> TmpArray;
596  typedef typename TmpArray::traverser TmpIterator;
597  typedef typename AccessorTraits<TmpType>::default_accessor TmpAcessor;
598 
599  SrcShape sstart, sstop, axisorder, tmpshape;
600  TinyVector<double, N> overhead;
601  for(int k=0; k<N; ++k)
602  {
603  axisorder[k] = k;
604  sstart[k] = start[k] - kit[k].right();
605  if(sstart[k] < 0)
606  sstart[k] = 0;
607  sstop[k] = stop[k] - kit[k].left();
608  if(sstop[k] > shape[k])
609  sstop[k] = shape[k];
610  overhead[k] = double(sstop[k] - sstart[k]) / (stop[k] - start[k]);
611  }
612 
613  indexSort(overhead.begin(), overhead.end(), axisorder.begin(), std::greater<double>());
614  SrcShape dstart, dstop(sstop - sstart);
615  dstop[axisorder[0]] = stop[axisorder[0]] - start[axisorder[0]];
616 
617  // temporary array to hold the current line to enable in-place operation
618  MultiArray<N, TmpType> tmp(dstop);
619 
620  typedef MultiArrayNavigator<SrcIterator, N> SNavigator;
621  typedef MultiArrayNavigator<TmpIterator, N> TNavigator;
622 
623  TmpAcessor acc;
624 
625  {
626  // only operate on first dimension here
627  SNavigator snav( si, sstart, sstop, axisorder[0]);
628  TNavigator tnav( tmp.traverser_begin(), dstart, dstop, axisorder[0]);
629 
630  ArrayVector<TmpType> tmpline(sstop[axisorder[0]] - sstart[axisorder[0]]);
631 
632  int lstart = start[axisorder[0]] - sstart[axisorder[0]];
633  int lstop = lstart + (stop[axisorder[0]] - start[axisorder[0]]);
634 
635  for( ; snav.hasMore(); snav++, tnav++ )
636  {
637  // first copy source to tmp for maximum cache efficiency
638  copyLine(snav.begin(), snav.end(), src, tmpline.begin(), acc);
639 
640  convolveLine(srcIterRange(tmpline.begin(), tmpline.end(), acc),
641  destIter(tnav.begin(), acc),
642  kernel1d( kit[axisorder[0]] ), lstart, lstop);
643  }
644  }
645 
646  // operate on further dimensions
647  for( int d = 1; d < N; ++d)
648  {
649  TNavigator tnav( tmp.traverser_begin(), dstart, dstop, axisorder[d]);
650 
651  ArrayVector<TmpType> tmpline(dstop[axisorder[d]] - dstart[axisorder[d]]);
652 
653  int lstart = start[axisorder[d]] - sstart[axisorder[d]];
654  int lstop = lstart + (stop[axisorder[d]] - start[axisorder[d]]);
655 
656  for( ; tnav.hasMore(); tnav++ )
657  {
658  // first copy source to tmp because convolveLine() cannot work in-place
659  copyLine(tnav.begin(), tnav.end(), acc, tmpline.begin(), acc );
660 
661  convolveLine(srcIterRange(tmpline.begin(), tmpline.end(), acc),
662  destIter( tnav.begin() + lstart, acc ),
663  kernel1d( kit[axisorder[d]] ), lstart, lstop);
664  }
665 
666  dstart[axisorder[d]] = lstart;
667  dstop[axisorder[d]] = lstop;
668  }
669 
670  copyMultiArray(tmp.traverser_begin()+dstart, stop-start, acc, di, dest);
671 }
672 
673 
674 template <class K>
675 void
676 scaleKernel(K & kernel, double a)
677 {
678  for(int i = kernel.left(); i <= kernel.right(); ++i)
679  kernel[i] = detail::RequiresExplicitCast<typename K::value_type>::cast(kernel[i] * a);
680 }
681 
682 
683 } // namespace detail
684 
685 /** \addtogroup ConvolutionFilters
686 */
687 //@{
688 
689 /********************************************************/
690 /* */
691 /* separableConvolveMultiArray */
692 /* */
693 /********************************************************/
694 
695 /** \brief Separated convolution on multi-dimensional arrays.
696 
697  This function computes a separated convolution on all dimensions
698  of the given multi-dimensional array. Both source and destination
699  arrays are represented by iterators, shape objects and accessors.
700  The destination array is required to already have the correct size.
701 
702  There are two variants of this functions: one takes a single kernel
703  of type \ref vigra::Kernel1D which is then applied to all dimensions,
704  whereas the other requires an iterator referencing a sequence of
705  \ref vigra::Kernel1D objects, one for every dimension of the data.
706  Then the first kernel in this sequence is applied to the innermost
707  dimension (e.g. the x-axis of an image), while the last is applied to the
708  outermost dimension (e.g. the z-axis in a 3D image).
709 
710  This function may work in-place, which means that <tt>source.data() == dest.data()</tt> is allowed.
711  A full-sized internal array is only allocated if working on the destination
712  array directly would cause round-off errors (i.e. if
713  <tt>typeid(typename NumericTraits<T2>::RealPromote) != typeid(T2)</tt>).
714 
715  If <tt>start</tt> and <tt>stop</tt> have non-default values, they must represent
716  a valid subarray of the input array. The convolution is then restricted to that
717  subarray, and it is assumed that the output array only refers to the
718  subarray (i.e. <tt>dest.shape() == stop - start</tt>). Negative ROI boundaries are
719  interpreted relative to the end of the respective dimension
720  (i.e. <tt>if(stop[k] < 0) stop[k] += source.shape(k);</tt>).
721 
722  <b> Declarations:</b>
723 
724  pass arbitrary-dimensional array views:
725  \code
726  namespace vigra {
727  // apply each kernel from the sequence 'kernels' in turn
728  template <unsigned int N, class T1, class S1,
729  class T2, class S2,
730  class KernelIterator>
731  void
732  separableConvolveMultiArray(MultiArrayView<N, T1, S1> const & source,
733  MultiArrayView<N, T2, S2> dest,
734  KernelIterator kernels,
735  typename MultiArrayShape<N>::type start = typename MultiArrayShape<N>::type(),
736  typename MultiArrayShape<N>::type stop = typename MultiArrayShape<N>::type());
737 
738  // apply the same kernel to all dimensions
739  template <unsigned int N, class T1, class S1,
740  class T2, class S2,
741  class T>
742  void
743  separableConvolveMultiArray(MultiArrayView<N, T1, S1> const & source,
744  MultiArrayView<N, T2, S2> dest,
745  Kernel1D<T> const & kernel,
746  typename MultiArrayShape<N>::type const & start = typename MultiArrayShape<N>::type(),
747  typename MultiArrayShape<N>::type const & stop = typename MultiArrayShape<N>::type());
748  }
749  \endcode
750 
751  \deprecatedAPI{separableConvolveMultiArray}
752  pass \ref MultiIteratorPage "MultiIterators" and \ref DataAccessors :
753  \code
754  namespace vigra {
755  // apply the same kernel to all dimensions
756  template <class SrcIterator, class SrcShape, class SrcAccessor,
757  class DestIterator, class DestAccessor, class T>
758  void
759  separableConvolveMultiArray(SrcIterator siter, SrcShape const & shape, SrcAccessor src,
760  DestIterator diter, DestAccessor dest,
761  Kernel1D<T> const & kernel,
762  SrcShape const & start = SrcShape(),
763  SrcShape const & stop = SrcShape());
764 
765  // apply each kernel from the sequence 'kernels' in turn
766  template <class SrcIterator, class SrcShape, class SrcAccessor,
767  class DestIterator, class DestAccessor, class KernelIterator>
768  void
769  separableConvolveMultiArray(SrcIterator siter, SrcShape const & shape, SrcAccessor src,
770  DestIterator diter, DestAccessor dest,
771  KernelIterator kernels,
772  SrcShape const & start = SrcShape(),
773  SrcShape const & stop = SrcShape());
774  }
775  \endcode
776  use argument objects in conjunction with \ref ArgumentObjectFactories :
777  \code
778  namespace vigra {
779  // apply the same kernel to all dimensions
780  template <class SrcIterator, class SrcShape, class SrcAccessor,
781  class DestIterator, class DestAccessor, class T>
782  void
783  separableConvolveMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
784  pair<DestIterator, DestAccessor> const & dest,
785  Kernel1D<T> const & kernel,
786  SrcShape const & start = SrcShape(),
787  SrcShape const & stop = SrcShape());
788 
789  // apply each kernel from the sequence 'kernels' in turn
790  template <class SrcIterator, class SrcShape, class SrcAccessor,
791  class DestIterator, class DestAccessor, class KernelIterator>
792  void
793  separableConvolveMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
794  pair<DestIterator, DestAccessor> const & dest,
795  KernelIterator kernels,
796  SrcShape const & start = SrcShape(),
797  SrcShape const & stop = SrcShape());
798  }
799  \endcode
800  \deprecatedEnd
801 
802  <b> Usage:</b>
803 
804  <b>\#include</b> <vigra/multi_convolution.hxx><br/>
805  Namespace: vigra
806 
807  \code
808  Shape3 shape(width, height, depth);
809  MultiArray<3, unsigned char> source(shape);
810  MultiArray<3, float> dest(shape);
811  ...
812  Kernel1D<float> gauss;
813  gauss.initGaussian(sigma);
814 
815  // smooth all dimensions with the same kernel
816  separableConvolveMultiArray(source, dest, gauss);
817 
818  // create 3 Gauss kernels, one for each dimension, but smooth the z-axis less
819  ArrayVector<Kernel1D<float> > kernels(3, gauss);
820  kernels[2].initGaussian(sigma / 2.0);
821 
822  // perform Gaussian smoothing on all dimensions
823  separableConvolveMultiArray(source, dest, kernels.begin());
824 
825  // create output array for a ROI
826  MultiArray<3, float> destROI(shape - Shape3(10,10,10));
827 
828  // only smooth the given ROI (ignore 5 pixels on all sides of the array)
829  separableConvolveMultiArray(source, destROI, gauss, Shape3(5,5,5), Shape3(-5,-5,-5));
830  \endcode
831 
832  \deprecatedUsage{separableConvolveMultiArray}
833  \code
834  MultiArray<3, unsigned char>::size_type shape(width, height, depth);
835  MultiArray<3, unsigned char> source(shape);
836  MultiArray<3, float> dest(shape);
837  ...
838  Kernel1D<float> gauss;
839  gauss.initGaussian(sigma);
840  // create 3 Gauss kernels, one for each dimension
841  ArrayVector<Kernel1D<float> > kernels(3, gauss);
842 
843  // perform Gaussian smoothing on all dimensions
844  separableConvolveMultiArray(source, dest,
845  kernels.begin());
846  \endcode
847  <b> Required Interface:</b>
848  \code
849  see \ref separableConvolveImage(), in addition:
850 
851  NumericTraits<T1>::RealPromote s = src[0];
852 
853  s = s + s;
854  s = kernel(0) * s;
855  \endcode
856  \deprecatedEnd
857 
858  \see vigra::Kernel1D, convolveLine()
859 */
861 
862 template <class SrcIterator, class SrcShape, class SrcAccessor,
863  class DestIterator, class DestAccessor, class KernelIterator>
864 void
865 separableConvolveMultiArray( SrcIterator s, SrcShape const & shape, SrcAccessor src,
866  DestIterator d, DestAccessor dest,
867  KernelIterator kernels,
868  SrcShape start = SrcShape(),
869  SrcShape stop = SrcShape())
870 {
871  typedef typename NumericTraits<typename DestAccessor::value_type>::RealPromote TmpType;
872 
873 
874  if(stop != SrcShape())
875  {
876 
877  enum { N = 1 + SrcIterator::level };
878  detail::RelativeToAbsoluteCoordinate<N-1>::exec(shape, start);
879  detail::RelativeToAbsoluteCoordinate<N-1>::exec(shape, stop);
880 
881  for(int k=0; k<N; ++k)
882  vigra_precondition(0 <= start[k] && start[k] < stop[k] && stop[k] <= shape[k],
883  "separableConvolveMultiArray(): invalid subarray shape.");
884 
885  detail::internalSeparableConvolveSubarray(s, shape, src, d, dest, kernels, start, stop);
886  }
887  else if(!IsSameType<TmpType, typename DestAccessor::value_type>::boolResult)
888  {
889  // need a temporary array to avoid rounding errors
890  MultiArray<SrcShape::static_size, TmpType> tmpArray(shape);
891  detail::internalSeparableConvolveMultiArrayTmp( s, shape, src,
892  tmpArray.traverser_begin(), typename AccessorTraits<TmpType>::default_accessor(), kernels );
893  copyMultiArray(srcMultiArrayRange(tmpArray), destIter(d, dest));
894  }
895  else
896  {
897  // work directly on the destination array
898  detail::internalSeparableConvolveMultiArrayTmp( s, shape, src, d, dest, kernels );
899  }
900 }
901 
902 template <class SrcIterator, class SrcShape, class SrcAccessor,
903  class DestIterator, class DestAccessor, class T>
904 inline void
905 separableConvolveMultiArray( SrcIterator s, SrcShape const & shape, SrcAccessor src,
906  DestIterator d, DestAccessor dest,
907  Kernel1D<T> const & kernel,
908  SrcShape const & start = SrcShape(),
909  SrcShape const & stop = SrcShape())
910 {
911  ArrayVector<Kernel1D<T> > kernels(shape.size(), kernel);
912 
913  separableConvolveMultiArray( s, shape, src, d, dest, kernels.begin(), start, stop);
914 }
915 
916 template <class SrcIterator, class SrcShape, class SrcAccessor,
917  class DestIterator, class DestAccessor, class KernelIterator>
918 inline void
919 separableConvolveMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
920  pair<DestIterator, DestAccessor> const & dest,
921  KernelIterator kit,
922  SrcShape const & start = SrcShape(),
923  SrcShape const & stop = SrcShape())
924 {
925  separableConvolveMultiArray( source.first, source.second, source.third,
926  dest.first, dest.second, kit, start, stop );
927 }
928 
929 template <class SrcIterator, class SrcShape, class SrcAccessor,
930  class DestIterator, class DestAccessor, class T>
931 inline void
932 separableConvolveMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
933  pair<DestIterator, DestAccessor> const & dest,
934  Kernel1D<T> const & kernel,
935  SrcShape const & start = SrcShape(),
936  SrcShape const & stop = SrcShape())
937 {
938  ArrayVector<Kernel1D<T> > kernels(source.second.size(), kernel);
939 
940  separableConvolveMultiArray( source.first, source.second, source.third,
941  dest.first, dest.second, kernels.begin(), start, stop);
942 }
943 
944 template <unsigned int N, class T1, class S1,
945  class T2, class S2,
946  class KernelIterator>
947 inline void
948 separableConvolveMultiArray(MultiArrayView<N, T1, S1> const & source,
949  MultiArrayView<N, T2, S2> dest,
950  KernelIterator kit,
951  typename MultiArrayShape<N>::type start = typename MultiArrayShape<N>::type(),
952  typename MultiArrayShape<N>::type stop = typename MultiArrayShape<N>::type())
953 {
954  if(stop != typename MultiArrayShape<N>::type())
955  {
956  detail::RelativeToAbsoluteCoordinate<N-1>::exec(source.shape(), start);
957  detail::RelativeToAbsoluteCoordinate<N-1>::exec(source.shape(), stop);
958  vigra_precondition(dest.shape() == (stop - start),
959  "separableConvolveMultiArray(): shape mismatch between ROI and output.");
960  }
961  else
962  {
963  vigra_precondition(source.shape() == dest.shape(),
964  "separableConvolveMultiArray(): shape mismatch between input and output.");
965  }
966  separableConvolveMultiArray( srcMultiArrayRange(source),
967  destMultiArray(dest), kit, start, stop );
968 }
969 
970 template <unsigned int N, class T1, class S1,
971  class T2, class S2,
972  class T>
973 inline void
974 separableConvolveMultiArray(MultiArrayView<N, T1, S1> const & source,
975  MultiArrayView<N, T2, S2> dest,
976  Kernel1D<T> const & kernel,
977  typename MultiArrayShape<N>::type const & start = typename MultiArrayShape<N>::type(),
978  typename MultiArrayShape<N>::type const & stop = typename MultiArrayShape<N>::type())
979 {
980  ArrayVector<Kernel1D<T> > kernels(N, kernel);
981  separableConvolveMultiArray(source, dest, kernels.begin(), start, stop);
982 }
983 
984 /********************************************************/
985 /* */
986 /* convolveMultiArrayOneDimension */
987 /* */
988 /********************************************************/
989 
990 /** \brief Convolution along a single dimension of a multi-dimensional arrays.
991 
992  This function computes a convolution along one dimension (specified by
993  the parameter <tt>dim</tt> of the given multi-dimensional array with the given
994  <tt>kernel</tt>. The destination array must already have the correct size.
995 
996  If <tt>start</tt> and <tt>stop</tt> have non-default values, they must represent
997  a valid subarray of the input array. The convolution is then restricted to that
998  subarray, and it is assumed that the output array only refers to the
999  subarray (i.e. <tt>dest.shape() == stop - start</tt>). Negative ROI boundaries are
1000  interpreted relative to the end of the respective dimension
1001  (i.e. <tt>if(stop[k] < 0) stop[k] += source.shape(k);</tt>).
1002 
1003  This function may work in-place, which means that <tt>source.data() == dest.data()</tt> is allowed.
1004 
1005  <b> Declarations:</b>
1006 
1007  pass arbitrary-dimensional array views:
1008  \code
1009  namespace vigra {
1010  template <unsigned int N, class T1, class S1,
1011  class T2, class S2,
1012  class T>
1013  void
1014  convolveMultiArrayOneDimension(MultiArrayView<N, T1, S1> const & source,
1015  MultiArrayView<N, T2, S2> dest,
1016  unsigned int dim,
1017  Kernel1D<T> const & kernel,
1018  typename MultiArrayShape<N>::type start = typename MultiArrayShape<N>::type(),
1019  typename MultiArrayShape<N>::type stop = typename MultiArrayShape<N>::type());
1020  }
1021  \endcode
1022 
1023  \deprecatedAPI{convolveMultiArrayOneDimension}
1024  pass \ref MultiIteratorPage "MultiIterators" and \ref DataAccessors :
1025  \code
1026  namespace vigra {
1027  template <class SrcIterator, class SrcShape, class SrcAccessor,
1028  class DestIterator, class DestAccessor, class T>
1029  void
1030  convolveMultiArrayOneDimension(SrcIterator siter, SrcShape const & shape, SrcAccessor src,
1031  DestIterator diter, DestAccessor dest,
1032  unsigned int dim, vigra::Kernel1D<T> const & kernel,
1033  SrcShape const & start = SrcShape(),
1034  SrcShape const & stop = SrcShape());
1035  }
1036  \endcode
1037  use argument objects in conjunction with \ref ArgumentObjectFactories :
1038  \code
1039  namespace vigra {
1040  template <class SrcIterator, class SrcShape, class SrcAccessor,
1041  class DestIterator, class DestAccessor, class T>
1042  void
1043  convolveMultiArrayOneDimension(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
1044  pair<DestIterator, DestAccessor> const & dest,
1045  unsigned int dim, vigra::Kernel1D<T> const & kernel,
1046  SrcShape const & start = SrcShape(),
1047  SrcShape const & stop = SrcShape());
1048  }
1049  \endcode
1050  \deprecatedEnd
1051 
1052  <b> Usage:</b>
1053 
1054  <b>\#include</b> <vigra/multi_convolution.hxx><br/>
1055  Namespace: vigra
1056 
1057  \code
1058  Shape3 shape(width, height, depth);
1059  MultiArray<3, unsigned char> source(shape);
1060  MultiArray<3, float> dest(shape);
1061  ...
1062  Kernel1D<float> gauss;
1063  gauss.initGaussian(sigma);
1064 
1065  // perform Gaussian smoothing along dimension 1 (height)
1066  convolveMultiArrayOneDimension(source, dest, 1, gauss);
1067  \endcode
1068 
1069  \see separableConvolveMultiArray()
1070 */
1072 
1073 template <class SrcIterator, class SrcShape, class SrcAccessor,
1074  class DestIterator, class DestAccessor, class T>
1075 void
1076 convolveMultiArrayOneDimension(SrcIterator s, SrcShape const & shape, SrcAccessor src,
1077  DestIterator d, DestAccessor dest,
1078  unsigned int dim, vigra::Kernel1D<T> const & kernel,
1079  SrcShape const & start = SrcShape(),
1080  SrcShape const & stop = SrcShape())
1081 {
1082  enum { N = 1 + SrcIterator::level };
1083  vigra_precondition( dim < N,
1084  "convolveMultiArrayOneDimension(): The dimension number to convolve must be smaller "
1085  "than the data dimensionality" );
1086 
1087  typedef typename NumericTraits<typename DestAccessor::value_type>::RealPromote TmpType;
1088  typedef typename AccessorTraits<TmpType>::default_const_accessor TmpAccessor;
1089  ArrayVector<TmpType> tmp( shape[dim] );
1090 
1091  typedef MultiArrayNavigator<SrcIterator, N> SNavigator;
1092  typedef MultiArrayNavigator<DestIterator, N> DNavigator;
1093 
1094  SrcShape sstart, sstop(shape), dstart, dstop(shape);
1095 
1096  if(stop != SrcShape())
1097  {
1098  sstart = start;
1099  sstop = stop;
1100  sstart[dim] = 0;
1101  sstop[dim] = shape[dim];
1102  dstop = stop - start;
1103  }
1104 
1105  SNavigator snav( s, sstart, sstop, dim );
1106  DNavigator dnav( d, dstart, dstop, dim );
1107 
1108  for( ; snav.hasMore(); snav++, dnav++ )
1109  {
1110  // first copy source to temp for maximum cache efficiency
1111  copyLine(snav.begin(), snav.end(), src,
1112  tmp.begin(), typename AccessorTraits<TmpType>::default_accessor() );
1113 
1114  convolveLine(srcIterRange( tmp.begin(), tmp.end(), TmpAccessor()),
1115  destIter( dnav.begin(), dest ),
1116  kernel1d( kernel), start[dim], stop[dim]);
1117  }
1118 }
1119 
1120 template <class SrcIterator, class SrcShape, class SrcAccessor,
1121  class DestIterator, class DestAccessor, class T>
1122 inline void
1123 convolveMultiArrayOneDimension(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
1124  pair<DestIterator, DestAccessor> const & dest,
1125  unsigned int dim,
1126  Kernel1D<T> const & kernel,
1127  SrcShape const & start = SrcShape(),
1128  SrcShape const & stop = SrcShape())
1129 {
1130  convolveMultiArrayOneDimension(source.first, source.second, source.third,
1131  dest.first, dest.second, dim, kernel, start, stop);
1132 }
1133 
1134 template <unsigned int N, class T1, class S1,
1135  class T2, class S2,
1136  class T>
1137 inline void
1138 convolveMultiArrayOneDimension(MultiArrayView<N, T1, S1> const & source,
1139  MultiArrayView<N, T2, S2> dest,
1140  unsigned int dim,
1141  Kernel1D<T> const & kernel,
1142  typename MultiArrayShape<N>::type start = typename MultiArrayShape<N>::type(),
1143  typename MultiArrayShape<N>::type stop = typename MultiArrayShape<N>::type())
1144 {
1145  if(stop != typename MultiArrayShape<N>::type())
1146  {
1147  detail::RelativeToAbsoluteCoordinate<N-1>::exec(source.shape(), start);
1148  detail::RelativeToAbsoluteCoordinate<N-1>::exec(source.shape(), stop);
1149  vigra_precondition(dest.shape() == (stop - start),
1150  "convolveMultiArrayOneDimension(): shape mismatch between ROI and output.");
1151  }
1152  else
1153  {
1154  vigra_precondition(source.shape() == dest.shape(),
1155  "convolveMultiArrayOneDimension(): shape mismatch between input and output.");
1156  }
1157  convolveMultiArrayOneDimension(srcMultiArrayRange(source),
1158  destMultiArray(dest), dim, kernel, start, stop);
1159 }
1160 
1161 /********************************************************/
1162 /* */
1163 /* gaussianSmoothMultiArray */
1164 /* */
1165 /********************************************************/
1166 
1167 /** \weakgroup ParallelProcessing
1168  \sa gaussianSmoothMultiArray <B>(...,</B> BlockwiseConvolutionOptions<B>)</B>
1169  */
1170 
1171 /** \brief Isotropic Gaussian smoothing of a multi-dimensional arrays.
1172 
1173  This function computes an isotropic convolution of the given N-dimensional
1174  array with a Gaussian filter at the given standard deviation <tt>sigma</tt>.
1175  Both source and destination arrays are represented by
1176  iterators, shape objects and accessors. The destination array is required to
1177  already have the correct size. This function may work in-place, which means
1178  that <tt>source.data() == dest.data()</tt> is allowed. It is implemented by a call to
1179  \ref separableConvolveMultiArray() with the appropriate kernel.
1180 
1181  Anisotropic data should be provided with appropriate \ref vigra::ConvolutionOptions
1182  to adjust the filter sizes for the resolution of each axis.
1183  Otherwise, the parameter <tt>opt</tt> is optional unless the parameter
1184  <tt>sigma</tt> is omitted.
1185 
1186  If you pass \ref vigra::BlockwiseConvolutionOptions instead, the algorithm will
1187  be executed in parallel on data blocks of a certain size. The block size can be
1188  customized via <tt>BlockwiseConvolutionOptions::blockShape()</tt>, but the defaults
1189  usually work reasonably. By default, the number of threads equals the capabilities
1190  of your hardware, but you can change this via <tt>BlockwiseConvolutionOptions::numThreads()</tt>.
1191 
1192  <b> Declarations:</b>
1193 
1194  pass arbitrary-dimensional array views:
1195  \code
1196  namespace vigra {
1197  // pass filter scale explicitly
1198  template <unsigned int N, class T1, class S1,
1199  class T2, class S2>
1200  void
1201  gaussianSmoothMultiArray(MultiArrayView<N, T1, S1> const & source,
1202  MultiArrayView<N, T2, S2> dest,
1203  double sigma,
1204  ConvolutionOptions<N> opt = ConvolutionOptions<N>());
1205 
1206  // pass filer scale(s) in the option object
1207  template <unsigned int N, class T1, class S1,
1208  class T2, class S2>
1209  void
1210  gaussianSmoothMultiArray(MultiArrayView<N, T1, S1> const & source,
1211  MultiArrayView<N, T2, S2> dest,
1212  ConvolutionOptions<N> opt);
1213 
1214  // as above, but execute algorithm in parallel
1215  template <unsigned int N, class T1, class S1,
1216  class T2, class S2>
1217  void
1218  gaussianSmoothMultiArray(MultiArrayView<N, T1, S1> const & source,
1219  MultiArrayView<N, T2, S2> dest,
1220  BlockwiseConvolutionOptions<N> opt);
1221  }
1222  \endcode
1223 
1224  \deprecatedAPI{gaussianSmoothMultiArray}
1225  pass \ref MultiIteratorPage "MultiIterators" and \ref DataAccessors :
1226  \code
1227  namespace vigra {
1228  template <class SrcIterator, class SrcShape, class SrcAccessor,
1229  class DestIterator, class DestAccessor>
1230  void
1231  gaussianSmoothMultiArray(SrcIterator siter, SrcShape const & shape, SrcAccessor src,
1232  DestIterator diter, DestAccessor dest,
1233  double sigma,
1234  const ConvolutionOptions<N> & opt = ConvolutionOptions<N>());
1235  }
1236  \endcode
1237  use argument objects in conjunction with \ref ArgumentObjectFactories :
1238  \code
1239  namespace vigra {
1240  template <class SrcIterator, class SrcShape, class SrcAccessor,
1241  class DestIterator, class DestAccessor>
1242  void
1243  gaussianSmoothMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
1244  pair<DestIterator, DestAccessor> const & dest,
1245  double sigma,
1246  const ConvolutionOptions<N> & opt = ConvolutionOptions<N>());
1247  }
1248  \endcode
1249  \deprecatedEnd
1250 
1251  <b> Usage:</b>
1252 
1253  <b>\#include</b> <vigra/multi_convolution.hxx> (sequential version)<br/>
1254  <b>\#include</b> <vigra/multi_blockwise.hxx> (parallel version)<br/>
1255  Namespace: vigra
1256 
1257  \code
1258  Shape3 shape(width, height, depth);
1259  MultiArray<3, unsigned char> source(shape);
1260  MultiArray<3, float> dest(shape);
1261  ...
1262  // perform isotropic Gaussian smoothing at scale 'sigma'
1263  gaussianSmoothMultiArray(source, dest, sigma);
1264  \endcode
1265 
1266  <b> Multi-threaded execution:</b>
1267 
1268  \code
1269  Shape3 shape(width, height, depth);
1270  MultiArray<3, unsigned char> source(shape);
1271  MultiArray<3, float> dest(shape);
1272  ...
1273  BlockwiseConvolutionOptions<3> opt;
1274  opt.numThreads(4); // use 4 threads (uses hardware default if not given)
1275  opt.innerScale(sigma);
1276 
1277  // perform isotropic Gaussian smoothing at scale 'sigma' in parallel
1278  gaussianSmoothMultiArray(source, dest, sigma, opt);
1279  \endcode
1280 
1281  <b> Usage with anisotropic data:</b>
1282 
1283  \code
1284  Shape3 shape(width, height, depth);
1285  MultiArray<3, unsigned char> source(shape);
1286  MultiArray<3, float> dest(shape);
1287  TinyVector<float, 3> step_size;
1288  TinyVector<float, 3> resolution_sigmas;
1289  ...
1290  // perform anisotropic Gaussian smoothing at scale 'sigma'
1291  gaussianSmoothMultiArray(source, dest, sigma,
1292  ConvolutionOptions<3>().stepSize(step_size).resolutionStdDev(resolution_sigmas));
1293  \endcode
1294 
1295  \see separableConvolveMultiArray()
1296 */
1298 
1299 template <class SrcIterator, class SrcShape, class SrcAccessor,
1300  class DestIterator, class DestAccessor>
1301 void
1302 gaussianSmoothMultiArray( SrcIterator s, SrcShape const & shape, SrcAccessor src,
1303  DestIterator d, DestAccessor dest,
1304  const ConvolutionOptions<SrcShape::static_size> & opt,
1305  const char *const function_name = "gaussianSmoothMultiArray" )
1306 {
1307  static const int N = SrcShape::static_size;
1308 
1309  typename ConvolutionOptions<N>::ScaleIterator params = opt.scaleParams();
1310  ArrayVector<Kernel1D<double> > kernels(N);
1311 
1312  for (int dim = 0; dim < N; ++dim, ++params)
1313  kernels[dim].initGaussian(params.sigma_scaled(function_name, true),
1314  1.0, opt.window_ratio);
1315 
1316  separableConvolveMultiArray(s, shape, src, d, dest, kernels.begin(), opt.from_point, opt.to_point);
1317 }
1318 
1319 template <class SrcIterator, class SrcShape, class SrcAccessor,
1320  class DestIterator, class DestAccessor>
1321 inline void
1322 gaussianSmoothMultiArray( SrcIterator s, SrcShape const & shape, SrcAccessor src,
1323  DestIterator d, DestAccessor dest, double sigma,
1324  const ConvolutionOptions<SrcShape::static_size> & opt = ConvolutionOptions<SrcShape::static_size>())
1325 {
1326  ConvolutionOptions<SrcShape::static_size> par = opt;
1327  gaussianSmoothMultiArray(s, shape, src, d, dest, par.stdDev(sigma));
1328 }
1329 
1330 template <class SrcIterator, class SrcShape, class SrcAccessor,
1331  class DestIterator, class DestAccessor>
1332 inline void
1333 gaussianSmoothMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
1334  pair<DestIterator, DestAccessor> const & dest,
1335  const ConvolutionOptions<SrcShape::static_size> & opt)
1336 {
1337  gaussianSmoothMultiArray( source.first, source.second, source.third,
1338  dest.first, dest.second, opt );
1339 }
1340 
1341 template <class SrcIterator, class SrcShape, class SrcAccessor,
1342  class DestIterator, class DestAccessor>
1343 inline void
1344 gaussianSmoothMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
1345  pair<DestIterator, DestAccessor> const & dest, double sigma,
1346  const ConvolutionOptions<SrcShape::static_size> & opt = ConvolutionOptions<SrcShape::static_size>())
1347 {
1348  gaussianSmoothMultiArray( source.first, source.second, source.third,
1349  dest.first, dest.second, sigma, opt );
1350 }
1351 
1352 template <unsigned int N, class T1, class S1,
1353  class T2, class S2>
1354 inline void
1355 gaussianSmoothMultiArray(MultiArrayView<N, T1, S1> const & source,
1356  MultiArrayView<N, T2, S2> dest,
1357  ConvolutionOptions<N> opt)
1358 {
1359  if(opt.to_point != typename MultiArrayShape<N>::type())
1360  {
1361  detail::RelativeToAbsoluteCoordinate<N-1>::exec(source.shape(), opt.from_point);
1362  detail::RelativeToAbsoluteCoordinate<N-1>::exec(source.shape(), opt.to_point);
1363  vigra_precondition(dest.shape() == (opt.to_point - opt.from_point),
1364  "gaussianSmoothMultiArray(): shape mismatch between ROI and output.");
1365  }
1366  else
1367  {
1368  vigra_precondition(source.shape() == dest.shape(),
1369  "gaussianSmoothMultiArray(): shape mismatch between input and output.");
1370  }
1371 
1372  gaussianSmoothMultiArray( srcMultiArrayRange(source),
1373  destMultiArray(dest), opt );
1374 }
1375 
1376 template <unsigned int N, class T1, class S1,
1377  class T2, class S2>
1378 inline void
1379 gaussianSmoothMultiArray(MultiArrayView<N, T1, S1> const & source,
1380  MultiArrayView<N, T2, S2> dest,
1381  double sigma,
1382  ConvolutionOptions<N> opt = ConvolutionOptions<N>())
1383 {
1384  gaussianSmoothMultiArray( source, dest, opt.stdDev(sigma) );
1385 }
1386 
1387 
1388 /********************************************************/
1389 /* */
1390 /* gaussianGradientMultiArray */
1391 /* */
1392 /********************************************************/
1393 
1394 /** \weakgroup ParallelProcessing
1395  \sa gaussianGradientMultiArray <B>(...,</B> BlockwiseConvolutionOptions<B>)</B>
1396  */
1397 
1398 /** \brief Calculate Gaussian gradient of a multi-dimensional arrays.
1399 
1400  This function computes the Gaussian gradient of the given N-dimensional
1401  array with a sequence of first-derivative-of-Gaussian filters at the given
1402  standard deviation <tt>sigma</tt> (differentiation is applied to each dimension
1403  in turn, starting with the innermost dimension). The destination array is
1404  required to have a vector valued pixel type with as many elements as the number of
1405  dimensions. This function is implemented by calls to
1406  \ref separableConvolveMultiArray() with the appropriate kernels.
1407 
1408  Anisotropic data should be provided with appropriate \ref vigra::ConvolutionOptions
1409  to adjust the filter sizes for the resolution of each axis.
1410  Otherwise, the parameter <tt>opt</tt> is optional unless the parameter
1411  <tt>sigma</tt> is omitted.
1412 
1413  If you pass \ref vigra::BlockwiseConvolutionOptions instead, the algorithm will
1414  be executed in parallel on data blocks of a certain size. The block size can be
1415  customized via <tt>BlockwiseConvolutionOptions::blockShape()</tt>, but the defaults
1416  usually work reasonably. By default, the number of threads equals the capabilities
1417  of your hardware, but you can change this via <tt>BlockwiseConvolutionOptions::numThreads()</tt>.
1418 
1419  <b> Declarations:</b>
1420 
1421  pass arbitrary-dimensional array views:
1422  \code
1423  namespace vigra {
1424  // pass filter scale explicitly
1425  template <unsigned int N, class T1, class S1,
1426  class T2, class S2>
1427  void
1428  gaussianGradientMultiArray(MultiArrayView<N, T1, S1> const & source,
1429  MultiArrayView<N, TinyVector<T2, N>, S2> dest,
1430  double sigma,
1431  ConvolutionOptions<N> opt = ConvolutionOptions<N>());
1432 
1433  // pass filter scale(s) in option object
1434  template <unsigned int N, class T1, class S1,
1435  class T2, class S2>
1436  void
1437  gaussianGradientMultiArray(MultiArrayView<N, T1, S1> const & source,
1438  MultiArrayView<N, TinyVector<T2, N>, S2> dest,
1439  ConvolutionOptions<N> opt);
1440 
1441  // likewise, but execute algorithm in parallel
1442  template <unsigned int N, class T1, class S1,
1443  class T2, class S2>
1444  void
1445  gaussianGradientMultiArray(MultiArrayView<N, T1, S1> const & source,
1446  MultiArrayView<N, TinyVector<T2, N>, S2> dest,
1447  BlockwiseConvolutionOptions<N> opt);
1448  }
1449  \endcode
1450 
1451  \deprecatedAPI{gaussianGradientMultiArray}
1452  pass \ref MultiIteratorPage "MultiIterators" and \ref DataAccessors :
1453  \code
1454  namespace vigra {
1455  template <class SrcIterator, class SrcShape, class SrcAccessor,
1456  class DestIterator, class DestAccessor>
1457  void
1458  gaussianGradientMultiArray(SrcIterator siter, SrcShape const & shape, SrcAccessor src,
1459  DestIterator diter, DestAccessor dest,
1460  double sigma,
1461  const ConvolutionOptions<N> & opt = ConvolutionOptions<N>());
1462  }
1463  \endcode
1464  use argument objects in conjunction with \ref ArgumentObjectFactories :
1465  \code
1466  namespace vigra {
1467  template <class SrcIterator, class SrcShape, class SrcAccessor,
1468  class DestIterator, class DestAccessor>
1469  void
1470  gaussianGradientMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
1471  pair<DestIterator, DestAccessor> const & dest,
1472  double sigma,
1473  const ConvolutionOptions<N> & opt = ConvolutionOptions<N>());
1474  }
1475  \endcode
1476  \deprecatedEnd
1477 
1478  <b> Usage:</b>
1479 
1480  <b>\#include</b> <vigra/multi_convolution.hxx> (sequential version)<br/>
1481  <b>\#include</b> <vigra/multi_blockwise.hxx> (parallel version)<br/>
1482  Namespace: vigra
1483 
1484  \code
1485  Shape3 shape(width, height, depth);
1486  MultiArray<3, unsigned char> source(shape);
1487  MultiArray<3, TinyVector<float, 3> > dest(shape);
1488  ...
1489  // compute Gaussian gradient at scale sigma
1490  gaussianGradientMultiArray(source, dest, sigma);
1491  \endcode
1492 
1493  <b> Usage with anisotropic data:</b>
1494 
1495  \code
1496  Shape3 shape(width, height, depth);
1497  MultiArray<3, unsigned char> source(shape);
1498  MultiArray<3, TinyVector<float, 3> > dest(shape);
1499  TinyVector<float, 3> step_size;
1500  TinyVector<float, 3> resolution_sigmas;
1501  ...
1502  // compute Gaussian gradient at scale sigma
1503  gaussianGradientMultiArray(source, dest, sigma,
1504  ConvolutionOptions<3>().stepSize(step_size).resolutionStdDev(resolution_sigmas));
1505  \endcode
1506 
1507  \see separableConvolveMultiArray()
1508 */
1510 
1511 template <class SrcIterator, class SrcShape, class SrcAccessor,
1512  class DestIterator, class DestAccessor>
1513 void
1514 gaussianGradientMultiArray(SrcIterator si, SrcShape const & shape, SrcAccessor src,
1515  DestIterator di, DestAccessor dest,
1516  ConvolutionOptions<SrcShape::static_size> const & opt,
1517  const char *const function_name = "gaussianGradientMultiArray")
1518 {
1519  typedef typename DestAccessor::value_type DestType;
1520  typedef typename DestType::value_type DestValueType;
1521  typedef typename NumericTraits<DestValueType>::RealPromote KernelType;
1522 
1523  static const int N = SrcShape::static_size;
1524  typedef typename ConvolutionOptions<N>::ScaleIterator ParamType;
1525 
1526  for(int k=0; k<N; ++k)
1527  if(shape[k] <=0)
1528  return;
1529 
1530  vigra_precondition(N == (int)dest.size(di),
1531  "gaussianGradientMultiArray(): Wrong number of channels in output array.");
1532 
1533  ParamType params = opt.scaleParams();
1534  ParamType params2(params);
1535 
1536  ArrayVector<Kernel1D<KernelType> > plain_kernels(N);
1537  for (int dim = 0; dim < N; ++dim, ++params)
1538  {
1539  double sigma = params.sigma_scaled(function_name);
1540  plain_kernels[dim].initGaussian(sigma, 1.0, opt.window_ratio);
1541  }
1542 
1543  typedef VectorElementAccessor<DestAccessor> ElementAccessor;
1544 
1545  // compute gradient components
1546  for (int dim = 0; dim < N; ++dim, ++params2)
1547  {
1548  ArrayVector<Kernel1D<KernelType> > kernels(plain_kernels);
1549  kernels[dim].initGaussianDerivative(params2.sigma_scaled(), 1, 1.0, opt.window_ratio);
1550  detail::scaleKernel(kernels[dim], 1.0 / params2.step_size());
1551  separableConvolveMultiArray(si, shape, src, di, ElementAccessor(dim, dest), kernels.begin(),
1552  opt.from_point, opt.to_point);
1553  }
1554 }
1555 
1556 template <class SrcIterator, class SrcShape, class SrcAccessor,
1557  class DestIterator, class DestAccessor>
1558 void
1559 gaussianGradientMultiArray(SrcIterator si, SrcShape const & shape, SrcAccessor src,
1560  DestIterator di, DestAccessor dest, double sigma,
1561  ConvolutionOptions<SrcShape::static_size> opt = ConvolutionOptions<SrcShape::static_size>())
1562 {
1563  gaussianGradientMultiArray(si, shape, src, di, dest, opt.stdDev(sigma));
1564 }
1565 
1566 template <class SrcIterator, class SrcShape, class SrcAccessor,
1567  class DestIterator, class DestAccessor>
1568 inline void
1569 gaussianGradientMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
1570  pair<DestIterator, DestAccessor> const & dest,
1571  ConvolutionOptions<SrcShape::static_size> const & opt )
1572 {
1573  gaussianGradientMultiArray( source.first, source.second, source.third,
1574  dest.first, dest.second, opt );
1575 }
1576 
1577 template <class SrcIterator, class SrcShape, class SrcAccessor,
1578  class DestIterator, class DestAccessor>
1579 inline void
1580 gaussianGradientMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
1581  pair<DestIterator, DestAccessor> const & dest,
1582  double sigma,
1583  const ConvolutionOptions<SrcShape::static_size> & opt = ConvolutionOptions<SrcShape::static_size>())
1584 {
1585  gaussianGradientMultiArray( source.first, source.second, source.third,
1586  dest.first, dest.second, sigma, opt );
1587 }
1588 
1589 template <unsigned int N, class T1, class S1,
1590  class T2, class S2>
1591 inline void
1592 gaussianGradientMultiArray(MultiArrayView<N, T1, S1> const & source,
1593  MultiArrayView<N, TinyVector<T2, N>, S2> dest,
1594  ConvolutionOptions<N> opt )
1595 {
1596  if(opt.to_point != typename MultiArrayShape<N>::type())
1597  {
1598  detail::RelativeToAbsoluteCoordinate<N-1>::exec(source.shape(), opt.from_point);
1599  detail::RelativeToAbsoluteCoordinate<N-1>::exec(source.shape(), opt.to_point);
1600  vigra_precondition(dest.shape() == (opt.to_point - opt.from_point),
1601  "gaussianGradientMultiArray(): shape mismatch between ROI and output.");
1602  }
1603  else
1604  {
1605  vigra_precondition(source.shape() == dest.shape(),
1606  "gaussianGradientMultiArray(): shape mismatch between input and output.");
1607  }
1608 
1609  gaussianGradientMultiArray( srcMultiArrayRange(source),
1610  destMultiArray(dest), opt );
1611 }
1612 
1613 template <unsigned int N, class T1, class S1,
1614  class T2, class S2>
1615 inline void
1616 gaussianGradientMultiArray(MultiArrayView<N, T1, S1> const & source,
1617  MultiArrayView<N, TinyVector<T2, N>, S2> dest,
1618  double sigma,
1619  ConvolutionOptions<N> opt = ConvolutionOptions<N>())
1620 {
1621  gaussianGradientMultiArray( source, dest, opt.stdDev(sigma) );
1622 }
1623 
1624 /********************************************************/
1625 /* */
1626 /* gaussianGradientMagnitude */
1627 /* */
1628 /********************************************************/
1629 
1630 namespace detail {
1631 
1632 template <unsigned int N, class T1, class S1,
1633  class T2, class S2>
1634 void
1635 gaussianGradientMagnitudeImpl(MultiArrayView<N+1, T1, S1> const & src,
1636  MultiArrayView<N, T2, S2> dest,
1637  ConvolutionOptions<N> opt = ConvolutionOptions<N>())
1638 {
1639  typename MultiArrayShape<N>::type shape(src.shape().template subarray<0,N>());
1640  if(opt.to_point != typename MultiArrayShape<N>::type())
1641  {
1642  detail::RelativeToAbsoluteCoordinate<N-1>::exec(shape, opt.from_point);
1643  detail::RelativeToAbsoluteCoordinate<N-1>::exec(shape, opt.to_point);
1644  vigra_precondition(dest.shape() == (opt.to_point - opt.from_point),
1645  "gaussianGradientMagnitude(): shape mismatch between ROI and output.");
1646  }
1647  else
1648  {
1649  vigra_precondition(shape == dest.shape(),
1650  "gaussianGradientMagnitude(): shape mismatch between input and output.");
1651  }
1652 
1653  dest.init(0.0);
1654 
1655  typedef typename NumericTraits<T1>::RealPromote TmpType;
1656  MultiArray<N, TinyVector<TmpType, N> > grad(dest.shape());
1657 
1658  using namespace multi_math;
1659 
1660  for(int k=0; k<src.shape(N); ++k)
1661  {
1662  gaussianGradientMultiArray(src.bindOuter(k), grad, opt);
1663 
1664  dest += squaredNorm(grad);
1665  }
1666  dest = sqrt(dest);
1667 }
1668 
1669 } // namespace detail
1670 
1671  // documentation is in convolution.hxx
1672 template <unsigned int N, class T1, class S1,
1673  class T2, class S2>
1674 inline void
1675 gaussianGradientMagnitude(MultiArrayView<N+1, Multiband<T1>, S1> const & src,
1676  MultiArrayView<N, T2, S2> dest,
1677  ConvolutionOptions<N> const & opt)
1678 {
1679  detail::gaussianGradientMagnitudeImpl<N, T1>(src, dest, opt);
1680 }
1681 
1682 template <unsigned int N, class T1, class S1,
1683  class T2, class S2>
1684 inline void
1685 gaussianGradientMagnitude(MultiArrayView<N, T1, S1> const & src,
1686  MultiArrayView<N, T2, S2> dest,
1687  ConvolutionOptions<N> const & opt)
1688 {
1689  detail::gaussianGradientMagnitudeImpl<N, T1>(src.insertSingletonDimension(N), dest, opt);
1690 }
1691 
1692 template <unsigned int N, class T1, int M, class S1,
1693  class T2, class S2>
1694 inline void
1695 gaussianGradientMagnitude(MultiArrayView<N, TinyVector<T1, M>, S1> const & src,
1696  MultiArrayView<N, T2, S2> dest,
1697  ConvolutionOptions<N> const & opt)
1698 {
1699  detail::gaussianGradientMagnitudeImpl<N, T1>(src.expandElements(N), dest, opt);
1700 }
1701 
1702 template <unsigned int N, class T1, unsigned int R, unsigned int G, unsigned int B, class S1,
1703  class T2, class S2>
1704 inline void
1705 gaussianGradientMagnitude(MultiArrayView<N, RGBValue<T1, R, G, B>, S1> const & src,
1706  MultiArrayView<N, T2, S2> dest,
1707  ConvolutionOptions<N> const & opt)
1708 {
1709  detail::gaussianGradientMagnitudeImpl<N, T1>(src.expandElements(N), dest, opt);
1710 }
1711 
1712 template <unsigned int N, class T1, class S1,
1713  class T2, class S2>
1714 inline void
1715 gaussianGradientMagnitude(MultiArrayView<N, T1, S1> const & src,
1716  MultiArrayView<N, T2, S2> dest,
1717  double sigma,
1718  ConvolutionOptions<N> opt = ConvolutionOptions<N>())
1719 {
1720  gaussianGradientMagnitude(src, dest, opt.stdDev(sigma));
1721 }
1722 
1723 template <unsigned int N, class T1, class S1,
1724  class T2, class S2>
1725 inline void
1726 gaussianGradientMagnitude(MultiArrayView<N+1, Multiband<T1>, S1> const & src,
1727  MultiArrayView<N, T2, S2> dest,
1728  double sigma,
1729  ConvolutionOptions<N> opt = ConvolutionOptions<N>())
1730 {
1731  gaussianGradientMagnitude<N>(src, dest, opt.stdDev(sigma));
1732 }
1733 
1734 /********************************************************/
1735 /* */
1736 /* symmetricGradientMultiArray */
1737 /* */
1738 /********************************************************/
1739 
1740 /** \weakgroup ParallelProcessing
1741  \sa symmetricGradientMultiArray <B>(...,</B> BlockwiseConvolutionOptions<B>)</B>
1742  */
1743 
1744 /** \brief Calculate gradient of a multi-dimensional arrays using symmetric difference filters.
1745 
1746  This function computes the gradient of the given N-dimensional
1747  array with a sequence of symmetric difference filters a (differentiation is applied
1748  to each dimension in turn, starting with the innermost dimension).
1749  The destination array is required to have a vector valued pixel type with as many
1750  elements as the number of dimensions. This function is implemented by calls to
1751  \ref convolveMultiArrayOneDimension() with the symmetric difference kernel.
1752 
1753  Anisotropic data should be provided with appropriate \ref vigra::ConvolutionOptions
1754  to adjust the filter sizes for the resolution of each axis.
1755  Otherwise, the parameter <tt>opt</tt> is optional unless the parameter
1756  <tt>sigma</tt> is omitted.
1757 
1758  If you pass \ref vigra::BlockwiseConvolutionOptions instead, the algorithm will
1759  be executed in parallel on data blocks of a certain size. The block size can be
1760  customized via <tt>BlockwiseConvolutionOptions::blockShape()</tt>, but the defaults
1761  usually work reasonably. By default, the number of threads equals the capabilities
1762  of your hardware, but you can change this via <tt>BlockwiseConvolutionOptions::numThreads()</tt>.
1763 
1764  <b> Declarations:</b>
1765 
1766  pass arbitrary-dimensional array views:
1767  \code
1768  namespace vigra {
1769  // execute algorithm sequentially
1770  template <unsigned int N, class T1, class S1,
1771  class T2, class S2>
1772  void
1773  symmetricGradientMultiArray(MultiArrayView<N, T1, S1> const & source,
1774  MultiArrayView<N, TinyVector<T2, N>, S2> dest,
1775  ConvolutionOptions<N> opt = ConvolutionOptions<N>());
1776 
1777  // execute algorithm in parallel
1778  template <unsigned int N, class T1, class S1,
1779  class T2, class S2>
1780  void
1781  symmetricGradientMultiArray(MultiArrayView<N, T1, S1> const & source,
1782  MultiArrayView<N, TinyVector<T2, N>, S2> dest,
1783  BlockwiseConvolutionOptions<N> opt);
1784  }
1785  \endcode
1786 
1787  \deprecatedAPI{symmetricGradientMultiArray}
1788  pass \ref MultiIteratorPage "MultiIterators" and \ref DataAccessors :
1789  \code
1790  namespace vigra {
1791  template <class SrcIterator, class SrcShape, class SrcAccessor,
1792  class DestIterator, class DestAccessor>
1793  void
1794  symmetricGradientMultiArray(SrcIterator siter, SrcShape const & shape, SrcAccessor src,
1795  DestIterator diter, DestAccessor dest,
1796  const ConvolutionOptions<N> & opt = ConvolutionOptions<N>());
1797  }
1798  \endcode
1799  use argument objects in conjunction with \ref ArgumentObjectFactories :
1800  \code
1801  namespace vigra {
1802  template <class SrcIterator, class SrcShape, class SrcAccessor,
1803  class DestIterator, class DestAccessor>
1804  void
1805  symmetricGradientMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
1806  pair<DestIterator, DestAccessor> const & dest,
1807  const ConvolutionOptions<N> & opt = ConvolutionOptions<N>());
1808  }
1809  \endcode
1810  \deprecatedEnd
1811 
1812  <b> Usage:</b>
1813 
1814  <b>\#include</b> <vigra/multi_convolution.hxx> (sequential version)<br/>
1815  <b>\#include</b> <vigra/multi_blockwise.hxx> (parallel version)<br/>
1816  Namespace: vigra
1817 
1818  \code
1819  MultiArray<3, unsigned char>::size_type shape(width, height, depth);
1820  MultiArray<3, unsigned char> source(shape);
1821  MultiArray<3, TinyVector<float, 3> > dest(shape);
1822  ...
1823  // compute gradient
1824  symmetricGradientMultiArray(srcMultiArrayRange(source), destMultiArray(dest));
1825  \endcode
1826 
1827  <b> Usage with anisotropic data:</b>
1828 
1829  \code
1830  Shape3 shape(width, height, depth);
1831  MultiArray<3, unsigned char> source(shape);
1832  MultiArray<3, TinyVector<float, 3> > dest(shape);
1833  TinyVector<float, 3> step_size;
1834  ...
1835  // compute gradient
1836  symmetricGradientMultiArray(source, dest,
1837  ConvolutionOptions<3>().stepSize(step_size));
1838  \endcode
1839 
1840  \see convolveMultiArrayOneDimension()
1841 */
1843 
1844 template <class SrcIterator, class SrcShape, class SrcAccessor,
1845  class DestIterator, class DestAccessor>
1846 void
1847 symmetricGradientMultiArray(SrcIterator si, SrcShape const & shape, SrcAccessor src,
1848  DestIterator di, DestAccessor dest,
1849  const ConvolutionOptions<SrcShape::static_size> & opt = ConvolutionOptions<SrcShape::static_size>())
1850 {
1851  typedef typename DestAccessor::value_type DestType;
1852  typedef typename DestType::value_type DestValueType;
1853  typedef typename NumericTraits<DestValueType>::RealPromote KernelType;
1854 
1855  static const int N = SrcShape::static_size;
1856  typedef typename ConvolutionOptions<N>::StepIterator StepType;
1857 
1858  for(int k=0; k<N; ++k)
1859  if(shape[k] <=0)
1860  return;
1861 
1862  vigra_precondition(N == (int)dest.size(di),
1863  "symmetricGradientMultiArray(): Wrong number of channels in output array.");
1864 
1865  Kernel1D<KernelType> filter;
1866  filter.initSymmetricDifference();
1867 
1868  StepType step_size_it = opt.stepParams();
1869 
1870  typedef VectorElementAccessor<DestAccessor> ElementAccessor;
1871 
1872  // compute gradient components
1873  for (int d = 0; d < N; ++d, ++step_size_it)
1874  {
1875  Kernel1D<KernelType> symmetric(filter);
1876  detail::scaleKernel(symmetric, 1 / *step_size_it);
1877  convolveMultiArrayOneDimension(si, shape, src,
1878  di, ElementAccessor(d, dest),
1879  d, symmetric, opt.from_point, opt.to_point);
1880  }
1881 }
1882 
1883 template <class SrcIterator, class SrcShape, class SrcAccessor,
1884  class DestIterator, class DestAccessor>
1885 inline void
1886 symmetricGradientMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
1887  pair<DestIterator, DestAccessor> const & dest,
1888  const ConvolutionOptions<SrcShape::static_size> & opt = ConvolutionOptions<SrcShape::static_size>())
1889 {
1890  symmetricGradientMultiArray(source.first, source.second, source.third,
1891  dest.first, dest.second, opt);
1892 }
1893 
1894 template <unsigned int N, class T1, class S1,
1895  class T2, class S2>
1896 inline void
1897 symmetricGradientMultiArray(MultiArrayView<N, T1, S1> const & source,
1898  MultiArrayView<N, TinyVector<T2, N>, S2> dest,
1899  ConvolutionOptions<N> opt = ConvolutionOptions<N>())
1900 {
1901  if(opt.to_point != typename MultiArrayShape<N>::type())
1902  {
1903  detail::RelativeToAbsoluteCoordinate<N-1>::exec(source.shape(), opt.from_point);
1904  detail::RelativeToAbsoluteCoordinate<N-1>::exec(source.shape(), opt.to_point);
1905  vigra_precondition(dest.shape() == (opt.to_point - opt.from_point),
1906  "symmetricGradientMultiArray(): shape mismatch between ROI and output.");
1907  }
1908  else
1909  {
1910  vigra_precondition(source.shape() == dest.shape(),
1911  "symmetricGradientMultiArray(): shape mismatch between input and output.");
1912  }
1913 
1914  symmetricGradientMultiArray(srcMultiArrayRange(source),
1915  destMultiArray(dest), opt);
1916 }
1917 
1918 /********************************************************/
1919 /* */
1920 /* laplacianOfGaussianMultiArray */
1921 /* */
1922 /********************************************************/
1923 
1924 /** \weakgroup ParallelProcessing
1925  \sa laplacianOfGaussianMultiArray <B>(...,</B> BlockwiseConvolutionOptions<B>)</B>
1926  */
1927 
1928 /** \brief Calculate Laplacian of a N-dimensional arrays using Gaussian derivative filters.
1929 
1930  This function computes the Laplacian of the given N-dimensional
1931  array with a sequence of second-derivative-of-Gaussian filters at the given
1932  standard deviation <tt>sigma</tt>. Both source and destination
1933  arrays must have scalar value_type. This function is implemented by calls to
1934  \ref separableConvolveMultiArray() with the appropriate kernels, followed by summation.
1935 
1936  Anisotropic data should be provided with appropriate \ref vigra::ConvolutionOptions
1937  to adjust the filter sizes for the resolution of each axis.
1938  Otherwise, the parameter <tt>opt</tt> is optional unless the parameter
1939  <tt>sigma</tt> is omitted.
1940 
1941  If you pass \ref vigra::BlockwiseConvolutionOptions instead, the algorithm will
1942  be executed in parallel on data blocks of a certain size. The block size can be
1943  customized via <tt>BlockwiseConvolutionOptions::blockShape()</tt>, but the defaults
1944  usually work reasonably. By default, the number of threads equals the capabilities
1945  of your hardware, but you can change this via <tt>BlockwiseConvolutionOptions::numThreads()</tt>.
1946 
1947  <b> Declarations:</b>
1948 
1949  pass arbitrary-dimensional array views:
1950  \code
1951  namespace vigra {
1952  // pass scale explicitly
1953  template <unsigned int N, class T1, class S1,
1954  class T2, class S2>
1955  void
1956  laplacianOfGaussianMultiArray(MultiArrayView<N, T1, S1> const & source,
1957  MultiArrayView<N, T2, S2> dest,
1958  double sigma,
1959  ConvolutionOptions<N> opt = ConvolutionOptions<N>());
1960 
1961  // pass scale(s) in option object
1962  template <unsigned int N, class T1, class S1,
1963  class T2, class S2>
1964  void
1965  laplacianOfGaussianMultiArray(MultiArrayView<N, T1, S1> const & source,
1966  MultiArrayView<N, T2, S2> dest,
1967  ConvolutionOptions<N> opt );
1968 
1969  // likewise, but execute algorithm in parallel
1970  template <unsigned int N, class T1, class S1,
1971  class T2, class S2>
1972  void
1973  laplacianOfGaussianMultiArray(MultiArrayView<N, T1, S1> const & source,
1974  MultiArrayView<N, T2, S2> dest,
1975  BlockwiseConvolutionOptions<N> opt );
1976  }
1977  \endcode
1978 
1979  \deprecatedAPI{laplacianOfGaussianMultiArray}
1980  pass \ref MultiIteratorPage "MultiIterators" and \ref DataAccessors :
1981  \code
1982  namespace vigra {
1983  template <class SrcIterator, class SrcShape, class SrcAccessor,
1984  class DestIterator, class DestAccessor>
1985  void
1986  laplacianOfGaussianMultiArray(SrcIterator siter, SrcShape const & shape, SrcAccessor src,
1987  DestIterator diter, DestAccessor dest,
1988  double sigma,
1989  const ConvolutionOptions<N> & opt = ConvolutionOptions<N>());
1990  }
1991  \endcode
1992  use argument objects in conjunction with \ref ArgumentObjectFactories :
1993  \code
1994  namespace vigra {
1995  template <class SrcIterator, class SrcShape, class SrcAccessor,
1996  class DestIterator, class DestAccessor>
1997  void
1998  laplacianOfGaussianMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
1999  pair<DestIterator, DestAccessor> const & dest,
2000  double sigma,
2001  const ConvolutionOptions<N> & opt = ConvolutionOptions<N>());
2002  }
2003  \endcode
2004  \deprecatedEnd
2005 
2006  <b> Usage:</b>
2007 
2008  <b>\#include</b> <vigra/multi_convolution.hxx> (sequential version)<br/>
2009  <b>\#include</b> <vigra/multi_blockwise.hxx> (parallel version)<br/>
2010  Namespace: vigra
2011 
2012  \code
2013  Shape3 shape(width, height, depth);
2014  MultiArray<3, float> source(shape);
2015  MultiArray<3, float> laplacian(shape);
2016  ...
2017  // compute Laplacian at scale sigma
2018  laplacianOfGaussianMultiArray(source, laplacian, sigma);
2019  \endcode
2020 
2021  <b> Usage with anisotropic data:</b>
2022 
2023  \code
2024  MultiArray<3, float> source(shape);
2025  MultiArray<3, float> laplacian(shape);
2026  TinyVector<float, 3> step_size;
2027  TinyVector<float, 3> resolution_sigmas;
2028  ...
2029  // compute Laplacian at scale sigma
2030  laplacianOfGaussianMultiArray(source, laplacian, sigma,
2031  ConvolutionOptions<3>().stepSize(step_size).resolutionStdDev(resolution_sigmas));
2032  \endcode
2033 
2034  \see separableConvolveMultiArray()
2035 */
2037 
2038 template <class SrcIterator, class SrcShape, class SrcAccessor,
2039  class DestIterator, class DestAccessor>
2040 void
2041 laplacianOfGaussianMultiArray(SrcIterator si, SrcShape const & shape, SrcAccessor src,
2042  DestIterator di, DestAccessor dest,
2043  ConvolutionOptions<SrcShape::static_size> const & opt )
2044 {
2045  using namespace functor;
2046 
2047  typedef typename DestAccessor::value_type DestType;
2048  typedef typename NumericTraits<DestType>::RealPromote KernelType;
2049  typedef typename AccessorTraits<KernelType>::default_accessor DerivativeAccessor;
2050 
2051  static const int N = SrcShape::static_size;
2052  typedef typename ConvolutionOptions<N>::ScaleIterator ParamType;
2053 
2054  ParamType params = opt.scaleParams();
2055  ParamType params2(params);
2056 
2057  ArrayVector<Kernel1D<KernelType> > plain_kernels(N);
2058  for (int dim = 0; dim < N; ++dim, ++params)
2059  {
2060  double sigma = params.sigma_scaled("laplacianOfGaussianMultiArray");
2061  plain_kernels[dim].initGaussian(sigma, 1.0, opt.window_ratio);
2062  }
2063 
2064  SrcShape dshape(shape);
2065  if(opt.to_point != SrcShape())
2066  dshape = opt.to_point - opt.from_point;
2067 
2068  MultiArray<N, KernelType> derivative(dshape);
2069 
2070  // compute 2nd derivatives and sum them up
2071  for (int dim = 0; dim < N; ++dim, ++params2)
2072  {
2073  ArrayVector<Kernel1D<KernelType> > kernels(plain_kernels);
2074  kernels[dim].initGaussianDerivative(params2.sigma_scaled(), 2, 1.0, opt.window_ratio);
2075  detail::scaleKernel(kernels[dim], 1.0 / sq(params2.step_size()));
2076 
2077  if (dim == 0)
2078  {
2079  separableConvolveMultiArray( si, shape, src,
2080  di, dest, kernels.begin(), opt.from_point, opt.to_point);
2081  }
2082  else
2083  {
2084  separableConvolveMultiArray( si, shape, src,
2085  derivative.traverser_begin(), DerivativeAccessor(),
2086  kernels.begin(), opt.from_point, opt.to_point);
2087  combineTwoMultiArrays(di, dshape, dest, derivative.traverser_begin(), DerivativeAccessor(),
2088  di, dest, Arg1() + Arg2() );
2089  }
2090  }
2091 }
2092 
2093 template <class SrcIterator, class SrcShape, class SrcAccessor,
2094  class DestIterator, class DestAccessor>
2095 void
2096 laplacianOfGaussianMultiArray(SrcIterator si, SrcShape const & shape, SrcAccessor src,
2097  DestIterator di, DestAccessor dest, double sigma,
2098  ConvolutionOptions<SrcShape::static_size> opt = ConvolutionOptions<SrcShape::static_size>())
2099 {
2100  laplacianOfGaussianMultiArray(si, shape, src, di, dest, opt.stdDev(sigma));
2101 }
2102 
2103 template <class SrcIterator, class SrcShape, class SrcAccessor,
2104  class DestIterator, class DestAccessor>
2105 inline void
2106 laplacianOfGaussianMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
2107  pair<DestIterator, DestAccessor> const & dest,
2108  ConvolutionOptions<SrcShape::static_size> const & opt )
2109 {
2110  laplacianOfGaussianMultiArray( source.first, source.second, source.third,
2111  dest.first, dest.second, opt );
2112 }
2113 
2114 template <class SrcIterator, class SrcShape, class SrcAccessor,
2115  class DestIterator, class DestAccessor>
2116 inline void
2117 laplacianOfGaussianMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
2118  pair<DestIterator, DestAccessor> const & dest,
2119  double sigma,
2120  const ConvolutionOptions<SrcShape::static_size> & opt = ConvolutionOptions<SrcShape::static_size>())
2121 {
2122  laplacianOfGaussianMultiArray( source.first, source.second, source.third,
2123  dest.first, dest.second, sigma, opt );
2124 }
2125 
2126 template <unsigned int N, class T1, class S1,
2127  class T2, class S2>
2128 inline void
2129 laplacianOfGaussianMultiArray(MultiArrayView<N, T1, S1> const & source,
2130  MultiArrayView<N, T2, S2> dest,
2131  ConvolutionOptions<N> opt )
2132 {
2133  if(opt.to_point != typename MultiArrayShape<N>::type())
2134  {
2135  detail::RelativeToAbsoluteCoordinate<N-1>::exec(source.shape(), opt.from_point);
2136  detail::RelativeToAbsoluteCoordinate<N-1>::exec(source.shape(), opt.to_point);
2137  vigra_precondition(dest.shape() == (opt.to_point - opt.from_point),
2138  "laplacianOfGaussianMultiArray(): shape mismatch between ROI and output.");
2139  }
2140  else
2141  {
2142  vigra_precondition(source.shape() == dest.shape(),
2143  "laplacianOfGaussianMultiArray(): shape mismatch between input and output.");
2144  }
2145 
2146  laplacianOfGaussianMultiArray( srcMultiArrayRange(source),
2147  destMultiArray(dest), opt );
2148 }
2149 
2150 template <unsigned int N, class T1, class S1,
2151  class T2, class S2>
2152 inline void
2153 laplacianOfGaussianMultiArray(MultiArrayView<N, T1, S1> const & source,
2154  MultiArrayView<N, T2, S2> dest,
2155  double sigma,
2156  ConvolutionOptions<N> opt = ConvolutionOptions<N>())
2157 {
2158  laplacianOfGaussianMultiArray( source, dest, opt.stdDev(sigma) );
2159 }
2160 
2161 /********************************************************/
2162 /* */
2163 /* gaussianDivergenceMultiArray */
2164 /* */
2165 /********************************************************/
2166 
2167 /** \weakgroup ParallelProcessing
2168  \sa gaussianDivergenceMultiArray <B>(...,</B> BlockwiseConvolutionOptions<B>)</B>
2169  */
2170 
2171 /** \brief Calculate the divergence of a vector field using Gaussian derivative filters.
2172 
2173  This function computes the divergence of the given N-dimensional vector field
2174  with a sequence of first-derivative-of-Gaussian filters at the given
2175  standard deviation <tt>sigma</tt>. The input vector field can either be given as a sequence
2176  of scalar array views (one for each vector field component), represented by an
2177  iterator range, or by a single vector array with the appropriate shape.
2178  This function is implemented by calls to
2179  \ref separableConvolveMultiArray() with the suitable kernels, followed by summation.
2180 
2181  Anisotropic data should be provided with appropriate \ref vigra::ConvolutionOptions
2182  to adjust the filter sizes for the resolution of each axis.
2183  Otherwise, the parameter <tt>opt</tt> is optional unless the parameter
2184  <tt>sigma</tt> is omitted.
2185 
2186  If you pass \ref vigra::BlockwiseConvolutionOptions instead, the algorithm will
2187  be executed in parallel on data blocks of a certain size. The block size can be
2188  customized via <tt>BlockwiseConvolutionOptions::blockShape()</tt>, but the defaults
2189  usually work reasonably. By default, the number of threads equals the capabilities
2190  of your hardware, but you can change this via <tt>BlockwiseConvolutionOptions::numThreads()</tt>.
2191 
2192  <b> Declarations:</b>
2193 
2194  pass arbitrary-dimensional array views:
2195  \code
2196  namespace vigra {
2197  // specify input vector field as a sequence of scalar arrays
2198  template <class Iterator,
2199  unsigned int N, class T, class S>
2200  void
2201  gaussianDivergenceMultiArray(Iterator vectorField, Iterator vectorFieldEnd,
2202  MultiArrayView<N, T, S> divergence,
2203  ConvolutionOptions<N> const & opt);
2204 
2205  template <class Iterator,
2206  unsigned int N, class T, class S>
2207  void
2208  gaussianDivergenceMultiArray(Iterator vectorField, Iterator vectorFieldEnd,
2209  MultiArrayView<N, T, S> divergence,
2210  double sigma,
2211  ConvolutionOptions<N> opt = ConvolutionOptions<N>());
2212 
2213  // pass input vector field as an array of vectors
2214  template <unsigned int N, class T1, class S1,
2215  class T2, class S2>
2216  void
2217  gaussianDivergenceMultiArray(MultiArrayView<N, TinyVector<T1, N>, S1> const & vectorField,
2218  MultiArrayView<N, T2, S2> divergence,
2219  ConvolutionOptions<N> const & opt);
2220 
2221  template <unsigned int N, class T1, class S1,
2222  class T2, class S2>
2223  void
2224  gaussianDivergenceMultiArray(MultiArrayView<N, TinyVector<T1, N>, S1> const & vectorField,
2225  MultiArrayView<N, T2, S2> divergence,
2226  double sigma,
2227  ConvolutionOptions<N> opt = ConvolutionOptions<N>());
2228 
2229  // pass input vector field as an array of vectors and
2230  // execute algorithm in parallel
2231  template <unsigned int N, class T1, class S1,
2232  class T2, class S2>
2233  void
2234  gaussianDivergenceMultiArray(MultiArrayView<N, TinyVector<T1, N>, S1> const & vectorField,
2235  MultiArrayView<N, T2, S2> divergence,
2236  BlockwiseConvolutionOptions<N> const & opt);
2237  }
2238  \endcode
2239 
2240  <b> Usage:</b>
2241 
2242  <b>\#include</b> <vigra/multi_convolution.hxx> (sequential version)<br/>
2243  <b>\#include</b> <vigra/multi_blockwise.hxx> (parallel version)<br/>
2244  Namespace: vigra
2245 
2246  \code
2247  Shape3 shape(width, height, depth);
2248  MultiArray<3, TinyVector<float, 3> > source(shape);
2249  MultiArray<3, float> laplacian(shape);
2250  ...
2251  // compute divergence at scale sigma
2252  gaussianDivergenceMultiArray(source, laplacian, sigma);
2253  \endcode
2254 
2255  <b> Usage with anisotropic data:</b>
2256 
2257  \code
2258  MultiArray<3, TinyVector<float, 3> > source(shape);
2259  MultiArray<3, float> laplacian(shape);
2260  TinyVector<float, 3> step_size;
2261  TinyVector<float, 3> resolution_sigmas;
2262  ...
2263  // compute divergence at scale sigma
2264  gaussianDivergenceMultiArray(source, laplacian, sigma,
2265  ConvolutionOptions<3>().stepSize(step_size).resolutionStdDev(resolution_sigmas));
2266  \endcode
2267 */
2269 
2270 template <class Iterator,
2271  unsigned int N, class T, class S>
2272 void
2273 gaussianDivergenceMultiArray(Iterator vectorField, Iterator vectorFieldEnd,
2274  MultiArrayView<N, T, S> divergence,
2275  ConvolutionOptions<N> opt)
2276 {
2277  typedef typename std::iterator_traits<Iterator>::value_type ArrayType;
2278  typedef typename ArrayType::value_type SrcType;
2279  typedef typename NumericTraits<SrcType>::RealPromote TmpType;
2280  typedef Kernel1D<double> Kernel;
2281 
2282  vigra_precondition(std::distance(vectorField, vectorFieldEnd) == N,
2283  "gaussianDivergenceMultiArray(): wrong number of input arrays.");
2284  // more checks are performed in separableConvolveMultiArray()
2285 
2286  typename ConvolutionOptions<N>::ScaleIterator params = opt.scaleParams();
2287  ArrayVector<double> sigmas(N);
2288  ArrayVector<Kernel> kernels(N);
2289  for(unsigned int k = 0; k < N; ++k, ++params)
2290  {
2291  sigmas[k] = params.sigma_scaled("gaussianDivergenceMultiArray");
2292  kernels[k].initGaussian(sigmas[k], 1.0, opt.window_ratio);
2293  }
2294 
2295  MultiArray<N, TmpType> tmpDeriv(divergence.shape());
2296 
2297  for(unsigned int k=0; k < N; ++k, ++vectorField)
2298  {
2299  kernels[k].initGaussianDerivative(sigmas[k], 1, 1.0, opt.window_ratio);
2300  if(k == 0)
2301  {
2302  separableConvolveMultiArray(*vectorField, divergence, kernels.begin(), opt.from_point, opt.to_point);
2303  }
2304  else
2305  {
2306  separableConvolveMultiArray(*vectorField, tmpDeriv, kernels.begin(), opt.from_point, opt.to_point);
2307  divergence += tmpDeriv;
2308  }
2309  kernels[k].initGaussian(sigmas[k], 1.0, opt.window_ratio);
2310  }
2311 }
2312 
2313 template <class Iterator,
2314  unsigned int N, class T, class S>
2315 inline void
2316 gaussianDivergenceMultiArray(Iterator vectorField, Iterator vectorFieldEnd,
2317  MultiArrayView<N, T, S> divergence,
2318  double sigma,
2319  ConvolutionOptions<N> opt = ConvolutionOptions<N>())
2320 {
2321  gaussianDivergenceMultiArray(vectorField, vectorFieldEnd, divergence, opt.stdDev(sigma));
2322 }
2323 
2324 template <unsigned int N, class T1, class S1,
2325  class T2, class S2>
2326 inline void
2327 gaussianDivergenceMultiArray(MultiArrayView<N, TinyVector<T1, N>, S1> const & vectorField,
2328  MultiArrayView<N, T2, S2> divergence,
2329  ConvolutionOptions<N> const & opt)
2330 {
2331  ArrayVector<MultiArrayView<N, T1> > field;
2332  for(unsigned int k=0; k<N; ++k)
2333  field.push_back(vectorField.bindElementChannel(k));
2334 
2335  gaussianDivergenceMultiArray(field.begin(), field.end(), divergence, opt);
2336 }
2337 
2338 template <unsigned int N, class T1, class S1,
2339  class T2, class S2>
2340 inline void
2341 gaussianDivergenceMultiArray(MultiArrayView<N, TinyVector<T1, N>, S1> const & vectorField,
2342  MultiArrayView<N, T2, S2> divergence,
2343  double sigma,
2344  ConvolutionOptions<N> opt = ConvolutionOptions<N>())
2345 {
2346  gaussianDivergenceMultiArray(vectorField, divergence, opt.stdDev(sigma));
2347 }
2348 
2349 /********************************************************/
2350 /* */
2351 /* hessianOfGaussianMultiArray */
2352 /* */
2353 /********************************************************/
2354 
2355 /** \weakgroup ParallelProcessing
2356  \sa hessianOfGaussianMultiArray <B>(...,</B> BlockwiseConvolutionOptions<B>)</B>
2357  */
2358 
2359 /** \brief Calculate Hessian matrix of a N-dimensional arrays using Gaussian derivative filters.
2360 
2361  This function computes the Hessian matrix the given scalar N-dimensional
2362  array with a sequence of second-derivative-of-Gaussian filters at the given
2363  standard deviation <tt>sigma</tt>. The destination array must
2364  have a vector valued element type with N*(N+1)/2 elements (it represents the
2365  upper triangular part of the symmetric Hessian matrix, flattened row-wise).
2366  This function is implemented by calls to
2367  \ref separableConvolveMultiArray() with the appropriate kernels.
2368 
2369  Anisotropic data should be provided with appropriate \ref vigra::ConvolutionOptions
2370  to adjust the filter sizes for the resolution of each axis.
2371  Otherwise, the parameter <tt>opt</tt> is optional unless the parameter
2372  <tt>sigma</tt> is omitted.
2373 
2374  If you pass \ref vigra::BlockwiseConvolutionOptions instead, the algorithm will
2375  be executed in parallel on data blocks of a certain size. The block size can be
2376  customized via <tt>BlockwiseConvolutionOptions::blockShape()</tt>, but the defaults
2377  usually work reasonably. By default, the number of threads equals the capabilities
2378  of your hardware, but you can change this via <tt>BlockwiseConvolutionOptions::numThreads()</tt>.
2379 
2380  <b> Declarations:</b>
2381 
2382  pass arbitrary-dimensional array views:
2383  \code
2384  namespace vigra {
2385  // pass scale explicitly
2386  template <unsigned int N, class T1, class S1,
2387  class T2, class S2>
2388  void
2389  hessianOfGaussianMultiArray(MultiArrayView<N, T1, S1> const & source,
2390  MultiArrayView<N, TinyVector<T2, int(N*(N+1)/2)>, S2> dest,
2391  double sigma,
2392  ConvolutionOptions<N> opt = ConvolutionOptions<N>());
2393 
2394  // pass scale(s) in option object
2395  template <unsigned int N, class T1, class S1,
2396  class T2, class S2>
2397  void
2398  hessianOfGaussianMultiArray(MultiArrayView<N, T1, S1> const & source,
2399  MultiArrayView<N, TinyVector<T2, int(N*(N+1)/2)>, S2> dest,
2400  ConvolutionOptions<N> opt);
2401 
2402  // likewise, but execute algorithm in parallel
2403  template <unsigned int N, class T1, class S1,
2404  class T2, class S2>
2405  void
2406  hessianOfGaussianMultiArray(MultiArrayView<N, T1, S1> const & source,
2407  MultiArrayView<N, TinyVector<T2, int(N*(N+1)/2)>, S2> dest,
2408  BlockwiseConvolutionOptions<N> opt);
2409  }
2410  \endcode
2411 
2412  \deprecatedAPI{hessianOfGaussianMultiArray}
2413  pass \ref MultiIteratorPage "MultiIterators" and \ref DataAccessors :
2414  \code
2415  namespace vigra {
2416  template <class SrcIterator, class SrcShape, class SrcAccessor,
2417  class DestIterator, class DestAccessor>
2418  void
2419  hessianOfGaussianMultiArray(SrcIterator siter, SrcShape const & shape, SrcAccessor src,
2420  DestIterator diter, DestAccessor dest,
2421  double sigma,
2422  const ConvolutionOptions<N> & opt = ConvolutionOptions<N>());
2423  }
2424  \endcode
2425  use argument objects in conjunction with \ref ArgumentObjectFactories :
2426  \code
2427  namespace vigra {
2428  template <class SrcIterator, class SrcShape, class SrcAccessor,
2429  class DestIterator, class DestAccessor>
2430  void
2431  hessianOfGaussianMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
2432  pair<DestIterator, DestAccessor> const & dest,
2433  double sigma,
2434  const ConvolutionOptions<N> & opt = ConvolutionOptions<N>());
2435  }
2436  \endcode
2437  \deprecatedEnd
2438 
2439  <b> Usage:</b>
2440 
2441  <b>\#include</b> <vigra/multi_convolution.hxx> (sequential version)<br/>
2442  <b>\#include</b> <vigra/multi_blockwise.hxx> (parallel version)<br/>
2443  Namespace: vigra
2444 
2445  \code
2446  Shape3 shape(width, height, depth);
2447  MultiArray<3, float> source(shape);
2448  MultiArray<3, TinyVector<float, 6> > dest(shape);
2449  ...
2450  // compute Hessian at scale sigma
2451  hessianOfGaussianMultiArray(source, dest, sigma);
2452  \endcode
2453 
2454  <b> Usage with anisotropic data:</b>
2455 
2456  \code
2457  MultiArray<3, float> source(shape);
2458  MultiArray<3, TinyVector<float, 6> > dest(shape);
2459  TinyVector<float, 3> step_size;
2460  TinyVector<float, 3> resolution_sigmas;
2461  ...
2462  // compute Hessian at scale sigma
2463  hessianOfGaussianMultiArray(source, dest, sigma,
2464  ConvolutionOptions<3>().stepSize(step_size).resolutionStdDev(resolution_sigmas));
2465  \endcode
2466 
2467  \see separableConvolveMultiArray(), vectorToTensorMultiArray()
2468 */
2470 
2471 template <class SrcIterator, class SrcShape, class SrcAccessor,
2472  class DestIterator, class DestAccessor>
2473 void
2474 hessianOfGaussianMultiArray(SrcIterator si, SrcShape const & shape, SrcAccessor src,
2475  DestIterator di, DestAccessor dest,
2476  ConvolutionOptions<SrcShape::static_size> const & opt )
2477 {
2478  typedef typename DestAccessor::value_type DestType;
2479  typedef typename DestType::value_type DestValueType;
2480  typedef typename NumericTraits<DestValueType>::RealPromote KernelType;
2481 
2482  static const int N = SrcShape::static_size;
2483  static const int M = N*(N+1)/2;
2484  typedef typename ConvolutionOptions<N>::ScaleIterator ParamType;
2485 
2486  for(int k=0; k<N; ++k)
2487  if(shape[k] <=0)
2488  return;
2489 
2490  vigra_precondition(M == (int)dest.size(di),
2491  "hessianOfGaussianMultiArray(): Wrong number of channels in output array.");
2492 
2493  ParamType params_init = opt.scaleParams();
2494 
2495  ArrayVector<Kernel1D<KernelType> > plain_kernels(N);
2496  ParamType params(params_init);
2497  for (int dim = 0; dim < N; ++dim, ++params)
2498  {
2499  double sigma = params.sigma_scaled("hessianOfGaussianMultiArray");
2500  plain_kernels[dim].initGaussian(sigma, 1.0, opt.window_ratio);
2501  }
2502 
2503  typedef VectorElementAccessor<DestAccessor> ElementAccessor;
2504 
2505  // compute elements of the Hessian matrix
2506  ParamType params_i(params_init);
2507  for (int b=0, i=0; i<N; ++i, ++params_i)
2508  {
2509  ParamType params_j(params_i);
2510  for (int j=i; j<N; ++j, ++b, ++params_j)
2511  {
2512  ArrayVector<Kernel1D<KernelType> > kernels(plain_kernels);
2513  if(i == j)
2514  {
2515  kernels[i].initGaussianDerivative(params_i.sigma_scaled(), 2, 1.0, opt.window_ratio);
2516  }
2517  else
2518  {
2519  kernels[i].initGaussianDerivative(params_i.sigma_scaled(), 1, 1.0, opt.window_ratio);
2520  kernels[j].initGaussianDerivative(params_j.sigma_scaled(), 1, 1.0, opt.window_ratio);
2521  }
2522  detail::scaleKernel(kernels[i], 1 / params_i.step_size());
2523  detail::scaleKernel(kernels[j], 1 / params_j.step_size());
2524  separableConvolveMultiArray(si, shape, src, di, ElementAccessor(b, dest),
2525  kernels.begin(), opt.from_point, opt.to_point);
2526  }
2527  }
2528 }
2529 
2530 template <class SrcIterator, class SrcShape, class SrcAccessor,
2531  class DestIterator, class DestAccessor>
2532 inline void
2533 hessianOfGaussianMultiArray(SrcIterator si, SrcShape const & shape, SrcAccessor src,
2534  DestIterator di, DestAccessor dest, double sigma,
2535  ConvolutionOptions<SrcShape::static_size> opt = ConvolutionOptions<SrcShape::static_size>())
2536 {
2537  hessianOfGaussianMultiArray(si, shape, src, di, dest, opt.stdDev(sigma));
2538 }
2539 
2540 template <class SrcIterator, class SrcShape, class SrcAccessor,
2541  class DestIterator, class DestAccessor>
2542 inline void
2543 hessianOfGaussianMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
2544  pair<DestIterator, DestAccessor> const & dest,
2545  ConvolutionOptions<SrcShape::static_size> const & opt )
2546 {
2547  hessianOfGaussianMultiArray( source.first, source.second, source.third,
2548  dest.first, dest.second, opt );
2549 }
2550 
2551 template <class SrcIterator, class SrcShape, class SrcAccessor,
2552  class DestIterator, class DestAccessor>
2553 inline void
2554 hessianOfGaussianMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
2555  pair<DestIterator, DestAccessor> const & dest,
2556  double sigma,
2557  const ConvolutionOptions<SrcShape::static_size> & opt = ConvolutionOptions<SrcShape::static_size>())
2558 {
2559  hessianOfGaussianMultiArray( source.first, source.second, source.third,
2560  dest.first, dest.second, sigma, opt );
2561 }
2562 
2563 template <unsigned int N, class T1, class S1,
2564  class T2, class S2>
2565 inline void
2566 hessianOfGaussianMultiArray(MultiArrayView<N, T1, S1> const & source,
2567  MultiArrayView<N, TinyVector<T2, int(N*(N+1)/2)>, S2> dest,
2568  ConvolutionOptions<N> opt )
2569 {
2570  if(opt.to_point != typename MultiArrayShape<N>::type())
2571  {
2572  detail::RelativeToAbsoluteCoordinate<N-1>::exec(source.shape(), opt.from_point);
2573  detail::RelativeToAbsoluteCoordinate<N-1>::exec(source.shape(), opt.to_point);
2574  vigra_precondition(dest.shape() == (opt.to_point - opt.from_point),
2575  "hessianOfGaussianMultiArray(): shape mismatch between ROI and output.");
2576  }
2577  else
2578  {
2579  vigra_precondition(source.shape() == dest.shape(),
2580  "hessianOfGaussianMultiArray(): shape mismatch between input and output.");
2581  }
2582 
2583  hessianOfGaussianMultiArray( srcMultiArrayRange(source),
2584  destMultiArray(dest), opt );
2585 }
2586 
2587 template <unsigned int N, class T1, class S1,
2588  class T2, class S2>
2589 inline void
2590 hessianOfGaussianMultiArray(MultiArrayView<N, T1, S1> const & source,
2591  MultiArrayView<N, TinyVector<T2, int(N*(N+1)/2)>, S2> dest,
2592  double sigma,
2593  ConvolutionOptions<N> opt = ConvolutionOptions<N>())
2594 {
2595  hessianOfGaussianMultiArray( source, dest, opt.stdDev(sigma) );
2596 }
2597 
2598 namespace detail {
2599 
2600 template<int N, class VectorType>
2601 struct StructurTensorFunctor
2602 {
2603  typedef VectorType result_type;
2604  typedef typename VectorType::value_type ValueType;
2605 
2606  template <class T>
2607  VectorType operator()(T const & in) const
2608  {
2609  VectorType res;
2610  for(int b=0, i=0; i<N; ++i)
2611  {
2612  for(int j=i; j<N; ++j, ++b)
2613  {
2614  res[b] = detail::RequiresExplicitCast<ValueType>::cast(in[i]*in[j]);
2615  }
2616  }
2617  return res;
2618  }
2619 };
2620 
2621 } // namespace detail
2622 
2623 /********************************************************/
2624 /* */
2625 /* structureTensorMultiArray */
2626 /* */
2627 /********************************************************/
2628 
2629 /** \weakgroup ParallelProcessing
2630  \sa structureTensorMultiArray <B>(...,</B> BlockwiseConvolutionOptions<B>)</B>
2631  */
2632 
2633 /** \brief Calculate the structure tensor of a multi-dimensional arrays.
2634 
2635  This function computes the gradient (outer product) tensor for each element
2636  of the given N-dimensional array with first-derivative-of-Gaussian filters at
2637  the given <tt>innerScale</tt>, followed by Gaussian smoothing at <tt>outerScale</tt>.
2638  The destination array must have a vector valued pixel type with
2639  N*(N+1)/2 elements (it represents the upper triangular part of the symmetric
2640  structure tensor matrix, flattened row-wise). If the source array is also vector valued, the
2641  resulting structure tensor is the sum of the individual tensors for each channel.
2642  This function is implemented by calls to
2643  \ref separableConvolveMultiArray() with the appropriate kernels.
2644 
2645  Anisotropic data should be provided with appropriate \ref vigra::ConvolutionOptions
2646  to adjust the filter sizes for the resolution of each axis.
2647  Otherwise, the parameter <tt>opt</tt> is optional unless the parameters
2648  <tt>innerScale</tt> and <tt>outerScale</tt> are both omitted.
2649 
2650  If you pass \ref vigra::BlockwiseConvolutionOptions instead, the algorithm will
2651  be executed in parallel on data blocks of a certain size. The block size can be
2652  customized via <tt>BlockwiseConvolutionOptions::blockShape()</tt>, but the defaults
2653  usually work reasonably. By default, the number of threads equals the capabilities
2654  of your hardware, but you can change this via <tt>BlockwiseConvolutionOptions::numThreads()</tt>.
2655 
2656  <b> Declarations:</b>
2657 
2658  pass arbitrary-dimensional array views:
2659  \code
2660  namespace vigra {
2661  // pass scales explicitly
2662  template <unsigned int N, class T1, class S1,
2663  class T2, class S2>
2664  void
2665  structureTensorMultiArray(MultiArrayView<N, T1, S1> const & source,
2666  MultiArrayView<N, TinyVector<T2, int(N*(N+1)/2)>, S2> dest,
2667  double innerScale, double outerScale,
2668  ConvolutionOptions<N> opt = ConvolutionOptions<N>());
2669 
2670  // pass scales in option object
2671  template <unsigned int N, class T1, class S1,
2672  class T2, class S2>
2673  void
2674  structureTensorMultiArray(MultiArrayView<N, T1, S1> const & source,
2675  MultiArrayView<N, TinyVector<T2, int(N*(N+1)/2)>, S2> dest,
2676  ConvolutionOptions<N> opt );
2677 
2678  // likewise, but execute algorithm in parallel
2679  template <unsigned int N, class T1, class S1,
2680  class T2, class S2>
2681  void
2682  structureTensorMultiArray(MultiArrayView<N, T1, S1> const & source,
2683  MultiArrayView<N, TinyVector<T2, int(N*(N+1)/2)>, S2> dest,
2684  BlockwiseConvolutionOptions<N> opt );
2685  }
2686  \endcode
2687 
2688  \deprecatedAPI{structureTensorMultiArray}
2689  pass \ref MultiIteratorPage "MultiIterators" and \ref DataAccessors :
2690  \code
2691  namespace vigra {
2692  template <class SrcIterator, class SrcShape, class SrcAccessor,
2693  class DestIterator, class DestAccessor>
2694  void
2695  structureTensorMultiArray(SrcIterator siter, SrcShape const & shape, SrcAccessor src,
2696  DestIterator diter, DestAccessor dest,
2697  double innerScale, double outerScale,
2698  ConvolutionOptions<N> opt);
2699  }
2700  \endcode
2701  use argument objects in conjunction with \ref ArgumentObjectFactories :
2702  \code
2703  namespace vigra {
2704  template <class SrcIterator, class SrcShape, class SrcAccessor,
2705  class DestIterator, class DestAccessor>
2706  void
2707  structureTensorMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
2708  pair<DestIterator, DestAccessor> const & dest,
2709  double innerScale, double outerScale,
2710  const ConvolutionOptions<N> & opt);
2711  }
2712  \endcode
2713  \deprecatedEnd
2714 
2715  <b> Usage:</b>
2716 
2717  <b>\#include</b> <vigra/multi_convolution.hxx> (sequential version)<br/>
2718  <b>\#include</b> <vigra/multi_blockwise.hxx> (parallel version)<br/>
2719  Namespace: vigra
2720 
2721  \code
2722  Shape3 shape(width, height, depth);
2723  MultiArray<3, RGBValue<float> > source(shape);
2724  MultiArray<3, TinyVector<float, 6> > dest(shape);
2725  ...
2726  // compute structure tensor at scales innerScale and outerScale
2727  structureTensorMultiArray(source, dest, innerScale, outerScale);
2728  \endcode
2729 
2730  <b> Usage with anisotropic data:</b>
2731 
2732  \code
2733  MultiArray<3, RGBValue<float> > source(shape);
2734  MultiArray<3, TinyVector<float, 6> > dest(shape);
2735  TinyVector<float, 3> step_size;
2736  TinyVector<float, 3> resolution_sigmas;
2737  ...
2738  // compute structure tensor at scales innerScale and outerScale
2739  structureTensorMultiArray(source, dest, innerScale, outerScale,
2740  ConvolutionOptions<3>().stepSize(step_size).resolutionStdDev(resolution_sigmas));
2741  \endcode
2742 
2743  \see separableConvolveMultiArray(), vectorToTensorMultiArray()
2744 */
2746 
2747 template <class SrcIterator, class SrcShape, class SrcAccessor,
2748  class DestIterator, class DestAccessor>
2749 void
2750 structureTensorMultiArray(SrcIterator si, SrcShape const & shape, SrcAccessor src,
2751  DestIterator di, DestAccessor dest,
2752  ConvolutionOptions<SrcShape::static_size> opt)
2753 {
2754  static const int N = SrcShape::static_size;
2755  static const int M = N*(N+1)/2;
2756 
2757  typedef typename DestAccessor::value_type DestType;
2758  typedef typename DestType::value_type DestValueType;
2759  typedef typename NumericTraits<DestValueType>::RealPromote KernelType;
2760  typedef TinyVector<KernelType, N> GradientVector;
2761  typedef typename AccessorTraits<GradientVector>::default_accessor GradientAccessor;
2762  typedef typename AccessorTraits<DestType>::default_accessor GradientTensorAccessor;
2763 
2764  for(int k=0; k<N; ++k)
2765  if(shape[k] <=0)
2766  return;
2767 
2768  vigra_precondition(M == (int)dest.size(di),
2769  "structureTensorMultiArray(): Wrong number of channels in output array.");
2770 
2771  ConvolutionOptions<N> innerOptions = opt;
2772  ConvolutionOptions<N> outerOptions = opt.outerOptions();
2773  typename ConvolutionOptions<N>::ScaleIterator params = outerOptions.scaleParams();
2774 
2775  SrcShape gradientShape(shape);
2776  if(opt.to_point != SrcShape())
2777  {
2778  detail::RelativeToAbsoluteCoordinate<N-1>::exec(shape, opt.from_point);
2779  detail::RelativeToAbsoluteCoordinate<N-1>::exec(shape, opt.to_point);
2780 
2781  for(int k=0; k<N; ++k, ++params)
2782  {
2783  Kernel1D<double> gauss;
2784  gauss.initGaussian(params.sigma_scaled("structureTensorMultiArray"), 1.0, opt.window_ratio);
2785  int dilation = gauss.right();
2786  innerOptions.from_point[k] = std::max<MultiArrayIndex>(0, opt.from_point[k] - dilation);
2787  innerOptions.to_point[k] = std::min<MultiArrayIndex>(shape[k], opt.to_point[k] + dilation);
2788  }
2789  outerOptions.from_point -= innerOptions.from_point;
2790  outerOptions.to_point -= innerOptions.from_point;
2791  gradientShape = innerOptions.to_point - innerOptions.from_point;
2792  }
2793 
2794  MultiArray<N, GradientVector> gradient(gradientShape);
2795  MultiArray<N, DestType> gradientTensor(gradientShape);
2796  gaussianGradientMultiArray(si, shape, src,
2797  gradient.traverser_begin(), GradientAccessor(),
2798  innerOptions,
2799  "structureTensorMultiArray");
2800 
2801  transformMultiArray(gradient.traverser_begin(), gradientShape, GradientAccessor(),
2802  gradientTensor.traverser_begin(), GradientTensorAccessor(),
2803  detail::StructurTensorFunctor<N, DestType>());
2804 
2805  gaussianSmoothMultiArray(gradientTensor.traverser_begin(), gradientShape, GradientTensorAccessor(),
2806  di, dest, outerOptions,
2807  "structureTensorMultiArray");
2808 }
2809 
2810 template <class SrcIterator, class SrcShape, class SrcAccessor,
2811  class DestIterator, class DestAccessor>
2812 inline void
2813 structureTensorMultiArray(SrcIterator si, SrcShape const & shape, SrcAccessor src,
2814  DestIterator di, DestAccessor dest,
2815  double innerScale, double outerScale,
2816  ConvolutionOptions<SrcShape::static_size> opt = ConvolutionOptions<SrcShape::static_size>())
2817 {
2818  structureTensorMultiArray(si, shape, src, di, dest,
2819  opt.stdDev(innerScale).outerScale(outerScale));
2820 }
2821 
2822 template <class SrcIterator, class SrcShape, class SrcAccessor,
2823  class DestIterator, class DestAccessor>
2824 inline void
2825 structureTensorMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
2826  pair<DestIterator, DestAccessor> const & dest,
2827  ConvolutionOptions<SrcShape::static_size> const & opt )
2828 {
2829  structureTensorMultiArray( source.first, source.second, source.third,
2830  dest.first, dest.second, opt );
2831 }
2832 
2833 
2834 template <class SrcIterator, class SrcShape, class SrcAccessor,
2835  class DestIterator, class DestAccessor>
2836 inline void
2837 structureTensorMultiArray(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
2838  pair<DestIterator, DestAccessor> const & dest,
2839  double innerScale, double outerScale,
2840  const ConvolutionOptions<SrcShape::static_size> & opt = ConvolutionOptions<SrcShape::static_size>())
2841 {
2842  structureTensorMultiArray( source.first, source.second, source.third,
2843  dest.first, dest.second,
2844  innerScale, outerScale, opt);
2845 }
2846 
2847 template <unsigned int N, class T1, class S1,
2848  class T2, class S2>
2849 inline void
2850 structureTensorMultiArray(MultiArrayView<N, T1, S1> const & source,
2851  MultiArrayView<N, TinyVector<T2, int(N*(N+1)/2)>, S2> dest,
2852  ConvolutionOptions<N> opt )
2853 {
2854  if(opt.to_point != typename MultiArrayShape<N>::type())
2855  {
2856  detail::RelativeToAbsoluteCoordinate<N-1>::exec(source.shape(), opt.from_point);
2857  detail::RelativeToAbsoluteCoordinate<N-1>::exec(source.shape(), opt.to_point);
2858  vigra_precondition(dest.shape() == (opt.to_point - opt.from_point),
2859  "structureTensorMultiArray(): shape mismatch between ROI and output.");
2860  }
2861  else
2862  {
2863  vigra_precondition(source.shape() == dest.shape(),
2864  "structureTensorMultiArray(): shape mismatch between input and output.");
2865  }
2866 
2867  structureTensorMultiArray( srcMultiArrayRange(source),
2868  destMultiArray(dest), opt );
2869 }
2870 
2871 
2872 template <unsigned int N, class T1, class S1,
2873  class T2, class S2>
2874 inline void
2875 structureTensorMultiArray(MultiArrayView<N, T1, S1> const & source,
2876  MultiArrayView<N, TinyVector<T2, int(N*(N+1)/2)>, S2> dest,
2877  double innerScale, double outerScale,
2878  ConvolutionOptions<N> opt = ConvolutionOptions<N>())
2879 {
2880  structureTensorMultiArray(source, dest, opt.innerScale(innerScale).outerScale(outerScale));
2881 }
2882 
2883 //@}
2884 
2885 } //-- namespace vigra
2886 
2887 
2888 #endif //-- VIGRA_MULTI_CONVOLUTION_H
ConvolutionOptions< dim > & outerScale(...)
Generic 1 dimensional convolution kernel.
Definition: separableconvolution.hxx:52
void indexSort(Iterator first, Iterator last, IndexIterator index_first, Compare c)
Return the index permutation that would sort the input array.
Definition: algorithm.hxx:414
void symmetricGradientMultiArray(...)
Calculate gradient of a multi-dimensional arrays using symmetric difference filters.
void gaussianDivergenceMultiArray(...)
Calculate the divergence of a vector field using Gaussian derivative filters.
FFTWComplex< R >::SquaredNormType squaredNorm(const FFTWComplex< R > &a)
squared norm (= squared magnitude)
Definition: fftw3.hxx:1044
void separableConvolveMultiArray(...)
Separated convolution on multi-dimensional arrays.
ConvolutionOptions< dim > & subarray(Shape const &from, Shape const &to)
Definition: multi_convolution.hxx:498
void convolveLine(...)
Performs a 1-dimensional convolution of the source signal using the given kernel. ...
void gaussianGradientMultiArray(...)
Calculate Gaussian gradient of a multi-dimensional arrays.
ConvolutionOptions< dim > & stepSize(...)
Definition: multi_fwd.hxx:63
void laplacianOfGaussianMultiArray(...)
Calculate Laplacian of a N-dimensional arrays using Gaussian derivative filters.
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
ConvolutionOptions< dim > & filterWindowSize(double ratio)
Definition: multi_convolution.hxx:476
void gaussianGradientMagnitude(...)
Calculate the gradient magnitude by means of a 1st derivatives of Gaussian filter.
doxygen_overloaded_function(template<...> void separableConvolveBlockwise) template< unsigned int N
Separated convolution on ChunkedArrays.
Options class template for convolutions.
Definition: multi_convolution.hxx:335
void copyMultiArray(...)
Copy a multi-dimensional array.
void outer(const MultiArrayView< 2, T, C1 > &x, const MultiArrayView< 2, T, C2 > &y, MultiArrayView< 2, T, C3 > &r)
Definition: matrix.hxx:1459
void combineTwoMultiArrays(...)
Combine two multi-dimensional arrays into one using a binary function or functor. ...
void transformMultiArray(...)
Transform a multi-dimensional array with a unary function or functor.
ConvolutionOptions< dim > & innerScale(...)
void convolveMultiArrayOneDimension(...)
Convolution along a single dimension of a multi-dimensional arrays.
ConvolutionOptions< dim > & resolutionStdDev(...)
void gaussianSmoothMultiArray(...)
Isotropic Gaussian smoothing of a multi-dimensional arrays.
void hessianOfGaussianMultiArray(...)
Calculate Hessian matrix of a N-dimensional arrays using Gaussian derivative filters.
SquareRootTraits< FixedPoint< IntBits, FracBits > >::SquareRootResult sqrt(FixedPoint< IntBits, FracBits > v)
square root.
Definition: fixedpoint.hxx:616
ConvolutionOptions< dim > & stdDev(...)
void structureTensorMultiArray(...)
Calculate the structure tensor of a multi-dimensional arrays.

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