sharkdp / pastel

A command-line tool to generate, analyze, convert and manipulate colors
Apache License 2.0
5k stars 97 forks source link

"rgb-float" format should probable return linear "sRGB" values to be useful. #85

Open KelSolaar opened 4 years ago

KelSolaar commented 4 years ago

Hi,

I was testing pastel a bit, very nice tool! I noticed that the values returned by the rgb-float format are non-linearly encoded, i.e. they have the sRGB inverse EOTF applied, which limit their usefulness:

(colour-3.7) Kali:debug kelsolaar$ ./pastel format rgb-float "#808080"
rgb(0.502, 0.502, 0.502)
(colour-3.7) Kali:debug kelsolaar$ python -c "import colour;print(colour.convert('#808080', 'Hexadecimal', 'Scene-Referred RGB'))"
[ 0.2158605  0.2158605  0.2158605]

Often, when people are working with floating-point colours, they need to use linear, i.e. scene-referred RGB, colours to drive a shader for example. I would thus suggest adding a new format, e.g. rgb-linear, that would yield linear colours.

Cheers,

Thomas

KelSolaar commented 4 years ago

Interestingly luminance returns what I would expect:

(colour-3.7) Kali:debug kelsolaar$ ./pastel format luminance "#808080"
0.216

It makes me wonder if instead of having a new rgb-linear, rgb-float should simply not return scene-referred RGB colours.

sharkdp commented 4 years ago

Thank you for the feedback.

The current "rgb-float" format is simply the RGB values, divided by 255. In your example, 0x80 = 128 and 128/255 is approximately 0.502.

Luminance, on the other hand, is computed in Lab space.

If you could point me to some sources where I can read up on the differences between "linear"/scene-referred RGB, I'm happy to take a look.

KelSolaar commented 4 years ago

The current "rgb-float" format is simply the RGB values, divided by 255. In your example, 0x80 = 128 and 128/255 is approximately 0.502.

Yes I saw!

Luminance, on the other hand, is computed in Lab space.

It is actually more the fact that you are decoding the non-linearly encoded values with sRGB EOTF here:

https://github.com/sharkdp/pastel/blob/4305363ec1cdaa3821d223d3b5f97b3ad1b2b9a0/src/lib.rs#L553

Which is what I think the suggested rgb-linear format (or even rgb-float) should do IMHO.

A bit of reading on Motion Pictures Colour Management: https://cinematiccolor.org/

Definition of Scene-Referred (Image State) as per ISO 22028-1:

3.36
scene-referred image state
image state associated with image data that represents estimates of the colour-space coordinates of the elements of a scene

and definition of Output-Referred (Image State) also as per ISO 22028-1:

3.33
output-referred image state
image state associated with image data that represents the colour-space coordinates of the elements of an image that has undergone colour-rendering appropriate for a specified real or virtual output device and viewing conditions

The former represents linear light values in the scene, i.e. the real world colour values that can be measured with a light meter, given in relative or absolute terms. The latter represents non-linear light values that have been encoded, e.g. with sRGB inverse EOTF, to be displayed on a device.

Rendering and lighting-related computations are performed in a linear colourspace, i.e. a scene-referred colourspace, and values we input to those systems are of course most of the time linear.

For example, in the VFX industry, we traditionally use the 18% reflectance gray card as our perceptual middle gray. Its value is obviously 0.18 in Scene-Referred RGB and and 0.4613561295 in Output-Referred RGB assuming you used the sRGB inverse EOTF to encode it. In a rendering engine, we would use 0.18 directly as an input value for a shader.

Hope it makes sense :)

PS: Note how the non-linearly encoded sRGB value of 128 is close to 18%, i.e. 0.216, when decoded to Scene-Referred RGB.