Repository navigation
Add ITU-R BT.709 YCbCr conversions to the color conversion kernels - #6567
jantonguirao wants to merge 7 commits into
Conversation
Add itu_r_bt_709 (16-235 range, the same API as itu_r_bt_601; the gray conversions do not depend on the matrix and are inherited) and itu_r_bt_709_full_range (YCbCr to RGB over the whole range of the type, the counterpart of jpeg with the BT.709 matrix). HD video is normally BT.709, while only BT.601 was available. Signed-off-by: Joaquin Anton Guirao <janton@nvidia.com>
There was a problem hiding this comment.
🟡 Changes recommended
The new BT.709 inverse-conversion tests cover only uint8_t input and output, leaving mixed-type scaling, wider integer biases, and floating-output clamping behavior unverified.
1 open issue
What changed in this PR
Adds BT.709 color conversion kernels alongside the existing BT.601 and JPEG conversions, preparing for stream-specific matrix selection in video decoders. No callers use the new conversions yet.
Changes:
- Adds limited-range BT.709 RGB ↔ YCbCr conversions, reusing BT.601 gray conversions.
- Adds full-range BT.709 YCbCr → RGB conversion.
- Adds pinned-value, reference-grid, and round-trip tests.
| File | Description |
|---|---|
dali/kernels/imgproc/color_manipulation/color_space_conversion_impl.h |
Implements limited- and full-range BT.709 conversions. |
dali/kernels/imgproc/color_manipulation/color_space_conversion_impl_test.cc |
Adds BT.709 correctness and BT.601 comparison tests. |
Review effort: Balanced
| auto limited = itu_r_bt_709::ycbcr_to_rgb<uint8_t>(ycbcr); | ||
| auto limited_ref = ycbcr_to_rgb_ref(0.2126, 0.0722, false, y, cb, cr); | ||
| auto full = itu_r_bt_709_full_range::ycbcr_to_rgb<uint8_t>(ycbcr); |
There was a problem hiding this comment.
Since e8755f9 the typed suites (ColorSpaceConversionAnyTypeTest and the others) also run for itu_r_bt_709 and itu_r_bt_709_full_range, for every input and output type (unsigned and signed integers, float), against the double precision reference with the Kr, Kb of each method. The tests with uint8 only are the ones with pinned values.
[Low impact] The PR appears safe to merge; no actionable issues remain.
|
…all types The BT.709 conversions inherited from BT.601 only to reuse the gray conversions. Move those to a common base of both, which says what is shared. Run the BT.709 conversions through the typed tests (all input/output type combinations, signed and out-of-range inputs, clamping rules of floating point outputs) with a reference parametrized by the luma weights. Signed-off-by: Joaquin Anton Guirao <janton@nvidia.com>
Implement rgb_to_y/cb/cr, rgb_to_ycbcr and ycbcr_to_rgb once, in detail::ycbcr_method, from a matrix (the scaled coefficients) and a range (limited or full, with its gray conversions). itu_r_bt_601, itu_r_bt_709, jpeg and itu_r_bt_709_full_range are now one-line instantiations. The results of the existing methods are unchanged. itu_r_bt_709_full_range gets the forward conversion and the gray conversions too, and the tests cover it with the other methods; the separate YCbCr -> RGB reference test is dropped, as the typed tests already compare against the reference with the Kr, Kb of every method. Signed-off-by: Joaquin Anton Guirao <janton@nvidia.com>
State the RGB <-> YCbCr formulas once, in a comment, and compute the coefficients of every method from the luma weights Kr, Kb of the standard (BT.601, BT.709) and the scales of the range (limited or full), instead of listing the scaled constants of four matrices. The results are bit-identical to the previous constants. Signed-off-by: Joaquin Anton Guirao <janton@nvidia.com>
Move the coefficients into detail::ycbcr_coefficients so the conversion structs only expose the conversion functions, share the clamp/dot/offset/round code of rgb_to_y, rgb_to_cb and rgb_to_cr, use the range's y_bias in the gray conversions, note the floating point offsets in the formulas comment and add pinned values for itu_r_bt_709_full_range. The results are unchanged. Signed-off-by: Joaquin Anton Guirao <janton@nvidia.com>
Signed-off-by: Joaquin Anton Guirao <janton@nvidia.com>
| static constexpr DALI_HOST_DEV vec3 y_row() { | ||
| return vec3(kr * ys, kg * ys, kb * ys); | ||
| } | ||
| static constexpr DALI_HOST_DEV vec3 cb_row() { | ||
| return vec3(-kr / (2 * (1 - kb)) * cs, -kg / (2 * (1 - kb)) * cs, 0.5 * cs); | ||
| } | ||
| static constexpr DALI_HOST_DEV vec3 cr_row() { | ||
| return vec3(0.5 * cs, -kg / (2 * (1 - kr)) * cs, -kb / (2 * (1 - kr)) * cs); | ||
| } |
There was a problem hiding this comment.
This will cause debug builds to be very slow (these functions will be actually called and executed every time). Why not compute these vectors at compile time and just use a bunch of fields instead?
| static constexpr DALI_HOST_DEV vec3 y_row() { | |
| return vec3(kr * ys, kg * ys, kb * ys); | |
| } | |
| static constexpr DALI_HOST_DEV vec3 cb_row() { | |
| return vec3(-kr / (2 * (1 - kb)) * cs, -kg / (2 * (1 - kb)) * cs, 0.5 * cs); | |
| } | |
| static constexpr DALI_HOST_DEV vec3 cr_row() { | |
| return vec3(0.5 * cs, -kg / (2 * (1 - kr)) * cs, -kb / (2 * (1 - kr)) * cs); | |
| } | |
| static constexpr vec3 y_row = vec3(kr * ys, kg * ys, kb * ys); | |
| static constexpr vec3 cb_row = vec3(-kr / (2 * (1 - kb)) * cs, -kg / (2 * (1 - kb)) * cs, 0.5 * cs); | |
| static constexpr vec3 cr_row = vec3(0.5 * cs, -kg / (2 * (1 - kr)) * cs, -kb / (2 * (1 - kr)) * cs)); |
There was a problem hiding this comment.
Confirmed: with -O0 the row functions were called at run time (the rows were passed to my rgb_to_channel helper as arguments, and scaled there). The rows are now static constexpr data members as suggested, and each conversion function scales them in a constexpr local, so nothing is computed per call in a debug build. Checked: the -O0 assembly of rgb_to_y has no calls to the row functions any more; the results are bit-identical to the previous header at -O0 and -O1 (all uint8 triples and samples of the other types); the 135 gtests pass; the device code compiles, also with -G. Fixed in 6f8e4fa.
The rows of the matrix were constexpr functions whose results were passed to a helper as arguments, so they were called, and the rows scaled for the types, at run time in builds without optimization. Make the rows static constexpr data members and scale them in a constexpr in each conversion function, as before the helper was introduced. The results are unchanged. Signed-off-by: Joaquin Anton Guirao <janton@nvidia.com>
|
!build |
|
CI MESSAGE: [77328866]: BUILD STARTED |
|
CI MESSAGE: [77328866]: BUILD FAILED |

