-
Notifications
You must be signed in to change notification settings - Fork 690
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
[css-color-4] Gamut Mapping with Oklch - Odd Results #7071
Comments
LCH result seems to correlate with what you can see here in the top left corner : https://romainmenke.github.io/lab-lch-display-p3/lab-interactive.html Is it possible that the values in your interpolation go outside the spectral locus and are not real colors? That is what that corner is in my experiments. I can imagine that with interpolation you can easily hit this area. |
It may. And I could certainly analyze and plot the point relative to the spectral locus, but I'm probably less concerned with the why as much as I am with understanding whether this is truly the results that are desired for the gamut mapping algorithm in CSS. Every color space has strengths and weaknesses. Oklab/Oklch is generally a far better interpolation space than Lab/Lch. What I am questioning is whether it works as well as the gamut mapping space (at least in the algorithm's current form). There are a number of cases where I think a user would get something saner than maybe what is returned with using Oklch (as the algorithm currently works). Even in the example where we are interpolating in Oklch and then mapping with Oklch, the transition to blue has a very sharp transition. This is exaggerated far more in the Lch interpolation/Oklch mapping. I guess what it boils down to is what the priority in the gamut mapping algorithm is. Personally, I'd take slightly less "accurate" mapping if, visually, it gave something close enough to what I'd expect in most common, practical use cases. Jarring transitions, like in the opening post, makes it far less attractive. I just figured it was worth bringing up as a discussion point. |
@facelessuser wrote:
Oh, it certainly is worth bringing up, thanks for doing so. Worth pointing out that interpolation in a polar space is frequently going to produce out of gamut intermediate colors, because of the irregular gamut shape of all RGB spaces. We have a way to take a straight line between two points (use a rectangular space for interpolation) and to take the maximal-chroma arc, but not a way to take a shallower arc (apart from adding intermediate stops). On a first glance, the flat area in the OKLCH-mapped CIE LCH interpolation is troubling; it is as if the whole area has a single hue (in OKLCH) but different hues in CIE LCH. But then one might expect to see a similar effect for the CIE LCH-mapped OKLCH interpolation. I need to investigate this more deeply, with a look at the numerical values for the generated interpolation and for the gamut-mapped results, at key points along the gradient. @romainmenke wrote:
Looking at your page, was the first column generated in coloraide too, like this: color.convert('oklch').fit('srgb', method='oklch-chroma').convert('srgb').fit('srgb', method='clip').coords(); or by some other method? |
No problem 🙂. If it is helpful, here is the example live. |
@svgeesus wrote :
This was created using the sample code in this repo. There is a noticeable shift in luminosity for non-real colors in the teal region. let colorALAB = new Color('lab(50%, -127, -127)');
let colorAOKLCH = colorALAB.to('oklch');
let colorA = new Color('color(oklch 0.42 1.95 191)');
let colorB = colorA.toGamut({method: 'oklch.chroma', space: 'srgb'});
let colorC = colorB.to('srgb');
let colorA2LAB = new Color('lab(50%, 0, 127)');
let colorA2OKLCH = colorA2LAB.to('oklch');
let colorA2 = new Color('color(oklch 0.56 0.19 102)');
let colorB2 = colorA2.toGamut({method: 'oklch.chroma', space: 'srgb'});
let colorC2 = colorB2.to('srgb'); I do not want to drag the conversion off topic, so please ignore this if it is not relevant. Update : Do please ignore the above. |
I don't think Color.js has quite implemented things yet to work the same with Oklch. Notice the Hue shift in your example for You can see here that single colors do, in fact, have the same issue. It is not related to interpolation: live example We can see in the example that the hue is held pretty much constant with only mild deviations in hue and lightness due to clipping. >>> colorALAB = Color('lab(50% -127 -127)')
>>> colorAOKLCH = colorALAB.convert('oklch');
>>> colorA = Color('color(--oklch 0.42 1.95 191)')
>>> colorB = colorA.clone().fit("srgb", method='oklch-chroma')
>>> colorC = colorB.convert('srgb')
>>> colorA, colorB, colorC
(color(--oklch 0.42 1.95 191 / 1), color(--oklch 0.42897 0.0739 190.78 / 1), color(srgb 0 0.36119 0.34903 / 1)) And we get the same weird colors that were seen when interpolating: EDIT: The first color above is normal looking only because the visualizer uses Lch chroma reduction to show colors by default. The other two we forced Oklch chroma reduction. |
Yes, the oklch gamut mapping has been implemented, but is still on a separate branch because I always meant to test it more before merging to main. |
Interestingly, when the problematic interpolation colors are diluted with some white, the color in Oklch increases in hue throughout the interpolation. But when we don't, the hue increases up to a point, but then drops (through dark green-ish area) and then ramps back up at the end. In the good interpolation, it steadily increases from ~142 - ~264. In the problematic interpolation, the hue goes from ~142 - ~203 and then drops to as low as ~192 - ~198 and then rises more rapidly at the end catching back up to ~264. |
To corroborate some results, here's what I noticed in my demo, if you try to gamut map CIELCH in OKLCH (that is, Illustrate space: LCH D50, Mapping space: OKLCH), even with simple chroma reduction. And this bothersome band in the blues: These two effects make it sound like there's some sort of clipping going on in the conversion process? The plots use culori.js, so I'm not excluding implementation errors, and in my particular implementation gamut mapping CIELCH in OKLCH space involves the following conversion chain: cielch -> cielab -> xyz d50 -> xyz d65 -> lrgb -> rgb -> lrgb -> xyz d65 -> oklab -> oklch -> (chroma reduction) and back, so there are several moving pieces. |
I did some poking around and find weird discontinuities on CIE LCH as well. Also, exploring the CIE Lab L=50 plane over a,b = -125 to +125 I am struck by the OKLab a value in the -125,-125 corner: lab(50% 125 125) = oklab(60.58% 0.357 0.152) |
I'd like to preface this, stating, Oklab and Oklch work great as interpolation spaces, but I am questioning how well it works for gamut mapping (at least with the current algorithm). Now, I understand that Oklch does a much better job at preserving hue, but in practice, when gamut mapping spaces, it seems to give odd results (in very specific cases).
Consider these different interpolations. We are interpolating from
green
toblue
in various spaces. All colors are gamut mapped to sRGB.The first set of examples is gamut mapping the interpolated colors into sRGB with Oklch chroma reduction. Notice how poor the results are for the Lch interpolation. Also, notice the harsher transition in the Oklch interpolation example on the far right, it is more subtle (this is very minor, but we'll see the difference when compared to Lch gamut mapping).
Here we are using Lch to gamut map. Everything gives nice, clean transitions and gamut mapping is visually more appealing.
Obviously, such cases are specific to very certain color cases, but this case doesn't seem too far-fetched of one.
Granted, the interpolations of Oklab and Oklch generally look nicer on both, but gamut mapping with Oklch chroma reduction vs Lch chroma reduction turns out very differently.
Maybe this is all generally expected and accepted, but I wanted to at least bring the topic up as it would be a very jarring case when interpolating simple colors like
green
andblue
with Lch. Ignoring whether hue is preserved better or not through the gamut mapping process with one space vs the other. Visually, Lch seems to give better results for what I think people would expect.The text was updated successfully, but these errors were encountered: