MorphologicalAttributeFilters
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/CommittedTreeAccess.hpp"
8#include "../utils/CommittedImageAccess.hpp"
9#include "../utils/Contract.hpp"
10
11#include <cmath>
12#include <concepts>
13#include <span>
14#include <stack>
15#include <stdexcept>
16#include <string>
17#include <type_traits>
18#include <vector>
19
20namespace mmcfilters {
21
24
27
28namespace detail::attribute_filtering {
29
30inline void requireNodePreservationMaskShape(const MorphologicalTree& tree, const NodePreservationMask& nodePreservationMask, const char* context) {
32 nodePreservationMask.size() == static_cast<std::size_t>(tree.numInternalNodeSlots()),
33 throw std::invalid_argument(std::string(context) + " nodePreservationMask size must match the internal node slot count."));
34}
35
36template <std::floating_point Real>
37inline void requireNodePreservationScores(const MorphologicalTree& tree, std::span<const Real> nodePreservationScores, const char* context) {
39 nodePreservationScores.size() == static_cast<std::size_t>(tree.numInternalNodeSlots()),
40 throw std::invalid_argument(std::string(context) + " nodePreservationScores size must match the internal node slot count."));
41 if constexpr (contract::validationsEnabled) {
42 for (std::size_t index = 0; index < nodePreservationScores.size(); ++index) {
43 const Real score = nodePreservationScores[index];
44 if (!std::isfinite(score) || score < Real{0} || score > Real{1}) {
45 throw std::invalid_argument(std::string(context) + " requires every nodePreservationScore to be finite and in [0, 1].");
46 }
47 }
48 }
49}
50
51template <AltitudeValue T>
52inline void applyDirectReconstruction(ValuedMorphologicalTreeView<T> valuedTree, const NodePreservationMask& nodePreservationMask, ImagePtr<T> output) {
53 const char* context = "DirectAttributeFilter::applyDirectAttributeFilter";
54 valuedTree.requireTopologyUnchanged(context);
55 const MorphologicalTree& tree = valuedTree.topology();
56 requireNodePreservationMaskShape(tree, nodePreservationMask, context);
57 MMCFILTERS_CONTRACT_REQUIRE(nodePreservationMask[static_cast<std::size_t>(tree.root())],
58 throw std::invalid_argument(std::string(context) + " requires the root node to be preserved."));
59 MMCFILTERS_CONTRACT_REQUIRE(output != nullptr, throw std::invalid_argument(std::string(context) + " requires a non-null output image."));
60 MMCFILTERS_CONTRACT_REQUIRE(output->getNumRows() == tree.numRows() && output->getNumColumns() == tree.numColumns(),
61 throw std::invalid_argument(std::string(context) + " output image shape must match the tree image domain."));
62
63 const std::span<const T> nodeAltitudes = valuedTree.nodeAltitudes();
64 std::vector<T> reconstructedNodeAltitudes(static_cast<std::size_t>(tree.numInternalNodeSlots()), T{});
65 const NodeId root = tree.root();
66 reconstructedNodeAltitudes[static_cast<std::size_t>(root)] = nodeAltitudes[static_cast<std::size_t>(root)];
67
68 std::stack<NodeId> pending;
69 pending.push(root);
70 while (!pending.empty()) {
71 const NodeId nodeId = pending.top();
72 pending.pop();
73 for (NodeId childId : detail::CommittedTreeAccess::children(tree, nodeId)) {
74 reconstructedNodeAltitudes[static_cast<std::size_t>(childId)] =
75 nodePreservationMask[static_cast<std::size_t>(childId)] ? nodeAltitudes[static_cast<std::size_t>(childId)]
76 : reconstructedNodeAltitudes[static_cast<std::size_t>(nodeId)];
77 pending.push(childId);
78 }
79 }
80
81 T* pixels = output->rawData();
82 for (NodeId nodeId : tree.aliveNodeIds()) {
83 for (PixelId pixel : detail::CommittedTreeAccess::properParts(tree, nodeId)) {
84 pixels[pixel] = reconstructedNodeAltitudes[static_cast<std::size_t>(nodeId)];
85 }
86 }
87}
88
89template <AltitudeValue T>
90[[nodiscard]] inline std::vector<AltitudeDifference<T>> computeHardNodeContributions(
91 ValuedMorphologicalTreeView<T> valuedTree, const NodePreservationMask& nodePreservationMask) {
92 const MorphologicalTree& tree = valuedTree.topology();
93 requireNodePreservationMaskShape(tree, nodePreservationMask, "HardSubtractiveAttributeFilter::applyHardSubtractiveAttributeFilter");
94 std::vector<AltitudeDifference<T>> nodeContributions(static_cast<std::size_t>(tree.numInternalNodeSlots()), AltitudeDifference<T>{});
95 for (NodeId nodeId : tree.aliveNodeIds()) {
96 if (nodePreservationMask[static_cast<std::size_t>(nodeId)]) {
97 nodeContributions[static_cast<std::size_t>(nodeId)] = valuedTree.nodeResidue(nodeId);
98 }
99 }
100 return nodeContributions;
101}
102
103template <AltitudeValue T, std::floating_point Real>
104[[nodiscard]] inline std::vector<Real> computeSoftNodeContributions(ValuedMorphologicalTreeView<T> valuedTree,
105 std::span<const Real> nodePreservationScores) {
106 const MorphologicalTree& tree = valuedTree.topology();
107 requireNodePreservationScores(tree, nodePreservationScores, "SoftSubtractiveAttributeFilter::applySoftSubtractiveAttributeFilter");
108 std::vector<Real> nodeContributions(static_cast<std::size_t>(tree.numInternalNodeSlots()), Real{});
109 for (NodeId nodeId : tree.aliveNodeIds()) {
110 nodeContributions[static_cast<std::size_t>(nodeId)] =
111 static_cast<Real>(valuedTree.nodeResidue(nodeId)) * nodePreservationScores[static_cast<std::size_t>(nodeId)];
112 }
113 return nodeContributions;
114}
115
116} // namespace detail::attribute_filtering
117
122template <AltitudeValue T> class DirectAttributeFilter {
123 private:
125
126 public:
129
135
140 explicit DirectAttributeFilter(const ValuedMorphologicalTree<T>& valuedTree) : valuedTree_(valuedTree.asView()) {}
141
148 ImagePtr<T> output = detail::CommittedImageAccess::create<T>(valuedTree_.topology().numRows(), valuedTree_.topology().numColumns());
149 detail::attribute_filtering::applyDirectReconstruction(valuedTree_, nodePreservationMask, output);
150 return output;
151 }
152};
153
158template <AltitudeValue T> class HardSubtractiveAttributeFilter {
159 private:
161
162 public:
165
168
174
180
187 valuedTree_.requireTopologyUnchanged("HardSubtractiveAttributeFilter::applyHardSubtractiveAttributeFilter");
188 const std::vector<OutputValue> nodeContributions = detail::attribute_filtering::computeHardNodeContributions(valuedTree_, nodePreservationMask);
190 valuedTree_.topology(), std::span<const OutputValue>(nodeContributions),
191 "HardSubtractiveAttributeFilter::applyHardSubtractiveAttributeFilter");
192 }
193};
194
200template <AltitudeValue T, std::floating_point Real = float> class SoftSubtractiveAttributeFilter {
201 private:
203
204 public:
207
213
219
226 valuedTree_.requireTopologyUnchanged("SoftSubtractiveAttributeFilter::applySoftSubtractiveAttributeFilter");
227 const std::vector<Real> nodeContributions = detail::attribute_filtering::computeSoftNodeContributions(valuedTree_, nodePreservationScores);
229 valuedTree_.topology(), std::span<const Real>(nodeContributions),
230 "SoftSubtractiveAttributeFilter::applySoftSubtractiveAttributeFilter");
231 }
232};
233
234template <AltitudeValue T>
235[[nodiscard]] inline ImagePtr<T> applyDirectAttributeFilter(ValuedMorphologicalTreeView<T> valuedTree,
236 const NodePreservationMask& nodePreservationMask) {
237 return DirectAttributeFilter<T>(valuedTree).applyDirectAttributeFilter(nodePreservationMask);
238}
239
240template <AltitudeValue T>
241[[nodiscard]] inline ImagePtr<T> applyDirectAttributeFilter(const ValuedMorphologicalTree<T>& valuedTree,
242 const NodePreservationMask& nodePreservationMask) {
243 return DirectAttributeFilter<T>(valuedTree).applyDirectAttributeFilter(nodePreservationMask);
244}
245
246template <AltitudeValue T>
247[[nodiscard]] inline ImagePtr<AltitudeDifference<T>> applyHardSubtractiveAttributeFilter(
248 ValuedMorphologicalTreeView<T> valuedTree, const NodePreservationMask& nodePreservationMask) {
249 return HardSubtractiveAttributeFilter<T>(valuedTree).applyHardSubtractiveAttributeFilter(nodePreservationMask);
250}
251
252template <AltitudeValue T>
253[[nodiscard]] inline ImagePtr<AltitudeDifference<T>> applyHardSubtractiveAttributeFilter(
254 const ValuedMorphologicalTree<T>& valuedTree, const NodePreservationMask& nodePreservationMask) {
255 return HardSubtractiveAttributeFilter<T>(valuedTree).applyHardSubtractiveAttributeFilter(nodePreservationMask);
256}
257
258template <AltitudeValue T, std::floating_point Real>
259[[nodiscard]] inline ImagePtr<Real> applySoftSubtractiveAttributeFilter(ValuedMorphologicalTreeView<T> valuedTree,
260 std::span<const Real> nodePreservationScores) {
261 return SoftSubtractiveAttributeFilter<T, Real>(valuedTree).applySoftSubtractiveAttributeFilter(nodePreservationScores);
262}
263
264template <AltitudeValue T, std::floating_point Real>
265[[nodiscard]] inline ImagePtr<Real> applySoftSubtractiveAttributeFilter(const ValuedMorphologicalTree<T>& valuedTree,
266 std::span<const Real> nodePreservationScores) {
267 return SoftSubtractiveAttributeFilter<T, Real>(valuedTree).applySoftSubtractiveAttributeFilter(nodePreservationScores);
268}
269
270} // 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.
Hard subtractive filter using independently gated zero-baseline residues.
ImagePtr< OutputValue > applyHardSubtractiveAttributeFilter(const NodePreservationMask &nodePreservationMask) const
Reconstructs from residues selected by preservation decisions.
HardSubtractiveAttributeFilter(ValuedMorphologicalTreeView< T > valuedTree)
Creates a hard subtractive filter over a valued-tree view.
HardSubtractiveAttributeFilter(const ValuedMorphologicalTree< T > &valuedTree)
Creates a hard subtractive 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.
static ImagePtr< Contribution > reconstructFromNodeContributions(const MorphologicalTree &tree, std::span< const Contribution > nodeContributions, const char *context="TreeAltitudeAlgorithms::reconstructFromNodeContributions")
Reconstructs an image by summing node contributions on every root-to-node branch.
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.