289 votesby stackoverflow.com/users/10315269written 2019-06-20last updated 2024-03-17
The "Accepted" Answer is Incorrect and Incomplete
The only answers that are accurate are the @jive-dadson and @EddingtonsMonkey answers, and in support @nils-pipenbrinck. The other answers (including the accepted) are linking to or citing sources that are either wrong, irrelevant, obsolete, or broken.
Briefly:
sRGB must be LINEARIZED before applying the coefficients.
Luminance (L or Y) is linearly additive as is light.
Perceived lightness (L*) is nonlinear to light as is human perception.
HSV and HSL are not even remotely accurate in terms of perception. (Not perceptually uniform).
The IEC standard for sRGB specifies a threshold of 0.04045 it is NOT 0.03928 (that was from an obsolete early draft).
To be useful (i.e. relative to perception), Euclidian distances require a perceptually uniform Cartesian vector space such as CIELAB. sRGB is not one.
What follows is a correct and complete answer:
Because this thread appears highly in search engines, I am adding this answer to clarify the various misconceptions on the subject.
Luminance is a linear measure of light, spectrally weighted for normal vision but not adjusted for the non-linear perception of lightness. It can be a relative measure, Y as in CIEXYZ, or as L, an absolute measure in cd/m2 (not to be confused with L*).
Perceived lightness is used by some vision models such as CIELAB, here L* (Lstar) is a value of perceptual lightness, and is non-linear relative to light, to approximate the human vision non-linear response curve. (That is, linear to perception but therefore non-linear to light).
Brightness is a perceptual attribute, it does not have a "physical" measure. However some color appearance models do have a value, usually denoted as Q for perceived brightness, which is different than perceived lightness. Note: Brightness is not to be confused with luminous energy, which is also denoted with Q, but is not necessarily calculated the same as brightness for a given color appearance model.
Luma is a gamma encoded and spectrally weighted signal used in some video encodings (such as Y´I´Q´ or Y´U´V´). The prime symbol after the Y (Y´) indicates the data is gamma encoded. Luma is not to be confused with linear luminance (Y or L per above).
Weighting means the digital values for each of the red, green, or blue primary colors in a display are adjusted so that an equal value of red, green, and blue results in a neutral gray or white. This is defined in the given color space, such as BT.709. in other words, 255 blue is much darker than 255 green, in accordance with the luminous efficiency function.
Gamma or transfer curve (TRC) is a curve that is often similar to the perceptual curve, and is commonly applied to image data for storage or broadcast to reduce perceived noise and/or improve data utilization (and related reasons).
In the early days of electronic imaging, it was often referred to as Gamma Correction.
Solution: To estimate perceived lightness
(This assumes conditions close to the reference environment)
TO FIND LUMINANCE:
...Because apparently it was lost somewhere...
Step One:
Convert all sRGB 8 bit integer values to decimal 0.0-1.0
vR = sR / 255;
vG = sG / 255;
vB = sB / 255;
Step Two:
Convert a gamma encoded RGB to a linear value. sRGB (computer standard) for instance requires a power curve of approximately V^2.2, though the "accurate" transform is:
Where V´ is the gamma-encoded R, G, or B channel of sRGB.
Pseudocode:
function sRGBtoLin(colorChannel) {
// Send this function a decimal sRGB gamma encoded color value
// between 0.0 and 1.0, and it returns a linearized value.
if ( colorChannel <= 0.04045 ) {
return colorChannel / 12.92;
} else {
return pow((( colorChannel + 0.055)/1.055),2.4);
}
}
Step Three:
To find Luminance (Y) apply the standard coefficients for sRGB:
Pseudocode using above functions:
Y = (0.2126 * sRGBtoLin(vR) + 0.7152 * sRGBtoLin(vG) + 0.0722 * sRGBtoLin(vB))
TO FIND PERCEIVED LIGHTNESS:
Step Four:
Take luminance Y from above, and transform to L*
Pseudocode:
function YtoLstar(Y) {
// Send this function a luminance value between 0.0 and 1.0,
// and it returns L* which is "perceptual lightness"
if ( Y <= (216/24389)) { // The CIE standard states 0.008856 but 216/24389 is the intent for 0.008856451679036
return Y * (24389/27); // The CIE standard states 903.3, but 24389/27 is the intent, making 903.296296296296296
} else {
return pow(Y,(1/3)) * 116 - 16;
}
}
L* is a value from 0 (black) to 100 (white) where 50 is the perceptual "middle grey". L* = 50 is the equivalent of Y = 18.4, or in other words an 18% grey card, representing the middle of a photographic exposure (Ansel Adams zone V).
2023 Edit:
Adds by Myndex in an effort toward completeness:
Some of the other answers on this page are showing the math for Luma (Yʹ in other words Y prime) and luma should never be confused with relative luminance (Y). And neither is perceptually uniform lightness nor brightness.
Luma, while it is gamma compressed and may be useful for encoding image data for later decoding, is not perceptually uniform especially for more saturated colors. As such, Luma is generally not a good metric for perceived lightness.
Luminance is linear per light, but not linear per perception, in other words, it is not perceptually uniform, and therefore not useful for predicting lightness perception.
Lstar L* in my answer above is often used for predicting a perceived lightness. It is based on Munsell Value, which is derived from experiments in a specifically defined environment using large diffuse color samples.
L* is not particularly context sensitive, i.e. not sensitive to things like simultaneous contrast, HK, or other contextual sensations.
Therefore, L* can only take us part way to an accurate model of perceived lightness.
L* along with CIELAB is good for determining "small differences" between two items, i.e. close to the "Just Noticeable Difference" (JND threshold).
L* becomes less accurate at larger supra-threshold levels and large differences, where other contextual factors begin to have a more substantial effect.
Supra-threshold curve shapes are different than the JND threshold curve.
L* puts perceptual middle grey at 18%.
Actual perceived middle grey depends on context.
Middle contrast for high spatial frequency stimuli (such as text) is often much higher than 18%.
Contrast perception is more complicated than the difference between two lightness values, and simple difference or ratios are of limited utility, and not uniform over the visual range.
Additional References:
IEC 61966-2-1:1999 Standard
sRGB
CIELAB
CIEXYZ
Charles Poynton's Gamma FAQ
Reference links in the above text:
Luminance
Lightness
Lightness vs Brightness at CIE
Luminous energy
Luma
YIQ
YUV and others in the book Video Demystified
Rec._709
Luminous efficiency function
Gamma correction