Question about YUV<->Matrix in ColorSpace.cu

For Rec709, the code in ColorSpace.cu scales all of the values in the matrix by 255 / (235 - 16). This is correct for Y, but shouldn’t it be doing 255 / (240 - 16) for U and V? Since U and V are 16-240 range?

Hi MalcolmB,

would you mind sharing a bit more context? Like, where that file resides, which SDK or similar?

In general colors space conversions are not necessarily isolated like you make it out to be. Rec.709 does not mandate the enlarged range for UV but it depends on the used codecs as well as what kind of quantization steps follow in the conversion pipeline. So it is very well within range of the standard for YCbCr to use 235 as a basis for all channels.

Sorry, this is in the Video Codec SDK.

Does state the ranges are [16, 235] for Y and [16, 240] for U and V.

You refer to this part?

In the 8-bit encoding the R’ , B’ , G’ , and Y’ channels have a nominal range of [16…235], and the C B and C R channels have a nominal range of [16…240] with 128 as the neutral value. So in limited range R’G’B’ reference black is (16, 16, 16) and reference white is (235, 235, 235), and in Y’C BC R reference black is (16, 128, 128) and reference white is (235, 128, 128). Values outside the nominal ranges are allowed, but typically they would be clamped for broadcast or for display.

I think the text in bold is why we use 235 for all channels. But I try to get clarification.

Thanks, yeah agreed it’s not totally obvious. That quote to me says it should be [16,240], since that is what the nominal range is defined as. I would think at the very least the BT.601 path should use [16,240], since that is what is shown in:
YCbCr - Wikipedia
That article omits the range of BT.709 one way or the other.

Matlab seems to use the different ranges for Y vs CbCr for BT.2020
Convert YCbCr color values to wide-gamut RGB color values - MATLAB ycbcr2rgbwide (mathworks.com)

And the colour-science Python module also does for all cases by default:
https://colour.readthedocs.io/en/v0.3.9/colour.models.rgb.ycbcr.html

Maybe my confusion is whether we are re-ranging the YCbCr values (which seem like they should be [16,235], [16,240], or the RGB values, which would all be [16,235].

What you selected in bold is wrong for HDMI. 16-235 is limited range and all values except 0 and 255 are allowed. 0 and 255 are used for sync in DP and HDMI.

Also, in reality it says this:

Values outside the nominal ranges are allowed, but typically they would be clamped for broadcast or for display (except for Superwhite and xvYCC)

Superwhite is a simple enough thing.

Yes, of course it does. Cb, Cr are 16-240, same for 10 bit, just multiply by 4. Same for 12 bit.

Cb for 10 bit would be 64-960

That is correct.

I fixed that in YCbCr article. “The same quantisation ranges, different for Y and Cb, Cr also apply to BT.2020 and BT.709.”

You are right val.zapod.vz, my mistake.

Coming back to the original question, the confusing part of the sample code in ColorSpace.cu.

The constant value of 235 is in GetConstants():

void inline GetConstants(int iMatrix, float &wr, float &wb, int &black, int &white, int &max) {
    black = 16; white = 235;

which only fixes the max value for white. The actual clamping that happens is in [0..maxf] as expected.

That said, there is an engineer looking at the sample and checking if it has any inconsistencies.