Thursday, 17 May 2007

human biology - HSL Lightness vs Eye sensitivity

No, the L axis in the HSL color space does not really correspond to lightness as perceived by the human eye, except in the very crudest sense that, for given values of the H and S components, increasing L produces an increase in the perceived lightness.



The L component of the standard HSL color space is simply calculated as the average of the lowest and the highest RGB component of the color (in whichever RGB color space in being used): $$L = \frac{\min(R, G, B) + \max(R,G,B)}2$$



This definition alone should make it obvious that the L value cannot correspond to perceived lightness, since it completely ignores the middle one of the three RGB channels. Thus, the color (R=0, G=0, B=1), i.e. pure $\color{#0000ff}{\rm blue}$, has exactly the same L value (0.5) as the color (R=0, G=1, B=1), i.e. $\color{#00ffff}{\rm cyan}$, yet it's obvious at a glance that the latter color is visually much brighter.



Indeed, the HSL color space doesn't even try to match the perceptual lightness of the colors; rather, the definition of the L channel is chosen because it has the convenient feature that it maps black to L=0, white to L=1, and all the six maximally saturated pure RGB colors (red, yellow, green, cyan, blue and magenta), despite their wide perceptual lightness differences, to L=0.5.




A much better approximation to the perceived lightness of a color is the luma Y', which is defined as a weighted average of the RGB channels. The precise weights depend on the definition and the RGB color space used, but a common choice (from ITU-R Rec. 709) is: $$Y' = 0.2126\, R + 0.7152\, G + 0.0722\, B$$



The weighs are chosen to account for the different perceived lightness of the different RGB primary colors. A striking feature is the that the green component alone accounts for over 70% of the luma, nearly ten times as much as the blue component. Yet a visual comparison of $\color{#0000ff}{\rm 100\%\ blue}$ with $\color{#00ff00}{\rm 100\%\ green}$ does clearly confirm that this choice of weights is justified: the pure green appears much brighter than the blue, and almost as bright as the cyan (which is their sum) above.




However, the definition of the luma still ignores the non-linearity of both the standard RGB color spaces (such as sRGB) and the human eye. While these two effects partly cancel out each other, allowing luma to be used as a reasonable approximation of perceptual lightness, for truly accurate results they need to be accounted for. This involves several steps:



  1. Convert the RGB color into a linear color space. For sRGB, the following formula may be used: $R_L = f(R), G_L = f(G), B_L = f(B)$, where: $$f(C) = \begin{cases}C / 12.92 & \text{if } 0 \le C \le 0.04045 \\ ((C + 0.055) / 1.055)^{2.4} & \text{if } 0.04045 < C \le 1 \end{cases}$$ (Here, $R$, $G$ and $B$ are the sRGB component values, and $R_L$, $G_L$ and $B_L$ the linear ones, both scaled to the range from 0.0 to 1.0.) Alternatively, a very close approximation to the formula above is given by simple gamma-expansion with $\gamma = 2.2$: $f(C) = C^{2.2}$.


  2. Calculate the relative luminance as a weighted average of the linear RGB component values: $$Y = 0.2126\, R_L + 0.7152\, G_L + 0.0722\, B_L$$ (The weights are exactly the same as for the luma formula above, because that's where they come from; the luma calculation just blindly applies them to the gamma-compressed RGB values.)


  3. Convert the relative luminance value into an approximation of the percieved lightness, using something like the CIELAB formula: $\ell^* = 1.16\, g(Y) - 0.16$, where: $$g(Y) = \begin{cases} \tfrac13 \left(\tfrac{29}{6}\right)^2 Y + \tfrac{4}{29} & \text{if } 0 \le Y \le \left(\tfrac{6}{29}\right)^3\\ Y^{1/3} & \text{otherwise} \end{cases}$$ (Note: In the CIELAB standard, the $L^*$ value ranges from 0 to 100, but for the sake of consistency, in the formula above I chose to define $\ell^* = L^* / 100$, so that its range is also from 0 to 1.)


No comments:

Post a Comment