MorphologicalAttributeFilters
Public API documentation
Loading...
Searching...
No Matches
AttributeRegistry.hpp
1#pragma once
2
3#include "AttributeTypes.hpp"
4
5#include <array>
6#include <cstddef>
7#include <optional>
8#include <string_view>
9#include <unordered_map>
10#include <vector>
11
12namespace mmcfilters::attributes::registry {
13
24 Attribute attribute;
25
27 std::string_view name;
28
30 std::string_view description;
31
33 bool requiresAltitude = false;
34
36 bool topologyOnly = false;
37};
38
48inline constexpr std::array<AttributeMetadata, static_cast<std::size_t>(Attribute::CONTOUR_SIDE_SOUTH) + 1> ATTRIBUTE_METADATA{{
49 {AREA, "AREA", "Area: Number of pixels in the connected component.", false, false},
50 {VOLUME, "VOLUME", "Volume: Sum of the gray-level intensities of all pixels in the connected component. Interpreted as the total mass under the component, or the integral of the image function over its support.", true, false},
51 {RELATIVE_VOLUME, "RELATIVE_VOLUME", "Relative volume: Sum of differences between the node level and the gray-levels of pixels in the component. Measures the amount of intensity required to fill the component to its node level.", true, false},
52 {LEVEL, "LEVEL", "Level: Gray-level at which the connected component appears in the threshold decomposition hierarchy; corresponds to the altitude of the node in the component tree.", true, false},
53 {GRAY_HEIGHT, "GRAY_HEIGHT", "GRAY_HEIGHT: For a node in the max-tree, the absolute difference between its level and the maximum level among its descendants; analogously, in the min-tree, the difference to the minimum level among its descendants. Leaves have gray height 0.", true, false},
54 {MEAN_LEVEL, "MEAN_LEVEL", "Mean level: Average gray-level intensity of the pixels in the connected component.", true, false},
55 {VARIANCE_LEVEL, "VARIANCE_LEVEL", "Variance of level: Variance of the gray-level values of the pixels in the connected component.", true, false},
56
57 {BOX_WIDTH, "BOX_WIDTH", "Bounding box width: Width of the minimum rectangle enclosing the connected component.", false, true},
58 {BOX_HEIGHT, "BOX_HEIGHT", "Bounding box height: Height of the minimum rectangle enclosing the connected component.", false, true},
59 {DIAGONAL_LENGTH, "DIAGONAL_LENGTH", "Diagonal length: Euclidean length of the diagonal of the bounding box, computed as sqrt(width^2 + height^2).", false, true},
60 {RECTANGULARITY, "RECTANGULARITY", "Rectangularity: Ratio between the connected component area and the area of its bounding box. Values closer to 1 indicate shapes that efficiently fill their bounding box.", false, true},
61 {RATIO_WH, "RATIO_WH", "Aspect ratio: Ratio of the bounding box width to its height. Describes the elongation of the component.", false, true},
62 {BOX_COL_MIN, "BOX_COL_MIN", "Bounding box column min: Minimum column index covered by the connected component.", false, true},
63 {BOX_COL_MAX, "BOX_COL_MAX", "Bounding box column max: Maximum column index covered by the connected component.", false, true},
64 {BOX_ROW_MIN, "BOX_ROW_MIN", "Bounding box row min: Minimum row index covered by the connected component.", false, true},
65 {BOX_ROW_MAX, "BOX_ROW_MAX", "Bounding box row max: Maximum row index covered by the connected component.", false, true},
66
67 {CENTRAL_MOMENT_20, "CENTRAL_MOMENT_20", "Central moment (2,0): Second-order moment about the centroid along the x-axis. Measures the horizontal spread of the component.", false, true},
68 {CENTRAL_MOMENT_02, "CENTRAL_MOMENT_02", "Central moment (0,2): Second-order moment about the centroid along the y-axis. Measures the vertical spread of the component.", false, true},
69 {CENTRAL_MOMENT_11, "CENTRAL_MOMENT_11", "Central moment (1,1): Mixed second-order moment about the centroid. Represents the covariance between x and y coordinates.", false, true},
70 {CENTRAL_MOMENT_30, "CENTRAL_MOMENT_30", "Central moment (3,0): Third-order moment about the centroid along the x-axis. Describes horizontal asymmetry of the component.", false, true},
71 {CENTRAL_MOMENT_03, "CENTRAL_MOMENT_03", "Central moment (0,3): Third-order moment about the centroid along the y-axis. Describes vertical asymmetry of the component.", false, true},
72 {CENTRAL_MOMENT_21, "CENTRAL_MOMENT_21", "Central moment (2,1): Mixed third-order moment about the centroid. Captures joint spread and asymmetry in x and y.", false, true},
73 {CENTRAL_MOMENT_12, "CENTRAL_MOMENT_12", "Central moment (1,2): Mixed third-order moment about the centroid. Captures joint spread and asymmetry in y and x.", false, true},
74
75 {HU_MOMENT_1, "HU_MOMENT_1", "Hu moment 1: Invariant to translation, scale, and rotation. Represents overall spatial variance (shape dispersion).", false, true},
76 {HU_MOMENT_2, "HU_MOMENT_2", "Hu moment 2: Invariant capturing the difference between horizontal and vertical spread.", false, true},
77 {HU_MOMENT_3, "HU_MOMENT_3", "Hu moment 3: Sensitive to skewness and asymmetry in the pixel distribution.", false, true},
78 {HU_MOMENT_4, "HU_MOMENT_4", "Hu moment 4: Measures symmetry with respect to diagonal axes.", false, true},
79 {HU_MOMENT_5, "HU_MOMENT_5", "Hu moment 5: Descriptor sensitive to orientation and reflection; captures complex asymmetries.", false, true},
80 {HU_MOMENT_6, "HU_MOMENT_6", "Hu moment 6: Invariant capturing elliptic asymmetries, sensitive to specific shape curvature.", false, true},
81 {HU_MOMENT_7, "HU_MOMENT_7", "Hu moment 7: Highly sensitive to irregularities and fine variations; helps discriminate mirror-symmetric shapes.", false, true},
82
83 {INERTIA, "INERTIA", "Inertia: Sum of normalized second-order central moments (mu20 + mu02). Measures the dispersion of mass around the centroid. Higher values indicate objects with thin and elongated structures.", false, true},
84 {COMPACTNESS, "COMPACTNESS", "Compactness: Area normalized by the shape's dispersion (mu20 + mu02). Higher values indicate more compact and isotropic shapes.", false, true},
85 {ECCENTRICITY, "ECCENTRICITY", "Eccentricity: Ratio of principal inertia eigenvalues (λ_1/λ_2). Measures elongation; values near 1 indicate circularity, higher values indicate elongation. Degenerate line-like supports saturate at a finite maximum.", false, true},
86 {LENGTH_MAJOR_AXIS, "LENGTH_MAJOR_AXIS", "Major axis length: Length of the longest diameter of the shape.", false, true},
87 {LENGTH_MINOR_AXIS, "LENGTH_MINOR_AXIS", "Minor axis length: Length of the shortest diameter of the shape.", false, true},
88 {AXIS_ORIENTATION, "AXIS_ORIENTATION", "Axis orientation: Angle of the principal inertia axis, computed from central moments. Indicates the dominant orientation of the shape.", false, true},
89 {CIRCULARITY, "CIRCULARITY", "Circularity: Ratio of the minor to major eigenvalues of the inertia matrix (λ_2/λ_1), i.e., Inverse of eccentricity. Indicates how circular a shape is; values near 1 suggest circularity, values near 0 indicate elongation.", false, true},
90
91 {BITQUADS_AREA, "BITQUADS_AREA", "BitQuads area (Duda): Refined sub-pixel area estimation using fractional weights based on the geometric contribution of local 2x2 pixel patterns.", false, true},
92 {BITQUADS_NUMBER_EULER, "BITQUADS_NUMBER_EULER", "BitQuads Euler number: Topological invariant computed as the number of connected components minus the number of holes, using 2x2 pattern statistics under 4- or 8-connectivity.", false, true},
93 {BITQUADS_NUMBER_HOLES, "BITQUADS_NUMBER_HOLES", "BitQuads number of holes: Number of interior holes in the component, derived from the Euler characteristic assuming a single connected object.", false, true},
94 {BITQUADS_PERIMETER, "BITQUADS_PERIMETER", "BitQuads perimeter: Discrete approximation of the shape's boundary length, calculated by summing edge-contributing patterns in the 2x2 pixel grid.", false, true},
95 {BITQUADS_PERIMETER_CONTINUOUS, "BITQUADS_PERIMETER_CONTINUOUS", "BitQuads continuous perimeter: Smoothed estimation of the boundary length, incorporating weighted transitions across pixel edges and diagonals.", false, true},
96 {BITQUADS_CIRCULARITY, "BITQUADS_CIRCULARITY", "BitQuads circularity: Compactness measure defined as (4π x areaDuda) / perimeter². Values close to 1 indicate circular shapes; lower values suggest elongation or irregularity. Degenerate zero-perimeter supports return 0.", false, true},
97 {BITQUADS_PERIMETER_AVERAGE, "BITQUADS_PERIMETER_AVERAGE", "BitQuads average perimeter: Mean perimeter per connected component, accounting for complex structures and holes. Non-positive Euler component estimates return 0.", false, true},
98 {BITQUADS_LENGTH_AVERAGE, "BITQUADS_LENGTH_AVERAGE", "BitQuads average length: Estimated average longitudinal extent per component, derived from the average perimeter with a zero fallback for non-positive Euler component estimates.", false, true},
99 {BITQUADS_WIDTH_AVERAGE, "BITQUADS_WIDTH_AVERAGE", "BitQuads average width: Estimated transverse extent per component, computed as (2 x areaDuda) / continuous perimeter with a zero fallback for degenerate perimeter.", false, true},
100
101 {HEIGHT_NODE, "HEIGHT_NODE", "Height: Longest path from this node to any leaf in its subtree. Measures the depth of the subtree rooted at the node.", false, true},
102 {DEPTH_NODE, "DEPTH_NODE", "Depth: Number of steps from this node to the root of the tree. Indicates the level of embedding within the tree hierarchy.", false, true},
103 {IS_LEAF_NODE, "IS_LEAF_NODE", "Is leaf: True if the node has no children, i.e., it represents a minimal component in the hierarchy.", false, true},
104 {IS_ROOT_NODE, "IS_ROOT_NODE", "Is root: True if the node is the root of the tree, representing the entire image support.", false, true},
105 {NUM_CHILDREN_NODE, "NUM_CHILDREN_NODE", "Number of children: Count of direct child nodes. Reflects the immediate branching factor of the node.", false, true},
106 {NUM_SIBLINGS_NODE, "NUM_SIBLINGS_NODE", "Number of siblings: Number of other nodes that share the same parent.", false, true},
107 {NUM_DESCENDANTS_NODE, "NUM_DESCENDANTS_NODE", "Number of descendants: Total number of nodes in the subtree rooted at this node (excluding the node itself).", false, true},
108 {NUM_LEAF_DESCENDANTS_NODE, "NUM_LEAF_DESCENDANTS_NODE", "Number of leaf descendants: Number of leaf nodes in the subtree. Reflects the number of minimal patterns under this structure.", false, true},
109 {LEAF_RATIO_NODE, "LEAF_RATIO_NODE", "Leaf ratio: Ratio of leaf descendants to total descendants. Measures structural 'flatness' or terminal density of the subtree.", false, true},
110 {BALANCE_NODE, "BALANCE_NODE", "Balance: Difference between the maximum and minimum heights among the subtrees of the children. Indicates branching symmetry.", false, true},
111
112 {MAX_DIST, "MAX_DIST", "Maximum distance transform value of the node, computed using incremental contour extraction and Differential Image Foresting Transform.", true, false},
113
114 {AVG_CHILD_HEIGHT_NODE, "AVG_CHILD_HEIGHT_NODE", "Average child height: Mean height of all direct child subtrees. Useful for measuring uniformity of the subtree structure.", false, true},
115
116 {CONTOUR_PIXELS, "CONTOUR_PIXELS", "Contour pixels: Number of support pixels touching the 4-neighbour complement.", false, true},
117 {CONTOUR_PERIMETER, "CONTOUR_PERIMETER", "Contour perimeter: 4-neighbour exposed-side perimeter of the support.", false, true},
118 {CONTOUR_SIDE_NORTH, "CONTOUR_SIDE_NORTH", "Contour north sides: Number of exposed north sides over support pixels.", false, true},
119 {CONTOUR_SIDE_WEST, "CONTOUR_SIDE_WEST", "Contour west sides: Number of exposed west sides over support pixels.", false, true},
120 {CONTOUR_SIDE_EAST, "CONTOUR_SIDE_EAST", "Contour east sides: Number of exposed east sides over support pixels.", false, true},
121 {CONTOUR_SIDE_SOUTH, "CONTOUR_SIDE_SOUTH", "Contour south sides: Number of exposed south sides over support pixels.", false, true}
122}};
123
127inline const AttributeMetadata* metadata(Attribute attribute) noexcept {
128 const auto index = static_cast<std::size_t>(attribute);
129 if (index >= ATTRIBUTE_METADATA.size()) {
130 return nullptr;
131 }
132 const AttributeMetadata& item = ATTRIBUTE_METADATA[index];
133 return item.attribute == attribute ? &item : nullptr;
134}
135
142inline std::string_view name(Attribute attribute) noexcept {
143 const AttributeMetadata* item = metadata(attribute);
144 return item != nullptr ? item->name : std::string_view("UNKNOWN");
145}
146
150inline std::string_view description(Attribute attribute) noexcept {
151 const AttributeMetadata* item = metadata(attribute);
152 return item != nullptr ? item->description : std::string_view("Unknown attribute.");
153}
154
158inline bool requiresAltitude(Attribute attribute) noexcept {
159 const AttributeMetadata* item = metadata(attribute);
160 return item != nullptr && item->requiresAltitude;
161}
162
166inline bool isTopologyOnly(Attribute attribute) noexcept {
167 const AttributeMetadata* item = metadata(attribute);
168 return item != nullptr && item->topologyOnly;
169}
170
177inline bool isAttributePipelineAltitudeAttribute(Attribute attribute) noexcept {
178 return attribute == AREA || requiresAltitude(attribute);
179}
180
184inline bool isPipelineComputed(Attribute attribute) noexcept {
185 return attribute == AREA || requiresAltitude(attribute) || isTopologyOnly(attribute);
186}
187
194inline std::optional<Attribute> parse(std::string_view nameToFind) noexcept {
195 for (const AttributeMetadata& item : ATTRIBUTE_METADATA) {
196 if (item.name == nameToFind) {
197 return item.attribute;
198 }
199 }
200 return std::nullopt;
201}
202
210inline const std::unordered_map<AttributeGroup, std::vector<Attribute>>& attributeGroups() {
211 static const std::unordered_map<AttributeGroup, std::vector<Attribute>> groups = [] {
212 std::vector<Attribute> all;
213 all.reserve(ATTRIBUTE_METADATA.size());
214 for (const AttributeMetadata& item : ATTRIBUTE_METADATA) {
215 all.push_back(item.attribute);
216 }
217
218 return std::unordered_map<AttributeGroup, std::vector<Attribute>>{
219 {AttributeGroup::GRAY_LEVEL, {
220 VOLUME,
221 RELATIVE_VOLUME,
222 LEVEL,
223 GRAY_HEIGHT,
224 MEAN_LEVEL,
225 VARIANCE_LEVEL
226 }},
227 {AttributeGroup::SHAPE, {
228 AREA,
229 BOX_WIDTH,
230 BOX_HEIGHT,
231 DIAGONAL_LENGTH,
232 RECTANGULARITY,
233 RATIO_WH,
234 BOX_COL_MIN,
235 BOX_COL_MAX,
236 BOX_ROW_MIN,
237 BOX_ROW_MAX,
238 CENTRAL_MOMENT_20,
239 CENTRAL_MOMENT_02,
240 CENTRAL_MOMENT_11,
241 CENTRAL_MOMENT_30,
242 CENTRAL_MOMENT_03,
243 CENTRAL_MOMENT_21,
244 CENTRAL_MOMENT_12,
245 HU_MOMENT_1,
246 HU_MOMENT_2,
247 HU_MOMENT_3,
248 HU_MOMENT_4,
249 HU_MOMENT_5,
250 HU_MOMENT_6,
251 HU_MOMENT_7,
252 INERTIA,
253 COMPACTNESS,
254 ECCENTRICITY,
255 LENGTH_MAJOR_AXIS,
256 LENGTH_MINOR_AXIS,
257 AXIS_ORIENTATION,
258 CIRCULARITY,
259 BITQUADS_AREA,
260 BITQUADS_NUMBER_EULER,
261 BITQUADS_NUMBER_HOLES,
262 BITQUADS_PERIMETER,
263 BITQUADS_PERIMETER_CONTINUOUS,
264 BITQUADS_CIRCULARITY,
265 BITQUADS_PERIMETER_AVERAGE,
266 BITQUADS_LENGTH_AVERAGE,
267 BITQUADS_WIDTH_AVERAGE,
268 MAX_DIST,
269 CONTOUR_PIXELS,
270 CONTOUR_PERIMETER,
271 CONTOUR_SIDE_NORTH,
272 CONTOUR_SIDE_WEST,
273 CONTOUR_SIDE_EAST,
274 CONTOUR_SIDE_SOUTH
275 }},
276 {AttributeGroup::MOMENTS, {
277 CENTRAL_MOMENT_20,
278 CENTRAL_MOMENT_02,
279 CENTRAL_MOMENT_11,
280 CENTRAL_MOMENT_30,
281 CENTRAL_MOMENT_03,
282 CENTRAL_MOMENT_21,
283 CENTRAL_MOMENT_12,
284 HU_MOMENT_1,
285 HU_MOMENT_2,
286 HU_MOMENT_3,
287 HU_MOMENT_4,
288 HU_MOMENT_5,
289 HU_MOMENT_6,
290 HU_MOMENT_7,
291 INERTIA,
292 COMPACTNESS,
293 ECCENTRICITY,
294 LENGTH_MAJOR_AXIS,
295 LENGTH_MINOR_AXIS,
296 AXIS_ORIENTATION,
297 CIRCULARITY
298 }},
299 {AttributeGroup::BOUNDARY, {
300 BITQUADS_AREA,
301 BITQUADS_NUMBER_EULER,
302 BITQUADS_NUMBER_HOLES,
303 BITQUADS_PERIMETER,
304 BITQUADS_PERIMETER_CONTINUOUS,
305 BITQUADS_CIRCULARITY,
306 BITQUADS_PERIMETER_AVERAGE,
307 BITQUADS_LENGTH_AVERAGE,
308 BITQUADS_WIDTH_AVERAGE,
309 CONTOUR_PIXELS,
310 CONTOUR_PERIMETER,
311 CONTOUR_SIDE_NORTH,
312 CONTOUR_SIDE_WEST,
313 CONTOUR_SIDE_EAST,
314 CONTOUR_SIDE_SOUTH
315 }},
316 {AttributeGroup::TREE_TOPOLOGY, {
317 HEIGHT_NODE,
318 DEPTH_NODE,
319 IS_LEAF_NODE,
320 IS_ROOT_NODE,
321 NUM_CHILDREN_NODE,
322 NUM_SIBLINGS_NODE,
323 NUM_DESCENDANTS_NODE,
324 NUM_LEAF_DESCENDANTS_NODE,
325 LEAF_RATIO_NODE,
326 BALANCE_NODE,
327 AVG_CHILD_HEIGHT_NODE
328 }},
329 {AttributeGroup::ALL, std::move(all)}
330 };
331 }();
332 return groups;
333}
334
335} // namespace mmcfilters::attributes::registry
Stable metadata attached to one scalar attribute descriptor.
std::string_view name
Stable symbolic name used in Python dictionaries, string parsing, and docs.
std::string_view description
User-facing description suitable for notebooks and Attribute.describe.
bool requiresAltitude
Whether the descriptor reads node altitude values during computation.
bool topologyOnly
Whether the descriptor can be computed from topology/support alone.
Attribute attribute
Scalar attribute described by this row.