3#include "../ValuedMorphologicalTree.hpp"
4#include "../../utils/Image.hpp"
11namespace mmcfilters::adjust {
20enum class BoundingBoxMeasure {
72 buffer.resize(
static_cast<size_t>(tree.topology().numInternalNodeSlots()), 0.0);
201 buffer[
static_cast<size_t>(
nodeId)] =
static_cast<double>(tree.topology().properPartCardinality(
nodeId));
272 struct LocalBoxState {
290 int properPartCount = 0;
298 BoundingBoxMeasure measure_ = BoundingBoxMeasure::DiagonalLength;
300 mutable std::vector<LocalBoxState> local_;
302 mutable std::vector<BoxState> subtree_;
309 static void resetLocalBox(LocalBoxState&
local) {
318 local.properPartCount = 0;
327 void resetLocalSummary(NodeId nodeId)
const {
328 auto& local = local_[
static_cast<size_t>(nodeId)];
329 resetLocalBox(local);
338 void resetSubtreeSummary(NodeId nodeId)
const {
339 auto& subtree = subtree_[
static_cast<size_t>(nodeId)];
344 subtree.empty =
true;
354 void expandLocalBoxWithPixel(LocalBoxState& local, PixelId pixelId,
int numColumns)
const {
369 if (x < local.xmin) {
372 }
else if (x == local.xmin) {
376 if (x > local.xmax) {
379 }
else if (x == local.xmax) {
383 if (y < local.ymin) {
386 }
else if (y == local.ymin) {
390 if (y > local.ymax) {
393 }
else if (y == local.ymax) {
404 void rebuildLocalBox(NodeId nodeId,
const ValuedMorphologicalTree<T>& tree)
const {
405 auto& local = local_[
static_cast<size_t>(nodeId)];
406 resetLocalBox(local);
407 const int numColumns = tree.topology().numColumns();
408 for (PixelId pixelId : tree.topology().properPart(nodeId)) {
409 expandLocalBoxWithPixel(local, pixelId, numColumns);
411 local.properPartCount = tree.topology().properPartCardinality(nodeId);
424 void ensureLocalSummary(NodeId nodeId,
const ValuedMorphologicalTree<T>& tree)
const {
425 auto& local = local_[
static_cast<size_t>(nodeId)];
426 const int properPartCount = tree.topology().properPartCardinality(nodeId);
427 if (!local.dirty && local.properPartCount == properPartCount) {
430 rebuildLocalBox(nodeId, tree);
438 void copyLocalToSubtree(NodeId nodeId)
const {
439 const auto& local = local_[
static_cast<size_t>(nodeId)];
440 auto& subtree = subtree_[
static_cast<size_t>(nodeId)];
441 subtree.xmin = local.xmin;
442 subtree.xmax = local.xmax;
443 subtree.ymin = local.ymin;
444 subtree.ymax = local.ymax;
445 subtree.empty = local.empty;
454 static void mergeSubtreeStates(BoxState& target,
const BoxState& source) {
462 target.xmin = std::min(target.xmin, source.xmin);
463 target.xmax = std::max(target.xmax, source.xmax);
464 target.ymin = std::min(target.ymin, source.ymin);
465 target.ymax = std::max(target.ymax, source.ymax);
466 target.empty =
false;
475 static void mergeLocalBoxes(LocalBoxState& target,
const LocalBoxState& source) {
484 if (source.xmin < target.xmin) {
485 target.xmin = source.xmin;
486 target.xminCount = source.xminCount;
487 }
else if (source.xmin == target.xmin) {
488 target.xminCount += source.xminCount;
491 if (source.xmax > target.xmax) {
492 target.xmax = source.xmax;
493 target.xmaxCount = source.xmaxCount;
494 }
else if (source.xmax == target.xmax) {
495 target.xmaxCount += source.xmaxCount;
498 if (source.ymin < target.ymin) {
499 target.ymin = source.ymin;
500 target.yminCount = source.yminCount;
501 }
else if (source.ymin == target.ymin) {
502 target.yminCount += source.yminCount;
505 if (source.ymax > target.ymax) {
506 target.ymax = source.ymax;
507 target.ymaxCount = source.ymaxCount;
508 }
else if (source.ymax == target.ymax) {
509 target.ymaxCount += source.ymaxCount;
512 target.properPartCount += source.properPartCount;
513 target.empty =
false;
514 target.dirty =
false;
537 const size_t size =
static_cast<size_t>(tree.topology().numInternalNodeSlots());
539 subtree_.resize(size);
549 ensureLocalSummary(
nodeId, tree);
550 copyLocalToSubtree(
nodeId);
560 mergeSubtreeStates(subtree_[
static_cast<size_t>(
parentId)], subtree_[
static_cast<size_t>(
childId)]);
576 const double width =
static_cast<double>(
subtree.xmax -
subtree.xmin + 1);
577 const double height =
static_cast<double>(
subtree.ymax -
subtree.ymin + 1);
579 case BoundingBoxMeasure::Width:
582 case BoundingBoxMeasure::Height:
585 case BoundingBoxMeasure::DiagonalLength:
586 buffer[
static_cast<size_t>(
nodeId)] = std::sqrt(width * width + height * height);
601 mergeLocalBoxes(local_[
static_cast<size_t>(
targetId)], local_[
static_cast<size_t>(
sourceId)]);
622 const int numColumns = tree.topology().numColumns();
623 expandLocalBoxWithPixel(local_[
static_cast<size_t>(
targetId)],
pixelId, numColumns);
624 local_[
static_cast<size_t>(
targetId)].properPartCount += 1;
625 local_[
static_cast<size_t>(
targetId)].dirty =
false;
631 auto& source = local_[
static_cast<size_t>(
sourceId)];
632 if (source.properPartCount <= 1) {
638 --source.properPartCount;
646 if (
x == source.xmin) {
647 if (source.xminCount <= 1) {
653 if (
x == source.xmax) {
654 if (source.xmaxCount <= 1) {
660 if (
y == source.ymin) {
661 if (source.yminCount <= 1) {
667 if (
y == source.ymax) {
668 if (source.ymaxCount <= 1) {
685 resetLocalSummary(
nodeId);
686 resetSubtreeSummary(
nodeId);
int PixelId
Pixel identifier type used by source and active construction domains.
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(PixelId index, int numColumns) noexcept
Converts a row-major linear index to (row, column).
Mutable connected-subset tree on a finite pixel domain.
PostOrderNodeRange postOrder() const
Returns a post-order traversal range rooted at the connected root.
ChildrenRange children(NodeId nodeId) const
Returns a fail-fast range over the direct children of nodeId.
Incremental area attribute.
void postProcessing(NodeId, const ValuedMorphologicalTree< T > &, buffer_type &) const override
Area has no finalization step beyond child accumulation.
void preProcessing(NodeId nodeId, const ValuedMorphologicalTree< T > &tree, buffer_type &buffer) const override
Initializes one node area from its direct proper-part count.
void mergeProcessing(NodeId parentId, NodeId childId, const ValuedMorphologicalTree< T > &, buffer_type &buffer) const override
Adds an already-current child area to its parent.
typename base_t::buffer_type buffer_type
Dense per-node area buffer inherited from the dynamic attribute protocol.
Incremental bounding-box scalar attribute.
void onMoveProperParts(NodeId targetId, NodeId sourceId, const ValuedMorphologicalTree< T > &tree) const override
Updates local boxes after all proper parts move from sourceId to targetId.
void resize(const ValuedMorphologicalTree< T > &tree, buffer_type &buffer) const override
Resizes public and auxiliary buffers to the current tree slot space.
void onMoveProperPart(NodeId targetId, NodeId sourceId, PixelId pixelId, const ValuedMorphologicalTree< T > &tree) const override
Updates local boxes after one proper part moves between nodes.
void mergeProcessing(NodeId parentId, NodeId childId, const ValuedMorphologicalTree< T > &, buffer_type &) const override
Accumulates a child subtree box into its parent subtree box.
void postProcessing(NodeId nodeId, const ValuedMorphologicalTree< T > &, buffer_type &buffer) const override
Converts the accumulated subtree box into the configured scalar measure.
void preProcessing(NodeId nodeId, const ValuedMorphologicalTree< T > &tree, buffer_type &) const override
Initializes one node's subtree bounding box from its proper part.
void onNodeRemoved(NodeId nodeId, const ValuedMorphologicalTree< T > &) const override
Clears auxiliary summaries associated with a released node slot.
DynamicBoundingBoxAttributeComputer(BoundingBoxMeasure measure=BoundingBoxMeasure::DiagonalLength)
Creates a bounding-box computer returning the requested scalar measure.
typename base_t::buffer_type buffer_type
Dense per-node bounding-box attribute buffer inherited from the dynamic protocol.
Common protocol for attributes maintained during local tree adjustment.
virtual void mergeProcessing(NodeId parentId, NodeId childId, const ValuedMorphologicalTree< T > &tree, buffer_type &buffer) const =0
Accumulates an already-current child contribution into its parent.
void computeAttribute(const ValuedMorphologicalTree< T > &tree, buffer_type &buffer) const
Computes the attribute for the full current tree in post-order.
virtual ~DynamicTreeAttributeComputer()=default
Destroys a dynamic attribute computer through the protocol base.
virtual void postProcessing(NodeId nodeId, const ValuedMorphologicalTree< T > &tree, buffer_type &buffer) const =0
Materializes the final scalar value for one node after all child merges.
virtual void resize(const ValuedMorphologicalTree< T > &tree, buffer_type &buffer) const
Resizes an attribute buffer to the full internal node-id space.
virtual void onMoveProperParts(NodeId, NodeId, const ValuedMorphologicalTree< T > &) const
Incremental hook called after all pixels in one node's proper part move to another node.
virtual void preProcessing(NodeId nodeId, const ValuedMorphologicalTree< T > &tree, buffer_type &buffer) const =0
Initializes the direct contribution of one node before child merges.
virtual void onMoveProperPart(NodeId, NodeId, PixelId, const ValuedMorphologicalTree< T > &) const
Incremental hook called after one pixel moves between node proper parts.
std::vector< double > buffer_type
Dense per-node attribute buffer used by dynamic adjustment computers.
void computeAttributeOnNode(const ValuedMorphologicalTree< T > &tree, NodeId nodeId, buffer_type &buffer) const
Recomputes one node assuming all direct children are already up to date.
virtual void onNodeRemoved(NodeId, const ValuedMorphologicalTree< T > &) const
Incremental hook called when one node slot is released from the live tree.
Owning result for one computed scalar attribute layout and buffer.