[ VIGRA Homepage | Function Index | Class Index | Namespaces | File List | Main Page ]
Functor Expressions |
Simple automatic functor creation by means of expression templates (also known as a "lambda library"). Note, however, that the vigra::multi_math module offers similar functionality with an easier syntax.
#include <vigra/functorexpression.hxx>
Namespace: vigra::functor
Motivation
Many generic algorithms are made more flexible by means of functors which define part of the algorithms' behavior according to the needs of a specific situation. For example, we can apply an exponential to each pixel by passing a pointer to the exp
function to transformImage()
:
However, this only works for simple operations. If we wanted to apply the exponential to a scaled pixel value (i.e. we want to execute exp(-beta*v)
), we first need to implement a new functor:
This functor would be used like this:
However, this approach has some disadvantages:
Functors cannot be implemented directly at the point of use. Thus, to find out exactly what a functor is doing, one needs to look somewhere else. This complicates use and maintenance ot generic code.
Therefore, it is necessary to provide a means to generate functors on the fly where they are needed. The C++ standard library contains so called "functor combinators" that allow to construct complicated functors from simpler ones. The above problem "apply exp(-beta*v) to every pixel" would be solved like this:
I won't go into details on how this works. Suffice it to say that this technique requires a functional programming style that is unfamiliar to many programmers, and thus leads to code that is difficult to understand. Moreover, this technique has some limitations that prevent certain expressions from being implementable this way. Therefore, VIGRA provides a better and simpler means to create functors on the fly.
Automatic Functor Creation
Automatic functor creation in VIGRA is based on a technique called Expression Templates. This means that C++ operators are overloaded so that they don't execute the specified operation directly, but instead produce a functor which will later calculate the result. This technique has the big advantage that the familiar operator notation can be used, while all the flexibility of generic programming is preserved.
The above problem "apply <TT>exp(-beta*v)</TT> to every pixel" will be solved like this:
Here, four expression templates have been used to create the desired functor:
Param(-beta):
creates a functor that represents a constant (-beta
in this case)
Arg1():
represents the first argument of the expression (i.e. the pixels of image src
in the example). Likewise, Arg2()
and Arg3()
are defined to represent more arguments. These are needed for algorithms that have multiple input images, such as combineTwoImages() and combineThreeImages().
creates a functor that returns the product of its arguments. Likewise, the other C++ operators (i.e. +, -, *, /, %, ==, !=, <, <=, >, >=, &&, ||, &, |, ^, !, ~
) are overloaded.
exp():
creates a functor that takes the exponential of its argument. Likewise, the other algebraic functions (i.e. sq, sqrt, exp, log, log10, sin, asin, cos, acos, tan, atan, abs, floor, ceil, pow, atan2, fmod, min, max
) are overloaded.
We will explain additional capabilities of the functor creation mechanism by means of examples.
The same argument can be used several times in the expression. For example, to calculate the gradient magnitude from the components of the gradient vector, you may write:
It is also possible to build other functions into functor expressions. Suppose you want to apply my_complicated_function()
to the sum of two images:
[Note that the arguments of the wrapped function are passed as additional arguments to applyFct()
]
You can implement conditional expression by means of the ifThenElse()
functor. It corresponds to the "? :" operator that cannot be overloaded. ifThenElse()
can be used, for example, to threshold an image:
You can use the Var()
functor to assign values to a variable (=, +=, -=, *=, /=
are supported). For example, the average gray value of the image is calculated like this:
For use in inspectImage() and its relatives, there is a second conditional functor ifThen()
that emulates the if()
statement and does not return a value. Using ifThen()
, we can calculate the size of an image region:
Often, we want to execute several commands in one functor. This can be done by means of the overloaded operator,()
("operator comma"). Expressions separated by a comma will be executed in succession. We can thus simultaneously find the size and the average gray value of a region:
[Note that the list of comma-separated expressions must be enclosed in parentheses.]
A comma separated list of expressions can also be applied in the context of transformImage() and its cousins. Here, a general rule of C++ applies: The return value of a comma expression is the value of its last subexpression. For example, we can initialize an image so that each pixel contains its address in scan order:
Further information about how this mechanism works can be found in this paper (sorry, slightly out of date).
© Ullrich Köthe (ullrich.koethe@iwr.uni-heidelberg.de) |
html generated using doxygen and Python
|