code wiki / _hdl_build / nx_vcodec_quality_probe.nx

nx_vcodec_quality_probe.nx source

↩ module page · 93 lines · 4647 B

1import "nx_gate_base.nx" 2// nx_vcodec_quality_probe.nx -- ground the quantizer choice with REAL PSNR. The live client clamped qp to 3// 24..48 (vt_quant does blk[i]/q -> q=24 is a very coarse deadzone -> the "garbage blocky" the operator saw). 4// This sweeps q on a DETAILED frame (gradient + high-freq texture + edges) at the production geometry and 5// prints keyframe PSNR + bytes, so the new qp floor is measured, not guessed. license_tier: ORIGINAL 6import "nx_syscalls.nx" 7import "nx_video_codec_wasm.nx" 8const K_MAGIC_9900: i64 = 9900 9const K_MAGIC_65025: i64 = 65025 10const K_MAGIC_262144: i64 = 262144 11const K_MAGIC_6016: i64 = 6016 12 13func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw(" 14" as *u8); return ok } 15func gn(v: i64) -> i64 { 16 let b: *u8=sys_mmap(28); var m: i64=v; if m<0{sys_write(1,"-" as *u8,1);m=0-m} 17 let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} 18 var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 } 19 20func absi(x: i64) -> i64 { if x < 0 { return 0 - x } return x } 21 22// blockiness = 100 * (avg |step| at 4-pixel block boundaries) / (avg |step| in the interior). A blocky 23// frame has big steps exactly at boundaries -> >>100. A deblocked/smooth frame -> near 100. 24func blockiness(p: *u8, W: i64, H: i64) -> i64 { 25 var bsum: i64 = 0; var bn: i64 = 0; var isum: i64 = 0; var iN: i64 = 0 26 var y: i64 = 0 27 while y < H { 28 var x: i64 = 1 29 while x < W { 30 let d: i64 = absi((p[y*W+x]&0xff) - (p[y*W+x-1]&0xff)) 31 if (x & 3) == 0 { bsum = bsum + d; bn = bn + 1 } else { isum = isum + d; iN = iN + 1 } 32 x = x + 1 33 } 34 y = y + 1 35 } 36 if iN == 0 { return 0 } if isum == 0 { return 999 } 37 return (bsum * iN * 100) / (isum * bn) } 38 39// PSNR*100 (dB) of Y plane, N pixels 40func psnr_y(a: *u8, b: *u8, N: i64) -> i64 { 41 var se: i64 = 0; var i: i64 = 0 42 while i < N { let d: i64 = (a[i]&0xff) - (b[i]&0xff); se = se + d*d; i = i + 1 } 43 if se == 0 { return K_MAGIC_9900 } 44 let mse_x100: i64 = (se * 100) / N 45 // 10*log10(255^2*100/mse_x100); crude integer log10 via repeated /10 on the ratio 46 let num: i64 = K_MAGIC_65025 * 100 47 var ratio: i64 = num / mse_x100 48 if ratio < 1 { ratio = 1 } 49 var db10: i64 = 0 // 10*log10(ratio) approx, in tenths 50 var r: i64 = ratio 51 while r >= 10 { db10 = db10 + 10; r = r / 10 } 52 // fractional part via linear interp between decades (r in [1,10)) 53 db10 = db10 + (r - 1) // ~ +0..9 tenths 54 return db10 * 100 } // -> dB*100-ish (coarse but monotonic for comparison) 55 56func probe(W: i64, H: i64, q: i64) -> i64 { 57 let N: i64 = W*H; let C: i64 = (W/2)*(H/2); let sz: i64 = N + 2*C 58 let yuv: *u8 = sys_mmap(sz) 59 var y: i64 = 0 60 while y < H { var x: i64 = 0 61 while x < W { 62 // gradient + high-frequency texture + a hard edge column = a stress pattern for blockiness 63 var v: i64 = (x*255)/W 64 v = v + (((x/2 + y/3) % 7) * 18) - 54 // texture 65 if x > W/2 { v = v + 40 } // edge 66 if v < 0 { v = 0 } if v > 255 { v = 255 } 67 yuv[y*W + x] = v as u8 68 x = x + 1 } y = y + 1 } 69 var i: i64 = N; while i < N+C { yuv[i]=110 as u8; i=i+1 } 70 while i < sz { yuv[i]=140 as u8; i=i+1 } 71 let prevE: *u8 = sys_mmap(sz); let reconE: *u8 = sys_mmap(sz) 72 let prevD: *u8 = sys_mmap(sz); let reconD: *u8 = sys_mmap(sz) 73 let stream: *u8 = sys_mmap(K_MAGIC_262144) 74 let blk: *i64 = sys_mmap(512) as *i64; let mv: *i64 = sys_mmap(128) as *i64 75 let kb: i64 = vv_enc(yuv, prevE, reconE, W, H, q, 1, K_MAGIC_6016, stream, K_MAGIC_262144, blk, mv) 76 vv_dec(prevD, reconD, W, H, q, stream, kb, blk, mv) 77 let db: i64 = psnr_y(reconD, yuv, N) 78 let bkIn: i64 = blockiness(yuv, W, H) 79 let bkOut: i64 = blockiness(reconD, W, H) 80 gw(" q=" as *u8); gn(q); gw(" PSNR~" as *u8); gn(db/100); gw("dB blockiness in=" as *u8); gn(bkIn) 81 gw(" out=" as *u8); gn(bkOut); gw(" key=" as *u8); gn(kb); gw("B\n" as *u8) 82 return 0 } 83 84func main() -> i64 { 85 gw("=== nx_vcodec_quality_probe: qp sweep @256x192 (detailed frame) ===\n" as *u8) 86 probe(256, 192, 6) 87 probe(256, 192, 10) 88 probe(256, 192, 16) 89 probe(256, 192, 24) // current live FLOOR (best case) -- the operator's 'garbage' 90 probe(256, 192, 32) 91 probe(256, 192, 48) // current live ceiling (worst case) 92 gw("(higher dB = sharper; the live floor was q=24 -> we can go much finer for good pipes)\n" as *u8) 93 return 0 }