diff --git a/Modules/Core/Functor/CMakeLists.txt b/Modules/Core/Functor/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..b22e90e314ec765d319649b8fca0eb9220d835fc --- /dev/null +++ b/Modules/Core/Functor/CMakeLists.txt @@ -0,0 +1,22 @@ +# +# Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) +# +# This file is part of Orfeo Toolbox +# +# https://www.orfeo-toolbox.org/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +project(OTBFunctor) +otb_module_impl() diff --git a/Modules/Core/Functor/include/otbFunctorImageFilter.h b/Modules/Core/Functor/include/otbFunctorImageFilter.h new file mode 100644 index 0000000000000000000000000000000000000000..7aeb4e7efc3f9ae35d00bb1eb2c3151f5f3d0640 --- /dev/null +++ b/Modules/Core/Functor/include/otbFunctorImageFilter.h @@ -0,0 +1,459 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef otbFunctorImageFilter_h +#define otbFunctorImageFilter_h + +#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 + * + * Provides: + * - ValueType type set to false_type or true_type + * - value set to true or false + */ +template <class T> struct IsNeighborhood +{ + using ValueType = std::false_type; + static constexpr bool value = false; +}; + +/// Partial specialisation for itk::Neighborhood<T> +template <class T> struct IsNeighborhood<itk::Neighborhood<T>> +{ + using ValueType = std::true_type; + static constexpr bool value = true; +}; + + +/// Partial specialisation for const itk::Neighborhood<T> & +template <class T> struct IsNeighborhood<const itk::Neighborhood<T>&> +{ + using ValueType = std::true_type; + 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 + * \brief Helper struct to derive PixelType from template parameter. + * + * T -> PixelType = T + * itk::Neighborhood<T> -> PixelType = T + * const itk::Neighborhood<T>& -> PixelType = 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; +}; + +/// Discard const qualifier +template <class T> struct PixelTypeDeduction<const 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 + * + * T -> ImageType = otb::Image<T> + * itk::VariableLengthVector<T> -> ImageType = otb::VectorImage<T> + * const T & -> ImageType = ImageTypeDeduction<T>::ImageType + */ +template <class T> struct ImageTypeDeduction +{ + using ImageType = otb::Image<T>; +}; + +/// Partial specialisation for itk::VariableLengthVector<T> +template <class T> struct ImageTypeDeduction<itk::VariableLengthVector<T>> +{ + using ImageType = otb::VectorImage<T>; +}; + +/** +* \struct FunctorFilterSuperclassHelper +* \brief Struct allowing to derive the superclass prototype for the +* FunctorImageFilter class +* +* Provides the following: +* - OutputImageType : type of the output image +* - FilterType : correct instanciation of VariadicInputsImageFilter from +* - the operator() prototype +* - InputHasNeighborhood a tuple of N false_type or true_type to denote +* - if Ith arg of operator() expects a neighborhood. +*/ + + +template <typename T, typename TNameMap> struct FunctorFilterSuperclassHelper : public FunctorFilterSuperclassHelper<decltype(&T::operator()),TNameMap> {}; + +namespace functor_filter_details +{ +template <typename R, typename TNameMap, typename...T> struct FunctorFilterSuperclassHelperImpl +{ + // OutputImageType is derived from return type R + using OutputImageType = typename ImageTypeDeduction<R>::ImageType; + // InputImageType is derived using pixel type deduction and image + // type deduction + template <typename V> using InputImageType = typename ImageTypeDeduction<typename PixelTypeDeduction<V>::PixelType>::ImageType; + + // Filter type is either VariadicInputsImageFilter or + // VariadicNamedInputsImageFilter depending on if there is a + // TNameMap or not + using FilterType = typename std::conditional<std::is_void<TNameMap>::value, + VariadicInputsImageFilter<OutputImageType,InputImageType<T>...>, + VariadicNamedInputsImageFilter<OutputImageType,TNameMap,InputImageType<T>...>>::type; + + // InputHasNeighborhood is derived from IsNeighborhood + using InputHasNeighborhood = std::tuple<typename IsNeighborhood<T>::ValueType...>; +}; +} // End namespace functor_filter_details + +/// Partial specialisation for R(*)(T...) +template <typename R, typename... T, typename TNameMap> struct FunctorFilterSuperclassHelper<R(*)(T...),TNameMap> +{ + using OutputImageType = typename functor_filter_details::FunctorFilterSuperclassHelperImpl<R,TNameMap,T...>::OutputImageType; + using FilterType = typename functor_filter_details::FunctorFilterSuperclassHelperImpl<R,TNameMap,T...>::FilterType; + using InputHasNeighborhood = typename functor_filter_details::FunctorFilterSuperclassHelperImpl<R,TNameMap,T...>::InputHasNeighborhood; +}; + +/// 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 functor_filter_details::FunctorFilterSuperclassHelperImpl<R,TNameMap,T...>::OutputImageType; + using FilterType = typename functor_filter_details::FunctorFilterSuperclassHelperImpl<R,TNameMap,T...>::FilterType; + using InputHasNeighborhood = typename functor_filter_details::FunctorFilterSuperclassHelperImpl<R,TNameMap,T...>::InputHasNeighborhood; + +}; + +/// Partial specialisation for R(C::*)(T...) +template <typename C, typename R, typename... T, typename TNameMap> struct FunctorFilterSuperclassHelper<R(C::*)(T...), TNameMap> +{ + using OutputImageType = typename functor_filter_details::FunctorFilterSuperclassHelperImpl<R,TNameMap,T...>::OutputImageType; + using FilterType = typename functor_filter_details::FunctorFilterSuperclassHelperImpl<R,TNameMap,T...>::FilterType; + using InputHasNeighborhood = typename functor_filter_details::FunctorFilterSuperclassHelperImpl<R,TNameMap,T...>::InputHasNeighborhood; + +}; + +/// Partial specialisation for void(*)(R &,T...) +template <typename R, typename... T, typename TNameMap> struct FunctorFilterSuperclassHelper<void(*)(R&, T...), TNameMap> +{ + using OutputImageType = typename functor_filter_details::FunctorFilterSuperclassHelperImpl<R,TNameMap,T...>::OutputImageType; + using FilterType = typename functor_filter_details::FunctorFilterSuperclassHelperImpl<R,TNameMap,T...>::FilterType; + using InputHasNeighborhood = typename functor_filter_details::FunctorFilterSuperclassHelperImpl<R,TNameMap,T...>::InputHasNeighborhood; + +}; + +/// Partial specialisation for void(C::*)(R&,T...) const +template <typename C, typename R, typename... T, typename TNameMap> struct FunctorFilterSuperclassHelper<void(C::*)(R&,T...) const, TNameMap> +{ + using OutputImageType = typename functor_filter_details::FunctorFilterSuperclassHelperImpl<R,TNameMap,T...>::OutputImageType; + using FilterType = typename functor_filter_details::FunctorFilterSuperclassHelperImpl<R,TNameMap,T...>::FilterType; + using InputHasNeighborhood = typename functor_filter_details::FunctorFilterSuperclassHelperImpl<R,TNameMap,T...>::InputHasNeighborhood; + +}; + +/// Partial specialisation for void(C::*)(R&,T...) +template <typename C, typename R, typename... T, typename TNameMap> struct FunctorFilterSuperclassHelper<void(C::*)(R&,T...), TNameMap> +{ + using OutputImageType = typename functor_filter_details::FunctorFilterSuperclassHelperImpl<R,TNameMap,T...>::OutputImageType; + using FilterType = typename functor_filter_details::FunctorFilterSuperclassHelperImpl<R,TNameMap,T...>::FilterType; + using InputHasNeighborhood = typename functor_filter_details::FunctorFilterSuperclassHelperImpl<R,TNameMap,T...>::InputHasNeighborhood; +}; + + + +/** + * \brief This helper method builds a fully functional FunctorImageFilter from a functor instance + * + * Functor can be any operator() (const or non-const) that matches the following: + * - Accepts any number of arguments of T, + * (const) itk::VariableLengthVector<T> (&),(const) + * itk::Neighborhood<T> (&), (const) + * itk::Neighborhood<itk::VariableLengthVector<T>> (&) with T a scalar type + * - returns T or itk::VariableLengthVector<T>, with T a scalar type + * or returns void and has first parameter as output (i.e. T& or itk::VariableLengthVector<T>&) + * + * The returned filter is ready to use. Inputs can be set through the + * SetVariadicInputs() method (see VariadicInputsImageFilter class for + * details) + * + * \param f the Functor to build the filter from + * \param radius The size of neighborhood to use, if there is any + * itk::Neighborhood<T> in the operator() arguments. + * \return A ready to use OTB filter, which accepts n input image of + * type derived from the operator() arguments, and producing an image + * correpsonding to the operator() return type. + * + * Note that this function also works with a lambda as Functor, + * provided it returns a scalar type. If your lambda returns a + * VariableLengthVector, see the other NewFunctorFilter implementation. + */ +template <typename Functor, typename TNameMap = void> auto NewFunctorFilter(const Functor& f, itk::Size<2> radius = {{0,0}}); + + +/** \class FunctorImageFilter + * \brief A generic functor filter templated by its functor + * + * TFunction can be any operator() (const or non-const) that matches the following: + * - Accepts any number of arguments of T, + * (const) itk::VariableLengthVector<T> (&),(const) + * itk::Neighborhood<T> (&), (const) + * itk::Neighborhood<itk::VariableLengthVector<T>> (&) with T a scalar type + * - returns T or itk::VariableLengthVector<T>, with T a scalar type + * or returns void and has first parameter as output (i.e. T& or itk::VariableLengthVector<T>&) + * + * All image types will be deduced from the TFunction operator(). + * + * \sa VariadicInputsImageFilter + * \sa NewFunctorFilter + * + * \ingroup IntensityImageFilters Multithreaded Streamed + * + * \ingroup OTBFunctor +*/ +template <class TFunction, class TNameMap = void> + class ITK_EXPORT FunctorImageFilter + : public FunctorFilterSuperclassHelper<TFunction, TNameMap>::FilterType +{ + +public: + // Standard typedefs + using Self = FunctorImageFilter; + using FunctorType = TFunction; + using Pointer = itk::SmartPointer<Self>; + using ConstPointer = itk::SmartPointer<const Self>; + + // Superclass through the helper struct + using SuperclassHelper = FunctorFilterSuperclassHelper<TFunction,TNameMap>; + using Superclass = typename SuperclassHelper::FilterType; + using OutputImageType = typename Superclass::OutputImageType; + using OutputImageRegionType = typename OutputImageType::RegionType; + + // A tuple of bool of the same size as the number of arguments in + // the functor + using InputHasNeighborhood = typename SuperclassHelper::InputHasNeighborhood; + using InputTypesTupleType = typename Superclass::InputTypesTupleType; + template<size_t I> using InputImageType = typename Superclass::template InputImageType<I>; + using Superclass::NumberOfInputs; + + /** Run-time type information (and related methods). */ + itkTypeMacro(FunctorImageFilter, VariadicInputsImageFilter); + + /** Get the functor object. + * + * \return A non-const reference to the Functor in use. Note that + * this call will call the Modified() function of the filter, which + * will trigger a full output computation upon Update(). For + * inspection only, prefer using GetFunctor() + */ + FunctorType& GetModifiableFunctor() + { + this->Modified(); + return m_Functor; + } + + /** Get the functor object. + * + * \return A const reference to the Functor in use. For a + * modifiable version of the functor, see GetModifiableFunctor(). + */ + const FunctorType& GetFunctor() const + { + return m_Functor; + } + +protected: + /// Constructor of functor filter, will copy the functor + FunctorImageFilter(const FunctorType& f, itk::Size<2> radius) : m_Functor(f), m_Radius(radius) {}; + FunctorImageFilter(const Self &) = delete; + void operator =(const Self&) = delete; + ~FunctorImageFilter() = default; + +private: + /// Actual creation of the filter is handled by this free function + friend auto NewFunctorFilter<TFunction,TNameMap>(const TFunction& f, itk::Size<2> radius); + + /** Overload of ThreadedGenerateData */ + void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, itk::ThreadIdType threadId) override; + + /** + * Pad the input requested region by radius + */ + void GenerateInputRequestedRegion(void) override; + + /** + * Will use the OutputSize() method of the functor if output is VectorImage + */ + void GenerateOutputInformation() override; + + + // The functor member + FunctorType m_Functor; + + // Radius if needed + itk::Size<2> m_Radius; +}; + +// Actual implementation of NewFunctorFilter free function +template <typename Functor, typename TNameMap> auto NewFunctorFilter(const Functor& f, itk::Size<2> radius) +{ + using FilterType = FunctorImageFilter<Functor,TNameMap>; + using PointerType = typename FilterType::Pointer; + + PointerType p = new FilterType(f,radius); + p->UnRegister(); + return p; +} + +/** + * \struct NumberOfOutputBandsDecorator + * \brief This struct allows to forward the operator of template + * parameter, while adding number of ouptut components service. + * + * Its purpose is to enable the use of lambda or functor witht + * Outputsize() method with FunctorImageFilter. + * + * It is used internally in NewFunctorFilter version with + * numberOfOutputBands parameter. + */ + +template <typename F> struct NumberOfOutputBandsDecorator : F +{ +public: + constexpr NumberOfOutputBandsDecorator(const F t, unsigned int nbComp) : F(t), m_NumberOfOutputBands(nbComp) {} + using F::operator(); + + constexpr size_t OutputSize(...) const + { + return m_NumberOfOutputBands; + } + +private: + unsigned int m_NumberOfOutputBands; +}; + +/** + * brief This helper method builds a fully functional + * FunctorImageFilter from a functor instance which does not provide + * the OutputSize() service, or a lambda, returing a VariableLengthVector + * + * \param f the Functor to build the filter from + * \param numberOfOutputBands The number of output bands that + * this filter will return + * \param radius The size of neighborhood to use, if there is any + * itk::Neighborhood<T> in the operator() arguments. + * \return A ready to use OTB filter, which accepts n input image of + * type derived from the operator() arguments, and producing an image + * correpsonding to the operator() return type. + * + * Note that this function also works with a lambda as Functor, + * provided it returns a scalar type. If your lambda returns a + * VariableLengthVector, see the other NewFunctorFilter implementation. + + */ + +template <typename Functor, typename TNameMap = void> auto NewFunctorFilter(const Functor& f, unsigned int numberOfOutputBands, itk::Size<2> radius) +{ + using FunctorType = NumberOfOutputBandsDecorator<Functor>; + FunctorType decoratedF(f,numberOfOutputBands); + return NewFunctorFilter<FunctorType,TNameMap>(decoratedF,radius); +} + + +}// namespace otb + +#ifndef OTB_MANUAL_INSTANTIATION +#include "otbFunctorImageFilter.hxx" +#endif + +#endif diff --git a/Modules/Core/Functor/include/otbFunctorImageFilter.hxx b/Modules/Core/Functor/include/otbFunctorImageFilter.hxx new file mode 100644 index 0000000000000000000000000000000000000000..42a93c2f2eb420075a5e7b989366ab5ea94ea936 --- /dev/null +++ b/Modules/Core/Functor/include/otbFunctorImageFilter.hxx @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef otbFunctorImageFilter_hxx +#define otbFunctorImageFilter_hxx + +#include "otbFunctorImageFilter.h" +#include "itkProgressReporter.h" +#include "itkConstNeighborhoodIterator.h" +#include "itkImageRegionConstIterator.h" +#include "itkImageScanlineIterator.h" + +namespace otb +{ +namespace functor_filter_details +{ +// Variadic SetRequestedRegion + +// This function sets the requested region for one image +template<class T> int SetInputRequestedRegion(const T * img, const itk::ImageRegion<2> & region, const itk::Size<2>& radius, bool pad) +{ + assert(img&&"Input image is a nullptr"); + + auto currentRegion = region; + + // Hopefully this will be optimized out by compiler + if(pad) + 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__); + e.SetLocation("::SetInputRequestedRegion<>()"); + e.SetDescription("Requested region is (at least partially) outside the largest possible region."); + e.SetDataObject(nonConstImg); + throw e; + } +} + +// Will be easier to write in c++17 with std::apply and fold expressions +template <typename HasNeighborhood, 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,typename std::tuple_element<Is,HasNeighborhood>::type::value_type())...); +} + +// Will be easier to write in c++17 with std::apply and fold expressions +template <typename HasNeighborhood,typename... T> auto SetInputRequestedRegions(std::tuple<T...> && t,const itk::ImageRegion<2> & region, const itk::Size<2> & radius) +{ + return SetInputRequestedRegionsImpl<HasNeighborhood>(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)>{}); +} + +template <typename N> struct MakeIterator {}; + +template <> struct MakeIterator<std::false_type> +{ + template <class T> static auto Make(const T * img, const itk::ImageRegion<2> & region, const itk::Size<2>&) + { + itk::ImageRegionConstIterator<T> it(img,region); + return it; + } +}; + +template <> struct MakeIterator<std::true_type> +{ + template <class T> static auto Make(const T * img, const itk::ImageRegion<2> & region, const itk::Size<2>& radius) + { + itk::ConstNeighborhoodIterator<T> it(radius,img,region); + return it; + } +}; + +// 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); + } + +// Variadic call of operator from iterator tuple +template <typename T> struct GetProxy{}; + + +template <typename T> struct GetProxy<itk::ImageRegionConstIterator<T> > +{ + static decltype(auto) Get(const itk::ImageRegionConstIterator<T> & t) +{ + return t.Get(); +} +}; + +template <typename T> struct GetProxy<itk::ConstNeighborhoodIterator<T> > +{ + static decltype(auto) Get(const itk::ConstNeighborhoodIterator<T> & t) +{ + return t.GetNeighborhood(); +} +}; + +template <class Oper> struct OperProxy : public OperProxy<decltype(&Oper::operator())> {}; + +template<class Out, class ... In> struct OperProxy<Out(*)(In...)> +{ + template <class Oper> static void Compute(Oper& oper, Out& out, const In& ... in) + { + out = oper(in...); + } +}; + +template<class C, class Out, class ... In> struct OperProxy<Out(C::*)(In...)> +{ + template<class Oper> static void Compute(Oper& oper, Out& out, const In& ... in) + { + out = oper(in...); + } +}; + +template<class C, class Out, class ... In> struct OperProxy<Out(C::*)(In...) const> +{ + template<class Oper> static void Compute(Oper& oper, Out& out, const In& ... in) + { + out = oper(in...); + } +}; + +template<class Out, class ... In> struct OperProxy<void(*)(Out&, In...)> +{ + template<class Oper> static void Compute(Oper& oper, Out& out, const In& ... in) + { + oper(out,in...); + } +}; + +template<class C, class Out, class ... In> struct OperProxy<void(C::*)(Out&, In...)> +{ + template<class Oper> static void Compute(Oper& oper, Out& out, const In& ... in) + { + oper(out,in...); + } +}; + +template<class C, class Out, class ... In> struct OperProxy<void(C::*)(Out&, In...) const> +{ + template template<class Oper> static void Compute(Oper& oper, Out& out, const In& ... in) + { + oper(out,in...); + } +}; + + +// Will be easier to write in c++17 with std::apply and fold expressions +template <class Tuple, class Out, class Oper, size_t...Is> auto CallOperatorImpl(Tuple& t, Out & out, Oper & oper,std::index_sequence<Is...>) +{ + OperProxy<Oper>::Compute(oper,out,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 Out, class Oper, typename ... Args> auto CallOperator(Out & out, Oper& oper, std::tuple<Args...> & t) +{ + CallOperatorImpl(t,out,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) )...); +} + +template<typename ... Args> void MoveIterators(std::tuple<Args...> & t) +{ + MoveIteratorsImpl(t,std::make_index_sequence<sizeof...(Args)>{}); +} + + +// Default implementation does nothing +template <class F, class O, size_t N> struct NumberOfOutputComponents +{}; + +template <class F, class T, size_t N> struct NumberOfOutputComponents<F,otb::Image<T>,N> + { + // We can not be here if output type is VectorImage + static void Set(const F&, otb::Image<T> *, std::array<size_t,N>){} +}; + +// O is a VectorImage AND F has a fixed OuptutSize static constexrp size_t; +template <class F, class T, size_t N> struct NumberOfOutputComponents<F,otb::VectorImage<T>,N> +{ + static void Set(const F & f, otb::VectorImage<T> * outputImage, std::array<size_t,N> inNbBands) + { + outputImage->SetNumberOfComponentsPerPixel(f.OutputSize(inNbBands)); + } +}; + +} // end namespace functor_filter_details + +template <class TFunction, class TNameMap> +void +FunctorImageFilter<TFunction, TNameMap> +::GenerateInputRequestedRegion() +{ + // Get requested region for output + typename Superclass::OutputImagePointer outputPtr = this->GetOutput(); + auto requestedRegion = outputPtr->GetRequestedRegion(); + + // 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<InputHasNeighborhood>(this->GetVariadicInputs(),requestedRegion, m_Radius); +} + +template <class TFunction, class TNameMap> +void +FunctorImageFilter<TFunction, TNameMap>::GenerateOutputInformation() +{ + // Call Superclass implementation + Superclass::GenerateOutputInformation(); + + // Get All variadic inputs + auto inputs = this->GetVariadicInputs(); + + // Retrieve an array of number of components per input + auto inputNbComps = functor_filter_details::GetNumberOfComponentsPerInput(inputs); + + // Call the helper to set the number of components for the output image + functor_filter_details::NumberOfOutputComponents<TFunction,OutputImageType,inputNbComps.size()>::Set(m_Functor,this->GetOutput(),inputNbComps); +} + +/** + * ThreadedGenerateData Performs the neighborhood-wise operation + */ +template <class TFunction, class TNameMap> +void +FunctorImageFilter<TFunction, TNameMap> +::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, itk::ThreadIdType threadId) +{ + // Build output iterator + itk::ImageScanlineIterator<OutputImageType> outIt(this->GetOutput(),outputRegionForThread); + itk::ProgressReporter p(this,threadId,outputRegionForThread.GetNumberOfPixels()); + + // This will build a tuple of iterators to be used + auto inputIterators = functor_filter_details::MakeIterators(this->GetVariadicInputs(),outputRegionForThread, m_Radius,InputHasNeighborhood{}); + + // Build a default value + typename OutputImageType::PixelType outputValueHolder; + itk::NumericTraits<typename OutputImageType::PixelType>::SetLength(outputValueHolder,this->GetOutput()->GetNumberOfComponentsPerPixel()); + + while(!outIt.IsAtEnd()) + { + // MoveIterartors will ++ all iterators in the tuple + for(;!outIt.IsAtEndOfLine();++outIt,functor_filter_details::MoveIterators(inputIterators)) + { + // This will call the operator with inputIterators Get() results + // and fill outputValueHolder with the result. + functor_filter_details::CallOperator(outputValueHolder,m_Functor,inputIterators); + outIt.Set(outputValueHolder); + // Update progress + p.CompletedPixel(); + } + outIt.NextLine(); + } +} + +} // end namespace otb + +#endif diff --git a/Modules/Core/Functor/include/otbVariadicAddFunctor.h b/Modules/Core/Functor/include/otbVariadicAddFunctor.h new file mode 100644 index 0000000000000000000000000000000000000000..1ab244af33ab630ad0ce1543f1790bf1862961c3 --- /dev/null +++ b/Modules/Core/Functor/include/otbVariadicAddFunctor.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef otb_VariadicAddFunctor_h +#define otb_VariadicAddFunctor_h + +#include <numeric> + +namespace otb +{ + +namespace Functor +{ +/** + * \class VariadicAdd + * \brief This functor adds any number of compile time scalar inputs + * \ingroup OTBFunctor + */ +template <typename TOut, typename ...TIns> struct VariadicAdd +{ + auto operator()(TIns... ins) const + { + std::vector<TOut> outVector{static_cast<TOut>(ins)...}; + + return std::accumulate(outVector.begin(), outVector.end(),0); + } +}; + +} // end namespace Functor + +} // end namespace otb + +#endif diff --git a/Modules/Core/Functor/include/otbVariadicConcatenateFunctor.h b/Modules/Core/Functor/include/otbVariadicConcatenateFunctor.h new file mode 100644 index 0000000000000000000000000000000000000000..c4903141aad2ebc38cdd131f9d77d7bceea42741 --- /dev/null +++ b/Modules/Core/Functor/include/otbVariadicConcatenateFunctor.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef otb_VariadicConcatenateFunctor_h +#define otb_VariadicConcatenateFunctor_h + +#include "itkVariableLengthVector.h" +#include <vector> +#include <numeric> +#include <array> + +namespace otb +{ + +namespace Functor +{ + + +namespace variadic_concatenate_details +{ +// helper function to implement next functor (convert a scalar value +// to a VariableLengthVector) +template <typename T> itk::VariableLengthVector<T> toVector(const T & in) +{ + itk::VariableLengthVector<T> out; + out.SetSize(1); + out[0] = in; + return out; +} + +// helper function to implement next functor, VariableLengthVectorVersion (returns in) +template <typename T> const itk::VariableLengthVector<T> & toVector(const itk::VariableLengthVector<T> & in) +{ + return in; +} + +// helper function to implement next functor, Merge two VariableLengthVector in-place +template <typename v1, typename v2> void concatenateVectors(v1 & a, const v2 & b) +{ + const size_t previousSizeOfA = a.GetSize(); + + a.SetSize(previousSizeOfA+b.GetSize()); + + for(size_t it = 0; it<b.Size();++it) + { + a[previousSizeOfA+it] = static_cast<typename v1::ValueType>(b[it]); + } +} + +// helper function to implement next functor, Merge N VariableLengthVector in-place +template <typename v1, typename v2, typename ...vn> void concatenateVectors(v1 & a, const v2 & b, const vn&... z) +{ + concatenateVectors(a,b); + concatenateVectors(a,z...); +} +} // end namespace variadic_concatenate_details + +// N images (all types) -> vector image +// This functor concatenates N images (N = variadic) of type +// VectorImage and or Image, into a single VectorImage + +/** + * \class VariadicConcatenate + * \brief This functor concatenates any number of input of scalar type + * or VariableLengthVector. + * \ingroup OTBFunctor + */ +template<typename TOut, typename ...TIns> struct VariadicConcatenate +{ + auto operator()(const TIns &... ins) const + { + itk::VariableLengthVector<TOut> out; + variadic_concatenate_details::concatenateVectors(out, variadic_concatenate_details::toVector(ins)...); + + return out; + } + + // Must define OutputSize because output pixel is vector + constexpr size_t OutputSize(const std::array<size_t, sizeof...(TIns)> inputsNbBands) const + { + return std::accumulate(inputsNbBands.begin(),inputsNbBands.end(),0); + } +}; + +} // end namespace Functor + +} // end namespace otb + +#endif diff --git a/Modules/Core/Functor/include/otbVariadicInputsImageFilter.h b/Modules/Core/Functor/include/otbVariadicInputsImageFilter.h new file mode 100644 index 0000000000000000000000000000000000000000..9625815f24f3e54a2df56af5ee0560144b7fcc4d --- /dev/null +++ b/Modules/Core/Functor/include/otbVariadicInputsImageFilter.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef otb_VariadicInputsImageFilter_h +#define otb_VariadicInputsImageFilter_h + +#include "itkImageSource.h" + +namespace otb { + +/** + * \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: + using Self = VariadicInputsImageFilter<TOuptut, TInputs...>; + using Pointer = itk::SmartPointer<Self>; + using ConstPointer = itk::SmartPointer<const Self>; + using Superclass = itk::ImageSource<TOuptut>; + + using InputTypesTupleType = std::tuple<TInputs...>; + + 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); + + /** + * \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)); + } + +#define DefineLegacySetInputMacro(n) \ + 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 SetVariadicInput<n-1>(img); \ + } + + // The following defines legacy setters SetInput1() + // ... SetInput10(), only if the number of input type is sufficient + DefineLegacySetInputMacro(1); + DefineLegacySetInputMacro(2); + DefineLegacySetInputMacro(3); + DefineLegacySetInputMacro(4); + DefineLegacySetInputMacro(5); + DefineLegacySetInputMacro(6); + DefineLegacySetInputMacro(7); + DefineLegacySetInputMacro(8); + DefineLegacySetInputMacro(9); + DefineLegacySetInputMacro(10); + +#undef DefineLegacySetInputMacro + + /** + * \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)); + } + + /** + * \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)>{}); + } + + /** + * \return A tuple with all inputs + */ + auto GetVariadicInputs() + { + return GetInputsImpl(std::make_index_sequence<sizeof...(TInputs)>{}); + } + +protected: + VariadicInputsImageFilter() + { + this->SetNumberOfRequiredInputs(sizeof...(TInputs)); + }; + + ~VariadicInputsImageFilter() = default; + +private: + template<class Tuple, size_t...Is> auto SetInputsImpl(Tuple& t, std::index_sequence<Is...>) + { + 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->GetVariadicInput<Is>()...); + } + + VariadicInputsImageFilter(const Self&) = delete; + void operator=(const Self&) = delete; +}; + +} + +#endif diff --git a/Modules/Core/Functor/include/otbVariadicNamedInputsImageFilter.h b/Modules/Core/Functor/include/otbVariadicNamedInputsImageFilter.h new file mode 100644 index 0000000000000000000000000000000000000000..3110a0ef478fe52ac28180b6654e3ceefe83efdf --- /dev/null +++ b/Modules/Core/Functor/include/otbVariadicNamedInputsImageFilter.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef otb_VariadicNamedInputsImageFilter_h +#define otb_VariadicNamedInputsImageFilter_h + +#include "otbVariadicInputsImageFilter.h" + +namespace otb { + +/// TODO: Documentation + +namespace internal +{ +template<typename Arg, typename Tuple> struct tuple_index; +template<typename Arg, typename...Args> struct tuple_index<Arg, std::tuple<Arg,Args...> > +{ + static constexpr std::size_t value = 0; +}; + +template<typename Arg, typename NotMatching, typename...Args> struct tuple_index<Arg, std::tuple<NotMatching,Args...>> +{ + static_assert(sizeof...(Args)>0,"Could not find requested type in tuple"); + static constexpr std::size_t value = 1 + tuple_index<Arg,std::tuple<Args...>>::value; +}; +} + +template<class TOuptut, class TInputNameMap, class ... TInputs> class VariadicNamedInputsImageFilter : public VariadicInputsImageFilter<TOuptut,TInputs...> +{ +public: + using Self = VariadicNamedInputsImageFilter<TOuptut,TInputNameMap, TInputs...>; + using Pointer = itk::SmartPointer<Self>; + using ConstPointer = itk::SmartPointer<const Self>; + using Superclass = VariadicInputsImageFilter<TOuptut, TInputs...>;; + + using InputTypesTupleType = typename Superclass::InputTypesTupleType; + template <size_t I> using InputImageType = typename Superclass::template InputImageType<I>; + static constexpr size_t NumberOfInputs = Superclass::NumberOfInputs; + + static_assert(std::tuple_size<TInputNameMap>::value == NumberOfInputs,"Tuple for input name does not match the size of ... TInputs"); + + itkNewMacro(Self); + + template <typename Tag> void SetVariadicNamedInput(const InputImageType<internal::tuple_index<Tag, TInputNameMap>::value> * inputPtr) + { + constexpr size_t idx = internal::tuple_index<Tag, TInputNameMap>::value; + this->SetNthInput(idx,const_cast<InputImageType<idx> *>(inputPtr)); + } + + template <typename Tag> const InputImageType<internal::tuple_index<Tag,TInputNameMap>::value> * GetVariadicNamedInput() + { + constexpr size_t idx = internal::tuple_index<Tag, TInputNameMap>::value; + return dynamic_cast<const InputImageType<idx> *>(this->GetInput(idx)); + } + +protected: + VariadicNamedInputsImageFilter() = default; + ~VariadicNamedInputsImageFilter() = default; + +private: + VariadicNamedInputsImageFilter(const Self&) = delete; + void operator=(const Self&) = delete; +}; + +} + +#endif diff --git a/Modules/Core/Functor/otb-module.cmake b/Modules/Core/Functor/otb-module.cmake new file mode 100644 index 0000000000000000000000000000000000000000..01d7f0cf94dc980a08e61ba9514e9792c4876d90 --- /dev/null +++ b/Modules/Core/Functor/otb-module.cmake @@ -0,0 +1,34 @@ +# +# Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) +# +# This file is part of Orfeo Toolbox +# +# https://www.orfeo-toolbox.org/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set(DOCUMENTATION "This module contains a set of classes that allow parsing +metadata files from different types of sensor (both optical and radar sensor types +are supported. for instance: Pleiades, SPOT6, TerraSar, and so on).") + +otb_module(OTBFunctor + DEPENDS + OTBITK + OTBCommon + OTBImageBase + TEST_DEPENDS + OTBTestKernel +DESCRIPTION + "${DOCUMENTATION}" +) diff --git a/Modules/Core/Functor/test/CMakeLists.txt b/Modules/Core/Functor/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..b51af1d22e27c3345ed3a69c2ea98485b918f31f --- /dev/null +++ b/Modules/Core/Functor/test/CMakeLists.txt @@ -0,0 +1,33 @@ +# +# Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) +# +# This file is part of Orfeo Toolbox +# +# https://www.orfeo-toolbox.org/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +otb_module_test() + +set(OTBFunctorTests +otbFunctorTestDriver.cxx +otbFunctorImageFilter.cxx +) + +add_executable(otbFunctorTestDriver ${OTBFunctorTests}) +target_link_libraries(otbFunctorTestDriver ${OTBFunctor-Test_LIBRARIES}) +otb_module_target_label(otbFunctorTestDriver) + +otb_add_test(NAME bfTvFunctorImageFilter COMMAND otbImageManipulationTestDriver + otbFunctorImageFilter) diff --git a/Modules/Core/Functor/test/otbFunctorImageFilter.cxx b/Modules/Core/Functor/test/otbFunctorImageFilter.cxx new file mode 100644 index 0000000000000000000000000000000000000000..464a4086b9eef59319754d841c5efb3cced2b3d3 --- /dev/null +++ b/Modules/Core/Functor/test/otbFunctorImageFilter.cxx @@ -0,0 +1,454 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "itkMacro.h" +#include "otbFunctorImageFilter.h" +#include "otbImage.h" +#include "otbVectorImage.h" +#include "itkNeighborhood.h" +#include "otbVariadicAddFunctor.h" +#include "otbVariadicConcatenateFunctor.h" +#include "otbVariadicNamedInputsImageFilter.h" +#include <tuple> + +#include <numeric> +#include <complex> +#include <math.h> + +// static tests + +// Tests of IsNeighborhood, PixelTypeDeduction and ImageTypeDeduction structs +using namespace otb; +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 + 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 + 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,""); + + // Fake test operator + template <typename TOut,typename TIn> struct TestOperator + { + auto operator()(const TIn&) const + { + TOut res(OutputSize()); + return res; + } + + constexpr size_t OutputSize(...) const + { + return 1; + } + }; + +template <typename TOut,typename TIn> struct TestOperatorVoidReturn + { + void operator()(TOut& out,const TIn&) const + { + out = TOut(OutputSize()); + } + + constexpr size_t OutputSize(...) const + { + return 1; + } + }; + + // Fake test operator non const + template <typename TOut,typename TIn> struct TestOperatorNonConst + { + auto operator()(const TIn&) + { + TOut res(OutputSize()); + return res; + } + + constexpr size_t OutputSize(...) const + { + return 1; + } + }; + + + template <typename TOut, typename TIn> void TestFilter() + { + // Deduce types + using InputPixelType = typename PixelTypeDeduction<TIn>::PixelType; + using InputImageType = typename ImageTypeDeduction<InputPixelType>::ImageType; + + // Allocate fake input + auto in = InputImageType::New(); + typename InputImageType::SizeType size = {{10,10}}; + in->SetRegions(size); + in->SetNumberOfComponentsPerPixel(1); + in->Allocate(); + InputPixelType defaultValue(1); + in->FillBuffer(defaultValue); + + // Build and run filter + auto functor = TestOperator<TOut,TIn>{}; + auto filter = NewFunctorFilter(functor); + + using FilterType = typename decltype(filter)::ObjectType; + static_assert(FilterType::NumberOfInputs == 1,""); + static_assert(std::is_same<typename FilterType::template InputImageType<0>, InputImageType>::value, ""); + filter->SetVariadicInputs(in); + filter->SetInput1(in); + filter->template SetVariadicInput<0>(in); // template keyword to + // avoid C++ parse + // ambiguity + auto res = filter->template GetVariadicInput<0>(); + filter->Update(); + + // Test named input version + struct tag{}; + using inputNames = std::tuple<tag>; + auto filter1 = NewFunctorFilter<decltype(functor),inputNames>(functor); + filter1->template SetVariadicNamedInput<tag>(in); + res = filter1->template GetVariadicNamedInput<tag>(); + filter1->Update(); + + // Test with void return + auto functorWithVoidReturn = TestOperatorVoidReturn<TOut,TIn>{}; + auto filterWithVoidReturn = NewFunctorFilter(functorWithVoidReturn); + using FilterWithVoidReturnType = typename decltype(filter)::ObjectType; + static_assert(FilterWithVoidReturnType::NumberOfInputs == 1,""); + static_assert(std::is_same<typename FilterWithVoidReturnType::template InputImageType<0>, InputImageType>::value, ""); + + // Test with non const operator + auto functorNonConstOperator = TestOperatorNonConst<TOut,TIn>{}; + auto filterWithNonConstOperator = NewFunctorFilter(functorNonConstOperator); + filterWithNonConstOperator->SetInput1(in); + filterWithNonConstOperator->Update(); + + filterWithVoidReturn->SetVariadicInputs(in); + filterWithVoidReturn->SetInput1(in); + filterWithVoidReturn->template SetVariadicInput<0>(in); // template keyword to avoid C++ parse ambiguity + filterWithVoidReturn->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(); + + // Test with standard filter use + using FullFilterType = otb::FunctorImageFilter<TestOperator<TOut,TIn>>; + auto filter2 = FullFilterType::New(); + } + + TypesCheck() + { + // Test possible combinations + TestFilter<ScalarType,ScalarType>(); + TestFilter<ScalarType,VectorType>(); + TestFilter<ScalarType,NeighborhoodType>(); + TestFilter<VectorType,ScalarType>(); + } +}; + +auto checksInt = TypesCheck<int>{}; +auto checksDouble = TypesCheck<double>{}; +auto checksComplex = TypesCheck<std::complex<double>>{}; + +// Example functors + + + +// 1 VectorImage -> 1 VectorImage with a different size depending on a +// parameter of the functor +// This Functor +template<typename TOut, typename TIn> struct BandExtraction +{ + BandExtraction(unsigned int indices...) : m_Indices({indices}) + {} + + // TODO define a constructor to initialize m_Indices + auto operator()(const itk::VariableLengthVector<TIn> & in) const + { + itk::VariableLengthVector<TOut> out(m_Indices.size()); + + size_t idx = 0; + for(auto v: m_Indices) + { + out[idx] = static_cast<TOut>(in[v]); + ++idx; + } + + return out; + } + + // This time OutputSize does not depend on input image size, hence + // the ... + size_t OutputSize(...) const + { + return m_Indices.size(); + } + + // set of band indices to extract + std::set<unsigned int> m_Indices; +}; + +// 1 Image with neighborhood -> 1 Image +// This Functor computes the mean in neighborhood +template<typename TOut, typename TIn> struct Mean +{ + auto operator()(const itk::Neighborhood<TIn> & in) const + { + TOut out(0); + + for(auto it = in.Begin(); it!=in.End();++it) + out+=static_cast<TOut>(*it); + + out/=in.Size(); + + return out; + } +}; + +// 1 Image with neighborhood of VariableLengthVector -> 1 image with +// VariableLengthVector +// For each channel, returns the maximum value in neighborhood +template<typename T> struct MaxInEachChannel +{ + auto operator()(const itk::Neighborhood<itk::VariableLengthVector<T>> & in) const + { + auto out = in.GetCenterValue(); + + for(auto it = in.Begin(); it!=in.End(); ++it) + { + for(auto band = 0u; band < out.Size();++band) + { + if((*it)[band]>out[band]) + out[band] = (*it)[band]; + } + } + return out; + } + + size_t OutputSize(const std::array<size_t,1> & nbBands) const + { + return nbBands[0]; + } +}; + + +template<typename T> struct VectorModulus +{ + itk::VariableLengthVector<double> operator()(const itk::VariableLengthVector<std::complex<T>> & in) const + { + itk::VariableLengthVector<double> out(in.Size()); + + for(auto band = 0u; band < out.Size(); ++band) + { + out[band] = std::abs(in[band]); + } + return out; + } + + size_t OutputSize(const std::array<size_t,1> & nbBands) const + { + return nbBands[0]; + } +}; + +int otbFunctorImageFilter(int itkNotUsed(argc), char * itkNotUsed(argv) []) +{ + // test functions in functor_filter_details namespace + using VectorImageType = VectorImage<double>; + using ImageType = Image<double>; + using ComplexVectorImageType = VectorImage<std::complex<double>>; + using ComplexImageType = Image<std::complex<double>>; + using RegionType = typename ImageType::RegionType; + using SizeType = typename RegionType::SizeType; + + auto vimage = VectorImageType::New(); + auto image = ImageType::New(); + auto cvimage = ComplexVectorImageType::New(); + auto cimage = ComplexImageType::New(); + + SizeType size = {{200,200}}; + + vimage->SetRegions(size); + vimage->SetNumberOfComponentsPerPixel(2); + vimage->Allocate(); + itk::VariableLengthVector<double> v(2); + v.Fill(0); + vimage->FillBuffer(v); + + cvimage->SetRegions(size); + cvimage->SetNumberOfComponentsPerPixel(2); + cvimage->Allocate(); + itk::VariableLengthVector<std::complex<double>> cv(2); + cv.Fill(0); + cvimage->FillBuffer(cv); + + image->SetRegions(size); + image->Allocate(); + image->FillBuffer(0); + + cimage->SetRegions(size); + cimage->Allocate(); + cimage->FillBuffer(0); + + // Test VariadicInputsImageFilter + auto filter = otb::VariadicInputsImageFilter<VectorImageType,VectorImageType,ImageType>::New(); + filter->SetVariadicInput<0>(vimage); + filter->SetVariadicInput<1>(image); + + filter->SetInput1(vimage); + filter->SetInput2(image); + + filter->SetVariadicInputs(vimage,image); + std::cout<<filter->GetVariadicInput<0>()<< filter->GetVariadicInput<1>()<<std::endl; + + // Test VariadicNamedInputsImageFilter + struct xs {}; + struct pan {}; + using Names = std::tuple<xs,pan>; + auto filterWithNames = otb::VariadicNamedInputsImageFilter<VectorImageType, Names, VectorImageType,ImageType>::New(); + filterWithNames->SetVariadicNamedInput<xs>(vimage); + + // Test FunctorImageFilter with a built-in math function + using CosType = double(double); + auto filterCos = NewFunctorFilter(static_cast<CosType *>(std::cos)); + filterCos->SetVariadicInputs(image); + filterCos->Update(); + + filterWithNames->SetVariadicNamedInput<pan>(image); + + std::cout<<filterWithNames->GetVariadicNamedInput<xs>()<< filterWithNames->GetVariadicNamedInput<pan>()<<std::endl; + + + + // test FunctorImageFilter with a lambda + double scale = 10.; + auto Lambda1 = [scale](double p) + { + return scale*p; + }; + auto filterLambda = NewFunctorFilter(Lambda1); + filterLambda->SetVariadicInputs(image); + filterLambda->Update(); + + // test FunctorImageFilter with a lambda that returns a + // VariableLengthVector + // Converts a neighborhood to a VariableLengthVector + auto Lambda2 = [](const itk::Neighborhood<double> & in) + { + itk::VariableLengthVector<double> out(in.Size()); + std::size_t idx{0}; + for(auto it = in.Begin(); it!=in.End();++it,++idx) + { + out[idx]=*it; + } + return out; + }; + + // 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->SetVariadicInputs(image); + filterLambda2->Update(); + + // Test FunctorImageFilter with the VariadicConcatenate operator + using ConcatFunctorType = Functor::VariadicConcatenate<double, double, itk::VariableLengthVector<double> >; + auto concatenate = NewFunctorFilter(ConcatFunctorType{}); + concatenate->SetVariadicInputs(image,vimage); + concatenate->Update(); + + // Test FunctorImageFilter With VariadicAdd functor + using AddFunctorType = Functor::VariadicAdd<double, double, double>; + auto add = NewFunctorFilter(AddFunctorType{}); + add->SetVariadicInput<0>(image); + add->SetVariadicInput<1>(image); + add->Update(); + + // Test FunctorImageFilter with BandExtraction functor + using ExtractFunctorType = BandExtraction<double,double>; + ExtractFunctorType extractFunctor{1,2}; + auto extract = NewFunctorFilter(extractFunctor); + extract->SetVariadicInputs(vimage); + extract->Update(); + + // Test FunctorImageFilter With Mean functor + using MeanFunctorType = Mean<double,double>; + auto median = NewFunctorFilter(MeanFunctorType{},{{2,2}}); + median->SetVariadicInputs(image); + median->Update(); + + // Test FunctorImageFilter with MaxInEachChannel + using MaxInEachChannelType = MaxInEachChannel<double>; + auto maxInEachChannel = NewFunctorFilter(MaxInEachChannelType{},{{3,3}}); + maxInEachChannel->SetVariadicInputs(vimage); + maxInEachChannel->Update(); + + // Test FunctorImageFilter with Module (complex= + using ModulusType = VectorModulus<double>; + auto modulus = NewFunctorFilter(ModulusType{}); + modulus->SetVariadicInputs(cvimage); + modulus->Update(); + + auto LambdaComplex = [] (const std::complex<double> & in) {return std::arg(in);}; + auto argFilter = NewFunctorFilter(LambdaComplex); + argFilter->SetVariadicInputs(cimage); + argFilter->Update(); + + return EXIT_SUCCESS; +} + diff --git a/Modules/Core/Functor/test/otbFunctorTestDriver.cxx b/Modules/Core/Functor/test/otbFunctorTestDriver.cxx new file mode 100644 index 0000000000000000000000000000000000000000..860a5e38ddbbdea1b8bed4a94dd4ca09b4667643 --- /dev/null +++ b/Modules/Core/Functor/test/otbFunctorTestDriver.cxx @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "otbTestMain.h" + +void RegisterTests() +{ + REGISTER_TEST(otbFunctorImageFilter); +} diff --git a/Modules/Core/ImageBase/test/CMakeLists.txt b/Modules/Core/ImageBase/test/CMakeLists.txt index 4e1896b536bbbf6e149d29f06e37783b755e2fce..abed0364f142c81d85af113a7cdf0d7fd67e0f26 100644 --- a/Modules/Core/ImageBase/test/CMakeLists.txt +++ b/Modules/Core/ImageBase/test/CMakeLists.txt @@ -40,7 +40,6 @@ set(OTBImageBaseTests otbImageTest.cxx otbImageFunctionAdaptor.cxx otbMetaImageFunction.cxx - ) add_executable(otbImageBaseTestDriver ${OTBImageBaseTests})