3#include "HierarchySaliencyMap.hpp"
4#include "../../utils/Image.hpp"
5#include "../detail/MorphologicalTreeConstructionContextQueries.hpp"
53enum class EdgeToPixelReducer {
177 if (edgeMap.sources.size() != edgeMap.targets.size() || edgeMap.sources.size() != edgeMap.
values.size()) {
178 throw std::invalid_argument(std::string(
context) +
" requires sources, targets, and values to have the same length.");
180 if (edgeMap.numRows <= 0 || edgeMap.numColumns <= 0) {
181 throw std::invalid_argument(std::string(
context) +
" requires a non-empty image domain.");
193 throw std::invalid_argument(std::string(
context) +
" requires a non-empty image domain.");
196 throw std::invalid_argument(std::string(
context) +
" requires a non-negative node-slot count.");
199 throw std::invalid_argument(std::string(
context) +
" requires sources and targets to have the same length.");
202 throw std::invalid_argument(std::string(
context) +
" requires offsets to have numNodeSlots + 1 entries.");
205 throw std::invalid_argument(std::string(
context) +
" requires offsets to span exactly the contour edge arrays.");
207 for (std::size_t
i = 1;
i < contours.
offsets.size(); ++
i) {
209 throw std::invalid_argument(std::string(
context) +
" requires monotonically increasing offsets inside the edge-array bounds.");
221 const auto pixelCount =
static_cast<std::size_t
>(edgeMap.numRows) *
static_cast<std::size_t
>(edgeMap.numColumns);
222 for (std::size_t
i = 0;
i < edgeMap.size(); ++
i) {
223 const NodeId source = edgeMap.sources[
i];
224 const NodeId target = edgeMap.targets[
i];
226 throw std::invalid_argument(std::string(
context) +
" requires sources and targets inside the image domain.");
240 throw std::invalid_argument(std::string(
context) +
" requires a non-empty rooted tree.");
243 throw std::invalid_argument(std::string(
context) +
" requires a non-empty image/pixel domain.");
246 throw std::invalid_argument(std::string(
context) +
" adjacency domain must match the tree image domain.");
267 throw std::runtime_error(
"HierarchySaliencyMapProjection::computeIncrementalNodeContours found an LCA outside the dense node-id domain.");
276 for (std::size_t
i = 0;
i <
flat.size(); ++
i) {
300 template <
class Value>
302 constexpr const char*
context =
"HierarchySaliencyMapProjection::edgeMapToPixelImage";
303 validateEdgeMap(edgeMap,
context);
304 validateEdgeEndpoints(edgeMap,
context);
307 double* pixels =
image->rawData();
308 const auto pixelCount =
static_cast<std::size_t
>(edgeMap.numRows) *
static_cast<std::size_t
>(edgeMap.numColumns);
311 case EdgeToPixelReducer::Max: {
313 for (std::size_t
i = 0;
i < edgeMap.size(); ++
i) {
314 const double value =
static_cast<double>(edgeMap.
values[
i]);
315 const auto source =
static_cast<std::size_t
>(edgeMap.sources[
i]);
316 const auto target =
static_cast<std::size_t
>(edgeMap.targets[
i]);
317 if (!
initialized[source] || pixels[source] < value) {
318 pixels[source] = value;
321 if (!
initialized[target] || pixels[target] < value) {
322 pixels[target] = value;
328 case EdgeToPixelReducer::Mean: {
330 for (std::size_t
i = 0;
i < edgeMap.size(); ++
i) {
331 const double value =
static_cast<double>(edgeMap.
values[
i]);
332 const auto source =
static_cast<std::size_t
>(edgeMap.sources[
i]);
333 const auto target =
static_cast<std::size_t
>(edgeMap.targets[
i]);
334 pixels[source] += value;
335 pixels[target] += value;
341 pixels[
i] /=
static_cast<double>(
count[
i]);
347 throw std::invalid_argument(std::string(
context) +
" received an unknown edge-to-pixel reducer.");
363 constexpr const char*
context =
"HierarchySaliencyMapProjection::thresholdCut";
364 validateEdgeMap(edgeMap,
context);
367 contours.
numRows = edgeMap.numRows;
370 contours.
sources.reserve(edgeMap.size());
371 contours.
targets.reserve(edgeMap.size());
373 for (std::size_t
i = 0;
i < edgeMap.size(); ++
i) {
375 contours.
sources.push_back(edgeMap.sources[
i]);
376 contours.
targets.push_back(edgeMap.targets[
i]);
394 constexpr const char*
context =
"HierarchySaliencyMapProjection::nodeContourEdges";
395 validateTreeAndAdjacency(tree, adjacency,
context);
403 for (
NodeId source = 0; source < numPixels; ++source) {
406 throw std::runtime_error(std::string(
context) +
" found a pixel without a live smallest node.");
412 throw std::runtime_error(std::string(
context) +
" found a neighbour pixel without a live smallest node.");
420 throw std::runtime_error(std::string(
context) +
" could not find a live LCA for an adjacency edge.");
423 contours.
sources.push_back(source);
424 contours.
targets.push_back(target);
425 contours.
nodes.push_back(lca);
439 if (adjacency ==
nullptr) {
440 throw std::invalid_argument(
441 "HierarchySaliencyMapProjection::nodeContourEdges requires an attached adjacency relation; pass an explicit adjacency relation instead.");
453 template <AltitudeValue T>
500 template <AltitudeValue T>
529 template <
class Value>
531 constexpr const char*
context =
"HierarchySaliencyMapProjection::projectNodeValuation";
532 validateIncrementalContours(contours,
context);
534 throw std::invalid_argument(std::string(
context) +
" requires one valuation value per dense node slot.");
538 edgeMap.numRows = contours.
numRows;
541 edgeMap.sources.reserve(contours.
size());
542 edgeMap.targets.reserve(contours.
size());
548 edgeMap.sources.push_back(contours.
sources[
i]);
549 edgeMap.targets.push_back(contours.
targets[
i]);
550 edgeMap.
values.push_back(value);
570 template <
class Value,
class Threshold>
573 constexpr const char*
context =
"HierarchySaliencyMapProjection::thresholdByNodeValuation";
574 validateIncrementalContours(contours,
context);
576 throw std::invalid_argument(std::string(
context) +
" requires one valuation value per dense node slot.");
583 cut.sources.reserve(contours.
size());
584 cut.targets.reserve(contours.
size());
int NodeId
Node identifier type used throughout the project.
constexpr NodeId InvalidNode
Sentinel value used to denote an invalid node identifier.
Derived projections and contour materializations of saliency maps.
static ImagePtr< double > edgeMapToPixelImage(const EdgeSaliencyMap< Value > &edgeMap, EdgeToPixelReducer reducer=EdgeToPixelReducer::Max)
Rasterizes an edge-indexed map into a pixel image for display.
static IncrementalNodeContourMap computeIncrementalNodeContours(const ValuedMorphologicalTree< T > &tree, const RegularGridAdjacency2D &adjacency)
Computes valued-tree incremental contours with explicit adjacency.
static IncrementalNodeContourMap computeIncrementalNodeContours(const MorphologicalTree &tree)
Computes per-node incremental contour edges using stored adjacency.
static EdgeContourMap thresholdCut(const EdgeSaliencyMap< Value > &edgeMap, Threshold threshold)
Thresholds an edge saliency map into an edge contour set.
static EdgeContourMap thresholdByNodeValuation(const IncrementalNodeContourMap &contours, std::span< const Value > nodeValuation, Threshold threshold)
Thresholds incremental contour edges by dense node valuation.
static IncrementalNodeContourMap computeIncrementalNodeContours(const MorphologicalTree &tree, const RegularGridAdjacency2D &adjacency)
Computes per-node incremental contour edges with explicit adjacency.
static EdgeSaliencyMap< Value > projectNodeValuation(const IncrementalNodeContourMap &contours, std::span< const Value > nodeValuation)
Projects dense node valuation onto transition contour edges.
static NodeContourEdgeMap nodeContourEdges(const ValuedMorphologicalTree< T > &tree)
Projects valued-tree stored-adjacency edges onto hierarchy nodes.
static IncrementalNodeContourMap computeIncrementalNodeContours(const ValuedMorphologicalTree< T > &tree)
Computes valued-tree incremental contours using stored adjacency.
static NodeContourEdgeMap nodeContourEdges(const ValuedMorphologicalTree< T > &tree, const RegularGridAdjacency2D &adjacency)
Projects valued-tree adjacency edges onto their hierarchy node.
static NodeContourEdgeMap nodeContourEdges(const MorphologicalTree &tree, const RegularGridAdjacency2D &adjacency)
Projects transition adjacency edges onto their hierarchy node.
static NodeContourEdgeMap nodeContourEdges(const MorphologicalTree &tree)
Projects stored-adjacency transition edges onto hierarchy nodes.
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.
int numPixels() const
Returns the cardinality of the pixel domain.
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.
int numColumns() const
Returns the number of columns in the regular 2D pixel domain.
NodeId root() const
Returns the current hierarchy root.
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.
Owning result for one computed scalar attribute layout and buffer.
std::vector< Real > & values() noexcept
Returns the mutable flat attribute buffer.
Edge-indexed contour set induced by thresholding a saliency map.
std::vector< NodeId > sources
Source pixel identifier of each selected edge.
int numColumns
Number of columns in the image grid.
int numRows
Number of rows in the image grid.
std::vector< NodeId > targets
Target pixel identifier of each selected edge.
std::size_t size() const noexcept
Returns the number of selected edges.
bool empty() const noexcept
Returns whether no contour edge is selected.
double adjacencyRadius
Radius of the adjacency used to enumerate the edges.
Per-node incremental contour edges induced by a hierarchy.
std::vector< NodeId > sources
Source pixel ids grouped by boundary-owning node.
int numNodeSlots
Number of slots in the dense node-id domain.
int numRows
Number of rows in the image grid.
std::size_t size() const noexcept
Returns the total number of stored contour edges.
std::vector< NodeId > targets
Target pixel ids parallel to sources.
std::size_t nodeEnd(NodeId nodeId) const
Returns the exclusive edge offset for nodeId.
std::vector< std::size_t > offsets
CSR-style boundaries of the edge slice owned by each node.
std::size_t nodeBegin(NodeId nodeId) const
Returns the inclusive edge offset for nodeId.
double adjacencyRadius
Radius of the adjacency used to enumerate the edges.
std::size_t nodeSize(NodeId nodeId) const
Returns the number of contour edges owned by nodeId.
bool empty() const noexcept
Returns whether no contour edge is stored.
int numColumns
Number of columns in the image grid.
Edge-indexed contour set projected onto hierarchy nodes.
double adjacencyRadius
Radius of the adjacency used to enumerate the edges.
std::vector< NodeId > sources
Source pixel identifier of each contour edge.
std::size_t size() const noexcept
Returns the number of projected contour edges.
int numColumns
Number of columns in the image grid.
std::vector< NodeId > targets
Target pixel identifier of each contour edge.
std::vector< NodeId > nodes
Boundary-owning hierarchy node parallel to each edge.
bool empty() const noexcept
Returns whether the projection has no contour edges.
int numRows
Number of rows in the image grid.