mmcfilters
Public API documentation
Loading...
Searching...
No Matches
AttributeReconstructionFilters.hpp
1#pragma once
2
3#include "NodeDecisionMasks.hpp"
4#include "../trees/TreeAltitudeAlgorithms.hpp"
5#include "../trees/ValuedMorphologicalTree.hpp"
6#include "../trees/ValuedMorphologicalTreeView.hpp"
7#include "../trees/detail/TreeKernelAccess.hpp"
8#include "../utils/CommittedImageAccess.hpp"
9#include "../utils/Contract.hpp"
10
11#include <algorithm>
12#include <cmath>
13#include <concepts>
14#include <span>
15#include <stack>
16#include <stdexcept>
17#include <string>
18#include <type_traits>
19#include <vector>
20
21namespace mmcfilters {
22
25
28
29namespace detail::attribute_filtering {
30
31inline void requireNodePreservationMaskShape(const MorphologicalTree& tree, const NodePreservationMask& nodePreservationMask, const char* context) {
33 nodePreservationMask.size() == static_cast<std::size_t>(tree.numInternalNodeSlots()),
34 throw std::invalid_argument(std::string(context) + " nodePreservationMask size must match the internal node slot count."));
35}
36
37template <std::floating_point Real>
38inline void requireNodePreservationScores(const MorphologicalTree& tree, std::span<const Real> nodePreservationScores, const char* context) {
40 nodePreservationScores.size() == static_cast<std::size_t>(tree.numInternalNodeSlots()),
41 throw std::invalid_argument(std::string(context) + " nodePreservationScores size must match the internal node slot count."));
42 if constexpr (contract::validationsEnabled) {
43 for (std::size_t index = 0; index < nodePreservationScores.size(); ++index) {
44 const Real score = nodePreservationScores[index];
45 if (!std::isfinite(score) || score < Real{0} || score > Real{1}) {
46 throw std::invalid_argument(std::string(context) + " requires every nodePreservationScore to be finite and in [0, 1].");
47 }
48 }
49 }
50}
51
52template <AltitudeValue T>
53[[nodiscard]] inline ImagePtr<T> applyDirectReconstruction(ValuedMorphologicalTreeView<T> valuedTree,
54 const NodePreservationMask& nodePreservationMask) {
55 const char* context = "DirectAttributeFilter::applyDirectAttributeFilter";
56 valuedTree.requireTopologyUnchanged(context);
57 const MorphologicalTree& tree = valuedTree.topology();
58 tree.requireNotEditing(context);
59 requireNodePreservationMaskShape(tree, nodePreservationMask, context);
60 MMCFILTERS_CONTRACT_REQUIRE(nodePreservationMask[static_cast<std::size_t>(tree.root())],
61 throw std::invalid_argument(std::string(context) + " requires the root node to be preserved."));
62 const std::span<const T> nodeAltitudes = valuedTree.nodeAltitudes();
63 std::vector<T> reconstructedNodeAltitudes(static_cast<std::size_t>(tree.numInternalNodeSlots()), T{});
64 const NodeId root = tree.root();
65 reconstructedNodeAltitudes[static_cast<std::size_t>(root)] = nodeAltitudes[static_cast<std::size_t>(root)];
66
67 std::stack<NodeId> pending;
68 pending.push(root);
69 while (!pending.empty()) {
70 const NodeId nodeId = pending.top();
71 pending.pop();
72 for (NodeId childId : detail::TreeKernelAccess::children(tree, nodeId)) {
73 reconstructedNodeAltitudes[static_cast<std::size_t>(childId)] =
74 nodePreservationMask[static_cast<std::size_t>(childId)] ? nodeAltitudes[static_cast<std::size_t>(childId)]
75 : reconstructedNodeAltitudes[static_cast<std::size_t>(nodeId)];
76 pending.push(childId);
77 }
78 }
79
80 ImagePtr<T> output = CommittedImageAccess::create<T>(tree.numRows(), tree.numColumns());
81 T* pixels = output->rawData();
82 for (NodeId nodeId : tree.aliveNodeIds()) {
83 for (PixelId pixel : detail::TreeKernelAccess::properParts(tree, nodeId)) {
84 pixels[pixel] = reconstructedNodeAltitudes[static_cast<std::size_t>(nodeId)];
85 }
86 }
87 return output;
88}
89
91template <AltitudeValue T>
92[[nodiscard]] inline AltitudeDifference<T> nodeResidueEstablished(const MorphologicalTree& tree, std::span<const T> nodeAltitudes, NodeId nodeId) {
93 const auto altitude = static_cast<AltitudeDifference<T>>(nodeAltitudes[static_cast<std::size_t>(nodeId)]);
94 if (nodeId == tree.root()) {
95 return altitude;
96 }
97 const NodeId parent = TreeKernelAccess::nodeParent(tree, nodeId);
98 return altitude - static_cast<AltitudeDifference<T>>(nodeAltitudes[static_cast<std::size_t>(parent)]);
99}
100
102template <class Contribution>
103[[nodiscard]] inline ImagePtr<Contribution> reconstructNodeContributionsEstablished(const MorphologicalTree& tree,
104 std::span<const Contribution> nodeContributions) {
105 const auto pixelValues = tree_altitude::reconstructNodeContributionValuesEstablished(tree, nodeContributions);
106 ImagePtr<Contribution> output = CommittedImageAccess::create<Contribution>(tree.numRows(), tree.numColumns());
107 std::copy(pixelValues.begin(), pixelValues.end(), output->rawData());
108 return output;
109}
110
111template <AltitudeValue T>
112[[nodiscard]] inline std::vector<AltitudeDifference<T>> computeGatedNodeContributions(
113 ValuedMorphologicalTreeView<T> valuedTree, const NodePreservationMask& nodePreservationMask) {
114 const char* context = "SubtractiveAttributeFilter::applySubtractiveAttributeFilter";
115 const MorphologicalTree& tree = valuedTree.topology();
116 tree.requireNotEditing(context);
117 requireNodePreservationMaskShape(tree, nodePreservationMask, context);
118 const auto nodeAltitudes = valuedTree.nodeAltitudes();
119 std::vector<AltitudeDifference<T>> nodeContributions(static_cast<std::size_t>(tree.numInternalNodeSlots()), AltitudeDifference<T>{});
120 for (NodeId nodeId : tree.aliveNodeIds()) {
121 if (nodePreservationMask[static_cast<std::size_t>(nodeId)]) {
122 const auto contribution = nodeResidueEstablished(tree, nodeAltitudes, nodeId);
123 tree_altitude::requireFiniteNodeContribution(contribution, context);
124 nodeContributions[static_cast<std::size_t>(nodeId)] = contribution;
125 }
126 }
127 return nodeContributions;
128}
129
130template <AltitudeValue T, std::floating_point Real>
131[[nodiscard]] inline std::vector<Real> computeSoftNodeContributions(ValuedMorphologicalTreeView<T> valuedTree,
132 std::span<const Real> nodePreservationScores) {
133 const char* context = "SoftSubtractiveAttributeFilter::applySoftSubtractiveAttributeFilter";
134 const MorphologicalTree& tree = valuedTree.topology();
135 tree.requireNotEditing(context);
136 requireNodePreservationScores(tree, nodePreservationScores, context);
137 const auto nodeAltitudes = valuedTree.nodeAltitudes();
138 std::vector<Real> nodeContributions(static_cast<std::size_t>(tree.numInternalNodeSlots()), Real{});
139 for (NodeId nodeId : tree.aliveNodeIds()) {
140 const Real contribution =
141 static_cast<Real>(nodeResidueEstablished(tree, nodeAltitudes, nodeId)) * nodePreservationScores[static_cast<std::size_t>(nodeId)];
142 tree_altitude::requireFiniteNodeContribution(contribution, context);
143 nodeContributions[static_cast<std::size_t>(nodeId)] = contribution;
144 }
145 return nodeContributions;
146}
147
148} // namespace detail::attribute_filtering
149
154template <AltitudeValue T> class DirectAttributeFilter {
155 private:
157
158 public:
161
167
172 explicit DirectAttributeFilter(const ValuedMorphologicalTree<T>& valuedTree) : valuedTree_(valuedTree.asView()) {}
173
180 return detail::attribute_filtering::applyDirectReconstruction(valuedTree_, nodePreservationMask);
181 }
182};
183
188template <AltitudeValue T> class SubtractiveAttributeFilter {
189 private:
191
192 public:
195
198
204
210
217 valuedTree_.requireTopologyUnchanged("SubtractiveAttributeFilter::applySubtractiveAttributeFilter");
218 const std::vector<OutputValue> nodeContributions = detail::attribute_filtering::computeGatedNodeContributions(valuedTree_, nodePreservationMask);
219 return detail::attribute_filtering::reconstructNodeContributionsEstablished(
220 valuedTree_.topology(), std::span<const OutputValue>(nodeContributions));
221 }
222};
223
229template <AltitudeValue T, std::floating_point Real = float> class SoftSubtractiveAttributeFilter {
230 private:
232
233 public:
236
242
248
255 valuedTree_.requireTopologyUnchanged("SoftSubtractiveAttributeFilter::applySoftSubtractiveAttributeFilter");
256 const std::vector<Real> nodeContributions = detail::attribute_filtering::computeSoftNodeContributions(valuedTree_, nodePreservationScores);
257 return detail::attribute_filtering::reconstructNodeContributionsEstablished(
258 valuedTree_.topology(), std::span<const Real>(nodeContributions));
259 }
260};
261
262template <AltitudeValue T>
263[[nodiscard]] inline ImagePtr<T> applyDirectAttributeFilter(ValuedMorphologicalTreeView<T> valuedTree,
264 const NodePreservationMask& nodePreservationMask) {
265 return DirectAttributeFilter<T>(valuedTree).applyDirectAttributeFilter(nodePreservationMask);
266}
267
268template <AltitudeValue T>
269[[nodiscard]] inline ImagePtr<T> applyDirectAttributeFilter(const ValuedMorphologicalTree<T>& valuedTree,
270 const NodePreservationMask& nodePreservationMask) {
271 return DirectAttributeFilter<T>(valuedTree).applyDirectAttributeFilter(nodePreservationMask);
272}
273
274template <AltitudeValue T>
275[[nodiscard]] inline ImagePtr<AltitudeDifference<T>> applySubtractiveAttributeFilter(
276 ValuedMorphologicalTreeView<T> valuedTree, const NodePreservationMask& nodePreservationMask) {
277 return SubtractiveAttributeFilter<T>(valuedTree).applySubtractiveAttributeFilter(nodePreservationMask);
278}
279
280template <AltitudeValue T>
281[[nodiscard]] inline ImagePtr<AltitudeDifference<T>> applySubtractiveAttributeFilter(
282 const ValuedMorphologicalTree<T>& valuedTree, const NodePreservationMask& nodePreservationMask) {
283 return SubtractiveAttributeFilter<T>(valuedTree).applySubtractiveAttributeFilter(nodePreservationMask);
284}
285
286template <AltitudeValue T, std::floating_point Real>
287[[nodiscard]] inline ImagePtr<Real> applySoftSubtractiveAttributeFilter(ValuedMorphologicalTreeView<T> valuedTree,
288 std::span<const Real> nodePreservationScores) {
289 return SoftSubtractiveAttributeFilter<T, Real>(valuedTree).applySoftSubtractiveAttributeFilter(nodePreservationScores);
290}
291
292template <AltitudeValue T, std::floating_point Real>
293[[nodiscard]] inline ImagePtr<Real> applySoftSubtractiveAttributeFilter(const ValuedMorphologicalTree<T>& valuedTree,
294 std::span<const Real> nodePreservationScores) {
295 return SoftSubtractiveAttributeFilter<T, Real>(valuedTree).applySoftSubtractiveAttributeFilter(nodePreservationScores);
296}
297
298} // namespace mmcfilters
int NodeId
Node identifier type used throughout the project.
Definition Common.hpp:17
#define MMCFILTERS_CONTRACT_REQUIRE(condition,...)
Evaluates a caller precondition and its failure action only in checked builds.
Definition Contract.hpp:53
Direct attribute filter using reconstructed-parent altitude propagation.
DirectAttributeFilter(ValuedMorphologicalTreeView< T > valuedTree)
Creates a direct filter over a valued-tree view.
ImagePtr< T > applyDirectAttributeFilter(const NodePreservationMask &nodePreservationMask) const
Applies direct reconstruction under node-preservation decisions.
DirectAttributeFilter(const ValuedMorphologicalTree< T > &valuedTree)
Creates a direct filter over an owning valued tree.
Mutable connected-subset tree on a finite pixel domain.
int numInternalNodeSlots() const
Returns the size of the dense internal-node id domain.
Dense Boolean decisions where true means preserve the node.
Soft subtractive filter using scores in [0,1] to gate zero-baseline residues.
SoftSubtractiveAttributeFilter(ValuedMorphologicalTreeView< T > valuedTree)
Creates a soft subtractive filter over a valued-tree view.
SoftSubtractiveAttributeFilter(const ValuedMorphologicalTree< T > &valuedTree)
Creates a soft subtractive filter over an owning valued tree.
ImagePtr< Real > applySoftSubtractiveAttributeFilter(std::span< const Real > nodePreservationScores) const
Reconstructs from residues modulated by preservation scores.
Subtractive filter using independently gated zero-baseline residues.
SubtractiveAttributeFilter(const ValuedMorphologicalTree< T > &valuedTree)
Creates a subtractive filter over an owning valued tree.
ImagePtr< OutputValue > applySubtractiveAttributeFilter(const NodePreservationMask &nodePreservationMask) const
Reconstructs from residues selected by preservation decisions.
SubtractiveAttributeFilter(ValuedMorphologicalTreeView< T > valuedTree)
Creates a subtractive filter over a valued-tree view.
Owning result for one computed scalar attribute layout and buffer.
Policy tag for parent-altitude propagation after node rejection.
Policy tag for independent zero-baseline modulation of node residues.