mmcfilters
Public API documentation
Loading...
Searching...
No Matches
NodePreservationStability.hpp
1#pragma once
2
3#include "DepthStableRegionComputer.hpp"
4#include "MSERComputer.hpp"
5#include "NodeDecisionMasks.hpp"
6#include "../trees/MorphologicalTree.hpp"
7#include "../trees/ValuedMorphologicalTree.hpp"
8#include "../utils/Altitude.hpp"
9#include "../utils/Contract.hpp"
10
11#include <concepts>
12#include <span>
13#include <stdexcept>
14#include <string>
15#include <type_traits>
16#include <utility>
17#include <vector>
18
19namespace mmcfilters {
20
22enum class IncompleteStabilityWindowPolicy {
24 PreserveInputDecision
25};
26
33template <class AttributeValue>
34 requires(std::is_arithmetic_v<AttributeValue> && !std::is_same_v<std::remove_cv_t<AttributeValue>, bool>)
35[[nodiscard]] NodePreservationMask computeNodePreservationMask(std::span<const AttributeValue> nodeAttributes, AttributeValue threshold) {
36 std::vector<bool> nodePreservationDecisions(nodeAttributes.size(), false);
37 for (std::size_t index = 0; index < nodeAttributes.size(); ++index) {
38 nodePreservationDecisions[index] = nodeAttributes[index] >= threshold;
39 }
40 return NodePreservationMask(std::move(nodePreservationDecisions));
41}
42
43namespace detail::node_preservation_stability {
44
45inline void requireNodePreservationMaskShape(const MorphologicalTree& tree, const NodePreservationMask& nodePreservationMask,
46 const char* context) {
48 nodePreservationMask.size() == static_cast<std::size_t>(tree.numInternalNodeSlots()),
49 throw std::invalid_argument(std::string(context) + " nodePreservationMask size must match the internal node slot count."));
50}
51
52template <std::floating_point Real, class StabilityComputer>
53[[nodiscard]] NodePreservationMask adjustNodePreservationMask(const MorphologicalTree& tree,
54 const NodePreservationMask& nodePreservationMask,
55 StabilityComputer& stabilityComputer,
56 IncompleteStabilityWindowPolicy incompleteWindowPolicy,
57 const char* context) {
58 requireNodePreservationMaskShape(tree, nodePreservationMask, context);
59 const std::vector<Real>& variations = stabilityComputer.getVariations();
60 std::vector<bool> adjustedDecisions(static_cast<std::size_t>(tree.numInternalNodeSlots()), true);
61
62 for (NodeId nodeId : tree.aliveNodeIds()) {
63 if (nodePreservationMask[static_cast<std::size_t>(nodeId)]) {
64 continue;
65 }
66
67 if (detail::isFiniteVariation(variations[static_cast<std::size_t>(nodeId)])) {
68 const NodeId adjustedRejection = stabilityComputer.nodeWithMinimumVariationInWindow(nodeId);
69 adjustedDecisions[static_cast<std::size_t>(adjustedRejection)] = false;
70 continue;
71 }
72
73 switch (incompleteWindowPolicy) {
74 case IncompleteStabilityWindowPolicy::PreserveInputDecision:
75 adjustedDecisions[static_cast<std::size_t>(nodeId)] = false;
76 break;
77 }
78 }
79
80 return NodePreservationMask(std::move(adjustedDecisions));
81}
82
83} // namespace detail::node_preservation_stability
84
90template <AltitudeValue T>
91[[nodiscard]] NodePreservationMask adjustNodePreservationMaskByAltitudeStability(
92 const ValuedMorphologicalTree<T>& valuedTree, const NodePreservationMask& nodePreservationMask,
93 AltitudeDifference<T> altitudeWindowRadius,
94 IncompleteStabilityWindowPolicy incompleteWindowPolicy = IncompleteStabilityWindowPolicy::PreserveInputDecision) {
95 MSERComputer<T> stabilityComputer(valuedTree);
96 (void)stabilityComputer.computeMSER(altitudeWindowRadius);
97 return detail::node_preservation_stability::adjustNodePreservationMask<float>(
98 valuedTree.topology(), nodePreservationMask, stabilityComputer, incompleteWindowPolicy,
99 "adjustNodePreservationMaskByAltitudeStability");
100}
101
103[[nodiscard]] inline NodePreservationMask adjustNodePreservationMaskByDepthStability(
104 const MorphologicalTree& tree, const NodePreservationMask& nodePreservationMask, int depthWindowRadius,
105 IncompleteStabilityWindowPolicy incompleteWindowPolicy = IncompleteStabilityWindowPolicy::PreserveInputDecision) {
106 DepthStableRegionComputer<float> stabilityComputer(tree);
107 (void)stabilityComputer.computeByDepth(depthWindowRadius);
108 return detail::node_preservation_stability::adjustNodePreservationMask<float>(
109 tree, nodePreservationMask, stabilityComputer, incompleteWindowPolicy,
110 "adjustNodePreservationMaskByDepthStability");
111}
112
114template <AltitudeValue T>
115[[nodiscard]] inline NodePreservationMask adjustNodePreservationMaskByDepthStability(
116 const ValuedMorphologicalTree<T>& valuedTree, const NodePreservationMask& nodePreservationMask, int depthWindowRadius,
117 IncompleteStabilityWindowPolicy incompleteWindowPolicy = IncompleteStabilityWindowPolicy::PreserveInputDecision) {
118 return adjustNodePreservationMaskByDepthStability(valuedTree.topology(), nodePreservationMask, depthWindowRadius,
119 incompleteWindowPolicy);
120}
121
122} // 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