3#include "../utils/Altitude.hpp"
4#include "../utils/Contract.hpp"
5#include "../utils/Image.hpp"
6#include "MorphologicalTree.hpp"
7#include "detail/HigraExportLayoutDetail.hpp"
22namespace detail::tree_altitude {
24template <
class Contribution>
25 requires(std::is_arithmetic_v<Contribution> && !std::is_same_v<std::remove_cv_t<Contribution>,
bool>)
26[[nodiscard]] std::vector<Contribution> reconstructNodeContributionValues(
const MorphologicalTree& tree,
27 std::span<const Contribution> nodeContributions,
28 const char* context) {
29 tree.requireNotEditing(context);
31 nodeContributions.size() ==
static_cast<std::size_t
>(tree.numInternalNodeSlots()),
32 throw std::invalid_argument(std::string(context) +
" nodeContributions size must match the internal node slot count."));
33 if constexpr (std::is_floating_point_v<Contribution> && contract::validationsEnabled) {
34 for (Contribution contribution : nodeContributions) {
35 if (!std::isfinite(contribution)) {
36 throw std::invalid_argument(std::string(context) +
" requires finite nodeContributions.");
41 std::vector<Contribution> accumulated(
static_cast<std::size_t
>(tree.numInternalNodeSlots()), Contribution{});
42 const NodeId root = tree.root();
43 accumulated[
static_cast<std::size_t
>(root)] = nodeContributions[
static_cast<std::size_t
>(root)];
45 std::vector<NodeId> pending{root};
46 while (!pending.empty()) {
47 const NodeId nodeId = pending.back();
49 for (NodeId childId : tree.children(nodeId)) {
50 accumulated[
static_cast<std::size_t
>(childId)] =
51 accumulated[
static_cast<std::size_t
>(nodeId)] + nodeContributions[
static_cast<std::size_t
>(childId)];
52 pending.push_back(childId);
56 std::vector<Contribution> pixels(
static_cast<std::size_t
>(tree.numPixels()), Contribution{});
57 for (PixelId pixel = 0; pixel < tree.numPixels(); ++pixel) {
58 pixels[
static_cast<std::size_t
>(pixel)] = accumulated[
static_cast<std::size_t
>(tree.smallestNode(pixel))];
82 throw std::runtime_error(
"Altitude buffer size must match the dense internal-node domain."));
93 if constexpr (std::is_floating_point_v<T>) {
94 const long double level =
static_cast<long double>(altitude);
96 std::ostringstream
oss;
97 oss <<
context <<
" requires finite floating-point altitudes; value at index " << index <<
" is " <<
level <<
".";
98 throw std::invalid_argument(
oss.str());
110 if constexpr (std::is_floating_point_v<T> && contract::validationsEnabled) {
111 for (std::size_t index = 0; index < altitude.size(); ++index) {
124 if constexpr (std::is_floating_point_v<T> && contract::validationsEnabled) {
126 throw std::invalid_argument(
"Image altitude validation requires a non-null image.");
141 throw std::invalid_argument(
"Altitude access requires a valid internal NodeId."));
142 return altitude[
static_cast<std::size_t
>(
nodeId)];
176 const long double level =
static_cast<long double>(altitude);
177 if constexpr (std::is_floating_point_v<T>) {
178 if (!std::isfinite(
level)) {
179 std::ostringstream
oss;
180 oss <<
context <<
" requires finite node altitudes in the uint8 domain [0, 255]; node " <<
nodeId <<
" has altitude " <<
level <<
".";
181 throw std::invalid_argument(
oss.str());
185 std::ostringstream
oss;
186 oss <<
context <<
" requires node altitudes in the uint8 domain [0, 255]; node " <<
nodeId <<
" has altitude " <<
level <<
".";
187 throw std::invalid_argument(
oss.str());
189 return static_cast<std::uint8_t
>(altitude);
218 template <AltitudeValue T>
220 const char*
context =
"TreeAltitudeAlgorithms::reconstructFromNodeAltitudes") {
245 template <
class Contribution>
246 requires(std::is_arithmetic_v<Contribution> && !std::is_same_v<std::remove_cv_t<Contribution>,
bool>)
249 const char*
context =
"TreeAltitudeAlgorithms::reconstructFromNodeContributions") {
270 template <AltitudeValue T>
273 const detail::ExportedHigraLayout
layout = detail::computeExportedHigraLayout(tree, altitude);
289 throw std::runtime_error(
"Each proper part must belong to one alive node when exporting a compact Higra hierarchy.");
291 parent[
static_cast<std::size_t
>(
leafIndex)] =
layout.nodeToHigra[
static_cast<std::size_t
>(smallestNodeId)];
298 parent[
static_cast<std::size_t
>(
newNodeId)] =
315 switch (nodeAltitudeOrder) {
316 case NodeAltitudeOrder::Increasing:
319 case NodeAltitudeOrder::Decreasing:
322 case NodeAltitudeOrder::Unconstrained:
333 throw std::runtime_error(
"Monotonic validation requires every alive non-root node to have an alive parent.");
338 throw std::runtime_error(
"Hierarchy altitude buffer must be strictly increasing from parent to child.");
341 throw std::runtime_error(
"Hierarchy altitude buffer must be strictly decreasing from parent to child.");
int NodeId
Node identifier type used throughout the project.
constexpr NodeId InvalidNode
Sentinel value used to denote an invalid node identifier.
#define MMCFILTERS_CONTRACT_REQUIRE(condition,...)
Evaluates a caller precondition and its failure action only in checked builds.
NodeAltitudeOrder
Global ordering constraint of node altitudes along parent-child arcs.
static Ptr create(int rows, int columns)
Creates an owned image with uninitialised pixel values.
Mutable connected-subset tree on a finite pixel domain.
int numRows() const
Returns the number of rows in the regular 2D pixel domain.
int numInternalNodeSlots() const
Returns the size of the dense internal-node id domain.
bool isAlive(NodeId nodeId) const
Tests whether a node slot currently represents a live node.
NodeAltitudeOrder nodeAltitudeOrder() const noexcept
Returns the global parent-to-child altitude ordering constraint.
int numPixels() const
Returns the cardinality of the pixel domain.
bool isRoot(NodeId nodeId) const
Tests whether nodeId is the current root.
AliveNodeRange aliveNodeIds() const
Returns a fail-fast range over all live node ids.
NodeId smallestNode(PixelId pixel) const
Returns the smallest node containing pixel.
void requireNotEditing(const char *context) const
Rejects operations that require a committed connected topology.
int numColumns() const
Returns the number of columns in the regular 2D pixel domain.
NodeId parent(NodeId nodeId) const
Returns the direct parent of nodeId.
Pure operations over a topology and an explicit altitude buffer.
static void validateNodeAltitudeBufferShape(const MorphologicalTree &tree, std::span< const T > altitude)
Validates that an altitude buffer covers the dense internal-node domain.
static void validateUInt8AltitudeDomain(const MorphologicalTree &tree, std::span< const T > altitude, const char *context)
Validates all live node altitudes before materialising an ImageUInt8.
static std::uint8_t requireUInt8AltitudeValue(T altitude, NodeId nodeId, const char *context)
Converts one altitude value to uint8_t, rejecting values outside the output domain.
static void validateMonotoneNodeAltitudes(const MorphologicalTree &tree, std::span< const T > altitude)
Validates the hierarchy's declared global altitude order.
static void validateFiniteImageAltitudes(const ImagePtr< T > &image, const char *context)
Rejects non-finite floating-point pixels before using an image as altitude source.
static ImagePtr< Contribution > reconstructFromNodeContributions(const MorphologicalTree &tree, std::span< const Contribution > nodeContributions, const char *context="TreeAltitudeAlgorithms::reconstructFromNodeContributions")
Reconstructs an image by summing node contributions on every root-to-node branch.
static T nodeAltitude(std::span< const T > altitude, NodeId nodeId)
Reads one node altitude from an explicit altitude buffer.
static void validateFiniteAltitudeValue(T altitude, std::size_t index, const char *context)
Rejects non-finite floating-point altitudes while compiling to a no-op for integral types.
static void validateFiniteAltitudeValues(std::span< const T > altitude, const char *context)
Rejects non-finite floating-point altitudes in a contiguous input range.
static AltitudeDifference< T > nodeResidue(const MorphologicalTree &tree, std::span< const T > altitude, NodeId nodeId)
Computes the altitude difference between one node and its parent.
static std::pair< std::vector< NodeId >, std::vector< T > > exportHigraHierarchy(const MorphologicalTree &tree, std::span< const T > altitude)
Exports a live rooted topology and explicit altitudes to a compact parent/altitude representation.
static ImagePtr< T > reconstructFromNodeAltitudes(const MorphologicalTree &tree, std::span< const T > altitude, const char *context="TreeAltitudeAlgorithms::reconstructFromNodeAltitudes")
Reconstructs a typed image from topology storage and explicit node altitudes.
Owning result for one computed scalar attribute layout and buffer.