RGB to L*ab conversion

Home Forums General Discussion RGB to L*ab conversion

Viewing 15 posts - 1 through 15 (of 17 total)
• Author
Posts
• #10162

Hello

I am hoping a colour expert can answer my question (hopefully Florian 🙂 )

If we convert RGB values of 127.5, 127.5 , 127.5  to L*ab,  assuming a white point of D65 and 2 degree observer, the L component turns out to be 53 instead of the expected 50.

formulas here : https://www.easyrgb.com/en/math.php

I presume this is due to some mismatch between some assumption about RGB gamma encoding and what L*ab assumes the encoding was?     If so, is there some way to correct for this?

Thanks!

#10182

Hi,

If we convert RGB values of 127.5, 127.5 , 127.5 to L*ab, assuming a white point of D65 and 2 degree observer, the L component turns out to be 53 instead of the expected 50.

which RGB are we talking about? sRGB? Why would you expect a 50% RGB gray value to yield an L* of 50?

https://www.easyrgb.com/en/convert.php

I can see the default there is sRGB. For input R=G=B 127.5, L* 53 (53.389) is correct.

#10204

Why would you expect a 50% RGB gray value to yield an L* of 50?

I read on wikipedia  “L* represents the darkest black at L* = 0, and the brightest white at L* = 100”.     I figured 127.5 is the midpoint of those two points and so L* should be 50.    And then I saw HSB,HSL and YCrCb are reporting 50% as well which lead me to believe it should be 50%.   But  then I found HSB and HSL are very bad models and abandoned them after reading Charles Poynton.    I am only learning this in the last 48hrs so you’ll have to forgive my ignorance here 🙂

So I think I found the reason:  sRGB gamma is only 2.2 at 50% grey , and perceived lightness is more like 2.4 or thereabouts, so sRGB 50% is really a little bit brighter than half.  Is my understanding correct?  If so, is it possible to modify the conversion from sRGB  to XYZ  to use a simple power law instead of sRGB’s funky gamma?

Thanks

#10205

First attempt at a solution.

Replacing:

if ( var_R > 0.04045 ) var_R = ( ( var_R + 0.055 ) / 1.055 ) ^ 2.4
else var_R = var_R / 12.92

with just:

var_R = var_R ^ 2.4

Does that seem correct?

#10218

Ok I get it now, Lab is using L* function for lightness (modified cube root) so even with the above modification its never going to be on parity with the RGB values anyway, unless someone happens to calibrate their monitor to the L* function, which will never happen.

Basically what I am trying to do is determine an “RGB combined” value for the video card gamma table contents.    eg. if we have a white balance that required reducing a lot of green, then more perceived brightness was lost than if we had picked a white balance that reduced, say, only the  blue channel.     The trouble I’m having is that the vcgt values are nonlinear values in that they correspond to perceived brightness and not luminance, so the “RGB combined” result would be a measure of perception and therefore needs to make some assumption about the way we perceive different combinations of R, G and B.

So L*ab is not suitable for above reasons, HSB and HSL are not suitable either because they say many white points have the same brightness, so what does that leave?   YCrCb it seems is the only option left?   The Y component is determined by ( 0.2126 * R)  +  (0.7152 * G) +  (0.0722 * B) and these are considered perceptual quantities (“luma” , not to be confused with luminance) which does give us what seems like a decent result.   But I’m not sure it’s correct either, because it seems to assume that our perception of colour is a linear function of the luminance coefficients ( 0.2126, 0.7152, 0.0722 ) .

edit: and then there’s the issue of the user having a nonlinear gamma table which corresponds to a calibrated display state,  so the values in the vcgt don’t necessarily correspond to perceived brightness anyway.    Maybe just use (R+G+B)/3  ?

#10241

vcgt values are nonlinear values in that they correspond to perceived brightness and not luminance,

vcgt in computer monitor calibration for any given R’G’B’ input value represents the change that is needed to the video card output values to make the native display response match the tone curve chosen for calibration.

Basically what I am trying to do is determine an “RGB combined” value for the video card gamma table contents.

What’s the use case?

YCrCb

Note that Y’CbCr luma is gamma-compressed, so it can’t be used in a case where you want linear light (relative luminance) without first performing gamma expansion.

I still have to understand what you actually want to do.

#10249

I still have to understand what you actually want to do.

Sorry, sometimes I don’t even know myself 🙂

So if we view vcgt curves on a graph,  we normally get RGB plotted on the graph at the same time (3 lines in the one graph).    I am trying to come up with a fourth line (“all” or “combined”) which is the combined values of R+G+B.

But this is probably a lost cause, as you say, all the vcgt does is “represent the change needed to make the display response a certain way” so it doesn’t really make sense to try to infer anything perceptual from the vcgt.    Unless maybe if the user starts off with linear.cal and knows their monitor gamma, then maybe we could make some perceptual inferences based on the vcgt.

#10918

I think I have finally got my head around this.   It seems we could take a row of RGB values from the vcgt, convert them to linear, then weight them by the primary coefficients , then add the values together, then convert that value back to nonlinear.     The end result seems like it should represent the “average” of an entire row of the vcgt.

