|
mmcfilters
Public API documentation
|
This guide documents the attribute-computer architecture and the extension path for adding or changing attributes. For public usage, see Attributes; for the attribute table, see Attribute catalog.
The public API calls each scalar an attribute. In this guide, descriptor refers only to the mathematical quantity represented by a public scalar attribute.
This subsystem contains several computation strategies, including bottom-up accumulation, finite-window event compilation, and family-specific providers and reducers. Incremental describes how a particular computer derives its result over the current tree; it does not imply one universal strategy or that every public attribute buffer stays live after arbitrary topology edits.
Ordinary application code should include mmcfilters/attributes/Attributes.hpp and call AttributeComputation. Concrete computers are advanced extension components, not alternate public orchestration APIs.
The C++ library is header-only, so installed packages include some detail headers as transitive implementation dependencies. Those headers are shipped so public headers compile downstream; they are not compatibility-contract headers. The explicit installation manifest is cmake/mmcfiltersPublicHeaders.cmake; adding a repository header does not implicitly publish or install it.
An attribute computer owns one coherent attribute family. Every computer must provide:
inline static constexpr familyName for diagnostics;inline static constexpr family for scheduler grouping;inline static constexpr domain for execution routing;inline static constexpr producedAttributes as the canonical attribute list;static compute(context) for internal-node rows;static computeUnitRows(unitContext) for compact exported-Higra unit rows.Computers are stateless static kernels. The produced-attribute list has a single source of truth: the computer class. runtimeProducedAttributes<Computer>() materializes it only for call sites that need runtime storage.
Unit-row support is mandatory. If an attribute has a degenerate one-pixel meaning, the computer defines that value explicitly. Otherwise it still defines the exported unit-row convention.
AttributeRegistry.hpp stores public attribute metadata:
topologyOnly in the registry).Group membership is metadata. Public requests may mix scalar attributes and groups; the pipeline expands groups, deduplicates scalars, and returns only the requested public attributes.
AttributeComputerRegistry.hpp defines the computer protocol and RegisteredAttributeComputers. Produced attributes are declared only by Computer::producedAttributes, and scheduler grouping is declared only by Computer::family.
At a high level, a request follows this path:
AttributeFamilyScheduler adds hidden dependencies, groups attributes by family, and preserves dependency order. The central executors are:
executeAttributeComputationPlan(...) for altitude-aware requests;executeTopologyAttributeComputationPlan(...) for topology/support requests.The internal orchestration path is detail::AttributePipeline; topology/support families are delegated to TopologyAttributeBackend. New code should extend this path instead of adding another top-level execution pipeline.
Dependencies are ordinary attribute results consumed by another computer. They are passed as DependencySourceT<Real>, a non-owning pair of AttributeNames and const Real*. Dependency buffers are reusable only when they contain the requested attributes and use NodeIdSpace::MorphologicalTree.
Several computers use bottom-up accumulation: preprocess the current node, merge children into the parent, then finalize the current node. Increment-augmented public calls compute the base attribute first, then materialize ancestor/descendant sample offsets from a typed positive altitude step, sampling radius, representative-descendant policy, and missing-sample policy.
For multiple sampling distances, the support cardinality and minimum pixel index in the node support are computed once for each node. Each distance reuses those metadata and dense neighborhood buffers while performing its ancestor search and representative descendant selection. See Attributes for sampling semantics.
Concrete families may introduce providers, reducers, caches, or specialized traversals behind the common computer contract. Those mechanisms remain implementation details and must not create an alternate public orchestration path. See Distance-transform architecture for the exact and approximate distance-field backends.
The context types in AttributeKernelSupport.hpp are the adapter boundary:
AttributeComputeContext<Real>;AltitudeAttributeComputeContext<Real, T>;UnitAttributeComputeContext<Real>;AltitudeUnitAttributeComputeContext<Real, T>.TopologyAttributeComputer and AltitudeAttributeComputer enforce the standard computer protocol. A new family should not add public family-specific method names. Private helpers and detail kernels may keep narrower signatures when that makes implementation or testing clearer.
The generic finite-window C++ extension has its own role-typed event and aggregation contract. See Finite-window local-attribute C++ extension for its localization semantics, decision/algebra split, bitquad specialization, and extension example. Family-specific storage remains an implementation detail and is not part of the common attribute-computer contract described here.
Computers use AttributeNumericPolicy.hpp for degenerate divisions, square roots, non-negative clamping, finite fallbacks, and ratio bounds. Attribute buffers should not expose accidental NaN or infinite values for ordinary finite inputs.
Start by deciding whether the attribute belongs to an existing family or requires a new family. Prefer an existing family when traversal, dependencies, or intermediate state are shared.
Common metadata steps:
AttributeTypes.hpp and one matching row in AttributeRegistry.hpp.For an attribute in an existing family:
producedAttributes.compute(context).DependencyResolver<Real> for semantic dependencies and AttributeNumericPolicy.hpp for finite fallbacks.AttributeFamilyScheduler.hpp only when another materialized attribute is consumed.computeUnitRows(unitContext).For a new family:
AttributeComputerFamily value.mmcfilters/attributes/computers/.familyName, family, domain, and producedAttributes.compute(context) and computeUnitRows(unitContext).RegisteredAttributeComputers.AttributePipeline.hpp or TopologyAttributeBackend.hpp.When the public surface or attribute semantics change, update Python bindings as needed and keep Attributes and the Attribute catalog synchronized with the registry.
Useful checks while changing this subsystem are:
Run Python tests when bindings or the Python facade change. Subsystem guides list any additional focused validation targets.