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

Free lesson

Counting objects in an image

After this lesson: I can count and locate the objects in a picture by labelling its connected regions.

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

Counting objects means counting connected regions

To count the objects in a picture, make a mask, then count each separate group of touching object pixels. Each group is a connected region, and the program gives it a number called a label. The same idea counts coins, cells in a slide and cars in a car park.

In this lesson you label regions, measure their size and find a small piece inside a larger picture.

By the end of this lesson

Note

I can count and locate the objects in a picture by labelling its connected regions.

Key words

WordWhat it meansExample
Connected regiona group of object pixels that touch each other
Connectivitythe rule for which pixels count as touching4 or 8
Labelthe number given to one region
Areahow many pixels a region holds
Template matchingsliding a small piece over a picture to find where it fits

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

A connected region is a touching group

Take a mask of 0 and 1. Two 1-pixels belong to the same connected region if you can walk from one to the other through 1-pixels that touch.

Give each region its own number, 1, 2, 3 and so on. Zero stays the background. That numbering is labelling, and the biggest label is the count.

Worked example: a mask and its labels

RowMaskLabels
01 1 0 01 1 0 0
10 1 0 10 1 0 2
20 0 0 10 0 0 2
31 0 0 13 0 0 2

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

Label the shapes picture and list the areas

Command
docker exec jupyter python3 -c "import numpy as np; from PIL import Image; from skimage import measure; a = np.array(Image.open('images/shapes.png').convert('L')); l = measure.label(a < 250); print(l.max(), sorted(int(r.area) for r in measure.regionprops(l)))"

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

Each region has an area you can measure

The Run keeps every pixel darker than 250, so the white page is background. It labels what is left and prints the count and the areas.

The answer has the form 6 [1581, 1581, 2909, 2909, 3621, 4941]: six shapes. The area is a count of pixels, so the two matching pairs are the same size: two triangles and two circles.

Count regions with a loop

Code: python
grid = [
    [1, 1, 0, 0],
    [0, 1, 0, 1],
    [0, 0, 0, 1],
    [1, 0, 0, 1],
]
seen = []
for row in grid:
    seen.append([False] * len(row))

count = 0
for r in range(len(grid)):
    for c in range(len(grid[r])):
        if grid[r][c] == 1 and not seen[r][c]:
            count = count + 1
            todo = [[r, c]]
            seen[r][c] = True
            while len(todo) > 0:
                here = todo.pop()
                for step in [[1, 0], [-1, 0], [0, 1], [0, -1]]:
                    nr = here[0] + step[0]
                    nc = here[1] + step[1]
                    if 0 <= nr < len(grid) and 0 <= nc < len(grid[0]):
                        if grid[nr][nc] == 1 and not seen[nr][nc]:
                            seen[nr][nc] = True
                            todo.append([nr, nc])
print("regions:", count)

Connectivity decides whether a corner touches

Do two pixels that meet only at a corner touch? With 4-connectivity they do not: only left, right, up and down count. With 8-connectivity they do.

The rule changes the count. The loop above uses four directions, so two pixels on a diagonal are two regions.

Count a diagonal pair both ways

Command
docker exec jupyter python3 -c "import numpy as np; from skimage import measure; a = np.array([[1, 0], [0, 1]]); print(measure.label(a, connectivity=1).max(), measure.label(a, connectivity=2).max())"

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

Count the coins at two threshold levels

Command
docker exec jupyter python3 -c "import numpy as np; from PIL import Image; from skimage import measure; a = np.array(Image.open('images/coins.png').convert('L')); print(measure.label(a > 30).max(), measure.label(a > 160).max())"

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

The count depends on the threshold you chose

The coin picture holds nine coins. The Run prints 3 11. A level of 30 is inside the background noise, so the noise joins several coins into one lump and the count falls to 3. A level of 160 cuts into the coins and leaves 11 bright pieces.

A level of 100, in the gap between background and coins, gives 9. Always check the count against what you can see.

Template matching finds a piece in a picture

To find a known object, cut out a small piece of it, called a template. Slide it over the picture and score how closely each place matches. The best score marks the place.

A score of 1.0 is a perfect match. The method works when the object looks the same each time, but it struggles when the object is turned or resized.

Find a piece cut from the coins

Command
docker exec jupyter python3 steps/find.py

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

A common mistake

Warning

Many people think the label count is the number of objects, whatever the threshold. It is the number of connected regions in the mask. Touching objects join into one region, and noise can break one object into many. Look at the mask, and check the areas for a lump or a speck.

Going further

In a notebook, label coins.png above 100 and print [(int(r.centroid[0]), int(r.centroid[1])) for r in measure.regionprops(labels)].

Each pair is the row and column of a coin's centre. Check two of them against the picture.

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.