otbFunctorImageFilter.h 18 KB
Newer Older
1
/*
Julien Michel's avatar
Julien Michel committed
2
 * Copyright (C) 2005-2019 Centre National d'Etudes Spatiales (CNES)
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
 *
 * 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>
32 33
#include "itkConstNeighborhoodIterator.h"
#include "otbImage.h"
34 35 36 37 38 39 40 41 42 43 44

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
 */
45
template <class T> struct IsNeighborhood : std::false_type {};
46

47 48 49 50 51
/// Partial specialisation for const ConstNeighborhoodIterator<Image::T> &
template <class T> struct IsNeighborhood<const itk::ConstNeighborhoodIterator<Image<T>>&> : std::true_type {};

/// Partial specialisation for const ConstNeighborhoodIterator<VectorImage::T> &
template <class T> struct IsNeighborhood<const itk::ConstNeighborhoodIterator<VectorImage<T>>&> : std::true_type {};
52 53 54 55 56 57 58

/**
 * \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.
 */
59
template <class T> struct IsSuitableType : std::is_scalar<T>::type {};
60 61

/// Unwrap complex
62
template <class T> struct IsSuitableType<std::complex<T>> : IsSuitableType<T>::type {};
63 64

/// Unwrap VariableLengthVector
65
template <class T> struct IsSuitableType<itk::VariableLengthVector<T>> : IsSuitableType<T>::type {};
66 67

/// Unwrap FixedArray
68
template <class T, unsigned int N> struct IsSuitableType<itk::FixedArray<T,N>> : IsSuitableType<T>::type {};
69 70

/// Unwrap RGBPixel
71
template <class T> struct IsSuitableType<itk::RGBPixel<T>> : IsSuitableType<T>::type {};
72 73

/// Unwrap RGBAPixel
74
template <class T> struct IsSuitableType<itk::RGBAPixel<T>> : IsSuitableType<T>::type {};
75 76 77 78 79

/**
 * \struct PixelTypeDeduction
 * \brief Helper struct to derive PixelType from template parameter.
 * 
80 81 82
 * T                                                -> PixelType = T
 * const ConstNeighborhoodIterator<Image::T>&       -> PixelType = T
 * const ConstNeighborhoodIterator<VectorImage::T>& -> PixelType = itk::VariableLengthVector<T>
83 84 85
*/
template <class T> struct PixelTypeDeduction
{
86 87
  static_assert(IsSuitableType<T>::value,
                "T can not be used as a template parameter for Image or VectorImage classes.");
88 89 90
  using PixelType = T;
};

91
/// Partial specialisation for itk::ConstNeighborhoodIterator<Image<T>>
92
template <class T> struct PixelTypeDeduction<itk::ConstNeighborhoodIterator<Image<T>>>
93
{
94 95
  static_assert(IsSuitableType<T>::value,
                "T can not be used as a template parameter for Image or VectorImage classes.");
96 97 98
  using PixelType = T;
};

99
/// Partial specialisation for itk::ConstNeighborhoodIterator<VectorImage<T>>
100 101 102 103 104 105 106
template <class T> struct PixelTypeDeduction<itk::ConstNeighborhoodIterator<VectorImage<T>>>
{
  static_assert(IsSuitableType<T>::value,
                "T can not be used as a template parameter for Image or VectorImage classes.");
  using PixelType = itk::VariableLengthVector<T>;
};

107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
/** 
 * \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>;
};

126 127 128 129
// Helper to remove const, volatite and Ref qualifier (until c++20
/// that has std::remove_cvref)
template <typename T> using RemoveCVRef = typename std::remove_cv<typename std::remove_reference<T>::type>::type;

130 131 132 133 134 135 136 137 138 139 140 141 142 143
/**
* \struct RetrieveOperator
*
* \brief Struct to retrieve the operator type
* 
* \tparam T the type to retrieve operator() from
*
*/
template <typename T> struct RetrieveOperator
{
  static_assert(std::is_class<T>::value || std::is_function<T>::value, "T is not a class or function");
  using Type = decltype(&T::operator());
};

