//-- -*- c++ -*- /************************************************************************/ /* */ /* Copyright 2003 by Christian-Dennis Rahn */ /* and 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_MULTI_CONVOLUTION_H #define VIGRA_MULTI_CONVOLUTION_H #include #include #include #include #include #include #include #include namespace vigra { namespace detail { /********************************************************/ /* */ /* internalSeparableConvolveMultiArray */ /* */ /********************************************************/ template void internalSeparableConvolveMultiArrayTmp( SrcIterator si, SrcShape const & shape, SrcAccessor src, DestIterator di, DestAccessor dest, KernelIterator kit) { enum { N = 1 + SrcIterator::level }; typedef typename NumericTraits::RealPromote TmpType; // temporay array to hold the current line to enable in-place operation ArrayVector tmp( shape[0] ); typedef MultiArrayNavigator SNavigator; typedef MultiArrayNavigator DNavigator; { // only operate on first dimension here SNavigator snav( si, shape, 0 ); DNavigator dnav( di, shape, 0 ); for( ; snav.hasMore(); snav++, dnav++ ) { // first copy source to temp for maximum cache efficiency copyLine( snav.begin(), snav.end(), src, tmp.begin(), typename AccessorTraits::default_accessor() ); convolveLine( srcIterRange(tmp.begin(), tmp.end(), typename AccessorTraits::default_const_accessor()), destIter( dnav.begin(), dest ), kernel1d( *kit ) ); } ++kit; } // operate on further dimensions for( int d = 1; d < N; ++d, ++kit ) { DNavigator dnav( di, shape, d ); tmp.resize( shape[d] ); for( ; dnav.hasMore(); dnav++ ) { // first copy source to temp for maximum cache efficiency copyLine( dnav.begin(), dnav.end(), dest, tmp.begin(), typename AccessorTraits::default_accessor() ); convolveLine( srcIterRange(tmp.begin(), tmp.end(), typename AccessorTraits::default_const_accessor()), destIter( dnav.begin(), dest ), kernel1d( *kit ) ); } } } } // namespace detail /** \addtogroup MultiArrayConvolutionFilters Convolution filters for multi-dimensional arrays. These functions realize a separable convolution on an arbitrary dimensional array that is specified by iterators (compatible to \ref MultiIteratorPage) and shape objects. It can therefore be applied to a wide range of data structures (\ref vigra::MultiArrayView, \ref vigra::MultiArray etc.). */ //@{ /********************************************************/ /* */ /* separableConvolveMultiArray */ /* */ /********************************************************/ /** \brief Separated convolution on multi-dimensional arrays. This function computes a separated convolution on all dimensions of the given multi-dimensional array. Both source and destination arrays are represented by iterators, shape objects and accessors. The destination array is required to already have the correct size. There are two variants of this functions: one takes a single kernel of type \ref vigra::Kernel1D which is then applied to all dimensions, whereas the other requires an iterator referencing a sequence of \ref vigra::Kernel1D objects, one for every dimension of the data. Then the first kernel in this sequence is applied to the innermost dimension (e.g. the x-dimension of an image), while the last is applied to the outermost dimension (e.g. the z-dimension in a 3D image). This function may work in-place, which means that siter == diter is allowed. A full-sized internal array is only allocated if working on the destination array directly would cause round-off errors (i.e. if typeid(typename NumericTraits::RealPromote) != typeid(typename DestAccessor::value_type). Declarations: pass arguments explicitly: \code namespace vigra { // apply the same kernel to all dimensions template void separableConvolveMultiArray(SrcIterator siter, SrcShape const & shape, SrcAccessor src, DestIterator diter, DestAccessor dest, Kernel1D const & kernel); // apply each kernel from the sequence `kernels³ in turn template void separableConvolveMultiArray(SrcIterator siter, SrcShape const & shape, SrcAccessor src, DestIterator diter, DestAccessor dest, KernelIterator kernels); } \endcode use argument objects in conjunction with \ref ArgumentObjectFactories: \code namespace vigra { // apply the same kernel to all dimensions template void separableConvolveMultiArray(triple const & source, pair const & dest, Kernel1D const & kernel); // apply each kernel from the sequence `kernels³ in turn template void separableConvolveMultiArray(triple const & source, pair const & dest, KernelIterator kernels); } \endcode Usage: \#include "vigra/multi_convolution.hxx" \code MultiArray<3, unsigned char>::size_type shape(width, height, depth); MultiArray<3, unsigned char> source(shape); MultiArray<3, float> dest(shape); ... Kernel1D gauss; gauss.initGaussian(sigma); // create 3 Gauss kernels, one for each dimension ArrayVector > kernels(3, gauss); // perform Gaussian smoothing on all dimensions separableConvolveMultiArray(srcMultiArrayRange(source), destMultiArray(dest), kernels.begin()); \endcode \see vigra::Kernel1D, convolveLine() */ template void separableConvolveMultiArray( SrcIterator s, SrcShape const & shape, SrcAccessor src, DestIterator d, DestAccessor dest, KernelIterator kernels ) { typedef typename NumericTraits::RealPromote TmpType; if(!IsSameType::boolResult) { // need a temporary array to avoid rounding errors MultiArray tmpArray(shape); detail::internalSeparableConvolveMultiArrayTmp( s, shape, src, tmpArray.traverser_begin(), typename AccessorTraits::default_accessor(), kernels ); copyMultiArray(srcMultiArrayRange(tmpArray), destIter(d, dest)); } else { // work directly on the destination array detail::internalSeparableConvolveMultiArrayTmp( s, shape, src, d, dest, kernels ); } } template inline void separableConvolveMultiArray( triple const & source, pair const & dest, KernelIterator kit ) { separableConvolveMultiArray( source.first, source.second, source.third, dest.first, dest.second, kit ); } template inline void separableConvolveMultiArray( SrcIterator s, SrcShape const & shape, SrcAccessor src, DestIterator d, DestAccessor dest, Kernel1D const & kernel ) { ArrayVector > kernels(shape.size(), kernel); separableConvolveMultiArray( s, shape, src, d, dest, kernels.begin() ); } template inline void separableConvolveMultiArray(triple const & source, pair const & dest, Kernel1D const & kernel ) { ArrayVector > kernels(source.second.size(), kernel); separableConvolveMultiArray( source.first, source.second, source.third, dest.first, dest.second, kernels.begin() ); } /********************************************************/ /* */ /* convolveMultiArrayOneDimension */ /* */ /********************************************************/ /** \brief Convolution along a single dimension of a multi-dimensional arrays. This function computes a convolution along one dimension (specified by the parameter dim of the given multi-dimensional array with the given kernel. Both source and destination arrays are represented by iterators, shape objects and accessors. The destination array is required to already have the correct size. This function may work in-place, which means that siter == diter is allowed. Declarations: pass arguments explicitly: \code namespace vigra { template void convolveMultiArrayOneDimension(SrcIterator siter, SrcShape const & shape, SrcAccessor src, DestIterator diter, DestAccessor dest, unsigned int dim, vigra::Kernel1D const & kernel); } \endcode use argument objects in conjunction with \ref ArgumentObjectFactories: \code namespace vigra { template void convolveMultiArrayOneDimension(triple const & source, pair const & dest, unsigned int dim, vigra::Kernel1D const & kernel); } \endcode Usage: \#include "vigra/multi_convolution.hxx" \code MultiArray<3, unsigned char>::size_type shape(width, height, depth); MultiArray<3, unsigned char> source(shape); MultiArray<3, float> dest(shape); ... Kernel1D gauss; gauss.initGaussian(sigma); // perform Gaussian smoothing along dimensions 1 (height) convolveMultiArrayOneDimension(srcMultiArrayRange(source), destMultiArray(dest), 1, gauss); \endcode \see separableConvolveMultiArray() */ template void convolveMultiArrayOneDimension(SrcIterator s, SrcShape const & shape, SrcAccessor src, DestIterator d, DestAccessor dest, unsigned int dim, vigra::Kernel1D const & kernel ) { enum { N = 1 + SrcIterator::level }; vigra_precondition( dim < N, "convolveMultiArrayOneDimension(): The dimension number to convolve must be smaller " "than the data dimensionality" ); typedef typename NumericTraits::RealPromote TmpType; ArrayVector tmp( shape[dim] ); typedef MultiArrayNavigator SNavigator; typedef MultiArrayNavigator DNavigator; SNavigator snav( s, shape, dim ); DNavigator dnav( d, shape, dim ); for( ; snav.hasMore(); snav++, dnav++ ) { // first copy source to temp for maximum cache efficiency copyLine( snav.begin(), snav.end(), src, tmp.begin(), typename AccessorTraits::default_accessor() ); convolveLine( srcIterRange( tmp.begin(), tmp.end(), typename AccessorTraits::default_const_accessor()), destIter( dnav.begin(), dest ), kernel1d( kernel ) ); } } template inline void convolveMultiArrayOneDimension(triple const & source, pair const & dest, unsigned int dim, vigra::Kernel1D const & kernel ) { convolveMultiArrayOneDimension( source.first, source.second, source.third, dest.first, dest.second, dim, kernel ); } /********************************************************/ /* */ /* gaussianSmoothMultiArray */ /* */ /********************************************************/ /** \brief Isotropic Gaussian smoothing of a multi-dimensional arrays. This function computes an isotropic convolution of the given multi-dimensional array with a Gaussian filter at the given standard deviation sigma. Both source and destination arrays are represented by iterators, shape objects and accessors. The destination array is required to already have the correct size. This function may work in-place, which means that siter == diter is allowed. It is implemented by a call to \ref separableConvolveMultiArray() with the appropriate kernel. If the data are anisotropic (different pixel size along different dimensions) you should call \ref separableConvolveMultiArray() directly with the appropriate anisotropic Gaussians. Declarations: pass arguments explicitly: \code namespace vigra { template void gaussianSmoothMultiArray(SrcIterator siter, SrcShape const & shape, SrcAccessor src, DestIterator diter, DestAccessor dest, double sigma); } \endcode use argument objects in conjunction with \ref ArgumentObjectFactories: \code namespace vigra { template void gaussianSmoothMultiArray(triple const & source, pair const & dest, double sigma); } \endcode Usage: \#include "vigra/multi_convolution.hxx" \code MultiArray<3, unsigned char>::size_type shape(width, height, depth); MultiArray<3, unsigned char> source(shape); MultiArray<3, float> dest(shape); ... // perform isotropic Gaussian smoothing at scale `sigma³ gaussianSmoothMultiArray(srcMultiArrayRange(source), destMultiArray(dest), sigma); \endcode \see separableConvolveMultiArray() */ template void gaussianSmoothMultiArray( SrcIterator s, SrcShape const & shape, SrcAccessor src, DestIterator d, DestAccessor dest, double sigma ) { typedef typename NumericTraits::RealPromote kernel_type; Kernel1D gauss; gauss.initGaussian( sigma ); separableConvolveMultiArray( s, shape, src, d, dest, gauss); } template inline void gaussianSmoothMultiArray(triple const & source, pair const & dest, double sigma ) { gaussianSmoothMultiArray( source.first, source.second, source.third, dest.first, dest.second, sigma ); } /********************************************************/ /* */ /* gaussianGradientMultiArray */ /* */ /********************************************************/ /** \brief Calculate Gaussian gradient of a multi-dimensional arrays. This function computes the Gaussian gradient of the given multi-dimensional array with a sequence of first-derivative-of-Gaussian filters at the given standard deviation sigma (differentiation is applied to each dimension in turn, starting with the innermost dimension). Both source and destination arrays are represented by iterators, shape objects and accessors. The destination array is required to have a vector valued pixel type with as many elements as the number of dimensions. This function is implemented by calls to \ref separableConvolveMultiArray() with the appropriate kernels. If the data are anisotropic (different pixel size along different dimensions) you should call \ref separableConvolveMultiArray() directly with the appropriate anisotropic Gaussian derivatives. Declarations: pass arguments explicitly: \code namespace vigra { template void gaussianGradientMultiArray(SrcIterator siter, SrcShape const & shape, SrcAccessor src, DestIterator diter, DestAccessor dest, double sigma); } \endcode use argument objects in conjunction with \ref ArgumentObjectFactories: \code namespace vigra { template void gaussianGradientMultiArray(triple const & source, pair const & dest, double sigma); } \endcode Usage: \#include "vigra/multi_convolution.hxx" \code MultiArray<3, unsigned char>::size_type shape(width, height, depth); MultiArray<3, unsigned char> source(shape); MultiArray<3, TinyVector > dest(shape); ... // compute Gaussian gradient at scale sigma gaussianGradientMultiArray(srcMultiArrayRange(source), destMultiArray(dest), sigma); \endcode Required Interface: see \ref convolveImage(), in addition: \code int dimension = 0; VectorElementAccessor elementAccessor(0, dest); \endcode \see separableConvolveMultiArray() */ template void gaussianGradientMultiArray( SrcIterator si, SrcShape const & shape, SrcAccessor src, DestIterator di, DestAccessor dest, double sigma ) { typedef typename DestAccessor::value_type DestType; typedef typename NumericTraits::RealPromote kernel_type; Kernel1D gauss, derivative; gauss.initGaussian(sigma); derivative.initGaussianDerivative(sigma, 1); typedef VectorElementAccessor ElementAccessor; // compute gradient components for(unsigned int d = 0; d < shape.size(); ++d ) { ArrayVector > kernels(shape.size(), gauss); kernels[d] = derivative; separableConvolveMultiArray( si, shape, src, di, ElementAccessor(d, dest), kernels.begin()); } } template inline void gaussianGradientMultiArray(triple const & source, pair const & dest, double sigma ) { gaussianGradientMultiArray( source.first, source.second, source.third, dest.first, dest.second, sigma ); } /********************************************************/ /* */ /* symmetricGradientMultiArray */ /* */ /********************************************************/ /** \brief Calculate gradient of a multi-dimensional arrays using symmetric difference filters. This function computes the gradient of the given multi-dimensional array with a sequence of symmetric difference filters a (differentiation is applied to each dimension in turn, starting with the innermost dimension). Both source and destination arrays are represented by iterators, shape objects and accessors. The destination array is required to have a vector valued pixel type with as many elements as the number of dimensions. This function is implemented by calls to \ref convolveMultiArrayOneDimension() with the symmetric difference kernel. Declarations: pass arguments explicitly: \code namespace vigra { template void symmetricGradientMultiArray(SrcIterator siter, SrcShape const & shape, SrcAccessor src, DestIterator diter, DestAccessor dest); } \endcode use argument objects in conjunction with \ref ArgumentObjectFactories: \code namespace vigra { template void symmetricGradientMultiArray(triple const & source, pair const & dest); } \endcode Usage: \#include "vigra/multi_convolution.hxx" \code MultiArray<3, unsigned char>::size_type shape(width, height, depth); MultiArray<3, unsigned char> source(shape); MultiArray<3, TinyVector > dest(shape); ... // compute gradient symmetricGradientMultiArray(srcMultiArrayRange(source), destMultiArray(dest)); \endcode Required Interface: see \ref convolveImage(), in addition: \code int dimension = 0; VectorElementAccessor elementAccessor(0, dest); \endcode \see convolveMultiArrayOneDimension() */ template void symmetricGradientMultiArray(SrcIterator si, SrcShape const & shape, SrcAccessor src, DestIterator di, DestAccessor dest) { typedef typename DestAccessor::value_type DestType; typedef typename NumericTraits::RealPromote kernel_type; Kernel1D filter; filter.initSymmetricGradient(); typedef VectorElementAccessor ElementAccessor; // compute gradient components for(unsigned int d = 0; d < shape.size(); ++d ) { convolveMultiArrayOneDimension(si, shape, src, di, ElementAccessor(d, dest), d, filter); } } template inline void symmetricGradientMultiArray(triple const & source, pair const & dest ) { symmetricGradientMultiArray(source.first, source.second, source.third, dest.first, dest.second); } //@} } //-- namespace vigra #endif //-- VIGRA_MULTI_CONVOLUTION_H