MorphologicalAttributeFilters
Public API documentation
Loading...
Searching...
No Matches
ExtinctionValues.hpp
1#pragma once
2
3#include "../trees/TreeAltitudeAlgorithms.hpp"
4#include "../trees/WeightedMorphologicalTree.hpp"
5#include "../trees/WeightedTreeView.hpp"
6#include "../trees/detail/TreeKindValidation.hpp"
7#include "../utils/Image.hpp"
8#include "../utils/Common.hpp"
9#include "../contours/ContoursComputedIncrementally.hpp"
10
11#include <algorithm>
12#include <concepts>
13#include <limits>
14#include <memory>
15#include <stack>
16#include <stdexcept>
17#include <string>
18#include <vector>
19
20namespace mmcfilters {
21
25template <std::floating_point Real = float>
47
74template<AltitudeValue T, std::floating_point Real = float>
76protected:
79
80 std::vector<RegionalExtremaNode<Real>> regionalExtremaNodes;
82 const WeightedMorphologicalTree<T>* weighted_ = nullptr;
83 const MorphologicalTree& tree;
84 std::size_t treeMutationVersion_ = 0;
85
86 AltitudeView view() const {
87 return weighted_ != nullptr ? weighted_->asView() : view_;
88 }
89
90 void requireStableTree(const char* context) const {
91 tree.requireMutationVersion(treeMutationVersion_, context);
92 }
93
94 static void requireAttributePointer(const Real* attr, const char* context) {
95 if (attr == nullptr) {
96 throw std::invalid_argument(std::string(context) + " requires a non-null attribute buffer.");
97 }
98 }
99
100 static const Real* requireAttributeBuffer(const MorphologicalTree& tree, const std::vector<Real>& attr, const char* context) {
101 if (attr.size() != static_cast<std::size_t>(tree.getNumInternalNodeSlots())) {
102 throw std::invalid_argument(std::string(context) + " attribute size must match the internal node slot count.");
103 }
104 return attr.data();
105 }
106
107 static T altitudeOf(const AltitudeView& view, NodeId nodeId) {
108 return view.getAltitude(nodeId);
109 }
110
111 std::vector<NodeId> collectComponentTreeExtrema() const {
112 return this->tree.getLeaves();
113 }
114
115 static Real dominantExtremumSentinel() noexcept {
116 return std::numeric_limits<Real>::max();
117 }
118
119 static void requireNonNegativeExtremaToKeep(int extremaToKeep, const char* context) {
120 if (extremaToKeep < 0) {
121 throw std::invalid_argument(std::string(context) + " requires a non-negative extremaToKeep value.");
122 }
123 }
124
125 void initialize(const Real* attr) {
126 requireAttributePointer(attr, "ExtinctionValues");
127 std::vector<NodeId> leaves = collectComponentTreeExtrema();
128 regionalExtremaNodes.reserve(leaves.size());
129 std::vector<uint8_t> visited(this->tree.getNumInternalNodeSlots(), false);
130 for (NodeId leafNodeId : leaves) {
131 Real extinction = dominantExtremumSentinel();
134 bool flag = true;
135 while (flag && !this->tree.isRoot(cutoffNodeId)) {
136 if (this->tree.getNumChildren(parentNodeId) > 1) {
137 for (NodeId sonNodeId : this->tree.getChildren(parentNodeId)) {
138 if (flag) {
139 if (visited[sonNodeId] && sonNodeId != cutoffNodeId && attr[sonNodeId] == attr[cutoffNodeId]) {
140 flag = false;
141 } else if (sonNodeId != cutoffNodeId && attr[sonNodeId] > attr[cutoffNodeId]) {
142 flag = false;
143 }
144 visited[sonNodeId] = true;
145 }
146 }
147 }
148 if (flag) {
151 }
152 }
153 if (!this->tree.isRoot(cutoffNodeId)) {
154 extinction = attr[cutoffNodeId];
155 }
156 regionalExtremaNodes.emplace_back(leafNodeId, cutoffNodeId, extinction);
157 }
158
159 std::sort(regionalExtremaNodes.begin(), regionalExtremaNodes.end(), [](const auto& a, const auto& b) {
160 if (a.extinction != b.extinction) {
161 return a.extinction > b.extinction;
162 }
163 if (a.cutoffNode != b.cutoffNode) {
164 return a.cutoffNode < b.cutoffNode;
165 }
166 return a.leaf < b.leaf;
167 });
168 }
170
171public:
180 ExtinctionValues(const AltitudeView& view, const std::shared_ptr<Real[]>& attr)
181 : ExtinctionValues(view, attr.get()) {}
182
189 ExtinctionValues(const AltitudeView& view, const std::vector<Real>& attr)
190 : ExtinctionValues(view, requireAttributeBuffer(view.topology(), attr, "ExtinctionValues")) {}
191
201 : view_(view),
202 tree(view_.topology()),
203 treeMutationVersion_(tree.getMutationVersion()) {
204 view_.requireTopologyUnchanged("ExtinctionValues");
205 detail::validateComponentTreeKind(this->tree, "ExtinctionValues");
206 initialize(attr);
207 }
208
214 ExtinctionValues(const WeightedMorphologicalTree<T>& weighted, const std::shared_ptr<Real[]>& attr)
215 : ExtinctionValues(weighted.asView(), attr.get()) {
216 weighted_ = &weighted;
217 }
218
227 ExtinctionValues(const WeightedMorphologicalTree<T>& weighted, const std::vector<Real>& attr)
228 : ExtinctionValues(weighted.asView(), attr) {
229 weighted_ = &weighted;
230 }
231
242 : ExtinctionValues(weighted.asView(), attr) {
243 weighted_ = &weighted;
244 }
245
257 requireStableTree("ExtinctionValues::saliencyMap");
258 requireNonNegativeExtremaToKeep(extremaToKeep, "ExtinctionValues::saliencyMap");
259 std::vector<uint8_t> keep(tree.getNumInternalNodeSlots(), false);
260 std::vector<Real> extinctionByNode(tree.getNumInternalNodeSlots(), Real{0});
261 std::vector<NodeId> keptNodes;
262 const int leafToKeep = std::min(extremaToKeep, static_cast<int>(regionalExtremaNodes.size()));
263 for (int i = 0; i < leafToKeep; ++i) {
264 const NodeId cutoffNode = this->regionalExtremaNodes[i].cutoffNode;
265 if (!keep[cutoffNode]) {
266 keptNodes.push_back(cutoffNode);
267 }
268 keep[cutoffNode] = true;
269 extinctionByNode[cutoffNode] = unweighted ? static_cast<Real>(leafToKeep - i) : this->regionalExtremaNodes[i].extinction;
270 }
271
272 ImagePtr<Real> imgOutputPtr = Image<Real>::create(tree.getNumRowsOfImage(), tree.getNumColsOfImage(), Real{0});
273 auto saliencyOutput = imgOutputPtr->rawData();
274
276 for (NodeId node : keptNodes) {
277 for (int p : contours.getContour(node)) {
279 }
280 }
281
282 return imgOutputPtr;
283 }
284
294 requireStableTree("ExtinctionValues::filtering");
295 requireNonNegativeExtremaToKeep(extremaToKeep, "ExtinctionValues::filtering");
297 std::vector<uint8_t> criterion(tree.getNumInternalNodeSlots(), false);
298 const int leafToKeep = std::min(extremaToKeep, static_cast<int>(regionalExtremaNodes.size()));
299 for (int i = 0; i < leafToKeep; i++) {
300 criterion[regionalExtremaNodes[i].leaf] = true;
301 }
302 for (NodeId nodeId : tree.getPostOrderNodes()) {
303 if (!tree.isRoot(nodeId) && criterion[nodeId]) {
304 criterion[tree.getNodeParent(nodeId)] = true;
305 }
306 }
307
308 ImagePtr<T> imgOutputPtr = Image<T>::create(tree.getNumRowsOfImage(), tree.getNumColsOfImage(), T{});
309 auto imgOutput = imgOutputPtr->rawData();
310 std::stack<NodeId> stack;
311 stack.push(tree.getRoot());
312 while (!stack.empty()) {
313 const NodeId nodeId = stack.top();
314 stack.pop();
315 const T level = altitudeOf(altitudeView, nodeId);
316 for (int pixel : tree.getProperParts(nodeId)) {
317 imgOutput[pixel] = level;
318 }
319 for (NodeId childNodeId : tree.getChildren(nodeId)) {
320 if (criterion[childNodeId]) {
321 stack.push(childNodeId);
322 } else {
323 for (NodeId subtreeNodeId : tree.getNodeSubtree(childNodeId)) {
324 for (int pixel : tree.getProperParts(subtreeNodeId)) {
325 imgOutput[pixel] = level;
326 }
327 }
328 }
329 }
330 }
331 return imgOutputPtr;
332 }
333
340 std::vector<RegionalExtremaNode<Real>>& getExtinctionValues() {
341 requireStableTree("ExtinctionValues::getExtinctionValues");
343 }
344};
345
346
347} // namespace mmcfilters
static IncrementalContours extractCompactContours(const MorphologicalTree &tree)
Runs incremental contour computation and returns compact contours.
Computes and stores extinction values for regional extrema.
ExtinctionValues(const AltitudeView &view, const std::shared_ptr< Real[]> &attr)
Computes extinction values from a weighted view and shared attribute buffer.
ExtinctionValues(const WeightedMorphologicalTree< T > &weighted, const Real *attr)
Computes extinction values from a weighted tree and raw attribute buffer.
ImagePtr< T > filtering(int extremaToKeep)
Reconstructs an image by keeping the strongest extrema.
ExtinctionValues(const AltitudeView &view, const Real *attr)
Computes extinction values from a weighted view and raw attribute buffer.
std::vector< RegionalExtremaNode< Real > > & getExtinctionValues()
Returns the extinction records sorted by decreasing extinction.
ImagePtr< Real > saliencyMap(int extremaToKeep, bool unweighted=true)
Builds a contour saliency image from the strongest extrema.
ExtinctionValues(const WeightedMorphologicalTree< T > &weighted, const std::vector< Real > &attr)
Computes extinction values from a weighted tree and vector attribute buffer.
ExtinctionValues(const WeightedMorphologicalTree< T > &weighted, const std::shared_ptr< Real[]> &attr)
Computes extinction values from a weighted tree and shared attribute buffer.
ExtinctionValues(const AltitudeView &view, const std::vector< Real > &attr)
Computes extinction values from a weighted view and vector attribute buffer.
static Ptr create(int rows, int cols)
Creates an owned image with uninitialised pixel values.
Definition Image.hpp:79
Mutable morphological tree built directly on proper parts and dense node ids.
ChildrenRange getChildren(NodeId nodeId) const
Returns a fail-fast range over the direct children of nodeId.
void requireMutationVersion(std::size_t expectedVersion, const char *context) const
Rejects stale read-only views that captured an older mutation version.
int getNumInternalNodeSlots() const
Returns the size of the dense internal-node id domain.
std::vector< NodeId > getLeaves() const
Returns all live leaf nodes in the current hierarchy.
bool isRoot(NodeId nodeId) const
Tests whether nodeId is the current root.
int getNumChildren(NodeId nodeId) const
Returns the number of direct children of nodeId.
NodeId getNodeParent(NodeId nodeId) const
Returns the direct parent of nodeId.
Owning result for one computed scalar attribute layout and buffer.
Record describing one regional extremum and its extinction value.
RegionalExtremaNode(NodeId leaf, NodeId cutoffNode, Real extinction)
Builds one extinction-value record.
Real extinction
Attribute value at cutoffNode, or numeric_limits<Real>::max() for the dominant extremum that survives...
NodeId cutoffNode
Highest node retained before the extremum merges with a stronger branch.
NodeId leaf
Leaf node that represents the regional extremum in a max-tree/min-tree.