nx_breast_localize.nx source
↩ module page · 129 lines · 4531 B
1// nx_breast_localize.nx -- find left/right breast sub-regions in torso.
2//
3// In real renders the two breasts are connected to torso as one CC,
4// so we can't isolate them by connected-component alone. The substrate
5// splits the upper-third of the torso at midline and computes the
6// bounding box of skin-mask pixels in each half. Outputs per-breast
7// bbox + centroid + area_px.
8//
9// Output flat-array (packed):
10// result[0..4] left breast: x0, y0, x1, y1, area_px
11// result[5..9] right breast: x0, y0, x1, y1, area_px
12// result[10] left centroid_x
13// result[11] left centroid_y
14// result[12] right centroid_x
15// result[13] right centroid_y
16
17// nx_safety_envelope:
18// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
19// sil_target: SIL1
20// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
21// verdict: NOT_YET_EVALUATED
22
23import "nx_syscalls.nx"
24import "nx_image.nx"
25
26const NX_BLOC_RES_L_X0: i64 = 0
27const NX_BLOC_RES_L_Y0: i64 = 1
28const NX_BLOC_RES_L_X1: i64 = 2
29const NX_BLOC_RES_L_Y1: i64 = 3
30const NX_BLOC_RES_L_AREA: i64 = 4
31const NX_BLOC_RES_R_X0: i64 = 5
32const NX_BLOC_RES_R_Y0: i64 = 6
33const NX_BLOC_RES_R_X1: i64 = 7
34const NX_BLOC_RES_R_Y1: i64 = 8
35const NX_BLOC_RES_R_AREA: i64 = 9
36const NX_BLOC_RES_L_CX: i64 = 10
37const NX_BLOC_RES_L_CY: i64 = 11
38const NX_BLOC_RES_R_CX: i64 = 12
39const NX_BLOC_RES_R_CY: i64 = 13
40const NX_BLOC_RES_FIELDS: i64 = 14
41
42// Localize left/right breast in upper-third of torso.
43// bbox layout: [torso_x0, torso_y0, torso_x1, torso_y1]
44func nx_breast_localize(skin_mask: *Image, bbox: *i64, result: *i64) -> i64 {
45 var i: i64 = 0
46 while i < NX_BLOC_RES_FIELDS { result[i] = 0; i = i + 1 }
47
48 let torso_x0: i64 = bbox[0]
49 let torso_y0: i64 = bbox[1]
50 let torso_x1: i64 = bbox[2]
51 let torso_y1: i64 = bbox[3]
52 let torso_w: i64 = torso_x1 - torso_x0
53 let torso_h: i64 = torso_y1 - torso_y0
54 if torso_w <= 0 { return 1 }
55 if torso_h <= 0 { return 2 }
56
57 let upper_y1: i64 = torso_y0 + torso_h / 3 // upper third of torso
58 let midline_x: i64 = torso_x0 + torso_w / 2
59
60 // Init left bbox to invalid; we'll narrow it.
61 var l_x0: i64 = torso_x1
62 var l_y0: i64 = torso_y1
63 var l_x1: i64 = torso_x0
64 var l_y1: i64 = torso_y0
65 var l_area: i64 = 0
66 var l_sum_x: i64 = 0
67 var l_sum_y: i64 = 0
68
69 var r_x0: i64 = torso_x1
70 var r_y0: i64 = torso_y1
71 var r_x1: i64 = torso_x0
72 var r_y1: i64 = torso_y0
73 var r_area: i64 = 0
74 var r_sum_x: i64 = 0
75 var r_sum_y: i64 = 0
76
77 var y: i64 = torso_y0
78 while y < upper_y1 {
79 if y >= 0 { if y < skin_mask.height {
80 var x: i64 = torso_x0
81 while x < torso_x1 {
82 if x >= 0 { if x < skin_mask.width {
83 let m: i64 = nx_image_get(skin_mask, x, y, 0)
84 if m > 0 {
85 if x < midline_x {
86 if x < l_x0 { l_x0 = x }
87 if x > l_x1 { l_x1 = x }
88 if y < l_y0 { l_y0 = y }
89 if y > l_y1 { l_y1 = y }
90 l_area = l_area + 1
91 l_sum_x = l_sum_x + x
92 l_sum_y = l_sum_y + y
93 } else {
94 if x < r_x0 { r_x0 = x }
95 if x > r_x1 { r_x1 = x }
96 if y < r_y0 { r_y0 = y }
97 if y > r_y1 { r_y1 = y }
98 r_area = r_area + 1
99 r_sum_x = r_sum_x + x
100 r_sum_y = r_sum_y + y
101 }
102 }
103 } }
104 x = x + 1
105 }
106 } }
107 y = y + 1
108 }
109
110 if l_area > 0 {
111 result[NX_BLOC_RES_L_X0] = l_x0
112 result[NX_BLOC_RES_L_Y0] = l_y0
113 result[NX_BLOC_RES_L_X1] = l_x1
114 result[NX_BLOC_RES_L_Y1] = l_y1
115 result[NX_BLOC_RES_L_AREA] = l_area
116 result[NX_BLOC_RES_L_CX] = l_sum_x / l_area
117 result[NX_BLOC_RES_L_CY] = l_sum_y / l_area
118 }
119 if r_area > 0 {
120 result[NX_BLOC_RES_R_X0] = r_x0
121 result[NX_BLOC_RES_R_Y0] = r_y0
122 result[NX_BLOC_RES_R_X1] = r_x1
123 result[NX_BLOC_RES_R_Y1] = r_y1
124 result[NX_BLOC_RES_R_AREA] = r_area
125 result[NX_BLOC_RES_R_CX] = r_sum_x / r_area
126 result[NX_BLOC_RES_R_CY] = r_sum_y / r_area
127 }
128 return 0
129}