Seeded watershed for membrane-based cell segmentation#

In this section we will use a seeded watershed approach to cell segmentation. This approach is very common when cell segmentation based on images of membrane markers are given. Therefore, we use the napari plugin napari-segment-blobs-and-things-with-membranes. Under the hood, this plugins uses functions from scikit-image.

See also

from napari_segment_blobs_and_things_with_membranes import voronoi_otsu_labeling, \
                                                           seeded_watershed, \
                                                           local_minima_seeded_watershed
from skimage.io import imread
from skimage.filters import gaussian
from skimage import data
from pyclesperanto_prototype import imshow

We load the Cells3d example image from scikit-image which is a two-channel image showing nuclei and membrains.

cells = data.cells3d()
cells.shape
(60, 2, 256, 256)
nuclei_channel = cells[30, 1]

imshow(nuclei_channel)
../_images/48ec40b26877c86f9b1576deb06dea99716314d4f2bc2fca2ce0422e1ddee3bb.png
membrane_channel = cells[30, 0]

imshow(membrane_channel, max_display_intensity=6000)
../_images/94deb1a34bb5d8ad6a58f232d0c3369dee64e6b96553da040e3b8b544c546b8c.png

Voronoi-Otsu-Labeling for nuclei segmentation#

First, we start with segmenting the nuclei using the Voronoi-Otsu-Labeling algorithm.

labeled_nuclei = voronoi_otsu_labeling(nuclei_channel, spot_sigma=10, outline_sigma=2)
labeled_nuclei
nsbatwm made image
shape(256, 256)
dtypeint32
size256.0 kB
min0
max25

Seeded watershed#

We can use the image of labeled nuclei as starting point for flooding the low-intensity areas in the membrane image. This allows us to determine a cell segmentation.

labeled_cells = seeded_watershed(membrane_channel, labeled_nuclei)
labeled_cells
nsbatwm made image
shape(256, 256)
dtypeint32
size256.0 kB
min1
max25

If the outlines of the cells are not 100% accurate, it may make sense to blur the membrane image a bit before segmenting the cells.

blurred = gaussian(membrane_channel, sigma=3)

labeled_cells = seeded_watershed(blurred, labeled_nuclei)
labeled_cells
nsbatwm made image
shape(256, 256)
dtypeint32
size256.0 kB
min1
max25

Seeded watershed using automatic seed detection#

In case we didn’t image a separate nuclei channel and only have the membrane channel available for segmentation, we can use the membrane image to search for local minima (dark areas).

labeles_cells2 = local_minima_seeded_watershed(membrane_channel)
labeles_cells2
nsbatwm made image
shape(256, 256)
dtypeint32
size256.0 kB
min1
max27

This function also has some parameters to allow fine tuning the segmentation. The parameter outline_sigma allows to control a Gaussian blur filter that allows fine-tuning the outlines of the segmented cells as shown above.

labeles_cells3 = local_minima_seeded_watershed(
                      membrane_channel, 
                      outline_sigma=3)
labeles_cells3
nsbatwm made image
shape(256, 256)
dtypeint32
size256.0 kB
min1
max27

If there multiple cells sticking together, it may make sense to specify spot_sigma. This parameter allows to configure how close / large cells are.

labeles_cells4 = local_minima_seeded_watershed(
                       membrane_channel, 
                       spot_sigma=9, 
                       outline_sigma=3)
labeles_cells4
nsbatwm made image
shape(256, 256)
dtypeint32
size256.0 kB
min1
max28

Exercise#

Load the following dataset and find good parameters for processing it using a seeded watershed approach. This example image data is a courtesy of Sascha M. Kuhn, Nadler Lab, MPI-CBG Dresden.

image_slice = imread("../../data/membrane_2d_timelapse.tif")[2]

imshow(image_slice)
../_images/89170cd60610412fc7cc87e64ecf0259448f516bd2ea1014fbaa5d85c41d7f86.png