code wiki / (root) / nx_arm_count.nx

nx_arm_count.nx source

↩ module page · 159 lines · 6067 B

1// nx_arm_count.nx -- limb-count detector for SDXL hallucination class. 2// v0: bbox-touches. v1 ADDS: skin-bridge ratio along centroid line. 3 4// nx_safety_envelope: 5// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 6// sil_target: SIL1 7// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 8// verdict: NOT_YET_EVALUATED 9 10import "nx_syscalls.nx" 11import "nx_image.nx" 12import "nx_color_v2.nx" 13import "nx_features.nx" 14const NX_MAGIC_1024: i64 = 1024 15 16const NX_ARM_NOISE_AREA_Q10: i64 = 5 17const NX_ARM_LIMB_AREA_Q10: i64 = 20 18const NX_ARM_TORSO_AREA_Q10: i64 = 100 19const NX_ARM_BBOX_MARGIN: i64 = 4 20const NX_ARM_BRIDGE_RATIO_Q10: i64 = 256 21const NX_ARM_LINE_SAMPLES: i64 = 20 22 23struct ArmCountVerdict { 24 total_skin_components: i64, 25 torso_present: i64, 26 torso_area: i64, 27 limb_candidate_count: i64, 28 plausible_count: i64, 29 implausible_count: i64, 30 severity_q10: i64, 31 fidelity_q10: i64, 32 min_bridge_ratio_q10: i64, 33} 34 35func nx_arm_bbox_touches(a_min_x: i64, a_min_y: i64, a_max_x: i64, a_max_y: i64, 36 b_min_x: i64, b_min_y: i64, b_max_x: i64, b_max_y: i64, 37 margin: i64) -> i64 { 38 if a_max_x + margin < b_min_x { return 0 } 39 if b_max_x + margin < a_min_x { return 0 } 40 if a_max_y + margin < b_min_y { return 0 } 41 if b_max_y + margin < a_min_y { return 0 } 42 return 1 43} 44 45func nx_arm_line_skin_ratio(mask: *Image, x0: i64, y0: i64, x1: i64, y1: i64) -> i64 { 46 let n: i64 = NX_ARM_LINE_SAMPLES 47 let denom: i64 = n - 1 48 var hits: i64 = 0 49 var s: i64 = 0 50 while s < n { 51 let x: i64 = x0 + ((x1 - x0) * s) / denom 52 let y: i64 = y0 + ((y1 - y0) * s) / denom 53 if x >= 0 { 54 if x < mask.width { 55 if y >= 0 { 56 if y < mask.height { 57 if nx_image_get(mask, x, y, 0) > 0 { hits = hits + 1 } 58 } 59 } 60 } 61 } 62 s = s + 1 63 } 64 return (hits * NX_MAGIC_1024) / n 65} 66 67func nx_arm_count_compute(rgb: *Image, verdict: *ArmCountVerdict) -> i64 { 68 let w: i64 = rgb.width 69 let h: i64 = rgb.height 70 let total_area: i64 = w * h 71 if total_area <= 0 { return 1 } 72 73 let noise_floor: i64 = (total_area * NX_ARM_NOISE_AREA_Q10) / NX_MAGIC_1024 74 let limb_floor: i64 = (total_area * NX_ARM_LIMB_AREA_Q10) / NX_MAGIC_1024 75 let torso_floor: i64 = (total_area * NX_ARM_TORSO_AREA_Q10) / NX_MAGIC_1024 76 77 let mask: *Image = nx_color_skin_mask(rgb) 78 let cc: *CCResult = nx_feat_connected_components(mask) 79 let n: i64 = cc.n_components 80 81 verdict.total_skin_components = 0 82 verdict.torso_present = 0 83 verdict.torso_area = 0 84 verdict.limb_candidate_count = 0 85 verdict.plausible_count = 0 86 verdict.implausible_count = 0 87 verdict.severity_q10 = 0 88 verdict.fidelity_q10 = 0 89 verdict.min_bridge_ratio_q10 = NX_MAGIC_1024 90 if n == 0 { return 0 } 91 92 let stats: *i64 = (sys_mmap(n * 7 * 8 + 16)) as *i64 93 nx_feat_component_stats(cc, stats) 94 95 var torso_idx: i64 = -1 96 var torso_area_val: i64 = 0 97 var i: i64 = 0 98 while i < n { 99 let area: i64 = stats[i * 7] 100 if area >= noise_floor { verdict.total_skin_components = verdict.total_skin_components + 1 } 101 if area >= torso_floor { 102 if area > torso_area_val { torso_area_val = area; torso_idx = i } 103 } 104 i = i + 1 105 } 106 if torso_idx < 0 { verdict.fidelity_q10 = 256; return 0 } 107 108 verdict.torso_present = 1 109 verdict.torso_area = torso_area_val 110 let t_min_x: i64 = stats[torso_idx * 7 + 1] 111 let t_min_y: i64 = stats[torso_idx * 7 + 2] 112 let t_max_x: i64 = stats[torso_idx * 7 + 3] 113 let t_max_y: i64 = stats[torso_idx * 7 + 4] 114 let t_cx: i64 = stats[torso_idx * 7 + 5] / 1000 115 let t_cy: i64 = stats[torso_idx * 7 + 6] / 1000 116 117 i = 0 118 while i < n { 119 if i != torso_idx { 120 let area: i64 = stats[i * 7] 121 if area >= limb_floor { 122 if area < torso_floor { 123 verdict.limb_candidate_count = verdict.limb_candidate_count + 1 124 let c_min_x: i64 = stats[i * 7 + 1] 125 let c_min_y: i64 = stats[i * 7 + 2] 126 let c_max_x: i64 = stats[i * 7 + 3] 127 let c_max_y: i64 = stats[i * 7 + 4] 128 let c_cx: i64 = stats[i * 7 + 5] / 1000 129 let c_cy: i64 = stats[i * 7 + 6] / 1000 130 let touches: i64 = nx_arm_bbox_touches( 131 c_min_x, c_min_y, c_max_x, c_max_y, 132 t_min_x, t_min_y, t_max_x, t_max_y, NX_ARM_BBOX_MARGIN) 133 let bridge_q10: i64 = nx_arm_line_skin_ratio(mask, c_cx, c_cy, t_cx, t_cy) 134 if bridge_q10 < verdict.min_bridge_ratio_q10 { verdict.min_bridge_ratio_q10 = bridge_q10 } 135 var bbox_ok: i64 = 0 136 var bridge_ok: i64 = 0 137 if touches == 1 { bbox_ok = 1 } 138 if bridge_q10 >= NX_ARM_BRIDGE_RATIO_Q10 { bridge_ok = 1 } 139 if bbox_ok == 1 { 140 if bridge_ok == 1 { verdict.plausible_count = verdict.plausible_count + 1 } 141 } 142 if bbox_ok == 0 { verdict.implausible_count = verdict.implausible_count + 1 } 143 if bbox_ok == 1 { 144 if bridge_ok == 0 { verdict.implausible_count = verdict.implausible_count + 1 } 145 } 146 } 147 } 148 } 149 i = i + 1 150 } 151 var sev: i64 = verdict.implausible_count * 256 152 if sev > NX_MAGIC_1024 { sev = NX_MAGIC_1024 } 153 verdict.severity_q10 = sev 154 var fid: i64 = (torso_area_val * NX_MAGIC_1024) / total_area 155 fid = fid * 4 156 if fid > NX_MAGIC_1024 { fid = NX_MAGIC_1024 } 157 verdict.fidelity_q10 = fid 158 return 0 159}