New Functor Filter with lambda support

This is in regards to the following topic: http://review.source.kitware.com/#/c/23352/

This patch introduces the UnaryGeneratorImageFilter BinaryGeneratorImageFilters, as replacements for the UnaryFunctorImageFilter and the BinaryFunctorImageFilters. The two big advantages are:

  • Support for C++ lambda’s
  • Removed class’s template parameter for functor, which enables reuse of a parent class.

These new classes enable the setting for the functor with a “SetFunctor” method, where the type of functor is specified when the method is envoked as opposed to being a class argument. This enables the following uses:

using FilterType = itk::UnaryGeneratorImageFilter<ImageType, ImageType>;
using ValueFunctionType = FilterType::ValueFunctionType;

auto filter = FilterType::New();

// set with C style function pointer
filter->SetFunctor( static_cast<ValueFunctionType*>(std::cos) );

// set with std::functional
std::function<float(float)> func1 = static_cast<ValueFunctionType*>(std::sin);
filter->SetFunctor( func1 );

// set with C++ lambda function
filter->SetFunctor( [](const float &v) { return v+2.0;});

In addition to the itk::Functors from the same namespace.

The implementation passes the functor type to a ThreadedGenerateData method so that the functor can be inlined, and the filters can retain the efficiency. Additionally, the filter no longer holds a direct copy of the functor, to when SetFunctor is called a copy of the argument is captured in a lambda expression, and that copy is not accessible.

5 Likes

Wow, this is great!!

1 Like

The following are the signatures added for “SetFunctor”:

  using ConstRefFunctionType = OutputImagePixelType (const Input1ImagePixelType &,
                                                     const Input2ImagePixelType &);
  using ValueFunctionType = OutputImagePixelType (Input1ImagePixelType,
                                                  Input2ImagePixelType);

  /** Set the pixel functor
   *
   * The functor defines an operation done per pixel.
   */
 void SetFunctor( const std::function<ConstRefFunctionType> &f);
 void SetFunctor( const std::function<ValueFunctionType> &f);

  /** Set the pixel functor by a C function pointer
   *
   * The functor defines an operation done per pixel.
   */
  void SetFunctor( ConstRefFunctionType *funcPointer);
  void SetFunctor( ValueFunctionType *funcPointer);


  /** Set the pixel functor by a "Functor Object"
   *
   * The functor defines an operation done per pixel. A single copy of
   * the argument is created an used for all threads, so the functor
   * must be concurrent thread-safe. The functor must a have an
   * operator() method which accept arguments of Input1ImagePixelType,
   * Input2ImagePixelType.
   */
  template <typename TFunctor>
  void SetFunctor( const TFunctor &functor);

So these signatures accept the C++ lambdas, C++ std::functions, C-Style function pointers, and ITK Style functors. The one negative thing is that the last template SetFunctorhas no constraints so if “junk” is passed to it then an obscure compiler error message can be generated.

Also passing SetFunctor C++ lambda, will use the templated signature. While the type of a lambda can be converted to a std::function, the SetFunction’ temple signature has higher priority than implicitly converting to std::function. Because the template signature is used there is no enforcement of the specific arguments and return type for the lambda ( neither is there for the ITK Functors either ).

So this current approach has chosen flexibility over strong typing for the SetFunctor argument. Is there any options or feedback from the modern C++ gurus around?

The native ITK Python will need some updates following this patch.

  • The new Generator classes need to be directly wrapped
  • The old Functor wrapping convention of wrapping the superclass:
itk_wrap_class("itk::TanImageFilter" POINTER_WITH_SUPERCLASS)
  itk_wrap_image_filter("${WRAP_ITK_REAL}" 2)
itk_end_wrap_class()

Should be removed. This manifestation of the benefit of reduced generated code an symbols so it should have the benefit of improving compilation time and reduce binary size.

I presume one can programmatically generate a list of classes now derived from the Generator filters and programmatically remove POINTER_WITH_SUPERCLASS form the .wrap file.

Are there any volunteers?

It is excellent we have this feature – thanks again for contributing this @blowekamp !

I will look into fixing the Python wrapping.

I’ve just converted one of my programs to use this - it’s great! All the boilerplate to do with the Functor objects is gone.

4 Likes