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/TreeKernelAccess.hpp"
9#include "../utils/CommittedImageAccess.hpp"
10#include "../utils/Contract.hpp"
11#include "AttributeReconstructionFilters.hpp"
12#include "detail/ViterbiDecision.hpp"
13
14#include <cmath>
15#include <concepts>
16#include <memory>
17#include <stack>
18#include <stdexcept>
19#include <string>
20#include <vector>
21
22namespace mmcfilters {
23
69template <AltitudeValue T> class AttributeFilters {
70 protected:
72
74
78 const ValuedMorphologicalTree<T>* valuedTree_ = nullptr;
80 const MorphologicalTree& tree;
82 std::size_t treeMutationVersion_ = 0;
83
89 AltitudeView view() const noexcept { return view_; }
90
96 void requireStableTree(const char* context) const { MMCFILTERS_CONTRACT_CHECKED_ONLY(tree.requireMutationVersion(treeMutationVersion_, context)); }
97
104 template <std::floating_point Real> static void requireAttributePointer(const Real* attribute, const char* context) {
105 MMCFILTERS_CONTRACT_REQUIRE(attribute != nullptr,
106 throw std::invalid_argument(std::string(context) + " requires a non-null attribute buffer."));
107 }
108
117 const char* context) {
118 MMCFILTERS_CONTRACT_REQUIRE(nodePreservationMask.size() == static_cast<std::size_t>(tree.numInternalNodeSlots()),
119 throw std::invalid_argument(std::string(context) +
120 " node-preservation-mask size must match the internal node slot count."));
121 }
122
130 template <typename TImagePtr> static void requireOutputImage(const MorphologicalTree& tree, const TImagePtr& image, const char* context) {
131 MMCFILTERS_CONTRACT_REQUIRE(image != nullptr, throw std::invalid_argument(std::string(context) + " requires a non-null output image."));
132 MMCFILTERS_CONTRACT_REQUIRE(image->getNumRows() == tree.numRows() && image->getNumColumns() == tree.numColumns(),
133 throw std::invalid_argument(std::string(context) + " output image shape must match the tree image domain."));
134 }
135
143 static T altitudeOf(const AltitudeView& view, NodeId nodeId) noexcept { return view.nodeAltitudes()[static_cast<std::size_t>(nodeId)]; }
144
156 template <typename TValue> static void writeProperParts(const MorphologicalTree& tree, NodeId nodeId, TValue* output, TValue value) {
157 for (PixelId pixel : detail::TreeKernelAccess::properParts(tree, nodeId)) {
158 output[pixel] = value;
159 }
160 }
161
174 template <typename TValue> static void writeSubtreeProperParts(const MorphologicalTree& tree, NodeId nodeId, TValue* output, TValue value) {
175 for (NodeId subtreeNodeId : detail::TreeKernelAccess::subtree(tree, nodeId)) {
177 }
178 }
179
194 const char* context = "AttributeFilters::filteringByPruningMin";
195 view.requireTopologyUnchanged(context);
196 const MorphologicalTree& tree = view.topology();
199 std::stack<NodeId> stack;
200 stack.push(tree.root());
201 auto imgOutput = imgOutputPtr->rawData();
202
203 while (!stack.empty()) {
204 const NodeId nodeId = stack.top();
205 stack.pop();
206 writeProperParts(tree, nodeId, imgOutput, altitudeOf(view, nodeId));
207 for (NodeId childNodeId : detail::TreeKernelAccess::children(tree, nodeId)) {
208 if (nodePreservationMask[static_cast<std::size_t>(childNodeId)]) {
209 stack.push(childNodeId);
210 } else {
212 }
213 }
214 }
215 }
216
231 const char* context = "AttributeFilters::filteringByPruningMax";
232 view.requireTopologyUnchanged(context);
233 const MorphologicalTree& tree = view.topology();
236 // This internal marker means "this whole subtree can be collapsed",
237 // which is the opposite of the caller's preservation decision at the leaves
238 // before descendant information is merged.
239 std::vector<uint8_t> collapsibleRejectedSubtree(tree.numInternalNodeSlots(), false);
240 detail::TreeKernelAccess::traversePostOrder(
241 tree, tree.root(),
243 collapsibleRejectedSubtree[nodeId] = !nodePreservationMask[static_cast<std::size_t>(nodeId)];
244 },
246 collapsibleRejectedSubtree[parentNodeId] =
247 (collapsibleRejectedSubtree[parentNodeId] & collapsibleRejectedSubtree[childNodeId]);
248 },
249 [](NodeId) -> void {});
250
251 auto imgOutput = imgOutputPtr->rawData();
252 std::stack<NodeId> stack;
253 stack.push(tree.root());
254 while (!stack.empty()) {
255 const NodeId nodeId = stack.top();
256 stack.pop();
257 writeProperParts(tree, nodeId, imgOutput, altitudeOf(view, nodeId));
258 for (NodeId childNodeId : detail::TreeKernelAccess::children(tree, nodeId)) {
260 stack.push(childNodeId);
261 } else {
263 }
264 }
265 }
266 }
267
282 template <std::floating_point Real>
283 static void filteringByPruningMinAttributeImpl(AltitudeView view, const Real* attribute, Real threshold, ImagePtr<T> imgOutputPtr) {
284 const char* context = "AttributeFilters::filteringByPruningMin";
285 view.requireTopologyUnchanged(context);
286 const MorphologicalTree& tree = view.topology();
289 auto imgOutput = imgOutputPtr->rawData();
290 std::stack<NodeId> stack;
291 stack.push(tree.root());
292 while (!stack.empty()) {
293 const NodeId nodeId = stack.top();
294 stack.pop();
295 writeProperParts(tree, nodeId, imgOutput, altitudeOf(view, nodeId));
296 for (NodeId childNodeId : detail::TreeKernelAccess::children(tree, nodeId)) {
297 if (attribute[childNodeId] > threshold) {
298 stack.push(childNodeId);
299 } else {
301 }
302 }
303 }
304 }
305
319 template <std::floating_point Real>
320 static void filteringByPruningMaxAttributeImpl(AltitudeView view, const Real* attribute, Real threshold, ImagePtr<T> imgOutputPtr) {
321 const char* context = "AttributeFilters::filteringByPruningMax";
322 view.requireTopologyUnchanged(context);
323 const MorphologicalTree& tree = view.topology();
326 // This internal marker records collapsible rejected subtrees, not a
327 // direct keep decision. The post-order merge implements the universal
328 // quantifier over descendants.
329 std::vector<uint8_t> collapsibleRejectedSubtree(tree.numInternalNodeSlots(), false);
330 detail::TreeKernelAccess::traversePostOrder(
331 tree, tree.root(),
332 [&collapsibleRejectedSubtree, attribute, threshold](NodeId nodeId) -> void {
333 if (attribute[nodeId] <= threshold) {
334 collapsibleRejectedSubtree[nodeId] = true;
335 }
336 },
340 },
341 [](NodeId) -> void {});
342
343 auto imgOutput = imgOutputPtr->rawData();
344 std::stack<NodeId> stack;
345 stack.push(tree.root());
346 while (!stack.empty()) {
347 const NodeId nodeId = stack.top();
348 stack.pop();
349 writeProperParts(tree, nodeId, imgOutput, altitudeOf(view, nodeId));
350 for (NodeId childNodeId : detail::TreeKernelAccess::children(tree, nodeId)) {
352 stack.push(childNodeId);
353 } else {
355 }
356 }
357 }
358 }
359
361
362 public:
371 explicit AttributeFilters(AltitudeView view) : view_{view}, tree{view_.topology()}, treeMutationVersion_{tree.getMutationVersion()} {
372 MMCFILTERS_CONTRACT_CHECKED_ONLY(view_.requireTopologyUnchanged("AttributeFilters"));
373 }
374
385
389 ~AttributeFilters() = default;
390
403 template <std::floating_point Real> [[nodiscard]] ImagePtr<T> filteringByPruningMin(const std::shared_ptr<Real[]>& attr, Real threshold) {
404 return filteringByPruningMin(attr.get(), threshold);
405 }
406
415 template <std::floating_point Real> [[nodiscard]] ImagePtr<T> filteringByPruningMin(const Real* attr, Real threshold) {
416 ImagePtr<T> imgOutput = Image<T>::create(this->tree.numRows(), this->tree.numColumns());
418 return imgOutput;
419 }
420
432 template <std::floating_point Real> [[nodiscard]] ImagePtr<T> filteringByPruningMax(const std::shared_ptr<Real[]>& attr, Real threshold) {
433 return filteringByPruningMax(attr.get(), threshold);
434 }
435
444 template <std::floating_point Real> [[nodiscard]] ImagePtr<T> filteringByPruningMax(const Real* attr, Real threshold) {
445 ImagePtr<T> imgOutput = Image<T>::create(this->tree.numRows(), this->tree.numColumns());
447 return imgOutput;
448 }
449
464 template <std::floating_point Real> [[nodiscard]] ImagePtr<T> filteringByViterbiRule(const Real* attr, Real threshold) {
465 requireStableTree("AttributeFilters::filteringByViterbiRule");
466 auto costs = detail::makeThresholdViterbiCosts(tree, attr, threshold);
467 NodePreservationMask nodePreservationMask(detail::computeViterbiPreservationDecisions(tree, costs));
468 return applyDirectAttributeFilter(view(), nodePreservationMask);
469 }
470
483
496
509
522
535
548
558 template <std::floating_point Real>
559 static void filteringByPruningMin(const ValuedMorphologicalTreeView<T>& valuedTree, const std::shared_ptr<Real[]>& attribute, Real threshold, ImagePtr<T> imgOutputPtr) {
560 filteringByPruningMin(valuedTree, attribute.get(), threshold, imgOutputPtr);
561 }
562
572 template <std::floating_point Real>
573 static void filteringByPruningMin(const ValuedMorphologicalTree<T>& valuedTree, const std::shared_ptr<Real[]>& attribute, Real threshold,
575 filteringByPruningMin(valuedTree.asView(), attribute.get(), threshold, imgOutputPtr);
576 }
577
587 template <std::floating_point Real>
591
601 template <std::floating_point Real>
603 filteringByPruningMin(valuedTree.asView(), attribute, threshold, imgOutputPtr);
604 }
605
615 template <std::floating_point Real>
616 static void filteringByPruningMax(const ValuedMorphologicalTreeView<T>& valuedTree, const std::shared_ptr<Real[]>& attribute, Real threshold, ImagePtr<T> imgOutputPtr) {
617 filteringByPruningMax(valuedTree, attribute.get(), threshold, imgOutputPtr);
618 }
619
629 template <std::floating_point Real>
630 static void filteringByPruningMax(const ValuedMorphologicalTree<T>& valuedTree, const std::shared_ptr<Real[]>& attribute, Real threshold,
632 filteringByPruningMax(valuedTree.asView(), attribute.get(), threshold, imgOutputPtr);
633 }
634
644 template <std::floating_point Real>
648
658 template <std::floating_point Real>
660 filteringByPruningMax(valuedTree.asView(), attribute, threshold, imgOutputPtr);
661 }
662
663};
664
665} // 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:89
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.