%matplotlib inline

Cell-segmentation for H&E stains

This example shows how to use processing and segmentation functions to segment images with H&E stains.

For a general example of how to use squidpy.im.segment(), see Cell-segmentation for fluorescence images.

Note that we only provide a basic built-in segmentation model. If you require precise cell-segmentation and cell-counts, you might want to add more pre-processing and/or use a pre-trained model to do the segmentation (using squidpy.im.SegmentationCustom()).

See also

import numpy as np

import matplotlib.pyplot as plt
import seaborn as sns

import squidpy as sq

# load the H&E stained tissue image and crop to a smaller segment
img = sq.datasets.visium_hne_image_crop()
crop = img.crop_corner(0, 0, size=1000)

Before segmenting the image, we smooth it using squidpy.im.process.

# smooth image
sq.im.process(crop, layer="image", method="smooth", sigma=4)

# plot the result
fig, axes = plt.subplots(1, 2)
for layer, ax in zip(["image", "image_smooth"], axes):
    crop.show(layer, ax=ax)
    ax.set_title(layer)
../../../_images/31bb6f9bbe8ae0c1f45130511b90067ff76b87c1733a68aa504b0f7b133edc3b.png

We will use channel 0 to do the segmentation, as this channel contains most of the nuclei information within an H&E stain. Instead of using automatic threshold with Otsu’s method, we will define a manual fixed threshold. Note that using Otsu’s method to determine the threshold also yields good results.

Judging by peak in the histogram and the thresholded example image, a threshold of 90, seems to be a good choice for this example.

fig, axes = plt.subplots(1, 3, figsize=(15, 4))
crop.show("image_smooth", cmap="gray", ax=axes[0])
axes[1].imshow(crop["image_smooth"][:, :, 0, 0] < 90)
_ = sns.histplot(np.array(crop["image_smooth"]).flatten(), bins=50, ax=axes[2])
plt.tight_layout()
../../../_images/a0388f0c022c077e4af06ea4096ad17f24a55495fee33db243d75f68e8065be4.png

We use squidpy.im.segment with method = 'watershed' to do the segmentation. Since, opposite to the fluorescence DAPI stain, in the H&E stain nuclei appear darker, we need to indicate to the model that it should treat lower-intensity values as foreground. We do this by specifying the geq = False in the kwargs.

sq.im.segment(img=crop, layer="image_smooth", method="watershed", thresh=90, geq=False)

The segmented crop is saved in the layer [segmented_watershed]{.title-ref}. This behavior can be changed with the arguments copy and layer_added. The result of the segmentation is a label image that can be used to extract features like the number of cells from the image.

print(crop)
print(f"Number of segments in crop: {len(np.unique(crop['segmented_watershed']))}")

fig, axes = plt.subplots(1, 2)
crop.show("image", channel=0, ax=axes[0])
_ = axes[0].set_title("H&E")
crop.show("segmented_watershed", cmap="jet", interpolation="none", ax=axes[1])
_ = axes[1].set_title("segmentation")
ImageContainer[shape=(1000, 1000), layers=['image', 'image_smooth', 'segmented_watershed']]
Number of segments in crop: 1182
../../../_images/96942f43e5cf4f29c78f72c7402115c3f9f94ebbae163fee7312935f0657e02d.png