|
MorphologicalAttributeFilters
Public API documentation
|
This document describes the public attribute-computation API and the result contracts for node-indexed attribute buffers.
The attribute layer turns public attribute requests into dense per-node buffers. It handles:
NodeId space to public export layouts.The canonical execution layout is always the dense internal NodeId space of MorphologicalTree. Projection is a boundary operation, not the internal representation used by computers.
The subsystem is designed for incremental, tree-structured attribute computation: computers accumulate family-specific state over the current morphological tree and materialize dense public buffers at API boundaries. After topology edits, recompute public attribute buffers unless a higher-level operator documents its own edit-aware update path.
For ordinary C++ use, include:
Main entry points:
AttributeComputation: public facade for computing one attribute, one group, or a heterogeneous request.AttributeNames: describes column layout in flat attribute buffers.ComputedAttributeData<Real> and ComputedAttributeDataWithDelta<Real>: owning result types returned by the facade.Use AttributeComputation for normal application code. Concrete computers are advanced extension components, not an alternate public orchestration path.
For the descriptor list, see Attribute Catalog. For the tree ownership, altitude, and NodeId model that attribute buffers use, see Morphological Trees. For reconstruction operators that consume node-indexed attribute buffers, see Attribute Filters, Extinction Values, And UAO.
Topology-only requests may run on MorphologicalTree. Requests that read altitude must use WeightedMorphologicalTree<T> or WeightedTreeView<T>.
The attribute layer relies on the tree contracts documented in Morphological Trees:
WeightedMorphologicalTree<T> owns topology plus a dense altitude buffer.WeightedTreeView<T> borrows topology plus an external altitude span.Individual attributes may add stricter checks. For example, MAX_DIST requires max-tree or min-tree topology with adjacency metadata and rejects unsupported tree kinds explicitly.
Choose the public entry point from the input contract:
Single altitude-aware attribute:
Attribute value buffers default to float, but the public facade can also materialize double buffers by selecting the Real template argument:
The Real argument selects the public result storage. The typed attribute facade computes through the same internal double pipeline and casts only when materializing the returned buffer. Integer-valued support descriptors are still counted discretely and then materialized in the requested real type.
Several scalar attributes or groups in one coordinated request:
Topology-only attributes without requiring an altitude-bearing owner:
Returning values in a preserved public node-id space, for a tree created by createFromHigraParent(...) and not edited since import:
Delta-augmented sampling around one scalar attribute:
Projecting node attributes to pixels or to an exported Higra layout:
Python keeps a smaller public surface than C++:
np.uint8 WeightedMorphologicalTree path;Attribute.computeSingleAttribute(...) and Attribute.computeAttributes(...) are the weighted attribute entry points;Attribute.computeSingleTopologyAttribute(...) and Attribute.computeTopologyAttributes(...) are the explicit topology/support-only entry points;NodeIdSpace can be passed to the Python attribute methods when a preserved output node-id space is needed;dtype=np.float32 or dtype=np.float64; the default remains np.float32;AttributePipeline, concrete C++ computers, local-event deltas, and bitquad delta buffers are not Python API.Typical Python calls use the same weighted versus topology-only split:
The dtype keyword selects the public result storage. Both np.float32 and np.float64 requests use the same internal double attribute pipeline; the only difference is the dtype of the returned NumPy buffer.
Python filtering helpers such as AttributeFilters, ExtinctionValues, and UltimateAttributeOpening consume both np.float32 and np.float64 attribute buffers, matching the dtype selected by the attribute computation call.
Attribute results are dense flat buffers interpreted by AttributeNames. The canonical internal layout is:
where node_id is an internal dense MorphologicalTree node slot. Dead internal slots keep the default buffer value; consumers that reason about tree nodes should iterate tree.getAliveNodeIds().
ComputedAttributeData<Real> and ComputedAttributeDataWithDelta<Real> also store the NodeIdSpace of the returned buffer. The default public computation type is ComputedAttributeData<float> or ComputedAttributeDataWithDelta<float>; selecting Real=double returns the corresponding double specialization. Public computation methods can request a different public node-id space, but projection always happens after the internal pipeline has computed the result in NodeIdSpace::MORPHOLOGICAL_TREE.
NodeIdSpace::HIGRA means the preserved imported Higra node-id domain. It is available only for trees imported from Higra whose original node-id space has not been invalidated by edits. Direct projection to this space copies live internal-node rows and fills proper-part rows with unit-component values for the requested attributes.
For a compact Higra layout exported from the current tree, use AttributeComputation::projectNodeValuesToExportedHigra(...). That helper emits the [proper parts | live internal nodes] layout produced by hierarchy export and fills unit proper-part rows through the responsible attribute computers. computeAttributeMapping(...) is the image-domain helper: each proper part receives the value stored at its owner node.
For the distinction between preserved imported Higra ids and exported compact Higra snapshots, see Higra Interoperability.
Scalar attributes returned by the ordinary public attribute APIs are finite for valid live nodes and exported proper-part rows. This includes degenerate supports such as one-pixel components, line-like components, zero continuous BitQuads perimeter, and BitQuads configurations whose Euler estimate is zero.
The finite fallbacks are part of the public attribute contract:
| Attribute family | Degenerate condition | Returned value |
|---|---|---|
BITQUADS_CIRCULARITY | BITQUADS_PERIMETER_CONTINUOUS <= eps | 0 |
BITQUADS_PERIMETER_AVERAGE | estimated Euler component count <= 0 | 0 |
BITQUADS_LENGTH_AVERAGE | estimated Euler component count <= 0 | 0 |
BITQUADS_WIDTH_AVERAGE | continuous perimeter <= eps | 0 |
ECCENTRICITY | both inertia eigenvalues are numerically zero | 1 |
ECCENTRICITY | smallest inertia eigenvalue <= eps or ratio overflows the practical range | 1e6 |
BITQUADS_WIDTH_AVERAGE is computed with the algebraically equivalent finite formula 2 * BITQUADS_AREA / BITQUADS_PERIMETER_CONTINUOUS when the continuous perimeter is positive. This avoids the inf / inf pattern that appears if the formula is evaluated through average area and average perimeter separately.
The finite-scalar contract does not mean every buffer cell is finite in every API mode:
NaN values;computeSingleAttributeWithDelta(..., "nan-padding") and "null-padding" deliberately keep NaN for missing ancestor or descendant samples;