MorphologicalAttributeFilters
Public API documentation
Loading...
Searching...
No Matches
GrayLevelStatsComputer.hpp
1#pragma once
2
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"
9
10#include <algorithm>
11#include <array>
12#include <cmath>
13#include <span>
14#include <stdexcept>
15#include <string_view>
16#include <vector>
17
18namespace mmcfilters::attributes::computers {
19
20namespace detail {
21inline NodeId grayStatsSlotOf(const MorphologicalTree&, NodeId nodeId) noexcept {
22 return nodeId;
23}
24
25struct GrayLevelStatsRequest {
26 bool level = false;
27 bool meanLevel = false;
28 bool varianceLevel = false;
29 bool grayHeight = false;
30
31 [[nodiscard]] bool any() const noexcept {
32 return level || meanLevel || varianceLevel || grayHeight;
33 }
34
35 [[nodiscard]] bool needsAggregateDependencies() const noexcept {
36 return meanLevel || varianceLevel;
37 }
38
39 [[nodiscard]] static GrayLevelStatsRequest from(std::span<const Attribute> requestedAttributes) {
40 return {
41 .level = containsGrayStatsAttribute(requestedAttributes, LEVEL),
42 .meanLevel = containsGrayStatsAttribute(requestedAttributes, MEAN_LEVEL),
43 .varianceLevel = containsGrayStatsAttribute(requestedAttributes, VARIANCE_LEVEL),
44 .grayHeight = containsGrayStatsAttribute(requestedAttributes, GRAY_HEIGHT)};
45 }
46
47private:
48 [[nodiscard]] static bool containsGrayStatsAttribute(
49 std::span<const Attribute> requestedAttributes,
50 Attribute attribute)
51 {
52 return std::find(requestedAttributes.begin(), requestedAttributes.end(), attribute) != requestedAttributes.end();
53 }
54};
55
70template<std::floating_point 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)
78{
80
81 const GrayLevelStatsRequest request = GrayLevelStatsRequest::from(requestedAttributes);
82 if (!request.any()) {
83 return;
84 }
85
86 const bool needsAggregateDependencies = request.needsAggregateDependencies();
87
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); };
92
93 const DependencyResolver<Real> dependencies{dependencySources};
94 const DependencySourceT<Real>* dependencyVol = needsAggregateDependencies
95 ? &dependencies.require(VOLUME)
96 : nullptr;
97 const DependencySourceT<Real>* dependencyArea = needsAggregateDependencies
98 ? &dependencies.require(AREA)
99 : nullptr;
100 auto indexOfVol = [&](NodeId idx) { return dependencyVol->attrNames->linearIndex(idx, VOLUME); };
101 auto indexOfArea = [&](NodeId idx) { return dependencyArea->attrNames->linearIndex(idx, AREA); };
102
103 std::vector<double> sumGrayLevelSquare;
104 if (request.varianceLevel) {
105 sumGrayLevelSquare.assign(tree.getNumInternalNodeSlots(), 0.0);
106 }
107
108 ::mmcfilters::detail::traversePostOrder(
109 tree,
110 tree.getRoot(),
111 [&](NodeId nodeId) {
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;
121 }
122 if (request.level)
123 buffer[indexOfLevel(node)] = nodeAltitudeAsReal;
124 if (request.grayHeight)
125 buffer[indexOfGrayHeight(node)] = nodeAltitudeAsReal;
126 },
127 [&](NodeId parentNodeId, NodeId childNodeId) {
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);
137 else
138 parentValue = std::min(parentValue, childValue);
139 }
140 },
141 [&](NodeId nodeId) {
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) {
148 Real meanGrayLevel =
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);
154 }
155 });
156
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;
162 } else {
163 buffer[indexOfGrayHeight(node)] =
164 std::abs(static_cast<Real>(TreeAltitudeAlgorithms::getAltitude(altitude, nodeId)) -
165 buffer[indexOfGrayHeight(node)]);
166 }
167 }
168 }
169}
170
171}
172
196public:
198 static constexpr std::string_view familyName = "gray-level-stats";
199
201 static constexpr AttributeComputerFamily family = AttributeComputerFamily::GrayLevelStats;
202
204 static constexpr AttributeComputerDomain domain = AttributeComputerDomain::Altitude;
205
209 inline static constexpr std::array<Attribute, 4> producedAttributes{
210 LEVEL,
211 MEAN_LEVEL,
212 VARIANCE_LEVEL,
213 GRAY_HEIGHT};
214
224 template <std::floating_point Real, AltitudeValue T>
226 {
227 requireAttributeBufferShape(context.tree, context.buffer, context.attrNames);
228 detail::computeGrayLevelStatsAttributeKernel(
229 context.tree,
230 context.altitude,
231 context.buffer,
232 context.attrNames,
233 context.requestedAttributes,
234 context.dependencySources);
235 }
236
243 template <std::floating_point Real, AltitudeValue T>
245 {
246 requireUnitAttributeBufferShape(context.tree, context.unitProperParts, context.buffer, context.attrNames);
247 TreeAltitudeAlgorithms::validateAltitudeBufferShape(context.tree, context.altitude);
248
249 const detail::GrayLevelStatsRequest request = detail::GrayLevelStatsRequest::from(context.requestedAttributes);
250 if (!request.any()) {
251 return;
252 }
253
254 for (NodeId leafIndex = 0; leafIndex < static_cast<NodeId>(context.unitProperParts.size()); ++leafIndex) {
255 const NodeId properPart = context.unitProperParts[static_cast<size_t>(leafIndex)];
256 const Real altitudeValue =
257 (request.level || request.meanLevel)
258 ? static_cast<Real>(unitAltitude(context.tree, context.altitude, properPart))
259 : Real{0};
260
261 if (request.level) {
262 context.buffer[context.attrNames.linearIndex(leafIndex, LEVEL)] = altitudeValue;
263 }
264 if (request.meanLevel) {
265 context.buffer[context.attrNames.linearIndex(leafIndex, MEAN_LEVEL)] = altitudeValue;
266 }
267 if (request.varianceLevel) {
268 context.buffer[context.attrNames.linearIndex(leafIndex, VARIANCE_LEVEL)] = Real{0};
269 }
270 if (request.grayHeight) {
271 context.buffer[context.attrNames.linearIndex(leafIndex, GRAY_HEIGHT)] = Real{0};
272 }
273 }
274 }
275
276};
277
278} // namespace mmcfilters::attributes::computers
int NodeId
Node identifier type used throughout the project.
Definition Common.hpp:17
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.