Skip to content
BetaDE
Lesson 3 of 5Exploring computer science

Exploring computer science · Lesson 3 · 40 minutes

Pictures are made of pixels

What you'll learn

  • Store a picture as a list of rows with brightness values
  • Understand how colours are made from red, green and blue
  • Program your own image filters

A picture is a table of numbers

If you zoom right into a photo, you see little squares: pixels. Every pixel has exactly one colour. For the computer, a picture is therefore just a big table of numbers.

The simplest kind is a greyscale picture: every pixel is a number from 0 (black) to 255 (white) – exactly one byte. In Python that’s a list of rows. We show it with the game module:

from codelix import game

picture = [
    [255, 255, 200, 200, 200, 200, 255, 255],
    [255, 200, 200, 200, 200, 200, 200, 255],
    [200, 200, 0, 200, 200, 0, 200, 200],
    [200, 200, 200, 200, 200, 200, 200, 200],
    [200, 60, 200, 200, 200, 200, 60, 200],
    [200, 200, 60, 60, 60, 60, 200, 200],
    [255, 200, 200, 200, 200, 200, 200, 255],
    [255, 255, 200, 200, 200, 200, 255, 255],
]


def show(picture, x0=0, size=20):
    for y, row in enumerate(picture):
        for x, grey in enumerate(row):
            game.rect(x0 + x * size, y * size, size, size, f"rgb({grey},{grey},{grey})")


game.window(160, 160)
show(picture)
print("Width:", len(picture[0]), "Height:", len(picture))

picture[2][2] is the pixel in row 2, column 2 – the left eye. Change a number and see what happens.

Colours from three numbers

A colour picture stores three numbers for every pixel: how much red, green and blue it contains, each from 0 to 255. The screen mixes all other colours from these three:

from codelix import game

colours = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (255, 0, 255), (255, 255, 255)]
game.window(360, 120)
for number, (r, g, b) in enumerate(colours):
    game.rect(number * 60, 0, 60, 60, f"rgb({r},{g},{b})")
    grey = round(0.299 * r + 0.587 * g + 0.114 * b)
    game.rect(number * 60, 60, 60, 60, f"rgb({grey},{grey},{grey})")
    print((r, g, b), "→ grey value", grey)

At the bottom you see the same colours in greyscale. Green looks much brighter than blue – that’s how your eye sees it too. That’s why green counts most in the formula.

A filter changes every pixel

An image filter goes through all pixels and calculates a new value for each one. This filter makes the picture brighter. With min(255, …) every value stays in the allowed range:

from codelix import game

picture = [
    [0, 40, 80, 120],
    [40, 80, 120, 160],
    [80, 120, 160, 200],
    [120, 160, 200, 240],
]


def brighter(picture, plus):
    new = []
    for row in picture:
        new.append([min(255, value + plus) for value in row])
    return new


def show(picture, x0=0, size=30):
    for y, row in enumerate(picture):
        for x, grey in enumerate(row):
            game.rect(x0 + x * size, y * size, size, size, f"rgb({grey},{grey},{grey})")


game.window(260, 120)
show(picture)
show(brighter(picture, 80), 140)

On the left the original, on the right the brighter picture. That’s exactly how filters in photo apps work – just with millions of pixels instead of 16.

Keep building: When your task works: try your filters on the smiley. Or program a mirror filter (reverse every row with row[::-1]) or a blur that replaces every pixel with the average of its neighbours.

Your task

Your turn

Write two image filters for greyscale pictures (lists of rows with values from 0 to 255). invert(picture) makes a negative: every value v becomes 255 - v. black_white(picture, limit) turns every value from limit upwards into 255 (white) and all smaller ones into 0 (black). Both return a new picture and leave the old one unchanged.

picture = [
    [0, 60, 120, 180],
    [60, 120, 180, 240],
]


def invert(picture):
    new = []
    return new


def black_white(picture, limit):
    new = []
    return new


print(invert(picture))
print(black_white(picture, 128))

Solve it without hints to earn a ⭐ on your progress page.

Show solution

Try it yourself first – mistakes teach you the most. Here is one possible solution:

picture = [
    [0, 60, 120, 180],
    [60, 120, 180, 240],
]


def invert(picture):
    new = []
    for row in picture:
        new.append([255 - value for value in row])
    return new


def black_white(picture, limit):
    new = []
    for row in picture:
        new.append([255 if value >= limit else 0 for value in row])
    return new


print(invert(picture))
print(black_white(picture, 128))