mmcfilters
Public API documentation
Loading...
Searching...
No Matches
FiniteWindowLocalAttributeComputer.hpp
1#pragma once
2
3#include "ConnectedSubsetTreeLocalizer.hpp"
4#include "FiniteWindowLocalEventCompiler.hpp"
5#include "LocalEventModel.hpp"
6#include "../trees/MorphologicalTree.hpp"
7#include "../trees/detail/TreeTraversalDetail.hpp"
8#include "../utils/Common.hpp"
9#include "../utils/Contract.hpp"
10
11#include <algorithm>
12#include <array>
13#include <cstddef>
14#include <cstdint>
15#include <optional>
16#include <span>
17#include <stdexcept>
18#include <string>
19#include <utility>
20#include <vector>
21
22namespace mmcfilters::local_attributes {
23
24namespace detail::kernel {
25
26template <class Value, class Algebra>
27 requires EventAlgebra<Algebra, Value>
28inline void aggregateLocalAttributeIncrementValues(const MorphologicalTree& tree, std::span<Value> increments, const Algebra& algebra) {
29 ::mmcfilters::detail::kernel::traversePostOrder(
30 tree, tree.root(), [](NodeId) {},
31 [&](NodeId parent, NodeId child) { algebra.addAssign(increments[static_cast<std::size_t>(parent)], increments[static_cast<std::size_t>(child)]); },
32 [](NodeId) {});
33}
34
35template <class Value, class Algebra>
36 requires EventAlgebra<Algebra, Value>
37inline std::vector<NodeAttribute<Value>> aggregateLocalAttributeIncrements(const MorphologicalTree& tree,
38 std::span<const LocalAttributeIncrement<Value>> increments,
39 const Algebra& algebra) {
40 std::vector<NodeAttribute<Value>> attributes;
41 attributes.reserve(increments.size());
42 for (const LocalAttributeIncrement<Value>& increment : increments) {
43 attributes.push_back({increment.node, increment.value});
44 }
45
46 ::mmcfilters::detail::kernel::traversePostOrder(
47 tree, tree.root(), [](NodeId) {},
48 [&](NodeId parent, NodeId child) {
49 algebra.addAssign(attributes[static_cast<std::size_t>(parent)].value, attributes[static_cast<std::size_t>(child)].value);
50 },
51 [](NodeId) {});
52 return attributes;
53}
54
55template <LocalDecision Decision, class Algebra>
56 requires EventAlgebra<Algebra, typename Decision::Value>
57inline std::vector<NodeAttribute<typename Decision::Value>> computeFiniteWindowLocalAttribute(const MorphologicalTree& tree,
58 const ObservationWindow& window,
59 const Decision& decision, const Algebra& algebra) {
60 using Value = typename Decision::Value;
61 std::vector<Value> values = computeLocalAttributeIncrementValues(tree, window, decision, algebra);
62 aggregateLocalAttributeIncrementValues(tree, std::span<Value>(values), algebra);
63
64 std::vector<NodeAttribute<Value>> attributes;
65 attributes.reserve(values.size());
66 for (NodeId node = 0; node < tree.numInternalNodeSlots(); ++node) {
67 attributes.push_back({node, std::move(values[static_cast<std::size_t>(node)])});
68 }
69 return attributes;
70}
71
72} // namespace detail::kernel
73
76 public:
79 [[nodiscard]] static std::optional<NodeId> anchoredEntry(const MorphologicalTree& tree, PixelId anchorPixel, PixelId samplePixel) {
81 return std::nullopt;
82 }
83 const NodeId anchorSmallestNode = tree.smallestNode(anchorPixel);
86 return std::nullopt;
87 }
88 const NodeId entry = detail::kernel::anchoredEntry(tree, anchorPixel, samplePixel);
89 return entry == InvalidNode ? std::nullopt : std::optional<NodeId>{entry};
90 }
91
94 [[nodiscard]] static std::optional<NodeId> anchoredEntry(const MorphologicalTree& tree, PixelId anchorPixel, WindowOffset windowOffset) {
95 const GridDomain2D& domain = tree.requireGridDomain2D("FiniteWindowLocalAttributeComputer::anchoredEntry");
96 if (anchorPixel < 0 || anchorPixel >= domain.rows * domain.columns) {
97 return std::nullopt;
98 }
99 const NodeId entry = detail::kernel::anchoredEntry(tree, anchorPixel, windowOffset);
100 return entry == InvalidNode ? std::nullopt : std::optional<NodeId>{entry};
101 }
102
105 [[nodiscard]] static AnchorBranch anchorBranch(const MorphologicalTree& tree, PixelId anchorPixel) {
107 return {};
108 }
109 NodeId node = tree.smallestNode(anchorPixel);
110 if (!tree.isAlive(node)) {
111 return {};
112 }
113
114 AnchorBranch branch;
115 while (true) {
116 branch.push_back(node);
117 if (node == tree.root()) {
118 break;
119 }
120 node = tree.parent(node);
121 }
122 return branch;
123 }
124
128 detail::validateFiniteWindowLocalAttributeInput(tree);
130 return AnchoredEntryMap(anchorPixel, std::vector<std::optional<NodeId>>(observationWindow.size()));
131 }
132 return detail::kernel::anchoredEntryMap(tree, anchorPixel, observationWindow);
133 }
134
137 [[nodiscard]] static AnchoredEntrySet anchoredEntrySet(const MorphologicalTree& tree, PixelId anchorPixel, const ObservationWindow& observationWindow) {
138 AnchoredEntrySet entries;
140 for (const std::optional<NodeId>& entry : entryMap.entries()) {
141 if (entry.has_value()) {
142 entries.insert(*entry);
143 }
144 }
145 return entries;
146 }
147
150 [[nodiscard]] static OrderedAnchoredEntries orderedAnchoredEntries(const MorphologicalTree& tree, PixelId anchorPixel,
153 return detail::kernel::orderedAnchoredEntries(tree, entryMap);
154 }
155
158 [[nodiscard]] static OrderedAnchoredEntries orderAnchoredEntriesByInclusion(const MorphologicalTree& tree, const AnchoredEntryMap& entryMap) {
159 detail::validateAnchoredEntryMap(tree, entryMap);
160 return detail::kernel::orderedAnchoredEntries(tree, entryMap);
161 }
162
166 std::uint32_t stateBits = 0;
167 if (entryMap.size() == 0) {
168 throw std::invalid_argument("Binary visibility state requires a non-empty anchored-entry map.");
169 }
170 detail::validateAnchoredEntryMap(tree, entryMap);
171 if (!tree.isAlive(node)) {
172 throw std::invalid_argument("Binary visibility state requires a live node.");
173 }
174 if (entryMap.anchorPixel() < 0 || entryMap.anchorPixel() >= tree.numPixels()) {
175 throw std::invalid_argument("Binary visibility state requires a valid anchor pixel.");
176 }
177 const NodeId anchorSmallestNode = tree.smallestNode(entryMap.anchorPixel());
178 if (!tree.isAlive(anchorSmallestNode) || !tree.isAncestor(node, anchorSmallestNode)) {
179 throw std::invalid_argument("Binary visibility state is defined only on the anchor branch.");
180 }
181 for (std::size_t coordinate = 0; coordinate < entryMap.size(); ++coordinate) {
182 if (entryMap[coordinate].has_value() && tree.isAncestor(node, *entryMap[coordinate])) {
183 stateBits |= std::uint32_t{1} << coordinate;
184 }
185 }
187 }
188
200 template <LocalDecision Decision, class Algebra>
202 [[nodiscard]] static std::vector<EventDelta<typename Decision::Value>> computeEventDeltas(const MorphologicalTree& tree, PixelId anchorPixel,
204 const Decision& decision, const Algebra& algebra) {
206 }
207
218 template <LocalDecision Decision, class Algebra>
220 [[nodiscard]] static std::vector<LocalAttributeIncrement<typename Decision::Value>>
225
235 template <class Value, class Algebra>
237 [[nodiscard]] static std::vector<NodeAttribute<Value>> aggregateEventIncrements(const MorphologicalTree& tree,
239 const Algebra& algebra) {
240 detail::validateLocalAttributeIncrements(tree, increments);
241 return detail::kernel::aggregateLocalAttributeIncrements(tree, increments, algebra);
242 }
243
254 template <LocalDecision Decision, class Algebra>
256 [[nodiscard]] static std::vector<NodeAttribute<typename Decision::Value>> compute(const MorphologicalTree& tree,
258 const Decision& decision, const Algebra& algebra) {
259 MMCFILTERS_CONTRACT_CHECKED_ONLY(detail::validateFiniteWindowLocalAttributeInput(tree));
260 return detail::kernel::computeFiniteWindowLocalAttribute(tree, observationWindow, decision, algebra);
261 }
262};
263
264} // namespace mmcfilters::local_attributes
constexpr NodeId InvalidNode
Sentinel value used to denote an invalid node identifier.
Definition Common.hpp:34
#define MMCFILTERS_CONTRACT_CHECKED_ONLY(...)
Executes validation statements only when defensive checks are enabled.
Definition Contract.hpp:67
Mutable connected-subset tree on a finite pixel domain.
bool isAlive(NodeId nodeId) const
Tests whether a node slot currently represents a live node.
int numPixels() const
Returns the cardinality of the pixel domain.
NodeId smallestNode(PixelId pixel) const
Returns the smallest node containing pixel.
bool isAncestor(NodeId u, NodeId v) const
Tests whether u is an ancestor of v.
const GridDomain2D & requireGridDomain2D(const char *context) const
Returns the regular 2D domain or rejects a geometry-dependent call.
NodeId parent(NodeId nodeId) const
Returns the direct parent of nodeId.
NodeId root() const
Returns the current hierarchy root.
Window-coordinate map whose missing entries represent out-of-domain samples.
Binary sample-visibility vector encoded in observation-window order.
Generic finite-window local-attribute computation over a connected-subset tree model.
static std::vector< LocalAttributeIncrement< typename Decision::Value > > computeLocalAttributeIncrements(const MorphologicalTree &tree, const ObservationWindow &observationWindow, const Decision &decision, const Algebra &algebra)
Computes dense increments from a decision and an independent additive algebra.
static AnchoredEntrySet anchoredEntrySet(const MorphologicalTree &tree, PixelId anchorPixel, const ObservationWindow &observationWindow)
Materializes the distinct anchored entries.
static OrderedAnchoredEntries orderedAnchoredEntries(const MorphologicalTree &tree, PixelId anchorPixel, const ObservationWindow &observationWindow)
Groups and orders entries from the smallest node toward the root.
static std::vector< NodeAttribute< typename Decision::Value > > compute(const MorphologicalTree &tree, const ObservationWindow &observationWindow, const Decision &decision, const Algebra &algebra)
Computes a finite-window attribute from independent decision and algebra policies.
static std::vector< EventDelta< typename Decision::Value > > computeEventDeltas(const MorphologicalTree &tree, PixelId anchorPixel, const ObservationWindow &observationWindow, const Decision &decision, const Algebra &algebra)
Computes event deltas from a decision and a separately supplied additive algebra.
static AnchoredEntryMap anchoredEntryMap(const MorphologicalTree &tree, PixelId anchorPixel, const ObservationWindow &observationWindow)
Materializes anchored entries in window-coordinate order.
static std::optional< NodeId > anchoredEntry(const MorphologicalTree &tree, PixelId anchorPixel, WindowOffset windowOffset)
Locates one translated sample on the anchor branch.
static std::optional< NodeId > anchoredEntry(const MorphologicalTree &tree, PixelId anchorPixel, PixelId samplePixel)
Locates a valid absolute sample on the anchor branch.
static AnchorBranch anchorBranch(const MorphologicalTree &tree, PixelId anchorPixel)
Materializes the branch from the smallest node to the root.
static std::vector< NodeAttribute< Value > > aggregateEventIncrements(const MorphologicalTree &tree, std::span< const LocalAttributeIncrement< Value > > increments, const Algebra &algebra)
Aggregates previously compiled increments with an independent event algebra.
static OrderedAnchoredEntries orderAnchoredEntriesByInclusion(const MorphologicalTree &tree, const AnchoredEntryMap &entryMap)
Groups and orders an existing entry map by increasing inclusion.
static BinaryVisibilityState binaryVisibilityState(const MorphologicalTree &tree, const AnchoredEntryMap &entryMap, NodeId node)
Evaluates visibility at one node of the anchor branch.
static std::vector< LocalAttributeIncrement< typename Decision::Value > > computeLocalAttributeIncrements(const MorphologicalTree &tree, const ObservationWindow &window, const Decision &decision, const Algebra &algebra)
Compiles every anchor into dense node-local increments.
static std::vector< EventDelta< typename Decision::Value > > computeEventDeltas(const MorphologicalTree &tree, PixelId anchorPixel, const ObservationWindow &window, const Decision &decision, const Algebra &algebra)
Compiles the state changes of one anchor into sparse node events.
Indexed finite set of translated-sample offsets.
Additive event algebra kept separate from the local decision.
Owning result for one computed scalar attribute layout and buffer.
Shape metadata optionally attached to the pixel domain.
int columns
Number of grid columns.
int rows
Number of grid rows.
Relative row-column offset of one translated local sample.