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"
18namespace mmcfilters::attributes::computers {
27inline NodeId volumeSlotOf(
const MorphologicalTree&, NodeId nodeId)
noexcept {
return nodeId; }
34 bool relative =
false;
41 [[nodiscard]]
bool any() const noexcept {
return volume || relative; }
48 [[nodiscard]]
bool needsAreaDependency() const noexcept {
return relative; }
56 [[nodiscard]]
static VolumeRequest from(std::span<const Attribute> requestedAttributes) {
57 return {.volume = containsVolumeAttribute(requestedAttributes, Volume), .relative = containsVolumeAttribute(requestedAttributes, RelativeVolume)};
68 [[nodiscard]]
static bool containsVolumeAttribute(std::span<const Attribute> requestedAttributes, Attribute attribute) {
69 return std::find(requestedAttributes.begin(), requestedAttributes.end(), attribute) != requestedAttributes.end();
93template <std::
floating_po
int Real, AltitudeValue T>
94void computeVolume(
const AltitudeAttributeComputeContext<Real, T>& context,
const VolumeRequest& request,
95 const DependencySourceT<Real>* areaDependency) {
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); };
110 ::mmcfilters::detail::TreeKernelAccess::traversePostOrder(
111 context.tree, context.tree.root(),
113 const NodeId node = detail::volumeSlotOf(context.tree, nodeId);
114 const T nodeAltitude = context.altitude[static_cast<std::size_t>(nodeId)];
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};
122 const NodeId parent = detail::volumeSlotOf(context.tree, parentNodeId);
123 const NodeId child = detail::volumeSlotOf(context.tree, childNodeId);
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)])));
134 const NodeId node = detail::volumeSlotOf(context.tree, nodeId);
135 if (request.relative)
136 context.buffer[indexOfRelative(node)] += areaDependency->buffer[indexOfArea(node)];
142template <std::
floating_po
int 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)) {
152template <std::
floating_po
int Real, AltitudeValue T>
153inline void validateVolumeContext(
const AltitudeAttributeComputeContext<Real, T>& context,
const VolumeRequest& request) {
154 requireAttributeBufferShape(context.tree, context.buffer, context.attrNames);
156 if (request.volume && !context.attrNames.contains(Volume)) {
157 throw std::invalid_argument(
"VOLUME computation requires a VOLUME column in the output layout.");
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.");
188 static constexpr std::string_view familyName =
"volume";
191 static constexpr AttributeComputerFamily family = AttributeComputerFamily::Volume;
194 static constexpr AttributeComputerDomain domain = AttributeComputerDomain::Altitude;
199 inline static constexpr std::array<Attribute, 2> producedAttributes{Volume, RelativeVolume};
213 const detail::VolumeRequest
request = detail::VolumeRequest::from(
context.requestedAttributes);
216 if (
request.needsAreaDependency()) {
217 if constexpr (contract::validationsEnabled) {
237 TreeAltitudeAlgorithms::validateNodeAltitudeBufferShape(
context.tree,
context.altitude);
238 computeUnitRowsEstablished(
context);
248 template <
class Computer,
class Context>
friend void mmcfilters::detail::kernel::computeUnitRows(
const Context&
context);
253 const detail::VolumeRequest
request = detail::VolumeRequest::from(
context.requestedAttributes);
262 static_cast<Real
>(::mmcfilters::detail::kernel::unitAltitude(
context.tree,
context.altitude, pixel));
int NodeId
Node identifier type used throughout the project.
#define MMCFILTERS_CONTRACT_CHECKED_ONLY(...)
Executes validation statements only when defensive checks are enabled.
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.