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

multi_convolution.hxx |

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>
335 class ConvolutionOptions
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
349 ConvolutionOptions()
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 */
394 ConvolutionOptions<dim> & stepSize(...);
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 */
410 ConvolutionOptions<dim> & resolutionStdDev(...);
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 */
429 ConvolutionOptions<dim> & stdDev(...);
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 */
441 ConvolutionOptions<dim> & innerScale(...);
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 */
459 ConvolutionOptions<dim> & outerScale(...);
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 */
476 ConvolutionOptions<dim> & filterWindowSize(double ratio)
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 */
860 doxygen_overloaded_function(template <...> void separableConvolveMultiArray)
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 */
1071 doxygen_overloaded_function(template <...> void convolveMultiArrayOneDimension)
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 */
1297 doxygen_overloaded_function(template <...> void gaussianSmoothMultiArray)
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, int(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, int(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, int(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 */
1509 doxygen_overloaded_function(template <...> void gaussianGradientMultiArray)
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, int(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, int(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, int(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, int(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, int(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 */
1842 doxygen_overloaded_function(template <...> void symmetricGradientMultiArray)
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, int(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 */
2036 doxygen_overloaded_function(template <...> void laplacianOfGaussianMultiArray)
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, int(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, int(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, int(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 */
2268 doxygen_overloaded_function(template <...> void gaussianDivergenceMultiArray)
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, int(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, int(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 */
2469 doxygen_overloaded_function(template <...> void hessianOfGaussianMultiArray)
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 */
2745 doxygen_overloaded_function(template <...> void structureTensorMultiArray)
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
vigra::sq
NumericTraits< T >::Promote sq(T t)
The square function.
Definition: mathutil.hxx:382
vigra::separableConvolveMultiArray
void separableConvolveMultiArray(...)
Separated convolution on multi-dimensional arrays.
vigra::ConvolutionOptions::stepSize
ConvolutionOptions< dim > & stepSize(...)
vigra::combineTwoMultiArrays
void combineTwoMultiArrays(...)
Combine two multi-dimensional arrays into one using a binary function or functor.
vigra::gaussianGradientMagnitude
void gaussianGradientMagnitude(...)
Calculate the gradient magnitude by means of a 1st derivatives of Gaussian filter.
vigra::ConvolutionOptions::outerScale
ConvolutionOptions< dim > & outerScale(...)
vigra
vigra::doxygen_overloaded_function
doxygen_overloaded_function(template<... > void separableConvolveBlockwise) template< unsigned int N
Separated convolution on ChunkedArrays.
vigra::sqrt
SquareRootTraits< FixedPoint< IntBits, FracBits > >::SquareRootResult sqrt(FixedPoint< IntBits, FracBits > v)
square root.
Definition: fixedpoint.hxx:616
vigra::ConvolutionOptions::innerScale
ConvolutionOptions< dim > & innerScale(...)
vigra::structureTensorMultiArray
void structureTensorMultiArray(...)
Calculate the structure tensor of a multi-dimensional arrays.
vigra::ConvolutionOptions::filterWindowSize
ConvolutionOptions< dim > & filterWindowSize(double ratio)
Definition: multi_convolution.hxx:476
vigra::gaussianDivergenceMultiArray
void gaussianDivergenceMultiArray(...)
Calculate the divergence of a vector field using Gaussian derivative filters.
vigra::squaredNorm
FFTWComplex< R >::SquaredNormType squaredNorm(const FFTWComplex< R > &a)
squared norm (= squared magnitude)
Definition: fftw3.hxx:1044
vigra::ConvolutionOptions::stdDev
ConvolutionOptions< dim > & stdDev(...)
vigra::gaussianGradientMultiArray
void gaussianGradientMultiArray(...)
Calculate Gaussian gradient of a multi-dimensional arrays.
vigra::ConvolutionOptions
Options class template for convolutions.
Definition: multi_convolution.hxx:335
vigra::hessianOfGaussianMultiArray
void hessianOfGaussianMultiArray(...)
Calculate Hessian matrix of a N-dimensional arrays using Gaussian derivative filters.
vigra::Kernel1D
Generic 1 dimensional convolution kernel.
Definition: separableconvolution.hxx:52
vigra::copyMultiArray
void copyMultiArray(...)
Copy a multi-dimensional array.
vigra::indexSort
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
vigra::laplacianOfGaussianMultiArray
void laplacianOfGaussianMultiArray(...)
Calculate Laplacian of a N-dimensional arrays using Gaussian derivative filters.
vigra::transformMultiArray
void transformMultiArray(...)
Transform a multi-dimensional array with a unary function or functor.
vigra::ConvolutionOptions::resolutionStdDev
ConvolutionOptions< dim > & resolutionStdDev(...)
vigra::ConvolutionOptions::subarray
ConvolutionOptions< dim > & subarray(Shape const &from, Shape const &to)
Definition: multi_convolution.hxx:498
vigra::convolveLine
void convolveLine(...)
Performs a 1-dimensional convolution of the source signal using the given kernel.
vigra::convolveMultiArrayOneDimension
void convolveMultiArrayOneDimension(...)
Convolution along a single dimension of a multi-dimensional arrays.
vigra::MultiArrayShape
Definition: multi_fwd.hxx:63
vigra::symmetricGradientMultiArray
void symmetricGradientMultiArray(...)
Calculate gradient of a multi-dimensional arrays using symmetric difference filters.
vigra::gaussianSmoothMultiArray
void gaussianSmoothMultiArray(...)
Isotropic Gaussian smoothing of a multi-dimensional arrays.
vigra::MultiArrayShape::type
TinyVector< MultiArrayIndex, N > type
Definition: multi_shape.hxx:272

© 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