code wiki / _hdl_build / nx_vcodec_deblock_gate.nx

nx_vcodec_deblock_gate.nx source

↩ module page · 99 lines · 6078 B

1import "nx_gate_base.nx" 2// nx_vcodec_deblock_gate.nx -- the in-loop DEBLOCK safety + quality gate. A deblocking filter is IN-LOOP: 3// it rewrites the recon that becomes the next frame's prev. If the encoder's deblock and the decoder's 4// deblock ever disagree by one bit, the P-reference diverges and the picture rots over seconds. So the 5// CARDINAL test: run a real P-sequence through vv_enc + vv_dec (both deblock internally) and assert the 6// decoder's recon == the encoder's recon on EVERY frame. Then confirm the filter actually REDUCES 7// block-edge energy (the point) without wrecking fidelity (MSE). license_tier: ORIGINAL 8import "nx_syscalls.nx" 9import "nx_video_codec_wasm.nx" 10 11func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw(" 12" as *u8); return ok } 13func gn(v: i64) -> i64 { 14 let b: *u8=sys_mmap(28); var m: i64=v; if m<0{sys_write(1,"-" as *u8,1);m=0-m} 15 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} 16 var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 } 17func ck(name: *u8, ok: i64, p: *i64, t: *i64) -> i64 { 18 t[0]=t[0]+1; gw(" " as *u8); gw(name); if ok==1 { gw(" PASS\n" as *u8); p[0]=p[0]+1 } else { gw(" FAIL\n" as *u8) } return 0 } 19func absi(x: i64) -> i64 { if x<0 { return 0-x } return x } 20func blockiness(p: *u8, W: i64, H: i64) -> i64 { 21 var bs: i64=0; var bn: i64=0; var isum: i64=0; var iN: i64=0; var y: i64=0 22 while y<H { var x: i64=1 23 while x<W { let d: i64=absi((p[y*W+x]&0xff)-(p[y*W+x-1]&0xff)) 24 if (x&3)==0 { bs=bs+d; bn=bn+1 } else { isum=isum+d; iN=iN+1 } x=x+1 } y=y+1 } 25 if iN==0 { return 0 } if isum==0 { return 999 } return (bs*iN*100)/(isum*bn) } 26func mse(a: *u8, b: *u8, N: i64) -> i64 { 27 var se: i64=0; var i: i64=0; while i<N { let d: i64=(a[i]&0xff)-(b[i]&0xff); se=se+d*d; i=i+1 } return se/N } 28 29// fill a detailed frame; shift = horizontal pan (fakes motion for P-frames) 30func fill(yuv: *u8, W: i64, H: i64, shift: i64) -> i64 { 31 let N: i64=W*H; let C: i64=(W/2)*(H/2) 32 var y: i64=0 33 while y<H { var x: i64=0 34 while x<W { let sx: i64=x+shift 35 var v: i64=(sx*255)/W + (((sx/2+y/3)%7)*20-60) 36 if sx>W/2 { v=v+45 } 37 if v<0 {v=0} if v>255 {v=255} 38 yuv[y*W+x]=v as u8; x=x+1 } y=y+1 } 39 var i: i64=N; while i<N+C { yuv[i]=112 as u8; i=i+1 } 40 while i<N+2*C { yuv[i]=138 as u8; i=i+1 } 41 return 0 } 42 43func main() -> i64 { 44 gw("=== nx_vcodec_deblock_gate: in-loop deblock -- bit-exact P-chain + blockiness ===\n" as *u8) 45 let p: *i64=sys_mmap(16) as *i64; let t: *i64=sys_mmap(16) as *i64 46 let W: i64=256; let H: i64=192; let q: i64=24 47 let N: i64=W*H; let C: i64=(W/2)*(H/2); let sz: i64=N+2*C 48 let cur: *u8=sys_mmap(sz) 49 var prevE: *u8=sys_mmap(sz); var reconE: *u8=sys_mmap(sz) 50 var prevD: *u8=sys_mmap(sz); var reconD: *u8=sys_mmap(sz) 51 let stream: *u8=sys_mmap(262144) 52 let blk: *i64=sys_mmap(512) as *i64; let mv: *i64=sys_mmap(128) as *i64 53 var i: i64=0; while i<sz { prevE[i]=0 as u8; prevD[i]=0 as u8; i=i+1 } 54 55 var exact: i64=1 56 var bkLast: i64=0; var mseLast: i64=0 57 var fr: i64=0 58 while fr < 8 { 59 fill(cur, W, H, fr*3) // pan 3px/frame 60 let key: i64 = (fr==0) as i64 61 let kb: i64 = vv_enc(cur, prevE, reconE, W, H, q, key, 6016, stream, 262144, blk, mv) 62 if kb <= 0 { exact=0 } 63 vv_dec(prevD, reconD, W, H, q, stream, kb, blk, mv) 64 // CARDINAL: decoder recon must equal encoder recon (both deblocked), every byte, every frame 65 var j: i64=0 66 while j<sz { if (reconE[j]&0xff) != (reconD[j]&0xff) { exact=0; j=sz } else { j=j+1 } } 67 bkLast = blockiness(reconD, W, H); mseLast = mse(reconD, cur, N) 68 // ping-pong: this recon becomes next prev 69 let te: i64=prevE as i64; let se: i64=reconE as i64 70 prevE = se as *u8; reconE = te as *u8 71 let td: i64=prevD as i64; let sd: i64=reconD as i64 72 prevD = sd as *u8; reconD = td as *u8 73 fr = fr + 1 74 } 75 ck("bit-exact P-chain across 8 frames (enc recon == dec recon)\x00" as *u8, exact, p, t) 76 // HONEST A/B on ONE keyframe: decode the SAME stream two ways -- raw (no deblock) vs vv_dec (deblock). 77 fill(cur, W, H, 0) 78 let pz: *u8 = sys_mmap(sz); var z: i64=0; while z<sz { pz[z]=0 as u8; z=z+1 } 79 let kb2: i64 = vv_enc(cur, pz, reconE, W, H, q, 1, 6016, stream, 262144, blk, mv) 80 let reconRaw: *u8 = sys_mmap(sz) 81 let yb: i64 = vv_rd_u32b(stream, 0); let ub: i64 = vv_rd_u32b(stream, 4) 82 // raw arm MUST decode with the SAME transform vv_enc used (tf=1 DCT-II since build 781) -- the tf=0 83 // delegator here silently became a MISMATCHED decode when DCT went default, poisoning the A/B. 84 vc_dec_frame_packed_tf(pz, reconRaw, W, H, q, ((stream as i64)+12) as *u8, blk, mv, 1) 85 vc_dec_frame_packed_tf(((pz as i64)+N) as *u8, ((reconRaw as i64)+N) as *u8, W/2, H/2, q, ((stream as i64)+12+yb) as *u8, blk, mv, 1) 86 vc_dec_frame_packed_tf(((pz as i64)+N+C) as *u8, ((reconRaw as i64)+N+C) as *u8, W/2, H/2, q, ((stream as i64)+12+yb+ub) as *u8, blk, mv, 1) 87 let reconDB: *u8 = sys_mmap(sz) 88 vv_dec(pz, reconDB, W, H, q, stream, kb2, blk, mv) 89 let bkRaw: i64 = blockiness(reconRaw, W, H) 90 let bkDB: i64 = blockiness(reconDB, W, H) 91 let mseDB: i64 = mse(reconDB, cur, N) 92 gw(" keyframe blockiness: raw(no deblock)=" as *u8); gn(bkRaw); gw(" deblocked=" as *u8); gn(bkDB) 93 gw(" (-" as *u8); gn((bkRaw-bkDB)*100/bkRaw); gw("%) MSE(deblocked,orig)=" as *u8); gn(mseDB); gw("\n" as *u8) 94 ck("deblock REDUCES block-edge energy vs raw decode\x00" as *u8, (bkDB < bkRaw) as i64, p, t) 95 ck("fidelity preserved (MSE < 130 ~ >27dB, not over-smoothed)\x00" as *u8, (mseDB < 130) as i64, p, t) 96 97 gw("DEBLOCK-GATE: " as *u8); gn(p[0]); gw("/" as *u8); gn(t[0]) 98 if p[0]==t[0] { gw(" GREEN -- in-loop deblock is bit-exact + de-blocks + keeps detail\n" as *u8); return 0 } 99 gw(" RED\n" as *u8); return 1 }