Gamma

Pixel values are not proportional to light. A value of 128 is not half as bright as 255; it emits only about a fifth of the light. Images are stored gamma-encoded, and every operation that models light has to decode them first. See how much this matters.

Pixel value → light the decoding curve
Medium gray step back or squint: which center matches its surround?
Blur the same Gaussian, applied to encoded values and to linear light
original
blurred encoded values
blurred linear light
Gamma adjustment v→vγv \to v^\gamma applied per channel and to luma only
original
per channel
luma only

Encoded values and light

The relation between a pixel value v∈[0,1]v \in [0, 1] and the light intensity II it stands for is roughly a power law, with an exponent called gamma:

I=vγ,v=I1/γ,γ≈2.2I = v^{\gamma}, \qquad v = I^{1/\gamma}, \qquad \gamma \approx 2.2

The sRGB standard, used by nearly all images and displays, replaces the power law with a short linear segment near black and an exponent of 2.4 elsewhere. Overall it is very close to γ=2.2\gamma = 2.2:

I={v12.92v≤0.04045(v+0.0551.055)2.4otherwiseI = \begin{cases} \dfrac{v}{12.92} & v \le 0.04045 \\[1ex] \left( \dfrac{v + 0.055}{1.055} \right)^{2.4} & \text{otherwise} \end{cases}

Why encode?

Our perception of brightness is far from linear: we notice small differences between dark tones much more easily than between bright ones. Encoding with v=I1/2.2v = I^{1/2.2} spreads the 256 levels of an 8-bit image roughly evenly in perceived brightness, with many levels for dark tones and fewer for bright ones. With 8 bits of linear intensity, dark gradients would show visible bands, while many of the bright levels would be wasted. Historically the curve also matched the response of CRT displays.

Medium gray

Alternating black and white lines emit exactly half the light of white. The gray value that looks the same is not 128, but

v=0.51/2.2⋅255≈186(with the exact sRGB curve: 188).v = 0.5^{1/2.2} \cdot 255 \approx 186 \qquad (\text{with the exact sRGB curve: } 188).

Conversely, 128 is only (128/255)2.2≈22 %(128/255)^{2.2} \approx 22\,\% of the light. The comparison only works if the lines are displayed at one line per screen pixel and your display follows sRGB; if you see moiré, set the page zoom to 100 %.

Linear vs encoded

Light adds up linearly. Any operation that mixes light (blurring, resizing, anti-aliasing, blending two images, computing a camera's exposure) must therefore work on linear intensities:

decode  →  process  →  encode\text{decode} \;\to\; \text{process} \;\to\; \text{encode}

Averaging encoded values is too dark: black and white give 128 instead of 188. Between red and green the mix gets a dark, muddy band, and small bright lights lose most of their energy when blurred. The table in the values panel shows these averages.

Adjusting gamma

Raising values to a power γ\gamma is also a common tone adjustment: γ<1\gamma < 1 brightens the shadows, γ>1\gamma > 1 darkens them. Applied to each channel separately, it changes the ratios between R, G and B, so colors shift and saturation changes. Applied to the luma only, the three channels are scaled by the same factor and the colors keep their hue:

(R,G,B)  →  Y′ γY′ (R,G,B),Y′=0.299 R+0.587 G+0.114 B(R, G, B) \;\to\; \frac{Y'^{\,\gamma}}{Y'} \, (R, G, B), \qquad Y' = 0.299\,R + 0.587\,G + 0.114\,B

Channels that would exceed 1 are clipped. This is often described as applying gamma to the luminance only. Strictly, a weighted sum of encoded values like Y′Y' is the luma; luminance would be computed from linear light, Y=0.2126 R+0.7152 G+0.0722 BY = 0.2126\,R + 0.7152\,G + 0.0722\,B on decoded values.

Try this