code wiki / (root) / nx_critvis_lib.nx

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}