FunctorImageFilter¶
In image processing and remote sensing, it is very common to write custom pixel-based or neighborhood-based operations between one or several co-registered images. Starting OTB 7.0, there is now a unique filter otb::FunctorImageFilter that will handle most cases:
- Any number of input images, being either
Image
,VectorImage
or a mix of both, - An
Image
orVectorImage
output - Operation based on pixels, neighborhoods or a mix of both,
- Functor classes, function, lambdas.
With otb::FunctorImageFilter you only need to write the operation you want to perform, and the filter will take care of everything (including multi-threading and streaming).
Quickstart¶
Defining the operation to perform¶
The operation to perform can be defined as a free function:
double myFreeFunction(const double& int in) {...}
It can also be defined as a functor (i.e. a class defining operator()
:
class MyFunctor
{
public:
double operator()(const double& int in) {...}
};
It can also be defined as a lambda:
auto myLambda = [](const double & int in) -> double {...}
Creating a FunctorImageFilter
¶
Once the operation to perform has been implemented, it is very easy to get an instance of FunctorImageFilter
from it:
auto filterFromFreeFunction = NewFunctorFilter(myFreeFunction);
auto filterFromFunctor = NewFunctorFilter(MyFunctor);
auto filterFromLambda = NewFunctorFilter(myLambda);
And you can use it just like any other filter:
filterFromLambda->SetInput(upStreamFilter->GetOutput());
downstreamFilter->SetInput(filterFromLambda->GetOutput());
You can also directly define instances of FunctorImageFilter
with built-in math functions:
using CosType = double(double);
auto filterCos = NewFunctorFilter(static_cast<CosType*>(std::cos));
Note, the static_cast
trick, which allows to disambguiate between different prototypes of the cos
function.
Automatic types deduction¶
You probably notice that, contrary to other filters in ITK and OTB, there is no
need to specify input and output image types. This is because
FunctorImageFilter
uses C++ metaprogramming to automatically derive the
input and output image types from the free function, functor or lambda, with
the following rules.
Let R (T1 t1, T2 t2 ..., TN tn)
be the signature of the free function, operator()
or lambda. Note that the filter conversely supports passing by value TN tn
or by const reference const TN & tn
.
First lets define basic types:
- Scalar type (double, float, unsigned int, short …)
std::complex<T>
with T a scalar typeitk::FixedArray<T,N>
,itk::RGBPixel<T>
,itk::RGBAPixel<T>
with T a scalar type
Automatic input type deduction¶
From the basic types, the following deduction rules apply:
- If
TN
is a basic type as defined above, the Nth input will be of typeotb::Image<TN>
- If
TN
is of typeitk::VariableLengthVector<T>
with T a basic type as defined above, the Nth input will be of typeotb::VectorImage<T>
- If
TN
is of typeconst itk::ConstNeighborhoodIterator<otb::Image<T>> &
with T a basic type as defined above, the Nth input will be of typeotb::Image<TN>
- If
TN
is of typeconst itk::ConstNeighborhoodIterator<otb::VectorImage<T>> &
with T a basic type as defined above, the Nth input will be of typeotb::VectorImage<TN>
Note that this will work for any number of inputs.
Automatic output type deduction¶
Rules for output type deduction are simpler:
- If R
is a basic type, output of the filter will be of type otb::Image<R>
- If R
is of type itk::VariableLengthVector<T>
with T a basic type as defined above, the output of the filter will be of type otb::VectorImage<R>
Note that if R
is of type itk::VariableLengthVector<T>
, you need extra steps so that the filter can allocate the correct number of output bands, as explained in NumberOfOutputBands section.
Alternative prototype for performance¶
Automatic type deduction will also work with the following signature:
void (const R&, T1 t1, T2 t2 ..., TN tn)
This will be more efficient when R
is of type itk::VariableLengthVector<T>
and should be preferred in this case.
Automatic type deduction examples¶
Consider the following free function:
itk::VariableLengthVector<double> myFreeFunction(unsigned char a,
const std::complex<float>& b,
const itk::VariableLengthVector<short>& c,
const itk::ConstNeighborhoodIterator<otb::Image<double>>& d) {...}
When a FunctorImageFilter
is built from this function, the following types will be deduced:
- First input (corresponding to a) will be of type
otb::Image<unsigned char>
- Second input (corresponding to b) will be of type
otb::Image<std::complex<float>>
- Third input (corresponding to c) will be of type
otb::VectorImage<short>
- Fourth input (corresponding to d) will be of type
otb::Image<double>
- Output type will be of type
otb::VectorImage<double>
This is strictly equivalent to:
void myFreeFunction(itk::VariableLengthVector<double> & out ,
unsigned char a,
const std::complex<float> & b,
const itk::VariableLengthVector<short> &c,
const itk::ConstNeighborhoodIterator<otb::Image<double>> & d) {...}
Since the output type is of type itk::VariableLengthVector<T>
, the latter should be preferred.
Using the filter¶
Setting inputs¶
The Nth parameter can be set with the template SetInput()
method:
myFilter->SetInput<N>(imageN);
You can also set all inputs at once with the SetInputs()
method:
myFilter->SetInputs(image0,...,imageN);
If you only have one input, you can simply call:
myFilter->SetInput(image);
Of course, input types must match the types deducted from the operator(), free function or lambda!
Accessing the function¶
If FunctorImageFilter
was built from a functor class, this class may have parameters that you wish to change or read.
You can call GetFunctor()
to access a const reference to the functor in order to read a parameter value:
auto a = myFilter->GetFunctor().GetParameterA();
If you wish to modify a parameter of the functor, you will have to call GetModifiableFunctor()
, which will return a non-const reference to the functor and ensure that the filter will be re-run when updated.
Setting the neighborhood radius¶
If you have itk::ConstNeighborhoodIterator<otb::Image<T>>
or itk::ConstNeighborhoodIterator<otb::VectorImage<T>>
as input type, you can set the neighborhood radius when building the filter instance, with:
auto filterFromFunctor = NewFunctorFilter(MyFunctor,{{3,3}});
Advanced use¶
Number of output bands¶
If is of type itk::VariableLengthVector<T>
, then the functor class should provide an OutputSize()
method as follows.
If the number of output bands is fixed:
class MyFunctor {
public:
...
constexpr size_t OutputSize(...) const
{
// Return the number of output bands
return 3;
}
};
If the number of output bands depends on the number of bands in one or more input images:
class MyFunctor {
public:
...
size_t OutputSize(const std::array<size_t,N> & nbBands) const
{
// N Is the number of inputs
// nbBands is an array containing the number of bands for each input
...
return outputNumberOfBands;
}
};
In this case you can use the information provided by the nbBands
parameter
which contain the number of bands for each input, to derive and return the
output number of bands.
If you are using a lambda, a free function or an existing functor which does not
offer the OutputSize()
method, you can still use FunctorImageFilter
but
you need to provide the number of bands when constructing the filter instance:
// Specify that the lambda output has 3 bands
auto filterFromLambda = NewFunctorFilter(myLambda,3);