Conditional execution consists in executing a part of a program, or not, depending on whether some condition is met. There are two possible ways to achieve this in FabImage Studio:
- Conditional Connections – more simple; preferred when the program structure is linear and only some steps may be skipped in some circumstances.
- Variant Macrofilters – more elegant; preferred when there are two or more alternative paths of execution.
This section is devoted to the former, conditional connections.
Before conditional connections can be discussed, we need to explain what conditional data is. There are some filters, for which it may be impossible to compute the output data in some situations. For example, the SegmentSegmentIntersection filter computes the intersection point of two line segments, but for some input segments the intersection may not exist. In such cases, the filter returns a special Nil value which indicates lack of data (usually read: "not detected").
Other examples of filters producing conditional data include:
- ScanSingleEdge – will not find any edge on a plain image.
- DecodeBarcode – will not return any decoded string, if the checksum is incorrect.
- GigEVision_GrabImage_WithTimeout – will not return any image, if communication with the camera times out.
- MakeConditional – explicitly creates a conditional output; returns Nil when a specific condition is met.
Filter outputs that produce conditional data can be recognized by their types which have a question mark suffix. For example, the output type in SegmentSegmentIntersection is "Point2D?".
The Nil value corresponds to the NULL pointer or a special value, such as -1. Conditional types, however, provide more type safety and clearly define, in which parts of the program special cases are anticipated.
Conditional connections () appear when a conditional output is connected to a non-conditional input. The second filter is then said to be executed conditionally and has its output types changed to conditional as well. It is actually executed only if the incoming data is not Nil. Otherwise, i.e. when a Nil comes, the second filter also returns Nil on all of its outputs and it becomes subdued (darker) in the Program Editor.
As output types of conditionally executed filters change into conditional, an entire sequence of conditionally executed filters can easily be created. This sequence usually ends at a filter that accepts conditional data on its input.
Resolving Nil Values
In general, if we have a conditional execution, a Nil value has to be anticipated, and it has to be resolved at some point. Here is a list of the most typical solutions:
First of all, the Nil case can be ignored. Some part of the program will be not executed
and it will not be signaled in any way.
Example:Use this approach when your camera is running in a free-run mode and an inspection routine has to be executed only if an object has been detected.
The most simple way to resolve Nil is by using the MergeDefault filter, which
replaces the Nil with another value, specified in the program.
Example: Most typically for industrial quality inspection systems, we can replace a Nil ("object not detected") with "NOT OK" (False) inspection result.
- A conditional value can also be transformed into a logical value by making use of IsNil and IsNil.Not property outputs. (Before version 4.8 the same could have been done with TestObjectNotNil and TestObjectNil filters).
- If conditional values appear in an array, e.g. because a filter with a conditional output is executed in array mode, then the RemoveNils filter can create a copy of this array with all Nils removed.
- [Advanced] Sometimes we have a Task with conditional values at some point where Nil appears only in some iterations. If we are interested in returning the latest non-Nil value, a connection to a non-conditional macrofilter output will do just that. (Another option is to use the LastNotNil filter).
- Another possibility is to use a variant macrofilter with a conditional forking input and exactly two variants: "Nil" and "Default". The former will be executed for Nil, the latter for any other value.
Conditional execution can be illustrated with a simplified version of the "Capsules" example:
Other Alternatives to Conditional Execution
If the objects that have to be processed conditionally belong to an array, then it is advisable to use neither variant macrofilters nor conditional connections, but the classification filters from the Classify group. Each of these filters receives an array on its input and then splits it into several arrays with elements grouped accordingly to one of the three criterions.
Especially the ClassifyByRange filter is very useful when classifying objects on a basis of values of some extracted numeric features.
In the simplest cases it is also possible to compute a value conditionally with the filters from the Choose group (ChooseByPredicate, ChooseByRange, ChooseByCase) or with the ternary <if> ? <then> : <else> operator in the formula blocks.