code wiki / (root) / nx_anatomy_measure.nx

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}