Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

D65 values are incorrect? #79

Closed
makew0rld opened this issue Jan 22, 2021 · 5 comments
Closed

D65 values are incorrect? #79

makew0rld opened this issue Jan 22, 2021 · 5 comments

Comments

@makew0rld
Copy link

makew0rld commented Jan 22, 2021

The standard D65 XYZ values are (0.95047, 1.00000, 1.08883). This is written in Wikipedia and by Bruce Lindbloom (see here and search for D65).

But HSLuv defines them like so:

[ref_X, ref_Y, ref_Z] : xyY_to_XYZ(rat(0.3127), rat(0.3290), 1);

If I remove the rat and run that calculation, the output XYZ values are (0.95045592705167, 1, 1.089057750759878). This is quite a bit off from what I believe are the correct values, and it is throwing off my calculations that use the standard D65 as I defined at the beginning. Here's an example of the difference between what using the standard D65 will output, versus the HSLuv D65:

    hsluv_test.go:98: Testing public methods for test case #ffff00
    hsluv_test.go:67: result: [1.000054 *, 0.999991 *, -0.002083 *] expected: [1 1 0], testing HsluvToRGB with test case #ffff00
    hsluv_test.go:67: result: [85.880145 *, 1.000332 *, 0.971386] expected: [85.87432021817473 1.0000000000007272 0.9713855934179674], testing HsluvFromHex with test case #ffff00
    hsluv_test.go:67: result: [85.880145 *, 1.000332 *, 0.971386] expected: [85.87432021817473 1.0000000000007272 0.9713855934179674], testing HsluvFromRGB with test case #ffff00
    hsluv_test.go:117: Testing internal methods for test case #ffff00
    hsluv_test.go:67: result: [1.000054 *, 0.999991 *, -0.002083 *] expected: [1 1 0], testing convLchRgb with test case #ffff00
    hsluv_test.go:67: result: [0.971386, 1.070642 *, 85.880145 *] expected: [0.9713855934179674 1.0708560884692067 85.87432021817473], testing convRgbLch with test case #ffff00
    hsluv_test.go:67: result: [0.971386, 0.076918 *, 1.067875 *] expected: [0.9713855934179674 0.07704219177275 1.06808111250898], testing convXyzLuv with test case #ffff00
    hsluv_test.go:67: result: [0.769989 *, 0.927808, 0.138372 *] expected: [0.76997513864982 0.92780768463926 0.13852559851021098], testing convLuvXyz with test case #ffff00

The D65 values affect these variables:

hsluv/math/cie.mac

Lines 48 to 49 in 005e50b

ref_U: (4 * ref_X) / (ref_X + (15 * ref_Y) + (3 * ref_Z));
ref_V: (9 * ref_Y) / (ref_X + (15 * ref_Y) + (3 * ref_Z));

Which goes on to affect everything else.

I believe this could all be fixed by manually setting the [ref_X, ref_Y, ref_Z] variables to (0.95047, 1.00000, 1.08883). I know that I am quite inexperienced with color theory though, and so I'd appreciate learning if I'm wrong here.

For now I will specify a custom white reference for HSLuv calculations only.

@boronine
Copy link
Member

D65 XYZ values are derived from D65 xyY values using the standard formula (with Y being set to 1). It seems that the discrepancy comes from whether to use 4- or 5-decimal space rounding for the x and y numbers. A lot of sources use 5 decimal place rounding 1 2. Bruce Lindbloom references ASTM E308 - 01 which I assume also rounds like this.

HSLuv uses 4 decimal place rounding which seems to be encoded in the sRGB standard:
https://en.wikipedia.org/wiki/SRGB#The_sRGB_gamut
https://en.wikipedia.org/wiki/Rec._709

Even if 5 decimal place rounding is better, it's certaintly not worth making a revision of the color space for this. I added some comments about this to cie.mac for future reference. If there is every a revision 5 of HSLuv we can consider sneaking higher precision numbers into it.

@makew0rld
Copy link
Author

So you're saying if I used a white reference equal to (0.9505, 1.0000, 1.0888), that would be more correct? And the problem with the one I was using was that it had one extra decimal place?

If that's true than I'm confused why using the longer one (0.95045592705167, 1, 1.089057750759878) would make my code work. Especially since that one isn't even accurate to 4 decimal places, when compared to the other one.

@boronine
Copy link
Member

With regards to your code, I can't really comment since I don't know what exactly you're testing.

I'm urging you to think practically though. What are you trying to achieve where this difference is an issue?

Numbers generated by all these algorithms are always approximations. In the case of colors, remember that the general standard is 24-bit color, which means 8 bits per channel, which means the smallest difference that can be encoded is 1 / 256 ~ 0.004. That means all decimal places after that are noise.

Another thing to keep is mind is that the precision of your output numbers can only be as good as the least precise of your input numbers. (depending on the calculation.. see: https://en.wikipedia.org/wiki/Propagation_of_uncertainty)

So any benefit of increasing the precision of these:
https://github.com/hsluv/hsluv/blob/master/math/cie.mac#L26-L27

... might be lost unless you also increase the precision of these:
https://github.com/hsluv/hsluv/blob/master/math/cie.mac#L20-L25

@makew0rld
Copy link
Author

If I use a D65 value of (0.95047, 1.00000, 1.08883) and run the HSLuv test suite with a delta of 1.0/256.0, none of the tests that measure the conversion to RGB fail. But I get errors for the conversions from RGB. Obviously this does not really matter practically, but it'd be nice to get these to pass as well. So I used the different white point.

Here's all the errors for one color. Keep in mind this library scales the values from 0-1, rather than 0-255 or 0-100.

    hsluv_test.go:65: result: [85.885855 *, 0.504866, 0.423764] expected: [85.87432021817236 0.5050715546882035 0.4237638616967416], testing HsluvFromHex with test case #666644
    hsluv_test.go:65: result: [85.885855 *, 0.504866, 0.423764] expected: [85.87432021817236 0.5050715546882035 0.4237638616967416], testing HsluvFromRGB with test case #666644
    hsluv_test.go:65: result: [85.885855 *, 0.706252, 0.423764] expected: [85.87432021817236 0.7065317593121714 0.4237638616967416], testing HpluvFromHex with test case #666644
    hsluv_test.go:65: result: [85.885855 *, 0.706252, 0.423764] expected: [85.87432021817236 0.7065317593121714 0.4237638616967416], testing HpluvFromRGB with test case #666644
    hsluv_test.go:65: result: [0.423764, 0.235854, 85.885855 *] expected: [0.4237638616967416 0.23594798873422232 85.87432021817236], testing convRgbLch with test case #666644

I think I'd rather keep the accuracy and those tests, and I don't think using a different white point will really cause any issues.

@Tynach
Copy link

Tynach commented Sep 15, 2022

So you're saying if I used a white reference equal to (0.9505, 1.0000, 1.0888), that would be more correct? And the problem with the one I was using was that it had one extra decimal place?

@makeworld-the-better-one, I think they're talking about the rounding applied to the chromaticity coordinates, not the final XYZ value for the white point. The sRGB standard states that D65 should be considered to have the chromaticity coordinates '0.3127, 0.3290', which approximately expands to an XYZ value of (0.9504559270516717, 1, 1.089057750759878).

If that's true than I'm confused why using the longer one (0.95045592705167, 1, 1.089057750759878) would make my code work. Especially since that one isn't even accurate to 4 decimal places, when compared to the other one.

Personally, I'm confused why nobody else just defines it as '0.3127, 0.3290' and then compute the more useful parameters at compile time at maximum precision.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants