3#include "HierarchySaliencyMap.hpp"
4#include "../../utils/Image.hpp"
5#include "../MorphologicalTree.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.");
239 detail::requireCommittedRootedHierarchy(tree,
context);
241 throw std::invalid_argument(std::string(
context) +
" requires a non-empty image/pixel domain.");
244 throw std::invalid_argument(std::string(
context) +
" adjacency domain must match the tree image domain.");
264 ++contours.
offsets[
static_cast<std::size_t
>(nodeId) + 1];
271 for (std::size_t
i = 0;
i <
flat.size(); ++
i) {
273 const std::size_t
dst =
cursor[
static_cast<std::size_t
>(nodeId)]++;
295 template <
class Value>
297 constexpr const char*
context =
"HierarchySaliencyMapProjection::edgeMapToPixelImage";
298 validateEdgeMap(edgeMap,
context);
299 validateEdgeEndpoints(edgeMap,
context);
302 double* pixels =
image->rawData();
303 const auto pixelCount =
static_cast<std::size_t
>(edgeMap.numRows) *
static_cast<std::size_t
>(edgeMap.numColumns);
306 case EdgeToPixelReducer::Max: {
308 for (std::size_t
i = 0;
i < edgeMap.size(); ++
i) {
309 const double value =
static_cast<double>(edgeMap.
values[
i]);
310 const auto source =
static_cast<std::size_t
>(edgeMap.sources[
i]);
311 const auto target =
static_cast<std::size_t
>(edgeMap.targets[
i]);
312 if (!
initialized[source] || pixels[source] < value) {
313 pixels[source] = value;
316 if (!
initialized[target] || pixels[target] < value) {
317 pixels[target] = value;
323 case EdgeToPixelReducer::Mean: {
324 std::vector<std::size_t> count(
pixelCount, 0);
325 for (std::size_t
i = 0;
i < edgeMap.size(); ++
i) {
326 const double value =
static_cast<double>(edgeMap.
values[
i]);
327 const auto source =
static_cast<std::size_t
>(edgeMap.sources[
i]);
328 const auto target =
static_cast<std::size_t
>(edgeMap.targets[
i]);
329 pixels[source] += value;
330 pixels[target] += value;
336 pixels[
i] /=
static_cast<double>(count[
i]);
342 throw std::invalid_argument(std::string(
context) +
" received an unknown edge-to-pixel reducer.");
358 constexpr const char*
context =
"HierarchySaliencyMapProjection::thresholdCut";
359 validateEdgeMap(edgeMap,
context);
362 contours.
numRows = edgeMap.numRows;
365 contours.
sources.reserve(edgeMap.size());
366 contours.
targets.reserve(edgeMap.size());
368 for (std::size_t
i = 0;
i < edgeMap.size(); ++
i) {
370 contours.
sources.push_back(edgeMap.sources[
i]);
371 contours.
targets.push_back(edgeMap.targets[
i]);
389 constexpr const char*
context =
"HierarchySaliencyMapProjection::nodeContourEdges";
390 validateTreeAndAdjacency(tree, adjacency,
context);
398 for (
NodeId source = 0; source < numPixels; ++source) {
401 for (
int target : detail::CommittedGridAccess::forwardNeighbors(adjacency, source)) {
409 contours.
sources.push_back(source);
410 contours.
targets.push_back(target);
411 contours.
nodes.push_back(lca);
425 if (adjacency ==
nullptr) {
426 throw std::invalid_argument(
427 "HierarchySaliencyMapProjection::nodeContourEdges requires an attached adjacency relation; pass an explicit adjacency relation instead.");
439 template <AltitudeValue T>
486 template <AltitudeValue T>
515 template <
class Value>
517 constexpr const char*
context =
"HierarchySaliencyMapProjection::projectNodeValuation";
518 validateIncrementalContours(contours,
context);
520 throw std::invalid_argument(std::string(
context) +
" requires one valuation value per dense node slot.");
524 edgeMap.numRows = contours.
numRows;
527 edgeMap.sources.reserve(contours.
size());
528 edgeMap.targets.reserve(contours.
size());
533 for (std::size_t
i = contours.
offsets[
static_cast<std::size_t
>(nodeId)];
i < contours.
offsets[
static_cast<std::size_t
>(nodeId) + 1]; ++
i) {
534 edgeMap.sources.push_back(contours.
sources[
i]);
535 edgeMap.targets.push_back(contours.
targets[
i]);
536 edgeMap.
values.push_back(value);
556 template <
class Value,
class Threshold>
559 constexpr const char*
context =
"HierarchySaliencyMapProjection::thresholdByNodeValuation";
560 validateIncrementalContours(contours,
context);
562 throw std::invalid_argument(std::string(
context) +
" requires one valuation value per dense node slot.");
569 cut.sources.reserve(contours.
size());
570 cut.targets.reserve(contours.
size());
574 for (std::size_t
i = contours.
offsets[
static_cast<std::size_t
>(nodeId)];
i < contours.
offsets[
static_cast<std::size_t
>(nodeId) + 1]; ++
i) {
int NodeId
Node identifier type used throughout the project.
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.
int numPixels() const
Returns the cardinality of the pixel domain.
const RegularGridAdjacency2D * constructionAdjacency() const noexcept
Returns the adjacency retained by a shared construction context.
int numColumns() const
Returns the number of columns in the 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.
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.