Gamma is a kind of compression algorithm. It exploits the fact that our perception of brightness, as with many other sensory stimuli, follow the power law. Our eyes are much better at detecting the difference between 2 values that are darker, as opposed to two values that are brighter. From an evolutionary standpoint, this makes sense: it is much more advantageous to be able to see in the dark, than to accurately tell between two really bright shades.

Gamma encoding exploits this so that each bit in the image data is used as efficiently as possible. Instead of storing the camera sensor data linearly, the data is encoded so that more bit space is given to darker ranges, and less to lighter ranges, mimicking our perception. This is why gamma encoded images are said to be perceptually linear.

At the very simplest, gamma encoding uses a single value, gamma, which is typically 2.2. I cannot find any good explanation for where this value comes from, though my Macbook Pro monitor shows a gamma settings of 2.4. To gamma encode, we simply take pow(input,1/2.2). And to decode, we take pow(input, 2.2).

Why is this important?

Image algorithms are made to work on linear values, not perceptually linear values.

Say we wanted the average of 2 pixels, 1 black, 1 white. If simply average their rgb values, we get 128. But 128 appears to be only 22% as bright, which is not what we expect. What we want is to get 50% brightness. So the correct way of doing this is to gamma decode the values first, do the average, then reencode them.

These two python functions describe how one might encode and decode a value.

def encode(x):
	return math.pow(x/255.0, 1/2.2) * 255

def decode(x):
	return math.pow(x/255.0, 2.2) * 255

Photography

As a bonus, if you are a photography nerd and have messed around with tonal curves, then you already understand gamma intuitively. A higher slope in the tonal curve causes the values in the input range to be redistributed to a wider output range, resulting in more contrast. Gamma encoding is like a tonal curve applied to the entire input range.

Helpful links:

http://blog.johnnovak.net/2016/09/21/what-every-coder-should-know-about-gamma

https://www.cambridgeincolour.com/tutorials/gamma-correction.htm