code wiki / (root) / nx_vulva_innie_outie.nx

nx_vulva_innie_outie.nx source

↩ module page · 164 lines · 5985 B

1// nx_vulva_innie_outie.nx -- labia minora protrusion classifier. 2// 3// Vulva aesthetic axis: innie vs outie. Measure how far inner-labia 4// pixels protrude BEYOND the outer-labia centerline. Positive = outie; 5// negative = innie (minora tucked inside). 6// 7// Algorithm: 8// 1. Within vulva bbox, find the outer-labia midline (vertical axis). 9// 2. Identify "inner labia" pixels: darker pink than outer skin. 10// 3. For each row in vulva band, measure max horizontal extent of 11// inner-labia pixels from the midline. 12// 4. Average extent → labia_protrusion_q10 (relative to vulva half-width). 13// 14// Output flat-array: 15// result[0] = protrusion_avg_px (avg per-row inner-labia max extent) 16// result[1] = vulva_half_width_px 17// result[2] = protrusion_q10 (avg_extent * 1024 / half_width) 18// result[3] = inner_labia_pixel_count 19// result[4] = verdict 20// 0=DEEP_INNIE / 1=INNIE / 2=NEUTRAL / 3=OUTIE / 4=PROMINENT_OUTIE 21 22// nx_safety_envelope: 23// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 24// sil_target: SIL1 25// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 26// verdict: NOT_YET_EVALUATED 27 28import "nx_syscalls.nx" 29import "nx_image.nx" 30const NX_MAGIC_1024: i64 = 1024 31 32const NX_INNIE_RES_PROT_PX: i64 = 0 33const NX_INNIE_RES_HALF_W: i64 = 1 34const NX_INNIE_RES_PROT_Q10: i64 = 2 35const NX_INNIE_RES_INNER_COUNT: i64 = 3 36const NX_INNIE_RES_VERDICT: i64 = 4 37const NX_INNIE_RES_FIELDS: i64 = 5 38 39const NX_INNIE_V_DEEP_INNIE: i64 = 0 40const NX_INNIE_V_INNIE: i64 = 1 41const NX_INNIE_V_NEUTRAL: i64 = 2 42const NX_INNIE_V_OUTIE: i64 = 3 43const NX_INNIE_V_PROMINENT_OUTIE: i64 = 4 44 45// Thresholds in Q10 of vulva half-width. 46const NX_INNIE_DEEP_INNIE_MAX: i64 = 102 // <= 10% = deep innie 47const NX_INNIE_INNIE_MAX: i64 = 256 // 10-25% = innie 48const NX_INNIE_NEUTRAL_MAX: i64 = 410 // 25-40% = neutral 49const NX_INNIE_OUTIE_MAX: i64 = 614 // 40-60% = outie; > 60% = prominent 50 51// Inner labia detection threshold: R-channel > outer skin by this delta. 52// Outer labia / surrounding skin is pinkish; inner labia is more red-pink. 53const NX_INNIE_R_DELTA_MIN: i64 = 12 54 55// Mean R of the perimeter band (outer labia / vulva margin baseline). 56func nx_innie_perimeter_r(rgb: *Image, x0: i64, y0: i64, 57 x1: i64, y1: i64) -> i64 { 58 var sum: i64 = 0 59 var n: i64 = 0 60 var y: i64 = y0 61 while y <= y1 { 62 if y >= 0 { if y < rgb.height { 63 // Top + bottom row. 64 if y == y0 { 65 var x: i64 = x0 66 while x <= x1 { 67 if x >= 0 { if x < rgb.width { 68 sum = sum + nx_image_get(rgb, x, y, 0) 69 n = n + 1 70 } } 71 x = x + 1 72 } 73 } 74 if y == y1 { 75 var x: i64 = x0 76 while x <= x1 { 77 if x >= 0 { if x < rgb.width { 78 sum = sum + nx_image_get(rgb, x, y, 0) 79 n = n + 1 80 } } 81 x = x + 1 82 } 83 } 84 // Left + right columns. 85 if x0 >= 0 { if x0 < rgb.width { 86 sum = sum + nx_image_get(rgb, x0, y, 0) 87 n = n + 1 88 } } 89 if x1 >= 0 { if x1 < rgb.width { 90 sum = sum + nx_image_get(rgb, x1, y, 0) 91 n = n + 1 92 } } 93 } } 94 y = y + 1 95 } 96 if n == 0 { return 0 } 97 return sum / n 98} 99 100// Score labia protrusion. 101// bbox: [vulva_x0, vulva_y0, vulva_x1, vulva_y1] 102func nx_vulva_innie_outie(rgb: *Image, bbox: *i64, result: *i64) -> i64 { 103 var i: i64 = 0 104 while i < NX_INNIE_RES_FIELDS { result[i] = 0; i = i + 1 } 105 let x0: i64 = bbox[0] 106 let y0: i64 = bbox[1] 107 let x1: i64 = bbox[2] 108 let y1: i64 = bbox[3] 109 if x1 <= x0 { return 1 } 110 if y1 <= y0 { return 2 } 111 112 let vulva_w: i64 = x1 - x0 + 1 113 let half_w: i64 = vulva_w / 2 114 let midline_x: i64 = (x0 + x1) / 2 115 result[NX_INNIE_RES_HALF_W] = half_w 116 117 let baseline_r: i64 = nx_innie_perimeter_r(rgb, x0, y0, x1, y1) 118 let inner_thresh: i64 = baseline_r + NX_INNIE_R_DELTA_MIN 119 120 var sum_max_extent: i64 = 0 121 var row_count: i64 = 0 122 var inner_count: i64 = 0 123 124 var y: i64 = y0 125 while y <= y1 { 126 if y >= 0 { if y < rgb.height { 127 var max_extent: i64 = 0 128 var x: i64 = x0 129 while x <= x1 { 130 if x >= 0 { if x < rgb.width { 131 let r: i64 = nx_image_get(rgb, x, y, 0) 132 if r > inner_thresh { 133 inner_count = inner_count + 1 134 var dx: i64 = x - midline_x 135 if dx < 0 { dx = -dx } 136 if dx > max_extent { max_extent = dx } 137 } 138 } } 139 x = x + 1 140 } 141 sum_max_extent = sum_max_extent + max_extent 142 row_count = row_count + 1 143 } } 144 y = y + 1 145 } 146 147 result[NX_INNIE_RES_INNER_COUNT] = inner_count 148 if row_count == 0 { return 3 } 149 let avg_extent: i64 = sum_max_extent / row_count 150 result[NX_INNIE_RES_PROT_PX] = avg_extent 151 152 if half_w == 0 { return 4 } 153 let prot_q10: i64 = (avg_extent * NX_MAGIC_1024) / half_w 154 result[NX_INNIE_RES_PROT_Q10] = prot_q10 155 156 var verdict: i64 = NX_INNIE_V_NEUTRAL 157 if prot_q10 <= NX_INNIE_DEEP_INNIE_MAX { verdict = NX_INNIE_V_DEEP_INNIE } 158 else { if prot_q10 <= NX_INNIE_INNIE_MAX { verdict = NX_INNIE_V_INNIE } 159 else { if prot_q10 <= NX_INNIE_NEUTRAL_MAX { verdict = NX_INNIE_V_NEUTRAL } 160 else { if prot_q10 <= NX_INNIE_OUTIE_MAX { verdict = NX_INNIE_V_OUTIE } 161 else { verdict = NX_INNIE_V_PROMINENT_OUTIE } } } } 162 result[NX_INNIE_RES_VERDICT] = verdict 163 return 0 164}