nx_anatomy_measure.nx source
↩ module page · 171 lines · 6330 B
1// nx_anatomy_measure.nx -- measure named anatomical axes from binary mask.
2//
3// Composes existing primitives (no duplication):
4// - nx_color_skin_mask (already in nx_color_v2)
5// - nx_region_segment (already in nx_region)
6// - nx_eye_detect (Layer 7, KEYSTONE)
7//
8// Cross-section measurement is the central technique here:
9// - bust = widest mask-width across upper torso Y-band
10// - waist = narrowest mask-width across mid torso Y-band
11// - hip = widest mask-width across lower torso Y-band
12// - navel = darkest pixel along torso vertical midline
13//
14// Output: flat-array (axis_id, value_px) bundle ready for
15// nx_beauty_score(). All primitives are flat-array-API to avoid the
16// nxc2 codegen bug with struct-pointer returns.
17
18// nx_safety_envelope:
19// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
20// sil_target: SIL1
21// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
22// verdict: NOT_YET_EVALUATED
23
24import "nx_syscalls.nx"
25import "nx_image.nx"
26import "nx_canon_proportions.nx"
27import "nx_beauty_score.nx"
28const K_MAGIC_9999: i64 = 9999
29
30// Count consecutive skin pixels horizontally at row y within [x0, x1).
31// Returns the LONGEST run found.
32func nx_mask_longest_run_at_y(mask: *Image, y: i64, x0: i64, x1: i64) -> i64 {
33 if y < 0 { return 0 }
34 if y >= mask.height { return 0 }
35 var best: i64 = 0
36 var cur: i64 = 0
37 var x: i64 = x0
38 while x < x1 {
39 let v: i64 = nx_image_get(mask, x, y, 0)
40 if v > 0 {
41 cur = cur + 1
42 if cur > best { best = cur }
43 } else {
44 cur = 0
45 }
46 x = x + 1
47 }
48 return best
49}
50
51// Find Y in [y0, y1) where mask is WIDEST. Returns (y, width) packed.
52// Out: out_y receives the Y, returns the width.
53func nx_mask_widest_y(mask: *Image, y0: i64, y1: i64,
54 x0: i64, x1: i64, out_y: *i64) -> i64 {
55 var best_w: i64 = 0
56 var best_y: i64 = y0
57 var y: i64 = y0
58 while y < y1 {
59 let w: i64 = nx_mask_longest_run_at_y(mask, y, x0, x1)
60 if w > best_w { best_w = w; best_y = y }
61 y = y + 1
62 }
63 out_y[0] = best_y
64 return best_w
65}
66
67// Find Y in [y0, y1) where mask is NARROWEST (but nonzero). Returns
68// (out_y, width).
69func nx_mask_narrowest_y(mask: *Image, y0: i64, y1: i64,
70 x0: i64, x1: i64, out_y: *i64) -> i64 {
71 var best_w: i64 = K_MAGIC_9999
72 var best_y: i64 = y0
73 var found: i64 = 0
74 var y: i64 = y0
75 while y < y1 {
76 let w: i64 = nx_mask_longest_run_at_y(mask, y, x0, x1)
77 if w > 0 {
78 if w < best_w { best_w = w; best_y = y; found = 1 }
79 }
80 y = y + 1
81 }
82 out_y[0] = best_y
83 if found == 0 { return 0 }
84 return best_w
85}
86
87// Measure standard body axes from a skin mask + torso bbox + eye width.
88// Caller pre-allocates measurement bundle (NX_MEAS_FIELDS * count i64).
89// Returns number of (axis, value) pairs written.
90//
91// Inputs:
92// mask: full-frame skin mask
93// eye_width_px: from nx_eye_detect
94// face_min_x..face_max_y: face bbox from nx_region
95// torso_min_x..torso_max_y: torso bbox from nx_region
96// meas: output flat-array, layout per nx_beauty_score's NX_MEAS_FIELDS
97func nx_anatomy_measure_body(mask: *Image, eye_width_px: i64,
98 face_min_x: i64, face_min_y: i64,
99 face_max_x: i64, face_max_y: i64,
100 torso_min_x: i64, torso_min_y: i64,
101 torso_max_x: i64, torso_max_y: i64,
102 meas: *i64) -> i64 {
103 var n: i64 = 0
104 let face_w: i64 = face_max_x - face_min_x + 1
105 let face_h: i64 = face_max_y - face_min_y + 1
106 let torso_h: i64 = torso_max_y - torso_min_y + 1
107
108 // Keystone.
109 meas[n * NX_MEAS_FIELDS] = NX_AXIS_EYE_WIDTH
110 meas[n * NX_MEAS_FIELDS + 1] = eye_width_px
111 n = n + 1
112
113 // Face dimensions (bbox-derived).
114 meas[n * NX_MEAS_FIELDS] = NX_AXIS_FACE_WIDTH
115 meas[n * NX_MEAS_FIELDS + 1] = face_w
116 n = n + 1
117 meas[n * NX_MEAS_FIELDS] = NX_AXIS_FACE_HEIGHT
118 meas[n * NX_MEAS_FIELDS + 1] = face_h
119 n = n + 1
120
121 // Bust = widest mask row in UPPER third of torso.
122 let bust_y0: i64 = torso_min_y
123 let bust_y1: i64 = torso_min_y + torso_h / 3
124 let bust_y_out: *i64 = (sys_mmap(8 + 8)) as *i64
125 let bust_w: i64 = nx_mask_widest_y(mask, bust_y0, bust_y1,
126 torso_min_x, torso_max_x + 1, bust_y_out)
127 if bust_w > 0 {
128 meas[n * NX_MEAS_FIELDS] = NX_AXIS_BUST_WIDTH
129 meas[n * NX_MEAS_FIELDS + 1] = bust_w
130 n = n + 1
131 }
132
133 // Waist = narrowest mask row in MIDDLE third of torso.
134 let waist_y0: i64 = torso_min_y + torso_h / 3
135 let waist_y1: i64 = torso_min_y + (torso_h * 2) / 3
136 let waist_y_out: *i64 = (sys_mmap(8 + 8)) as *i64
137 let waist_w: i64 = nx_mask_narrowest_y(mask, waist_y0, waist_y1,
138 torso_min_x, torso_max_x + 1, waist_y_out)
139 if waist_w > 0 {
140 meas[n * NX_MEAS_FIELDS] = NX_AXIS_WAIST_WIDTH
141 meas[n * NX_MEAS_FIELDS + 1] = waist_w
142 n = n + 1
143 }
144
145 // Hip = widest mask row in LOWER third of torso.
146 let hip_y0: i64 = torso_min_y + (torso_h * 2) / 3
147 let hip_y1: i64 = torso_max_y + 1
148 let hip_y_out: *i64 = (sys_mmap(8 + 8)) as *i64
149 let hip_w: i64 = nx_mask_widest_y(mask, hip_y0, hip_y1,
150 torso_min_x, torso_max_x + 1, hip_y_out)
151 if hip_w > 0 {
152 meas[n * NX_MEAS_FIELDS] = NX_AXIS_HIP_WIDTH
153 meas[n * NX_MEAS_FIELDS + 1] = hip_w
154 n = n + 1
155 }
156
157 // Shoulder width: widest row in TOP 15% of torso, where pectoral fold
158 // is largest. For a face-on subject this approximates shoulder span.
159 let shoulder_y0: i64 = torso_min_y
160 let shoulder_y1: i64 = torso_min_y + torso_h / 6
161 let shoulder_y_out: *i64 = (sys_mmap(8 + 8)) as *i64
162 let shoulder_w: i64 = nx_mask_widest_y(mask, shoulder_y0, shoulder_y1,
163 torso_min_x, torso_max_x + 1, shoulder_y_out)
164 if shoulder_w > 0 {
165 meas[n * NX_MEAS_FIELDS] = NX_AXIS_SHOULDER_WIDTH
166 meas[n * NX_MEAS_FIELDS + 1] = shoulder_w
167 n = n + 1
168 }
169
170 return n
171}