code wiki / (root) / nx_thigh_proportion.nx

nx_thigh_proportion.nx source

↩ module page · 132 lines · 5054 B

1// nx_thigh_proportion.nx -- thigh thickness + thigh gap detector. 2// 3// Two axes from the same measurement scan: 4// thickness: mid-thigh width / hip width (canon 0.5-0.7) 5// gap: inner-thigh gap / hip width (0 = touching, > 0 = visible gap) 6// 7// Both inputs are pixel widths from row scans of the skin mask in the 8// upper-thigh / mid-thigh band. 9// 10// Output flat-array: 11// result[0] = mid_thigh_width_px (avg of L + R single thigh) 12// result[1] = thigh_gap_px (gap at top, 0 if legs touching) 13// result[2] = thickness_ratio_q10 14// result[3] = gap_ratio_q10 15// result[4] = thickness_verdict (0=TOO_THIN, 1=SLIM, 2=ATHLETIC, 3=CURVY, 4=THICK) 16// result[5] = gap_verdict (0=NO_GAP, 1=SMALL_GAP, 2=MODEST_GAP, 3=WIDE_GAP) 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" 26const NX_MAGIC_1024: i64 = 1024 27 28const NX_THIGH_RES_THICK_PX: i64 = 0 29const NX_THIGH_RES_GAP_PX: i64 = 1 30const NX_THIGH_RES_THICK_Q10: i64 = 2 31const NX_THIGH_RES_GAP_Q10: i64 = 3 32const NX_THIGH_RES_THICK_VRDT: i64 = 4 33const NX_THIGH_RES_GAP_VRDT: i64 = 5 34const NX_THIGH_RES_FIELDS: i64 = 6 35 36const NX_THIGH_V_THIN: i64 = 0 // < 0.45 hip — concerning 37const NX_THIGH_V_SLIM: i64 = 1 // 0.45-0.55 38const NX_THIGH_V_ATHLETIC: i64 = 2 // 0.55-0.65 39const NX_THIGH_V_CURVY: i64 = 3 // 0.65-0.75 40const NX_THIGH_V_THICK: i64 = 4 // > 0.75 41 42const NX_THIGH_GAP_NONE: i64 = 0 // touching 43const NX_THIGH_GAP_SMALL: i64 = 1 // 1-5% hip 44const NX_THIGH_GAP_MODEST: i64 = 2 // 5-12% hip 45const NX_THIGH_GAP_WIDE: i64 = 3 // > 12% hip 46 47// Measure mask widths in upper-thigh band. 48// bbox layout (8 entries): 49// [0..3] hip_band x0,y0,x1,y1 50// [4..7] thigh_band x0,y0,x1,y1 (covers BOTH thighs from upper to mid) 51func nx_thigh_proportion(skin_mask: *Image, bbox: *i64, 52 hip_width_px: i64, result: *i64) -> i64 { 53 var i: i64 = 0 54 while i < NX_THIGH_RES_FIELDS { result[i] = 0; i = i + 1 } 55 if hip_width_px <= 0 { return 1 } 56 57 let t_x0: i64 = bbox[4] 58 let t_y0: i64 = bbox[5] 59 let t_x1: i64 = bbox[6] 60 let t_y1: i64 = bbox[7] 61 let band_h: i64 = t_y1 - t_y0 62 if band_h <= 0 { return 2 } 63 64 // Sample mid-thigh row (middle of band). 65 let mid_y: i64 = t_y0 + band_h / 2 66 let top_y: i64 = t_y0 + band_h / 8 67 68 // Sum L thigh width + R thigh width at mid_y by scanning for runs. 69 // We also count GAP pixels at top_y between L and R thigh runs. 70 var l_w: i64 = 0 71 var r_w: i64 = 0 72 var midline_x: i64 = (t_x0 + t_x1) / 2 73 74 // Mid-row: count consecutive skin pixels left and right of midline. 75 var x: i64 = midline_x - 1 76 while x >= t_x0 { 77 if nx_image_get(skin_mask, x, mid_y, 0) > 0 { l_w = l_w + 1 } else { x = t_x0 - 1 } 78 x = x - 1 79 } 80 x = midline_x 81 while x <= t_x1 { 82 if nx_image_get(skin_mask, x, mid_y, 0) > 0 { r_w = r_w + 1 } else { x = t_x1 + 1 } 83 x = x + 1 84 } 85 86 // Top-row: walk inward from each thigh edge to find gap. 87 // Find leftmost skin at top_y starting from midline going left, then 88 // first non-skin going right of that — that's the gap. 89 var gap_px: i64 = 0 90 var gx: i64 = midline_x 91 var have_l: i64 = 0 92 while gx >= t_x0 { 93 if nx_image_get(skin_mask, gx, top_y, 0) > 0 { 94 have_l = 1 95 gx = gx + 1 96 // Walk right from here counting non-skin until we hit skin again. 97 while gx <= t_x1 { 98 if nx_image_get(skin_mask, gx, top_y, 0) > 0 { gx = t_x1 + 2 } 99 else { gap_px = gap_px + 1 } 100 gx = gx + 1 101 } 102 gx = t_x0 - 1 103 } else { gx = gx - 1 } 104 } 105 106 let avg_thigh_w: i64 = (l_w + r_w) / 2 107 result[NX_THIGH_RES_THICK_PX] = avg_thigh_w 108 result[NX_THIGH_RES_GAP_PX] = gap_px 109 110 let thick_q10: i64 = (avg_thigh_w * NX_MAGIC_1024) / hip_width_px 111 let gap_q10: i64 = (gap_px * NX_MAGIC_1024) / hip_width_px 112 result[NX_THIGH_RES_THICK_Q10] = thick_q10 113 result[NX_THIGH_RES_GAP_Q10] = gap_q10 114 115 // Thickness verdict. 116 var tv: i64 = NX_THIGH_V_ATHLETIC 117 if thick_q10 < 461 { tv = NX_THIGH_V_THIN } 118 else { if thick_q10 < 563 { tv = NX_THIGH_V_SLIM } 119 else { if thick_q10 < 666 { tv = NX_THIGH_V_ATHLETIC } 120 else { if thick_q10 < 768 { tv = NX_THIGH_V_CURVY } 121 else { tv = NX_THIGH_V_THICK } } } } 122 result[NX_THIGH_RES_THICK_VRDT] = tv 123 124 // Gap verdict. 125 var gv: i64 = NX_THIGH_GAP_NONE 126 if gap_q10 < 10 { gv = NX_THIGH_GAP_NONE } 127 else { if gap_q10 < 51 { gv = NX_THIGH_GAP_SMALL } 128 else { if gap_q10 < 123 { gv = NX_THIGH_GAP_MODEST } 129 else { gv = NX_THIGH_GAP_WIDE } } } 130 result[NX_THIGH_RES_GAP_VRDT] = gv 131 return 0 132}