74 AltitudeView view()
const {
return valuedTree_ !=
nullptr ? valuedTree_->asView() :
view_; }
81 void requireStableTree(
const char* context)
const { tree.
requireMutationVersion(treeMutationVersion_, context); }
90 static T altitudeOf(
const AltitudeView& view, NodeId nodeId)
noexcept {
91 return view.nodeAltitudes()[
static_cast<std::size_t
>(nodeId)];
100 static void requireAttributePointer(
const Real* attr,
const char* context) {
102 throw std::invalid_argument(std::string(context) +
" requires a non-null attribute buffer."));
113 static const Real* requireAttributeBuffer(
const MorphologicalTree& tree,
const std::vector<Real>& attr,
const char* context) {
115 throw std::invalid_argument(std::string(context) +
" attribute size must match the internal node slot count."));
126 static T absoluteAltitudeDifference(AltitudeDifference<T> lhs, AltitudeDifference<T> rhs) {
127 const long double difference = std::abs(
static_cast<long double>(lhs) -
static_cast<long double>(rhs));
132 return static_cast<T
>(difference);
144 void computeUAO(
const AltitudeView& view, NodeId currentNodeId, AltitudeDifference<T> altitudeNodeNotInNR,
bool qPropag,
bool isCalculateResidue) {
145 const NodeId parentNodeId = detail::CommittedTreeAccess::nodeParent(tree, currentNodeId);
146 const AltitudeDifference<T> altitudeNodeInNR =
static_cast<AltitudeDifference<T>
>(altitudeOf(view, currentNodeId));
147 bool flagPropag =
false;
150 if (this->isSelectedForPruning(currentNodeId)) {
151 altitudeNodeNotInNR =
static_cast<AltitudeDifference<T>
>(altitudeOf(view, parentNodeId));
152 if (this->attrs_increasing[currentNodeId] <= this->maximumAttributeThreshold) {
153 isCalculateResidue = hasNodeSelectedInPrimitive(currentNodeId);
157 if (this->attrs_increasing[currentNodeId] <= this->maximumAttributeThreshold) {
158 if (isCalculateResidue) {
159 contrast = absoluteAltitudeDifference(altitudeNodeInNR, altitudeNodeNotInNR);
162 if (this->maxContrastLUT[parentNodeId] >= contrast) {
163 this->maxContrastLUT[currentNodeId] = this->maxContrastLUT[parentNodeId];
164 this->associatedIndexLUT[currentNodeId] = this->associatedIndexLUT[parentNodeId];
166 this->maxContrastLUT[currentNodeId] = contrast;
167 this->associatedIndexLUT[currentNodeId] =
168 !qPropag ?
static_cast<int>(this->attrs_increasing[currentNodeId] + 1) : this->associatedIndexLUT[parentNodeId];
173 for (NodeId childNodeId : detail::CommittedTreeAccess::children(tree, currentNodeId)) {
174 this->computeUAO(view, childNodeId, altitudeNodeNotInNR, flagPropag, isCalculateResidue);
184 void executeImpl(Real maximumAttributeThreshold,
const std::vector<uint8_t>& selectedForFiltering) {
185 const AltitudeView altitudeView = view();
186 this->maximumAttributeThreshold = maximumAttributeThreshold;
187 this->selectedForFiltering = selectedForFiltering;
189 for (NodeId
id : tree.aliveNodeIds()) {
190 maxContrastLUT[id] = T{};
191 associatedIndexLUT[id] = 0;
194 const NodeId rootNodeId = tree.root();
195 const AltitudeDifference<T> level =
static_cast<AltitudeDifference<T>
>(altitudeOf(altitudeView, rootNodeId));
196 for (NodeId childNodeId : detail::CommittedTreeAccess::children(tree, rootNodeId)) {
197 computeUAO(altitudeView, childNodeId, level,
false,
false);
207 bool isSelectedForPruning(NodeId currentNodeId)
const {
208 const NodeId parentNodeId = detail::CommittedTreeAccess::nodeParent(tree, currentNodeId);
209 if (parentNodeId == InvalidNode) {
212 return this->attrs_increasing[currentNodeId] != this->attrs_increasing[parentNodeId];
221 bool hasNodeSelectedInPrimitive(NodeId currentNodeId)
const {
222 std::stack<NodeId> stack;
223 stack.push(currentNodeId);
224 while (!stack.empty()) {
225 const NodeId nodeId = stack.top();
227 if (selectedForFiltering[nodeId]) {
231 for (NodeId childNodeId : detail::CommittedTreeAccess::children(tree, nodeId)) {
232 if (this->attrs_increasing[childNodeId] == this->attrs_increasing[nodeId]) {
233 stack.push(childNodeId);
281 view_.requireTopologyUnchanged(
"UltimateAttributeOpening");
283 this->selectedForFiltering.assign(this->tree.numInternalNodeSlots(),
true);
347 requireStableTree(
"UltimateAttributeOpening::execute");
348 std::vector<uint8_t>
tmp(this->tree.numInternalNodeSlots(),
true);
364 requireStableTree(
"UltimateAttributeOpening::execute");
367 throw std::invalid_argument(
"UltimateAttributeOpening::execute selectedForFiltering size must match the internal node slot count."));
382 requireStableTree(
"UltimateAttributeOpening::executeWithMSER");
383 if (valuedTree_ ==
nullptr) {
384 throw std::logic_error(
385 "UltimateAttributeOpening::executeWithMSER requires a ValuedMorphologicalTree owner because MSER uses the tree-owned altitude.");
402 requireStableTree(
"UltimateAttributeOpening::executeWithDepthStability");
414 requireStableTree(
"UltimateAttributeOpening::getMaxContrastImage");
415 const int size = this->tree.numColumns() * this->tree.numRows();
420 out[
pidx] = this->maxContrastLUT[detail::CommittedTreeAccess::smallestNodeMap(tree,
pidx)];
432 requireStableTree(
"UltimateAttributeOpening::getAssociatedImage");
433 const int size = this->tree.numColumns() * this->tree.numRows();
438 out[
pidx] = this->associatedIndexLUT[detail::CommittedTreeAccess::smallestNodeMap(tree,
pidx)];
451 this->tree.numColumns());