code wiki / (root) / nx_region.nx

nx_region.nx source

↩ module page · 194 lines · 6841 B

1// nx_region.nx -- typed body-region segmentation (V7 substrate). 2// 3// Composes V1 Kovac skin mask + L7 CC + L7 component stats into a 4// typed RegionSet with sealed BodyPart enum. Mirrors region.py. 5// 6// genealogy_id: rosenfeld_pfaltz_1966_cc + kovac_2003_skin 7// lineage_id: typed_anatomical_regioning_from_skin_topology 8 9// nx_safety_envelope: 10// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 11// sil_target: SIL1 12// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 13// verdict: NOT_YET_EVALUATED 14 15import "nx_syscalls.nx" 16import "nx_image.nx" 17import "nx_color_v2.nx" 18import "nx_features.nx" 19 20// Sealed BodyPart enum 21const NX_REGION_UNKNOWN: i64 = 0 22const NX_REGION_TORSO: i64 = 1 23const NX_REGION_FACE: i64 = 2 24const NX_REGION_LIMB: i64 = 3 25const NX_REGION_BACKGROUND: i64 = 4 26 27// Flat-array field indices 28const NX_REGION_F_KIND: i64 = 0 29const NX_REGION_F_BBOX_MIN_X: i64 = 1 30const NX_REGION_F_BBOX_MIN_Y: i64 = 2 31const NX_REGION_F_BBOX_MAX_X: i64 = 3 32const NX_REGION_F_BBOX_MAX_Y: i64 = 4 33const NX_REGION_F_CENTROID_X: i64 = 5 34const NX_REGION_F_CENTROID_Y: i64 = 6 35const NX_REGION_F_AREA_PX: i64 = 7 36const NX_REGION_F_CONF_Q10: i64 = 8 37const NX_REGION_FIELDS: i64 = 9 38const NX_REGION_MAX_COUNT: i64 = 16 39 40// Area thresholds (Q10 of total image area) 41const NX_REGION_NOISE_AREA_Q10: i64 = 5 42const NX_REGION_LIMB_AREA_Q10: i64 = 20 43const NX_REGION_TORSO_AREA_Q10: i64 = 100 44const NX_REGION_FACE_FRAC_Q10: i64 = 307 // top 30% of torso for FACE 45 46struct RegionSet { 47 data: *i64, 48 count: i64, 49 cap: i64, 50} 51 52func nx_region_get_field(set: *RegionSet, i: i64, field: i64) -> i64 { 53 return set.data[i * NX_REGION_FIELDS + field] 54} 55 56func nx_region_add(set: *RegionSet, kind: i64, 57 bmin_x: i64, bmin_y: i64, bmax_x: i64, bmax_y: i64, 58 cx: i64, cy: i64, area: i64, conf_q10: i64) -> i64 { 59 if set.count >= set.cap { return 1 } 60 let base: i64 = set.count * NX_REGION_FIELDS 61 set.data[base + NX_REGION_F_KIND] = kind 62 set.data[base + NX_REGION_F_BBOX_MIN_X] = bmin_x 63 set.data[base + NX_REGION_F_BBOX_MIN_Y] = bmin_y 64 set.data[base + NX_REGION_F_BBOX_MAX_X] = bmax_x 65 set.data[base + NX_REGION_F_BBOX_MAX_Y] = bmax_y 66 set.data[base + NX_REGION_F_CENTROID_X] = cx 67 set.data[base + NX_REGION_F_CENTROID_Y] = cy 68 set.data[base + NX_REGION_F_AREA_PX] = area 69 set.data[base + NX_REGION_F_CONF_Q10] = conf_q10 70 set.count = set.count + 1 71 return 0 72} 73 74func nx_region_count_kind(set: *RegionSet, kind: i64) -> i64 { 75 var i: i64 = 0 76 var n: i64 = 0 77 while i < set.count { 78 if nx_region_get_field(set, i, NX_REGION_F_KIND) == kind { 79 n = n + 1 80 } 81 i = i + 1 82 } 83 return n 84} 85 86func nx_region_find_kind(set: *RegionSet, kind: i64) -> i64 { 87 var i: i64 = 0 88 while i < set.count { 89 if nx_region_get_field(set, i, NX_REGION_F_KIND) == kind { 90 return i 91 } 92 i = i + 1 93 } 94 return -1 95} 96 97func nx_region_segment(rgb: *Image) -> *RegionSet { 98 let w: i64 = rgb.width 99 let h: i64 = rgb.height 100 let total_area: i64 = w * h 101 let set: *RegionSet = (sys_mmap(32)) as *RegionSet 102 set.cap = NX_REGION_MAX_COUNT 103 set.count = 0 104 set.data = (sys_mmap(NX_REGION_MAX_COUNT * NX_REGION_FIELDS * 8 + 16)) as *i64 105 if total_area <= 0 { return set } 106 107 let noise_floor: i64 = (total_area * NX_REGION_NOISE_AREA_Q10) / 1024 108 let limb_floor: i64 = (total_area * NX_REGION_LIMB_AREA_Q10) / 1024 109 let torso_floor: i64 = (total_area * NX_REGION_TORSO_AREA_Q10) / 1024 110 111 let mask: *Image = nx_color_skin_mask(rgb) 112 let cc: *CCResult = nx_feat_connected_components(mask) 113 let n: i64 = cc.n_components 114 if n == 0 { 115 nx_region_add(set, NX_REGION_BACKGROUND, 0, 0, w-1, h-1, w/2, h/2, total_area, 1024) 116 return set 117 } 118 119 let stats: *i64 = (sys_mmap(n * 7 * 8 + 16)) as *i64 120 nx_feat_component_stats(cc, stats) 121 122 var torso_idx: i64 = -1 123 var torso_area: i64 = 0 124 var i: i64 = 0 125 while i < n { 126 let area: i64 = stats[i * 7] 127 if area >= torso_floor { 128 if area > torso_area { 129 torso_area = area 130 torso_idx = i 131 } 132 } 133 i = i + 1 134 } 135 136 if torso_idx >= 0 { 137 let t_min_x: i64 = stats[torso_idx * 7 + 1] 138 let t_min_y: i64 = stats[torso_idx * 7 + 2] 139 let t_max_x: i64 = stats[torso_idx * 7 + 3] 140 let t_max_y: i64 = stats[torso_idx * 7 + 4] 141 let t_cx: i64 = stats[torso_idx * 7 + 5] / 1000 142 let t_cy: i64 = stats[torso_idx * 7 + 6] / 1000 143 var t_conf: i64 = (torso_area * 1024) / total_area 144 t_conf = t_conf * 4 145 if t_conf > 1024 { t_conf = 1024 } 146 nx_region_add(set, NX_REGION_TORSO, 147 t_min_x, t_min_y, t_max_x, t_max_y, 148 t_cx, t_cy, torso_area, t_conf) 149 150 // FACE region: top 30% of torso bbox 151 let height: i64 = t_max_y - t_min_y 152 let face_h: i64 = (height * NX_REGION_FACE_FRAC_Q10) / 1024 153 var f_max_y: i64 = t_min_y + face_h 154 if f_max_y > t_max_y { f_max_y = t_max_y } 155 let f_cx: i64 = (t_min_x + t_max_x) / 2 156 let f_cy: i64 = (t_min_y + f_max_y) / 2 157 let face_area: i64 = (t_max_x - t_min_x + 1) * (f_max_y - t_min_y + 1) 158 nx_region_add(set, NX_REGION_FACE, 159 t_min_x, t_min_y, t_max_x, f_max_y, 160 f_cx, f_cy, face_area, 768) 161 } 162 163 i = 0 164 while i < n { 165 if i != torso_idx { 166 let area: i64 = stats[i * 7] 167 if area >= limb_floor { 168 if area < torso_floor { 169 let l_min_x: i64 = stats[i * 7 + 1] 170 let l_min_y: i64 = stats[i * 7 + 2] 171 let l_max_x: i64 = stats[i * 7 + 3] 172 let l_max_y: i64 = stats[i * 7 + 4] 173 let l_cx: i64 = stats[i * 7 + 5] / 1000 174 let l_cy: i64 = stats[i * 7 + 6] / 1000 175 var l_conf: i64 = 1024 176 if torso_area > 0 { 177 l_conf = (area * 1024) / torso_area 178 if l_conf > 1024 { l_conf = 1024 } 179 } 180 nx_region_add(set, NX_REGION_LIMB, 181 l_min_x, l_min_y, l_max_x, l_max_y, 182 l_cx, l_cy, area, l_conf) 183 } 184 } 185 } 186 i = i + 1 187 } 188 189 // BACKGROUND: inverse skin (whole-image bbox, area = non-skin count) 190 var bg_area: i64 = total_area 191 if torso_area > 0 { bg_area = total_area - torso_area } 192 nx_region_add(set, NX_REGION_BACKGROUND, 0, 0, w-1, h-1, w/2, h/2, bg_area, 1024) 193 return set 194}