h264: Intra_8x8 luma (High profile) — pre-filter + 3 modes (V/H/DC) #21
Reference in New Issue
Block a user
Delete Branch "noether/h264-intra-pred-8x8-luma"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Adds the High-profile Intra_8x8 luma primitive set per H.264 §8.3.2.1. Distinct from Intra_4x4 in two ways: (1) the 25 raw neighbour samples are pre-filtered with a 1-2-1 smoothing filter with spec-defined 3× boundary weights at the right/bottom edges; (2) prediction at 8x8 on filtered samples.
This PR ships the pre-filter + 3 simple modes (V/H/DC). The 6 directional modes (DDL/DDR/VR/HD/VL/HU) follow as spec-mechanical follow-ups.
All 5 tests PASS first try, including the gradient sanity that exercises the pre-filter's boundary cases. The 3× boundary weight is easy to miss when transcribing; gradient pass means it's right.
Intra-prediction primitive coverage now:
Adds the High-profile Intra_8x8 luma primitive set. Per H.264 §8.3.2.1, this is distinct from Intra_4x4 in two ways: 1. REFERENCE SAMPLE PRE-FILTER (§8.3.2.1.1). The 25 raw neighbour samples are smoothed with a 1-2-1 filter BEFORE prediction. Spec-defined boundary handling at corners and the right edge: - top-left filt'd: (top[0] + 2*tl + left[0] + 2) >> 2 - top[0] filt'd: (tl + 2*t[0] + t[1] + 2) >> 2 - top[i] for 1..14: (t[i-1] + 2*t[i] + t[i+1] + 2) >> 2 - top[15] filt'd: (t[14] + 3*t[15] + 2) >> 2 ← 3× boundary - left analogous, with l[7] using 3× boundary. 2. SCALE. All 9 prediction modes operate at 8x8 on the filtered samples (Intra_4x4 is 4x4 on raw samples). This PR ships the pre-filter + the 3 simple modes (V, H, DC): - Mode 0 Vertical (§8.3.2.1.2): pred[r,c] = filt_top[c] - Mode 1 Horizontal (§8.3.2.1.3): pred[r,c] = filt_left[r] - Mode 2 DC (§8.3.2.1.4): ((sum_filt_top[0..7] + sum_filt_left[0..7] + 8) >> 4) broadcast The 6 directional modes (DDL, DDR, VR, HD, VL, HU at 8x8 per §8.3.2.1.5..§8.3.2.1.10) follow in a separate PR. They use the same filtered samples; only the per-cell formula differs. Test design (tests/test_intra_pred_8x8_luma.c): - 3 uniform-context tests, one per mode (sanity). - 2 gradient tests that exercise the pre-filter's interior + boundary cases: * Vertical with top = 0..15: spec arithmetic gives filtered top[c] = c for c in 0..7 (gradient input → identity through the 1-2-1 filter on the interior; boundaries arithmetically verify too). Test expects pred[r,c] = c. * Horizontal with left = 0..7: same arithmetic chain on the left col. Test expects pred[r,c] = r. Verified on hertz: $ ./build/test_intra_pred_8x8_luma Vertical (mode 0, uniform top) PASS Horizontal (mode 1, uniform left) PASS DC (mode 2, uniform) PASS Vertical (mode 0, gradient) PASS (filtered gradient) Horizontal (mode 1, gradient) PASS (filtered gradient) ALL Intra_8x8 luma PASS (3 modes — V, H, DC) The pre-filter being right first try is meaningful — the boundary samples use a 3× weight rather than 2× (filt[top 15] = (t[14] + 3*t[15] + 2) >> 2), which is easy to forget when transcribing. The gradient test would have surfaced any boundary mistake immediately. Combined intra-prediction primitive coverage after this PR: Intra_4x4 luma ✓ (9 modes, PR #12) Intra_16x16 luma ✓ (4 modes, PR #13) Intra_8x8 chroma ✓ (4 modes, PR #14) Intra_8x8 luma △ (3 of 9 modes — V, H, DC ✓; DDL/DDR/VR/HD/VL/HU pending) The 6 remaining Intra_8x8 luma directional modes are spec-mechanical follow-ups; each is a ~30-line formula per §8.3.2.1.5+.