3#include "AttributeComputerDomain.hpp"
4#include "AttributeComputerFamily.hpp"
5#include "../detail/AttributeKernelSupport.hpp"
6#include "../../trees/detail/TreeTraversalDetail.hpp"
7#include "../../trees/detail/CommittedTreeAccess.hpp"
8#include "../../trees/MorphologicalTree.hpp"
9#include "../../utils/Contract.hpp"
10#include "../../utils/Image.hpp"
20namespace mmcfilters::attributes::computers {
29inline NodeId boundingBoxSlotOf(
const MorphologicalTree&, NodeId nodeId)
noexcept {
return nodeId; }
32struct BoundingBoxRequest {
38 bool rectangularity =
false;
42 bool columnMin =
false;
44 bool columnMax =
false;
50 bool diagonalLength =
false;
57 [[nodiscard]]
bool any() const noexcept {
return width || height || rectangularity || ratioWH || columnMin || columnMax || rowMin || rowMax || diagonalLength; }
64 [[nodiscard]]
bool needsAreaDependency() const noexcept {
return rectangularity; }
72 [[nodiscard]]
static BoundingBoxRequest from(std::span<const Attribute> requestedAttributes) {
73 return {.width = contains(requestedAttributes, BoxWidth),
74 .height = contains(requestedAttributes, BoundingBoxHeight),
75 .rectangularity = contains(requestedAttributes, Rectangularity),
76 .ratioWH = contains(requestedAttributes, RatioWh),
77 .columnMin = contains(requestedAttributes, BoxColumnMin),
78 .columnMax = contains(requestedAttributes, BoxColumnMax),
79 .rowMin = contains(requestedAttributes, BoxRowMin),
80 .rowMax = contains(requestedAttributes, BoxRowMax),
81 .diagonalLength = contains(requestedAttributes, DiagonalLength)};
92 [[nodiscard]]
static bool contains(std::span<const Attribute> requestedAttributes, Attribute attribute) {
93 return std::find(requestedAttributes.begin(), requestedAttributes.end(), attribute) != requestedAttributes.end();
105template <std::
floating_po
int Real>
106inline void computeBoundingBox(
const AttributeComputeContext<Real>& context,
const BoundingBoxRequest& request,
107 const DependencySourceT<Real>* areaDependency) {
108 if (!request.any()) {
112 const int stride = context.attrNames.NUM_ATTRIBUTES;
113 const auto offsetOf = [&](Attribute attribute) {
return context.attrNames.indexMap.find(attribute)->
second; };
114 const int widthOffset = request.width ? offsetOf(BoxWidth) : 0;
115 const int heightOffset = request.height ? offsetOf(BoundingBoxHeight) : 0;
116 const int rectangularityOffset = request.rectangularity ? offsetOf(Rectangularity) : 0;
117 const int ratioOffset = request.ratioWH ? offsetOf(RatioWh) : 0;
118 const int columnMinOffset = request.columnMin ? offsetOf(BoxColumnMin) : 0;
119 const int columnMaxOffset = request.columnMax ? offsetOf(BoxColumnMax) : 0;
120 const int rowMinOffset = request.rowMin ? offsetOf(BoxRowMin) : 0;
121 const int rowMaxOffset = request.rowMax ? offsetOf(BoxRowMax) : 0;
122 const int diagonalOffset = request.diagonalLength ? offsetOf(DiagonalLength) : 0;
123 auto outputIndex = [&](
NodeId node,
int offset) {
return static_cast<std::size_t
>(node * stride + offset); };
125 const int areaStride = areaDependency !=
nullptr ? areaDependency->attrNames->NUM_ATTRIBUTES : 0;
126 const int areaOffset = areaDependency !=
nullptr ? areaDependency->attrNames->indexMap.find(Area)->second : 0;
127 auto areaIndex = [&](
NodeId node) {
return static_cast<std::size_t
>(node * areaStride + areaOffset); };
129 const int numNodes = context.tree.numInternalNodeSlots();
130 const GridDomain2D& domain = ::mmcfilters::detail::CommittedTreeAccess::gridDomain2D(context.tree);
131 std::vector<int> columnMin(
static_cast<std::size_t
>(numNodes), domain.columns);
132 std::vector<int> columnMax(
static_cast<std::size_t
>(numNodes), 0);
133 std::vector<int> rowMin(
static_cast<std::size_t
>(numNodes), domain.rows);
134 std::vector<int> rowMax(
static_cast<std::size_t
>(numNodes), 0);
136 ::mmcfilters::detail::kernel::traversePostOrder(
137 context.tree, context.tree.root(),
139 for (PixelId pixel : ::mmcfilters::detail::CommittedTreeAccess::properParts(context.tree, node)) {
140 const int row = pixel / domain.columns;
141 const int column = pixel % domain.columns;
142 columnMin[static_cast<std::size_t>(node)] = std::min(columnMin[static_cast<std::size_t>(node)], column);
143 columnMax[static_cast<std::size_t>(node)] = std::max(columnMax[static_cast<std::size_t>(node)], column);
144 rowMin[static_cast<std::size_t>(node)] = std::min(rowMin[static_cast<std::size_t>(node)], row);
145 rowMax[static_cast<std::size_t>(node)] = std::max(rowMax[static_cast<std::size_t>(node)], row);
149 columnMin[
static_cast<std::size_t
>(parent)] =
150 std::min(columnMin[
static_cast<std::size_t
>(parent)], columnMin[
static_cast<std::size_t
>(child)]);
151 columnMax[
static_cast<std::size_t
>(parent)] =
152 std::max(columnMax[
static_cast<std::size_t
>(parent)], columnMax[
static_cast<std::size_t
>(child)]);
153 rowMin[
static_cast<std::size_t
>(parent)] = std::min(rowMin[
static_cast<std::size_t
>(parent)], rowMin[
static_cast<std::size_t
>(child)]);
154 rowMax[
static_cast<std::size_t
>(parent)] = std::max(rowMax[
static_cast<std::size_t
>(parent)], rowMax[
static_cast<std::size_t
>(child)]);
157 const Real width =
static_cast<Real
>(columnMax[
static_cast<std::size_t
>(node)] - columnMin[
static_cast<std::size_t
>(node)] + 1);
158 const Real height =
static_cast<Real
>(rowMax[
static_cast<std::size_t
>(node)] - rowMin[
static_cast<std::size_t
>(node)] + 1);
160 context.buffer[outputIndex(node, widthOffset)] = width;
162 context.buffer[outputIndex(node, heightOffset)] = height;
163 if (request.rectangularity)
164 context.buffer[outputIndex(node, rectangularityOffset)] =
165 ::mmcfilters::attributes::numeric::safeDivide(areaDependency->buffer[areaIndex(node)], width * height);
167 context.buffer[outputIndex(node, ratioOffset)] =
168 ::mmcfilters::attributes::numeric::safeDivide(std::max(width, height), std::min(width, height));
169 if (request.columnMin)
170 context.buffer[outputIndex(node, columnMinOffset)] =
static_cast<Real
>(columnMin[
static_cast<std::size_t
>(node)]);
171 if (request.columnMax)
172 context.buffer[outputIndex(node, columnMaxOffset)] =
static_cast<Real
>(columnMax[
static_cast<std::size_t
>(node)]);
174 context.buffer[outputIndex(node, rowMinOffset)] =
static_cast<Real
>(rowMin[
static_cast<std::size_t
>(node)]);
176 context.buffer[outputIndex(node, rowMaxOffset)] =
static_cast<Real
>(rowMax[
static_cast<std::size_t
>(node)]);
177 if (request.diagonalLength)
178 context.buffer[outputIndex(node, diagonalOffset)] = ::mmcfilters::attributes::numeric::safeSqrt(width * width + height * height);
184template <std::
floating_po
int Real>
185inline void validateBoundingBoxContext(
const AttributeComputeContext<Real>& context) {
186 requireAttributeBufferShape(context.tree, context.buffer, context.attrNames);
187 requireRequestedAttributeColumns(context);
188 static_cast<void>(context.tree.requireGridDomain2D(
"BoundingBoxComputer"));
218 static constexpr std::string_view familyName =
"bounding-box";
221 static constexpr AttributeComputerFamily family = AttributeComputerFamily::BoundingBox;
224 static constexpr AttributeComputerDomain domain = AttributeComputerDomain::Topology;
229 inline static constexpr std::array<Attribute, 9> producedAttributes{BoxWidth, BoundingBoxHeight, DiagonalLength, Rectangularity, RatioWh,
230 BoxColumnMin, BoxColumnMax, BoxRowMin, BoxRowMax};
245 const detail::BoundingBoxRequest
request = detail::BoundingBoxRequest::from(
context.requestedAttributes);
249 if (
request.needsAreaDependency()) {
250 if constexpr (contract::validationsEnabled) {
270 const detail::BoundingBoxRequest
request = detail::BoundingBoxRequest::from(
context.requestedAttributes);
275 const int numColumns =
context.tree.numColumns();
278 const auto [row, column] = ImageUtils::to2D(pixel, numColumns);
int NodeId
Node identifier type used throughout the project.
#define MMCFILTERS_CONTRACT_CHECKED_ONLY(...)
Executes validation statements only when defensive checks are enabled.
Computes descriptors derived from the axis-aligned bounding box of the node support.
static void computeUnitRows(const UnitAttributeComputeContext< Real > &context)
Materializes bounding-box descriptors for one-pixel unit supports.
static void compute(const AttributeComputeContext< Real > &context)
Computes the requested bounding-box descriptors.
Owning result for one computed scalar attribute layout and buffer.
std::vector< Real > second
Flat per-node attribute buffer indexed through first.