MorphologicalAttributeFilters
Public API documentation
Loading...
Searching...
No Matches
CasfComponentTrees.hpp
1#pragma once
2
3#include "DualMinMaxTreeIncrementalFilter.hpp"
4#include "../MorphologicalTreeFactory.hpp"
5
6#include <memory>
7#include <stdexcept>
8#include <utility>
9#include <vector>
10
11namespace mmcfilters::adjust {
12
21enum class CasfComponentTreesAttribute {
22 AREA,
23 BOUNDING_BOX_WIDTH,
24 BOUNDING_BOX_HEIGHT,
25 BOUNDING_BOX_DIAGONAL
26};
27
83template<AltitudeValue T>
85private:
89
90 AdjacencyRelation adjacency_;
91 tree_t minTree_;
92 tree_t maxTree_;
93 std::unique_ptr<attribute_computer_t> minAttributeComputer_;
94 std::unique_ptr<attribute_computer_t> maxAttributeComputer_;
95 std::unique_ptr<DualMinMaxTreeIncrementalFilter<T>> adjust_;
96 std::vector<double> minAttributeBuffer_;
97 std::vector<double> maxAttributeBuffer_;
98 std::vector<NodeId> pruneCandidateQueue_;
99 std::vector<NodeId> selectedPruneCandidates_;
100 CasfComponentTreesAttribute attribute_ = CasfComponentTreesAttribute::AREA;
101
107 static std::unique_ptr<attribute_computer_t> makeAttributeComputer(CasfComponentTreesAttribute attribute) {
108 switch (attribute) {
109 case CasfComponentTreesAttribute::AREA:
110 return std::make_unique<DynamicAreaAttributeComputer<T>>();
111 case CasfComponentTreesAttribute::BOUNDING_BOX_WIDTH:
112 return std::make_unique<DynamicBoundingBoxAttributeComputer<T>>(BoundingBoxMeasure::WIDTH);
113 case CasfComponentTreesAttribute::BOUNDING_BOX_HEIGHT:
114 return std::make_unique<DynamicBoundingBoxAttributeComputer<T>>(BoundingBoxMeasure::HEIGHT);
115 case CasfComponentTreesAttribute::BOUNDING_BOX_DIAGONAL:
116 return std::make_unique<DynamicBoundingBoxAttributeComputer<T>>(BoundingBoxMeasure::DIAGONAL_LENGTH);
117 }
118 throw std::runtime_error("Unknown CASF component-tree attribute.");
119 }
120
130 const std::vector<NodeId>& selectPruneCandidates(const tree_t& tree, const std::vector<double>& attribute, double threshold) {
131 pruneCandidateQueue_.clear();
132 selectedPruneCandidates_.clear();
133
134 const MorphologicalTree& topology = tree.topology();
135 const NodeId root = topology.getRoot();
136 if (root == InvalidNode || !topology.isAlive(root)) {
137 return selectedPruneCandidates_;
138 }
139
140 pruneCandidateQueue_.push_back(root);
141 size_t head = 0;
142
143 while (head < pruneCandidateQueue_.size()) {
144 const NodeId nodeId = pruneCandidateQueue_[head++];
145 if (!topology.isAlive(nodeId)) {
146 continue;
147 }
148
149 const double nodeAttribute = attribute[static_cast<size_t>(nodeId)];
150 if (!topology.isRoot(nodeId) && nodeAttribute <= threshold) {
151 selectedPruneCandidates_.push_back(nodeId);
152 continue;
153 }
154
155 for (NodeId childId : topology.getChildren(nodeId)) {
156 if (topology.isAlive(childId)) {
157 pruneCandidateQueue_.push_back(childId);
158 }
159 }
160 }
161
162 return selectedPruneCandidates_;
163 }
164
173 void applyFilterStep(double threshold) {
174 const std::vector<NodeId> maxCandidates = selectPruneCandidates(maxTree_, maxAttributeBuffer_, threshold);
175 adjust_->pruneMaxTreeAndUpdateMinTree(maxCandidates);
176
177 const std::vector<NodeId> minCandidates = selectPruneCandidates(minTree_, minAttributeBuffer_, threshold);
178 adjust_->pruneMinTreeAndUpdateMaxTree(minCandidates);
179 }
180
181public:
188 CasfComponentTrees(image_ptr_t image, CasfComponentTreesAttribute attribute = CasfComponentTreesAttribute::AREA, double radius = 1.5)
189 : adjacency_(image ? image->getNumRows() : 0, image ? image->getNumCols() : 0, radius),
190 minTree_(MorphologicalTreeFactory::createMinTree(image, radius)),
191 maxTree_(MorphologicalTreeFactory::createMaxTree(image, radius)),
192 minAttributeComputer_(makeAttributeComputer(attribute)),
193 maxAttributeComputer_(makeAttributeComputer(attribute)),
194 attribute_(attribute) {
195 if (!image || image->getNumRows() <= 0 || image->getNumCols() <= 0 || image->getSize() <= 0) {
196 throw std::invalid_argument("CasfComponentTrees requires a non-empty image.");
197 }
198
199 adjust_ = std::make_unique<DualMinMaxTreeIncrementalFilter<T>>(&minTree_, &maxTree_, adjacency_);
200 adjust_->setAttributeComputer(*minAttributeComputer_, *maxAttributeComputer_, minAttributeBuffer_, maxAttributeBuffer_);
201 minAttributeComputer_->computeAttribute(minTree_, minAttributeBuffer_);
202 maxAttributeComputer_->computeAttribute(maxTree_, maxAttributeBuffer_);
203
204 const size_t maxNodes = static_cast<size_t>(
205 std::max(minTree_.topology().getNumInternalNodeSlots(), maxTree_.topology().getNumInternalNodeSlots()));
206 pruneCandidateQueue_.reserve(maxNodes);
207 selectedPruneCandidates_.reserve(maxNodes);
208 }
209
217 [[nodiscard]] image_ptr_t filter(const std::vector<double>& thresholds) {
218 for (double threshold : thresholds) {
219 applyFilterStep(threshold);
220 }
221 return minTree_.reconstructionImage();
222 }
223
228 return minTree_;
229 }
230
235 return maxTree_;
236 }
237
241 [[nodiscard]] CasfComponentTreesAttribute attribute() const noexcept {
242 return attribute_;
243 }
244
251 [[nodiscard]] std::pair<std::vector<NodeId>, std::vector<T>> exportMinTree() const {
252 return minTree_.exportHigraHierarchy();
253 }
254
261 [[nodiscard]] std::pair<std::vector<NodeId>, std::vector<T>> exportMaxTree() const {
262 return maxTree_.exportHigraHierarchy();
263 }
264};
265
266} // namespace mmcfilters::adjust
int NodeId
Node identifier type used throughout the project.
Definition Common.hpp:17
constexpr NodeId InvalidNode
Sentinel value used to denote an invalid node identifier.
Definition Common.hpp:25
Two-dimensional adjacency relation with configurable radius and efficient iteration.
Public construction facade for all high-level morphological trees.
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.
bool isAlive(NodeId nodeId) const
Tests whether a node slot currently represents a live node.
bool isRoot(NodeId nodeId) const
Tests whether nodeId is the current root.
NodeId getRoot() const
Returns the current hierarchy root.
Wrapper pairing MorphologicalTree topology with an external altitude buffer.
ImagePtr< T > reconstructionImage() const
Reconstructs an image by assigning each proper part its owner altitude.
std::pair< std::vector< NodeId >, std::vector< T > > exportHigraHierarchy() const
Exports the current live rooted tree to a new compact Higra parent/altitude representation.
const MorphologicalTree & topology() const noexcept
Returns read-only access to the owned topology.
Connected alternating sequential filter on paired component trees.
image_ptr_t filter(const std::vector< double > &thresholds)
Runs the CASF on the threshold sequence and returns the filtered image.
std::pair< std::vector< NodeId >, std::vector< T > > exportMinTree() const
Exports the current min-tree as a compact static parent/altitude pair.
std::pair< std::vector< NodeId >, std::vector< T > > exportMaxTree() const
Exports the current max-tree as a compact static parent/altitude pair.
const tree_t & minTree() const noexcept
Returns the current min-tree state.
CasfComponentTreesAttribute attribute() const noexcept
Returns the increasing attribute configured for this CASF instance.
const tree_t & maxTree() const noexcept
Returns the current max-tree state.
CasfComponentTrees(image_ptr_t image, CasfComponentTreesAttribute attribute=CasfComponentTreesAttribute::AREA, double radius=1.5)
Initializes the CASF state from the input image and the chosen attribute.
Owning result for one computed scalar attribute layout and buffer.