nx_compose.nx source
↩ module page · 211 lines · 7286 B
1// nx_compose.nx -- compositional primitives (V2 of vision rollout).
2//
3// Pure-math aesthetic measures that work on image PROPERTIES, never
4// on subjects. Per CAPTAIN_MORONI_DOCTRINE these are objective
5// scores; they never rank persons or assign beauty to humans.
6//
7// Ships:
8// - nx_compose_thirds_score: rule-of-thirds alignment
9// - nx_compose_h_symmetry: left/right mirror symmetry
10// - nx_compose_v_symmetry: top/bottom mirror symmetry
11// - nx_compose_visual_centroid: weighted center-of-intensity
12// - nx_compose_balance_score: distance from geometric center
13//
14// All scores in Q10 fixed-point (1024 = "perfect" depending on metric).
15//
16// What this unlocks:
17// + objective composition feedback ("subject not on thirds")
18// + symmetry detection (architecture, portrait framing, reflections)
19// + balance assessment (heavy on one side vs centered)
20// + aesthetic measurement at the image level (never the subject)
21//
22// genealogy_id: birkhoff_1933_aesthetic + datta_2006_56features
23// + cropping_aesthetics_research
24// lineage_id: image_property_measurement (not subject ranking)
25
26// nx_safety_envelope:
27// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
28// sil_target: SIL1
29// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
30// verdict: NOT_YET_EVALUATED
31
32import "syscalls.nx"
33import "nx_image.nx"
34
35const NX_COMP_Q: i64 = 1024
36
37// ===== Rule of thirds ===================================================
38//
39// Score how much grayscale intensity mass falls within bands of width
40// W/9 centered on the thirds lines (x = W/3, 2W/3) and height H/9
41// centered on (y = H/3, 2H/3). Returns Q10 score (1024 = all mass
42// on thirds; 0 = none).
43//
44// A baseline image with uniform brightness scores around 444 (4 bands
45// each W/9 wide = 4/9 of image area). A subject placed exactly on a
46// thirds intersection scores higher.
47
48func nx_compose_thirds_score(gray: *Image) -> i64 {
49 let w: i64 = gray.width
50 let h: i64 = gray.height
51 let band_w: i64 = w / 9
52 let band_h: i64 = h / 9
53 let x_third_1: i64 = w / 3
54 let x_third_2: i64 = (2 * w) / 3
55 let y_third_1: i64 = h / 3
56 let y_third_2: i64 = (2 * h) / 3
57 var on_thirds: i64 = 0
58 var total: i64 = 0
59 var y: i64 = 0
60 while y < h {
61 var x: i64 = 0
62 while x < w {
63 let v: i64 = nx_image_get(gray, x, y, 0)
64 total = total + v
65 // Pixel is "on a thirds line" if x within band_w of either
66 // vertical-third, OR y within band_h of either horizontal-third.
67 var on_x: i64 = 0
68 let dx1: i64 = x - x_third_1
69 var adx1: i64 = dx1
70 if adx1 < 0 { adx1 = -adx1 }
71 let dx2: i64 = x - x_third_2
72 var adx2: i64 = dx2
73 if adx2 < 0 { adx2 = -adx2 }
74 if adx1 < band_w { on_x = 1 }
75 if adx2 < band_w { on_x = 1 }
76 var on_y: i64 = 0
77 let dy1: i64 = y - y_third_1
78 var ady1: i64 = dy1
79 if ady1 < 0 { ady1 = -ady1 }
80 let dy2: i64 = y - y_third_2
81 var ady2: i64 = dy2
82 if ady2 < 0 { ady2 = -ady2 }
83 if ady1 < band_h { on_y = 1 }
84 if ady2 < band_h { on_y = 1 }
85 if on_x == 1 { on_thirds = on_thirds + v }
86 if on_y == 1 {
87 if on_x == 0 { // avoid double-count
88 on_thirds = on_thirds + v
89 }
90 }
91 x = x + 1
92 }
93 y = y + 1
94 }
95 if total == 0 { return 0 }
96 return (on_thirds * NX_COMP_Q) / total
97}
98
99// ===== Horizontal mirror symmetry ======================================
100//
101// For each pixel (x, y), compare to mirror (W-1-x, y). Sum absolute
102// differences and normalize by total intensity. Return Q10 where
103// 1024 = perfect mirror symmetry, 0 = anti-symmetric.
104
105func nx_compose_h_symmetry(gray: *Image) -> i64 {
106 let w: i64 = gray.width
107 let h: i64 = gray.height
108 var diff_sum: i64 = 0
109 var total: i64 = 0
110 var y: i64 = 0
111 while y < h {
112 var x: i64 = 0
113 while x < w / 2 {
114 let lv: i64 = nx_image_get(gray, x, y, 0)
115 let rv: i64 = nx_image_get(gray, w - 1 - x, y, 0)
116 var d: i64 = lv - rv
117 if d < 0 { d = -d }
118 diff_sum = diff_sum + d
119 total = total + lv + rv
120 x = x + 1
121 }
122 y = y + 1
123 }
124 if total == 0 { return NX_COMP_Q } // empty image -> trivially symmetric
125 let asymmetry: i64 = (diff_sum * NX_COMP_Q) / total
126 if asymmetry >= NX_COMP_Q { return 0 }
127 return NX_COMP_Q - asymmetry
128}
129
130// ===== Vertical mirror symmetry ========================================
131
132func nx_compose_v_symmetry(gray: *Image) -> i64 {
133 let w: i64 = gray.width
134 let h: i64 = gray.height
135 var diff_sum: i64 = 0
136 var total: i64 = 0
137 var y: i64 = 0
138 while y < h / 2 {
139 var x: i64 = 0
140 while x < w {
141 let tv: i64 = nx_image_get(gray, x, y, 0)
142 let bv: i64 = nx_image_get(gray, x, h - 1 - y, 0)
143 var d: i64 = tv - bv
144 if d < 0 { d = -d }
145 diff_sum = diff_sum + d
146 total = total + tv + bv
147 x = x + 1
148 }
149 y = y + 1
150 }
151 if total == 0 { return NX_COMP_Q }
152 let asymmetry: i64 = (diff_sum * NX_COMP_Q) / total
153 if asymmetry >= NX_COMP_Q { return 0 }
154 return NX_COMP_Q - asymmetry
155}
156
157// ===== Visual-weight centroid ==========================================
158//
159// Center of intensity (weighted by pixel brightness). Returned in
160// Q10 of image dimensions:
161// out_cx_q = 1024 * centroid_x / width
162// out_cy_q = 1024 * centroid_y / height
163// So 512 = exact center, 0 = far left/top, 1024 = far right/bottom.
164
165func nx_compose_visual_centroid(gray: *Image, out_cx_q: *i64,
166 out_cy_q: *i64) -> i64 {
167 let w: i64 = gray.width
168 let h: i64 = gray.height
169 var sum_v: i64 = 0
170 var sum_xv: i64 = 0
171 var sum_yv: i64 = 0
172 var y: i64 = 0
173 while y < h {
174 var x: i64 = 0
175 while x < w {
176 let v: i64 = nx_image_get(gray, x, y, 0)
177 sum_v = sum_v + v
178 sum_xv = sum_xv + x * v
179 sum_yv = sum_yv + y * v
180 x = x + 1
181 }
182 y = y + 1
183 }
184 if sum_v == 0 {
185 out_cx_q[0] = 512
186 out_cy_q[0] = 512
187 return 0
188 }
189 out_cx_q[0] = (sum_xv * NX_COMP_Q) / (sum_v * w)
190 out_cy_q[0] = (sum_yv * NX_COMP_Q) / (sum_v * h)
191 return 0
192}
193
194// ===== Balance score ===================================================
195//
196// Distance from geometric center. Q10 where 1024 = at corner (max
197// distance), 0 = perfectly centered.
198
199func nx_compose_balance_score(gray: *Image) -> i64 {
200 let cx_q: *i64 = (sys_mmap(8)) as *i64
201 let cy_q: *i64 = (sys_mmap(8)) as *i64
202 nx_compose_visual_centroid(gray, cx_q, cy_q)
203 var dx: i64 = cx_q[0] - 512
204 if dx < 0 { dx = -dx }
205 var dy: i64 = cy_q[0] - 512
206 if dy < 0 { dy = -dy }
207 // Manhattan distance from center, scaled so corner = 1024
208 var dist: i64 = dx + dy
209 if dist > NX_COMP_Q { dist = NX_COMP_Q }
210 return dist
211}