|
MorphologicalAttributeFilters
Public API documentation
|
This guide covers the public filtering layer in mmcfilters/filters. It connects AttributeFilters<T>, ExtinctionValues<T, Real>, UltimateAttributeOpening<T, Real>, and the MSER helper used by adaptive filtering.
For the tree ownership, NodeId, proper-part, altitude, and mutation-version model used here, see Morphological Trees. For computing the node-indexed attributes consumed by these operators, see Attributes.
The filtering layer reconstructs image-domain outputs from dense node-indexed tree data. The common contracts are:
NodeId space;Use WeightedMorphologicalTree<T> when the operation needs owner state, such as Python bindings, MSER-assisted filtering, executeWithMSER, image reconstruction, or topology edits. Use WeightedTreeView<T> for read-only C++ filtering when the caller owns an external altitude buffer and does not need MSER.
All node buffers are indexed by internal NodeId:
The expected sizes are:
tree.getNumInternalNodeSlots() floating-point values;tree.getNumInternalNodeSlots() boolean values;tree.getNumInternalNodeSlots() float values;tree.getNumInternalNodeSlots() byte/boolean values.Dead internal slots may exist after edits. Buffers still keep the full slot count; filtering traverses live nodes through the current tree topology.
Python arrays passed as attributes must be one-dimensional, C-contiguous np.float32 or np.float64 arrays with length tree.numInternalNodeSlots. Python boolean criteria are passed as lists or vectors of the same length.
AttributeFilters<T> groups the ordinary attribute-filter reconstruction rules. It can be constructed from either a weighted owner or a non-owning weighted view:
When extracting one attribute column from a flat multi-attribute C++ result, copy it to a dense per-node vector before calling filters. Multi-attribute results are node-major, so one column is strided by AttributeNames::NUM_ATTRIBUTES. A single-attribute result is already directly usable as a dense node buffer.
The object API allocates and returns output images. Static overloads write into caller-owned output images and are useful in tight loops:
Choose the reconstruction rule according to the intended attribute-filter semantics:
The criterion-based overloads receive the keep/reject decision directly. The attribute-threshold overloads build the decision from a floating-point node attribute and a threshold.
getAdaptiveCriterion(...) adjusts a criterion with an MSER-style stability analysis. It requires WeightedMorphologicalTree<T> ownership because MSER uses the tree-owned altitude buffer to build delta neighbourhoods. Classical MSER is defined only for max-trees and min-trees, where altitude is monotone along every root-to-leaf path:
MSERComputer<T, Real> is the advanced helper behind this path. For a positive altitude delta, it pairs each node with an altitude-delta ascendant and descendant, computes a variation score from an increasing attribute, and marks strict local minima that pass the configured variation and attribute bounds. If no attribute buffer is provided, it lazily computes AREA. Missing windows produce NaN; nodes with NaN variation are not selected as MSERs.
The variation score is:
Lower finite values are more stable. Adaptive pruning therefore moves a rejected node to the minimum-variation representative among the node, its selected ascendant, and its selected descendant.
MSER is not a general WeightedTreeView<T> operation. View-based filter objects can run ordinary direct, subtractive, and pruning rules, but reject MSER-assisted methods that need owner state.
Tree-of-shapes and self-dual residual trees do not have a single max/min altitude polarity. For them, use depth stability instead:
DepthStableRegionComputer<Real> implements that rule. Here depthDelta = 2 means exactly two tree edges: climb two parent links for the ascendant and select a descendant exactly two child links below the center. If several descendants exist at that depth, the largest-area one is chosen; ties use the smallest NodeId. This operator does not read altitude and should be described as topological depth stability, not classical MSER. Its numeric score is still a variation value; use getVariation(...)/getVariations() when inspecting it directly.
ExtinctionValues<T, Real> ranks regional extrema by a scalar node attribute on max-trees and min-trees. In this classical component-tree setting, the regional extrema processed by the implementation are the tree leaves. The attribute is a dense floating-point buffer indexed by internal NodeId; larger attribute values are interpreted as stronger extrema. Real defaults to float; select double when consuming double-precision attribute buffers. Results are stored as RegionalExtremaNode<Real> records sorted by decreasing extinction:
filtering(extremaToKeep) reconstructs an image by retaining the strongest extrema. saliencyMap(extremaToKeep, unweighted) writes saliency on compact contours of the retained cutoff nodes. With unweighted=true, contours receive rank-like scores; with unweighted=false, they receive extinction values. extremaToKeep must be non-negative. The dominant extremum has no stronger merge point; its extinction is represented by the explicit finite sentinel std::numeric_limits<Real>::max().
Trees of shapes and self-dual residual trees are rejected by ExtinctionValues. Their leaves can be regional extrema, but the complete regional-extrema set is not generally equivalent to tree.getLeaves(). Support for those tree kinds should therefore use a separate extrema-collection stage before applying an extinction ranking.
UltimateAttributeOpening<T, Real> consumes a dense increasing-attribute buffer and computes two image-domain outputs. Real defaults to float; use double for double-precision attribute buffers:
The basic workflow is:
execute(maxCriterion) treats all internal nodes as selectable candidates. execute(maxCriterion, selectedForFiltering) accepts an explicit dense selection mask. executeWithMSER(maxCriterion, deltaMSER) builds that mask from classical altitude MSER and therefore requires a WeightedMorphologicalTree<T> owner, not just a view. executeWithDepthStability(maxCriterion, depthDelta) builds the mask from topological depth stability and does not use altitude for the stability selection.
The increasing attribute is expected to encode the primitive scale used by UAO, for example area, height, or another monotone criterion. The implementation stores contrasts in the same type T as the altitude. Integral altitude types therefore keep the historical API shape; use a wider or floating altitude type in C++ when the contrast range matters.
Python exposes the owner-oriented uint8 workflow:
Extinction values are available for max-trees and min-trees either through ExtinctionValues directly or through convenience methods on AttributeFilters:
Pass unweighted explicitly when the saliency score convention matters because the direct ExtinctionValues.saliencyMap(...) API and convenience wrappers use different defaults.
UAO follows the same dense attribute-buffer convention:
Create filter helper objects after the topology edits that should affect the operation:
Do not reuse AttributeFilters, ExtinctionValues, or UltimateAttributeOpening objects after mutating the tree topology. The objects record the mutation version and fail explicitly once the underlying topology changes. Attribute arrays should also be recomputed after edits because their values and live-node interpretation may have changed.
NodeId, proper parts, altitude, and topology mutation.