144 145 146 147 148 149 150 151 152 153 154 155
/**
* \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.
*/
156
template <typename T, typename TNameMap> struct FunctorFilterSuperclassHelper : public FunctorFilterSuperclassHelper<typename RetrieveOperator<T>::Type,TNameMap> {};
157 158 159 160 161 162 163 164 165

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
166
  template <typename V> using InputImageType = typename ImageTypeDeduction<typename PixelTypeDeduction<RemoveCVRef<V>>::PixelType>::ImageType;
167 168 169 170 171 172 173 174 175

  // 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
176
  using InputHasNeighborhood = std::tuple<typename IsNeighborhood<T>::type...>;
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
};
} // 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
 * 
237
 * \tparam Functor can be any operator() (const or non-const) that matches the following:
238
 * - Accepts any number of arguments of T,
239 240 241
 * (const) itk::VariableLengthVector<T> (&),const
 * itk::ConstNeighborhoodIterator<VectorImage<T>> &, (const)
 * itk::ConstNeighborhoodIterator<Image<T>> & with T a scalar type
242 243 244 245
 * - 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
246
 * SetInputs() method (see VariadicInputsImageFilter class for
247 248
 * details)
 * 
249 250
 * \param[in] the Functor to build the filter from
 * \param[in] radius The size of neighborhood to use, if there is any
251
 * ConstNeighborhoodIterator<VectorImage<T>> in the operator() arguments.
252 253 254 255 256 257 258 259
 * \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.
 */
260
template <typename Functor, typename TNameMap = void> auto NewFunctorFilter(Functor f, itk::Size<2> radius = {{0,0}});
261 262 263 264 265


/** \class FunctorImageFilter
 * \brief A generic functor filter templated by its functor
 * 
266
 * \tparam TFunction can be any operator() (const or non-const) that matches the following:
267
 * - Accepts any number of arguments of T,
268 269 270
 * (const) itk::VariableLengthVector<T> (&),const
 * itk::ConstNeighborhoodIterator<Image<T>> &, const
 * itk::ConstNeighborhoodIterator<VectorImage<T>> & with T a scalar type
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
 * - 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);
310 311 312 313 314 315 316 317 318 319 320 321

  /** New() macro defined only if TFunction is default constructible */
  template <typename F = TFunction> static std::enable_if_t<std::is_default_constructible<F>::value, Pointer> New()
  {
    // Explicit default construct
    FunctorType f;

    // Create a filter out of it
    Pointer  p = new Self(f, {{0,0}});
    p->UnRegister();
    return p;
  }
322 323 324 325 326 327

    /** New() macro defined only if TFunction is NOT default constructible
     * This will yield an error message since New() can not be implemented in this case. */
  template <typename F = TFunction> static std::enable_if_t<!std::is_default_constructible<F>::value, Pointer> New()
  {
    static_assert(std::is_default_constructible<F>::value,"Cannot call New() "
Julien Michel's avatar
Julien Michel committed
328
      "function as the functor used for the filter creation is not default "
329 330 331 332
      "constructible");

    return nullptr;
  }
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
  
  /** 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
366
  friend auto NewFunctorFilter<TFunction,TNameMap>(TFunction f, itk::Size<2> radius);
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389

  /** 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
390
template <typename Functor, typename TNameMap> auto NewFunctorFilter(Functor f, itk::Size<2> radius)
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
{
  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.
 */ 
411
template <typename F> struct NumberOfOutputBandsDecorator : public F
412 413
{
public:
414
  constexpr NumberOfOutputBandsDecorator(F t, unsigned int nbComp) : F(t), m_NumberOfOutputBands(nbComp) {}
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429

  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
 * 
430 431
 * \param[in] f the Functor to build the filter from
 * \param[in] numberOfOutputBands The number of output bands that
432 433
 * this filter will return
 * \param radius The size of neighborhood to use, if there is any
434
 * ConstNeighborhoodIterator in the operator() arguments.
435 436 437 438 439 440 441 442 443 444
 * \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.

 */ 

445
template <typename Functor, typename TNameMap = void> auto NewFunctorFilter(Functor f, unsigned int numberOfOutputBands, itk::Size<2> radius)
446 447 448 449 450 451 452 453 454 455 456 457 458
{
  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