nx_critvis_lib.nx source
↩ module page · 86 lines · 3761 B
1// nx_critvis_lib.nx -- IMPORTABLE photoreal-critic scoring axes, extracted verbatim from the gate
2// _hdl_build/nx_critic_visual.nx (which holds a main() and so cannot be imported). Same axes, same scaling,
3// same names -> a future dedupe can swap the gate's local copies for this import with zero behavior change.
4// Axes: fine_detail (1px micro-contrast over foreground) + STRUCTURE (8x8 block-mean variance; rejects noise)
5// + chroma MAD; score = MIN of scaled axes, /1000; verdict PHOTOREAL>=700 / STYLIZED-CG>=400 / CLAY.
6// license_tier: ORIGINAL
7import "nx_syscalls.nx"
8
9func cv_lum(g: i64) -> i64 { return ((g & 255) + ((g >> 8) & 255) + ((g >> 16) & 255)) / 3 }
10func cv_pl(g: i64) -> i64 { let r: i64 = g & 255; let b: i64 = (g >> 16) & 255; if r >= b { return 1 } return 0 }
11func cv_chroma(g: i64) -> i64 {
12 let r: i64 = g & 255; let gg: i64 = (g >> 8) & 255; let b: i64 = (g >> 16) & 255
13 var mx: i64 = r; if gg > mx { mx = gg } if b > mx { mx = b }
14 var mn: i64 = r; if gg < mn { mn = gg } if b < mn { mn = b }
15 return mx - mn
16}
17func cv_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v }
18func cv_clamp(v: i64) -> i64 { if v < 0 { return 0 } if v > 1000 { return 1000 } return v }
19
20func ax_fine_detail(fb: *i64, w: i64, h: i64) -> i64 {
21 var fg: i64 = 0; var s: i64 = 0
22 var y: i64 = 1
23 while y < h - 1 {
24 var x: i64 = 1
25 while x < w - 1 {
26 let c: i64 = fb[y * w + x]
27 if cv_pl(c) == 1 {
28 let nb: i64 = (cv_lum(fb[y * w + x - 1]) + cv_lum(fb[y * w + x + 1]) + cv_lum(fb[(y - 1) * w + x]) + cv_lum(fb[(y + 1) * w + x])) / 4
29 s = s + cv_abs(cv_lum(c) - nb); fg = fg + 1
30 }
31 x = x + 1
32 }
33 y = y + 1
34 }
35 if fg < 1 { fg = 1 }
36 return s / fg
37}
38func ax_structure(fb: *i64, w: i64, h: i64) -> i64 {
39 let G: i64 = 8
40 let means: *i64 = sys_mmap(G * G * 8) as *i64
41 var gy: i64 = 0
42 while gy < G {
43 var gx: i64 = 0
44 while gx < G {
45 let x0: i64 = gx * w / G; let x1: i64 = (gx + 1) * w / G
46 let y0: i64 = gy * h / G; let y1: i64 = (gy + 1) * h / G
47 var sum: i64 = 0; var cnt: i64 = 0
48 var y: i64 = y0
49 while y < y1 { var x: i64 = x0; while x < x1 { sum = sum + cv_lum(fb[y * w + x]); cnt = cnt + 1; x = x + 1 } y = y + 1 }
50 if cnt < 1 { cnt = 1 }
51 means[gy * G + gx] = sum / cnt
52 gx = gx + 1
53 }
54 gy = gy + 1
55 }
56 var mean: i64 = 0; var i: i64 = 0
57 while i < G * G { mean = mean + means[i]; i = i + 1 }
58 mean = mean / (G * G)
59 var vsum: i64 = 0; i = 0
60 while i < G * G { let d: i64 = means[i] - mean; vsum = vsum + d * d; i = i + 1 }
61 return vsum / (G * G)
62}
63func ax_chroma(fb: *i64, w: i64, h: i64) -> i64 {
64 var fg: i64 = 0; var s: i64 = 0
65 var y: i64 = 0
66 while y < h { var x: i64 = 0; while x < w { let c: i64 = fb[y * w + x]; if cv_pl(c) == 1 { s = s + cv_chroma(c); fg = fg + 1 } x = x + 1 } y = y + 1 }
67 if fg < 1 { fg = 1 }
68 let cm: i64 = s / fg
69 var mad: i64 = 0; y = 0
70 while y < h { var x: i64 = 0; while x < w { let c: i64 = fb[y * w + x]; if cv_pl(c) == 1 { mad = mad + cv_abs(cv_chroma(c) - cm) } x = x + 1 } y = y + 1 }
71 return mad / fg
72}
73func cv_score(fb: *i64, w: i64, h: i64) -> i64 {
74 let d: i64 = cv_clamp(ax_fine_detail(fb, w, h) * 1000 / 32)
75 let st: i64 = cv_clamp(ax_structure(fb, w, h) * 1000 / 45)
76 let ch: i64 = cv_clamp(ax_chroma(fb, w, h) * 1000 / 16)
77 var mn: i64 = d
78 if st < mn { mn = st }
79 if ch < mn { mn = ch }
80 return mn
81}
82func cv_verdict(s: i64) -> *u8 {
83 if s >= 700 { return "PHOTOREAL" as *u8 }
84 if s >= 400 { return "STYLIZED-CG" as *u8 }
85 return "CLAY / not-a-person" as *u8
86}