6

It seems there are 3 main variations on the mapping matrix, and they diverge from about the 3rd or 4th decimal place. Which would be considered the standard matrix?

  1. Bruce Lindbloom http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
    Calculated from (x, y) for red, green, blue, and D65 ref white (X, Y, Z)
  RGB -> XYZ
  +0.4124564 +0.3575761 +0.1804375  
  +0.2126729 +0.7151522 +0.0721750  
  +0.0193339 +0.1191920 +0.9503041  
  XYZ -> RGB (by inverting RGB -> XYZ)
  +3.2404542 -1.5371385 -0.4985314  
  -0.9692660 +1.8760108 +0.0415560  
  +0.0556434 -0.2040259 +1.0572252  
  1. W3C https://www.w3.org/Graphics/Color/srgb
    EasyRGB https://easyrgb.com/en/math.php seems to use W3C, but truncated
  XYZ -> RGB
  +3.2406255 -1.5372080 -0.4986286  
  -0.9689307 +1.8757561 +0.0415175  
  +0.0557101 -0.2040211 +1.0569959  
  1. Wikipedia https://en.wikipedia.org/wiki/SRGB#Specification_of_the_transformation
    Claims to be the sRGB specification
  XYZ -> RGB
  +3.24096994 -1.53738318 -0.49861076  
  -0.96924364 +1.87596750 +0.04155506  
  +0.05563008 -0.20397696 +1.05697151  
boot-and-bonnet
  • 731
  • 2
  • 5
  • 16

4 Answers4

5

Strictly speaking, none of those is correct because they have been derived from the primaries and whitepoint given by IEC 61966-2-1:1999 and rounded at some arbitrary decimal places. So two real choices here, either you use the matrices as given by the standard, i.e. rounded at 4 decimal places or you compute the normalised primary matrix and its inverse directly at full machine precision, ideally double precision.

IEC 61966-2-1:1999

MATRIX_sRGB_TO_XYZ = np.array([
    [0.4124, 0.3576, 0.1805],
    [0.2126, 0.7152, 0.0722],
    [0.0193, 0.1192, 0.9505],
])
"""
*sRGB* colourspace to *CIE XYZ* tristimulus values matrix.

MATRIX_sRGB_TO_XYZ : array_like, (3, 3)
"""

MATRIX_XYZ_TO_sRGB = np.array([
    [3.2406, -1.5372, -0.4986],
    [-0.9689, 1.8758, 0.0415],
    [0.0557, -0.2040, 1.0570],
])
"""
*CIE XYZ* tristimulus values to *sRGB* colourspace matrix.

MATRIX_XYZ_TO_sRGB : array_like, (3, 3)
"""

ITU-R BT.709 which IEC 61966-2-1:1999 uses the primaries and whitepoint from, does not specify the matrices, so for example, computing them at double-precision:

>>> import colour
>>> import numpy as np
>>> np.set_printoptions(formatter={'float': '{:0.15f}'.format}, suppress=True)
>>> colour.models.RGB_COLOURSPACE_BT709.matrix_RGB_to_XYZ
array([[0.412390799265959, 0.357584339383878, 0.180480788401834],
       [0.212639005871510, 0.715168678767756, 0.072192315360734],
       [0.019330818715592, 0.119194779794626, 0.950532152249661]])
>>> colour.models.RGB_COLOURSPACE_BT709.matrix_XYZ_to_RGB
array([[3.240969941904523, -1.537383177570094, -0.498610760293003],
       [-0.969243636280880, 1.875967501507721, 0.041555057407176],
       [0.055630079696994, -0.203976958888977, 1.056971514242879]])

Technically, the rounding differences should be absorbed by any quantization effect of using 8-bit integer representation, but they have consequences when you working with floating-point values and the IEC 61966-2-1:1999 matrices do not round-trip properly for example.

The problem lies in the fact that providing both primaries/whitepoint and conversion matrices from/to RGB to/from CIE XYZ creates ambiguity. Which one do you choose? People will tend to pick the matrices because they are already computed, which is easy to verify by running a basic Google Search.

For interchange with other software, you would probably want to pick the published matrices, however, for internal colour conversion work, the derived matrices are preferred because you will suffer less from the rounding if you perform a lot of back and forth conversions. Practically speaking though, you will find that it does not matter too much.

Kel Solaar
  • 3,660
  • 1
  • 23
  • 29
  • The important condition is that the second raw of the matrix sums up to 1 (in RGB to XYZ matrix) and it is what is BT.709 YCbCr coeff. are and BT.709 matrix is contructed from. This is all discussed in SMPTE RP 177. Also XYZ to RGB matrix specified in this more accurate form (not that it is correct) in NIF RGB spec, which was the initial version of sRGB. http://www.graphcomp.com/info/specs/livepicture/fpx.pdf page 55 – Валерий Заподовников Apr 10 '21 at 14:38
3

An earlier revision of CSS Color 4 used the matrices from Lindbloom's site. I changed the sample code to use directly calculated ones, which removed some troubling round-tripping errors.

If I follow the methodology for calculating those values (also given on Lindbloom's site) I don't get the answers he does, for sRGB. I suspect an error crept into his calculation, perhaps the wrong white point value?

  • Hi Chris, I believe Bruce is/was using the whitepoint from ASTM E308-01, the values of which are: 0.3127266150 and 0.3290231300 RELATED: Elle Stone has an article on the many different whitepoints in various "standards" at https://ninedegreesbelow.com/photography/well-behaved-profiles-quest.html#white-point-values – Myndex Sep 16 '21 at 00:33
2

The W3C definition linked to is the earliest, pre-standardization proposal. It is deprecated, because it had errors due to excessive roundoff. The eventual IEC 61966-2-1:1999 corrected this error, and was further revised in IEC 61966-2-1:1999/AMD1:2003.

There is a W3C definition in CSS Color 4, which references the primary chromaticities and white point from the IEC standard https://drafts.csswg.org/css-color-4/#valdef-color-srgb The (informative) sample code accompanying that specification has matrices calculated from those chromaticities at double precision, and given without any rounding https://drafts.csswg.org/css-color-4/#color-conversion-code

If rounded to 8 decimal places, they exactly match the matrices given in the Wikpedia article.

1

So wikipedia had a mistake, in the IEC standard 4 decimal places XYZ matrix is used (since the inverted matrix 2nd row must be BT.709-2 matrix coefficients, see SMPTE 177). It was introduced here: https://en.wikipedia.org/w/index.php?title=SRGB&diff=949593434&oldid=946057212

I reverted that change and now both matrices are as in IEC. Also I added higher precision XYZ to BT.709 matrix from 1st ammendment of sRGB IEC standard (yeah, I have it). They say it is enough for 16 bit. They also just revert 4 decimal places matrix to XYZ, not further defining it. https://en.wikipedia.org/w/index.php?title=SRGB&diff=1018331095&oldid=1018328711

Also, of course, @kel-solaar is correct here, you can futher define both of the matrices, but what he is not clarifying is that the inversion should be much higher precision, since that is how inversion of matrices works. (Not YCbCr that is just how YCbCr matrix works (2 zeroes and 3 ones in the inverted matrix after all), but that is the case for XYZ and ICtCp, with ICtCp matrix with 14 decimal points is recommended to use by ITU.) Also, I do not think you need to really increase precision, just increasing the precision of inverted matrix is enough.