Mapping to output gamut

Hi,

If you have a TIFF, please send it. No need for the Rec709 one, I think, since the gamut of R709 is the same as that of sRGB, isn’t it?

Anyway, I’ve added a simple Lab-based tone mapper (if I understand correctly, that’s completely wrong, since Lab is no good for HDR). This is the output:

LCh(ab):

LCh(uv):

The SonyF25 scene with the Lab version:

1 Like

In Björn Ottosson’s writeup of his OKLab design, he touches on the problems that CIE-Lab has (I’m sure there are more and better sources for this but his writeup is very nice). It’s more than just not-good-for-HDR. for CIE-Lab the hue skews in the blues are readily visible when white is mixed in, reds skew very orange, lightness prediction is so-so etc.
https://bottosson.github.io/posts/oklab/
And more specific
https://bottosson.github.io/posts/oklab/#blending-colors
and for completeness OKLCh which kind of needed a different L that aligns more with LCh(ab)
https://bottosson.github.io/posts/colorpicker/#intermission—a-new-lightness-estimate-for-oklab

Sorry if you knew all this already and should I sound like a broken record.

1 Like

A few papers address this topic if you want to explore CIELAB and HDR. :wink:

The more I think about it, converting that one to a TIFF that has bounded range doesn’t really convey what I was going for. But your tone mapping experiment showed the same thing quite well - I was anticipating to see the skew to purple that is produced by CIELAB. That is very well visible in the sweep images you posted.

If you are going to do more tests, Oklab certainly would be worth testing. No added complexity over CIELAB, but better results.

(Re)read all the blog posts while you are at it. :wink: (@PhotoPhysicsGuy already linked to specific sections.)

OKLab is giving me a real headache.

A value of OKLCh(0.999933, 0, 0), or OKLab(0.999933, 0, 0), using the matrices provided on the page, is translated into XYZ(0.950279, 0.999799, 1.088081) in double maths, which is sRGB(1.000222, 0.999754, 0.998999). So, starting from a neutral colour below white (L < 1), I end up outside sRGB. :frowning:
In 16-bit linear terms, that’s about 65550, so my safety checks kill the processing.

Your result seems to be consistent with this table (which contains rounded values)
XYZ - OKLab pairs
But yeah, it’s odd that it doesnt convert to an achromatic in gamut value. Do the matrices to go from OKLab to linear-sRGB do the same thing?
lin-sRGB OKLab

EDIT:
this may be due to the fact that OKLab is defined by the LMS cone fundamentals and not in terms of the old XYZ1931 definition of color-matching-functions. In this regard OKLab is a “new” design just like Yrg is, if I recall correctly.

This would mean that if you convert from XYZ1931 to sRGB that you can get out of gamut colors, as the “newer” XYZ that you are in technically needs a different matrix that converts to sRGB. I assume your matrix for converting from XYZ to sRGB leans on a XYZ1931 definition.

Also: I could still be missing more technicalities here.

The matrices were updated 2021-01-25. The new matrices have been derived using a higher precision sRGB matrix and with exactly matching D65 values. The old matrices are available here for reference. The values only differ after the first three decimals.

Depending on use case you might want to use the sRGB matrices your application uses directly instead of the ones provided here.

It could be factors such as precision, choice of transforms, illuminants and constants. G’MIC is a good resource for experimentation.

I took my D65 XYZ values from IEC 61966-2-1, Illuminant D65 - Wikipedia
0.9504559, 1, 1.0890578

Using the matrices from A perceptual color space for image processing, that causes a failure:
Comparison failed for actual = Xyz[0.950470, 1.000000, 1.088300] and expected = Xyz[0.950456, 1.000000, 1.089058]

However, Bruce Lindbloom’s page Welcome to Bruce Lindbloom's Web Site lists values from ASTM E308-01, and with those, the test passes:
0.95047, 1, 1.08883

That white point breaks some other tests, where I took the test data from Online Color Converter - Colormath, which apparently uses the other D65 definition. :stuck_out_tongue:

See also https://www.colour-science.org/, I think it has all of these color spaces and transforms defined, and more: GitHub - colour-science/colour: Colour Science for Python

Curiously, ASTM E308-01 AND IEC 61966-2-1 specify the whitepoint as CIE 1931 XYZ. Hmm. IEC 61966-2-1 defines two matrices with different precisions.
IEC 61966-2-1 (first hit preview in google)
One with four decimal points for converting from XYZ to 8-bit sRGB and one for higher bit depth sRGB with 7 decimal points (F8 and F8’).

Wikipedia is not the best source. Not saying this particular entry is wrong — I am not checking for you :stuck_out_tongue: — but I have seen incorrect numbers and misinformation in colour-related articles.

I recall Elle pointing out that Bruce had an error in his transform docs.

@KelSolaar has an account on this forum. :wink:

Some OKLab conversions, including tone mapping using OKLab L:


From HDR input:

2 Likes

Looks great! Especially for the simplicity of the approach. Benefits a lot from swapping out CIELch(ab) to OKLch.

Note that you cannot compare them directly, as tone mapping is always done using the ‘native L’ of the space.

Fun explorations! And good progress so far :smiley:

I have been thinking about the topic as well and I see a need for being very specific in what boundaries we want to map to and from!

Do we have any upstream promises from modules coming before the output gamut mapping in pipeline?

I can say what sigmoid gives as a promise.
All pixels will always stay within [black target, white target) of the chosen linear working profile.

This means that you always now the theoretical max C! The damping curve can then be computed from this theoretical max and applied to all pixels without any if-else logic → should keep the results more consistent over large sets of images!

Hmmm… The difference you see may come from the tone mapping, which I only added because we have HDR input. Without that, I see pretty much no difference – but I’m not really sensitive to colour, I’m afraid.

This left side of this image comes from OKLab, the right from CIELab. Chroma is compressed when it exceeds 90% of the max allowed by sRGB:

Amplified differences:

Same here (left is OKLab, right is CIELab):

Amplified differences:

With LUV, the differences (due to the pink shift) are noticeable even for me. :slight_smile:

1 Like

@kofa At a glance, using an uncalibrated display, Oklab looks more uniform. Could you please flip the left and right-hand sides for comparison?

fwiw, this is how vkdt handles explicitly requested gamut bounds and chromaticity changes:

this is the input from a playraw:


(note that it’s a screengrab colourmanaged for my display that is slightly > srgb but colours are still out of gamut here)

and this is what it looks like if i desaturate (it follows lines of constant hue, nonlinear):


(so what looked red in clipped is actually a bit orange)

and this is the image with gamut restricted to rec709 (note that the sat parameter is still at 1.0 here):

and increasing chromaticity from here will “squish” the colours near the specified gamut boundary, by moving the less saturated colours more:

i haven’t been very successful when experimenting with OKLab, especially the far out of gamut and hdr cases yielded funny results if i remember correctly.

Feel free to compare them. Chroma compression starts at 70 or 90% of the gamut maximum (see the filename).

CIELAB:


CIELUV:


OKLAB:


CIELAB:


CIELUV:


OKLAB: