# Using HSV colour space to compute luminance of an RGB value

This topic is 4412 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Courtesy of the stuff in my journal today, I've been thinking a bit about colour spaces and their properties. My other project is an update of the 'HDR Demo' sample I released a few months back, and I got thinking that I could improve the luminance calculations that the shaders employ. Currently I do a simple inner product on the RGB with a suitable bias to get a greyscale value. It does the job, but it's not particularly scientific. The problem occurs when you have a single channel that is very bright - say RGB(15.0, 0.0, 0.0) - the overall average is brought down by the two very low values. I'm wondering if the Value component of HSV or the Lightness component of HSL would do a better job? Has anyone seen such an implementation, or care to comment on it as a potential idea? Looking at the mathematics on the linked pages, I'm not sure if the respective components will measure luminance any better than the current one [oh] Cheers, Jack

##### Share on other sites
How about doing a simple vector magnetude? Since you don't actually care about the specific colour, it should work fine...

##### Share on other sites
Quote:
 Original post by python_regiousHow about doing a simple vector magnetude? Since you don't actually care about the specific colour, it should work fine...

That sounds to simple, but it'd probably work pretty well [smile]

Hadn't thought about interpretting the values as vectors - was too hung up on them being *colours* rather than just tuples/numbers [headshake]

I think I'd best go run some tests with these 4 metrics and see which one generates the best results...

Cheers,
Jack

##### Share on other sites
You can just take max value of R, G and B. It works pretty well in practice.

##### Share on other sites
Quote:
 Original post by jollyjeffersI'm wondering if the Value component of HSV or the Lightness component of HSL would do a better job?

I was visiting Paul Bourke's web again, and I found this on the RGB to HSV page
Quote:
 Value is similar to Luminance except it also varies the colour saturation.

As a consequence I guess that you should use L rather than V. You can see the difference between HSV and HSL on Bourke's page.

The main problem in your application is your range - I can't see what a luminance > 1 would represent in our physical world. Would it be whiter than white? [smile]

##### Share on other sites
Quote:
 Original post by Emmanuel DelogetThe main problem in your application is your range - I can't see what a luminance > 1 would represent in our physical world. Would it be whiter than white? [smile]

That's called HDR ;)

##### Share on other sites
Quote:
 Original post by jollyjeffersI think I'd best go run some tests with these 4 metrics and see which one generates the best results...

As much for reference as anything else... I've implemented the 4 metrics and they look like so:

Simple Average: GreyValue = dot( color.rgb, float3( 0.33f, 0.33f, 0.33f ) )

Weighted Average: GreyValue = dot( color.rgb, float3( 0.299f, 0.587f, 0.114f ) )

Maximum Value: GreyValue = max( color.r, max( color.g, color.b ) )

Luminance according to HSL colour space: GreyValue = 0.5f * ( max( color.r, max( color.g, color.b ) ) + min( color.r, min( color.g, color.b ) ) )

Can't say any of them are exactly what I wanted, but then again the scene isn't exactly the best for HDR rendering [headshake]

Cheers,
Jack

##### Share on other sites
Did you try the simple magnetude one? Would be interesting to just see what that looks like...

##### Share on other sites
Quote:
 Original post by python_regiousDid you try the simple magnetude one? Would be interesting to just see what that looks like...

Yeah, seems I forgot that one [headshake]

I was expecting it to be about the same as the max() one, but it seems to look a little better/different to me [smile]

Cheers,
Jack

##### Share on other sites
Pure R, G or B is to artificial to look good IMO.
In real life it's only lasers that are even close to pure R, G, B.
The problem is that if a red tap in your eye get's oversaturated it's "bleeds" it's energy over to the G and B taps.
In real life if something is "red enough" it starts to turn white, this is something that is hard to simulate using CG.
I even remeber a very unscientific experiment on some mailing list (or here?), where a gut actually looked directly into a red laser!
He claimed to se a white dot with a red halo.

Another classic in CG games is to have a pure red suface (1, 0, 0) lit be a very strong pure green light (0, 1, 0), what's the resulting color?
Black! In real life it's not black, why?
There are NO pure red surfaces and almost no pure green lights.

