55 detail::MaterializedVersionPolicy::PreserveLinkedConstruction);
57 detail::MaterializedVersionPolicy::PreserveLinkedConstruction);
60 detail::DualMinMaxTreeIncrementalFilterLeaf<T>
updater(&minTree, &maxTree, adjacency_);
83 updater.getLastProperPartTransfers());
92 const T
terminalAltitude = minTree.nodeAltitudes()[
static_cast<std::size_t
>(minTree.topology().root())];
93 assert(minTree.topology().numNodes() == 1 && maxTree.topology().numNodes() == 1);
95 return std::move(
extTree).completeTree(
106 friend struct detail::ExtremumResidualTreeTestAccess<T>;
110 enum class Polarity {
119 [[nodiscard]]
static constexpr Polarity oppositePolarity(Polarity polarity) {
120 return polarity == Polarity::Maximum ? Polarity::Minimum : Polarity::Maximum;
126 struct ExtremumCandidate {
127 std::size_t supportCardinality = 0;
135 struct ExtremumCandidatePriority {
142 bool operator()(
const ExtremumCandidate& lhs,
const ExtremumCandidate& rhs)
const {
143 if (lhs.supportCardinality != rhs.supportCardinality)
144 return lhs.supportCardinality > rhs.supportCardinality;
145 return lhs.minimumPixel > rhs.minimumPixel;
152 struct ExtremumAgenda {
153 std::priority_queue<ExtremumCandidate, std::vector<ExtremumCandidate>, ExtremumCandidatePriority> candidates;
162 void enqueue(
const ValuedMorphologicalTree<T>& tree, Polarity polarity, NodeId node,
const detail::DualMinMaxTreeIncrementalFilterLeaf<T>& updater) {
163 if (!isNonterminalLeaf(tree, node))
165 candidates.push({
static_cast<std::size_t
>(detail::TreeKernelAccess::properPartCardinality(tree.topology(), node)), updater.minPixel(tree, node),
175 void initialize(
const ValuedMorphologicalTree<T>& maxTree,
const ValuedMorphologicalTree<T>& minTree,
176 const detail::DualMinMaxTreeIncrementalFilterLeaf<T>& updater) {
177 for (NodeId node : maxTree.topology().aliveNodeIds())
178 enqueue(maxTree, Polarity::Maximum, node, updater);
179 for (NodeId node : minTree.topology().aliveNodeIds())
180 enqueue(minTree, Polarity::Minimum, node, updater);
189 [[nodiscard]] std::optional<ExtremumCandidate> next(
const ValuedMorphologicalTree<T>& maxTree,
const ValuedMorphologicalTree<T>& minTree) {
190 while (!candidates.empty()) {
191 const ExtremumCandidate selected = candidates.top();
193 const ValuedMorphologicalTree<T>& primal = selected.polarity == Polarity::Maximum ? maxTree : minTree;
194 if (!isNonterminalLeaf(primal, selected.nodeId))
198 if (
static_cast<std::size_t
>(detail::TreeKernelAccess::properPartCardinality(primal.topology(), selected.nodeId)) !=
199 selected.supportCardinality)
201 if (detail::TreeKernelAccess::smallestNodeMap(primal.topology(), selected.minimumPixel) != selected.nodeId)
212 struct SelectedSupport {
213 std::vector<PixelId> pixels;
223 void collectFromLeaf(NodeId leaf,
const ValuedMorphologicalTree<T>& primal,
const ValuedMorphologicalTree<T>& dual) {
224 const auto& dualTopology = dual.topology();
227 for (PixelId pixel : detail::TreeKernelAccess::properParts(primal.topology(), leaf)) {
228 pixels.push_back(pixel);
231 dualProperPartNode = detail::TreeKernelAccess::smallestNodeMap(dualTopology, pixels.front());
233 for (PixelId pixel : pixels) {
234 assert(detail::TreeKernelAccess::smallestNodeMap(dualTopology, pixel) == dualProperPartNode);
237 if (detail::TreeKernelAccess::properPartCardinality(dualTopology, dualProperPartNode) ==
static_cast<int>(pixels.size())) {
238 wholeDualProperPartNode = dualProperPartNode;
249 [[gnu::always_inline]]
static void applyElementaryLeveling(detail::DualMinMaxTreeIncrementalFilterLeaf<T>& updater,
bool isMaximum, NodeId selectedLeafNode,
250 const SelectedSupport& selectedSupport) {
252 updater.pruneMaxLeafAndUpdateMinTree(selectedLeafNode, selectedSupport.pixels, selectedSupport.dualProperPartNode,
253 selectedSupport.wholeDualProperPartNode);
255 updater.pruneMinLeafAndUpdateMaxTree(selectedLeafNode, selectedSupport.pixels, selectedSupport.dualProperPartNode,
256 selectedSupport.wholeDualProperPartNode);
263 class PendingSupportsByProperPart {
264 std::vector<std::list<NodeId>> residualNodesByProperPart_;
265 std::vector<std::list<NodeId>::iterator> positionByResidualNode_;
272 explicit PendingSupportsByProperPart(std::size_t componentNodeSlots) : residualNodesByProperPart_(componentNodeSlots) {}
279 std::list<NodeId>& pendingSupportsIn(NodeId properPartNode) {
return residualNodesByProperPart_[
static_cast<std::size_t
>(properPartNode)]; }
285 const std::list<NodeId>& pendingSupportsIn(NodeId properPartNode)
const {
return residualNodesByProperPart_[
static_cast<std::size_t
>(properPartNode)]; }
292 void addPendingSupport(NodeId residualNode, NodeId properPartNode) {
293 auto& pendingNodes = pendingSupportsIn(properPartNode);
294 if (
static_cast<std::size_t
>(residualNode) >= positionByResidualNode_.size())
295 positionByResidualNode_.resize(
static_cast<std::size_t
>(residualNode) + 1);
296 pendingNodes.push_back(residualNode);
297 positionByResidualNode_[
static_cast<std::size_t
>(residualNode)] = std::prev(pendingNodes.end());
305 void removePendingSupport(NodeId residualNode, NodeId properPartNode) {
306 pendingSupportsIn(properPartNode).erase(positionByResidualNode_[
static_cast<std::size_t
>(residualNode)]);
314 void transferPendingSupports(NodeId sourceNode, NodeId targetNode) {
315 auto& targetPendingSupports = pendingSupportsIn(targetNode);
316 targetPendingSupports.splice(targetPendingSupports.end(), pendingSupportsIn(sourceNode));
324 struct ResidualTreeConstruction {
325 PendingSupportsByProperPart maxTreePendingSupports;
326 PendingSupportsByProperPart minTreePendingSupports;
328 std::vector<NodeId> smallestNodeMap;
329 std::vector<T> nodeAltitudes{T{}};
336 ResidualTreeConstruction(std::size_t numPixels, std::size_t maxTreeNodeSlots, std::size_t minTreeNodeSlots)
337 : maxTreePendingSupports(maxTreeNodeSlots), minTreePendingSupports(minTreeNodeSlots), smallestNodeMap(numPixels,
InvalidNode) {
338 nodeParents.reserve(numPixels);
339 nodeAltitudes.reserve(numPixels);
349 NodeId addSelectedSupport(
const SelectedSupport& selectedSupport,
const ExtremumCandidate& selectedExtremum, T supportAltitude) {
352 PendingSupportsByProperPart& primalPendingSupports = pendingSupportsFor(selectedExtremum.polarity);
353 PendingSupportsByProperPart& dualPendingSupports = pendingSupportsFor(oppositePolarity(selectedExtremum.polarity));
354 auto& pendingChildren = primalPendingSupports.pendingSupportsIn(selectedExtremum.nodeId);
355 const NodeId residualNode =
static_cast<NodeId>(nodeParents.size());
356 nodeParents.push_back(InvalidNode);
357 nodeAltitudes.push_back(supportAltitude);
358 for (PixelId pixel : selectedSupport.pixels) {
359 if (smallestNodeMap[
static_cast<std::size_t
>(pixel)] == InvalidNode) {
360 smallestNodeMap[
static_cast<std::size_t
>(pixel)] = residualNode;
363 while (!pendingChildren.empty()) {
364 const NodeId childResidualNode = pendingChildren.front();
365 nodeParents[
static_cast<std::size_t
>(childResidualNode)] = residualNode;
366 dualPendingSupports.removePendingSupport(childResidualNode, selectedSupport.dualProperPartNode);
367 primalPendingSupports.removePendingSupport(childResidualNode, selectedExtremum.nodeId);
381 template <
class ProperPartTransfer>
382 void updatePendingSupports(NodeId residualNode, Polarity selectedPolarity, NodeId primalDestinationNode, NodeId dualDestinationNode,
383 std::span<const ProperPartTransfer> dualProperPartTransfers) {
384 PendingSupportsByProperPart& dualPendingSupports = pendingSupportsFor(oppositePolarity(selectedPolarity));
387 for (
const auto& transfer : dualProperPartTransfers)
388 dualPendingSupports.transferPendingSupports(transfer.source, transfer.target);
391 pendingSupportsFor(selectedPolarity).addPendingSupport(residualNode, primalDestinationNode);
392 dualPendingSupports.addPendingSupport(residualNode, dualDestinationNode);
402 [[nodiscard]] NativeHierarchyOwned<T> completeTree(
const ImagePtr<T>& image, MorphologicalTreeSemantics treeSemantics, T rootAltitude) && {
404 nodeAltitudes[0] = rootAltitude;
406 for (NodeId& parentNode : nodeParents)
409 for (NodeId& smallestNode : smallestNodeMap) {
410 if (smallestNode == InvalidNode) {
414 return makeNativeHierarchyOwned<T>(std::move(nodeParents), std::move(smallestNodeMap), std::move(nodeAltitudes), NodeId{0},
415 GridDomain2D{image->getNumRows(), image->getNumColumns()}, std::move(treeSemantics));
424 PendingSupportsByProperPart& pendingSupportsFor(Polarity polarity) {
425 return polarity == Polarity::Maximum ? maxTreePendingSupports : minTreePendingSupports;
429 RegularGridAdjacency2D adjacency_;
437 [[nodiscard]]
static bool isNonterminalLeaf(
const ValuedMorphologicalTree<T>& tree, NodeId node) {
438 const auto& topology = tree.topology();
439 return detail::TreeKernelAccess::isAlive(topology, node) && node != topology.root() && detail::TreeKernelAccess::numChildren(topology, node) == 0;