w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.52k stars 673 forks source link

[css-color] D50 mismatch in Lab ↔ sRGB algorithms #2492

Closed danburzo closed 6 years ago

danburzo commented 6 years ago

In Section 17. Sample code for color conversions of the CSS Color Module Level 4 spec, there's a discrepancy with the D50 white point values.

In the XYZ ↔ Lab conversion code, it's assumed to be [0.9642, 1.0000, 0.8249] (as specified by the ICC spec).

However, the D50 ↔ D65 chromatic adaptation uses Bradford matrices from this page, which have been computed for D50 being [0.96422, 1.00000, 0.82521].

I think the correct approach is to use the ICC-defined D50 throughout, no?

(Also, I'm not clear on why the ICC value for the D50 white point differs from the generally accepted one. e.g. Matlab).

danburzo commented 6 years ago

It may be useful to specify in the section about lab() and lch() colors the specific values for the D50 white point as defined in the Profile Connection Space: [0.9642, 1.0000, 0.8249].

This document contains some matrices that may be useful.

svgeesus commented 6 years ago

Thanks for reporting the discrepancy!

neither ICC nor Lindbloom are authoritative for D50, which is defined by the CIE. I will check up and update the spec (and sample code) with the correct value.

I will also check in Wyszecki & Stiles when I get home.

danburzo commented 6 years ago

Thanks!

So if I understand correctly, the intent of the spec is to:

  1. assume CIELAB is relative to D50 (as defined by CIE, not ICC); and furthermore,
  2. the method of computing sRGB ↔️ CIELAB (in particular, the choice of matrices) is non-normative.

In regards to 1, this sentence:

D50 is also the whitepoint used for the profile connection space in ICC color interconversion,

I think may benefit from clarifying the distinction between the ICC definition of D50 and the CIE definition of D50.

In regards to 2, I'm a bit concerned that I've seen many variations on the matrices used to get sRGB ↔️ XYZD65 ↔️ XYZD50, or direct sRGB ↔️ XYZD50 pairs, and I wonder whether some well-defined matrices in the spec won't lead to more consistent implementations, since the approximations and the floating-point errors have a compounding effect on the result.

P.S. This page another set of matrices, and this sentence which further confuses things:

and the values of D65 and D50 from CIE 15.2 (and the ICC specification for D50) D65 = [95.04; 100; 108.89]; D50 = [96.42; 100; 82.49];

Not having access to CIE 15.2, I'm having a hard time piecing things together...

svgeesus commented 6 years ago

I've seen many variations on the matrices used to get sRGB ↔️ XYZD65 ↔️ XYZD50, or direct sRGB ↔️ XYZD50 pairs

Firstly, you can't do any of those with just a matrix; the sRGB needs to be linearized first. But having done that step, the operations for lin_sRGB to XYZ_D65, and XYZ_D65 to LMS, and white adjustment, and LMS to XYZ_D50 are all 3x3 matrices so the overall matrix is obtained by matrix multiplication.

The slightly different value for the Z component of D50 in ICC is a mistake, not an intentional change, but one they are now unable to correct without requiring all deployed CMS to be updated.

I feel that it would be better to express the correct values in the spec, with enough significant digits to avoid roundoff error.

danburzo commented 6 years ago

Also, in the Lab to LCH code, I think Math.atan2() should be normalized to the interval [0, 360).

svgeesus commented 6 years ago

Yes, indeed it should. Good catch. Although H is also defined to wrap, if someone enters a hue outside that range.

danburzo commented 6 years ago

Ah yes, I had somehow misread that H is <number> not <hue>. Too much time in front of the screen :)

danburzo commented 6 years ago

Some updates from my D50 saga: As I wrote in this thread, I was not able to find appropriate matrices for the conversion between sRGB and CIELab that did not introduce non-negligible errors. One particular doozy is that grayscale RGB colors will produce a Chroma of up to 0.0035 in the LCH equivalent.

danburzo commented 6 years ago

One last thing is nagging me: is there a benefit to keeping the CIELab values relative to D50 rather than sRGB's D65? Since the standard D50 does not match the values from ICC / Profile Connection Space... Defining lab() as relative to D65 would make computations simpler.

svgeesus commented 6 years ago

Yes, the value is that everyone else uses D50. If you use a spectrometer to measure color it will give you a readout in Lab with a D50 whitepoint.

danburzo commented 6 years ago

Thank you for the clarification!