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

Free lesson

Edge detection basics

After this lesson: I can find the edges in a picture by measuring how fast the grey level changes.

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

An edge is where grey level changes fast

Edge detection finds the places where brightness changes sharply, such as the outline of a shape or a lane line on a road. Edges are a compact summary of a picture, because they keep the outlines and drop the flat areas.

In this lesson you measure the change between neighbouring pixels and mark the places where it is large.

By the end of this lesson

Note

I can find the edges in a picture by measuring how fast the grey level changes.

Key words

WordWhat it means
Gradienthow fast the grey level changes from one pixel to the next
Edgea place where the grey level changes sharply
Edge strengththe size of the change at one pixel
Edge mapa picture of 0 and 1 that marks the edge pixels
Cannya standard method that finds thin, clean edges

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

A gradient is the step between neighbouring pixels

Subtract one pixel from the next. In a flat area the answer is near 0. At the border of a bright shape on a dark page it is large.

That difference is the gradient. Its size, ignoring whether it goes up or down, tells you how sharp the change is.

Worked example: steps along one row

PlacePixelsWorkingSize of the step
0 to 110 then 1010 - 100
1 to 210 then 1010 - 100
2 to 310 then 200200 - 10190
3 to 4200 then 200200 - 2000

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

Find the biggest step in the grey ramp

Command
docker exec jupyter python3 -c "import numpy as np; from PIL import Image; a = np.array(Image.open('images/gradient.png')).astype(int); print(np.abs(np.diff(a, axis=1)).max(), np.abs(np.diff(a, axis=0)).max())"

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

A smooth ramp has no edge at all

The Run takes the differences along each row, then down each column, and prints the biggest of each. It prints 1 0.

The ramp rises by 1 grey level per pixel across and not at all down, so its largest step is 1. It changes all the way along, but never fast, so there is no edge.

Measure the steps along a row

Code: python
row = [10, 10, 10, 200, 200]

for place in range(len(row) - 1):
    step = row[place + 1] - row[place]
    print("between", place, "and", place + 1, ":", abs(step))
# Change the 200 at the end of the row to 10 and press Run.
# How many edges are there now?

See why differences need whole numbers

Command
docker exec jupyter python3 -c "import numpy as np; a = np.array([10, 200], dtype=np.uint8); print((a[:1] - a[1:]).tolist(), (a[:1].astype(int) - a[1:]).tolist())"

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

Subtracting in uint8 wraps round and gives nonsense

A uint8 number cannot go below 0. NumPy wraps it round instead. The Run prints [66] [-190].

The first is 10 minus 200 in uint8: it wraps to 66, which is wrong. The second changes to ordinary integers first and gives the true -190. Always use astype(int) before you subtract pixels.

Edge strength adds the steps across and down

An edge can run in any direction, so look both ways. At each pixel, find the step to the pixel on its right and the step to the pixel below. Add their sizes.

That sum is the edge strength. A pixel whose strength is over a chosen limit is marked 1 in the edge map.

Worked example: edge strength on a grid

PixelStep rightStep downStrength
Row 0, column 0000
Row 0, column 11900190
Row 1, column 11900190
Row 1, column 20190190

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

Count the big steps along the road

Command
docker exec jupyter python3 -c "import numpy as np; from PIL import Image; a = np.array(Image.open('images/road.png')).astype(int); g = np.abs(np.diff(a, axis=1)); print(g.shape, int((g > 60).sum()), int(g.max()))"

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

A limit keeps strong steps only

The Run prints (240, 319) 1236 153. A picture 320 wide has 319 steps along a row, so the array is 240 by 319.

Of those, 1236 are bigger than 60, mostly the sides of the lane lines and the road. The biggest is 153. The small steps from noise stay under the limit and are dropped.

Find the edges of the shapes picture

Command
docker exec jupyter python3 steps/edges.py

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

Canny smooths first and then keeps thin edges

The Canny method is what scikit-image offers. It blurs the picture first, measures the strength, keeps only the thin ridge of each edge, and drops the weak ones.

The Run finds 1326 edge pixels in a picture of 60000: a small share, because most of the page is flat. The blur is set by a number called sigma, so more sigma means fewer false edges and softer outlines.

A common mistake

Warning

Many people think an edge map shows whole objects. It shows only the change at their borders, and a border can have gaps or extra bits from noise. Smooth first, choose the limit by looking at the map, and treat the result as a clue about shapes, not a shape.

Going further

In a notebook, run feature.canny(a, sigma=1) and then sigma=3 on road.png, and print int(edges.sum()) for each. Use from skimage import feature.

Which sigma finds more edge pixels, and which gives you cleaner lane lines?

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.