diff --git a/Modules/Filtering/ImageManipulation/include/otbFunctorImageFilter.h b/Modules/Filtering/ImageManipulation/include/otbFunctorImageFilter.h index b7a5dfbdcd7160a7cc5b484f395c61663b9bfc7b..9125660e60424c20423c71106a5a91a0aac35d62 100644 --- a/Modules/Filtering/ImageManipulation/include/otbFunctorImageFilter.h +++ b/Modules/Filtering/ImageManipulation/include/otbFunctorImageFilter.h @@ -26,6 +26,7 @@ #include "otbImage.h" #include "otbVectorImage.h" #include "itkConstNeighborhoodIterator.h" +#include "itkImageScanlineIterator.h" #include "itkImageRegionConstIterator.h" #include "itkProcessObject.h" #include <type_traits> @@ -37,16 +38,76 @@ namespace otb namespace functor_filter_details { +// Variadic SetRequestedRegion +template<class T> int SetInputRequestedRegion(const T * img, const itk::ImageRegion<2> & region, const itk::Size<2>& radius) +{ + auto currentRegion = region; + currentRegion.PadByRadius(radius); + + // The ugly cast in all ITK filters + T * nonConstImg = const_cast<T*>(img); + + if(currentRegion.Crop(img->GetLargestPossibleRegion())) + { + nonConstImg->SetRequestedRegion(currentRegion); + return 0; + } + else + { + nonConstImg->SetRequestedRegion(currentRegion); + + // build an exception + itk::InvalidRequestedRegionError e(__FILE__, __LINE__); + std::ostringstream msg; + msg << "::SetInputRequestedRegion<>()"; + e.SetLocation(msg.str()); + e.SetDescription("Requested region is (at least partially) outside the largest possible region."); + e.SetDataObject(nonConstImg); + throw e; + } + + return 0; +} + +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)...); +} + +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); +} + + // Variadic creation of iterator tuple -template <class T> auto MakeIterator(itk::SmartPointer<T> img) +template <class T> auto MakeIterator(const T * img, const itk::ImageRegion<2> & region) +{ + itk::ImageRegionConstIterator<T> it(img,region); + return it; +} + +template <class T> auto MakeIterator(itk::SmartPointer<T> img, const itk::ImageRegion<2> & region) +{ + itk::ImageRegionConstIterator<T> it(img,region); + return it; +} + +template <class T> auto MakeIterator(itk::SmartPointer<const T> img, const itk::ImageRegion<2> & region) { - itk::ImageRegionConstIterator<T> it(img,img->GetLargestPossibleRegion()); + itk::ImageRegionConstIterator<T> it(img,region); return it; } -template<class... T> auto MakeIterators(itk::SmartPointer<T>... args) + +template <class Tuple, size_t...Is> auto MakeIteratorsImpl(const Tuple& t, const itk::ImageRegion<2> & region, std::index_sequence<Is...>) +{ + return std::make_tuple(MakeIterator(std::get<Is>(t),region)...); +} + +template<typename... T> auto MakeIterators(std::tuple<T...> &&t, const itk::ImageRegion<2> & region) { - return std::make_tuple(MakeIterator(args)...); + return MakeIteratorsImpl(t,region,std::make_index_sequence<sizeof...(T)>{}); } // Variadic call of operator from iterator tuple @@ -73,14 +134,6 @@ template<typename ... Args> void MoveIterators(std::tuple<Args...> & t) } // end namespace functor_filter_details -template<typename T> -using FTraits = typename FunctionTraits::function_traits<T>; - -template<typename T> -using FResultType = typename FTraits<T>::result_type; - -template<typename T, size_t i> -using ArgType = typename FTraits<T>::template arg<i>::type; template <class T> struct IsNeighborhood { @@ -142,6 +195,24 @@ template <typename C, typename R, typename... T> struct FunctorFilterSuperclassH using FilterType = VariadicInputsImageFilter<OutputImageType,typename TInputImage<T>::ImageType...>; }; +// Default implementation does nothing +template <class F, class O, class cond = void> struct NumberOfOutputComponents +{ + // We can not be here if output type is VectorImage + //static_assert(std::is_same<O, otb::VectorImage<typename O::InternalPixelType> >::value,"Return type of Functor is a VariableLenghtVector, add a constexpr size_t OutputSize member"); + static void Set(const F&, O *){} +}; + +// Case 1: O is a VectorImage AND F has a fixed OuptutSize unsigned +// int constexpr +template <class F, class T> struct NumberOfOutputComponents<F,T,typename std::enable_if<std::is_same<size_t, decltype(F::OutputSize)>::value>::type > +{ +static void Set(const F &, otb::VectorImage<T> * outputImage) + { + std::cout<<"Use OutputSize to set number of output components"<<std::endl; + outputImage->SetNumberOfComponentsPerPixel(F::OutputSize); + } +}; /** \class FunctorImageFilter * \brief Implements @@ -163,17 +234,6 @@ public: using Pointer = itk::SmartPointer<Self>; using ConstPointer = itk::SmartPointer<const Self>; - // using InputImageType = typename TInputImage<TFunction,0>::ImageType; - // using InputImagePointer = typename InputImageType::ConstPointer; - // using InputImageRegionType = typename InputImageType::RegionType; - // using InputImagePixelType = typename InputImageType::PixelType; - // using InputImageSizeType = typename InputImageType::SizeType; - // using InputImageIndexType = typename InputImageType::IndexType; - - // using OutputImageType = typename TOutputImage<TFunction>::ImageType; - // using OutputImagePointer = typename OutputImageType::Pointer; - // using OutputImageRegionType = typename OutputImageType::RegionType; - // using OutputImagePixelType = typename OutputImageType::PixelType; using Superclass = typename FunctorFilterSuperclassHelper<TFunction>::FilterType; using OutputImageType = typename Superclass::OutputImageType; using OutputImageRegionType = typename OutputImageType::RegionType; @@ -246,7 +306,8 @@ protected: */ virtual void GenerateInputRequestedRegion(void) override; - + virtual void GenerateOutputInformation() override; + FunctorType m_Functor; }; diff --git a/Modules/Filtering/ImageManipulation/include/otbFunctorImageFilter.hxx b/Modules/Filtering/ImageManipulation/include/otbFunctorImageFilter.hxx index fa05faf502fabe89bbc420b00e4d8db224ed20e9..e8b7808c82d640b3d06de7e015ed50ade77fc877 100644 --- a/Modules/Filtering/ImageManipulation/include/otbFunctorImageFilter.hxx +++ b/Modules/Filtering/ImageManipulation/include/otbFunctorImageFilter.hxx @@ -50,6 +50,20 @@ FunctorImageFilter<TFunction> { // call the superclass' implementation of this method Superclass::GenerateInputRequestedRegion(); + + // Get requested region for output + typename Superclass::OutputImagePointer outputPtr = this->GetOutput(); + auto requestedRegion = outputPtr->GetRequestedRegion(); + + // Propagate to each variadic inputs, including possible radius + functor_filter_details::SetInputRequestedRegions(this->GetVInputs(),requestedRegion, {{0,0}}); +} + +template <class TFunction> +void +FunctorImageFilter<TFunction>::GenerateOutputInformation() +{ + NumberOfOutputComponents<TFunction,OutputImageType>::Set(m_Functor,this->GetOutput()); } /** @@ -60,8 +74,23 @@ void FunctorImageFilter<TFunction> ::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, itk::ThreadIdType threadId) { - (void) outputRegionForThread; - (void) threadId; + + + itk::ImageScanlineIterator<OutputImageType> outIt(this->GetOutput(),outputRegionForThread); + itk::ProgressReporter p(this,threadId,outputRegionForThread.GetNumberOfPixels()); + + auto inputIterators = functor_filter_details::MakeIterators(this->GetVInputs(),outputRegionForThread); + + while(!outIt.IsAtEnd()) + { + for(;!outIt.IsAtEndOfLine();++outIt,functor_filter_details::MoveIterators(inputIterators)) + { + outIt.Set(functor_filter_details::CallOperator(m_Functor,inputIterators)); + // Update progress + p.CompletedPixel(); + } + outIt.NextLine(); + } } } // end namespace otb diff --git a/Modules/Filtering/ImageManipulation/include/otbVariadicInputsImageFilter.h b/Modules/Filtering/ImageManipulation/include/otbVariadicInputsImageFilter.h index ecc6269c632814078ed3ca25947818a5049a7734..5c02f97177c27b09e8c758c36b85d6f8758dacaa 100644 --- a/Modules/Filtering/ImageManipulation/include/otbVariadicInputsImageFilter.h +++ b/Modules/Filtering/ImageManipulation/include/otbVariadicInputsImageFilter.h @@ -52,6 +52,11 @@ public: SetInputsImpl(inTuple,std::make_index_sequence<sizeof...(inputs)>{}); } + auto GetVInputs() + { + return GetInputsImpl(std::make_index_sequence<sizeof...(TInputs)>{}); + } + protected: VariadicInputsImageFilter() { @@ -65,6 +70,11 @@ private: { return std::initializer_list<int>{(this->SetVInput<Is>(std::get<Is>(t)),0)...}; } + + template <size_t...Is> auto GetInputsImpl(std::index_sequence<Is...>) + { + return std::make_tuple(this->GetVInput<Is>()...); + } VariadicInputsImageFilter(const Self&) = delete; void operator=(const Self&) = delete; diff --git a/Modules/Filtering/ImageManipulation/test/otbFunctorImageFilter.cxx b/Modules/Filtering/ImageManipulation/test/otbFunctorImageFilter.cxx index 49986b1e5a0e72fd512bec0acb813bb19e69462f..466fc7d0c85353c541ba98e05e6c0a2781cc3904 100644 --- a/Modules/Filtering/ImageManipulation/test/otbFunctorImageFilter.cxx +++ b/Modules/Filtering/ImageManipulation/test/otbFunctorImageFilter.cxx @@ -32,7 +32,7 @@ struct Funct1 { - double operator()(double p) + double operator()(double p) const { return p; } @@ -42,16 +42,18 @@ struct Funct2 { itk::VariableLengthVector<double> operator()(double p) const { - itk::VariableLengthVector<double> result(2); + itk::VariableLengthVector<double> result(OutputSize); result[0] = result[1] = p; return result; } + + static constexpr size_t OutputSize = 2; }; using IntImageNeighborhood = itk::Neighborhood<int>; struct Funct3 { - double operator()(const IntImageNeighborhood & p) + double operator()(const IntImageNeighborhood & p) const { return static_cast<double>(p.GetCenterValue()); } @@ -67,7 +69,7 @@ struct Funct4 itk::VariableLengthVector<double> operator()(double p, const itk::VariableLengthVector<double> & vp) const { itk::VariableLengthVector<double> result(2); - result[0] = result[1] = p; + result.Fill(p); return result; } }; @@ -87,7 +89,7 @@ int otbFunctorImageFilter(int itkNotUsed(argc), char * itkNotUsed(argv) []) { // test functions in functor_filter_details namespace using VectorImageType = VectorImage<double>; - using ImageType = Image<unsigned int>; + using ImageType = Image<double>; using RegionType = typename ImageType::RegionType; using SizeType = typename RegionType::SizeType; using IndexType = typename RegionType::IndexType; @@ -98,12 +100,20 @@ int otbFunctorImageFilter(int itkNotUsed(argc), char * itkNotUsed(argv) []) auto vimage = VectorImageType::New(); auto image = ImageType::New(); - SizeType size = {200,200}; + SizeType size = {{200,200}}; vimage->SetRegions(size); + vimage->SetNumberOfComponentsPerPixel(2); + vimage->Allocate(); + itk::VariableLengthVector<double> v(2); + v.Fill(0); + vimage->FillBuffer(v); + image->SetRegions(size); + image->Allocate(); + image->FillBuffer(0); - auto iterators = MakeIterators(image,vimage); + auto iterators = MakeIterators(std::make_tuple(image,vimage),image->GetBufferedRegion()); MoveIterators(iterators); @@ -126,8 +136,23 @@ int otbFunctorImageFilter(int itkNotUsed(argc), char * itkNotUsed(argv) []) // test FunctorImageFilter auto filter1 = otb::FunctorImageFilter<Funct1>::New(Funct1{}); auto filter2 = otb::FunctorImageFilter<Funct2>::New(Funct2{}); - auto filter3 = otb::FunctorImageFilter<Funct3>::New(Funct3{}); - auto filter4 = otb::FunctorImageFilter<decltype(Lambda1)>::New(Lambda1); + //auto filter3 = otb::FunctorImageFilter<Funct3>::New(Funct3{}); + auto filter4 = otb::FunctorImageFilter<Funct4>::New(Funct4{}); + auto filterLambda = otb::FunctorImageFilter<decltype(Lambda1)>::New(Lambda1); + + filter1->SetVInputs(image); + filter1->Update(); + + filter2->SetVInputs(image); + filter2->Update(); + + filter4->SetVInputs(image,vimage); + filter4->Update(); + + filterLambda->SetVInputs(image); + filterLambda->Update(); + + return EXIT_SUCCESS; }