/************************************************************************/ /* */ /* Copyright 1998-2002 by Ullrich Koethe */ /* Cognitive Systems Group, University of Hamburg, Germany */ /* */ /* This file is part of the VIGRA computer vision library. */ /* The VIGRA Website is */ /* http://kogs-www.informatik.uni-hamburg.de/~koethe/vigra/ */ /* Please direct questions, bug reports, and contributions to */ /* koethe@informatik.uni-hamburg.de or */ /* vigra@kogs1.informatik.uni-hamburg.de */ /* */ /* Permission is hereby granted, free of charge, to any person */ /* obtaining a copy of this software and associated documentation */ /* files (the "Software"), to deal in the Software without */ /* restriction, including without limitation the rights to use, */ /* copy, modify, merge, publish, distribute, sublicense, and/or */ /* sell copies of the Software, and to permit persons to whom the */ /* Software is furnished to do so, subject to the following */ /* conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the */ /* Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */ /* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */ /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */ /* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */ /* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */ /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */ /* OTHER DEALINGS IN THE SOFTWARE. */ /* */ /************************************************************************/ #ifndef VIGRA_CONVOLUTION_HXX #define VIGRA_CONVOLUTION_HXX #include #include "vigra/stdconvolution.hxx" #include "vigra/separableconvolution.hxx" #include "vigra/recursiveconvolution.hxx" #include "vigra/nonlineardiffusion.hxx" #include "vigra/combineimages.hxx" /** \page Convolution Functions to Convolve Images and Signals 1D and 2D filters, including separable and recursive convolution, and non-linear diffusion \#include "vigra/convolution.hxx"
Namespace: vigra
- \ref CommonConvolutionFilters
Short-hands for the most common 2D convolution filters
- \ref MultiArrayConvolutionFilters
Convolution filters for arbitrary dimensional arrays (MultiArray etc.)
- \ref ResamplingConvolutionFilters
Resampling convolution filters
- \ref StandardConvolution
2D non-separable convolution, with and without ROI mask
- \ref vigra::Kernel2D
Generic 2-dimensional discrete convolution kernel
- \ref SeparableConvolution
1D convolution and separable filters in 2 dimensions
- \ref vigra::Kernel1D
Generic 1-dimensional discrete convolution kernel
- \ref RecursiveConvolution
Recursive filters (1st and 2nd order)
- \ref NonLinearDiffusion
Edge-preserving smoothing
- \ref BorderTreatmentMode
Choose between different border treatment modes
- \ref KernelArgumentObjectFactories
Factory functions to create argument objects to simplify passing kernels
*/ /** \page KernelArgumentObjectFactories Kernel Argument Object Factories These factory functions allow to create argument objects for 1D and 2D convolution kernel analogously to \ref ArgumentObjectFactories for images. \section Kernel1dFactory kernel1d() Pass a \ref vigra::Kernel1D to a 1D or separable convolution algorithm. These factories can be used to create argument objects when we are given instances or subclasses of \ref vigra::Kernel1D (analogous to the \ref ArgumentObjectFactories for images). These factory functions access kernel.center(), kernel.left(), kernel.right(), kernel.accessor(), and kernel.borderTreatment() to obtain the necessary information. The following factory functions are provided: \endhtmlonly
\htmlonly \endhtmlonly \ref vigra::Kernel1D "vigra::Kernel1D" kernel; \htmlonly
kernel1d(kernel) create argument object from information provided by kernel
kernel1d(kernel, vigra::BORDER_TREATMENT_CLIP) create argument object from information provided by kernel, but use given border treatment mode
kernel1d(kerneliterator, kernelaccessor,
kernelleft, kernelright,
vigra::BORDER_TREATMENT_CLIP)
create argument object from explicitly given iterator (pointing to the center of th kernel), accessor, left and right boundaries, and border treatment mode
For usage examples see \ref SeparableConvolution "one-dimensional and separable convolution functions". \section Kernel2dFactory kernel2d() Pass a \ref vigra::Kernel2D to a 2D (non-separable) convolution algorithm. These factories can be used to create argument objects when we are given instances or subclasses of \ref vigra::Kernel2D (analogous to the \ref ArgumentObjectFactories for images). These factory functions access kernel.center(), kernel.upperLeft(), kernel.lowerRight(), kernel.accessor(), and kernel.borderTreatment() to obtain the necessary information. The following factory functions are provided: \endhtmlonly
\htmlonly \endhtmlonly \ref vigra::Kernel2D "vigra::Kernel2D" kernel; \htmlonly
kernel2d(kernel) create argument object from information provided by kernel
kernel2d(kernel, vigra::BORDER_TREATMENT_CLIP) create argument object from information provided by kernel, but use given border treatment mode
kernel2d(kerneliterator, kernelaccessor, upperleft, lowerright, vigra::BORDER_TREATMENT_CLIP) create argument object from explicitly given iterator (pointing to the center of th kernel), accessor, upper left and lower right corners, and border treatment mode
For usage examples see \ref StandardConvolution "two-dimensional convolution functions". */ namespace vigra { /********************************************************/ /* */ /* Common convolution filters */ /* */ /********************************************************/ /** \addtogroup CommonConvolutionFilters Common Filters These functions calculate common filters by appropriate sequences of calls to \link SeparableConvolution#separableConvolveX separableConvolveX\endlink() and \link SeparableConvolution#separableConvolveY separableConvolveY\endlink(). */ //@{ /********************************************************/ /* */ /* convolveImage */ /* */ /********************************************************/ /** \brief Apply two separable filters successively, the first in x-direction, the second in y-direction. This function is a shorthand for the concatenation of a call to \link SeparableConvolution#separableConvolveX separableConvolveX\endlink() and \link SeparableConvolution#separableConvolveY separableConvolveY\endlink() with the given kernels. Declarations: pass arguments explicitly: \code namespace vigra { template void convolveImage(SrcIterator supperleft, SrcIterator slowerright, SrcAccessor sa, DestIterator dupperleft, DestAccessor da, Kernel1D const & kx, Kernel1D const & ky); } \endcode use argument objects in conjunction with \ref ArgumentObjectFactories: \code namespace vigra { template inline void convolveImage(triple src, pair dest, Kernel1D const & kx, Kernel1D const & ky); } \endcode Usage: \#include "vigra/convolution.hxx" \code vigra::FImage src(w,h), dest(w,h); ... // implement sobel filter in x-direction Kernel1D kx, ky; kx.initSymmetricGradient(); ky.initBinomial(1); vigra::convolveImage(srcImageRange(src), destImage(dest), kx, ky); \endcode */ template void convolveImage(SrcIterator supperleft, SrcIterator slowerright, SrcAccessor sa, DestIterator dupperleft, DestAccessor da, Kernel1D const & kx, Kernel1D const & ky) { typedef typename NumericTraits::RealPromote TmpType; BasicImage tmp(slowerright - supperleft); separableConvolveX(srcIterRange(supperleft, slowerright, sa), destImage(tmp), kernel1d(kx)); separableConvolveY(srcImageRange(tmp), destIter(dupperleft, da), kernel1d(ky)); } template inline void convolveImage(triple src, pair dest, Kernel1D const & kx, Kernel1D const & ky) { convolveImage(src.first, src.second, src.third, dest.first, dest.second, kx, ky); } /********************************************************/ /* */ /* simpleSharpening */ /* */ /********************************************************/ /** \brief Perform simple sharpening function. This function use \link StandardConvolution#convolveImage convolveImage\endlink( ) with following filter: \code -sharpening_factor/16.0, -sharpening_factor/8.0, -sharpening_factor/16.0, -sharpening_factor/8.0, 1.0+sharpening_factor*0.75, -sharpening_factor/8.0, -sharpening_factor/16.0, -sharpening_factor/8.0, -sharpening_factor/16.0; \endcode and use BORDER_TREATMENT_REFLECT as border treatment mode. Preconditions: \code 1. sharpening_factor >= 0 2. scale >= 0 \endcode Declarations: Declarations: pass arguments explicitly: \code namespace vigra { template void simpleSharpening(SrcIterator src_ul, SrcIterator src_lr, SrcAccessor src_acc, DestIterator dest_ul, DestAccessor dest_acc, double sharpening_factor) } \endcode use argument objects in conjunction with \ref ArgumentObjectFactories: \code namespace vigra { template inline void simpleSharpening(triple src, pair dest, double sharpening_factor) { simpleSharpening(src.first, src.second, src.third, dest.first, dest.second, sharpening_factor); } } \endcode Usage: \#include "vigra/convolution.hxx" \code vigra::FImage src(w,h), dest(w,h); ... // sharpening with sharpening_factor = 0.1 vigra::simpleSharpening(srcImageRange(src), destImage(dest), 0.1); \endcode */ template void simpleSharpening(SrcIterator src_ul, SrcIterator src_lr, SrcAccessor src_acc, DestIterator dest_ul, DestAccessor dest_acc, double sharpening_factor) { vigra_precondition(sharpening_factor >= 0.0, "simpleSharpening(): amount of sharpening must be >= 0."); Kernel2D kernel; kernel.initExplicitly(Diff2D(-1,-1), Diff2D(1,1)) = -sharpening_factor/16.0, -sharpening_factor/8.0, -sharpening_factor/16.0, -sharpening_factor/8.0, 1.0+sharpening_factor*0.75, -sharpening_factor/8.0, -sharpening_factor/16.0, -sharpening_factor/8.0, -sharpening_factor/16.0; convolveImage(src_ul, src_lr, src_acc, dest_ul, dest_acc, kernel.center(), kernel.accessor(), kernel.upperLeft(), kernel.lowerRight() , BORDER_TREATMENT_REFLECT ); } template inline void simpleSharpening(triple src, pair dest, double sharpening_factor) { simpleSharpening(src.first, src.second, src.third, dest.first, dest.second, sharpening_factor); } /********************************************************/ /* */ /* gaussianSharpening */ /* */ /********************************************************/ /** \brief Perform sharpening function with gaussian filter. This function use the \link vigra::gaussianSmoothing gaussianSmoothing \endlink() at first and scale the source image (\code src \endcode) with the \code scale \endcode factor in an temporary image (\code tmp \endcode). At second the new pixel in the destination image will be with following formel calculate: \code dest = (1 + sharpening_factor)*src - sharpening_factor*tmp \endcode Preconditions: \code 1. sharpening_factor >= 0 2. scale >= 0 \endcode Declarations: pass arguments explicitly: \code namespace vigra { template void gaussianSharpening(SrcIterator src_ul, SrcIterator src_lr, SrcAccessor src_acc, DestIterator dest_ul, DestAccessor dest_acc, double sharpening_factor, double scale) } \endcode use argument objects in conjunction with \ref ArgumentObjectFactories: \code namespace vigra { template void gaussianSharpening(triple src, pair dest, double sharpening_factor, double scale) } \endcode Usage: \#include "vigra/convolution.hxx" \code vigra::FImage src(w,h), dest(w,h); ... // sharpening with sharpening_factor = 3.0 // smoothing with scale = 0.5 vigra::gaussianSmoothing(srcImageRange(src), destImage(dest), 3.0, 0.5); \endcode */ template void gaussianSharpening(SrcIterator src_ul, SrcIterator src_lr, SrcAccessor src_acc, DestIterator dest_ul, DestAccessor dest_acc, double sharpening_factor, double scale) { vigra_precondition(sharpening_factor >= 0.0, "gaussianSharpening(): amount of sharpening must be >= 0"); vigra_precondition(scale >= 0.0, "gaussianSharpening(): scale parameter should be >= 0."); typedef typename NumericTraits::RealPromote ValueType; BasicImage tmp(src_lr - src_ul); gaussianSmoothing(src_ul, src_lr, src_acc, tmp.upperLeft(), tmp.accessor(), scale); SrcIterator i_src = src_ul; DestIterator i_dest = dest_ul; typename BasicImage::traverser tmp_ul = tmp.upperLeft(); typename BasicImage::traverser i_tmp = tmp_ul; typename BasicImage::Accessor tmp_acc = tmp.accessor(); for(; i_src.y != src_lr.y ; i_src.y++, i_dest.y++, i_tmp.y++ ) { for (;i_src.x != src_lr.x ; i_src.x++, i_dest.x++, i_tmp.x++ ) { dest_acc.set((1.0 + sharpening_factor)*src_acc(i_src) - sharpening_factor*tmp_acc(i_tmp), i_dest); } i_src.x = src_ul.x; i_dest.x = dest_ul.x; i_tmp.x = tmp_ul.x; } } template void gaussianSharpening(triple src, pair dest, double sharpening_factor, double scale) { gaussianSharpening(src.first, src.second, src.third, dest.first, dest.second, sharpening_factor, scale); } /********************************************************/ /* */ /* gaussianSmoothing */ /* */ /********************************************************/ /** \brief Perform isotropic Gaussian convolution. This function is a shorthand for the concatenation of a call to \link SeparableConvolution#separableConvolveX separableConvolveX\endlink() and \link SeparableConvolution#separableConvolveY separableConvolveY\endlink() with a Gaussian kernel of the given scale. The function uses BORDER_TREATMENT_REFLECT. Declarations: pass arguments explicitly: \code namespace vigra { template void gaussianSmoothing(SrcIterator supperleft, SrcIterator slowerright, SrcAccessor sa, DestIterator dupperleft, DestAccessor da, double scale); } \endcode use argument objects in conjunction with \ref ArgumentObjectFactories: \code namespace vigra { template inline void gaussianSmoothing(triple src, pair dest, double scale); } \endcode Usage: \#include "vigra/convolution.hxx" \code vigra::FImage src(w,h), dest(w,h); ... // smooth with scale = 3.0 vigra::gaussianSmoothing(srcImageRange(src), destImage(dest), 3.0); \endcode */ template void gaussianSmoothing(SrcIterator supperleft, SrcIterator slowerright, SrcAccessor sa, DestIterator dupperleft, DestAccessor da, double scale) { typedef typename NumericTraits::RealPromote TmpType; BasicImage tmp(slowerright - supperleft); Kernel1D smooth; smooth.initGaussian(scale); smooth.setBorderTreatment(BORDER_TREATMENT_REFLECT); separableConvolveX(srcIterRange(supperleft, slowerright, sa), destImage(tmp), kernel1d(smooth)); separableConvolveY(srcImageRange(tmp), destIter(dupperleft, da), kernel1d(smooth)); } template inline void gaussianSmoothing(triple src, pair dest, double scale) { gaussianSmoothing(src.first, src.second, src.third, dest.first, dest.second, scale); } /********************************************************/ /* */ /* gaussianGradient */ /* */ /********************************************************/ /** \brief Calculate the gradient vector by means of a 1st derivatives of Gaussian filter. This function is a shorthand for the concatenation of a call to \link SeparableConvolution#separableConvolveX separableConvolveX\endlink() and \link SeparableConvolution#separableConvolveY separableConvolveY\endlink() with the appropriate kernels at the given scale. Note that this function can either produce two separate result images for the x- and y-components of the gradient, or write into a vector valued image (with at least two components). Declarations: pass arguments explicitly: \code namespace vigra { // write x and y component of the gradient into separate images template void gaussianGradient(SrcIterator supperleft, SrcIterator slowerright, SrcAccessor sa, DestIteratorX dupperleftx, DestAccessorX dax, DestIteratorY dupperlefty, DestAccessorY day, double scale); // write x and y component of the gradient into a vector-valued image template void gaussianGradient(SrcIterator supperleft, SrcIterator slowerright, SrcAccessor src, DestIterator dupperleft, DestAccessor dest, double scale); } \endcode use argument objects in conjunction with \ref ArgumentObjectFactories: \code namespace vigra { // write x and y component of the gradient into separate images template void gaussianGradient(triple src, pair destx, pair desty, double scale); // write x and y component of the gradient into a vector-valued image template void gaussianGradient(triple src, pair dest, double scale); } \endcode Usage: \#include "vigra/convolution.hxx" \code vigra::FImage src(w,h), gradx(w,h), grady(w,h); ... // calculate gradient vector at scale = 3.0 vigra::gaussianGradient(srcImageRange(src), destImage(gradx), destImage(grady), 3.0); \endcode */ template void gaussianGradient(SrcIterator supperleft, SrcIterator slowerright, SrcAccessor sa, DestIteratorX dupperleftx, DestAccessorX dax, DestIteratorY dupperlefty, DestAccessorY day, double scale) { typedef typename NumericTraits::RealPromote TmpType; BasicImage tmp(slowerright - supperleft); Kernel1D smooth, grad; smooth.initGaussian(scale); grad.initGaussianDerivative(scale, 1); separableConvolveX(srcIterRange(supperleft, slowerright, sa), destImage(tmp), kernel1d(grad)); separableConvolveY(srcImageRange(tmp), destIter(dupperleftx, dax), kernel1d(smooth)); separableConvolveX(srcIterRange(supperleft, slowerright, sa), destImage(tmp), kernel1d(smooth)); separableConvolveY(srcImageRange(tmp), destIter(dupperlefty, day), kernel1d(grad)); } template void gaussianGradient(SrcIterator supperleft, SrcIterator slowerright, SrcAccessor src, DestIterator dupperleft, DestAccessor dest, double scale) { VectorElementAccessor gradx(0, dest), grady(1, dest); gaussianGradient(supperleft, slowerright, src, dupperleft, gradx, dupperleft, grady, scale); } template inline void gaussianGradient(triple src, pair destx, pair desty, double scale) { gaussianGradient(src.first, src.second, src.third, destx.first, destx.second, desty.first, desty.second, scale); } template inline void gaussianGradient(triple src, pair dest, double scale) { gaussianGradient(src.first, src.second, src.third, dest.first, dest.second, scale); } /********************************************************/ /* */ /* laplacianOfGaussian */ /* */ /********************************************************/ /** \brief Filter image with the Laplacian of Gaussian operator at the given scale. This function calls \link SeparableConvolution#separableConvolveX separableConvolveX\endlink() and \link SeparableConvolution#separableConvolveY separableConvolveY\endlink() with the appropriate 2nd derivative of Gaussian kernels in x- and y-direction and then sums the results to get the Laplacian. Declarations: pass arguments explicitly: \code namespace vigra { template void laplacianOfGaussian(SrcIterator supperleft, SrcIterator slowerright, SrcAccessor sa, DestIterator dupperleft, DestAccessor da, double scale); } \endcode use argument objects in conjunction with \ref ArgumentObjectFactories: \code namespace vigra { template inline void laplacianOfGaussian(triple src, pair dest, double scale); } \endcode Usage: \#include "vigra/convolution.hxx" \code vigra::FImage src(w,h), dest(w,h); ... // calculate Laplacian of Gaussian at scale = 3.0 vigra::laplacianOfGaussian(srcImageRange(src), destImage(dest), 3.0); \endcode */ template void laplacianOfGaussian(SrcIterator supperleft, SrcIterator slowerright, SrcAccessor sa, DestIterator dupperleft, DestAccessor da, double scale) { typedef typename NumericTraits::RealPromote TmpType; BasicImage tmp(slowerright - supperleft), tmpx(slowerright - supperleft), tmpy(slowerright - supperleft); Kernel1D smooth, deriv; smooth.initGaussian(scale); deriv.initGaussianDerivative(scale, 2); separableConvolveX(srcIterRange(supperleft, slowerright, sa), destImage(tmp), kernel1d(deriv)); separableConvolveY(srcImageRange(tmp), destImage(tmpx), kernel1d(smooth)); separableConvolveX(srcIterRange(supperleft, slowerright, sa), destImage(tmp), kernel1d(smooth)); separableConvolveY(srcImageRange(tmp), destImage(tmpy), kernel1d(deriv)); combineTwoImages(srcImageRange(tmpx), srcImage(tmpy), destIter(dupperleft, da), std::plus()); } template inline void laplacianOfGaussian(triple src, pair dest, double scale) { laplacianOfGaussian(src.first, src.second, src.third, dest.first, dest.second, scale); } /********************************************************/ /* */ /* hessianMatrixOfGaussian */ /* */ /********************************************************/ /** \brief Filter image with the 2nd derivatives of the Gaussian at the given scale to get the Hessian matrix. The Hessian matrix is a symmetric matrix defined as: \f[ \mbox{\rm Hessian}(I) = \left( \begin{array}{cc} G_{xx} \ast I & G_{xy} \ast I \\ G_{xy} \ast I & G_{yy} \ast I \end{array} \right) \f] where \f$G_{xx}, G_{xy}, G_{yy}\f$ denote 2nd derivatives of Gaussians at the given scale, and \f$\ast\f$ is the convolution symbol. This function calls \link SeparableConvolution#separableConvolveX separableConvolveX\endlink() and \link SeparableConvolution#separableConvolveY separableConvolveY\endlink() with the appropriate 2nd derivative of Gaussian kernels and puts the results in the three destination images. The first destination image will contain the second derivative in x-direction, the second one the mixed derivative, and the third one holds the derivative in y-direction. Declarations: pass arguments explicitly: \code namespace vigra { template void hessianMatrixOfGaussian(SrcIterator supperleft, SrcIterator slowerright, SrcAccessor sa, DestIteratorX dupperleftx, DestAccessorX dax, DestIteratorXY dupperleftxy, DestAccessorXY daxy, DestIteratorY dupperlefty, DestAccessorY day, double scale); } \endcode use argument objects in conjunction with \ref ArgumentObjectFactories: \code namespace vigra { template inline void hessianMatrixOfGaussian(triple src, pair destx, pair destxy, pair desty, double scale); } \endcode Usage: \#include "vigra/convolution.hxx" \code vigra::FImage src(w,h), hxx(w,h), hxy(w,h), hyy(w,h); ... // calculate Hessian of Gaussian at scale = 3.0 vigra::hessianMatrixOfGaussian(srcImageRange(src), destImage(hxx), destImage(hxy), destImage(hyy), 3.0); \endcode */ template void hessianMatrixOfGaussian(SrcIterator supperleft, SrcIterator slowerright, SrcAccessor sa, DestIteratorX dupperleftx, DestAccessorX dax, DestIteratorXY dupperleftxy, DestAccessorXY daxy, DestIteratorY dupperlefty, DestAccessorY day, double scale) { typedef typename NumericTraits::RealPromote TmpType; BasicImage tmp(slowerright - supperleft); Kernel1D smooth, deriv1, deriv2; smooth.initGaussian(scale); deriv1.initGaussianDerivative(scale, 1); deriv2.initGaussianDerivative(scale, 2); separableConvolveX(srcIterRange(supperleft, slowerright, sa), destImage(tmp), kernel1d(deriv2)); separableConvolveY(srcImageRange(tmp), destIter(dupperleftx, dax), kernel1d(smooth)); separableConvolveX(srcIterRange(supperleft, slowerright, sa), destImage(tmp), kernel1d(smooth)); separableConvolveY(srcImageRange(tmp), destIter(dupperlefty, day), kernel1d(deriv2)); separableConvolveX(srcIterRange(supperleft, slowerright, sa), destImage(tmp), kernel1d(deriv1)); separableConvolveY(srcImageRange(tmp), destIter(dupperleftxy, daxy), kernel1d(deriv1)); } template inline void hessianMatrixOfGaussian(triple src, pair destx, pair destxy, pair desty, double scale) { hessianMatrixOfGaussian(src.first, src.second, src.third, destx.first, destx.second, destxy.first, destxy.second, desty.first, desty.second, scale); } /********************************************************/ /* */ /* structureTensor */ /* */ /********************************************************/ /** \brief Calculate the Structure Tensor for each pixel of and image, using Gaussian (derivative) filters. The Structure Tensor is is a smoothed version of the Euclidean product of the gradient vector with itself. I.e. it's a symmetric matrix defined as: \f[ \mbox{\rm StructurTensor}(I) = \left( \begin{array}{cc} G \ast (I_x I_x) & G \ast (I_x I_y) \\ G \ast (I_x I_y) & G \ast (I_y I_y) \end{array} \right) = \left( \begin{array}{cc} A & C \\ C & B \end{array} \right) \f] where \f$G\f$ denotes Gaussian smoothing at the outer scale, \f$I_x, I_y\f$ are the gradient components taken at the inner scale, \f$\ast\f$ is the convolution symbol, and \f$I_x I_x\f$ etc. are pixelwise products of the 1st derivative images. This function calls \link SeparableConvolution#separableConvolveX separableConvolveX\endlink() and \link SeparableConvolution#separableConvolveY separableConvolveY\endlink() with the appropriate Gaussian kernels and puts the results in the three destination images. The first destination image will contain \f$G \ast (I_x I_x)\f$, the second one \f$G \ast (I_x I_y)\f$, and the third one holds \f$G \ast (I_y I_y)\f$. Declarations: pass arguments explicitly: \code namespace vigra { template void structureTensor(SrcIterator supperleft, SrcIterator slowerright, SrcAccessor sa, DestIteratorX dupperleftx, DestAccessorX dax, DestIteratorXY dupperleftxy, DestAccessorXY daxy, DestIteratorY dupperlefty, DestAccessorY day, double inner_scale, double outer_scale); } \endcode use argument objects in conjunction with \ref ArgumentObjectFactories: \code namespace vigra { template inline void structureTensor(triple src, pair destx, pair destxy, pair desty, double nner_scale, double outer_scale); } \endcode Usage: \#include "vigra/convolution.hxx" \code vigra::FImage src(w,h), stxx(w,h), stxy(w,h), styy(w,h); ... // calculate Structure Tensor at inner scale = 1.0 and outer scale = 3.0 vigra::structureTensor(srcImageRange(src), destImage(stxx), destImage(stxy), destImage(styy), 1.0, 3.0); \endcode */ template void structureTensor(SrcIterator supperleft, SrcIterator slowerright, SrcAccessor sa, DestIteratorX dupperleftx, DestAccessorX dax, DestIteratorXY dupperleftxy, DestAccessorXY daxy, DestIteratorY dupperlefty, DestAccessorY day, double inner_scale, double outer_scale) { typedef typename NumericTraits::RealPromote TmpType; BasicImage tmp(slowerright - supperleft), tmpx(slowerright - supperleft), tmpy(slowerright - supperleft); gaussianGradient(srcIterRange(supperleft, slowerright, sa), destImage(tmpx), destImage(tmpy), inner_scale); combineTwoImages(srcImageRange(tmpx), srcImage(tmpx), destImage(tmp), std::multiplies()); gaussianSmoothing(srcImageRange(tmp), destIter(dupperleftx, dax), outer_scale); combineTwoImages(srcImageRange(tmpy), srcImage(tmpy), destImage(tmp), std::multiplies()); gaussianSmoothing(srcImageRange(tmp), destIter(dupperlefty, day), outer_scale); combineTwoImages(srcImageRange(tmpx), srcImage(tmpy), destImage(tmp), std::multiplies()); gaussianSmoothing(srcImageRange(tmp), destIter(dupperleftxy, daxy), outer_scale); } template inline void structureTensor(triple src, pair destx, pair destxy, pair desty, double inner_scale, double outer_scale) { structureTensor(src.first, src.second, src.third, destx.first, destx.second, destxy.first, destxy.second, desty.first, desty.second, inner_scale, outer_scale); } //@} } // namespace vigra #endif // VIGRA_CONVOLUTION_HXX