mmcfilters
Public API documentation
Loading...
Searching...
No Matches
HierarchySaliencyMap.hpp
1#pragma once
2
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 "../ValuedMorphologicalTree.hpp"
9
10#include <algorithm>
11#include <cmath>
12#include <cstddef>
13#include <functional>
14#include <iterator>
15#include <sstream>
16#include <span>
17#include <stdexcept>
18#include <string>
19#include <type_traits>
20#include <utility>
21#include <vector>
22
23namespace mmcfilters {
24
33template <class Value> struct EdgeSaliencyMap {
35 int numRows = 0;
37 int numColumns = 0;
39 double adjacencyRadius = 0.0;
41 std::vector<NodeId> sources;
43 std::vector<NodeId> targets;
45 std::vector<Value> values;
46
52 [[nodiscard]] std::size_t size() const noexcept { return values.size(); }
53
59 [[nodiscard]] bool empty() const noexcept { return values.empty(); }
60};
61
70enum class HierarchyLevelConvention {
71 EdgeSaliencyValue,
72 PartitionAppearanceLevel,
73};
74
101 private:
109 static void validateTreeAndAdjacency(const MorphologicalTree& tree, const RegularGridAdjacency2D& adjacency, const char* context) {
110 detail::requireCommittedRootedHierarchy(tree, context);
111 if (tree.numRows() <= 0 || tree.numColumns() <= 0 || tree.numPixels() <= 0) {
112 throw std::invalid_argument(std::string(context) + " requires a non-empty image/pixel domain.");
113 }
114 if (adjacency.getNumRows() != tree.numRows() || adjacency.getNumColumns() != tree.numColumns()) {
115 std::ostringstream oss;
116 oss << context << " adjacency domain must match the tree image domain; got " << adjacency.getNumRows() << "x" << adjacency.getNumColumns()
117 << " for a tree domain of " << tree.numRows() << "x" << tree.numColumns() << ".";
118 throw std::invalid_argument(oss.str());
119 }
120 }
121
132 static bool sameAdjacencyGraph(const RegularGridAdjacency2D& lhs, const RegularGridAdjacency2D& rhs) noexcept {
133 if (lhs.getNumRows() != rhs.getNumRows() || lhs.getNumColumns() != rhs.getNumColumns() || lhs.getSize() != rhs.getSize()) {
134 return false;
135 }
136 for (int index = 0; index < lhs.getSize(); ++index) {
137 if (lhs.getOffsetRow(index) != rhs.getOffsetRow(index) || lhs.getOffsetColumn(index) != rhs.getOffsetColumn(index)) {
138 return false;
139 }
140 }
141 return true;
142 }
143
151 static RegularGridAdjacency2D requireStoredAdjacency(const MorphologicalTree& tree, const char* context) {
152 if (const RegularGridAdjacency2D* adjacency = tree.constructionAdjacency()) {
153 return *adjacency;
154 }
156 if (sameAdjacencyGraph(adjacencies->minAdjacency, adjacencies->maxAdjacency)) {
157 return adjacencies->minAdjacency;
158 }
159 throw std::invalid_argument(
160 std::string(context) +
161 " cannot infer one image graph from distinct minimum/maximum adjacencies; pass the intended adjacency explicitly.");
162 }
163 if (const TopographicConvention* convention = tree.topographicConvention();
164 convention != nullptr && std::holds_alternative<SelfDualSpanImmersion>(convention->immersion) && tree.hasGridDomain2D()) {
165 const GridDomain2D& domain = *tree.gridDomain2D();
166 return RegularGridAdjacency2D(domain.rows, domain.columns, 1.0);
167 }
168 throw std::invalid_argument(std::string(context) + " requires an attached adjacency relation; pass an explicit adjacency relation instead.");
169 }
170
171 public:
184 const MorphologicalTree& tree, const char* context = "HierarchySaliencyMap::requireProjectionAdjacency") {
185 return requireStoredAdjacency(tree, context);
186 }
187
200 [[nodiscard]] static std::vector<int> computeTopologicalLevels(const MorphologicalTree& tree) {
201 detail::requireCommittedRootedHierarchy(tree, "HierarchySaliencyMap::computeTopologicalLevels");
202 return computeTopologicalLevelsEstablished(tree);
203 }
204
216 [[nodiscard]] static std::vector<int> computePartitionAppearanceLevels(const MorphologicalTree& tree) {
217 std::vector<int> levels = computeTopologicalLevels(tree);
218 for (NodeId nodeId : tree.aliveNodeIds()) {
219 ++levels[static_cast<std::size_t>(nodeId)];
220 }
221 return levels;
222 }
223
237 template <class NodeValue>
238 [[nodiscard]] static auto computeEdgeMap(const MorphologicalTree& tree, const RegularGridAdjacency2D& adjacency, NodeValue&& nodeValue)
240 using Value = std::decay_t<std::invoke_result_t<NodeValue, NodeId>>;
241 static_assert(!std::is_void_v<Value>, "HierarchySaliencyMap nodeValue must return a value.");
242
243 constexpr const char* context = "HierarchySaliencyMap::computeEdgeMap";
244 validateTreeAndAdjacency(tree, adjacency, context);
245
247 saliency.numRows = tree.numRows();
248 saliency.numColumns = tree.numColumns();
249 saliency.adjacencyRadius = adjacency.getRadius();
250
251 const std::size_t mutationVersion = tree.getMutationVersion();
252 auto&& scorer = nodeValue;
253 const int numPixels = tree.numPixels();
254 for (NodeId source = 0; source < numPixels; ++source) {
255 const NodeId sourceSmallestNode = detail::TreeKernelAccess::smallestNodeMap(tree, source);
256
257 for (int target : detail::CommittedGridAccess::forwardNeighbors(adjacency, source)) {
258 const NodeId targetSmallestNode = detail::TreeKernelAccess::smallestNodeMap(tree, target);
259
260 const NodeId lca = detail::TreeKernelAccess::lowestCommonAncestor(tree, sourceSmallestNode, targetSmallestNode);
261
262 saliency.sources.push_back(source);
263 saliency.targets.push_back(target);
264 saliency.values.push_back(std::invoke(scorer, lca));
265 tree.requireMutationVersion(mutationVersion, context);
266 }
267 }
268
269 return saliency;
270 }
271
312 template <class Value>
314 computeSaliencyEdgeMap(const MorphologicalTree& tree, const RegularGridAdjacency2D& adjacency, std::span<const Value> valuation,
315 HierarchyValuationPolicy policy = HierarchyValuationPolicy::AllowLevelCollapse,
316 HierarchyLevelConvention levelConvention = HierarchyLevelConvention::EdgeSaliencyValue,
317 HierarchyConnectivityPolicy connectivityPolicy = HierarchyConnectivityPolicy::ValidateConnected) {
318 constexpr const char* context = "HierarchySaliencyMap::computeSaliencyEdgeMap";
319 validateTreeAndAdjacency(tree, adjacency, context);
320 HierarchySaliencyMapValidation::validateHierarchyValuationEstablished(tree, valuation, policy,
321 HierarchyValuationRangePolicy::RequireNonNegative, context);
322 if (connectivityPolicy == HierarchyConnectivityPolicy::ValidateConnected) {
323 HierarchySaliencyMapValidation::validateHierarchyConnectivityEstablished(tree, adjacency, context);
324 }
325 if (levelConvention == HierarchyLevelConvention::PartitionAppearanceLevel) {
326 for (NodeId nodeId : tree.aliveNodeIds()) {
327 const Value& level = valuation[static_cast<std::size_t>(nodeId)];
328 if (!(Value{} < level)) {
329 throw std::invalid_argument(std::string(context) + " requires positive partition-appearance levels.");
330 }
331 if constexpr (std::is_floating_point_v<Value>) {
332 if (std::floor(level) != level) {
333 throw std::invalid_argument(std::string(context) + " requires integer-valued partition-appearance levels.");
334 }
335 }
336 }
337 }
338
339 return computeSaliencyEdgeMapEstablished(tree, adjacency, valuation, levelConvention);
340 }
341
356 template <class Value>
358 computeSaliencyEdgeMap(const MorphologicalTree& tree, std::span<const Value> valuation,
359 HierarchyValuationPolicy policy = HierarchyValuationPolicy::AllowLevelCollapse,
360 HierarchyLevelConvention levelConvention = HierarchyLevelConvention::EdgeSaliencyValue,
361 HierarchyConnectivityPolicy connectivityPolicy = HierarchyConnectivityPolicy::ValidateConnected) {
362 return computeSaliencyEdgeMap(tree, requireStoredAdjacency(tree, "HierarchySaliencyMap::computeSaliencyEdgeMap"), valuation, policy, levelConvention,
364 }
365
382 template <class Value>
385 HierarchyValuationPolicy policy = HierarchyValuationPolicy::AllowLevelCollapse,
386 HierarchyConnectivityPolicy connectivityPolicy = HierarchyConnectivityPolicy::ValidateConnected) {
387 constexpr const char* context = "HierarchySaliencyMap::computeCanonicalRankedSaliencyEdgeMap";
388 validateTreeAndAdjacency(tree, adjacency, context);
389 HierarchySaliencyMapValidation::validateHierarchyValuationEstablished(tree, valuation, policy,
390 HierarchyValuationRangePolicy::AllowAnyFinite, context);
391 if (connectivityPolicy == HierarchyConnectivityPolicy::ValidateConnected) {
392 HierarchySaliencyMapValidation::validateHierarchyConnectivityEstablished(tree, adjacency, context);
393 }
394
395 return computeCanonicalRankedSaliencyEdgeMapEstablished(tree, adjacency, valuation);
396 }
397
408 template <class Value>
411 HierarchyValuationPolicy policy = HierarchyValuationPolicy::AllowLevelCollapse,
412 HierarchyConnectivityPolicy connectivityPolicy = HierarchyConnectivityPolicy::ValidateConnected) {
413 return computeCanonicalRankedSaliencyEdgeMap(tree, requireStoredAdjacency(tree, "HierarchySaliencyMap::computeCanonicalRankedSaliencyEdgeMap"),
415 }
416
425 template <class Value> [[nodiscard]] static EdgeSaliencyMap<int> rankEdgeSaliencyMap(const EdgeSaliencyMap<Value>& edgeMap) {
427 ranked.numRows = edgeMap.numRows;
428 ranked.numColumns = edgeMap.numColumns;
429 ranked.adjacencyRadius = edgeMap.adjacencyRadius;
430 ranked.sources = edgeMap.sources;
431 ranked.targets = edgeMap.targets;
432 std::vector<Value> uniqueValues = edgeMap.values;
433 std::sort(uniqueValues.begin(), uniqueValues.end());
434 uniqueValues.erase(std::unique(uniqueValues.begin(), uniqueValues.end()), uniqueValues.end());
435 ranked.values.reserve(edgeMap.values.size());
436 for (const Value& value : edgeMap.values) {
437 ranked.values.push_back(static_cast<int>(std::distance(uniqueValues.begin(), std::lower_bound(uniqueValues.begin(), uniqueValues.end(), value))));
438 }
439 return ranked;
440 }
441
449 template <class NodeValue>
452 return computeEdgeMap(tree, requireStoredAdjacency(tree, "HierarchySaliencyMap::computeEdgeMap"), std::forward<NodeValue>(nodeValue));
453 }
454
463 constexpr const char* context = "HierarchySaliencyMap::computeTopologicalLevelEdgeMap";
464 validateTreeAndAdjacency(tree, adjacency, context);
465 HierarchySaliencyMapValidation::validateHierarchyConnectivityEstablished(tree, adjacency, context);
466 std::vector<int> levels = computeTopologicalLevelsEstablished(tree);
467 return computeCanonicalRankedSaliencyEdgeMapEstablished(tree, adjacency, std::span<const int>(levels));
468 }
469
477 return computeTopologicalLevelEdgeMap(tree, requireStoredAdjacency(tree, "HierarchySaliencyMap::computeTopologicalLevelEdgeMap"));
478 }
479
487 template <AltitudeValue T>
489 const RegularGridAdjacency2D& adjacency) {
490 constexpr const char* context = "HierarchySaliencyMap::computeNormalizedAltitudeEdgeMap";
491 validateTreeAndAdjacency(tree.topology(), adjacency, context);
493 HierarchySaliencyMapValidation::validateHierarchyConnectivityEstablished(tree.topology(), adjacency, context);
494 return computeSaliencyEdgeMapEstablished(tree.topology(), adjacency, std::span<const double>(scores), HierarchyLevelConvention::EdgeSaliencyValue);
495 }
496
504 return computeNormalizedAltitudeEdgeMap(tree, requireStoredAdjacency(tree.topology(), "HierarchySaliencyMap::computeNormalizedAltitudeEdgeMap"));
505 }
506
507 private:
513 [[nodiscard]] static std::vector<int> computeTopologicalLevelsEstablished(const MorphologicalTree& tree) {
514
515 std::vector<int> levels(static_cast<std::size_t>(tree.numInternalNodeSlots()), 0);
516 for (NodeId nodeId : tree.postOrder()) {
517 int level = 0;
518 for (NodeId childId : detail::TreeKernelAccess::children(tree, nodeId)) {
519 level = std::max(level, levels[static_cast<std::size_t>(childId)] + 1);
520 }
521 levels[static_cast<std::size_t>(nodeId)] = level;
522 }
523 return levels;
524 }
525
535 template <class Value>
536 [[nodiscard]] static EdgeSaliencyMap<Value> computeSaliencyEdgeMapEstablished(const MorphologicalTree& tree,
537 const RegularGridAdjacency2D& adjacency, std::span<const Value> valuation, HierarchyLevelConvention levelConvention) {
538 EdgeSaliencyMap<Value> saliency;
539 saliency.numRows = tree.numRows();
540 saliency.numColumns = tree.numColumns();
541 saliency.adjacencyRadius = adjacency.getRadius();
542
543 const int numPixels = tree.numPixels();
544 for (NodeId source = 0; source < numPixels; ++source) {
545 const NodeId sourceSmallestNode = detail::TreeKernelAccess::smallestNodeMap(tree, source);
546
547 for (int target : detail::CommittedGridAccess::forwardNeighbors(adjacency, source)) {
548 const NodeId targetSmallestNode = detail::TreeKernelAccess::smallestNodeMap(tree, target);
549
550 Value edgeValue{};
551 if (sourceSmallestNode != targetSmallestNode) {
552 const NodeId lca = detail::TreeKernelAccess::lowestCommonAncestor(tree, sourceSmallestNode, targetSmallestNode);
553 edgeValue = valuation[static_cast<std::size_t>(lca)];
554 if (levelConvention == HierarchyLevelConvention::PartitionAppearanceLevel) {
555 edgeValue -= Value{1};
556 }
557 }
558
559 saliency.sources.push_back(source);
560 saliency.targets.push_back(target);
561 saliency.values.push_back(edgeValue);
562 }
563 }
564
565 return saliency;
566 }
567
576 template <class Value>
577 [[nodiscard]] static EdgeSaliencyMap<int> computeCanonicalRankedSaliencyEdgeMapEstablished(const MorphologicalTree& tree,
578 const RegularGridAdjacency2D& adjacency, std::span<const Value> valuation) {
579 EdgeSaliencyMap<int> saliency;
580 saliency.numRows = tree.numRows();
581 saliency.numColumns = tree.numColumns();
582 saliency.adjacencyRadius = adjacency.getRadius();
583
584 struct ProjectedLevel {
585 NodeId source;
586 NodeId target;
587 NodeId lca;
588 };
589 std::vector<ProjectedLevel> projected;
590 std::vector<Value> effectiveValues;
591 bool hasBaseEdge = false;
592 const int numPixels = tree.numPixels();
593 for (NodeId source = 0; source < numPixels; ++source) {
594 const NodeId sourceSmallestNode = detail::TreeKernelAccess::smallestNodeMap(tree, source);
595 for (int targetValue : detail::CommittedGridAccess::forwardNeighbors(adjacency, source)) {
596 const NodeId target = static_cast<NodeId>(targetValue);
597 const NodeId targetSmallestNode = detail::TreeKernelAccess::smallestNodeMap(tree, target);
598 NodeId lca = InvalidNode;
599 if (sourceSmallestNode == targetSmallestNode) {
600 hasBaseEdge = true;
601 } else {
602 lca = detail::TreeKernelAccess::lowestCommonAncestor(tree, sourceSmallestNode, targetSmallestNode);
603 effectiveValues.push_back(valuation[static_cast<std::size_t>(lca)]);
604 }
605 projected.push_back(ProjectedLevel{source, target, lca});
606 }
607 }
608
609 std::sort(effectiveValues.begin(), effectiveValues.end());
610 effectiveValues.erase(std::unique(effectiveValues.begin(), effectiveValues.end()), effectiveValues.end());
611 saliency.sources.reserve(projected.size());
612 saliency.targets.reserve(projected.size());
613 saliency.values.reserve(projected.size());
614 const int transitionOffset = hasBaseEdge ? 1 : 0;
615 for (const ProjectedLevel& edge : projected) {
616 int rank = 0;
617 if (edge.lca != InvalidNode) {
618 const Value& value = valuation[static_cast<std::size_t>(edge.lca)];
619 rank = transitionOffset +
620 static_cast<int>(std::distance(effectiveValues.begin(), std::lower_bound(effectiveValues.begin(), effectiveValues.end(), value)));
621 }
622 saliency.sources.push_back(edge.source);
623 saliency.targets.push_back(edge.target);
624 saliency.values.push_back(rank);
625 }
626 return saliency;
627 }
628};
629
647 public:
656 static void validate(const MorphologicalTree& tree, const RegularGridAdjacency2D& adjacency) {
657 HierarchySaliencyMapValidation::validateHierarchyConnectivity(tree, adjacency, "ComponentTreePartitionHierarchyAdapter::validate");
658 }
659
669
682 template <class Value>
684 std::span<const Value> partitionAppearanceLevels,
685 HierarchyValuationPolicy policy = HierarchyValuationPolicy::AllowLevelCollapse) {
687 HierarchyLevelConvention::PartitionAppearanceLevel, HierarchyConnectivityPolicy::ValidateConnected);
688 }
689};
690
691} // namespace mmcfilters
int NodeId
Node identifier type used throughout the project.
Definition Common.hpp:17
constexpr NodeId InvalidNode
Sentinel value used to denote an invalid node identifier.
Definition Common.hpp:34
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.
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.
const ComplementaryAdjacencies * complementaryAdjacencies() const noexcept
Returns the complementary adjacencies explicitly retained by a topographic immersion.
void requireMutationVersion(std::size_t expectedVersion, const char *context) const
Rejects stale read-only views that captured an older mutation version.
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.
const RegularGridAdjacency2D * constructionAdjacency() const noexcept
Returns the adjacency retained by a shared construction context.
AliveNodeRange aliveNodeIds() const
Returns a fail-fast range over all live node ids.
std::size_t getMutationVersion() const noexcept
Returns the monotonic mutation counter used by read-only views.
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.
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.