3#include "AttributeComputerDomain.hpp"
4#include "AttributeComputerFamily.hpp"
5#include "../detail/AttributeKernelSupport.hpp"
6#include "../../trees/detail/TreeTraversalDetail.hpp"
7#include "../../trees/TreeAltitudeAlgorithms.hpp"
8#include "../../utils/Altitude.hpp"
18namespace mmcfilters::attributes::computers {
21inline NodeId grayStatsSlotOf(
const MorphologicalTree&, NodeId nodeId)
noexcept {
25struct GrayLevelStatsRequest {
27 bool meanLevel =
false;
28 bool varianceLevel =
false;
29 bool grayHeight =
false;
31 [[nodiscard]]
bool any() const noexcept {
32 return level || meanLevel || varianceLevel || grayHeight;
35 [[nodiscard]]
bool needsAggregateDependencies() const noexcept {
36 return meanLevel || varianceLevel;
39 [[nodiscard]]
static GrayLevelStatsRequest from(std::span<const Attribute> requestedAttributes) {
41 .level = containsGrayStatsAttribute(requestedAttributes, LEVEL),
42 .meanLevel = containsGrayStatsAttribute(requestedAttributes, MEAN_LEVEL),
43 .varianceLevel = containsGrayStatsAttribute(requestedAttributes, VARIANCE_LEVEL),
44 .grayHeight = containsGrayStatsAttribute(requestedAttributes, GRAY_HEIGHT)};
48 [[nodiscard]]
static bool containsGrayStatsAttribute(
49 std::span<const Attribute> requestedAttributes,
52 return std::find(requestedAttributes.begin(), requestedAttributes.end(), attribute) != requestedAttributes.end();
70template<std::
floating_po
int Real, AltitudeValue T>
71void computeGrayLevelStatsAttributeKernel(
72 const MorphologicalTree& tree,
73 std::span<const T> altitude,
74 std::span<Real> buffer,
75 const AttributeNames& attrNames,
76 std::span<const Attribute> requestedAttributes,
77 std::span<
const DependencySourceT<Real>> dependencySources)
81 const GrayLevelStatsRequest request = GrayLevelStatsRequest::from(requestedAttributes);
86 const bool needsAggregateDependencies = request.needsAggregateDependencies();
88 auto indexOfMean = [&](
NodeId idx) {
return attrNames.linearIndex(idx, MEAN_LEVEL); };
89 auto indexOfLevel = [&](
NodeId idx) {
return attrNames.linearIndex(idx, LEVEL); };
90 auto indexOfVariance = [&](
NodeId idx) {
return attrNames.linearIndex(idx, VARIANCE_LEVEL); };
91 auto indexOfGrayHeight = [&](
NodeId idx) {
return attrNames.linearIndex(idx, GRAY_HEIGHT); };
93 const DependencyResolver<Real> dependencies{dependencySources};
94 const DependencySourceT<Real>* dependencyVol = needsAggregateDependencies
95 ? &dependencies.require(VOLUME)
97 const DependencySourceT<Real>* dependencyArea = needsAggregateDependencies
98 ? &dependencies.require(AREA)
100 auto indexOfVol = [&](
NodeId idx) {
return dependencyVol->attrNames->linearIndex(idx, VOLUME); };
101 auto indexOfArea = [&](
NodeId idx) {
return dependencyArea->attrNames->linearIndex(idx, AREA); };
103 std::vector<double> sumGrayLevelSquare;
104 if (request.varianceLevel) {
105 sumGrayLevelSquare.assign(tree.getNumInternalNodeSlots(), 0.0);
108 ::mmcfilters::detail::traversePostOrder(
112 const NodeId node = detail::grayStatsSlotOf(tree, nodeId);
113 const T nodeAltitude = TreeAltitudeAlgorithms::getAltitude(altitude, nodeId);
114 const Real nodeAltitudeAsReal = static_cast<Real>(nodeAltitude);
115 if (request.varianceLevel) {
116 const double nodeAltitudeAsDouble = static_cast<double>(nodeAltitude);
117 sumGrayLevelSquare[node] =
118 static_cast<double>(tree.getNumProperParts(nodeId)) *
119 nodeAltitudeAsDouble *
120 nodeAltitudeAsDouble;
123 buffer[indexOfLevel(node)] = nodeAltitudeAsReal;
124 if (request.grayHeight)
125 buffer[indexOfGrayHeight(node)] = nodeAltitudeAsReal;
128 const NodeId parent = detail::grayStatsSlotOf(tree, parentNodeId);
129 const NodeId child = detail::grayStatsSlotOf(tree, childNodeId);
130 if (request.varianceLevel)
131 sumGrayLevelSquare[parent] += sumGrayLevelSquare[child];
132 if (request.grayHeight) {
133 Real childValue = buffer[indexOfGrayHeight(child)];
134 Real& parentValue = buffer[indexOfGrayHeight(parent)];
135 if (tree.getTreeType() == MorphologicalTreeKind::MAX_TREE)
136 parentValue = std::max(parentValue, childValue);
138 parentValue = std::min(parentValue, childValue);
142 const NodeId node = detail::grayStatsSlotOf(tree, nodeId);
143 Real area = needsAggregateDependencies ? dependencyArea->buffer[indexOfArea(node)] : Real{0};
144 if (request.meanLevel)
145 buffer[indexOfMean(node)] =
146 ::mmcfilters::attributes::numeric::safeDivide(dependencyVol->buffer[indexOfVol(node)], area);
147 if (request.varianceLevel) {
149 ::mmcfilters::attributes::numeric::safeDivide(dependencyVol->buffer[indexOfVol(node)], area);
150 double meanGrayLevelSquare =
151 ::mmcfilters::attributes::numeric::safeDivide(sumGrayLevelSquare[node],
static_cast<double>(area));
152 Real var =
static_cast<Real
>(meanGrayLevelSquare - (
static_cast<double>(meanGrayLevel) *
static_cast<double>(meanGrayLevel)));
153 buffer[indexOfVariance(node)] = ::mmcfilters::attributes::numeric::clampNonNegative(var);
157 if (request.grayHeight) {
158 for (NodeId nodeId : tree.getPostOrderNodes()) {
159 const NodeId node = detail::grayStatsSlotOf(tree, nodeId);
160 if (tree.isLeaf(nodeId)) {
161 buffer[indexOfGrayHeight(node)] = 0.0f;
163 buffer[indexOfGrayHeight(node)] =
165 buffer[indexOfGrayHeight(node)]);
198 static constexpr std::string_view familyName =
"gray-level-stats";
201 static constexpr AttributeComputerFamily family = AttributeComputerFamily::GrayLevelStats;
204 static constexpr AttributeComputerDomain domain = AttributeComputerDomain::Altitude;
209 inline static constexpr std::array<Attribute, 4> producedAttributes{
224 template <std::
floating_po
int Real, AltitudeValue T>
228 detail::computeGrayLevelStatsAttributeKernel(
243 template <std::
floating_po
int Real, AltitudeValue T>
247 TreeAltitudeAlgorithms::validateAltitudeBufferShape(
context.tree,
context.altitude);
249 const detail::GrayLevelStatsRequest
request = detail::GrayLevelStatsRequest::from(
context.requestedAttributes);
int NodeId
Node identifier type used throughout the project.
static T getAltitude(std::span< const T > altitude, NodeId nodeId)
Reads one node altitude from an explicit altitude buffer.
static void validateAltitudeBufferShape(const MorphologicalTree &tree, std::span< const T > altitude)
Validates that an altitude buffer covers the dense internal-node domain.
Computes grey-level statistics derived from subtree aggregation.
static void compute(const AltitudeAttributeComputeContext< Real, T > &context)
Computes the requested grey-level statistics.
static void computeUnitRows(const AltitudeUnitAttributeComputeContext< Real, T > &context)
Materializes grey-level statistics for one-pixel unit supports.
Owning result for one computed scalar attribute layout and buffer.