Category:
New feature (non-breaking change which adds functionality)
Description:
The color conversion kernels have the YCbCr <-> RGB conversions for ITU-R BT.601 (
itu_r_bt_601, 16-235 range) and JPEG (jpeg, full range, also with the BT.601 matrix), but not for ITU-R BT.709, which is the matrix that HD video normally uses.This PR adds them, so that the video decoders can convert with the matrix of the stream (follow-up PR):
itu_r_bt_709: the same API asitu_r_bt_601(rgb_to_y,rgb_to_cb,rgb_to_cr,rgb_to_ycbcr,ycbcr_to_rgb, and the gray conversions), with the BT.709 matrix (Kr = 0.2126, Kb = 0.0722) and the 16-235 / 16-240 ranges.itu_r_bt_709_full_range: the same API asjpeg, with the BT.709 matrix over the whole dynamic range of the type. The video decoders need itsycbcr_to_rgbfor full range video.To avoid copying the conversions for each matrix,
itu_r_bt_601,itu_r_bt_709,jpeganditu_r_bt_709_full_rangeare now instantiations of one template,detail::ycbcr_method<Weights, Range>, with the luma weights of the standard (Kr, Kb) and the range (limited_rangeorfull_range). The results of the existing methods are bit-identical to the previous ones.Nothing uses the new conversions yet.
Additional information:
Affected modules and functionalities:
dali/kernels/imgproc/color_manipulation/color_space_conversion_impl.h: newitu_r_bt_709anditu_r_bt_709_full_range;itu_r_bt_601andjpegare expressed with the shared template.dali/kernels/imgproc/color_manipulation/color_space_conversion_impl_test.cc: new tests.Key points relevant for the review:
detailsection ofcolor_space_conversion_impl.h, and the coefficients of every method are computed from Kr, Kb and the scales of the range (219/255 for luma and 224/255 for chroma in the limited range). The compile-time values are the same floats as the constants thatitu_r_bt_601andjpeghad.Tests:
In
color_space_conversion_impl_test.cc:itu_r_bt_709anditu_r_bt_709_full_range, with the Kr, Kb of each.ColorSpaceConversionTest.*:ITU_R_BT709_RGB2YCbCr_u8: pinned values for the primaries and secondaries.ITU_R_BT709_FullRange_RGB2YCbCr_u8: pinned values of the full range method.ITU_R_BT709_DiffersFromBT601: BT.709 differs from BT.601 for colors, and the gray conversions are the same.ITU_R_BT709_YCbCr2RGB_RoundTripOfPrimaries: RGB -> YCbCr -> RGB gets the colors back up to quantization, and BT.601 doesn't.I ran this test file on its own (compiled with the host compiler against gtest): all 135 tests in it pass. I also checked that the conversions of
itu_r_bt_601,jpeganditu_r_bt_709give the same bits as before the refactor (all uint8 RGB/YCbCr values, and samples of float, int8, int16, uint16 and int32 inputs and outputs), and compiled all four methods in a CUDA kernel to PTX (sm_75). There is no GPU test: nothing uses the new conversions yet.Checklist
Documentation
DALI team only
Requirements
REQ IDs: N/A
JIRA TASK: DALI-4954