Grey keeps the brightness and drops the colour
Converting a colour picture to grey replaces the three numbers of each pixel with one number for its brightness. Most vision methods start from grey, because one number per pixel is easier to compare than three.
The skill is doing it so that the grey picture still looks right to a person.
By the end of this lesson
Note
I can turn a colour picture into grey using luminance weights.
Key words
| Word | What it means | Example |
|---|---|---|
| Channel | one number of a pixel, such as its red | |
| Luminance | how bright a colour looks to a person | |
| Weight | how much one channel counts in a sum | 0.587 |
| Grey level | a brightness from 0 (black) to 255 (white) | |
| Grey picture | a picture with one grey level per pixel |
The cells students fill in are left blank in this preview.
Why we convert a colour picture to grey
A colour pixel is three numbers, so it is hard to ask whether one pixel is brighter than another. A grey pixel is one number, so a comparison is easy.
Edges, thresholds and counting all work on grey levels. We convert once, then reuse the result.
Luminance weights count green most
The simple way is to average the three channels. It looks wrong. The eye is most sensitive to green and least to blue. Equal weights make a green field look too dark and a blue sky too bright.
Luminance weights fix this. The usual ones are 0.299 for red, 0.587 for green and 0.114 for blue. They add up to 1.
Worked example: the same pixel, two ways
| Pixel (R, G, B) | Simple average | Luminance weights | Working for the weights |
|---|---|---|---|
| (255, 0, 0) red | 85 | 76 | 0.299 x 255 |
| (0, 255, 0) green | 85 | 150 | 0.587 x 255 |
| (0, 0, 255) blue | 85 | 29 | 0.114 x 255 |
| (200, 40, 40) | 93 | 88 | 59.8 + 23.5 + 4.6 = 87.9, so 88 |
The cells students fill in are left blank in this preview.
Convert four pure colours with Pillow
docker exec jupyter python3 -c "from PIL import Image; print([Image.new('RGB', (1, 1), c).convert('L').getpixel((0, 0)) for c in [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 255)]])"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Pillow converts a whole picture with one call
The Run above makes four one-pixel pictures and converts each with convert("L"). It prints [76, 150, 29, 255]: red 76, green 150, blue 29 and white 255.
Those match the table, so Pillow uses the same weights. White stays at 255, because the weights add up to 1.
Compare the average and the weights
red = 200
green = 40
blue = 40
average = (red + green + blue) // 3
weighted = (299 * red + 587 * green + 114 * blue + 500) // 1000
print("simple average:", average)
print("weighted grey:", weighted)
# Change the pixel to red 40, green 200, blue 40 and press Run.
# Which one gets brighter, and why?
Read three shapes, colour and grey
docker exec jupyter python3 -c "from PIL import Image; p = Image.open('images/shapes.png'); print(p.getpixel((50, 40)), p.convert('L').getpixel((50, 40)), p.getpixel((150, 140)), p.convert('L').getpixel((150, 140)), p.getpixel((150, 45)), p.convert('L').getpixel((150, 45)))"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Different colours can share a similar grey
The Run reads one pixel from three shapes of shapes.png. The red shape is (200, 40, 40) and becomes 88. The blue one is (40, 70, 200) and becomes 76. The green one is (40, 160, 70) and becomes 114.
Three clear colours, but the greys sit close together. Grey throws the colour away, so a red and a blue shape can be hard to tell apart.
You can also look at one channel alone
Sometimes one channel is the useful one. A red shape on a white page is easier to find in the green channel, where it is dark. In the red channel it is as bright as the page.
In NumPy, a[:, :, 0] is the red channel, a[:, :, 1] the green and a[:, :, 2] the blue.
A common mistake
Warning
Many people think grey is just the average of red, green and blue. It is a weighted sum. The eye sees green as far brighter than blue, so use the weights unless you have a reason not to.
Going further
In a notebook cell, load shapes.png as an array and print the mean of each channel with a[:, :, 0].mean() and the same for 1 and 2.
The page is mostly white, so the three means are close. Which shape would be hardest to find in grey, and why?