colour-science / colour

Colour Science for Python
https://www.colour-science.org
BSD 3-Clause "New" or "Revised" License
2.12k stars 263 forks source link

About illuminant chromaticity coordinates calculation. #220

Closed henczati closed 8 years ago

henczati commented 9 years ago

Shouldn't these give the same results? Which should be preferred?

In:

# Pre-computed whitepoint
xy1 = colour.ILLUMINANTS['cie_2_1931']['D65']

# Spectrum of ill.-on-perfect-reflector to XYZ to xy
xy2 = colour.XYZ_to_xy(colour.spectral_to_XYZ(colour.ILLUMINANTS_RELATIVE_SPDS['D65'], colour.STANDARD_OBSERVERS_CMFS['cie_2_1931']) / 100.)

print np.array([xy1, xy2])

Out:

[[ 0.31271     0.32902   ]
 [ 0.31271107  0.32900848]]
KelSolaar commented 9 years ago

In the perfect world they should be the same, thing is that there are multiple "standards" defining the whitepoints with various degree of precision and method of computation, Elle Stone has a quite good informative page for D50 / D65 in that regard: http://ninedegreesbelow.com/photography/well-behaved-profiles-quest.html

Most of the illuminant chromaticity coordinates we define are coming from Wikipedia, if not they are computed.

This is not a big problem but still is something I have in mind and would like to address at some point.

nick-shaw commented 8 years ago

And the commonly used values are truncated further to (0.3127, 0.329). These should ideally give the same result, but don't:

In:

colour.RGB_COLOURSPACES['ALEXA Wide Gamut RGB'].RGB_to_XYZ_matrix

colour.normalised_primary_matrix(colour.RGB_COLOURSPACES['ALEXA Wide Gamut RGB'].primaries,
                                 colour.RGB_COLOURSPACES['ALEXA Wide Gamut RGB'].whitepoint)

Out:

array([[ 0.638008,  0.214704,  0.097744],
       [ 0.291954,  0.823841, -0.115795],
       [ 0.002798, -0.067034,  1.153294]])

array([[ 0.63799645,  0.21470105,  0.09773104],
       [ 0.29194867,  0.82383027, -0.11577894],
       [ 0.00279823, -0.06703336,  1.1531355 ]])

ARRI clearly use the truncated values in their calculation:

In:

colour.normalised_primary_matrix(colour.RGB_COLOURSPACES['ALEXA Wide Gamut RGB'].primaries,
                                 (0.3127, 0.329)).round(6)

Out:

array([[ 0.638008,  0.214704,  0.097744],
       [ 0.291954,  0.823841, -0.115795],
       [ 0.002798, -0.067034,  1.153294]])
KelSolaar commented 8 years ago

And the commonly used values are truncated further to (0.3127, 0.329).

I was considering adopting those actually, I will have to check the ASTM & CIE literature to see if there are any recommendations.

KevinJW commented 8 years ago

ah yes, I had that problem when dealing with colour spaces do you use the 'official' number so your matrix is the same as those often specified (best for when inter-operating with other tools) or use more precise definitions, derive from XYZ or specific co-ordinates, etc. I did not come to a good conclusion so I implemented all of them in my plugins. I did not consider deriving from spectral data. as that was one step too far for my requirements.

Kevin ​

KelSolaar commented 8 years ago

I did not come to a good conclusion

So do I :(

Re-reading @ellelstone page (now it is good that she is on Github! Hi Elle :)), I see that Bruce Lindbloom's tristimulus values are apparently taken from ASTM E308-1, I will have to check that specific version of the publication but I didn't see them in the up-to-date ASTM E308-15, there are some tristimulus values but they are rounded to 3 digits:

D65 ASTM E308-15: (95.407, 100.000, 108.883)

Latest https://github.com/colour-science/colour/tree/develop branch implements ASTM E308-15 spectral conversion and thus is as ASTM official you can be.

Note: Seems like CIE recommends linear interpolation for Illuminant D Series (I get same result for the non interpolated version anyway):

