Skip to content

Convert video to RGB on the CPU with the matrix of the stream - #6569

Open
jantonguirao wants to merge 4 commits into
NVIDIA:mainfrom
jantonguirao:janton/dali-4954-cpu-bt709
Open

jantonguirao wants to merge 4 commits into
NVIDIA:mainfrom
jantonguirao:janton/dali-4954-cpu-bt709

Conversation

@jantonguirao

@jantonguirao jantonguirao commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Category:

Bug fix (non-breaking change which fixes an issue)

Description:

The CPU video decoder (fn.experimental.readers.video, fn.experimental.decoders.video and fn.experimental.inputs.video on the CPU) converts YCbCr to RGB with swscale, and always tells it that the source uses the default matrix, ITU-R BT.601, whatever the stream says. HD video is normally BT.709, so its colors are wrong, noticeably for saturated colors: for example, pure red (255, 0, 0) in a BT.709 stream is decoded as about (234, 0, 3).

This PR tells swscale that the source is BT.709 when the stream is tagged as such, and keeps BT.601 for every other stream (untagged streams are not affected). The matrix is read from the decoded frame, or from the stream parameters if the frame doesn't have it, as is already done for the range, and swscale is reconfigured when either changes.

It is based on main and independent of the GPU change (#6568, which does the same for the GPU decoder). The new test uses the video clips and the ffmpeg references added in NVIDIA/DALI_extra#137 (db/video/color_matrix), which is merged: DALI_EXTRA_VERSION is updated to the commit that merged it.

On the test clip (1080p VP9, tagged BT.709, limited range; the CPU decoder supports only VP8 and VP9) the error of the mean color of the saturated bars against the original pattern, and the mean difference of the whole frame against ffmpeg's decode (the reference), are:

worst error in the bars mean difference to ffmpeg
before 22.0 6.6
after 2.0 0.97

(ffmpeg itself is within 2.0 of the original pattern.) Only BT.709 is added; other matrices are still converted as BT.601.

Additional information:

Affected modules and functionalities:

  • dali/operators/video/frames_decoder_cpu.{h,cc}: the source color space given to sws_setColorspaceDetails, and the member that remembers it.
  • dali/test/python/decoder/test_video.py: new test.

Key points relevant for the review:

Tests:

  • Existing tests apply
    • decoder/test_video.py: all of it (the GPU and CPU decoders, test_full_range_video*, the codec support tests).
  • New tests added
    • Python tests
    • GTests
    • Benchmark
    • Other

New test: decoder/test_video.py::test_color_matrix_bt709_cpu, on the VP9 clip. It checks the mean color of the interior of each of the 10 bars of the pattern (the 8 colors, a black and a white bar) against ffmpeg's decode (at most 4 apart; BT.601 gives 22) and the mean difference of the whole frame (below 2). It also checks that the black bar is at most 2 and the white bar at least 253, i.e. that the luma is mapped to the whole range and clamped there (Y=16 and Y=235 in the limited range); those two bars do not depend on the matrix, so that check passes without this change too, and the saturated bars are the ones that fail without it.

Run locally with this change on top of the other two: test_video.py passes (136 tests, 10 skipped). After the black and white bars were added to the clips, I ran test_color_matrix_bt709_cpu again with the new clips: the case pass.

Checklist

Documentation

  • Existing documentation applies
  • Documentation updated
    • Docstring
    • Doxygen
    • RST
    • Jupyter
    • Other
  • N/A

DALI team only

Requirements

  • Implements new requirements
  • Affects existing requirements
  • N/A

REQ IDs: N/A

JIRA TASK: DALI-4954

The CPU video decoder always used the BT.601 matrix for the source in
swscale, while HD video is normally BT.709, which gives visibly wrong colors
for saturated colors (e.g. pure red 255,0,0 came out as ~234,0,3).

Use the BT.709 matrix when the stream is tagged as such; streams that don't
specify it keep using BT.601.

Signed-off-by: Joaquin Anton Guirao <janton@nvidia.com>
Copilot AI balanced review requested due to automatic review settings October 28, 2026 08:34
@copy-pr-bot

copy-pr-bot Bot commented Oct 28, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@greptile-apps

greptile-apps Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable issues remain.

Summary

The CPU video decoder now uses BT.709 coefficients when converting tagged streams to RGB.

  • CPU video decoding uses BT.709 for tagged RGB frames.

Both earlier comments are addressed: the CPU test has no GPU skip, and its paths use os.path.join. No new actionable issues were found. Tests were not run during this review.

Reviews (4) · Last reviewed commit: "Update DALI_extra version for the BT.709..." · Reviewed by Greptile

Comment thread dali/test/python/decoder/test_video.py Outdated
Comment thread dali/test/python/decoder/test_video.py Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The BT.709 source-table selection also affects YCbCr output, causing libswscale to rematrix it to BT.601 and unintentionally change previously unaffected results.

1 open issue
What changed in this PR

Updates the shared CPU video decoder to use BT.709 metadata when converting video to RGB, correcting colors in tagged streams while retaining BT.601 as the fallback.

Changes:

  • Selects the source matrix from frame metadata, falling back to stream metadata.
  • Reconfigures swscale when the source matrix or range changes.
  • Adds a VP9 regression test against ffmpeg references from the linked DALI_extra fixtures.
File Description
dali/​test/​python/​decoder/​test_video.py Adds color-bar comparisons and a BT.709 CPU decoding regression test.
dali/​operators/​video/​frames_decoder_cpu.h Tracks the source matrix configured in swscale.
dali/​operators/​video/​frames_decoder_cpu.cc Selects conversion coefficients from color metadata and updates swscale configuration.

Review effort: Balanced

AVColorSpace src_colorspace = frame_->colorspace != AVCOL_SPC_UNSPECIFIED
? frame_->colorspace
: codec_params_->color_space;
int sws_colorspace = src_colorspace == AVCOL_SPC_BT709 ? SWS_CS_ITU709 : SWS_CS_DEFAULT;
YCbCr output is a copy of the decoded planes, so the source matrix must stay the one of the
destination. Also, run the CPU test on hosts without a GPU and build the paths with os.path.join.

Signed-off-by: Joaquin Anton Guirao <janton@nvidia.com>
The test pattern in DALI_extra has 10 bars now (black and white were added): use their width in the
helper that compares the bars, and check that black and white are mapped to the ends of the range
(the luma is not left at 16 and 235).

Signed-off-by: Joaquin Anton Guirao <janton@nvidia.com>
The color matrix tests read db/video/color_matrix, which DALI_extra#137 adds.

Signed-off-by: Joaquin Anton Guirao <janton@nvidia.com>
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.

4 participants