3#include "../WeightedMorphologicalTree.hpp"
10namespace mmcfilters::adjust {
19enum class BoundingBoxMeasure {
49template<AltitudeValue T>
72 buffer.resize(
static_cast<size_t>(tree.topology().getNumInternalNodeSlots()), 0.0);
183template<AltitudeValue T>
197 buffer[
static_cast<size_t>(
nodeId)] =
static_cast<double>(tree.topology().getNumProperParts(
nodeId));
230template<AltitudeValue T>
259 struct LocalBoxState {
268 int properPartCount = 0;
273 BoundingBoxMeasure measure_ = BoundingBoxMeasure::DIAGONAL_LENGTH;
274 mutable std::vector<LocalBoxState> local_;
275 mutable std::vector<BoxState> subtree_;
280 static void resetLocalBox(LocalBoxState&
local) {
289 local.properPartCount = 0;
296 void resetLocalSummary(NodeId nodeId)
const {
297 auto& local = local_[
static_cast<size_t>(nodeId)];
298 resetLocalBox(local);
305 void resetSubtreeSummary(NodeId nodeId)
const {
306 auto& subtree = subtree_[
static_cast<size_t>(nodeId)];
311 subtree.empty =
true;
317 void expandLocalBoxWithPixel(LocalBoxState& local, NodeId pixelId,
int numCols)
const {
332 if (x < local.xmin) {
335 }
else if (x == local.xmin) {
339 if (x > local.xmax) {
342 }
else if (x == local.xmax) {
346 if (y < local.ymin) {
349 }
else if (y == local.ymin) {
353 if (y > local.ymax) {
356 }
else if (y == local.ymax) {
364 void rebuildLocalBox(NodeId nodeId,
const WeightedMorphologicalTree<T>& tree)
const {
365 auto& local = local_[
static_cast<size_t>(nodeId)];
366 resetLocalBox(local);
367 const int numCols = tree.topology().getNumColsOfImage();
368 for (NodeId pixelId : tree.topology().getProperParts(nodeId)) {
369 expandLocalBoxWithPixel(local, pixelId, numCols);
371 local.properPartCount = tree.topology().getNumProperParts(nodeId);
381 void ensureLocalSummary(NodeId nodeId,
const WeightedMorphologicalTree<T>& tree)
const {
382 auto& local = local_[
static_cast<size_t>(nodeId)];
383 const int properPartCount = tree.topology().getNumProperParts(nodeId);
384 if (!local.dirty && local.properPartCount == properPartCount) {
387 rebuildLocalBox(nodeId, tree);
393 void copyLocalToSubtree(NodeId nodeId)
const {
394 const auto& local = local_[
static_cast<size_t>(nodeId)];
395 auto& subtree = subtree_[
static_cast<size_t>(nodeId)];
396 subtree.xmin = local.xmin;
397 subtree.xmax = local.xmax;
398 subtree.ymin = local.ymin;
399 subtree.ymax = local.ymax;
400 subtree.empty = local.empty;
406 static void mergeSubtreeStates(BoxState& target,
const BoxState& source) {
414 target.xmin = std::min(target.xmin, source.xmin);
415 target.xmax = std::max(target.xmax, source.xmax);
416 target.ymin = std::min(target.ymin, source.ymin);
417 target.ymax = std::max(target.ymax, source.ymax);
418 target.empty =
false;
424 static void mergeLocalBoxes(LocalBoxState& target,
const LocalBoxState& source) {
433 if (source.xmin < target.xmin) {
434 target.xmin = source.xmin;
435 target.xminCount = source.xminCount;
436 }
else if (source.xmin == target.xmin) {
437 target.xminCount += source.xminCount;
440 if (source.xmax > target.xmax) {
441 target.xmax = source.xmax;
442 target.xmaxCount = source.xmaxCount;
443 }
else if (source.xmax == target.xmax) {
444 target.xmaxCount += source.xmaxCount;
447 if (source.ymin < target.ymin) {
448 target.ymin = source.ymin;
449 target.yminCount = source.yminCount;
450 }
else if (source.ymin == target.ymin) {
451 target.yminCount += source.yminCount;
454 if (source.ymax > target.ymax) {
455 target.ymax = source.ymax;
456 target.ymaxCount = source.ymaxCount;
457 }
else if (source.ymax == target.ymax) {
458 target.ymaxCount += source.ymaxCount;
461 target.properPartCount += source.properPartCount;
462 target.empty =
false;
463 target.dirty =
false;
484 const size_t size =
static_cast<size_t>(tree.topology().getNumInternalNodeSlots());
486 subtree_.resize(
size);
493 ensureLocalSummary(
nodeId, tree);
494 copyLocalToSubtree(
nodeId);
501 mergeSubtreeStates(subtree_[
static_cast<size_t>(
parentId)], subtree_[
static_cast<size_t>(
childId)]);
514 const double width =
static_cast<double>(
subtree.xmax -
subtree.xmin + 1);
515 const double height =
static_cast<double>(
subtree.ymax -
subtree.ymin + 1);
517 case BoundingBoxMeasure::WIDTH:
520 case BoundingBoxMeasure::HEIGHT:
523 case BoundingBoxMeasure::DIAGONAL_LENGTH:
524 buffer[
static_cast<size_t>(
nodeId)] = std::sqrt(width * width + height * height);
535 mergeLocalBoxes(local_[
static_cast<size_t>(
targetId)], local_[
static_cast<size_t>(
sourceId)]);
551 const int numCols = tree.topology().getNumColsOfImage();
552 expandLocalBoxWithPixel(local_[
static_cast<size_t>(
targetId)],
pixelId, numCols);
553 local_[
static_cast<size_t>(
targetId)].properPartCount += 1;
554 local_[
static_cast<size_t>(
targetId)].dirty =
false;
561 if (
source.properPartCount <= 1) {
576 if (
source.xminCount <= 1) {
583 if (
source.xmaxCount <= 1) {
590 if (
source.yminCount <= 1) {
597 if (
source.ymaxCount <= 1) {
612 resetLocalSummary(
nodeId);
613 resetSubtreeSummary(
nodeId);
int NodeId
Node identifier type used throughout the project.
constexpr NodeId InvalidNode
Sentinel value used to denote an invalid node identifier.
static std::pair< int, int > to2D(int index, int numCols) noexcept
Converts a row-major linear index to (row, col).
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.
PostOrderNodeRange getPostOrderNodes() const
Returns a post-order traversal range rooted at the connected root.
Incremental area attribute.
void preProcessing(NodeId nodeId, const WeightedMorphologicalTree< T > &tree, buffer_type &buffer) const override
Initializes one node area from its direct proper-part count.
void mergeProcessing(NodeId parentId, NodeId childId, const WeightedMorphologicalTree< T > &, buffer_type &buffer) const override
Adds an already-current child area to its parent.
void postProcessing(NodeId, const WeightedMorphologicalTree< T > &, buffer_type &) const override
Area has no finalization step beyond child accumulation.
typename base_t::buffer_type buffer_type
Dense per-node area buffer inherited from the dynamic attribute protocol.
Incremental bounding-box scalar attribute.
void postProcessing(NodeId nodeId, const WeightedMorphologicalTree< T > &, buffer_type &buffer) const override
Converts the accumulated subtree box into the configured scalar measure.
void preProcessing(NodeId nodeId, const WeightedMorphologicalTree< T > &tree, buffer_type &) const override
Initializes the subtree box of one node from its direct proper parts.
void onMoveProperPart(NodeId targetId, NodeId sourceId, NodeId pixelId, const WeightedMorphologicalTree< T > &tree) const override
Updates local boxes after one proper part moves between nodes.
void resize(const WeightedMorphologicalTree< T > &tree, buffer_type &buffer) const override
Resizes public and auxiliary buffers to the current tree slot space.
DynamicBoundingBoxAttributeComputer(BoundingBoxMeasure measure=BoundingBoxMeasure::DIAGONAL_LENGTH)
Creates a bounding-box computer returning the requested scalar measure.
void mergeProcessing(NodeId parentId, NodeId childId, const WeightedMorphologicalTree< T > &, buffer_type &) const override
Accumulates a child subtree box into its parent subtree box.
void onNodeRemoved(NodeId nodeId, const WeightedMorphologicalTree< T > &) const override
Clears auxiliary summaries associated with a released node slot.
typename base_t::buffer_type buffer_type
Dense per-node bounding-box attribute buffer inherited from the dynamic protocol.
void onMoveProperParts(NodeId targetId, NodeId sourceId, const WeightedMorphologicalTree< T > &tree) const override
Updates local boxes after all proper parts move from sourceId to targetId.
Common protocol for attributes maintained during local tree adjustment.
virtual void mergeProcessing(NodeId parentId, NodeId childId, const WeightedMorphologicalTree< T > &tree, buffer_type &buffer) const =0
Accumulates an already-current child contribution into its parent.
virtual void resize(const WeightedMorphologicalTree< T > &tree, buffer_type &buffer) const
Resizes an attribute buffer to the full internal node-id space.
virtual void onMoveProperParts(NodeId, NodeId, const WeightedMorphologicalTree< T > &) const
Incremental hook called after all direct proper parts move from one node to another.
virtual ~DynamicTreeAttributeComputer()=default
Destroys a dynamic attribute computer through the protocol base.
virtual void preProcessing(NodeId nodeId, const WeightedMorphologicalTree< T > &tree, buffer_type &buffer) const =0
Initializes the direct contribution of one node before child merges.
void computeAttribute(const WeightedMorphologicalTree< T > &tree, buffer_type &buffer) const
Computes the attribute for the full current tree in post-order.
virtual void postProcessing(NodeId nodeId, const WeightedMorphologicalTree< T > &tree, buffer_type &buffer) const =0
Materializes the final scalar value for one node after all child merges.
virtual void onNodeRemoved(NodeId, const WeightedMorphologicalTree< T > &) const
Incremental hook called when one node slot is released from the live tree.
std::vector< double > buffer_type
Dense per-node attribute buffer used by dynamic adjustment computers.
void computeAttributeOnNode(const WeightedMorphologicalTree< T > &tree, NodeId nodeId, buffer_type &buffer) const
Recomputes one node assuming all direct children are already up to date.
virtual void onMoveProperPart(NodeId, NodeId, NodeId, const WeightedMorphologicalTree< T > &) const
Incremental hook called after one proper part moves between nodes.
Owning result for one computed scalar attribute layout and buffer.