print(colour.XYZ_to_xy(colour.spectral_to_XYZ(
        colour.ILLUMINANTS_RELATIVE_SPDS['D65'].interpolate(
            colour.SpectralShape(interval=1), 'linear') / 100)))
# [ 0.31272632  0.32902331]
KelSolaar commented 8 years ago

Well, actually, the discrepancies I saw between Lindbloom's values and ours are probably related to precision issues when calculating the chromaticity coordinates, I just saw those tristimulus values on his website: (0.95047 1.00000 1.08883), they are the same than ASTM E308-15.

I propose to replace the current D65 chromaticities with (0.31271, 0.32902), and add either in the comments (I prefer that version) or with explicit dict key names the other official values.

nick-shaw commented 8 years ago

I have looked at the documentation from ARRI, Sony and Panasonic, and the matrices they give from their respective primaries to XYZ all match those you get if you use (0.3127, 0.329), not (0.31271, 0.32902).

Canon's documentation does not give an NPM, but the matrix they give from CineGamut to P3 matches what you get if you use (0.3127, 0.329) and CAT02.

RED obviously don't publish primaries, so deriving D65 RED primaries and NPM will be self consistent if you use the same CAT and values for D65 throughout.

KelSolaar commented 8 years ago

@Nick-Shaw: Sorry, I was meaning (0.3127, 0.3290), I copy pasted the existing chromaticities and didn't edited them after the fact. :[

nick-shaw commented 8 years ago

I did wonder!

KelSolaar commented 8 years ago

I just quickly checked, so the following matrices are manually input and not computed using colour.normalised_primary_matrix definition:

Of those with the colourspaces that have discrepancies are the following:

If I change D65 chromaticities I end up having those colourspaces breaking now:

I'm wondering if we don't need more attributes in colour.RGB_COLOURSPACE class to store the official specification values.

KelSolaar commented 8 years ago

D65 has now chromaticities of (0.3127, 0.3290) in the feature/alternative_illuminants_chromaticities branch.

Two things:

References

  1. Spaulding, K. E., Woolfe, G. J., & Giorgianni, E. J. (2000). Reference Input/Output Medium Metric RGB Color Encodings (RIMM/ROMM RGB), 1–8. Retrieved from http://www.photo-lovers.org/pdf/color/romm.pdf
KevinJW commented 8 years ago

The copy of IEC 61966-2-1 I have refers to:

Display white point x = 0,3127, y = 0,3290 (D65)

and

Reference Ambient White Point x = 0,3457, y = 0,3585 (D50)

The reference observer is the CIE 1931 two-degree standard observer from ISO/CIE 10527.

​This is confirmed in https://www.w3.org/Graphics/Color/srgb FWIW.

The matrix is specified with less precision than the above link:

| X |   | 0.4124 0.3576 0.1805 | | R sRGB |
| Y | = | 0.2126 0.7152 0.0722 | | G sRGB |
| Z |   | 0.0193 0.1192 0.9505 | | B sRGB |

and the inverse:

| R sRGB |   |  3.2406 −1.5372 −0.4986 | | X |
| G sRGB | = | −0.9689  1.8758  0.0415 | | Y |
| B sRGB |   |  0.0557 −0.2040  1.0570 | | Z |
nick-shaw commented 8 years ago

I'm wondering if we don't need more attributes in colour.RGB_COLOURSPACE class to store the official specification values.

https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf gives (0.3127, 0.3290) as the reference white point for Adobe RGB 1998, so it would seem that using (0.3127, 0.3290) is consistent with the official spec values for Rec. 709, sRGB and Adobe RGB 1998.

That document also specifies an (inverse) NPM for Adobe RGB 1998 which is consistent with those values, although they only give it to five decimal places. I don't know what the matrix in adobe_rgb_1998.py is, but it probably needs to be made consistent with the new white point, either by direct calculation, or as an inverse of the five decimal place one in the document. Thoughts as to which?

nick-shaw commented 8 years ago

Oh. Ignore my NPM comment above. I did not read far enough down the document, and see an NPM is specified, and is used in the updated adobe_rgb_1998.py

KelSolaar commented 8 years ago

Thanks @KevinJW for the sRGB official NPM!

I'll roll the canonical D50 whitepoint now.