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 or VectorImage 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& in) {...}

It can also be defined as a functor (i.e. a class defining operator():

class MyFunctor
{
public:
    double operator()(const double& in) {...}
};

It can also be defined as a lambda:

auto myLambda = [](const double& 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 type

  • itk::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 type otb::Image<TN>

  • If TN is of type itk::VariableLengthVector<T> with T a basic type as defined above, the Nth input will be of type otb::VectorImage<T>

  • If TN is of type const itk::ConstNeighborhoodIterator<otb::Image<T>> & with T a basic type as defined above, the Nth input will be of type otb::Image<TN>

  • If TN is of type const itk::ConstNeighborhoodIterator<otb::VectorImage<T>> & with T a basic type as defined above, the Nth input will be of type otb::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 (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);