diff --git a/Documentation/Cookbook/rst/C++/DeveloperGuide.rst b/Documentation/Cookbook/rst/C++/DeveloperGuide.rst index 2808c4479c704bf71bd2ced9c010af2c65e57228..e34e034a08d94a074443ec8d26215f953c5ae8ca 100644 --- a/Documentation/Cookbook/rst/C++/DeveloperGuide.rst +++ b/Documentation/Cookbook/rst/C++/DeveloperGuide.rst @@ -7,6 +7,7 @@ Developer Guide Iterators Filters StreamingAndThreading + FunctorImageFilter PersistentFilters WriteAnApplication AddingNewModules diff --git a/Documentation/Cookbook/rst/C++/FunctorImageFilter.rst b/Documentation/Cookbook/rst/C++/FunctorImageFilter.rst new file mode 100644 index 0000000000000000000000000000000000000000..95fd6124f8c02481e07b7313481c677bbabd01ec --- /dev/null +++ b/Documentation/Cookbook/rst/C++/FunctorImageFilter.rst @@ -0,0 +1,253 @@ +.. _FunctorImageFilter: + +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 +:doxygen:`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 :doxygen:`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: + +.. code-block:: cpp + + double myFreeFunction(const double& int in) {...} + +It can also be defined as a functor (i.e. a class defining ``operator()``: + +.. code-block:: cpp + + class MyFunctor + { + public: + double operator()(const double& int in) {...} + }; + +It can also be defined as a lambda: + +.. code-block:: cpp + + 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: + +.. code-block:: cpp + + auto filterFromFreeFunction = NewFunctorFilter(myFreeFunction); + auto filterFromFunctor = NewFunctorFilter(MyFunctor); + auto filterFromLambda = NewFunctorFilter(myLambda); + +And you can use it just like any other filter: + +.. code-block:: cpp + + filterFromLambda->SetInput(upStreamFilter->GetOutput()); + downstreamFilter->SetInput(filterFromLambda->GetOutput()); + +You can also directly define instances of ``FunctorImageFilter`` with built-in math functions: + +.. code-block:: cpp + + 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 (const R&, T1 t1, T2 t2 ..., TN tn)`` + +This will be more efficient when ``R`` is of type ``itk::VariableLengthVector<T>`` and should be prefered in this case. + +Automatic type deduction examples +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Consider the following free function: + +.. code-block:: cpp + + itk::VariableLenghtVector<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: + +.. code-block:: cpp + + void myFreeFunction(const 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: + +.. code-block:: cpp + + myFilter->SetInput<N>(imageN); + +You can also set all inputs at once with the ``SetInputs()`` method: + +.. code-block:: cpp + + myFilter->SetInputs(image0,...,imageN); + +If you only have one input, you can simply call: + +.. code-block:: cpp + + 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: + +.. code-block:: cpp + + 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: + +.. code-block:: cpp + + auto filterFromFunctor = NewFunctorFilter(MyFunctor,{{3,3}}); + +Advanced use +------------ + +Number of output bands +~~~~~~~~~~~~~~~~~~~~~~ + +.. _NumberOfOutputBands: + +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: + +.. code-block:: cpp + + 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: + +.. code-block:: cpp + + 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: + +.. code-block:: cpp + + // Specify that the lambda output has 3 bands + auto filterFromLambda = NewFunctorFilter(myLambda,3); +