Understanding Image Histograms
What they tell us
Most images on this website will 'pop-up' in a lightbox, that fills the browser window, when clicked. At the top right of the lightbox there are four icons: Info, Love, Fullscreen, and Close.
Amongst other things, the Info icon will display a 'luminosity histogram' of the image. This is a graphical analysis that lets you better understand the (technical) nature of the image.
The example (left) is taken from the Dance 01 image in my Street Photography Gallery.
Each (colour) pixel in the image has been converted to its 'perceptual luminosity' - how dark or light it appears. There 256 different levels of luminosity, from level 0 (black) to level 255 (white). The histogram is a simple bar chart that shows how many pixels there are at each level.
In this example I can see there are no pixels at levels 0 through 6. I.e. there are no true blacks and no areas in the image with 'blocked-up shadows' (dark areas with no discernable details).
Similarly there are no true whites, the brightest pixels being at level 252. I.e. there are no blotches of 'blown-out highlights' (bright areas with no discernable details).
It is overall a bright image with a mean luminosity (141.59) well above the mid-point (level 127).
The standard deviation (sd) tells us about how the luminosity varies - it is an indicator of the image contrast. Very low values (<20) indicate a visually flat image, and very high values (>80) would indicate a very contrasty image.
This histogram has definite peaks (It isn't just flat, there are rises and falls) which we can expect means there is some good tonal seperation between foreground objects and the background scene. Interpreting histograms becomes quite subjective, but we tend to look for 'essential characteristics', such as the presence of peaks and troughs.
Spikes though (super narrow peaks) can be troublesome - very sharp step changes can result in 'banding' in an image (where smooth tonal areas appear blocky). Also gaps in the histogram (luminosity levels that have no pixels) are also unwelcome for similar reasons - and can be caused by heavy-handed over-processing.
Obviously images are judged on how they look, not on the nature of their luminosity histogram. But the histogram can help guide our appreciation of an image by helping us to ask (and answer) key questions:
Are the shadows blocked-up
Are the highlights blown-out
Is the image overly flat, or contrasty
Do smoothly drawn peaks and troughs imply good seperation in the image
Do spikes or gaps threaten bading in the image
Are we happy with the overall brightness
Is the tonal-range too bunched up, or skewed
How to read a histogram
This is what a blank histogram looks like (right), before any pixel information has been anlysed and drawn in.
There are 3 horizontal sections, top to bottom:
The graph area, where the histogram gets drawn.
The tonal graduate; a visual indicator of the tones represented by the histogram.
The numeric data panel; showing min and max tones found in the image, the mean luminosity of the pixels and the standard deviation of the pixel luminosities.
In the graph area the red line indicates the actual start of the histogram data. I.e. the luminosity level of the darkest pixels in the image. The green line indicates the actual end of the histogram data. I.e. the luminosity level of the brightest pixels in the image.
The remaining (cyan) lines divide the whole tonal range into 11 distinct 'zones'; where each zone represents a doubing of exposure. These divisions help us to understand how much adjustment is needed to shift the tones of the histogram.
Looking at this example (left, click to enlarge) we can see the original image with its (green) histogram below. It's quite a moody and deliberately dark image, with a mean luminosity of just under 54. And there are no pixels brighter than level 176, with the histogram coming to a stop in Zone 10.
This means I have a whole zone (and a bit) to expand the tonal range should I want to.
By dialling-in +1Ev of exposure compensation (bottom image) we can see the whole (yellow) histogram has stretched well into zone 11. We can also see the (green) peak in zone 6 has now moved to the very edge of zone 7/8. The zone 2 shadow peak has also moved (the small distance) into zone 3.
You might have expected that doubling the exposure (by adding +1Ev of compensation) would double all the values - with the top values (level 176) jumping up to level 352 (i.e. burning out as our brightest level is 255).
In fact that is exactly what happens in-camera. Doubling the exposure doubles the signal strength reported by each photosite, because cameras 'see' in a linear fashion. They also have lots of space to store their raw data, typically 12 or 14 bits for each colour channel of each pixel.
But neither eyes nor display devices behave in that linear fashion. We see the difference between levels 2 and 3 much more easilly than between levels 253 and 254 - even though in both cases the difference is 1 level. This is because level 3 is 50% brighter than level 2, but level 254 is only half a percent brighter than level 253.
We just don't need as many highlight levels as we do shadow levels. Plus we don't have a lot of room to store all these levels - most display devices (and all JPG files) are limited to just 8 bits per colour channel. Linear behaviours would be very wasteful.
So we never get to see the nice and simple 12 or 14 bits of linear RAW data from the camera sensor. Whenever we view that data as an image we are seeing a 'gamma encoded' version that fits into a non-linear 8-bit scheme. Because we are seeing (and storing) our image data in a non-linear fashion, the adjustments we make behave in a non-linear fashion also. Doubling the exposure more or less doubles the shadows, but has a much lesser effect on the highlights.
The 'zone grid lines', themselves being non-linear (they get further apart) help to indicate (or predict) this non-linear behaviour.
Calculating a histogram
The jpg file from which the histogram is to be generated contains 'gamma-encoded' (non-linear) values for each (rgb) colour channel of each pixel. These are each extracted and linearised by applying the inverse-gamma function.
These are then summed proportional to their perceptive brightness (approximately 21% green, 72% red and 7% blue) to create a single luminosity value for each pixel.
That luminosity value is then gamma-encoded as per the original data. The resulting value is in the range 0 to 255.
The number of pixels at each luminosity value is then tallied, as the basis for the histogram.
At the same time the luminosities of all the pixels is summed, so that the arithmetic mean can be found. A map of the individual luminosities is made while all the foregoing is going on, so they don't have to be recalculated...
...because once the mean has been found the standard deviation is derived. The sum of the square of the variance of each pixel's luminosity from the mean is calculated. This total is then divided by the number of pixels, with the square root giving the derived Standard Deviation value.
Going back to the histogram itself, the individual values are scaled so they will all fit within the allocated height of the final display (128 pixels).
The full PHP script that does all of this is included below, should it be useful to anyone. Note that the script presumes the input jpg file is encoded to the sRGB colour space - as that is most common on the web (and is the only colour space I use for web images). Some modification to the linearisation and gamma-encoding functions would be needed to create accurate histograms for other colour spaces...