Easier to read with cream paper, a tint, more contrast or larger text? Choose your colours and text, once, for every page.

Free lesson

Image filters and blur

After this lesson: I can smooth noise in a picture with a blur or a median filter.

60 min lesson3 commandsComputer vision lab: pixels, thresholds and counting objects in JupyterLab

A blur filter smooths noise by averaging neighbours

An image filter changes every pixel using the pixels around it. A blur filter replaces each pixel with the average of its neighbours, which smooths out the random speckle called noise. Smoothing first makes thresholds and edges far more reliable.

In this lesson you apply a kernel by hand, then compare a blur with a median filter.

By the end of this lesson

Note

I can smooth noise in a picture with a blur or a median filter.

Key words

WordWhat it meansExample
Noiserandom speckle that is not part of the scene
Kernela small grid of weights slid over a picture3 by 3
Convolutionsliding a kernel over a picture and adding the products
Blura filter that averages each pixel with its neighbours
Medianthe middle value when numbers are put in order

The cells students fill in are left blank in this preview.

A kernel slides over a picture, adding products

A kernel is a small grid of weights, often 3 by 3. Place it over a patch of nine pixels. Multiply each pixel by the weight above it, and add the nine products. That sum becomes one pixel of the new picture.

Slide the kernel one pixel along and repeat. This slide-and-add is called convolution.

Worked example: a box blur on a grid

PlaceThe nine pixelsSum with all weights 1Divide by 9
Columns 0 to 20 0 9 on each of three rows273
Columns 1 to 30 9 9 on each of three rows546

The cells students fill in are left blank in this preview.

Check the two sums with NumPy

Command
docker exec jupyter python3 -c "import numpy as np; a = np.array([[0, 0, 9, 9], [0, 0, 9, 9], [0, 0, 9, 9]]); print(a[:, 0:3].mean(), a[:, 1:4].mean())"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Slide a box blur over a grid

Code: python
grid = [
    [0, 0, 9, 9],
    [0, 0, 9, 9],
    [0, 0, 9, 9],
]
kernel = [[1, 1, 1], [1, 1, 1], [1, 1, 1]]

row = []
for column in range(2):
    total = 0
    for a in range(3):
        for b in range(3):
            total = total + grid[a][column + b] * kernel[a][b]
    row.append(total / 9)
print(row)
# Change the kernel to [[0, 0, 0], [0, 1, 0], [0, 0, 0]] and divide by 1.
# What does that kernel do to the picture?

A blur removes noise and softens detail together

Each new pixel is an average of its neighbours, so a lone bright speck is pulled towards its surroundings. The picture becomes smoother.

The cost is that real detail is averaged too. A sharp line becomes a soft one. A wider kernel smooths more and softens more.

Blur the page scan and measure the noise

Command
docker exec jupyter python3 steps/blur.py

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

The spread of an empty patch is noise

The lab's page scan has speckle. The script picks a patch with no writing in it, so any variation there is noise. It measures its spread, the standard deviation of its grey levels.

Before the blur it prints 25.3. After a Gaussian blur it prints 4.9. A Gaussian blur is an average that counts close neighbours more than far ones.

A median filter ignores a lone bright speck

A blur is thrown by an extreme pixel, because an average moves towards it. The median does not. Put the nine pixels in order and take the middle one.

The median filter replaces each pixel with the median of its patch. A single speck of 255 sits at the end of the order and is never picked.

Worked example: one speck among nine pixels

MeasureWorkingResult
The nine pixels10, 12, 11, 255, 9, 10, 12, 11, 10
In order9, 10, 10, 10, 11, 11, 12, 12, 255
Mean340 divided by 937.8
Medianthe fifth in order11

The cells students fill in are left blank in this preview.

Compare a median filter with a box blur

Command
docker exec jupyter python3 -c "import numpy as np; from PIL import Image, ImageFilter; a = Image.open('images/scan.png'); b = a.filter(ImageFilter.MedianFilter(3)); c = a.filter(ImageFilter.BoxBlur(1)); print(round(float(np.array(a)[10:50, 10:90].std()), 1), round(float(np.array(b)[10:50, 10:90].std()), 1), round(float(np.array(c)[10:50, 10:90].std()), 1))"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

A median beats a same-size box blur

The Run prints three spreads for the same empty patch: 25.3 6.0 9.1. They are the original, the median filter and the box blur.

The scan has black and white speckles, which a median handles well. At the same small size it cut the spread to 6.0, and the box blur only to 9.1.

A common mistake

Warning

Many people think a blur only removes noise. It also removes real detail, such as thin lines and sharp edges. Blur just enough to calm the noise, and no more, or the edges you want to find next are gone.

Going further

In a notebook, blur scan.png with ImageFilter.GaussianBlur(5) and with GaussianBlur(1). Print the spread of the empty patch after each.

Then look at the writing in both. Where does extra blur stop helping?

About this lab and its source: Computer vision lab: pixels, thresholds and counting objects in JupyterLab

Teaching a class? Set this module for your students with a teacher guide, marked assignments and a lab for every student.