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 }