mmcfilters
Public API documentation
Loading...
Searching...
No Matches
AttributeFilters.hpp
1#pragma once
2
3#include "../utils/Image.hpp"
4#include "../utils/Common.hpp"
5#include "../trees/TreeAltitudeAlgorithms.hpp"
6#include "../trees/ValuedMorphologicalTree.hpp"
7#include "../trees/ValuedMorphologicalTreeView.hpp"
8#include "../trees/detail/CommittedTreeAccess.hpp"
9#include "../trees/detail/TreeTraversalDetail.hpp"
10#include "../utils/CommittedImageAccess.hpp"
11#include "../utils/Contract.hpp"
12#include "AttributeReconstructionFilters.hpp"
13#include "detail/ViterbiDecision.hpp"
14
15#include <cmath>
16#include <concepts>
17#include <memory>
18#include <stack>
19#include <stdexcept>
20#include <string>
21#include <vector>
22
23namespace mmcfilters {
24
70template <AltitudeValue T> class AttributeFilters {
71 protected:
73
75
79 const ValuedMorphologicalTree<T>* valuedTree_ = nullptr;
81 const MorphologicalTree& tree;
83 std::size_t treeMutationVersion_ = 0;
84
90 AltitudeView view() const noexcept { return view_; }
91
97 void requireStableTree(const char* context) const { MMCFILTERS_CONTRACT_CHECKED_ONLY(tree.requireMutationVersion(treeMutationVersion_, context)); }
98
105 template <std::floating_point Real> static void requireAttributePointer(const Real* attribute, const char* context) {
106 MMCFILTERS_CONTRACT_REQUIRE(attribute != nullptr,
107 throw std::invalid_argument(std::string(context) + " requires a non-null attribute buffer."));
108 }
109
118 const char* context) {
119 MMCFILTERS_CONTRACT_REQUIRE(nodePreservationMask.size() == static_cast<std::size_t>(tree.numInternalNodeSlots()),
120 throw std::invalid_argument(std::string(context) +
121 " node-preservation-mask size must match the internal node slot count."));
122 }
123
131 template <typename TImagePtr> static void requireOutputImage(const MorphologicalTree& tree, const TImagePtr& image, const char* context) {
132 MMCFILTERS_CONTRACT_REQUIRE(image != nullptr, throw std::invalid_argument(std::string(context) + " requires a non-null output image."));
133 MMCFILTERS_CONTRACT_REQUIRE(image->getNumRows() == tree.numRows() && image->getNumColumns() == tree.numColumns(),
134 throw std::invalid_argument(std::string(context) + " output image shape must match the tree image domain."));
135 }
136
144 static T altitudeOf(const AltitudeView& view, NodeId nodeId) noexcept { return view.nodeAltitudes()[static_cast<std::size_t>(nodeId)]; }
145
157 template <typename TValue> static void writeProperParts(const MorphologicalTree& tree, NodeId nodeId, TValue* output, TValue value) {
158 for (PixelId pixel : detail::CommittedTreeAccess::properParts(tree, nodeId)) {
159 output[pixel] = value;
160 }
161 }
162
175 template <typename TValue> static void writeSubtreeProperParts(const MorphologicalTree& tree, NodeId nodeId, TValue* output, TValue value) {
176 for (NodeId subtreeNodeId : detail::CommittedTreeAccess::subtree(tree, nodeId)) {
178 }
179 }
180
195 const char* context = "AttributeFilters::filteringByPruningMin";
196 view.requireTopologyUnchanged(context);
197 const MorphologicalTree& tree = view.topology();
200 std::stack<NodeId> stack;
201 stack.push(tree.root());
202 auto imgOutput = imgOutputPtr->rawData();
203
204 while (!stack.empty()) {
205 const NodeId nodeId = stack.top();
206 stack.pop();
208 for (NodeId childNodeId : detail::CommittedTreeAccess::children(tree, nodeId)) {
209 if (nodePreservationMask[static_cast<std::size_t>(childNodeId)]) {
210 stack.push(childNodeId);
211 } else {
213 }
214 }
215 }
216 }
217
232 const char* context = "AttributeFilters::filteringByPruningMax";
233 view.requireTopologyUnchanged(context);
234 const MorphologicalTree& tree = view.topology();
237 // This internal marker means "this whole subtree can be collapsed",
238 // which is the opposite of the caller's preservation decision at the leaves
239 // before descendant information is merged.
240 std::vector<uint8_t> collapsibleRejectedSubtree(tree.numInternalNodeSlots(), false);
241 detail::traversePostOrder(
242 tree, tree.root(),
244 collapsibleRejectedSubtree[nodeId] = !nodePreservationMask[static_cast<std::size_t>(nodeId)];
245 },
247 collapsibleRejectedSubtree[parentNodeId] =
248 (collapsibleRejectedSubtree[parentNodeId] & collapsibleRejectedSubtree[childNodeId]);
249 },
250 [](NodeId) -> void {});
251
252 auto imgOutput = imgOutputPtr->rawData();
253 std::stack<NodeId> stack;
254 stack.push(tree.root());
255 while (!stack.empty()) {
256 const NodeId nodeId = stack.top();
257 stack.pop();
259 for (NodeId childNodeId : detail::CommittedTreeAccess::children(tree, nodeId)) {
261 stack.push(childNodeId);
262 } else {
264 }
265 }
266 }
267 }
268
283 template <std::floating_point Real>
284 static void filteringByPruningMinAttributeImpl(AltitudeView view, const Real* attribute, Real threshold, ImagePtr<T> imgOutputPtr) {
285 const char* context = "AttributeFilters::filteringByPruningMin";
286 view.requireTopologyUnchanged(context);
287 const MorphologicalTree& tree = view.topology();
290 auto imgOutput = imgOutputPtr->rawData();
291 std::stack<NodeId> stack;
292 stack.push(tree.root());
293 while (!stack.empty()) {
294 const NodeId nodeId = stack.top();
295 stack.pop();
297 for (NodeId childNodeId : detail::CommittedTreeAccess::children(tree, nodeId)) {
298 if (attribute[childNodeId] > threshold) {
299 stack.push(childNodeId);
300 } else {
302 }
303 }
304 }
305 }
306
320 template <std::floating_point Real>
321 static void filteringByPruningMaxAttributeImpl(AltitudeView view, const Real* attribute, Real threshold, ImagePtr<T> imgOutputPtr) {
322 const char* context = "AttributeFilters::filteringByPruningMax";
323 view.requireTopologyUnchanged(context);
324 const MorphologicalTree& tree = view.topology();
327 // This internal marker records collapsible rejected subtrees, not a
328 // direct keep decision. The post-order merge implements the universal
329 // quantifier over descendants.
330 std::vector<uint8_t> collapsibleRejectedSubtree(tree.numInternalNodeSlots(), false);
331 detail::traversePostOrder(
332 tree, tree.root(),
333 [&collapsibleRejectedSubtree, attribute, threshold](NodeId nodeId) -> void {
334 if (attribute[nodeId] <= threshold) {
335 collapsibleRejectedSubtree[nodeId] = true;
336 }
337 },
341 },
342 [](NodeId) -> void {});
343
344 auto imgOutput = imgOutputPtr->rawData();
345 std::stack<NodeId> stack;
346 stack.push(tree.root());
347 while (!stack.empty()) {
348 const NodeId nodeId = stack.top();
349 stack.pop();
351 for (NodeId childNodeId : detail::CommittedTreeAccess::children(tree, nodeId)) {
353 stack.push(childNodeId);
354 } else {
356 }
357 }
358 }
359 }
360
362
363 public:
372 explicit AttributeFilters(AltitudeView view) : view_{view}, tree{view_.topology()}, treeMutationVersion_{tree.getMutationVersion()} {
373 MMCFILTERS_CONTRACT_CHECKED_ONLY(view_.requireTopologyUnchanged("AttributeFilters"));
374 }
375
386
390 ~AttributeFilters() = default;
391
404 template <std::floating_point Real> [[nodiscard]] ImagePtr<T> filteringByPruningMin(const std::shared_ptr<Real[]>& attr, Real threshold) {
405 return filteringByPruningMin(attr.get(), threshold);
406 }
407
416 template <std::floating_point Real> [[nodiscard]] ImagePtr<T> filteringByPruningMin(const Real* attr, Real threshold) {
417 ImagePtr<T> imgOutput = Image<T>::create(this->tree.numRows(), this->tree.numColumns());
419 return imgOutput;
420 }
421
433 template <std::floating_point Real> [[nodiscard]] ImagePtr<T> filteringByPruningMax(const std::shared_ptr<Real[]>& attr, Real threshold) {
434 return filteringByPruningMax(attr.get(), threshold);
435 }
436
445 template <std::floating_point Real> [[nodiscard]] ImagePtr<T> filteringByPruningMax(const Real* attr, Real threshold) {
446 ImagePtr<T> imgOutput = Image<T>::create(this->tree.numRows(), this->tree.numColumns());
448 return imgOutput;
449 }
450
465 template <std::floating_point Real> [[nodiscard]] ImagePtr<T> filteringByViterbiRule(const Real* attr, Real threshold) {
466 requireStableTree("AttributeFilters::filteringByViterbiRule");
467 auto costs = detail::makeThresholdViterbiCosts(tree, attr, threshold);
468 NodePreservationMask nodePreservationMask(detail::computeViterbiPreservationDecisions(tree, costs));
469 return applyDirectAttributeFilter(view(), nodePreservationMask);
470 }
471
484
497
510
523
536
549
559 template <std::floating_point Real>
560 static void filteringByPruningMin(const ValuedMorphologicalTreeView<T>& valuedTree, const std::shared_ptr<Real[]>& attribute, Real threshold, ImagePtr<T> imgOutputPtr) {
561 filteringByPruningMin(valuedTree, attribute.get(), threshold, imgOutputPtr);
562 }
563
573 template <std::floating_point Real>
574 static void filteringByPruningMin(const ValuedMorphologicalTree<T>& valuedTree, const std::shared_ptr<Real[]>& attribute, Real threshold,
576 filteringByPruningMin(valuedTree.asView(), attribute.get(), threshold, imgOutputPtr);
577 }
578
588 template <std::floating_point Real>
592
602 template <std::floating_point Real>
604 filteringByPruningMin(valuedTree.asView(), attribute, threshold, imgOutputPtr);
605 }
606
616 template <std::floating_point Real>
617 static void filteringByPruningMax(const ValuedMorphologicalTreeView<T>& valuedTree, const std::shared_ptr<Real[]>& attribute, Real threshold, ImagePtr<T> imgOutputPtr) {
618 filteringByPruningMax(valuedTree, attribute.get(), threshold, imgOutputPtr);
619 }
620
630 template <std::floating_point Real>
631 static void filteringByPruningMax(const ValuedMorphologicalTree<T>& valuedTree, const std::shared_ptr<Real[]>& attribute, Real threshold,
633 filteringByPruningMax(valuedTree.asView(), attribute.get(), threshold, imgOutputPtr);
634 }
635
645 template <std::floating_point Real>
649
659 template <std::floating_point Real>
661 filteringByPruningMax(valuedTree.asView(), attribute, threshold, imgOutputPtr);
662 }
663
664};
665
666} // namespace mmcfilters
#define MMCFILTERS_CONTRACT_REQUIRE(condition,...)
Evaluates a caller precondition and its failure action only in checked builds.
Definition Contract.hpp:53
#define MMCFILTERS_CONTRACT_CHECKED_ONLY(...)
Executes validation statements only when defensive checks are enabled.
Definition Contract.hpp:67
Family of attribute-based image filtering operators on morphological trees.
AttributeFilters(const ValuedMorphologicalTree< T > &valuedTree)
Creates filtering operators over an owned valued tree.
static void filteringByPruningMin(const ValuedMorphologicalTree< T > &valuedTree, const std::shared_ptr< Real[]> &attribute, Real threshold, ImagePtr< T > imgOutputPtr)
Writes pruning-min filtering from a valued-tree owner and owned attribute buffer.
static void filteringByPruningMin(const ValuedMorphologicalTreeView< T > &valuedTree, const std::shared_ptr< Real[]> &attribute, Real threshold, ImagePtr< T > imgOutputPtr)
Writes pruning-min filtering from an owned attribute buffer into an output image.
static void filteringByPruningMax(const ValuedMorphologicalTree< T > &valuedTree, const Real *attribute, Real threshold, ImagePtr< T > imgOutputPtr)
Writes pruning-max filtering from a valued-tree owner and raw attribute buffer.
static void filteringByPruningMin(const ValuedMorphologicalTreeView< T > &valuedTree, const NodePreservationMask &nodePreservationMask, ImagePtr< T > imgOutputPtr)
Writes pruning-min filtering from a node-preservation mask into an output image.
static void filteringByPruningMin(const ValuedMorphologicalTree< T > &valuedTree, const NodePreservationMask &nodePreservationMask, ImagePtr< T > imgOutputPtr)
Writes pruning-min filtering from a valued-tree owner into an output image.
static void filteringByPruningMin(const ValuedMorphologicalTree< T > &valuedTree, const Real *attribute, Real threshold, ImagePtr< T > imgOutputPtr)
Writes pruning-min filtering from a valued-tree owner and raw attribute buffer.
static void filteringByPruningMax(const ValuedMorphologicalTree< T > &valuedTree, const std::shared_ptr< Real[]> &attribute, Real threshold, ImagePtr< T > imgOutputPtr)
Writes pruning-max filtering from a valued-tree owner and owned attribute buffer.
ImagePtr< T > filteringByPruningMin(const NodePreservationMask &nodePreservationMask)
Applies pruning-min filtering from a dense node-preservation mask.
static void filteringByPruningMax(const ValuedMorphologicalTreeView< T > &valuedTree, const NodePreservationMask &nodePreservationMask, ImagePtr< T > imgOutputPtr)
Writes pruning-max filtering from a node-preservation mask into an output image.
static void filteringByPruningMax(const ValuedMorphologicalTree< T > &valuedTree, const NodePreservationMask &nodePreservationMask, ImagePtr< T > imgOutputPtr)
Writes pruning-max filtering from a valued-tree owner into an output image.
ImagePtr< T > filteringByViterbiRule(const Real *attr, Real threshold)
Applies Salembier-style Viterbi filtering from a raw attribute buffer.
~AttributeFilters()=default
Destroys the attribute-filter facade.
AttributeFilters(AltitudeView view)
Creates filtering operators over a non-owning valued tree view.
static void filteringByPruningMin(const ValuedMorphologicalTreeView< T > &valuedTree, const Real *attribute, Real threshold, ImagePtr< T > imgOutputPtr)
Writes pruning-min filtering from a raw attribute buffer into an output image.
static void filteringByPruningMax(const ValuedMorphologicalTreeView< T > &valuedTree, const std::shared_ptr< Real[]> &attribute, Real threshold, ImagePtr< T > imgOutputPtr)
Writes pruning-max filtering from an owned attribute buffer into an output image.
ImagePtr< T > filteringByPruningMin(const std::shared_ptr< Real[]> &attr, Real threshold)
Applies pruning-min filtering from an attribute buffer.
ImagePtr< T > filteringByPruningMax(const Real *attr, Real threshold)
Applies pruning-max filtering from a raw internal-node attribute buffer.
static void filteringByPruningMax(const ValuedMorphologicalTreeView< T > &valuedTree, const Real *attribute, Real threshold, ImagePtr< T > imgOutputPtr)
Writes pruning-max filtering from a raw attribute buffer into an output image.
ImagePtr< T > filteringByPruningMax(const NodePreservationMask &nodePreservationMask)
Applies pruning-max filtering from a dense node-preservation mask.
ImagePtr< T > filteringByPruningMin(const Real *attr, Real threshold)
Applies pruning-min filtering from a raw internal-node attribute buffer.
ImagePtr< T > filteringByPruningMax(const std::shared_ptr< Real[]> &attr, Real threshold)
Applies pruning-max filtering from an attribute buffer.
static Ptr create(int rows, int columns)
Creates an owned image with uninitialised pixel values.
Definition Image.hpp:105
Mutable connected-subset tree on a finite pixel domain.
int numRows() const
Returns the number of rows in the regular 2D pixel domain.
int numInternalNodeSlots() const
Returns the size of the dense internal-node id domain.
void requireMutationVersion(std::size_t expectedVersion, const char *context) const
Rejects stale read-only views that captured an older mutation version.
int numColumns() const
Returns the number of columns in the regular 2D pixel domain.
NodeId root() const
Returns the current hierarchy root.
Dense Boolean decisions where true means preserve the node.
Owning result for one computed scalar attribute layout and buffer.