52 std::size_t treeMutationVersion_ = 0;
58 return weighted_ !=
nullptr ? weighted_->asView() :
view_;
61 void requireStableTree(
const char* context)
const {
65 static T altitudeOf(
const AltitudeView& view, NodeId nodeId) {
66 return view.getAltitude(nodeId);
69 static void requireAttributePointer(
const Real* attr,
const char* context) {
70 if (attr ==
nullptr) {
71 throw std::invalid_argument(std::string(context) +
" requires a non-null attribute buffer.");
75 static const Real* requireAttributeBuffer(
const MorphologicalTree& tree,
const std::vector<Real>& attr,
const char* context) {
76 if (attr.size() !=
static_cast<std::size_t
>(tree.getNumInternalNodeSlots())) {
77 throw std::invalid_argument(std::string(context) +
" attribute size must match the internal node slot count.");
82 static T absoluteAltitudeDifference(AltitudeDiff<T> lhs, AltitudeDiff<T> rhs) {
83 const long double difference = std::abs(
static_cast<long double>(lhs) -
static_cast<long double>(rhs));
88 return static_cast<T
>(difference);
91 void computeUAO(
const AltitudeView& view, NodeId currentNodeId, AltitudeDiff<T> altitudeNodeNotInNR,
bool qPropag,
bool isCalculateResidue) {
92 const NodeId parentNodeId = tree.getNodeParent(currentNodeId);
93 const AltitudeDiff<T> altitudeNodeInNR =
static_cast<AltitudeDiff<T>
>(altitudeOf(view, currentNodeId));
94 bool flagPropag =
false;
97 if (this->isSelectedForPruning(currentNodeId)) {
98 altitudeNodeNotInNR =
static_cast<AltitudeDiff<T>
>(altitudeOf(view, parentNodeId));
99 if (this->attrs_increasing[currentNodeId] <= this->maxCriterion) {
100 isCalculateResidue = hasNodeSelectedInPrimitive(currentNodeId);
104 if (this->attrs_increasing[currentNodeId] <= this->maxCriterion) {
105 if (isCalculateResidue) {
106 contrast = absoluteAltitudeDifference(altitudeNodeInNR, altitudeNodeNotInNR);
109 if (this->maxContrastLUT[parentNodeId] >= contrast) {
110 this->maxContrastLUT[currentNodeId] = this->maxContrastLUT[parentNodeId];
111 this->associatedIndexLUT[currentNodeId] = this->associatedIndexLUT[parentNodeId];
113 this->maxContrastLUT[currentNodeId] = contrast;
114 this->associatedIndexLUT[currentNodeId] = !qPropag
115 ?
static_cast<int>(this->attrs_increasing[currentNodeId] + 1)
116 : this->associatedIndexLUT[parentNodeId];
121 for (NodeId childNodeId : tree.getChildren(currentNodeId)) {
122 this->computeUAO(view, childNodeId, altitudeNodeNotInNR, flagPropag, isCalculateResidue);
126 void executeImpl(Real maxCriterion,
const std::vector<uint8_t>& selectedForFiltering) {
127 const AltitudeView altitudeView = view();
128 this->maxCriterion = maxCriterion;
129 this->selectedForFiltering = selectedForFiltering;
131 for (NodeId
id : tree.getAliveNodeIds()) {
132 maxContrastLUT[id] = T{};
133 associatedIndexLUT[id] = 0;
136 const NodeId rootNodeId = tree.getRoot();
137 const AltitudeDiff<T> level =
static_cast<AltitudeDiff<T>
>(altitudeOf(altitudeView, rootNodeId));
138 for (NodeId childNodeId : tree.getChildren(rootNodeId)) {
139 computeUAO(altitudeView, childNodeId, level,
false,
false);
143 bool isSelectedForPruning(NodeId currentNodeId)
const {
144 const NodeId parentNodeId = tree.getNodeParent(currentNodeId);
145 if (parentNodeId == InvalidNode) {
148 return this->attrs_increasing[currentNodeId] != this->attrs_increasing[parentNodeId];
151 bool hasNodeSelectedInPrimitive(NodeId currentNodeId)
const {
152 std::stack<NodeId> stack;
153 stack.push(currentNodeId);
154 while (!stack.empty()) {
155 const NodeId nodeId = stack.top();
157 if (selectedForFiltering[nodeId]) {
161 for (NodeId childNodeId : tree.getChildren(nodeId)) {
162 if (this->attrs_increasing[childNodeId] == this->attrs_increasing[nodeId]) {
163 stack.push(childNodeId);
212 tree(
view_.topology()),
213 treeMutationVersion_(tree.getMutationVersion()),
216 view_.requireTopologyUnchanged(
"UltimateAttributeOpening");
218 this->selectedForFiltering.assign(this->tree.getNumInternalNodeSlots(),
true);
279 requireStableTree(
"UltimateAttributeOpening::execute");
280 std::vector<uint8_t>
tmp(this->tree.getNumInternalNodeSlots(),
true);
296 requireStableTree(
"UltimateAttributeOpening::execute");
298 throw std::invalid_argument(
"UltimateAttributeOpening::execute selectedForFiltering size must match the internal node slot count.");
315 requireStableTree(
"UltimateAttributeOpening::executeWithMSER");
316 if (weighted_ ==
nullptr) {
317 throw std::logic_error(
"UltimateAttributeOpening::executeWithMSER requires a WeightedMorphologicalTree owner because MSER uses the tree-owned altitude.");
335 requireStableTree(
"UltimateAttributeOpening::executeWithDepthStability");
347 requireStableTree(
"UltimateAttributeOpening::getMaxContrastImage");
348 const int size = this->tree.getNumColsOfImage() * this->tree.getNumRowsOfImage();
353 out[
pidx] = this->maxContrastLUT[tree.getProperPartOwner(
pidx)];
365 requireStableTree(
"UltimateAttributeOpening::getAssociatedImage");
366 const int size = this->tree.getNumColsOfImage() * this->tree.getNumRowsOfImage();
371 out[
pidx] = this->associatedIndexLUT[tree.getProperPartOwner(
pidx)];
383 requireStableTree(
"UltimateAttributeOpening::getAssociatedColorImage");