mmcfilters
Public API documentation
Loading...
Searching...
No Matches
CasfComponentTrees.hpp
1#pragma once
2
3#include "DualMinMaxTreeIncrementalFilter.hpp"
4#include "../MorphologicalTreeFactory.hpp"
5#include "../../utils/Image.hpp"
6
7#include <memory>
8#include <stdexcept>
9#include <utility>
10#include <vector>
11
12namespace mmcfilters::adjust {
13
22enum class CasfComponentTreesAttribute { Area, BoundingBoxWidth, BoundingBoxHeight, BoundingBoxDiagonal };
23
79template <AltitudeValue T> class CasfComponentTrees {
80 private:
87
89 RegularGridAdjacency2D adjacency_;
91 tree_t minTree_;
93 tree_t maxTree_;
95 std::unique_ptr<attribute_computer_t> minAttributeComputer_;
97 std::unique_ptr<attribute_computer_t> maxAttributeComputer_;
99 std::unique_ptr<DualMinMaxTreeIncrementalFilter<T>> adjust_;
101 std::vector<double> minAttributeBuffer_;
103 std::vector<double> maxAttributeBuffer_;
105 std::vector<NodeId> pruneCandidateQueue_;
107 std::vector<NodeId> selectedPruneCandidates_;
109 CasfComponentTreesAttribute attribute_ = CasfComponentTreesAttribute::Area;
110
119 static std::unique_ptr<attribute_computer_t> makeAttributeComputer(CasfComponentTreesAttribute attribute) {
120 switch (attribute) {
121 case CasfComponentTreesAttribute::Area:
122 return std::make_unique<DynamicAreaAttributeComputer<T>>();
123 case CasfComponentTreesAttribute::BoundingBoxWidth:
124 return std::make_unique<DynamicBoundingBoxAttributeComputer<T>>(BoundingBoxMeasure::Width);
125 case CasfComponentTreesAttribute::BoundingBoxHeight:
126 return std::make_unique<DynamicBoundingBoxAttributeComputer<T>>(BoundingBoxMeasure::Height);
127 case CasfComponentTreesAttribute::BoundingBoxDiagonal:
128 return std::make_unique<DynamicBoundingBoxAttributeComputer<T>>(BoundingBoxMeasure::DiagonalLength);
129 }
130 throw std::runtime_error("Unknown CASF component-tree attribute.");
131 }
132
147 const std::vector<NodeId>& selectPruneCandidates(const tree_t& tree, const std::vector<double>& attribute, double threshold) {
148 pruneCandidateQueue_.clear();
149 selectedPruneCandidates_.clear();
150
151 const MorphologicalTree& topology = tree.topology();
152 const NodeId root = topology.root();
153 if (root == InvalidNode || !topology.isAlive(root)) {
154 return selectedPruneCandidates_;
155 }
156
157 pruneCandidateQueue_.push_back(root);
158 size_t head = 0;
159
160 while (head < pruneCandidateQueue_.size()) {
161 const NodeId nodeId = pruneCandidateQueue_[head++];
162 if (!topology.isAlive(nodeId)) {
163 continue;
164 }
165
166 const double nodeAttribute = attribute[static_cast<size_t>(nodeId)];
167 if (!topology.isRoot(nodeId) && nodeAttribute <= threshold) {
168 selectedPruneCandidates_.push_back(nodeId);
169 continue;
170 }
171
172 for (NodeId childId : topology.children(nodeId)) {
173 if (topology.isAlive(childId)) {
174 pruneCandidateQueue_.push_back(childId);
175 }
176 }
177 }
178
179 return selectedPruneCandidates_;
180 }
181
192 void applyFilterStep(double threshold) {
193 const std::vector<NodeId> maxCandidates = selectPruneCandidates(maxTree_, maxAttributeBuffer_, threshold);
194 adjust_->pruneMaxTreeAndUpdateMinTree(maxCandidates);
195
196 const std::vector<NodeId> minCandidates = selectPruneCandidates(minTree_, minAttributeBuffer_, threshold);
197 adjust_->pruneMinTreeAndUpdateMaxTree(minCandidates);
198 }
199
200 public:
207 CasfComponentTrees(image_ptr_t image, CasfComponentTreesAttribute attribute = CasfComponentTreesAttribute::Area, double radius = 1.5)
208 : adjacency_(image ? image->getNumRows() : 0, image ? image->getNumColumns() : 0, radius),
209 minTree_(MorphologicalTreeFactory::createMinTree(image, radius)), maxTree_(MorphologicalTreeFactory::createMaxTree(image, radius)),
210 minAttributeComputer_(makeAttributeComputer(attribute)), maxAttributeComputer_(makeAttributeComputer(attribute)), attribute_(attribute) {
211 if (!image || image->getNumRows() <= 0 || image->getNumColumns() <= 0 || image->getSize() <= 0) {
212 throw std::invalid_argument("CasfComponentTrees requires a non-empty image.");
213 }
214
215 adjust_ = std::make_unique<DualMinMaxTreeIncrementalFilter<T>>(&minTree_, &maxTree_, adjacency_);
216 adjust_->setAttributeComputer(*minAttributeComputer_, *maxAttributeComputer_, minAttributeBuffer_, maxAttributeBuffer_);
217 minAttributeComputer_->computeAttribute(minTree_, minAttributeBuffer_);
218 maxAttributeComputer_->computeAttribute(maxTree_, maxAttributeBuffer_);
219
220 const size_t maxNodes = static_cast<size_t>(std::max(minTree_.topology().numInternalNodeSlots(), maxTree_.topology().numInternalNodeSlots()));
221 pruneCandidateQueue_.reserve(maxNodes);
222 selectedPruneCandidates_.reserve(maxNodes);
223 }
224
235 [[nodiscard]] image_ptr_t filter(const std::vector<double>& thresholds) {
236 for (double threshold : thresholds) {
237 applyFilterStep(threshold);
238 }
239 return minTree_.reconstructFromNodeAltitudes();
240 }
241
247 [[nodiscard]] const tree_t& minTree() const noexcept { return minTree_; }
248
254 [[nodiscard]] const tree_t& maxTree() const noexcept { return maxTree_; }
255
261 [[nodiscard]] CasfComponentTreesAttribute attribute() const noexcept { return attribute_; }
262
271 [[nodiscard]] std::pair<std::vector<NodeId>, std::vector<T>> exportMinTree() const { return minTree_.exportHigraHierarchy(); }
272
281 [[nodiscard]] std::pair<std::vector<NodeId>, std::vector<T>> exportMaxTree() const { return maxTree_.exportHigraHierarchy(); }
282};
283
284} // 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:34
Public construction facade for all high-level morphological trees.
Mutable connected-subset tree on a finite pixel domain.
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.
ChildrenRange children(NodeId nodeId) const
Returns a fail-fast range over the direct children of nodeId.
NodeId root() const
Returns the current hierarchy root.
Immutable regular-grid 2D adjacency with allocation-free traversal.
Wrapper pairing MorphologicalTree topology with an external altitude buffer.
const MorphologicalTree & topology() const noexcept
Returns read-only access to the owned topology.
ImagePtr< T > reconstructFromNodeAltitudes() const
Reconstructs an image by assigning each proper part its smallest-node 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.
Connected alternating sequential filter on paired component trees.
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.
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.
Owning result for one computed scalar attribute layout and buffer.