question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Converting sRGB to LCH when: Red Green and Blue are Equal (Bug)

See original GitHub issue

When attempting to convert an sRGB to LCH, where the chroma should be 0, produces unexpected values in the Chroma and Hue channels. The chroma value appears to have a floating point error when it should be 0, and the hue value is always NaN, which causes math problems.

Take the following examples:

new Color('srgb', [0.1, 0.1, 0.1]).to('lch').coords
(3) [9.010443527068848, 0.000006337680094534959, NaN]

new Color('srgb', [0.13333, 0.13333, 0.13333]).to('lch').coords
(3) [13.227498037447411, 0.000007406287423610401, NaN]

new Color('srgb', [0.4, 0.4, 0.4]).to('lch').coords
(3) [43.192291386569806, 0.000014999406423138985, NaN]

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:16 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
svgeesuscommented, Apr 9, 2021

Unless I do the first-principles spectral conversion and get different results in the first 5 figures, it will be these ones going forward.

I ran your Python program with these XYZ values, and got the same results as my JavaScript program.

1reaction
facelessusercommented, Apr 7, 2021

So I’ve gone through the math, I now understand why color.js is so off.

The sRGB matrix that color.js uses is based off a calculated white point for D65:

white = [0.3127 / 0.3290, 0.3290 / 0.3290, 0.3583 / 0.3290]

This gives us

[0.9504559270516716, 1.0, 1.0890577507598784]

But then we use the following D65 white point any time we do chromatic adaption:

[0.95047, 1.00000, 1.08883]

This gives us quite a bit of error.

With the following code, doing nothing tricky, I follow the algorithm calculating the XYZ conversion matrix based on this algorithm: http://www.brucelindbloom.com/index.html?Math.html.

I will output the first results based on using a consistent white point with what is used in chromatic adaption and then I’ll output the same matrix with a calculated white point.

import numpy as np

white_consistent = [0.95047, 1.00000, 1.08883]
white_calc = [0.3127 / 0.3290, 0.3290 / 0.3290, 0.3583 / 0.3290]

def get_matrix(wp):
    # Red to xyY
    xr = 0.64
    yr = 0.33
    # Green to xyY
    xg = 0.30
    yg = 0.60
    # Blue to xyY
    xb = 0.15
    yb = 0.06

    m = [
        [xr/yr, 1.0, (1.0-xr-yr)/yr],
        [xg/yg, 1.0, (1.0-xg-yg)/yg],
        [xb/yb, 1.0, (1.0-xb-yb)/yb]
    ]
    mi = np.linalg.inv(m)

    r, g, b = np.dot(wp, mi)
    rgb = [
        [r],
        [g],
        [b]
    ]
    rgb2xyz = np.multiply(rgb, m).transpose()
    xyz2rgb = np.linalg.inv(rgb2xyz)
    print(rgb2xyz)
    print(xyz2rgb)

print('======== Using a consistent white point with our chromatic adaption')
get_matrix(white_consistent)

print('======== Using the calculated white point')
get_matrix(white_calc)

The first results match what is used in the brucelindbloom calculator and give much better results. The second matches the matrix that is currently used.

======== Using a consistent white point with our chromatic adaption
[[0.41245644 0.35757608 0.18043748]
 [0.21267285 0.71515216 0.07217499]
 [0.0193339  0.11919203 0.95030408]]
[[ 3.24045416 -1.53713851 -0.49853141]
 [-0.96926603  1.87601085  0.04155602]
 [ 0.05564343 -0.20402591  1.05722519]]
======== Using the calculated white point
[[0.4123908  0.35758434 0.18048079]
 [0.21263901 0.71516868 0.07219232]
 [0.01933082 0.11919478 0.95053215]]
[[ 3.24096994 -1.53738318 -0.49861076]
 [-0.96924364  1.8759675   0.04155506]
 [ 0.05563008 -0.20397696  1.05697151]]

Based on this, I am much more confident with my opinion on this. My recommendation would be to use the matrix that is consistent with the white point that is being used everywhere else or update the white point in chromatic adaption to match what is used in the matrix calculations.

Read more comments on GitHub >

github_iconTop Results From Across the Web

s RGB ↔L*a*b*↔LCh ab conversions - mina86.com
Importantly, the conversion between s RGB and L*a*b* goes through XYZ colour space. As such, the full process has multiple steps with a...
Read more >
LCH colors in CSS: what, why, and how? – Lea Verou
E.g. 0 is not red, but more of a magenta and 180 is not turquoise but more of a bluish green, and is...
Read more >
RGB Color Space Conversion - RyanJuckett.com
Red, Green and Blue Primaries. Chromaticity coordinates are given for each primary of the display. For sRGB space, they are as follows: red...
Read more >
How the L C H Values Could Be Seen as a value on an Image
I am trying to check an image file to reach its color values and I could see RGB but while trying to check...
Read more >
Better than Lab? Gamut reduction CIE Lab & OKLab - W3C
sRGB blue to neutral: CIE Lab. red is higher than green. Play Play. Compare that to the same thing in CIE LCH Again ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found