nx_vcodec_nf.nx source
↩ module page · 92 lines · 4339 B
1// nx_vcodec_nf.nx -- LEARNED decoder-side RESTORATION FILTER (NEURAL track rung 1, task #46; operator
2// 2026-07-07: "achieve true neural sota as we climb ... and these things are all going live").
3// A data-TRAINED piecewise-linear restorer (RAISR/Wiener-class -- the same family as AV1's loop
4// restoration): every interior pixel of the DEBLOCKED recon is classified by its local gradient
5// (flat + 4 directions x 2 strengths = 9 classes) inside a qp band (3 bands), and restored by that
6// class's TRAINED 3x3+bias filter (Q12 integer weights, trained by nx_vcodec_nf_train on
7// (our-codec-recon -> source) pairs from REAL frames -- no floats anywhere, sovereign end to end).
8// DISPLAY-PATH ONLY by design: the P-reference chain keeps the unfiltered recon (bit-exactness of the
9// codec is untouched; a receiver applies this after decode, before YUV->RGBA). The trained table is
10// caller-provided (vc_nf_load from the GENERATED nx_vcodec_nf_table.nx fills it) -- this module stays
11// allocation-free and wasm-clean. license_tier: ORIGINAL
12import "nx_syscalls.nx"
13
14const VC_NF_CLASSES: i64 = 9 // 0=flat, 1..4 = H/V/D1/D2 moderate, 5..8 = H/V/D1/D2 strong
15const VC_NF_BANDS: i64 = 3 // qp bands: <16, 16..27, >=28 (quant noise scales with qp)
16const VC_NF_TAPS: i64 = 10 // 3x3 raster taps + bias, Q12
17const VC_NF_TBL: i64 = 270 // table length in i64 (bands * classes * taps)
18
19func vc_nf_band(qp: i64) -> i64 {
20 if qp < 16 { return 0 }
21 if qp < 28 { return 1 }
22 return 2
23}
24// gradient classifier. The bin edges (8 = below-noise-floor sum, 48 = strong-edge sum, 2x = direction
25// dominance) only PARTITION the data -- the trained per-class weights adapt to whatever falls in each
26// bin, so these are classifier geometry, not quality knobs.
27func vc_nf_class(gx: i64, gy: i64) -> i64 {
28 var ax: i64 = gx
29 if ax < 0 { ax = 0 - ax }
30 var ay: i64 = gy
31 if ay < 0 { ay = 0 - ay }
32 if ax + ay < 8 { return 0 }
33 var d: i64 = 0
34 if ax >= 2*ay { d = 1 } else {
35 if ay >= 2*ax { d = 2 } else {
36 var s: i64 = 1
37 if gx < 0 { s = 0 - s }
38 if gy < 0 { s = 0 - s }
39 if s >= 0 { d = 3 } else { d = 4 }
40 }
41 }
42 if ax + ay >= 48 { return d + 4 }
43 return d
44}
45// apply the trained filter: src (deblocked recon plane) -> out. Interior pixels restored, 1-px border
46// copied. out MUST be a different buffer (the 3x3 context reads pre-filter values by construction).
47func vc_nf_apply(src: *u8, W: i64, H: i64, out: *u8, qp: i64, tbl: *i64) -> i64 {
48 let boff: i64 = vc_nf_band(qp) * VC_NF_CLASSES * VC_NF_TAPS
49 var x: i64 = 0
50 while x < W { out[x] = src[x]; out[(H-1)*W + x] = src[(H-1)*W + x]; x = x + 1 }
51 var y: i64 = 1
52 while y < H - 1 {
53 let r0: i64 = (y-1) * W
54 let r1: i64 = y * W
55 let r2: i64 = (y+1) * W
56 out[r1] = src[r1]
57 out[r1 + W - 1] = src[r1 + W - 1]
58 var xx: i64 = 1
59 while xx < W - 1 {
60 let p00: i64 = src[r0 + xx - 1] as i64
61 let p01: i64 = src[r0 + xx] as i64
62 let p02: i64 = src[r0 + xx + 1] as i64
63 let p10: i64 = src[r1 + xx - 1] as i64
64 let p11: i64 = src[r1 + xx] as i64
65 let p12: i64 = src[r1 + xx + 1] as i64
66 let p20: i64 = src[r2 + xx - 1] as i64
67 let p21: i64 = src[r2 + xx] as i64
68 let p22: i64 = src[r2 + xx + 1] as i64
69 let cls: i64 = vc_nf_class(p12 - p10, p21 - p01)
70 let base: i64 = boff + cls * VC_NF_TAPS
71 var acc: i64 = tbl[base]*p00 + tbl[base+1]*p01 + tbl[base+2]*p02
72 acc = acc + tbl[base+3]*p10 + tbl[base+4]*p11 + tbl[base+5]*p12
73 acc = acc + tbl[base+6]*p20 + tbl[base+7]*p21 + tbl[base+8]*p22
74 acc = acc + tbl[base+9]
75 var v: i64 = (acc + 2048) >> 12
76 if v < 0 { v = 0 }
77 if v > 255 { v = 255 }
78 out[r1 + xx] = v as u8
79 xx = xx + 1
80 }
81 y = y + 1
82 }
83 return 0
84}
85// identity table (center tap = 1.0): the safe do-nothing baseline the trainer must beat.
86func vc_nf_identity(tbl: *i64) -> i64 {
87 var i: i64 = 0
88 while i < VC_NF_TBL { tbl[i] = 0; i = i + 1 }
89 var bc: i64 = 0
90 while bc < VC_NF_BANDS * VC_NF_CLASSES { tbl[bc * VC_NF_TAPS + 4] = 4096; bc = bc + 1 }
91 return 0
92}