3#include "AttributeTypes.hpp"
9#include <unordered_map>
12namespace mmcfilters::attributes::registry {
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},
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},
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},
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},
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},
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},
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},
112 {MAX_DIST,
"MAX_DIST",
"Maximum distance transform value of the node, computed using incremental contour extraction and Differential Image Foresting Transform.",
true,
false},
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},
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}
127inline const AttributeMetadata* metadata(Attribute attribute)
noexcept {
128 const auto index =
static_cast<std::size_t
>(attribute);
129 if (index >= ATTRIBUTE_METADATA.size()) {
132 const AttributeMetadata& item = ATTRIBUTE_METADATA[index];
133 return item.attribute == attribute ? &item :
nullptr;
142inline std::string_view name(Attribute attribute)
noexcept {
143 const AttributeMetadata* item = metadata(attribute);
144 return item !=
nullptr ? item->name : std::string_view(
"UNKNOWN");
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.");
158inline bool requiresAltitude(Attribute attribute)
noexcept {
159 const AttributeMetadata* item = metadata(attribute);
160 return item !=
nullptr && item->requiresAltitude;
166inline bool isTopologyOnly(Attribute attribute)
noexcept {
167 const AttributeMetadata* item = metadata(attribute);
168 return item !=
nullptr && item->topologyOnly;
177inline bool isAttributePipelineAltitudeAttribute(Attribute attribute)
noexcept {
178 return attribute == AREA || requiresAltitude(attribute);
184inline bool isPipelineComputed(Attribute attribute)
noexcept {
185 return attribute == AREA || requiresAltitude(attribute) || isTopologyOnly(attribute);
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;
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);
218 return std::unordered_map<AttributeGroup, std::vector<Attribute>>{
219 {AttributeGroup::GRAY_LEVEL, {
227 {AttributeGroup::SHAPE, {
260 BITQUADS_NUMBER_EULER,
261 BITQUADS_NUMBER_HOLES,
263 BITQUADS_PERIMETER_CONTINUOUS,
264 BITQUADS_CIRCULARITY,
265 BITQUADS_PERIMETER_AVERAGE,
266 BITQUADS_LENGTH_AVERAGE,
267 BITQUADS_WIDTH_AVERAGE,
276 {AttributeGroup::MOMENTS, {
299 {AttributeGroup::BOUNDARY, {
301 BITQUADS_NUMBER_EULER,
302 BITQUADS_NUMBER_HOLES,
304 BITQUADS_PERIMETER_CONTINUOUS,
305 BITQUADS_CIRCULARITY,
306 BITQUADS_PERIMETER_AVERAGE,
307 BITQUADS_LENGTH_AVERAGE,
308 BITQUADS_WIDTH_AVERAGE,
316 {AttributeGroup::TREE_TOPOLOGY, {
323 NUM_DESCENDANTS_NODE,
324 NUM_LEAF_DESCENDANTS_NODE,
327 AVG_CHILD_HEIGHT_NODE
329 {AttributeGroup::ALL, std::move(all)}