mmcfilters
Public API documentation
Loading...
Searching...
No Matches
VolumeComputer.hpp
1#pragma once
2
3#include "AttributeComputerDomain.hpp"
4#include "AttributeComputerFamily.hpp"
5#include "../detail/AttributeKernelSupport.hpp"
6#include "../../trees/detail/TreeKernelAccess.hpp"
7#include "../../trees/TreeAltitudeAlgorithms.hpp"
8#include "../../utils/Altitude.hpp"
9#include "../../utils/Contract.hpp"
10
11#include <algorithm>
12#include <array>
13#include <cmath>
14#include <span>
15#include <stdexcept>
16#include <string_view>
17
18namespace mmcfilters::attributes::computers {
19
20namespace detail {
27inline NodeId volumeSlotOf(const MorphologicalTree&, NodeId nodeId) noexcept { return nodeId; }
28
30struct VolumeRequest {
32 bool volume = false;
34 bool relative = false;
35
41 [[nodiscard]] bool any() const noexcept { return volume || relative; }
42
48 [[nodiscard]] bool needsAreaDependency() const noexcept { return relative; }
49
56 [[nodiscard]] static VolumeRequest from(std::span<const Attribute> requestedAttributes) {
57 return {.volume = containsVolumeAttribute(requestedAttributes, Volume), .relative = containsVolumeAttribute(requestedAttributes, RelativeVolume)};
58 }
59
60 private:
68 [[nodiscard]] static bool containsVolumeAttribute(std::span<const Attribute> requestedAttributes, Attribute attribute) {
69 return std::find(requestedAttributes.begin(), requestedAttributes.end(), attribute) != requestedAttributes.end();
70 }
71};
72
85namespace kernel {
86
93template <std::floating_point Real, AltitudeValue T>
94void computeVolume(const AltitudeAttributeComputeContext<Real, T>& context, const VolumeRequest& request,
95 const DependencySourceT<Real>* areaDependency) {
96 if (!request.any()) {
97 return;
98 }
99
100 const int stride = context.attrNames.NUM_ATTRIBUTES;
101 const auto offsetOf = [&](Attribute attribute) { return context.attrNames.indexMap.find(attribute)->second; };
102 const int volumeOffset = request.volume ? offsetOf(Volume) : 0;
103 const int relativeOffset = request.relative ? offsetOf(RelativeVolume) : 0;
104 auto indexOfVolume = [&](NodeId node) { return static_cast<std::size_t>(node * stride + volumeOffset); };
105 auto indexOfRelative = [&](NodeId node) { return static_cast<std::size_t>(node * stride + relativeOffset); };
106 const int areaStride = areaDependency != nullptr ? areaDependency->attrNames->NUM_ATTRIBUTES : 0;
107 const int areaOffset = areaDependency != nullptr ? areaDependency->attrNames->indexMap.find(Area)->second : 0;
108 auto indexOfArea = [&](NodeId node) { return static_cast<std::size_t>(node * areaStride + areaOffset); };
109
110 ::mmcfilters::detail::TreeKernelAccess::traversePostOrder(
111 context.tree, context.tree.root(),
112 [&](NodeId nodeId) {
113 const NodeId node = detail::volumeSlotOf(context.tree, nodeId);
114 const T nodeAltitude = context.altitude[static_cast<std::size_t>(nodeId)];
115 if (request.volume)
116 context.buffer[indexOfVolume(node)] =
117 static_cast<Real>(::mmcfilters::detail::TreeKernelAccess::properPartCardinality(context.tree, nodeId)) * static_cast<Real>(nodeAltitude);
118 if (request.relative)
119 context.buffer[indexOfRelative(node)] = Real{0};
120 },
121 [&](NodeId parentNodeId, NodeId childNodeId) {
122 const NodeId parent = detail::volumeSlotOf(context.tree, parentNodeId);
123 const NodeId child = detail::volumeSlotOf(context.tree, childNodeId);
124 if (request.volume)
125 context.buffer[indexOfVolume(parent)] += context.buffer[indexOfVolume(child)];
126 if (request.relative)
127 context.buffer[indexOfRelative(parent)] +=
128 context.buffer[indexOfRelative(child)] +
129 static_cast<Real>(areaDependency->buffer[indexOfArea(child)] *
130 std::abs(static_cast<Real>(context.altitude[static_cast<std::size_t>(childNodeId)]) -
131 static_cast<Real>(context.altitude[static_cast<std::size_t>(parentNodeId)])));
132 },
133 [&](NodeId nodeId) {
134 const NodeId node = detail::volumeSlotOf(context.tree, nodeId);
135 if (request.relative)
136 context.buffer[indexOfRelative(node)] += areaDependency->buffer[indexOfArea(node)];
137 });
138}
139
140} // namespace kernel
141
142template <std::floating_point Real>
143inline const DependencySourceT<Real>* findDependency(std::span<const DependencySourceT<Real>> sources, Attribute attribute) {
144 for (const DependencySourceT<Real>& source : sources) {
145 if (source.attrNames->contains(attribute)) {
146 return &source;
147 }
148 }
149 return nullptr;
150}
151
152template <std::floating_point Real, AltitudeValue T>
153inline void validateVolumeContext(const AltitudeAttributeComputeContext<Real, T>& context, const VolumeRequest& request) {
154 requireAttributeBufferShape(context.tree, context.buffer, context.attrNames);
155 TreeAltitudeAlgorithms::validateNodeAltitudeBufferShape(context.tree, context.altitude);
156 if (request.volume && !context.attrNames.contains(Volume)) {
157 throw std::invalid_argument("VOLUME computation requires a VOLUME column in the output layout.");
158 }
159 if (request.relative && !context.attrNames.contains(RelativeVolume)) {
160 throw std::invalid_argument("RELATIVE_VOLUME computation requires a RELATIVE_VOLUME column in the output layout.");
161 }
162}
163
164} // namespace detail
165
186 public:
188 static constexpr std::string_view familyName = "volume";
189
191 static constexpr AttributeComputerFamily family = AttributeComputerFamily::Volume;
192
194 static constexpr AttributeComputerDomain domain = AttributeComputerDomain::Altitude;
195
199 inline static constexpr std::array<Attribute, 2> producedAttributes{Volume, RelativeVolume};
200
212 template <std::floating_point Real, AltitudeValue T> static void compute(const AltitudeAttributeComputeContext<Real, T>& context) {
213 const detail::VolumeRequest request = detail::VolumeRequest::from(context.requestedAttributes);
214 MMCFILTERS_CONTRACT_CHECKED_ONLY(detail::validateVolumeContext(context, request));
216 if (request.needsAreaDependency()) {
217 if constexpr (contract::validationsEnabled) {
218 areaDependency = &context.dependencies.require(Area);
219 } else {
220 areaDependency = detail::findDependency(context.dependencySources, Area);
221 }
222 }
223 detail::kernel::computeVolume(context, request, areaDependency);
224 }
225
235 template <std::floating_point Real, AltitudeValue T> static void computeUnitRows(const AltitudeUnitAttributeComputeContext<Real, T>& context) {
236 requireUnitAttributeBufferShape(context.tree, context.unitPixels, context.buffer, context.attrNames);
237 TreeAltitudeAlgorithms::validateNodeAltitudeBufferShape(context.tree, context.altitude);
238 computeUnitRowsEstablished(context);
239 }
240
241 private:
248 template <class Computer, class Context> friend void mmcfilters::detail::kernel::computeUnitRows(const Context& context);
249
251 template <std::floating_point Real, AltitudeValue T> static void computeUnitRowsEstablished(const AltitudeUnitAttributeComputeContext<Real, T>& context) {
252
253 const detail::VolumeRequest request = detail::VolumeRequest::from(context.requestedAttributes);
254 if (!request.any()) {
255 return;
256 }
257
258 for (NodeId leafIndex = 0; leafIndex < static_cast<NodeId>(context.unitPixels.size()); ++leafIndex) {
259 const PixelId pixel = context.unitPixels[static_cast<size_t>(leafIndex)];
260 if (request.volume) {
261 context.buffer[context.attrNames.linearIndex(leafIndex, Volume)] =
262 static_cast<Real>(::mmcfilters::detail::kernel::unitAltitude(context.tree, context.altitude, pixel));
263 }
264 if (request.relative) {
265 context.buffer[context.attrNames.linearIndex(leafIndex, RelativeVolume)] = Real{1};
266 }
267 }
268 }
269};
270
271} // namespace mmcfilters::attributes::computers
int NodeId
Node identifier type used throughout the project.
Definition Common.hpp:17
#define MMCFILTERS_CONTRACT_CHECKED_ONLY(...)
Executes validation statements only when defensive checks are enabled.
Definition Contract.hpp:67
static void validateNodeAltitudeBufferShape(const MorphologicalTree &tree, std::span< const T > altitude)
Validates that an altitude buffer covers the dense internal-node domain.
Computes cumulative grey-level volume descriptors on the hierarchy.
static void compute(const AltitudeAttributeComputeContext< Real, T > &context)
Computes the requested volume descriptors.
static void computeUnitRows(const AltitudeUnitAttributeComputeContext< Real, T > &context)
Materializes volume descriptors for one-pixel unit supports.
Owning result for one computed scalar attribute layout and buffer.
std::vector< Real > second
Flat per-node attribute buffer indexed through first.