Skip to content

feat(tools): vmaf-tune — HDR-aware encoding + HDR-VMAF scoring#379

Merged
lusoris merged 3 commits intomasterfrom
feat/vmaf-tune-hdr-aware
May 5, 2026
Merged

feat(tools): vmaf-tune — HDR-aware encoding + HDR-VMAF scoring#379
lusoris merged 3 commits intomasterfrom
feat/vmaf-tune-hdr-aware

Conversation

@lusoris
Copy link
Copy Markdown
Owner

@lusoris lusoris commented May 3, 2026

Summary

Closes Bucket #9 of the PR #354 vmaf-tune capability audit — HDR-aware encoding + HDR-VMAF scoring scaffolding.

  • New vmaftune.hdr module: detect_hdr (ffprobe-driven PQ / HLG classification with strict BT.2020-primaries gate), hdr_codec_args (per-encoder dispatch table for libx264, libx265, libsvtav1, hevc_nvenc, libvvenc), select_hdr_vmaf_model (returns model/vmaf_hdr_*.json if shipped).
  • Corpus driver gains --auto-hdr (default) / --force-sdr / --force-hdr-pq / --force-hdr-hlg mutually-exclusive CLI modes and three new schema-v2 row keys (hdr_transfer, hdr_primaries, hdr_forced); SCHEMA_VERSION bumped 1 → 2.
  • Encode-side correctness ships now; HDR-VMAF scoring is deferred — the fork has not yet ported Netflix's vmaf_hdr_v0.6.1.json. Until then, HDR sources are scored against the SDR model with a one-shot warning.

Type

  • feat — new capability inside tools/vmaf-tune/

Checklist

  • Conventional Commits
  • Tests: 34 / 34 mocked tests pass (13 existing + 21 new HDR cases)
  • No Netflix golden assertions touched
  • Pre-commit gate green (black, isort, ruff, semgrep, secret scan)

Bug-status hygiene

  • no state delta: capability addition, no bug interaction.

Netflix golden-data gate

  • Did not modify any assertAlmostEqual score.

Deep-dive deliverables (ADR-0108)

Reproducer

# Run the full tooling test suite (mocked ffmpeg + ffprobe + vmaf — no binaries required).
python -m pytest tools/vmaf-tune/tests/ -q
# Confirm the new CLI flags surface.
PYTHONPATH=tools/vmaf-tune/src python -m vmaftune.cli corpus --help | grep -E "hdr|sdr"

Expected: 34 passed; help output shows --auto-hdr, --force-sdr, --force-hdr-pq, --force-hdr-hlg.

HDR detection accuracy on test fixtures

Fixture Expected Result
Plain SDR (bt709 / bt709) SDR None ✓
HDR PQ (smpte2084 / bt2020nc + master-display SEI) PQ + master_display + max_cll matches ✓
HDR HLG (arib-std-b67 / bt2020) HLG matches ✓
Malformed (smpte2084 / bt709) SDR (fail-safe) None ✓
Missing file None None ✓
ffprobe rc=1 None None ✓
Invalid JSON None None ✓

Codec dispatch coverage

Encoder Tested HDR carrier
libx264 yes container -color_* only (x264 has no in-stream HDR SEI)
libx265 PQ + HLG -x265-params w/ master-display + max-cll + hdr10-opt (PQ only)
libsvtav1 PQ + HLG -svtav1-params AV1 enums (prim=9, transfer=16/18, matrix=9)
hevc_nvenc yes -pix_fmt p010le -profile:v main10 + global -color_*
libvvenc implicit (global path) container -color_*
unknown yes empty tuple

Out of scope

@lusoris lusoris force-pushed the feat/vmaf-tune-hdr-aware branch from 9e4b582 to 581f961 Compare May 3, 2026 20:04
@lusoris lusoris marked this pull request as ready for review May 5, 2026 15:35
Copilot AI review requested due to automatic review settings May 5, 2026 15:35
…#9, ADR-0261)

Closes Bucket #9 of the PR #354 vmaf-tune capability audit. Adds
ffprobe-driven HDR detection, codec-specific HDR encode flag dispatch,
and HDR-VMAF model resolution to the Phase A corpus driver.

New module ``tools/vmaf-tune/src/vmaftune/hdr.py``:

- ``detect_hdr(path)`` — runs ``ffprobe -show_streams -of json``,
  classifies the first video stream as PQ / HLG / SDR. Strict
  BT.2020-primaries gate so malformed signaling falls back to SDR.
- ``hdr_codec_args(encoder, info)`` — per-encoder dispatch table
  covering libx264 (container ``-color_*``), libx265 (``-x265-params``
  with master-display + max-cll + hdr10-opt), libsvtav1 (AV1 enums
  via ``-svtav1-params``), hevc_nvenc (``-pix_fmt p010le -profile:v
  main10``), libvvenc.
