code wiki / (root) / nx_deblock.nx

nx_deblock.nx source

↩ module page · 148 lines · 5135 B

1// nx_deblock.nx -- in-loop deblocking filter for 8x8 block-grid 2// reconstructions. Substrate primitive that smooths the quantisation 3// discontinuities at block boundaries without touching real edges. 4// 5// Algorithm (simple, public): for each boundary (horizontal between 6// rows 7-8, 15-16, ...; vertical between cols 7-8, 15-16, ...) take 7// the four pixels straddling the line: 8// p1, p0 | q0, q1 9// If the local variation is small (|p1-p0| < T && |q0-q1| < T && |p0-q0| 10// < T2), apply a low-pass filter that pulls p0 and q0 toward the 11// average across the boundary. Otherwise leave the pixels alone 12// (preserves real edges). 13// 14// This is structurally what H.263 Annex J and H.264's deblocking 15// filter do; the simple variant here re-derives from the underlying 16// public idea (filter only smooth regions) without copying their 17// boundary-strength tables or per-block-mode dispatch. Quality is 18// modest; the row goes from LOSE to WIN on the substrate axis once 19// the feature exists. Stronger variants are named improvements. 20// 21// genealogy_id: itu_t_h_263_annex_j + h264_8_7_deblock + jpeg2000_visual + 22// nx_video_codec_v1_q10 23// lineage_id: nishi_deblock_q10 24 25// nx_safety_envelope: 26// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 27// sil_target: SIL1 28// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 29// verdict: NOT_YET_EVALUATED 30 31import "nx_syscalls.nx" 32 33const NX_DB_VERDICT_UNKNOWN: i64 = 0 34const NX_DB_VERDICT_OK: i64 = 1 35const NX_DB_VERDICT_BAD_PARAMS: i64 = 2 36const NX_DB_VERDICT_N: i64 = 3 37 38const NX_DB_BLOCK: i64 = 8 39const NX_DB_T1: i64 = 16 // |p1-p0| threshold 40const NX_DB_T2: i64 = 32 // |p0-q0| threshold 41 42func _db_abs(x: i64) -> i64 { 43 if x < 0 { return -x } 44 return x 45} 46 47// Apply the boundary filter on one 4-pixel sample (p1, p0, q0, q1). 48// Writes new p0' and q0' to *p0_out and *q0_out. 49// 50// Conditions for filtering: 51// |p1 - p0| < T1 52// |q0 - q1| < T1 53// |p0 - q0| < T2 54// Filter (when conditions met): 55// delta = (q0 - p0) / 4 56// p0' = clamp(p0 + delta, 0, 255) 57// q0' = clamp(q0 - delta, 0, 255) 58// 59// When NOT filtering, the function returns the original values. 60func _db_filter4(p1: i64, p0: i64, q0: i64, q1: i64, 61 p0_out: *i64, q0_out: *i64) -> i64 { 62 if _db_abs(p1 - p0) >= NX_DB_T1 { 63 *p0_out = p0; *q0_out = q0 64 return 0 65 } 66 if _db_abs(q0 - q1) >= NX_DB_T1 { 67 *p0_out = p0; *q0_out = q0 68 return 0 69 } 70 if _db_abs(p0 - q0) >= NX_DB_T2 { 71 *p0_out = p0; *q0_out = q0 72 return 0 73 } 74 let delta: i64 = (q0 - p0) / 4 75 var new_p0: i64 = p0 + delta 76 var new_q0: i64 = q0 - delta 77 if new_p0 < 0 { new_p0 = 0 } 78 if new_p0 > 255 { new_p0 = 255 } 79 if new_q0 < 0 { new_q0 = 0 } 80 if new_q0 > 255 { new_q0 = 255 } 81 *p0_out = new_p0 82 *q0_out = new_q0 83 return 1 84} 85 86// Filter horizontal block boundaries (between rows ...7,8 ...15,16). 87// `img` is W*H, row-major. In-place. 88func nx_deblock_horizontal(img: *i64, W: i64, H: i64) -> i64 { 89 if W < 1 { return NX_DB_VERDICT_BAD_PARAMS } 90 if H < 16 { return NX_DB_VERDICT_OK } // no inner boundary to filter 91 var by: i64 = 1 92 while by * NX_DB_BLOCK < H { 93 let row: i64 = by * NX_DB_BLOCK 94 var x: i64 = 0 95 while x < W { 96 let p1: i64 = img[(row - 2) * W + x] 97 let p0: i64 = img[(row - 1) * W + x] 98 let q0: i64 = img[(row ) * W + x] 99 let q1: i64 = img[(row + 1) * W + x] 100 let p0_slot: *i64 = sys_mmap(16) as *i64 101 let q0_slot: *i64 = sys_mmap(16) as *i64 102 _db_filter4(p1, p0, q0, q1, p0_slot, q0_slot) 103 img[(row - 1) * W + x] = *p0_slot 104 img[(row ) * W + x] = *q0_slot 105 x = x + 1 106 } 107 by = by + 1 108 } 109 return NX_DB_VERDICT_OK 110} 111 112// Filter vertical block boundaries (between cols 7,8 ...15,16). 113func nx_deblock_vertical(img: *i64, W: i64, H: i64) -> i64 { 114 if W < 16 { return NX_DB_VERDICT_OK } 115 if H < 1 { return NX_DB_VERDICT_BAD_PARAMS } 116 var bx: i64 = 1 117 while bx * NX_DB_BLOCK < W { 118 let col: i64 = bx * NX_DB_BLOCK 119 var y: i64 = 0 120 while y < H { 121 let p1: i64 = img[y * W + (col - 2)] 122 let p0: i64 = img[y * W + (col - 1)] 123 let q0: i64 = img[y * W + (col )] 124 let q1: i64 = img[y * W + (col + 1)] 125 let p0_slot: *i64 = sys_mmap(16) as *i64 126 let q0_slot: *i64 = sys_mmap(16) as *i64 127 _db_filter4(p1, p0, q0, q1, p0_slot, q0_slot) 128 img[y * W + (col - 1)] = *p0_slot 129 img[y * W + (col )] = *q0_slot 130 y = y + 1 131 } 132 bx = bx + 1 133 } 134 return NX_DB_VERDICT_OK 135} 136 137// One-shot: filter both horizontal then vertical. 138func nx_deblock(img: *i64, W: i64, H: i64) -> i64 { 139 nx_deblock_horizontal(img, W, H) 140 nx_deblock_vertical(img, W, H) 141 return NX_DB_VERDICT_OK 142} 143 144func nx_deblock_verdict_is_valid(v: i64) -> i64 { 145 if v < 0 { return 0 } 146 if v >= NX_DB_VERDICT_N { return 0 } 147 return 1 148}