code wiki / _hdl_build / nx_vcodec_cdef_gate.nx

nx_vcodec_cdef_gate.nx source

↩ module page · 217 lines · 10106 B

1import "nx_gate_base.nx" 2// nx_vcodec_cdef_gate.nx -- CDEF-class directional deringing CEILING PROBE (R1c; measure BEFORE any vcv build). 3// A post-frame in-loop filter applied IDENTICALLY to the encoder and decoder recon chains in this gate is exactly 4// equivalent to wiring it at the end of vv_enc/vv_dec (deblock position) -- so the probe needs ZERO codec edits. 5// CDEF-lite (AV1-inspired, integer, luma, conservative): per 8x8 block, detect the dominant of 4 directions 6// (H/V/45/135) by line-energy, then filter each pixel with constrained taps at +-1 (w4) and +-2 (w2) ALONG that 7// direction: y = x + (8 + sum(w*constrain(diff, S))) >> 4, constrain = sign-preserving clamp with damping >>2. 8// ⚠DEBLOCK LESSON APPLIED: blocks whose recon is UNCHANGED vs the reference (skipped/static) are NOT re-filtered 9// (AV1 exempts skip blocks too) -- prevents the accumulate-over-GOP over-smoothing that made the old strong 10// deblock a net loss. Chains: A = ship config (emode 681, gentle deblock) · B = A + CDEF S=qp/4 · C = A + CDEF 11// S=qp/2. Reports P-total bytes + luma MSE per qp; bit-exact of the base codec is unaffected (filter is outside 12// the wire). Real BD-rate only if this shows a direction. license: ORIGINAL 13import "nx_syscalls.nx" 14import "nx_video_codec_wasm.nx" 15 16const SW: i64 = 352 17const SH: i64 = 288 18const NF: i64 = 48 19 20func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw(" 21" as *u8); return ok } 22func gn(v: i64) -> i64 { 23 let b: *u8=sys_mmap(28); var m: i64=v; if m<0{sys_write(1,"-" as *u8,1);m=0-m} 24 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} 25 var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 } 26func cpb(d: *u8, s: *u8, n: i64) -> i64 { var i: i64=0; while i<n { d[i]=s[i]; i=i+1 } return 0 } 27 28func frames(yuv: *u8, hi: i64, total: i64, fp: *i64, fdata: i64) -> i64 { 29 var nf: i64=0; var p: i64=hi 30 while nf < NF { if p>=total { break } 31 var q: i64=p; while q<total { if (yuv[q]&0xff)==10 { break } q=q+1 } q=q+1 32 if q+fdata>total { break } fp[nf]=(yuv as i64)+q; nf=nf+1; p=q+fdata } 33 return nf } 34 35func cd_constrain(d: i64, S: i64) -> i64 { 36 var a: i64 = d 37 if a < 0 { a = 0 - a } 38 var t: i64 = S - (a >> 2) 39 if t < 0 { t = 0 } 40 if a < t { t = a } 41 if d < 0 { return 0 - t } 42 return t 43} 44// dominant direction of an 8x8 at (bx8,by8) in src: 0=H lines, 1=V, 2=45 (x+y const), 3=135 (x-y const). 45// cost(dir) = sum over lines of (linesum^2 / npix); max cost = strongest directional structure. 46func cd_dir(src: *u8, W: i64, x0: i64, y0: i64) -> i64 { 47 let ls: *i64 = sys_mmap(16*8) as *i64 48 let lc: *i64 = sys_mmap(16*8) as *i64 49 var best: i64 = 0 50 var bestc: i64 = 0 - 1 51 var dir: i64 = 0 52 while dir < 4 { 53 var k: i64 = 0 54 while k < 16 { ls[k] = 0; lc[k] = 0; k = k + 1 } 55 var y: i64 = 0 56 while y < 8 { 57 var x: i64 = 0 58 while x < 8 { 59 let p: i64 = src[(y0+y)*W + x0+x] as i64 60 var li: i64 = 0 61 if dir == 0 { li = y } 62 if dir == 1 { li = x } 63 if dir == 2 { li = x + y } 64 if dir == 3 { li = x - y + 7 } 65 ls[li] = ls[li] + p 66 lc[li] = lc[li] + 1 67 x = x + 1 } 68 y = y + 1 } 69 var cost: i64 = 0 70 k = 0 71 while k < 16 { if lc[k] > 0 { cost = cost + (ls[k]*ls[k]) / lc[k] } k = k + 1 } 72 if cost > bestc { bestc = cost; best = dir } 73 dir = dir + 1 74 } 75 return best 76} 77// filter one 8x8 in dst reading UNfiltered pixels from ref (full-plane copy), taps along dir. 78func cd_filt8(ref: *u8, dst: *u8, W: i64, H: i64, x0: i64, y0: i64, dir: i64, S: i64) -> i64 { 79 var dx: i64 = 1 80 var dy: i64 = 0 81 if dir == 1 { dx = 0; dy = 1 } 82 if dir == 2 { dx = 1; dy = 0 - 1 } 83 if dir == 3 { dx = 1; dy = 1 } 84 var y: i64 = 0 85 while y < 8 { 86 var x: i64 = 0 87 while x < 8 { 88 let px: i64 = x0 + x 89 let py: i64 = y0 + y 90 let c: i64 = ref[py*W + px] as i64 91 var sum: i64 = 0 92 var t: i64 = 0 - 2 93 while t <= 2 { 94 if t != 0 { 95 var qx: i64 = px + dx*t 96 var qy: i64 = py + dy*t 97 if qx < 0 { qx = 0 } 98 if qx >= W { qx = W - 1 } 99 if qy < 0 { qy = 0 } 100 if qy >= H { qy = H - 1 } 101 var w: i64 = 2 102 if t == 1 { w = 4 } 103 if t == 0 - 1 { w = 4 } 104 sum = sum + w * cd_constrain((ref[qy*W + qx] as i64) - c, S) 105 } 106 t = t + 1 107 } 108 var v: i64 = c + ((8 + sum) >> 4) 109 if v < 0 { v = 0 } 110 if v > 255 { v = 255 } 111 dst[py*W + px] = v as u8 112 x = x + 1 } 113 y = y + 1 } 114 return 0 115} 116// CDEF-lite pass over the LUMA plane: per 8x8, skip if recon block == refprev block (skipped/static -> no 117// re-filter accumulation), else direction-detect + filter. scratch = full-plane copy (filter reads unfiltered). 118func cd_pass(recon: *u8, refprev: *u8, W: i64, H: i64, S: i64, scratch: *u8) -> i64 { 119 cpb(scratch, recon, W*H) 120 var y0: i64 = 0 121 while y0 < H { 122 var x0: i64 = 0 123 while x0 < W { 124 var same: i64 = 1 125 var yy: i64 = 0 126 while yy < 8 { 127 var xx: i64 = 0 128 while xx < 8 { 129 if (recon[(y0+yy)*W + x0+xx] as i64) != (refprev[(y0+yy)*W + x0+xx] as i64) { same = 0; xx = 8; yy = 8 } else { xx = xx + 1 } 130 } 131 if yy < 8 { yy = yy + 1 } 132 } 133 if same == 0 { 134 let dir: i64 = cd_dir(scratch, W, x0, y0) 135 cd_filt8(scratch, recon, W, H, x0, y0, dir, S) 136 } 137 x0 = x0 + 8 } 138 y0 = y0 + 8 } 139 return 0 140} 141 142// I+P roundtrip at ship config (emode 681); cdefS > 0 applies CDEF-lite post-frame to BOTH chains (in-loop). 143// out[0]=P-total bytes, out[1]=luma SSE (of the DISPLAYED = filtered recon), out[2]=desync flag. 144func runv(fp: *i64, qp: i64, sz: i64, cdefS: i64, out: *i64) -> i64 { 145 let prevE: *u8=sys_mmap(sz+64); let reconE: *u8=sys_mmap(sz+64) 146 let prevD: *u8=sys_mmap(sz+64); let reconD: *u8=sys_mmap(sz+64) 147 let wire: *u8=sys_mmap(4194304); let blk: *i64=sys_mmap(512) as *i64; let mv: *i64=sys_mmap(128) as *i64 148 let estE: *i64=sys_mmap(64) as *i64; let probsE: *i64=sys_mmap(32*8) as *i64; let rcE: *u8=sys_mmap(4194304); let t8cE: *i64=sys_mmap(5120) as *i64 149 let estD: *i64=sys_mmap(64) as *i64; let probsD: *i64=sys_mmap(32*8) as *i64; let t8cD: *i64=sys_mmap(5120) as *i64 150 let rctxE: *i64=sys_mmap(64) as *i64; let rctxD: *i64=sys_mmap(64) as *i64 151 let scratch: *u8=sys_mmap(SW*SH+64) 152 vc_t8_init(t8cE); vc_t8_init(t8cD) 153 var z: i64=0; while z<sz { prevE[z]=0 as u8; prevD[z]=0 as u8; z=z+1 } 154 rctxE[1]=estE as i64; rctxE[2]=probsE as i64; rctxE[3]=rcE as i64; rctxE[4]=t8cE as i64; rctxE[5]=0; rctxE[6]=0; rctxE[7]=0 155 rctxD[1]=estD as i64; rctxD[2]=probsD as i64; rctxD[3]=0; rctxD[4]=t8cD as i64; rctxD[5]=0; rctxD[6]=0; rctxD[7]=0 156 out[0]=0; out[1]=0; out[2]=0 157 let NL: i64 = SW*SH 158 var f: i64=0 159 while f < NF { 160 let cur: *u8=fp[f] as *u8 161 var key: i64=0; if f==0 { key=1 } 162 rctxE[0]=681 163 let nb: i64 = vv_enc_rct8(cur, prevE, reconE, SW, SH, qp, key, qp*30, wire, 4194304, blk, mv, rctxE) 164 if nb<=0 { out[2]=1; return 1 } 165 rctxD[0]=681 166 let dr: i64 = vv_dec_rct8(prevD, reconD, SW, SH, qp, wire, nb, blk, mv, rctxD) 167 if dr<0 { out[2]=1; return 1 } 168 if cdefS > 0 { 169 cd_pass(reconE, prevE, SW, SH, cdefS, scratch) 170 cd_pass(reconD, prevD, SW, SH, cdefS, scratch) 171 } 172 var mm: i64 = 0 - 1 173 var ii: i64 = 0 174 while ii < sz { if reconE[ii] != reconD[ii] { mm = ii; ii = sz } else { ii = ii + 1 } } 175 if mm >= 0 { out[2]=1 } 176 var pxi: i64=0 177 while pxi<NL { let d: i64=(reconE[pxi]&0xff)-(cur[pxi]&0xff); out[1]=out[1]+d*d; pxi=pxi+1 } 178 if key==1 { } else { out[0]=out[0]+nb } 179 cpb(prevE, reconE, sz); cpb(prevD, reconD, sz) 180 f=f+1 181 } 182 return 0 } 183 184func row(fp: *i64, sz: i64, qp: i64, cdefS: i64, label: *u8) -> i64 { 185 let o: *i64=sys_mmap(32) as *i64 186 runv(fp, qp, sz, cdefS, o) 187 let NL: i64 = NF*SW*SH 188 gw(" " as *u8); gw(label); gw(" P-total=" as *u8); gn(o[0]); gw("B lumaMSEx1000=" as *u8); gn(o[1]*1000/NL) 189 gw(" chains=" as *u8) 190 if o[2] == 0 { gw("IDENTICAL" as *u8) } else { gw("DESYNC" as *u8) } 191 gw("\n" as *u8) 192 return o[0] } 193 194func sweepqp(fp: *i64, sz: i64, qp: i64) -> i64 { 195 gw(" --- qp=" as *u8); gn(qp); gw(" ---\n" as *u8) 196 let a: i64 = row(fp, sz, qp, 0, "A ship 681 " as *u8) 197 let b: i64 = row(fp, sz, qp, qp/4, "B +CDEF S=qp/4 " as *u8) 198 let c: i64 = row(fp, sz, qp, qp/2, "C +CDEF S=qp/2 " as *u8) 199 if a > 0 { gw(" --> bytes delta: B=" as *u8); gn((a-b)*1000/a); gw(" permille C=" as *u8); gn((a-c)*1000/a); gw(" permille (positive = CDEF fewer P-bytes)\n" as *u8) } 200 return 0 } 201 202func main() -> i64 { 203 gw("=== nx_vcodec_cdef_gate: CDEF-lite in-loop deringing ceiling (ship 681 base) on /tmp/seq.y4m ===\n" as *u8) 204 let box: *i64 = sys_mmap(16) as *i64 205 let yuv: *u8 = sys_read_file("/tmp/seq.y4m" as *u8, box) 206 if (yuv as i64)==0 { gw("cannot read /tmp/seq.y4m -> RED\n" as *u8); return 1 } 207 let total: i64=box[0] 208 var hi: i64=0; while hi<total { if (yuv[hi]&0xff)==10 { break } hi=hi+1 } hi=hi+1 209 let C2: i64=(SW/2)*(SH/2); let fdata: i64=SW*SH+2*C2; let sz: i64=fdata 210 let fp: *i64=sys_mmap(128*8) as *i64 211 let nf: i64=frames(yuv, hi, total, fp, fdata) 212 if nf < NF { gw("too few frames -> RED\n" as *u8); return 1 } 213 sweepqp(fp, sz, 8) 214 sweepqp(fp, sz, 20) 215 sweepqp(fp, sz, 32) 216 gw("CDEF: DONE\n" as *u8) 217 return 0 }