It would be cool to se a HDR implementation that tried to mimic the way energy bleeding occurs in the eye between the taps.

PS.
I do NOT recommend any of you to conduct the same experiment!
If you don't understand why, get back to grammer school and start all over.

##### Share on other sites
Quote:
Original post by jollyjeffers
Quote:
 Original post by python_regiousDid you try the simple magnetude one? Would be interesting to just see what that looks like...

Yeah, seems I forgot that one [headshake]

I was expecting it to be about the same as the max() one, but it seems to look a little better/different to me [smile]

Cheers,
Jack

The difference is enormous - you are avoiding saturation nearly everywhere, and it looks like a different scene (actually: a readable scene [smile]). It is far better.

The thing I don't understand is that I was expecting this kind of results with the L version - it should be softer than the magnitude version. L = (max(r,g,b) + min(r,g,b))/2 is smaller than the magnitude of the vector (r,g,b). This is easily seen in your small luminance maps in fact (magnitude ones are far brighter than the L ones). This is where my undertsanding is choked: it seems that the source HDR frame is not identical among the screenshots. Thus, I don't know how I can compare the results.

##### Share on other sites
If you want a proper luminance measurement from an RGB color (with a relatively simple formula), you need to convert it to YCrCb color space. The Y component is a very good approximation of the luminance. You might need to apply a gama adjustment after the calculation to get it to look right.

##### Share on other sites
Quote:
 Original post by Emmanuel DelogetThe difference is enormous - you are avoiding saturation nearly everywhere, and it looks like a different scene (actually: a readable scene [smile]). It is far better.

Hmm, on double checking things.. there is a pretty good reason why it's better - I changed a load of stuff!

I'm going through fixing/tweaking/correcting loads of small things, so the sample isn't necessarily in the same state for very long. My mistake for forgetting about said changes before posting the 5th screenshot.

Quote:
 it seems that the source HDR frame is not identical among the screenshots. Thus, I don't know how I can compare the results.

The 4 pictures I originally posted are definitely equal - even down to rotation and camera properties. You can, if you wanted, run a "difference" operator against those 4 and get valid results

One of things I changed (you can see it in the sliders on the RHS if you look closely) is the default configuration of the pipeline. In particular theres a higher bright-pass threshold and higher SD for the gaussian.

If I change the parameters to be the same as the original four:

Luminance computed using the magnitude operator

Not quite so amazing now is it [headshake]

The problem I've noticed now is that I can tweak the parameters (as seen) to reduce the saturation on the bright parts of the image; but getting the dark parts to show up properly is a real pain.

I was hoping (expecting?) that when there is *no* HDR data in the source frame the "normal" 0.0 - 1.0 scale data is very dark:

No HDR Data, Same exposure as above images.

No HDR Data, 4x higher exposure.

Cheers,
Jack

##### Share on other sites
Quote:
 Original post by ExtrariusIf you want a proper luminance measurement from an RGB color (with a relatively simple formula), you need to convert it to YCrCb color space. The Y component is a very good approximation of the luminance. You might need to apply a gama adjustment after the calculation to get it to look right.

Thanks for the suggestion. I just dropped in some default values (from the wiki page) and tested it - the only noticeable difference was that it brought out some of the dark colours better. Still very over-saturated on the bright areas though. If I get time I might look into computing Kr and Kb coefficients properly and doing the gamma correction.

Cheers,
Jack

##### Share on other sites
why can't you use an exponential drop off?

given r,g,b do

r' = 1-e^-c*r
g' = 1-e^-c*g
b' = 1-e^-c*b

with an appropriate chosen constant c. this formula is highly sensitive to c. I think this is reasonably standard for hdr stuff isn't it? I'm surprised no one has mentioned it. there are many more advanced techniques of gamma correction but this is common and produces reasonable results. certinally better then a vector magnitude. more advanced approach's take a look at the scene in it's entirity and make decisions based on that. obviously not real time, but I'm by far an expert in the field of tone mapping.