I had gotten a bit confused because I thought I needed to convert sRGB nonlinear -> XYZ -> CIE L* -> sRGB nonlinear ,  as I was lead in that direction by this fire breather example on wikipedia   https://en.wikipedia.org/wiki/HSL_and_HSV#Disadvantages

Side note:  for the luma example, it’s strange that they used the rec 601 primaries  since sRGB and rec 709 share the same primaries.   The converter at colorizer.org also uses 601.   Any ideas why?

#10932

So this is the formula I seem to have working at the moment.

AverageBrightness =  (    ((Red^Gamma) * 0.2126)  + ((Green^Gamma) * 0.7152) + ((Blue^Gamma) * 0.0722)    )  ^  (1/Gamma)

It produces values similar to the Y component of YCrCb for many colours, but on some colours they get a very different result.

For example with this colour Red 0.5 , Green 0, Blue 0

Y of YCrCb gives  (0.5 * 0.2126) + (0 * 0.7152) + ( 0 * 0.0722) = 0.10

AverageBrightness formula with a gamma value of 2.2 =  0.24

But on a colour like a skin tone of Red 0.6 Green 0.4 Blue 0.2  they give 0.43 and 0.44 respectively.

I am not sure about the accuracy of Y, because it applies the linear primary coefficients to the nonlinear Red Green and Blue values.   It seems like it would be wrong to scale a nonlinear quantity by a linear quantity?

#10952

It seems we could take a row of RGB values from the vcgt, convert them to linear

No, you cannot convert vcgt values to linear light. They have no meaning without the display system they’re part of. If you want to get the relative luminance that a certain combination of RGB values would produce on screen, simply look them up through the display profile (e.g. using Argyll’s xicclu command line tool).

#10964

Thanks, understood.

I should have mentioned in my application I am getting the gamma from TARGET_GAMMA in the .cal file (if it exists, otherwise, the user has to enter it manually).

For the white point, I could get that from the TARGET_WHITE_XYZ in the .cal file if possible, or the icc file as you have mentioned (the user provides either .cal or .icc).

Another problem I have is because my i1d2 is not accurate, I set a custom white point by eye which to my eyes looks closer to neutral white (no idea if it actually is) so the white point reported in the .cal/icc file is not necessarily accurate and that would produce wrong results too when trying to convert vcgt to linear.

I take your point though, we can’t just go assuming whatever gamma and white point in the calculation and expect things to be accurate.    But if the user doesn’t provide their white point, then I am forced to make a guess, and the best guess would be… probably sRGB/Rec.709.

edit: also there is that issue of the vcgt values not actually correlating to nonlinear light anyway, eg. values of 0.5,0.5,0.5 might correlate to 0.55,0.55,0.55 (nonlinear) at the screen in order to correct for the display’s nonlinearities, so the table values can’t really be taken to mean anything.   It’s just a correction amount.   But still, the same applies to R,G and B individually when I plot them on the graph,  and I have to plot something on my graph for “all channels combined”.    So they don’t actually have to be an accurate representation of what’s happening at the screen.

#10967

Just to clarify, when I plot individual RGB channels on a graph,  those values aren’t intended to correlate to nonlinear quantities at the screen.   It’s just depicting the state of the vcgt, nothing more.     But I still need to plot an “all channels combined” on the chart.        This is kind of a weird problem, because doing so seems to require making some assumptions about the display’s characteristics, and yet, it’s not actually meant to be representative of the display characteristic (it’s just meant to show the state of the vcgt, nothing more).    In that sense, maybe it makes more sense to simply do (R+G+B/3)?

#10968

Also I should have mentioned that in my application, if the user provides a nonlinear cal file which correlates to a calibrated display state, then my colour transformations won’t operate on table values directly.    Instead it operates on table indexes and then matches the modified indexes to new values by interpolated lookup from the original table.     This might provide an opportunity for me to plot “all channels combined” for the modified indexes rather than the actual table values, possibly avoiding the need to plot an inverse curve (or however it is xicclu is doing it, the documentation for it is difficult for me to understand).   Also I think xicclu would be too slow for me to call directly because I need to look up all 255 values rapidly while the user is dragging a slider (the graph is plotted and updated in real time).

#10970

Ok, I just read at the bottom xicclu can do a batch lookup of lots of values, which speeds things up considerably, but it’s tricky, and frankly still too slow, and antivirus programs don’t like it when .exe files get called rapidly (this is why I had to switch to directly calling SetDeviceGammaRamp  instead of using dispwin.exe , also it’s less CPU usage, less page faults )

#10971

Just to clarify, when I plot individual RGB channels on a graph,  those values aren’t intended to correlate to nonlinear quantities at the screen.   It’s just depicting the state of the vcgt, nothing more.

EXCEPT when the user starts out with a linear vcgt (which is a typical use case scenario of my app), then they do correlate to nonlinear quantities at the screen!

Viewing 15 posts - 1 through 15 (of 17 total)

You must be logged in to reply to this topic.