# /// script
# requires-python = ">=3.11"
# ///
from pathlib import Path
from datetime import datetime
import numpy as np
from PIL import Image
import imageio
import skimage
import pooch
import matplotlib.pyplot as plt
%matplotlib inline
Welcome to the image segmentation chapter!
Just like the previous notebook, it is an interactive book: read, explore, and do.
You will learn core building blocks:
Chapter | Concept | Why it matters |
---|---|---|
0 | What is image segmentation? | Semantics vs instance segmentation |
1 | Gaussian smoothing + Otsu threshold | Simple foreground / background separation |
2 | Watershed | Split touching objects into individual pieces |
Each chapter follows the same pattern:
Segmentation means labeling each pixel so that we can tell which pile of pixels belongs to which real‑world object.
There are two main types of segmentation:
In bioimage analysis that usually means turning a fluorescence snapshot into a binary mask of some cellular, subcellular, or tissue structure. Once you have a mask you can measure size, shape, intensity, count cells, follow them over time – all the good stuff.
There are many ways to segment.
Today we will cover two classical yet powerful techniques that get you surprisingly far:
Chapter | Technique | Typical use‑case |
---|---|---|
1 | Gaussian + Otsu threshold | Simple foreground / background separation |
2 | Watershed | Split touching objects into individual pieces |
Each chapter follows the same pattern:
Ready? Scroll on!
Real microscope images are noisy.
Noisy pixels can trick a thresholding algorithm, and also sometimes we don't want to detect small details, so we smooth the image first with a Gaussian blur from skimage.filters.gaussian_filter
:
clean = gaussian(image, sigma)
sigma
≈ size of the noise blobs (in pixels).sigma
⟶ more blur, fewer details.Then we separate foreground & background, first, using the simplest thresholding method.
Then, we can threshold in a more automatic way using Otsu’s method (skimage.filters.threshold_otsu
).
Otsu finds the threshold that minimises the variance inside each class while maximising the variance between classes – no parameter to tune!
Finally we create a binary mask in a similar fashion:
mask = clean > thresh
That’s it – three lines for a first segmentation pass.
sigma
values (try 0.5, 1, 4, 8).sigma
and plot sigma
vs % foreground
.Extra point if you use a for loop!
Add extra cells below – the notebook is your sandbox!
Noticing all the little speckles in the mask? We can use morphological operations to clean it up:
skimage.morphology.binary_opening
) before remove_small_objects
to get rid of speckles.skimage.morphology.binary_closing
) after remove_small_objects
to fill holes.
Intuition
Imagine the grayscale image as a topographic surface:
In practice we often:
skimage.segmentation.watershed
on the negative distance map so water flows from centres to edges.
min_distance
in peak_local_max
.min_distance
⇒ more markers ⇒ over‑segmentation.skimage.morphology.watershed
on the gradient magnitude (e.g. Sobel filter) instead of the distance map.Extra cell slots below – go wild!