nx_critic_axes.nx source
↩ module page · 114 lines · 4319 B
1// nx_critic_axes.nx -- the SHARED measured-quality AXES (extracted so every loop/gate scores emitted output
2// with the SAME rulers as the critic -- one formula, many callers; nx_critic_visual/nx_photoreal_critic are
3// the calibration/epistemics home, this is the reusable measurement lib; consolidation: point them here later).
4// Axes: fine_detail (1px micro-contrast over foreground) · structure (8x8 block-mean variance -- a real object
5// has low-freq form, noise/flat do not) · chroma MAD. Overall = MIN axis (hard chain). license_tier: ORIGINAL
6import "nx_syscalls.nx"
7
8func ca_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v }
9func ca_clamp(v: i64) -> i64 { if v < 0 { return 0 } if v > 1000 { return 1000 } return v }
10func ca_lum(g: i64) -> i64 { return ((g & 255) + ((g >> 8) & 255) + ((g >> 16) & 255)) / 3 }
11func ca_fg(g: i64) -> i64 { let r: i64 = g & 255; let b: i64 = (g >> 16) & 255; if r >= b { return 1 } return 0 }
12func ca_chromaof(g: i64) -> i64 {
13 let r: i64 = g & 255
14 let gg: i64 = (g >> 8) & 255
15 let b: i64 = (g >> 16) & 255
16 var mx: i64 = r
17 if gg > mx { mx = gg }
18 if b > mx { mx = b }
19 var mn: i64 = r
20 if gg < mn { mn = gg }
21 if b < mn { mn = b }
22 return mx - mn
23}
24// foreground pixel count (is there content at all)
25func ca_fgcount(fb: *i64, w: i64, h: i64) -> i64 {
26 var n: i64 = 0
27 var i: i64 = 0
28 while i < w * h { if ca_fg(fb[i]) == 1 { n = n + 1 } i = i + 1 }
29 return n
30}
31// fine micro-contrast over foreground (raw; skin-pore target ~32)
32func ca_fine_detail(fb: *i64, w: i64, h: i64) -> i64 {
33 var fg: i64 = 0
34 var s: i64 = 0
35 var y: i64 = 1
36 while y < h - 1 {
37 var x: i64 = 1
38 while x < w - 1 {
39 let c: i64 = fb[y * w + x]
40 if ca_fg(c) == 1 {
41 let nb: i64 = (ca_lum(fb[y * w + x - 1]) + ca_lum(fb[y * w + x + 1]) + ca_lum(fb[(y - 1) * w + x]) + ca_lum(fb[(y + 1) * w + x])) / 4
42 s = s + ca_abs(ca_lum(c) - nb)
43 fg = fg + 1
44 }
45 x = x + 1
46 }
47 y = y + 1
48 }
49 if fg < 1 { fg = 1 }
50 return s / fg
51}
52// low-frequency STRUCTURE: variance of the 8x8 block-mean luminance grid (raw; object ~tens, noise/flat ~0)
53func ca_structure(fb: *i64, w: i64, h: i64) -> i64 {
54 let G: i64 = 8
55 let means: *i64 = sys_mmap(G * G * 8) as *i64
56 var gy: i64 = 0
57 while gy < G {
58 var gx: i64 = 0
59 while gx < G {
60 let x0: i64 = gx * w / G
61 let x1: i64 = (gx + 1) * w / G
62 let y0: i64 = gy * h / G
63 let y1: i64 = (gy + 1) * h / G
64 var sum: i64 = 0
65 var cnt: i64 = 0
66 var y: i64 = y0
67 while y < y1 {
68 var x: i64 = x0
69 while x < x1 { sum = sum + ca_lum(fb[y * w + x]); cnt = cnt + 1; x = x + 1 }
70 y = y + 1
71 }
72 if cnt < 1 { cnt = 1 }
73 means[gy * G + gx] = sum / cnt
74 gx = gx + 1
75 }
76 gy = gy + 1
77 }
78 var mean: i64 = 0
79 var i: i64 = 0
80 while i < G * G { mean = mean + means[i]; i = i + 1 }
81 mean = mean / (G * G)
82 var v: i64 = 0
83 i = 0
84 while i < G * G { let d: i64 = means[i] - mean; v = v + d * d; i = i + 1 }
85 return v / (G * G)
86}
87// chroma MAD over foreground (raw; skin variation target ~16)
88func ca_chroma_mad(fb: *i64, w: i64, h: i64) -> i64 {
89 var fg: i64 = 0
90 var s: i64 = 0
91 var i: i64 = 0
92 while i < w * h { if ca_fg(fb[i]) == 1 { s = s + ca_chromaof(fb[i]); fg = fg + 1 } i = i + 1 }
93 if fg < 1 { fg = 1 }
94 let cm: i64 = s / fg
95 var mad: i64 = 0
96 i = 0
97 while i < w * h { if ca_fg(fb[i]) == 1 { mad = mad + ca_abs(ca_chromaof(fb[i]) - cm) } i = i + 1 }
98 return mad / fg
99}
100// overall = MIN of the scaled axes (same hard-chain rule + scales as the critic)
101func ca_score(fb: *i64, w: i64, h: i64) -> i64 {
102 let d: i64 = ca_clamp(ca_fine_detail(fb, w, h) * 1000 / 32)
103 let st: i64 = ca_clamp(ca_structure(fb, w, h) * 1000 / 45)
104 let ch: i64 = ca_clamp(ca_chroma_mad(fb, w, h) * 1000 / 16)
105 var mn: i64 = d
106 if st < mn { mn = st }
107 if ch < mn { mn = ch }
108 return mn
109}
110func ca_verdict(s: i64) -> *u8 {
111 if s >= 700 { return "PHOTOREAL" as *u8 }
112 if s >= 400 { return "STYLIZED-CG" as *u8 }
113 return "CLAY" as *u8
114}