h264: Intra_8x8 luma (High profile) — pre-filter + 3 modes (V/H/DC) #21

Merged
marfrit merged 1 commits from noether/h264-intra-pred-8x8-luma into main 2026-05-25 07:51:58 +00:00
Owner

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:

  • Intra_4x4 luma (9) ✓
  • Intra_16x16 luma (4) ✓
  • Intra_8x8 chroma (4) ✓
  • Intra_8x8 luma — 3 of 9 ✓ (V/H/DC); 6 directional pending
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**: - Intra_4x4 luma (9) ✓ - Intra_16x16 luma (4) ✓ - Intra_8x8 chroma (4) ✓ - Intra_8x8 luma — 3 of 9 ✓ (V/H/DC); 6 directional pending
marfrit added 1 commit 2026-05-25 07:36:09 +00:00
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+.
marfrit merged commit 18ca708f87 into main 2026-05-25 07:51:58 +00:00
marfrit deleted branch noether/h264-intra-pred-8x8-luma 2026-05-25 07:52:00 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marfrit/daedalus-fourier#21