code wiki / _hdl_build / nx_bodybench.nx
nx_bodybench.nx source
↩ module page · 482 lines · 25306 B
1// nx_bodybench.nx -- ★SOVEREIGN BODY JUDGE, registerable as an MCP tool. Moves the shape ruler OFF the dev
2// laptop (it was PowerShell + System.Drawing) INTO a NishiLang organ that runs on the NAS: any agent, plan or
3// workflow can now measure a generated body against the BodyParts3D oracle with ONE call and get a permil
4// verdict back as JSON -- no image round-trip, no third-party runtime, no human in the loop. That is what
5// "it needs to scale" means: the measurement is a service, not a person at a keyboard.
6//
7// OOP, tagged-dispatch (NOT struct fn-pointer fields -- seq715: obj.fn(args) compiles and does NOT call):
8// * MeshView -- an object that renders ONE mesh at ONE yaw into coverage + per-pixel normal buffers
9// (a minimal measurement rasterizer: projection + edge-function fill + z-buffer + normal;
10// no shading/shadow/floor/texture -- those are display concerns, not measurement).
11// * Judge -- a small hierarchy dispatched by KIND: J_SILH (silhouette IoU) and J_NORMAL (normal-field
12// angular agreement). judge_eval(kind, ...) is the polymorphic entry; add a kind to extend.
13// * BodyBench -- orchestrates: renders ours+oracle at yaw 0 and 90, runs each judge, emits per-judge permil
14// and the HONEST HEADLINE = MIN over judges (hold several judges, publish the minimum).
15//
16// nx_bodybench <ours.nxmesh> <oracle.nxmesh> [W] [H]
17// -> {"front_iou":..,"side_iou":..,"shape":..,"headline":..,"self_iou":1000,"self_shape":1000, ...}
18// The self_* fields are a built-in NON-VACUITY proof: ours-vs-ours must score 1000/1000 or the ruler is broken.
19// license_tier: ORIGINAL expect_exit: 0
20import "nx_syscalls.nx"
21import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
22
23const BB_Q14: i64 = 16384
24const BB_ZFAR: i64 = 2000000000
25const BB_BIG: i64 = 2000000000
26const BB_M8388607: i64 = 8388607
27const BB_M8388608: i64 = 8388608
28const BB_POSQ0: i64 = 4096
29const BB_TARGET: i64 = 200000
30const BB_MAGIC_40500: i64 = 40500
31const BB_ACOSN: i64 = 8192 // acos LUT resolution over cos in [-Q14,+Q14]
32const BB_DETR: i64 = 2 // detail-judge window radius -> a (2R+1)^2 = 5x5 neighbourhood; big
33 // enough to catch muscle/feature-scale normal variation, small enough
34 // to stay local. The one magic knob of the detail judge (rule 11).
35
36func bb_streq(a: *u8, b: *u8) -> i64 {
37 var i: i64 = 0
38 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
39 if b[i] != (0 as u8) { return 0 }
40 return 1
41}
42func bb_satoi(s: *u8) -> i64 {
43 var i: i64 = 0; var n: i64 = 0; var sg: i64 = 1
44 if s[0] == (45 as u8) { sg = 0 - 1; i = 1 }
45 while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c >= 48 { if c <= 57 { n = n*10 + (c-48) } } i = i + 1 }
46 return n*sg
47}
48func bb_hw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n+1 } sys_write(1, s, n); return 0 }
49// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
50// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
51// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
52// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
53func bb_pn(v: i64) -> i64 { nxi_out(v); return 0 }
54func bb_rdbits(b: *u8, o: i64) -> i64 {
55 return (b[o] as i64) | ((b[o+1] as i64)<<8) | ((b[o+2] as i64)<<16) | ((b[o+3] as i64)<<24)
56}
57func bb_f32mul(b: *u8, o: i64, mul: i64) -> i64 {
58 let bits: i64 = bb_rdbits(b, o)
59 let sign: i64 = (bits>>31) & 1
60 let exp: i64 = (bits>>23) & 255
61 let mant: i64 = bits & BB_M8388607
62 if exp == 0 { return 0 }
63 let m: i64 = (mant | BB_M8388608) * mul
64 var e: i64 = exp - 127 - 23
65 var v: i64 = 0
66 if e >= 0 { v = m << e } else { let sh: i64 = 0 - e; v = (m + (1 << (sh-1))) >> sh }
67 if sign == 1 { v = 0 - v }
68 return v
69}
70func bb_isqrt(v: i64) -> i64 { if v <= 0 { return 0 } var x: i64 = v; var y: i64 = (x+1)/2; while y < x { x = y; y = (x + v/x)/2 } return x }
71func bb_wrap(d: i64) -> i64 { var x: i64 = d % 360; if x < 0 { x = x + 360 } return x }
72func bb_min(a: i64, b: i64) -> i64 { if a < b { return a } return b }
73func bb_max(a: i64, b: i64) -> i64 { if a > b { return a } return b }
74// Bhaskara-I degree sine, Q14, exact at 0/30/90/150/180 -- our own integer trig, no float, no table literal.
75func bb_sin_fill(t: *i64) -> i64 {
76 var d: i64 = 0
77 while d < 180 { let P: i64 = d*(180-d); t[d] = BB_Q14*4*P/BB_MAGIC_40500; t[d+180] = 0-t[d]; d = d+1 }
78 return 0
79}
80// acos LUT: degree (0..180) as a function of cos, quantised into BB_ACOSN bins over cos in [-Q14,+Q14].
81// Built by walking degrees (cos is monotonically decreasing) and forward-filling, so every bin resolves.
82func bb_acos_fill(acosT: *i64, sinT: *i64) -> i64 {
83 var i: i64 = 0
84 while i < BB_ACOSN { acosT[i] = 0-1; i = i+1 }
85 var d: i64 = 0
86 while d <= 180 {
87 let c: i64 = sinT[bb_wrap(d+90)] // cos(d) in Q14, +Q14 at 0, -Q14 at 180
88 var idx: i64 = (c + BB_Q14)*(BB_ACOSN-1)/(2*BB_Q14)
89 if idx < 0 { idx = 0 }
90 if idx >= BB_ACOSN { idx = BB_ACOSN-1 }
91 acosT[idx] = d
92 d = d + 1
93 }
94 // forward-fill from the high-cos end (idx high = small angle) downward
95 var last: i64 = 0
96 var j: i64 = BB_ACOSN-1
97 while j >= 0 { if acosT[j] < 0 { acosT[j] = last } else { last = acosT[j] } j = j - 1 }
98 return 0
99}
100func bb_acos_deg(acosT: *i64, dotq: i64) -> i64 {
101 var c: i64 = dotq
102 if c > BB_Q14 { c = BB_Q14 }
103 if c < 0-BB_Q14 { c = 0-BB_Q14 }
104 var idx: i64 = (c + BB_Q14)*(BB_ACOSN-1)/(2*BB_Q14)
105 if idx < 0 { idx = 0 }
106 if idx >= BB_ACOSN { idx = BB_ACOSN-1 }
107 return acosT[idx]
108}
109
110// ============================ MeshView ============================
111// Render ONE mesh at ONE yaw into cov[] (0/1) and nbx/nby/nbz[] (Q14 camera-facing normal per pixel).
112// Framing REPLICATES nx_anat_sov 'fit' mode EXACTLY: scale-invariant precision (pass 0), full AABB (pass 1),
113// height-only camera distance -- so two meshes of equal stature render at equal on-screen height and the
114// silhouette IoU measures SHAPE, not scale. (This is the fair-ruler fix that stopped the ruler moving with
115// the thing it measured.) Returns the coverage pixel count.
116func mv_render(mb: *u8, ntris: i64, triBase: i64, lidBase: i64, W: i64, H: i64, yaw: i64, sinT: *i64,
117 cov: *i64, nbx: *i64, nby: *i64, nbz: *i64) -> i64 {
118 let ysin: i64 = sinT[bb_wrap(yaw)]
119 let ycos: i64 = sinT[bb_wrap(yaw+90)]
120 // pass 0: scale-invariant working precision
121 var q0mn: i64 = BB_BIG; var q0mx: i64 = 0-BB_BIG
122 var t0: i64 = 0
123 while t0 < ntris {
124 let o0: i64 = triBase + t0*84
125 var c0: i64 = 0
126 while c0 < 3 { let vq: i64 = bb_f32mul(mb, o0 + c0*4, BB_POSQ0); if vq<q0mn {q0mn=vq} if vq>q0mx {q0mx=vq} c0 = c0+1 }
127 t0 = t0 + 1
128 }
129 var span0: i64 = q0mx - q0mn
130 if span0 < 1 { span0 = 1 }
131 var posq: i64 = BB_POSQ0 * BB_TARGET / span0
132 if posq < 1 { posq = 1 }
133 // pass 1: full AABB -> centroid + halfH
134 var mnx: i64 = BB_BIG; var mny: i64 = BB_BIG; var mnz: i64 = BB_BIG
135 var mxx: i64 = 0-BB_BIG; var mxy: i64 = 0-BB_BIG; var mxz: i64 = 0-BB_BIG
136 var t: i64 = 0
137 while t < ntris {
138 var v: i64 = 0
139 while v < 3 {
140 let o: i64 = triBase + t*84 + v*12
141 let x: i64 = bb_f32mul(mb,o,posq); let y: i64 = bb_f32mul(mb,o+4,posq); let z: i64 = bb_f32mul(mb,o+8,posq)
142 if x<mnx {mnx=x} if x>mxx {mxx=x} if y<mny {mny=y} if y>mxy {mxy=y} if z<mnz {mnz=z} if z>mxz {mxz=z}
143 v = v + 1
144 }
145 t = t + 1
146 }
147 let cx0: i64 = (mnx+mxx)/2; let cy0: i64 = (mny+mxy)/2; let cz0: i64 = (mnz+mxz)/2
148 let halfH: i64 = (mxy-mny)/2
149 let FOCAL: i64 = H
150 var dist: i64 = 2*halfH*118/100 // 'fit': height-only framing
151 if dist < 1 { dist = 1 }
152 let cxh: i64 = W/2; let cyh: i64 = H/2
153 // clear buffers
154 let npx: i64 = W*H
155 let zb: *i64 = sys_mmap(npx*8) as *i64
156 var p: i64 = 0
157 while p < npx { cov[p]=0; zb[p]=BB_ZFAR; nbx[p]=0; nby[p]=0; nbz[p]=BB_Q14; p = p+1 }
158 // pass 2: rasterize (measurement only)
159 var cnt: i64 = 0
160 t = 0
161 while t < ntris {
162 let ob: i64 = triBase + t*84
163 let rx0v: i64 = bb_f32mul(mb,ob,posq)-cx0; let y0: i64 = bb_f32mul(mb,ob+4,posq)-cy0; let rz0v: i64 = bb_f32mul(mb,ob+8,posq)-cz0
164 let rx1v: i64 = bb_f32mul(mb,ob+12,posq)-cx0; let y1: i64 = bb_f32mul(mb,ob+16,posq)-cy0; let rz1v: i64 = bb_f32mul(mb,ob+20,posq)-cz0
165 let rx2v: i64 = bb_f32mul(mb,ob+24,posq)-cx0; let y2: i64 = bb_f32mul(mb,ob+28,posq)-cy0; let rz2v: i64 = bb_f32mul(mb,ob+32,posq)-cz0
166 let x0: i64 = (rx0v*ycos + rz0v*ysin)/BB_Q14; let z0: i64 = (rz0v*ycos - rx0v*ysin)/BB_Q14
167 let x1: i64 = (rx1v*ycos + rz1v*ysin)/BB_Q14; let z1: i64 = (rz1v*ycos - rx1v*ysin)/BB_Q14
168 let x2: i64 = (rx2v*ycos + rz2v*ysin)/BB_Q14; let z2: i64 = (rz2v*ycos - rx2v*ysin)/BB_Q14
169 let d0: i64 = dist - z0; let d1: i64 = dist - z1; let d2: i64 = dist - z2
170 if d0 > 0 { if d1 > 0 { if d2 > 0 {
171 let sx0: i64 = cxh + (x0*FOCAL)/d0; let sy0: i64 = cyh - (y0*FOCAL)/d0
172 let sx1: i64 = cxh + (x1*FOCAL)/d1; let sy1: i64 = cyh - (y1*FOCAL)/d1
173 let sx2: i64 = cxh + (x2*FOCAL)/d2; let sy2: i64 = cyh - (y2*FOCAL)/d2
174 var area: i64 = (sx1-sx0)*(sy2-sy0) - (sx2-sx0)*(sy1-sy0)
175 if area != 0 {
176 let q0x: i64 = bb_f32mul(mb,ob+36,BB_Q14); let n0y: i64 = bb_f32mul(mb,ob+40,BB_Q14); let q0z: i64 = bb_f32mul(mb,ob+44,BB_Q14)
177 let q1x: i64 = bb_f32mul(mb,ob+48,BB_Q14); let n1y: i64 = bb_f32mul(mb,ob+52,BB_Q14); let q1z: i64 = bb_f32mul(mb,ob+56,BB_Q14)
178 let q2x: i64 = bb_f32mul(mb,ob+60,BB_Q14); let n2y: i64 = bb_f32mul(mb,ob+64,BB_Q14); let q2z: i64 = bb_f32mul(mb,ob+68,BB_Q14)
179 let n0x: i64 = (q0x*ycos + q0z*ysin)/BB_Q14; let n0z: i64 = (q0z*ycos - q0x*ysin)/BB_Q14
180 let n1x: i64 = (q1x*ycos + q1z*ysin)/BB_Q14; let n1z: i64 = (q1z*ycos - q1x*ysin)/BB_Q14
181 let n2x: i64 = (q2x*ycos + q2z*ysin)/BB_Q14; let n2z: i64 = (q2z*ycos - q2x*ysin)/BB_Q14
182 var bxmn: i64 = bb_max(0, bb_min(sx0, bb_min(sx1,sx2)))
183 var bxmx: i64 = bb_min(W-1, bb_max(sx0, bb_max(sx1,sx2)))
184 var bymn: i64 = bb_max(0, bb_min(sy0, bb_min(sy1,sy2)))
185 var bymx: i64 = bb_min(H-1, bb_max(sy0, bb_max(sy1,sy2)))
186 var sgn: i64 = 1
187 if area < 0 { sgn = 0-1 }
188 let aabs: i64 = area*sgn
189 var py2: i64 = bymn
190 while py2 <= bymx {
191 var px2: i64 = bxmn
192 while px2 <= bxmx {
193 let e0: i64 = ((sx2-sx1)*(py2-sy1) - (sy2-sy1)*(px2-sx1))*sgn
194 let e1: i64 = ((sx0-sx2)*(py2-sy2) - (sy0-sy2)*(px2-sx2))*sgn
195 let e2: i64 = ((sx1-sx0)*(py2-sy0) - (sy1-sy0)*(px2-sx0))*sgn
196 if e0 >= 0 { if e1 >= 0 { if e2 >= 0 {
197 let depth: i64 = (e0*d0 + e1*d1 + e2*d2)/aabs
198 let idx: i64 = py2*W + px2
199 if depth < zb[idx] {
200 var nx: i64 = (e0*n0x + e1*n1x + e2*n2x)/aabs
201 var ny: i64 = (e0*n0y + e1*n1y + e2*n2y)/aabs
202 var nz: i64 = (e0*n0z + e1*n1z + e2*n2z)/aabs
203 var nl: i64 = bb_isqrt(nx*nx + ny*ny + nz*nz)
204 if nl < 1 { nl = 1 }
205 nx = nx*BB_Q14/nl; ny = ny*BB_Q14/nl; nz = nz*BB_Q14/nl
206 if nz < 0 { nx = 0-nx; ny = 0-ny; nz = 0-nz }
207 if cov[idx] == 0 { cnt = cnt + 1 }
208 zb[idx] = depth; cov[idx] = 1; nbx[idx] = nx; nby[idx] = ny; nbz[idx] = nz
209 }
210 }}}
211 px2 = px2 + 1
212 }
213 py2 = py2 + 1
214 }
215 }
216 }}}
217 t = t + 1
218 }
219 return cnt
220}
221
222// ============================ Judge ============================
223const J_SILH: i64 = 1
224const J_NORMAL: i64 = 2
225// Polymorphic entry. Returns a permil (0..1000). Extend by adding a KIND and a branch.
226func judge_eval(kind: i64, npx: i64, covA: *i64, covB: *i64,
227 nax: *i64, nay: *i64, naz: *i64, nbx: *i64, nby: *i64, nbz: *i64, acosT: *i64) -> i64 {
228 var p: i64 = 0
229 if kind == J_SILH {
230 var inter: i64 = 0; var uni: i64 = 0
231 while p < npx {
232 let a: i64 = covA[p]; let b: i64 = covB[p]
233 if a==1 { if b==1 { inter = inter+1 } }
234 if a==1 { uni = uni+1 } else { if b==1 { uni = uni+1 } }
235 p = p + 1
236 }
237 if uni < 1 { uni = 1 }
238 return 1000*inter/uni
239 }
240 if kind == J_NORMAL {
241 var sumdeg: i64 = 0; var cnt: i64 = 0
242 while p < npx {
243 if covA[p]==1 { if covB[p]==1 {
244 let dotq: i64 = (nax[p]*nbx[p] + nay[p]*nby[p] + naz[p]*nbz[p])/BB_Q14
245 sumdeg = sumdeg + bb_acos_deg(acosT, dotq)
246 cnt = cnt + 1
247 }}
248 p = p + 1
249 }
250 if cnt < 1 { cnt = 1 }
251 let meandeg: i64 = sumdeg/cnt
252 var permil: i64 = 1000 - meandeg*1000/90 // matches the laptop ruler: 1 - meanDeg/90
253 if permil < 0 { permil = 0 }
254 return permil
255 }
256 return 0
257}
258
259// ★DETAIL JUDGE (surface-structure agreement). Silhouette IoU and front-view normal agreement are both
260// BLIND to missing surface detail: a smooth mannequin overlaps a real human's outline ~85% and its
261// mostly-camera-facing normals agree ~93%, so those judges cannot see that the face, hands, feet and muscle
262// definition are ABSENT. This fills a per-pixel LOCAL NORMAL VARIANCE (how busy the surface is in a small
263// window) -- high where anatomy creates normal swings (nostril, knuckle, muscle border), ~0 on a smooth tube.
264func bb_detail_fill(W: i64, H: i64, cov: *i64, nx: *i64, ny: *i64, nz: *i64, out: *i64) -> i64 {
265 var y: i64 = 0
266 while y < H {
267 var x: i64 = 0
268 while x < W {
269 let p: i64 = y*W + x
270 if cov[p] == 1 {
271 var sx: i64 = 0; var sy: i64 = 0; var sz: i64 = 0
272 var qx: i64 = 0; var qy: i64 = 0; var qz: i64 = 0; var c: i64 = 0
273 var dy: i64 = 0-BB_DETR
274 while dy <= BB_DETR {
275 var dx: i64 = 0-BB_DETR
276 while dx <= BB_DETR {
277 let yy: i64 = y+dy; let xx: i64 = x+dx
278 if yy>=0 { if yy<H { if xx>=0 { if xx<W {
279 let q: i64 = yy*W+xx
280 if cov[q]==1 {
281 let ax: i64 = nx[q]; let ay: i64 = ny[q]; let az: i64 = nz[q]
282 sx=sx+ax; sy=sy+ay; sz=sz+az
283 qx=qx+ax*ax/BB_Q14; qy=qy+ay*ay/BB_Q14; qz=qz+az*az/BB_Q14
284 c=c+1
285 }
286 }}}}
287 dx=dx+1
288 }
289 dy=dy+1
290 }
291 if c < 2 { out[p]=0 } else {
292 let vx: i64 = qx/c - (sx/c)*(sx/c)/BB_Q14 // var = E[n^2]-E[n]^2, n^2 pre-scaled by Q14
293 let vy: i64 = qy/c - (sy/c)*(sy/c)/BB_Q14
294 let vz: i64 = qz/c - (sz/c)*(sz/c)/BB_Q14
295 var v: i64 = vx+vy+vz
296 if v < 0 { v = 0 }
297 out[p] = v
298 }
299 } else { out[p]=0 }
300 x=x+1
301 }
302 y=y+1
303 }
304 return 0
305}
306// score the two detail fields as an intersection-over-union in VARIANCE space over the shared coverage:
307// identical detail -> 1000; smooth-where-oracle-is-detailed -> punished; AND noisy-where-oracle-is-smooth ->
308// punished symmetrically, so it CANNOT be gamed by adding noise (the FBM-texture Goodhart that once fooled
309// the statistical grader scores ZERO here unless the noise lands exactly where the oracle has real detail).
310func bb_detail_score(npx: i64, covA: *i64, covB: *i64, dA: *i64, dB: *i64) -> i64 {
311 var num: i64 = 0; var den: i64 = 0; var p: i64 = 0
312 while p < npx {
313 if covA[p]==1 { if covB[p]==1 {
314 var df: i64 = dA[p]-dB[p]; if df<0 { df=0-df }
315 num = num + df
316 den = den + dA[p] + dB[p]
317 }}
318 p = p + 1
319 }
320 if den < 1 { den = 1 }
321 var s: i64 = 1000 - 1000*num/den
322 if s < 0 { s = 0 }
323 return s
324}
325
326// HEAD-REGION DETAIL. A whole-body average cannot see a head-sized change: four new facial bases moved the
327// body number by -1 while the face visibly gained shadowed eye sockets. This restricts the detail score to
328// the top band of the rendered figure.
329// NOTE ON THE STANDING LAW "a horizontal image band is not an anatomical region": that law was earned on
330// MID-body bands, where a row merely CONTAINS the arms and hands. It does not apply to the top band -- at
331// the crown of a standing figure nothing but the head is present, so here the band IS the region. Stated
332// explicitly because the general law is right and this is a genuine exception, not an oversight.
333func bb_detail_head(W: i64, H: i64, covA: *i64, covB: *i64, dA: *i64, dB: *i64, frac: i64) -> i64 {
334 var ymin: i64 = 0-1
335 var ymax: i64 = 0-1
336 var y: i64 = 0
337 while y < H {
338 var x: i64 = 0
339 var any: i64 = 0
340 while x < W { let p: i64 = y*W+x; if covA[p]==1 { any=1 } else { if covB[p]==1 { any=1 } } x=x+1 }
341 if any == 1 { if ymin < 0 { ymin = y } ymax = y }
342 y = y+1
343 }
344 if ymin < 0 { return 0 }
345 let ycut: i64 = ymin + (ymax-ymin)*frac/1000
346 var num: i64 = 0
347 var den: i64 = 0
348 y = ymin
349 while y <= ycut {
350 var x2: i64 = 0
351 while x2 < W {
352 let p2: i64 = y*W+x2
353 if covA[p2]==1 { if covB[p2]==1 {
354 var df: i64 = dA[p2]-dB[p2]
355 if df < 0 { df = 0-df }
356 num = num + df
357 den = den + dA[p2] + dB[p2]
358 }}
359 x2 = x2+1
360 }
361 y = y+1
362 }
363 if den < 1 { den = 1 }
364 var sc: i64 = 1000 - 1000*num/den
365 if sc < 0 { sc = 0 }
366 return sc
367}
368func bb_emit_kv(name: *u8, v: i64, last: i64) -> i64 {
369 bb_hw("\x22" as *u8); bb_hw(name); bb_hw("\x22:" as *u8); bb_pn(v)
370 if last == 0 { bb_hw("," as *u8) }
371 return 0
372}
373
374func main(argc: i64, argv: *i64) -> i64 {
375 if argc < 3 { bb_hw("{\x22error\x22:\x22usage: nx_bodybench <ours.nxmesh> <oracle.nxmesh> [W] [H]\x22}\n" as *u8); return 2 }
376 let oursp: *u8 = argv[1] as *u8
377 let oracp: *u8 = argv[2] as *u8
378 var W: i64 = 620
379 var H: i64 = 950
380 if argc > 3 { W = bb_satoi(argv[3] as *u8) }
381 if argc > 4 { H = bb_satoi(argv[4] as *u8) }
382 let sinT: *i64 = sys_mmap(400*8) as *i64
383 bb_sin_fill(sinT)
384 let acosT: *i64 = sys_mmap(BB_ACOSN*8) as *i64
385 bb_acos_fill(acosT, sinT)
386
387 let l1: *i64 = sys_mmap(16) as *i64
388 let l2: *i64 = sys_mmap(16) as *i64
389 let mo: *u8 = sys_read_file(oursp, l1)
390 let mr: *u8 = sys_read_file(oracp, l2)
391 if (mo as i64) == 0 { bb_hw("{\x22error\x22:\x22cannot read ours\x22}\n" as *u8); return 3 }
392 if (mr as i64) == 0 { bb_hw("{\x22error\x22:\x22cannot read oracle\x22}\n" as *u8); return 3 }
393 let oNt: i64 = bb_rdbits(mo, 12); let oNl: i64 = bb_rdbits(mo, 8)
394 let rNt: i64 = bb_rdbits(mr, 12); let rNl: i64 = bb_rdbits(mr, 8)
395 let oTB: i64 = 16 + oNl*24; let oLB: i64 = oTB + oNt*84
396 let rTB: i64 = 16 + rNl*24; let rLB: i64 = rTB + rNt*84
397
398 let npx: i64 = W*H
399 // four coverage buffers (ours/oracle at yaw 0 and 90) + normals for the yaw-0 pair
400 let coA0: *i64 = sys_mmap(npx*8) as *i64
401 let coB0: *i64 = sys_mmap(npx*8) as *i64
402 let coA9: *i64 = sys_mmap(npx*8) as *i64
403 let coB9: *i64 = sys_mmap(npx*8) as *i64
404 let axn: *i64 = sys_mmap(npx*8) as *i64; let ayn: *i64 = sys_mmap(npx*8) as *i64; let azn: *i64 = sys_mmap(npx*8) as *i64
405 let bxn: *i64 = sys_mmap(npx*8) as *i64; let byn: *i64 = sys_mmap(npx*8) as *i64; let bzn: *i64 = sys_mmap(npx*8) as *i64
406 // scratch normals for the yaw-90 renders (silhouette only needs coverage)
407 let sxn: *i64 = sys_mmap(npx*8) as *i64; let syn: *i64 = sys_mmap(npx*8) as *i64; let szn: *i64 = sys_mmap(npx*8) as *i64
408
409 mv_render(mo, oNt, oTB, oLB, W, H, 0, sinT, coA0, axn, ayn, azn)
410 mv_render(mr, rNt, rTB, rLB, W, H, 0, sinT, coB0, bxn, byn, bzn)
411 mv_render(mo, oNt, oTB, oLB, W, H, 90, sinT, coA9, sxn, syn, szn)
412 mv_render(mr, rNt, rTB, rLB, W, H, 90, sinT, coB9, sxn, syn, szn)
413
414 let front: i64 = judge_eval(J_SILH, npx, coA0, coB0, axn,ayn,azn, bxn,byn,bzn, acosT)
415 let side: i64 = judge_eval(J_SILH, npx, coA9, coB9, axn,ayn,azn, bxn,byn,bzn, acosT)
416 let shape: i64 = judge_eval(J_NORMAL, npx, coA0, coB0, axn,ayn,azn, bxn,byn,bzn, acosT)
417 // ★DETAIL judge -- MULTI-VIEW so it credits the WHOLE surface, not just the front. Front-only detail was
418 // blind to back muscles and let a body with a defined front but a smooth back read the same as a fully
419 // detailed one. Detail is now the mean of FRONT (yaw 0) and BACK (yaw 180) surface-structure agreement.
420 let dA: *i64 = sys_mmap(npx*8) as *i64
421 let dB: *i64 = sys_mmap(npx*8) as *i64
422 bb_detail_fill(W, H, coA0, axn, ayn, azn, dA)
423 bb_detail_fill(W, H, coB0, bxn, byn, bzn, dB)
424 let detail_front: i64 = bb_detail_score(npx, coA0, coB0, dA, dB)
425 // head is roughly the top 13 percent of a standing figure (1 of 7.5 heads)
426 let detail_head: i64 = bb_detail_head(W, H, coA0, coB0, dA, dB, 130)
427
428 // self-checks use the FRONT normals -- compute them BEFORE the back render overwrites axn/bxn.
429 let self_iou: i64 = judge_eval(J_SILH, npx, coA0, coA0, axn,ayn,azn, axn,ayn,azn, acosT)
430 let self_shp: i64 = judge_eval(J_NORMAL, npx, coA0, coA0, axn,ayn,azn, axn,ayn,azn, acosT)
431 let self_det: i64 = bb_detail_score(npx, coA0, coA0, dA, dA)
432
433 // BACK view (yaw 180): reuse the normal buffers now that the front judges are done.
434 let coA18: *i64 = sys_mmap(npx*8) as *i64
435 let coB18: *i64 = sys_mmap(npx*8) as *i64
436 mv_render(mo, oNt, oTB, oLB, W, H, 180, sinT, coA18, axn, ayn, azn)
437 mv_render(mr, rNt, rTB, rLB, W, H, 180, sinT, coB18, bxn, byn, bzn)
438 let dA2: *i64 = sys_mmap(npx*8) as *i64
439 let dB2: *i64 = sys_mmap(npx*8) as *i64
440 bb_detail_fill(W, H, coA18, axn, ayn, azn, dA2)
441 bb_detail_fill(W, H, coB18, bxn, byn, bzn, dB2)
442 let detail_back: i64 = bb_detail_score(npx, coA18, coB18, dA2, dB2)
443 let detail: i64 = (detail_front + detail_back)/2
444 // THREE-QUARTER VIEW (yaw 45). A front view is structurally blind to LATERAL surface: anatomically
445 // correct ribs are zero at the sternum and live on the flank, so front-only detail scored them DOWN
446 // (381 -> 378) for being right. Judging structure with a camera that cannot see it is a broken
447 // instrument, not a failed feature. ADDITIVE field -- the headline is untouched so every number
448 // published before this stays comparable.
449 let coA45: *i64 = sys_mmap(npx*8) as *i64
450 let coB45: *i64 = sys_mmap(npx*8) as *i64
451 mv_render(mo, oNt, oTB, oLB, W, H, 45, sinT, coA45, axn, ayn, azn)
452 mv_render(mr, rNt, rTB, rLB, W, H, 45, sinT, coB45, bxn, byn, bzn)
453 let dA3: *i64 = sys_mmap(npx*8) as *i64
454 let dB3: *i64 = sys_mmap(npx*8) as *i64
455 bb_detail_fill(W, H, coA45, axn, ayn, azn, dA3)
456 bb_detail_fill(W, H, coB45, bxn, byn, bzn, dB3)
457 let detail_q: i64 = bb_detail_score(npx, coA45, coB45, dA3, dB3)
458 let quarter_iou: i64 = judge_eval(J_SILH, npx, coA45, coB45, axn,ayn,azn, bxn,byn,bzn, acosT)
459
460 // HONEST HEADLINE = MIN over ALL judges, detail included. Silhouette/shape flatter a smooth mannequin;
461 // detail is what drags the number down to what the eye actually sees.
462 var headline: i64 = bb_min(front, bb_min(side, bb_min(shape, detail)))
463
464 bb_hw("{" as *u8)
465 bb_emit_kv("front_iou" as *u8, front, 0)
466 bb_emit_kv("side_iou" as *u8, side, 0)
467 bb_emit_kv("shape" as *u8, shape, 0)
468 bb_emit_kv("detail" as *u8, detail, 0)
469 bb_emit_kv("detail_front" as *u8, detail_front, 0)
470 bb_emit_kv("detail_back" as *u8, detail_back, 0)
471 bb_emit_kv("detail_quarter" as *u8, detail_q, 0)
472 bb_emit_kv("detail_head" as *u8, detail_head, 0)
473 bb_emit_kv("quarter_iou" as *u8, quarter_iou, 0)
474 bb_emit_kv("headline" as *u8, headline, 0)
475 bb_emit_kv("self_iou" as *u8, self_iou, 0)
476 bb_emit_kv("self_shape" as *u8, self_shp, 0)
477 bb_emit_kv("self_detail" as *u8, self_det, 0)
478 bb_emit_kv("W" as *u8, W, 0)
479 bb_emit_kv("H" as *u8, H, 0)
480 bb_hw("\x22judges\x22:\x22front_iou,side_iou,shape,DETAIL=mean(front,back) (MIN=headline); detail = local-normal-variance IoU vs oracle, NOT blind to a smooth mannequin\x22,\x22ruler\x22:\x22sovereign nx_bodybench -- dual-render coverage+normal, height-fit framing, integer acos LUT; NO image round-trip, NO 3rd-party\x22}\n" as *u8)
481 return 0
482}