diff --git a/Modules/Core/Functor/include/otbFunctorImageFilter.h b/Modules/Core/Functor/include/otbFunctorImageFilter.h index 75a3fd1ab672d04f61ead3e7c71935d3db862430..451dc4720cd591a29dd13843ca1514dc5a86bcee 100644 --- a/Modules/Core/Functor/include/otbFunctorImageFilter.h +++ b/Modules/Core/Functor/include/otbFunctorImageFilter.h @@ -24,12 +24,14 @@ #include "otbVariadicNamedInputsImageFilter.h" #include "otbImage.h" #include "otbVectorImage.h" +#include "itkRGBPixel.h" +#include "itkRGBAPixel.h" +#include "itkFixedArray.h" #include "itkDefaultConvertPixelTraits.h" #include <type_traits> namespace otb { - /** * \struct IsNeighborhood * \brief Struct testing if T is a neighborhood @@ -59,6 +61,52 @@ template <class T> struct IsNeighborhood<const itk::Neighborhood<T>&> static constexpr bool value = true; }; +/** + * \struct IsSuitableType + * \brief Helper struct to check if a type can be used as pixel type. + * + * ::value maps to true if type can be used and false otherwhise. + */ +template <class T> struct IsSuitableType +{ + static constexpr bool value = std::is_scalar<T>::value; +}; + +/// Unwrap complex +template <class T> struct IsSuitableType<std::complex<T>> { + static constexpr bool value = IsSuitableType<T>::value; +}; + +/// Unwrap VariableLengthVector +template <class T> struct IsSuitableType<itk::VariableLengthVector<T>> { + static constexpr bool value = IsSuitableType<T>::value; +}; + +/// Unwrap FixedArray +template <class T, size_t N> struct IsSuitableType<itk::FixedArray<T,N>> { + static constexpr bool value = IsSuitableType<T>::value; +}; + +/// Unwrap RGBPixel +template <class T> struct IsSuitableType<itk::RGBPixel<T>> { + static constexpr bool value = IsSuitableType<T>::value; +}; + +/// Unwrap RGBAPixel +template <class T> struct IsSuitableType<itk::RGBAPixel<T>> { + static constexpr bool value = IsSuitableType<T>::value; +}; + +/// Discard const qualifier +template <class T> struct IsSuitableType<const T>{ + static constexpr bool value = IsSuitableType<T>::value; +}; + +/// Discard reference +template <class T> struct IsSuitableType<T &>{ + static constexpr bool value = IsSuitableType<T>::value; +}; + /** * \struct PixelTypeDeduction @@ -70,21 +118,31 @@ template <class T> struct IsNeighborhood<const itk::Neighborhood<T>&> */ template <class T> struct PixelTypeDeduction { + static_assert(IsSuitableType<T>::value,"T can not be used as a template parameter for Image or VectorImage classes."); using PixelType = T; }; /// Partial specialisation for itk::Neighborhood<T> template <class T> struct PixelTypeDeduction<itk::Neighborhood<T>> { + static_assert(IsSuitableType<T>::value,"T can not be used as a template parameter for Image or VectorImage classes."); using PixelType = T; }; -/// Partial specialisation for const itk::Neighborhood<T> & -template <class T> struct PixelTypeDeduction<const itk::Neighborhood<T>&> +/// Discard const qualifier +template <class T> struct PixelTypeDeduction<const T> { - using PixelType = T; + using PixelType = typename PixelTypeDeduction<T>::PixelType; +}; + +/// Discard reference +template <class T> struct PixelTypeDeduction<T &> +{ + using PixelType = typename PixelTypeDeduction<T>::PixelType; }; + + /** * \struct ImageTypeDeduction * \brief Helper struct to derive ImageType from template parameter @@ -104,12 +162,6 @@ template <class T> struct ImageTypeDeduction<itk::VariableLengthVector<T>> using ImageType = otb::VectorImage<T>; }; -/// Partial specialisation for const T & -template <class T> struct ImageTypeDeduction<const T &> -{ - using ImageType = typename ImageTypeDeduction<T>::ImageType; -}; - /** * \struct FunctorFilterSuperclassHelper * \brief Struct allowing to derive the superclass prototype for the @@ -139,7 +191,7 @@ template <typename R, typename... T, typename TNameMap> struct FunctorFilterSupe using InputHasNeighborhood = std::tuple<typename IsNeighborhood<T>::ValueType...>; }; -// Partial specialisation for R(C::*)(T...) const +/// Partial specialisation for R(C::*)(T...) const template <typename C, typename R, typename... T, typename TNameMap> struct FunctorFilterSuperclassHelper<R(C::*)(T...) const, TNameMap> { using OutputImageType = typename ImageTypeDeduction<R>::ImageType; @@ -152,9 +204,9 @@ template <typename C, typename R, typename... T, typename TNameMap> struct Funct using InputHasNeighborhood = std::tuple<typename IsNeighborhood<T>::ValueType...>; }; -// Partial specialisation for R(C::*)(T...) +/// Partial specialisation for R(C::*)(T...) template <typename C, typename R, typename... T, typename TNameMap> struct FunctorFilterSuperclassHelper<R(C::*)(T...), TNameMap> -{ +{ using OutputImageType = typename ImageTypeDeduction<R>::ImageType; template <typename V> using InputImageType = typename ImageTypeDeduction<typename PixelTypeDeduction<V>::PixelType>::ImageType; @@ -166,7 +218,7 @@ template <typename C, typename R, typename... T, typename TNameMap> struct Funct }; /** - * brief This helper method builds a fully functional FunctorImageFilter from a functor instance + * \brief This helper method builds a fully functional FunctorImageFilter from a functor instance * * Functor can be any operator() that matches the following: * - Accepts any number of arguments of T, @@ -176,7 +228,7 @@ template <typename C, typename R, typename... T, typename TNameMap> struct Funct * - returns T or itk::VariableLengthVector<T>, with T a scalar type * * The returned filter is ready to use. Inputs can be set through the - * SetVInputs() method (see VariadicInputsImageFilter class for + * SetVariadicInputs() method (see VariadicInputsImageFilter class for * details) * * \param f the Functor to build the filter from diff --git a/Modules/Core/Functor/include/otbFunctorImageFilter.hxx b/Modules/Core/Functor/include/otbFunctorImageFilter.hxx index cc6573d3c77e701fdb1ee680412eac06c6cb5922..043ccaa3b3d48cdbc192564626db454c0831cbb4 100644 --- a/Modules/Core/Functor/include/otbFunctorImageFilter.hxx +++ b/Modules/Core/Functor/include/otbFunctorImageFilter.hxx @@ -62,21 +62,25 @@ template<class T> int SetInputRequestedRegion(const T * img, const itk::ImageReg } } +// Will be easier to write in c++17 with std::apply and fold expressions template <class Tuple, size_t...Is> auto SetInputRequestedRegionsImpl(Tuple & t, const itk::ImageRegion<2> & region, std::index_sequence<Is...>,const itk::Size<2> & radius) { return std::make_tuple(SetInputRequestedRegion(std::get<Is>(t),region,radius)...); } +// Will be easier to write in c++17 with std::apply and fold expressions template <typename... T> auto SetInputRequestedRegions(std::tuple<T...> && t,const itk::ImageRegion<2> & region, const itk::Size<2> & radius) { return SetInputRequestedRegionsImpl(t,region,std::make_index_sequence<sizeof...(T)>{},radius); } +// Will be easier to write in c++17 with std::apply and fold expressions template <class Tuple, size_t...Is> auto GetNumberOfComponentsPerInputImpl(Tuple & t, std::index_sequence<Is...>) { return std::array<size_t,sizeof...(Is)>{{std::get<Is>(t)->GetNumberOfComponentsPerPixel()...}}; } +// Will be easier to write in c++17 with std::apply and fold expressions template <typename ...T> auto GetNumberOfComponentsPerInput(std::tuple<T...> & t) { return GetNumberOfComponentsPerInputImpl(t, std::make_index_sequence<sizeof...(T)>{}); @@ -102,12 +106,13 @@ template <> struct MakeIterator<std::true_type> } }; - +// Will be easier to write in c++17 with std::apply and fold expressions template <class TNeigh, class Tuple, size_t...Is> auto MakeIteratorsImpl(const Tuple& t, const itk::ImageRegion<2> & region, const itk::Size<2> & radius, std::index_sequence<Is...>, TNeigh) { return std::make_tuple(MakeIterator<typename std::tuple_element<Is,TNeigh>::type >::Make(std::get<Is>(t),region,radius)...); } +// Will be easier to write in c++17 with std::apply and fold expressions template<class TNeigh, typename... T> auto MakeIterators(std::tuple<T...> &&t,const itk::ImageRegion<2> & region, const itk::Size<2> & radius, TNeigh n) { return MakeIteratorsImpl(t,region,radius,std::make_index_sequence<sizeof...(T)>{},n); @@ -133,17 +138,20 @@ template <typename T> struct GetProxy<itk::ConstNeighborhoodIterator<T> > } }; +// Will be easier to write in c++17 with std::apply and fold expressions template <class Tuple, class Oper, size_t...Is> auto CallOperatorImpl(Tuple& t, const Oper & oper,std::index_sequence<Is...>) { return oper(GetProxy<typename std::remove_reference<decltype(std::get<Is>(t))>::type>::Get(std::get<Is>(t))...); } +// Will be easier to write in c++17 with std::apply and fold expressions template <class Oper, typename ... Args> auto CallOperator(const Oper& oper, std::tuple<Args...> & t) { return CallOperatorImpl(t,oper,std::make_index_sequence<sizeof...(Args)>{}); } // Variadic move of iterators +// Will be easier to write in c++17 with std::apply and fold expressions template<class Tuple,size_t...Is> auto MoveIteratorsImpl(Tuple & t, std::index_sequence<Is...>) { return std::make_tuple(++(std::get<Is>(t) )...); @@ -188,7 +196,7 @@ FunctorImageFilter<TFunction, TNameMap> // Propagate to each variadic inputs, including possible radius // TODO: For now all inputs are padded with the radius, even if they // are not neighborhood based - functor_filter_details::SetInputRequestedRegions(this->GetVInputs(),requestedRegion, m_Radius); + functor_filter_details::SetInputRequestedRegions(this->GetVariadicInputs(),requestedRegion, m_Radius); } template <class TFunction, class TNameMap> @@ -199,7 +207,7 @@ FunctorImageFilter<TFunction, TNameMap>::GenerateOutputInformation() Superclass::GenerateOutputInformation(); // Get All variadic inputs - auto inputs = this->GetVInputs(); + auto inputs = this->GetVariadicInputs(); // Retrieve an array of number of components per input auto inputNbComps = functor_filter_details::GetNumberOfComponentsPerInput(inputs); @@ -220,7 +228,7 @@ FunctorImageFilter<TFunction, TNameMap> itk::ImageScanlineIterator<OutputImageType> outIt(this->GetOutput(),outputRegionForThread); itk::ProgressReporter p(this,threadId,outputRegionForThread.GetNumberOfPixels()); - auto inputIterators = functor_filter_details::MakeIterators(this->GetVInputs(),outputRegionForThread, m_Radius,InputHasNeighborhood{}); + auto inputIterators = functor_filter_details::MakeIterators(this->GetVariadicInputs(),outputRegionForThread, m_Radius,InputHasNeighborhood{}); while(!outIt.IsAtEnd()) { diff --git a/Modules/Core/Functor/include/otbVariadicInputsImageFilter.h b/Modules/Core/Functor/include/otbVariadicInputsImageFilter.h index f66752e6f08313c717ea72b94b869d918b9b6cf2..9625815f24f3e54a2df56af5ee0560144b7fcc4d 100644 --- a/Modules/Core/Functor/include/otbVariadicInputsImageFilter.h +++ b/Modules/Core/Functor/include/otbVariadicInputsImageFilter.h @@ -24,8 +24,30 @@ namespace otb { -/// TODO: Documentation - +/** + * \brief Base class for image filter with variadic inputs. + * + * This filter act as a base class for all filters that will take + * several input images with different types and produce an output + * image. + * + * Type for each input is taken from the variadic template parameter + * TInputs. + * + * Inputs get be set/get with SetVariadicInput<N>() and + * GetVariadicInput<N>(), when N is the index (first input is 0) of + * the input. This is resolved at compile time: you can not call + * SetVariadicInput<N>() with an argument not matching the Nth input + * type (it will lead to type mismatch compilation error). + * + * Alternatively, you can call SetVariadicInputs() with all the input + * image in the same order as in the template parameters. + * + * Last, there is a macro that generates SetInput1() ... SetInput10() + * (iff the number of varidic input types is large enough) for + * backward compatibility. + * + */ template<class TOuptut, class ... TInputs> class VariadicInputsImageFilter : public itk::ImageSource<TOuptut> { public: @@ -39,11 +61,15 @@ public: template <size_t I> using InputImageType = typename std::tuple_element<I,InputTypesTupleType>::type; static constexpr size_t NumberOfInputs = std::tuple_size<InputTypesTupleType>::value; - + // Good old new macro itkNewMacro(Self); - - template <std::size_t I> void SetVInput(const typename std::tuple_element<I,InputTypesTupleType>::type * inputPtr) + + /** + * \param Set the Ith input + */ + template <std::size_t I> void SetVariadicInput(const typename std::tuple_element<I,InputTypesTupleType>::type * inputPtr) { + static_assert(std::tuple_size<InputTypesTupleType>::value>I,"Template value I is out of range."); this->SetNthInput(I,const_cast<typename std::tuple_element<I,InputTypesTupleType>::type *>(inputPtr)); } @@ -51,7 +77,7 @@ public: template<typename Tuple = InputTypesTupleType, typename Check = typename std::enable_if<n<=std::tuple_size<Tuple>::value >::type> \ void SetInput ## n(const typename std::tuple_element<n-1,Tuple>::type * img) \ { \ - this->template SetVInput<n-1>(img); \ + this->template SetVariadicInput<n-1>(img); \ } // The following defines legacy setters SetInput1() @@ -68,20 +94,30 @@ public: DefineLegacySetInputMacro(10); #undef DefineLegacySetInputMacro - - template <std::size_t I> const typename std::tuple_element<I,InputTypesTupleType>::type * GetVInput() + + /** + * \return the Ith variadic input + */ + template <std::size_t I> const typename std::tuple_element<I,InputTypesTupleType>::type * GetVariadicInput() { + static_assert(std::tuple_size<InputTypesTupleType>::value>I,"Template value I is out of range."); using ImageType = typename std::tuple_element<I,InputTypesTupleType>::type; return dynamic_cast<const ImageType *>(this->GetInput(I)); } - void SetVInputs(TInputs*... inputs) + /** + * \param inputs A vararg list of inputs + */ + void SetVariadicInputs(TInputs*... inputs) { auto inTuple = std::make_tuple(inputs...); SetInputsImpl(inTuple,std::make_index_sequence<sizeof...(inputs)>{}); } - auto GetVInputs() + /** + * \return A tuple with all inputs + */ + auto GetVariadicInputs() { return GetInputsImpl(std::make_index_sequence<sizeof...(TInputs)>{}); } @@ -97,12 +133,12 @@ protected: private: template<class Tuple, size_t...Is> auto SetInputsImpl(Tuple& t, std::index_sequence<Is...>) { - return std::initializer_list<int>{(this->SetVInput<Is>(std::get<Is>(t)),0)...}; + return std::initializer_list<int>{(this->SetVariadicInput<Is>(std::get<Is>(t)),0)...}; } template <size_t...Is> auto GetInputsImpl(std::index_sequence<Is...>) { - return std::make_tuple(this->GetVInput<Is>()...); + return std::make_tuple(this->GetVariadicInput<Is>()...); } VariadicInputsImageFilter(const Self&) = delete; diff --git a/Modules/Core/Functor/test/otbFunctorImageFilter.cxx b/Modules/Core/Functor/test/otbFunctorImageFilter.cxx index df88b7a10821e9a8cf61585575c4274af79636f0..2effb2f18b85b43513446524a2ecf597fd083afa 100644 --- a/Modules/Core/Functor/test/otbFunctorImageFilter.cxx +++ b/Modules/Core/Functor/test/otbFunctorImageFilter.cxx @@ -40,31 +40,41 @@ template <typename T> struct TypesCheck using ScalarType = T; using ImageType = otb::Image<T>; using VectorType = itk::VariableLengthVector<ScalarType>; + using FixedArrayType = itk::FixedArray<ScalarType,4>; + using RGBPixelType = itk::RGBPixel<ScalarType>; + using RGBAPixelType = itk::RGBAPixel<ScalarType>; using VectorImageType = otb::VectorImage<ScalarType>; using NeighborhoodType = itk::Neighborhood<ScalarType>; using VectorNeighborhoodType = itk::Neighborhood<VectorType>; - + // Test IsNeighborhood struct - static_assert(!otb::IsNeighborhood<ScalarType>::value,""); - static_assert(!otb::IsNeighborhood<VectorType>::value,""); - static_assert(otb::IsNeighborhood<NeighborhoodType>::value,""); - static_assert(otb::IsNeighborhood<VectorNeighborhoodType>::value,""); - static_assert(otb::IsNeighborhood<const NeighborhoodType &>::value,""); - static_assert(otb::IsNeighborhood<const VectorNeighborhoodType &>::value,""); + template <typename U> struct CheckIsNeighborhood + { + static constexpr bool value = !otb::IsNeighborhood<U>::value && otb::IsNeighborhood<itk::Neighborhood<U> >::value; + }; + + static_assert(CheckIsNeighborhood<T>::value,""); + static_assert(CheckIsNeighborhood<itk::VariableLengthVector<T>>::value,""); + static_assert(CheckIsNeighborhood<itk::FixedArray<T,4>>::value,""); + static_assert(CheckIsNeighborhood<itk::RGBPixel<T>>::value,""); + static_assert(CheckIsNeighborhood<itk::RGBAPixel<T>>::value,""); // Test PixelTypeDeduction struct - static_assert(std::is_same<typename PixelTypeDeduction<ScalarType>::PixelType,ScalarType>::value,""); - static_assert(std::is_same<typename PixelTypeDeduction<VectorType>::PixelType,VectorType>::value,""); - static_assert(std::is_same<typename PixelTypeDeduction<NeighborhoodType>::PixelType,ScalarType>::value,""); - static_assert(std::is_same<typename PixelTypeDeduction<VectorNeighborhoodType>::PixelType,VectorType>::value,""); - static_assert(std::is_same<typename PixelTypeDeduction<const NeighborhoodType &>::PixelType,ScalarType>::value,""); - static_assert(std::is_same<typename PixelTypeDeduction<const VectorNeighborhoodType &>::PixelType,VectorType>::value,""); + template<typename U> struct CheckPixelTypeDeduction + { + static constexpr bool value = std::is_same<typename PixelTypeDeduction<U>::PixelType,U>::value + && std::is_same<typename PixelTypeDeduction<itk::Neighborhood<U>>::PixelType,U>::value; + }; + static_assert(CheckPixelTypeDeduction<T>::value,""); + static_assert(CheckPixelTypeDeduction<itk::VariableLengthVector<T>>::value,""); + static_assert(CheckPixelTypeDeduction<itk::FixedArray<T,4>>::value,""); + static_assert(CheckPixelTypeDeduction<itk::RGBPixel<T>>::value,""); + static_assert(CheckPixelTypeDeduction<itk::RGBAPixel<T>>::value,""); + // Test ImageTypeDeduction struct static_assert(std::is_same<typename ImageTypeDeduction<ScalarType>::ImageType,ImageType>::value,""); static_assert(std::is_same<typename ImageTypeDeduction<VectorType>::ImageType,VectorImageType>::value,""); - static_assert(std::is_same<typename ImageTypeDeduction<const ScalarType &>::ImageType,ImageType>::value,""); - static_assert(std::is_same<typename ImageTypeDeduction<const VectorType &>::ImageType,VectorImageType>::value,""); // Fake test operator template <typename TOut,typename TIn> struct TestOperator @@ -105,9 +115,9 @@ template <typename T> struct TypesCheck static_assert(FilterType::NumberOfInputs == 1,""); static_assert(std::is_same<typename FilterType::template InputImageType<0>, InputImageType>::value, ""); - filter->SetVInputs(in); + filter->SetVariadicInputs(in); filter->SetInput1(in); - filter->template SetVInput<0>(in); // template keyword to avoid C++ parse ambiguity + filter->template SetVariadicInput<0>(in); // template keyword to avoid C++ parse ambiguity filter->Update(); // Test named input version @@ -116,6 +126,16 @@ template <typename T> struct TypesCheck auto filter1 = NewFunctorFilter<decltype(functor),inputNames>(functor); filter1->template SetVNamedInput<tag>(in); filter1->Update(); + + // Test with simple lambda + auto lambda = [] (const TIn &) + { + TOut ret(1); + return ret; + }; + auto filterWithLambda = NewFunctorFilter(lambda,1, {{0,0}}); + filterWithLambda->SetVariadicInputs(in); + filterWithLambda->Update(); } TypesCheck() @@ -128,7 +148,8 @@ template <typename T> struct TypesCheck } }; -auto checksDouble = TypesCheck<double>{}; +auto checksInt = TypesCheck<int>{}; +auto checksDouble = TypesCheck<double>{}; auto checksComplex = TypesCheck<std::complex<double>>{}; // Example functors @@ -273,14 +294,14 @@ int otbFunctorImageFilter(int itkNotUsed(argc), char * itkNotUsed(argv) []) // Test VariadicInputsImageFilter auto filter = otb::VariadicInputsImageFilter<VectorImageType,VectorImageType,ImageType>::New(); - filter->SetVInput<0>(vimage); - filter->SetVInput<1>(image); + filter->SetVariadicInput<0>(vimage); + filter->SetVariadicInput<1>(image); filter->SetInput1(vimage); filter->SetInput2(image); - filter->SetVInputs(vimage,image); - std::cout<<filter->GetVInput<0>()<< filter->GetVInput<1>()<<std::endl; + filter->SetVariadicInputs(vimage,image); + std::cout<<filter->GetVariadicInput<0>()<< filter->GetVariadicInput<1>()<<std::endl; // Test VariadicNamedInputsImageFilter struct xs {}; @@ -301,7 +322,7 @@ int otbFunctorImageFilter(int itkNotUsed(argc), char * itkNotUsed(argv) []) return scale*p; }; auto filterLambda = NewFunctorFilter(Lambda1); - filterLambda->SetVInputs(image); + filterLambda->SetVariadicInputs(image); filterLambda->Update(); // test FunctorImageFilter with a lambda that returns a @@ -321,13 +342,13 @@ int otbFunctorImageFilter(int itkNotUsed(argc), char * itkNotUsed(argv) []) // In this case, we use the helper function which allows to specify // the number of outputs auto filterLambda2 = NewFunctorFilter(Lambda2,vimage->GetNumberOfComponentsPerPixel(),{{3,3}}); - filterLambda2->SetVInputs(image); + filterLambda2->SetVariadicInputs(image); filterLambda2->Update(); // Test FunctorImageFilter with the VariadicConcatenate operator using ConcatFunctorType = Functor::VariadicConcatenate<double, double, itk::VariableLengthVector<double> >; auto concatenate = NewFunctorFilter(ConcatFunctorType{}); - concatenate->SetVInputs(image,vimage); + concatenate->SetVariadicInputs(image,vimage); concatenate->Update(); // Test FunctorImageFilter With VariadicAdd functor @@ -335,37 +356,36 @@ int otbFunctorImageFilter(int itkNotUsed(argc), char * itkNotUsed(argv) []) auto add = NewFunctorFilter(AddFunctorType{}); add->SetVInput<0>(image); add->SetVInput<1>(image); - add->SetVInputs(image,image); add->Update(); // Test FunctorImageFilter with BandExtraction functor using ExtractFunctorType = BandExtraction<double,double>; ExtractFunctorType extractFunctor{1,2}; auto extract = NewFunctorFilter(extractFunctor); - extract->SetVInputs(vimage); + extract->SetVariadicInputs(vimage); extract->Update(); // Test FunctorImageFilter With Mean functor using MeanFunctorType = Mean<double,double>; auto median = NewFunctorFilter(MeanFunctorType{},{{2,2}}); - median->SetVInputs(image); + median->SetVariadicInputs(image); median->Update(); // Test FunctorImageFilter with MaxInEachChannel using MaxInEachChannelType = MaxInEachChannel<double>; auto maxInEachChannel = NewFunctorFilter(MaxInEachChannelType{},{{3,3}}); - maxInEachChannel->SetVInputs(vimage); + maxInEachChannel->SetVariadicInputs(vimage); maxInEachChannel->Update(); // Test FunctorImageFilter with Module (complex= using ModulusType = VectorModulus<double>; auto modulus = NewFunctorFilter(ModulusType{}); - modulus->SetVInputs(cvimage); + modulus->SetVariadicInputs(cvimage); modulus->Update(); auto LambdaComplex = [] (const std::complex<double> & in) {return std::arg(in);}; auto argFilter = NewFunctorFilter(LambdaComplex); - argFilter->SetVInputs(cimage); + argFilter->SetVariadicInputs(cimage); argFilter->Update(); return EXIT_SUCCESS;