color-js / color.js

Color conversion & manipulation library by the editors of the CSS Color specifications
https://colorjs.io
MIT License
1.91k stars 82 forks source link

Add HSLuv color space #284

Closed yklcs closed 8 months ago

yklcs commented 1 year ago

The HSLuv color space bounds LCh's chroma component into a "saturation component". I'm not sure if this project is interested in adding every color space out there, but it does have some popularity, so it would be a welcome addition.

It is defined as a transformation on LCh, so implementation should be straightforward enough. I might look into creating a PR if I can.

facelessuser commented 1 year ago

Just for clarity, HSLuv is the transformation on CIELUV LCh, not LCh. The former is the cylindrical form of Luv while the latter is the cylindrical form of L*a*b*.

You'll have to at least implement Luv, CIELUV LCh, and HSLuv. The conversion path is XYZ D65 -> Luv -> CIELUV LCh -> HSLuv.

Myndex commented 1 year ago

Hi @facelessuser

The conversion path is XYZ D65....

The conversions to LUV are built into HSLuv, last I checked it was a complete conversion straight from sRGB.

I happen to like HSLuv, it updates Luv with a novel built-in constraint to realizable colors in sRGB. I like Luv in general for its simplicity, lack of purple shift¹, and the fact it has a saturation correlate in addition to chroma*, while CIELAB only has chroma.

And yes, HSLuv is otherwise essentially polar Luv as Lshuv, using the saturation correlate, which has useful advantages over chroma, one being that the saturation control is closer to being an orthogonally independent control of colorfulness or color purity.

¹ Regarding the purple shift

Click to reveal the colorful gradient examples and discussion Here are some Luv vs Lab examples from a couple years ago, before OKLab, for reference. Everything is D65, using standard CIE math. The top four are polar, and the bottom four are rectangular. For all, the gradients were created doing a simple average of each of the three correlates of which ever space, with the middle being an average of the first and last, and subdividing accordingly. Pay attention to the top row, second from left, as that is Lshuv, it is IMO the most consistent of all. **TOP, Polar Coordinates:** $`Std. HSL,\ \ Lsh_{uv},\ \ LCh_{ab},\ \ LCh_{ab}`$ **BOTTOM, Cartesian:** $` sRGB,\ \ CIE\ xyY,\ \ L^* u^* v^* ,\ \ L^* a^* b^* `$ _Click to enlarge_ Gradient explorer, showing blue gradients Here we see the infamous purple shift in LAB, especially in the rectangular coordinates version. LUV does not have such a plainly obvious shift. _Next_ Gradient explorer, showing blue to yellow gradients The purple shift in LAB continues to be an issue. Also notice that sRGB is noticeably darker and that the HSL is being more colorful than the rest, but also uneven. _Next Slide — Colorful!_ Gradient explorer, showing blue to yellow, reversed gradients In this case, the polar "hue rotation" direction was reversed, so the color palette of the top row using polar coordinates is suddenly more colorful. Also, the HSL gradient isn't doing too well. The sRGB is still a bit dark, and LAB is still polluting other colors with purple. The LAB LCh is a bit out of whack — but the LUV plots, especially Lsh are still demonstrating consistency _Last slide_ Gradient explorer, showing blue to white gradients What's going on? A pure blue to achromatic gradient can demonstrate unexpected behaviors. Notice the LAB plots and their shifts, but the LUV plots are still doing well, with expected behavior. ### _So what's wrong with luv?_ LUV is widely recognized as bad when used with diffuse, reflected surface colors. This is not in dispute. But as a color *chooser* for selecting colors for content on self-illuminated displays, it works well, and has advantages as mentioned. I have reason to believe that part of why Luv does poorly in certain contexts is that the choice of Munsell value for L*, while good with Lab, is _not_ a good fit for Luv, for reasons I am still investigating. Nevertheless Tektronix modified Luv, in particular scaling and axis substantially, for use in one of their professional broadcast color correction systems.... gosh if only I know of some perceptually uniform lightness thingy somewhere..... Thank you for reading

.

P.S. I use details/summary to make my posts appear much shorter than they really are.

facelessuser commented 1 year ago

@Myndex I'm not quite sure what you mean by "built into" HSLuv. You have to first get it into one of these spaces to then apply the transform to HSLuv: https://github.com/hsluv/hsluv/blob/master/math/cie.mac.

Is it self-contained in their repo (all of the conversions)? Sure, but the transformation to HSLuv requires you to first get it into LChuv, then transform it to HSLuv. These steps are separate even if they are all done within their repo. I've implemented it here: https://github.com/facelessuser/coloraide/blob/main/coloraide/spaces/luv.py and https://github.com/facelessuser/coloraide/blob/main/coloraide/spaces/hsluv.py.

If you are going to go ahead and add support for HSLuv, you might as well expose Luv and LCHuv as you have to apply the transform from XYZ anyway. If you want to hide it all in the HSLuv implementation, you can surely do that, but you deprive people of being able to use Luv/LCHuv directly.

Anyway, I think my statement holds true, but it can be implemented however it is desired here. Personally, I would expose all the color spaces as the full Luv color space can be useful if you are not confined to an sRGB-limited variant of it.

Myndex commented 1 year ago

@facelessuser Ah I misunderstood your point, my bad.

The other color spaces are already here, largely.

CIE Lab, XYZ are already in color.js ... Luv isn't but uses the same L* as Lab, and the LCh for uv is identical to the LCh for ab. So literally all that's missing is the uv projection from XYZ... and the HSLuv package...

facelessuser commented 1 year ago

CIE Lab, XYZ are already in color.js ... Luv isn't but uses the same L* as Lab, and the LCh for uv is identical to the LCh for ab. So literally all that's missing is the uv projection from XYZ... and the HSLuv package...

Yes, all of this could be leveraged, and if that is how it is wanted here, that is fine. I think it would be a shame to limit support to HSLuv only, but 🤷🏻‍♂️. Either way, I don't think it is difficult to add support for any of these.

The implementation is pretty straight forward. I think to officially claim HSLuv support, you should implement HPLuv as well (per their documentation). They provide tests to confirm your implementation: https://github.com/facelessuser/coloraide/blob/main/tests/test_hsluv/snapshot-rev4.json.