3#include "HierarchySaliencyMapValidation.hpp"
4#include "../../utils/RegularGridAdjacency2D.hpp"
5#include "../../utils/Altitude.hpp"
6#include "../../utils/Common.hpp"
7#include "../MorphologicalTree.hpp"
8#include "../detail/MorphologicalTreeConstructionContextQueries.hpp"
9#include "../ValuedMorphologicalTree.hpp"
71enum class HierarchyLevelConvention {
73 PartitionAppearanceLevel,
111 detail::requireCommittedRootedHierarchy(tree,
context);
113 throw std::invalid_argument(std::string(
context) +
" requires a non-empty image/pixel domain.");
116 std::ostringstream
oss;
118 <<
" for a tree domain of " << tree.
numRows() <<
"x" << tree.
numColumns() <<
".";
119 throw std::invalid_argument(
oss.str());
134 if (
lhs.getNumRows() !=
rhs.getNumRows() ||
lhs.getNumColumns() !=
rhs.getNumColumns() ||
lhs.getSize() !=
rhs.getSize()) {
137 for (
int index = 0; index <
lhs.getSize(); ++index) {
138 if (
lhs.getOffsetRow(index) !=
rhs.getOffsetRow(index) ||
lhs.getOffsetColumn(index) !=
rhs.getOffsetColumn(index)) {
160 throw std::invalid_argument(
162 " cannot infer one image graph from distinct minimum/maximum adjacencies; pass the intended adjacency explicitly.");
165 convention !=
nullptr && std::holds_alternative<SelfDualSpanImmersion>(convention->immersion) && tree.
hasGridDomain2D()) {
169 throw std::invalid_argument(std::string(
context) +
" requires an attached adjacency relation; pass an explicit adjacency relation instead.");
186 return requireStoredAdjacency(tree,
context);
202 detail::requireCommittedRootedHierarchy(tree,
"HierarchySaliencyMap::computeTopologicalLevels");
247 template <
class NodeValue>
250 using Value = std::decay_t<std::invoke_result_t<NodeValue, NodeId>>;
251 static_assert(!std::is_void_v<Value>,
"HierarchySaliencyMap nodeValue must return a value.");
253 constexpr const char*
context =
"HierarchySaliencyMap::computeEdgeMap";
254 validateTreeAndAdjacency(tree, adjacency,
context);
263 for (
NodeId source = 0; source < numPixels; ++source) {
266 throw std::runtime_error(
"HierarchySaliencyMap::computeEdgeMap found a pixel without a live smallest node.");
272 throw std::runtime_error(
"HierarchySaliencyMap::computeEdgeMap found a neighbour pixel without a live smallest node.");
277 throw std::runtime_error(
"HierarchySaliencyMap::computeEdgeMap could not find a live LCA for an adjacency edge.");
329 template <
class Value>
332 HierarchyValuationPolicy
policy = HierarchyValuationPolicy::AllowLevelCollapse,
333 HierarchyLevelConvention
levelConvention = HierarchyLevelConvention::EdgeSaliencyValue,
334 HierarchyConnectivityPolicy
connectivityPolicy = HierarchyConnectivityPolicy::ValidateConnected) {
335 constexpr const char*
context =
"HierarchySaliencyMap::computeSaliencyEdgeMap";
337 validateTreeAndAdjacency(tree, adjacency,
context);
341 if (
levelConvention == HierarchyLevelConvention::PartitionAppearanceLevel) {
345 throw std::invalid_argument(std::string(
context) +
" requires positive partition-appearance levels.");
347 if constexpr (std::is_floating_point_v<Value>) {
349 throw std::invalid_argument(std::string(
context) +
" requires integer-valued partition-appearance levels.");
361 for (
NodeId source = 0; source < numPixels; ++source) {
364 throw std::runtime_error(std::string(
context) +
" found a pixel without a live smallest node.");
370 throw std::runtime_error(std::string(
context) +
" found a neighbour pixel without a live smallest node.");
377 throw std::runtime_error(std::string(
context) +
" could not find a live LCA for an adjacency edge.");
380 if (
levelConvention == HierarchyLevelConvention::PartitionAppearanceLevel) {
408 template <
class Value>
411 HierarchyValuationPolicy
policy = HierarchyValuationPolicy::AllowLevelCollapse,
412 HierarchyLevelConvention
levelConvention = HierarchyLevelConvention::EdgeSaliencyValue,
413 HierarchyConnectivityPolicy
connectivityPolicy = HierarchyConnectivityPolicy::ValidateConnected) {
434 template <
class Value>
437 HierarchyValuationPolicy
policy = HierarchyValuationPolicy::AllowLevelCollapse,
438 HierarchyConnectivityPolicy
connectivityPolicy = HierarchyConnectivityPolicy::ValidateConnected) {
439 constexpr const char*
context =
"HierarchySaliencyMap::computeCanonicalRankedSaliencyEdgeMap";
441 validateTreeAndAdjacency(tree, adjacency,
context);
460 for (
NodeId source = 0; source < numPixels; ++source) {
471 throw std::runtime_error(std::string(
context) +
" could not find a live LCA for an adjacency edge.");
509 template <
class Value>
512 HierarchyValuationPolicy
policy = HierarchyValuationPolicy::AllowLevelCollapse,
513 HierarchyConnectivityPolicy
connectivityPolicy = HierarchyConnectivityPolicy::ValidateConnected) {
528 ranked.numRows = edgeMap.numRows;
529 ranked.numColumns = edgeMap.numColumns;
530 ranked.adjacencyRadius = edgeMap.adjacencyRadius;
531 ranked.sources = edgeMap.sources;
532 ranked.targets = edgeMap.targets;
550 template <
class NodeValue>
553 return computeEdgeMap(tree, requireStoredAdjacency(tree,
"HierarchySaliencyMap::computeEdgeMap"), std::forward<NodeValue>(
nodeValue));
585 template <AltitudeValue T>
589 return computeSaliencyEdgeMap(tree.topology(), adjacency, std::span<const double>(
scores), HierarchyValuationPolicy::AllowLevelCollapse);
655 template <
class Value>
658 HierarchyValuationPolicy
policy = HierarchyValuationPolicy::AllowLevelCollapse) {
660 HierarchyLevelConvention::PartitionAppearanceLevel, HierarchyConnectivityPolicy::ValidateConnected);
constexpr NodeId InvalidNode
Sentinel value used to denote an invalid node identifier.
Explicit proper-part completion of a morphological component tree.
static void validate(const MorphologicalTree &tree, const RegularGridAdjacency2D &adjacency)
Validates the connected complete-partition interpretation.
static EdgeSaliencyMap< Value > computeSaliencyEdgeMap(const MorphologicalTree &tree, const RegularGridAdjacency2D &adjacency, std::span< const Value > partitionAppearanceLevels, HierarchyValuationPolicy policy=HierarchyValuationPolicy::AllowLevelCollapse)
Projects explicit partition-appearance levels with level(LCA)-1.
static std::vector< int > computePartitionAppearanceLevels(const MorphologicalTree &tree)
Returns the structural partition-appearance levels of the completion.
static std::vector< double > computeNormalizedScores(const MorphologicalTree &tree, std::span< const Value > valuation, HierarchyValuationPolicy policy=HierarchyValuationPolicy::AllowLevelCollapse, HierarchyValuationRangePolicy rangePolicy=HierarchyValuationRangePolicy::AllowAnyFinite)
Normalizes a compatible hierarchy valuation to [0, 1].
static void validateHierarchyConnectivity(const MorphologicalTree &tree, const RegularGridAdjacency2D &adjacency, const char *context="HierarchySaliencyMapValidation::validateHierarchyConnectivity")
Validates that every hierarchy support is connected in adjacency.
static void validateHierarchyValuation(const MorphologicalTree &tree, std::span< const Value > valuation, HierarchyValuationPolicy policy=HierarchyValuationPolicy::AllowLevelCollapse, HierarchyValuationRangePolicy rangePolicy=HierarchyValuationRangePolicy::AllowAnyFinite, const char *context="HierarchySaliencyMapValidation::validateHierarchyValuation")
Validates that a node-indexed valuation is compatible with a hierarchy.
Projects a morphological tree hierarchy onto an image adjacency graph.
static EdgeSaliencyMap< int > computeCanonicalRankedSaliencyEdgeMap(const MorphologicalTree &tree, const RegularGridAdjacency2D &adjacency, std::span< const Value > valuation, HierarchyValuationPolicy policy=HierarchyValuationPolicy::AllowLevelCollapse, HierarchyConnectivityPolicy connectivityPolicy=HierarchyConnectivityPolicy::ValidateConnected)
Computes the canonical dense integer saliency scale of a valuation.
static std::vector< int > computeTopologicalLevels(const MorphologicalTree &tree)
Computes a dense topological level buffer indexed by internal NodeId.
static EdgeSaliencyMap< int > computeCanonicalRankedSaliencyEdgeMap(const MorphologicalTree &tree, std::span< const Value > valuation, HierarchyValuationPolicy policy=HierarchyValuationPolicy::AllowLevelCollapse, HierarchyConnectivityPolicy connectivityPolicy=HierarchyConnectivityPolicy::ValidateConnected)
Stored-adjacency overload of computeCanonicalRankedSaliencyEdgeMap.
static EdgeSaliencyMap< int > computeTopologicalLevelEdgeMap(const MorphologicalTree &tree)
Computes a topological-level map using one unambiguous stored graph.
static auto computeEdgeMap(const MorphologicalTree &tree, const RegularGridAdjacency2D &adjacency, NodeValue &&nodeValue) -> EdgeSaliencyMap< std::decay_t< std::invoke_result_t< NodeValue, NodeId > > >
Computes an edge saliency map using an explicit adjacency relation.
static EdgeSaliencyMap< double > computeNormalizedAltitudeEdgeMap(const ValuedMorphologicalTree< T > &tree, const RegularGridAdjacency2D &adjacency)
Computes a normalized-altitude edge saliency map with explicit adjacency.
static auto computeEdgeMap(const MorphologicalTree &tree, NodeValue &&nodeValue) -> EdgeSaliencyMap< std::decay_t< std::invoke_result_t< NodeValue, NodeId > > >
Computes an edge saliency map using one unambiguous stored graph.
static EdgeSaliencyMap< int > rankEdgeSaliencyMap(const EdgeSaliencyMap< Value > &edgeMap)
Densely ranks the values already present in an edge saliency map.
static EdgeSaliencyMap< Value > computeSaliencyEdgeMap(const MorphologicalTree &tree, std::span< const Value > valuation, HierarchyValuationPolicy policy=HierarchyValuationPolicy::AllowLevelCollapse, HierarchyLevelConvention levelConvention=HierarchyLevelConvention::EdgeSaliencyValue, HierarchyConnectivityPolicy connectivityPolicy=HierarchyConnectivityPolicy::ValidateConnected)
Computes a formal saliency map using one unambiguous stored graph.
static EdgeSaliencyMap< int > computeTopologicalLevelEdgeMap(const MorphologicalTree &tree, const RegularGridAdjacency2D &adjacency)
Computes a topological-level edge saliency map with explicit adjacency.
static RegularGridAdjacency2D requireProjectionAdjacency(const MorphologicalTree &tree, const char *context="HierarchySaliencyMap::requireProjectionAdjacency")
Returns the unambiguous adjacency stored by a hierarchy.
static EdgeSaliencyMap< Value > computeSaliencyEdgeMap(const MorphologicalTree &tree, const RegularGridAdjacency2D &adjacency, std::span< const Value > valuation, HierarchyValuationPolicy policy=HierarchyValuationPolicy::AllowLevelCollapse, HierarchyLevelConvention levelConvention=HierarchyLevelConvention::EdgeSaliencyValue, HierarchyConnectivityPolicy connectivityPolicy=HierarchyConnectivityPolicy::ValidateConnected)
Computes the formal edge-indexed saliency map induced by a valuation.
static std::vector< int > computePartitionAppearanceLevels(const MorphologicalTree &tree)
Computes partition-appearance indexes for the proper-part completion.
static EdgeSaliencyMap< double > computeNormalizedAltitudeEdgeMap(const ValuedMorphologicalTree< T > &tree)
Computes a normalized-altitude map using one unambiguous stored graph.
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.
PostOrderNodeRange postOrder() const
Returns a post-order traversal range rooted at the connected root.
bool isAlive(NodeId nodeId) const
Tests whether a node slot currently represents a live node.
bool hasGridDomain2D() const noexcept
Tests whether pixel ids have an attached row/column layout.
int numPixels() const
Returns the cardinality of the pixel domain.
const TopographicConvention * topographicConvention() const noexcept
Returns the topographic convention, or nullptr.
AliveNodeRange aliveNodeIds() const
Returns a fail-fast range over all live node ids.
NodeId smallestNode(PixelId pixel) const
Returns the smallest node containing pixel.
NodeId lowestCommonAncestor(NodeId u, NodeId v) const
Returns the lowest common ancestor of u and v.
ChildrenRange children(NodeId nodeId) const
Returns a fail-fast range over the direct children of nodeId.
int numColumns() const
Returns the number of columns in the regular 2D pixel domain.
const std::optional< GridDomain2D > & gridDomain2D() const noexcept
Returns the optional regular 2D pixel domain.
Immutable regular-grid 2D adjacency with allocation-free traversal.
double getRadius() const noexcept
Returns the configured or bounding Euclidean radius.
ForwardNeighborIndexRange getForwardNeighborIndices(int row, int column) const
Returns the directed positive half of the neighbourhood.
int getNumColumns() const noexcept
Returns the number of columns in the attached grid domain.
int getNumRows() const noexcept
Returns the number of rows in the attached grid domain.
Ordered pair of complementary minimum and maximum adjacencies.
Owning result for one computed scalar attribute layout and buffer.
std::vector< Real > & values() noexcept
Returns the mutable flat attribute buffer.
Edge-indexed saliency map induced by a morphological hierarchy.
int numRows
Number of rows in the image grid.
bool empty() const noexcept
Returns whether the edge map has no values.
int numColumns
Number of columns in the image grid.
double adjacencyRadius
Radius of the adjacency used to enumerate the edges.
std::vector< NodeId > sources
Source pixel identifier of each undirected edge.
std::vector< Value > values
Saliency value parallel to sources and targets.
std::vector< NodeId > targets
Target pixel identifier of each undirected edge.
std::size_t size() const noexcept
Returns the number of edge values.
Shape metadata optionally attached to the pixel domain.
int columns
Number of grid columns.
int rows
Number of grid rows.
Complete discrete convention retained by a tree-of-shapes result.