Add Functor module with FunctorImageFilter
Summary
This MR introduces a new Functor
module, which contains the FunctorImageFilter
.
It is the same as MR !268 (closed) with squashed commit and a better documentation.
For examples of use, please refer to the test of the class.
Accepted types in functor arguments and return type
Prototype for this filter is as follows : FunctorImageFilter<TFunction,TNameMap = void>
.
Where TFunction
can be any lambda, free function, or class member operator()
, with any number of arguments.
Let R (T1 t1, T2 t2 ..., TN tn) (const)
be TFunction
signature. T1, T2, ...TN
can be:
- A value, ref or const ref to the following,
- A scalar type (unsigned int, double),
- A std::complex
- An instance of
itk::FixedArray
,itk::RGBPixel
,itk::RGBAPixel
- A
itk::VariableLengthVector
- A
itk::Neighborhood
of any of the above
R
can be:
- A value, ref or const ref to the following,
- A scalar type (unsigned int, double),
- A std::complex
- An instance of
itk::FixedArray
,itk::RGBPixel
,itk::RGBAPixel
- A
itk::VariableLengthVector
Note that the following operator prototype is also supported: void (R& return, T1 t1, T2 t2 ..., TN tn) (const)
Setting the inputs
FunctorImageFilter
will automatically deduce the correct image type from return type and arguments of the function or operator()
, and will offer setters of the form SetVariadicInput<N>(...)
excepting the correct type for Nth input.
Note that one can also use the SetVariadicInputs(in1, in2, in3 ...)
variadic method to set all inputs at once.
VectorImage
Correct allocation of ouptut when output image is If the TFunction
produces a VariableLengthVector
, then it should also provide a OutputSize()
method which will be called by FunctorImageFilter
to allocate the correct number of bands in output image.
Full prototype of OutputSize()
method is constexpr size_t OutputSize(const std::array<size_t, N> inputsNbBands) const
with N being the number of argument of the functor. inputsNbBands
will contain the number of bands for each input which can be used to derive the output number of bands. However, if the number of bands in input is not relevant to derive the number of output band, the following simpler signature can be used instead constexpr size_t OutputSize(...) const
.
If a lambda returning a VariableLengthVector
is used, one can not add the OutputSize()
method but the NumberOfOutputBandsDecorator
template class can be used to wrap the lambda and still use the filter.
Last, for the sake of backward compatibility, SetInput1()
, ..., SetInput10()
are also defined (if and only if the number of arguments in the functor is high enough).
Accessing the Functor
Functor can be retrieved with GetFunctor()
(returns a const reference) or GetModifiableFunctor()
(returns a non-const reference AND calls Modified()
on filter).
Naming inputs
In order to ease the use of the filter by users, it is possible to name input with tags instead of indices.
In that case TNameMap
is not void, but a std::tuple
of tags (i.e. empty classes):
struct tag1{};
struct tag2{};
If TNameMap = std::tuple<tag1,tag2>
then FunctorImageFilter
will also support settings input as follows : SetVariadicNamedInput<tag1>(in)
or SetVariadicNamedInput(tag1{},in)
... A good practice could be that this name mapping is provided by the functor itself.
Setting the Radius
If one of the arguments of the functor maps to itk::Neighborhood
the corresponding input will automatically be padded to the filter radius, which can be changed using SetRadius()
.
Easy creation of filters
The FunctorImageFilter
offers two free functions that make it very easy to create ready to use instances of the filter:
// Simple case
SimpleFunctor f1;
auto filter1 = NewFunctorFilter(f1);
filter1->SetVariadicInputs(in1,...,inN);
filter1->Update();
// Functor With Radius
FunctorWithRadius f2;
auto filter2 = NewFunctorFilter(f1 {{3,3}}); // passing the radius directly
filter2->SetVariadicInputs(in1,...,inN);
filter2->Update();
// Lambda returning a VariableLengthVector of 3 bands
auto lambdaWithVariableLengthVector = [](const double &){return itk::VariableLengthVector<double> res(3); return res;};
auto filter2 = NewFunctorFilter(lambdaWithVariableLengthVector,3); // passing the number of output bands directly
filter2->SetVariadicInputs(in1);
filter2->Update();
Of course one can also directly use FunctorImageFilter
typedef to create the filter through the classical New()
static method.
Using this for refactoring of existing functor based filters
- If functor outputs a
VariableLengthVector
:- change signature to
void operator(TOut & out, ...) const
(more efficient). - Implement the
OutputSize()
method if functor produces aVariableLengthVector
- change signature to
- If the functor is used in filter with custom setters (such as
SetInputRed()
,SetInputMask()
,SetInputHH()
...), create a set of corresponding tags somewhere (i.e.struct red{};
...) - Replace filter with template using:
template <typename TInputImage1, typename TInputImage2, typename TOuputImage>
using TheFunctorFilter = FunctorImageFilter< TheFunctor < typename TInputImage1::PixelType,
typename TInputImage2::PixelType,
typename TOutputImage::PixelType>,
std::tuple<tag1,tag2> >;
- Rewrite the tests and applications to replace (and add this to migration guide):
- calls to
SetInputTag1()
bySetVariadicNamedInput<tag1>()
- calls to
SetAlpha()
byGetModifiableFunctor().SetAlpha()
- calls to
- Remove/deprecate old code (the fun part)
Implementation Details
I tried to document the code as best as I could. If something is missing, please tell me I will fix it.
Tests
An extensive test suite has been added to cover all possible cases, with a lot of static_assert
. Most cases are also covered in the test with real functors.
Limitations
This will not work with:
- Functor with more than one
operator()
- Functor template
operator()
method - Something called polymorphic lambdas (not sure if this is even useful)
Copyright
The copyright owner is CNES and has signed the ORFEO ToolBox Contributor License Agreement.
Thanks to Jordi for helping starting this.
Check before merging:
- All discussions are resolved
- At least 2
👍 votes from core developers, no👎 vote. - The feature branch is (reasonably) up-to-date with the base branch
- Dashboard is green
- Copyright owner has signed the ORFEO ToolBox Contributor License Agreement