RGB Channels and Relative Luminance

  • color science
  • python

The relative luminance of a color in the sRGB colorspace is defined by ITU-R BT.709 using the luma coefficients:

def relative_luminance(r: float, g: float, b: float) -> float:
    """
    r, g, b in linear light [0, 1].
    Returns luminance Y in [0, 1].
    """
    return 0.2126 * r + 0.7152 * g + 0.0722 * b

The weighting — 21% red, 72% green, 7% blue — reflects the spectral sensitivity of the human eye. The L-cone and M-cone photoreceptors (sensitive to longer and medium wavelengths) overlap strongly in the green region, making it the dominant contributor to perceived brightness.

Try it below. Notice how shifting the green channel has far more impact on luminance than equivalent shifts in red or blue:

#8040C8 — luminance 0.343

This has practical consequences throughout color science: a “neutral” 50% grey in display-referred sRGB has a luminance of 0.2140, not 0.5, because the nonlinear OETF (gamma) and the luma weighting combine to shift the perceptual midpoint.

The OETF correction

Values from a camera sensor or a Python array are typically linear light. sRGB display values are gamma-encoded. Before applying the luma coefficients, you need to linearize:

import numpy as np

def srgb_to_linear(v: np.ndarray) -> np.ndarray:
    """Piecewise IEC 61966-2-1 linearization."""
    return np.where(v <= 0.04045, v / 12.92, ((v + 0.055) / 1.055) ** 2.4)

Applying luma coefficients directly to gamma-encoded values is a common error that produces incorrect luminance estimates — especially noticeable in HDR workflows.