to convert to grey scale just take the weighed average after this transformation.

like

//edit
grey = sum(1/3*r' + 1/3*b' + 1/3*g');

if this is to much chop off the range again

grey' = 1 - e^-b*grey

where b is another constant

or perhaps a combonation of brightening and dimming

grey' = t*grey + (1-t)*sqrt(grey)

one possible t value could obviously be
t = grey

I'm likeing this one, if you try it show me.

for t = grey youd have

grey' = grey^2 + (1-grey)*sqrt(grey)

this would have the effect of dampening bright and stuff and boosting dim stuff best of both worlds.

perhaps a cubic interpolant would work too.

Tim

[Edited by - timw on November 14, 2005 7:33:58 AM]

##### Share on other sites
Quote:
 Original post by jollyjeffers[...]Thanks for the suggestion. I just dropped in some default values (from the wiki page) and tested it - the only noticeable difference was that it brought out some of the dark colours better. Still very over-saturated on the bright areas though. If I get time I might look into computing Kr and Kb coefficients properly and doing the gamma correction.[...]
Did you make sure to do gamma adjustment on each end of the calculation? I'm not sure its possible to do it efficiently in shaders, but if so it should make some significant difference:
http://www.poynton.com/GammaFAQ.html
http://scanline.ca/ycbcr/

-Extrarius

##### Share on other sites
Thanks for all the ideas everyone - v. useful [smile]

@timw - I tried implementing your idea, but I exploded the ps_2_0 limits (>65 arithmetic instructions) [sad]

Given that I need to get this thing tidied up and shipped off today I don't think I'll be able to spend the time optimizing it in order to get it working. I'll keep a note of it should I get time later on though!

Quote:
 Did you make sure to do gamma adjustment on each end of the calculation?

Nope, I just plugged in a couple of the values from that wiki article. Apparently for computer displays and the like:

Kb = 0.0722
Kr = 0.2126

I shall look into the gamma correction, but for the same reasons as posted above I might simply end up running out of time [headshake]

I'll post back here if I get anything else worth showing...

Cheers,
Jack

##### Share on other sites
jollyjeffers: Essentially, gamma adjustment is just a way to adjust for the way different devices respond to, record, or display colors. The adjustment is usually a power function at it's core, but the exact formula depends on the standard and exactness desired.

To simplify to the specific case of 'typical, properly-adjusted CRT monitors' and ignore standards to simplify the math:

Gamma = number between 2.35 and 2.55 (just selecting

R' = R ^ (1 / Gamma)
G' = G ^ (1 / Gamma)
B' = B ^ (1 / Gamma)

//Convert (R', G', B') -> (Y', Cb, Cr)
//Gamma doesn't affect Cb or Cr, only Y'
Y = Y' ^ Gamma

##### Share on other sites
Quote:
 Original post by Extrariusjollyjeffers: Essentially, gamma adjustment is just a way to adjust for the way different devices respond to, record, or display colors. The adjustment is usually a power function at it's core, but the exact formula depends on the standard and exactness desired.To simplify to the specific case of 'typical, properly-adjusted CRT monitors' and ignore standards to simplify the math:Gamma = number between 2.35 and 2.55 (just selecting R' = R ^ (1 / Gamma)G' = G ^ (1 / Gamma)B' = B ^ (1 / Gamma)//Convert (R', G', B') -> (Y', Cb, Cr)//Gamma doesn't affect Cb or Cr, only Y'Y = Y' ^ Gamma

Thanks for that [smile] Sounds pretty close to what I was thinking of...

Anyway, I'll look into experimenting with it when I get the time.

I fixed most of the problems I was talking about in the latter half of this thread due to realising I made a real dumbass mistake (forgetting to set the luminance to GxxRxxF instead of the original RxxF [headshake]). It looks pretty sweet now.

Cheers,
Jack

##### Share on other sites

This topic is 4412 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Create an account

Register a new account

• ### Forum Statistics

• Total Topics
628674
• Total Posts
2984159

• 13
• 12
• 10
• 9
• 9