Could certainly do it that way, if you don't need the brightness value.
We have a color picker that sorts a hash of "available" hexadecimal colors by their brightness value, though, so we needed to have it available as an independent method.
Yeah. I wrote the long one of those. What you should use for best results is something like CIELAB lightness. Basically the steps are:
1. Take "gamma-compressed" RGB and scale to the range [0, 1] (if it's an 8-bit integer representation, divide by 255), and convert to linear RGB: undo the sRGB nonlinearity or for the lazy, take each component to the 2.2 power.
2. Take a weighted sum of R, G, B components. The weights for an sRGB/HDTV image are .2126, .7152, and .0722 for R, G, and B respectively. This gives you the luminance, Y.
3. Apply some non-linearity to the luminance to get a decent correlate of human-perceived lightness. A gamma curve w/ 2.2 gamma (that is, raise your value of Y to the (1/2.2) power) actually is an okay function to use. If you want a better one, feel free to use the CIELAB definition: L = 116 Y^(1/3) - 16, where L ranges from 0 to 100.
4. Anything with L >= 50, use black, anything with L < 50, use white.
(properly L*, but hacker news will think I'm using the asterisk to indicate emphasis if there is more than one of them)
Since this is likely only being calculated for a few colors (even up to dozens or hundreds), there's really not enough overhead in doing this math to worry about the speed.
I found the HSV/HSL article immensely useful on a semi-recent project, as Google seems to have forgotten about the original references I used when I first learned about those two colorspaces (i.e. students' and professors' personal web sites, found using the Altavista search engine).
Actually, now that I think about it, if the only goal is to decide on black or white text, steps 3–4 aren't really necessary. Just undo the gamma compression (1), take a weighted sum or linear RGB intensities (2), and then pick a cut-off for Y of 18% or 18.4% (or whatever you prefer), about at mid gray.
I wanted to do something similar to figure out which of the 256 colours available in my terminal-emulator would be reasonably visible against a black background. I wrote a bash script to answer the question, at the heart of which was this function:
get_luminance_from_color() {
# Return the luminance of a color as a number 0-65535.
local red=$1
local green=$2
local blue=$3
# Colour weights from Wikipedia. Note bash doesn't have floats.
echo $(( (2126 * $red + 7152 * $green + 722 * $blue) / 10000 ))
}
...which is to say, then input is an RGB tuple where each field is 0-65535, and the output is a single integer between 0-65535 representing the luminance. If the luminance is > 32767, it's a light colour, otherwise it's a dark colour.
13 comments
[ 3.1 ms ] story [ 62.4 ms ] threaddef contrasting_text_color(background_hex_color) (background_hex_color.scan(/../).map {|color| color.hex}).sum > 382.5 ? '#000' : '#fff' end
We have a color picker that sorts a hash of "available" hexadecimal colors by their brightness value, though, so we needed to have it available as an independent method.
'#000' if int(my_color, 16) > 0xffffff/2 else '#fff'
This approach isn't perfect, but it does the job.
"#000000" if sum([int(i,16) for i in re.findall("..",myColour)]) > 382.5 else "#ffffff"
http://en.wikipedia.org/wiki/Colorspace http://en.wikipedia.org/wiki/YUV http://en.wikipedia.org/wiki/HSL_and_HSV http://en.wikipedia.org/wiki/YIQ
The sample images at http://en.wikipedia.org/wiki/HSL_and_HSV#Disadvantages show the difference between various brightness calculations.
1. Take "gamma-compressed" RGB and scale to the range [0, 1] (if it's an 8-bit integer representation, divide by 255), and convert to linear RGB: undo the sRGB nonlinearity or for the lazy, take each component to the 2.2 power.
2. Take a weighted sum of R, G, B components. The weights for an sRGB/HDTV image are .2126, .7152, and .0722 for R, G, and B respectively. This gives you the luminance, Y.
3. Apply some non-linearity to the luminance to get a decent correlate of human-perceived lightness. A gamma curve w/ 2.2 gamma (that is, raise your value of Y to the (1/2.2) power) actually is an okay function to use. If you want a better one, feel free to use the CIELAB definition: L = 116 Y^(1/3) - 16, where L ranges from 0 to 100.
4. Anything with L >= 50, use black, anything with L < 50, use white.
(properly L*, but hacker news will think I'm using the asterisk to indicate emphasis if there is more than one of them)
Since this is likely only being calculated for a few colors (even up to dozens or hundreds), there's really not enough overhead in doing this math to worry about the speed.