- ``select_hdr_vmaf_model()`` — globs ``model/vmaf_hdr_*.json``;
  returns ``None`` when none shipped (current state — fork hasn't
  ported Netflix's HDR model yet).

Corpus driver wiring:

- ``CorpusOptions.hdr_mode`` ∈ {``auto``, ``force-sdr``,
  ``force-hdr-pq``, ``force-hdr-hlg``}; CLI flags
  ``--auto-hdr`` / ``--force-sdr`` / ``--force-hdr-pq`` /
  ``--force-hdr-hlg`` (mutually exclusive). Auto is the default.
- New schema-v2 row keys ``hdr_transfer`` / ``hdr_primaries`` /
  ``hdr_forced``; ``SCHEMA_VERSION`` bumped 1 → 2. Phase B / C
  loaders treat missing keys as SDR (additive change, v1 rows
  remain readable).
- ``score._model_arg`` now passes pre-formatted ``path=`` /
  ``version=`` strings through unchanged so the HDR model path can
  be injected via ``vmaf --model``.
- HDR detected but no HDR model shipped → log warning, fall back
  to SDR model with notice that scores trend low.

Tests (``tools/vmaf-tune/tests/test_hdr.py``, 21 cases):

- detection: SDR / PQ / HLG / mismatched-primaries / missing-file /
  ffprobe-failure / invalid-JSON
- codec dispatch: shape per encoder (x264, x265 PQ + HLG, SVT-AV1
  PQ + HLG, NVENC HEVC, unknown encoder)
- model resolution: empty dir / shipped / multi-version pick-latest
  / missing dir
- corpus integration: end-to-end ``force-hdr-pq`` (verify HDR fields
  in row + ``-color_*`` in encode argv) and ``force-sdr``

ADR-0261 (Accepted, encode-side; HDR-VMAF scoring deferred until
fork-local model port). Research-0054 digest, rebase-notes 0261,
AGENTS.md invariant note, docs/usage/vmaf-tune.md HDR section,
changelog fragment all included.
@lusoris lusoris force-pushed the feat/vmaf-tune-hdr-aware branch from 581f961 to b8664af Compare May 5, 2026 15:36
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds HDR-aware scaffolding to tools/vmaf-tune so corpus runs can detect PQ/HLG sources, attach encoder HDR metadata, and record HDR provenance in emitted rows while keeping SDR-model fallback for scoring until an HDR model is shipped.

Changes:

  • Added new vmaftune.hdr module for ffprobe-based HDR classification, encoder-specific HDR flag generation, and HDR-model discovery.
  • Wired HDR mode selection through corpus generation/CLI, bumped the corpus schema to v2, and allowed explicit path=/version= VMAF model arguments.
  • Added HDR-focused tests and accompanying ADR/research/changelog/docs updates.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tools/vmaf-tune/tests/test_hdr.py New HDR unit/integration-style mocked tests for detection, dispatch, model lookup, and corpus wiring.
tools/vmaf-tune/tests/test_corpus.py Updated schema contract assertions for v2 HDR row fields.
tools/vmaf-tune/src/vmaftune/score.py Lets --model accept preformatted key=value strings.
tools/vmaf-tune/src/vmaftune/hdr.py New HDR detection, metadata extraction, encoder flag dispatch, and HDR model resolution logic.
tools/vmaf-tune/src/vmaftune/corpus.py Integrates HDR detection/model selection into corpus row generation and schema output.
tools/vmaf-tune/src/vmaftune/cli.py Adds ffprobe override and mutually exclusive HDR mode flags.
tools/vmaf-tune/src/vmaftune/__init__.py Bumps schema version and adds HDR row keys.
tools/vmaf-tune/AGENTS.md Documents HDR invariants and fallback behavior for future contributors.
docs/usage/vmaf-tune.md Documents new HDR CLI options and schema fields.
docs/research/0071-vmaf-tune-hdr-aware.md Adds research digest for HDR-aware tuning.
docs/rebase-notes.md Adds a rebase note for the fork-local HDR tooling changes.
docs/adr/0295-vmaf-tune-hdr-aware.md Adds the ADR describing the HDR-aware design/decision.
docs/adr/_index_fragments/0261-vmaf-tune-hdr-aware.md Adds ADR index fragment entry for the new ADR.
docs/adr/_index_fragments/_order.txt Appends the ADR slug to the generated index order manifest.
changelog.d/added/T-VMAF-TUNE-hdr-aware.md Adds changelog fragment for the HDR-aware tooling feature.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +385 to +388
candidates = sorted(base.glob("vmaf_hdr_*.json"))
if not candidates:
return None
return candidates[-1]
Comment on lines +241 to +248
try:
return int(round((float(num) / float(den)) * scale))
except (ValueError, ZeroDivisionError):
return 0
try:
return int(round(float(text) * scale))
except ValueError:
return 0
Comment on lines +256 to +265
Returns ``(hdr_info, forced)``. ``forced`` is true iff the user
overrode the probe via ``--force-hdr-*`` / ``--force-sdr``.
"""
mode = opts.hdr_mode
if mode == "force-sdr":
return None, True
if mode == "force-hdr-pq":
return _synthetic_hdr_info("pq"), True
if mode == "force-hdr-hlg":
return _synthetic_hdr_info("hlg"), True
Comment on lines +307 to +312
def _hdr_args_x265(info: HdrInfo) -> tuple[str, ...]:
"""x265: in-stream SEI via ``-x265-params``."""
parts = [
"colorprim=bt2020",
f"transfer={'smpte2084' if info.transfer == 'pq' else 'arib-std-b67'}",
"colormatrix=bt2020nc",
Comment on lines 87 to +89

def _model_arg(model: str) -> str:
"""Format the ``--model`` argument for the libvmaf CLI.
Comment on lines +331 to +336
tc = 16 if info.transfer == "pq" else 18
parts = [
"color-primaries=9",
f"transfer-characteristics={tc}",
"matrix-coefficients=9",
"color-range=0" if info.color_range != "pc" else "color-range=1",
@lusoris lusoris merged commit 604ce6b into master May 5, 2026
54 checks passed
@lusoris lusoris deleted the feat/vmaf-tune-hdr-aware branch May 5, 2026 16:05
@lusoris lusoris restored the feat/vmaf-tune-hdr-aware branch May 6, 2026 17:42
@lusoris lusoris mentioned this pull request May 6, 2026
9 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants