Post-traitement pour la segmentation cellulaire basée sur les membranes#
Dans ce notebook, nous utilisons un algorithme de ligne de partage des eaux avec germes de napari-segment-blobs-and-things-with-membranes pour segmenter les cellules et ensuite filtrer les cellules pour éliminer les objets segmentés de manière erronée.
import matplotlib.pyplot as plt
import numpy as np
from skimage.io import imread
import pyclesperanto_prototype as cle
import napari_segment_blobs_and_things_with_membranes as nsbatwm
Les données d’image d’exemple suivantes sont une séquence temporelle de cellules marquées avec un marqueur membranaire. Le jeu de données est une courtoisie de Sascha M. Kuhn, Nadler Lab, MPI-CBG Dresden.
image_timelapse = imread("../../data/membrane_2d_timelapse.tif")
image_timelapse.shape
(5, 256, 256)
Nous commençons par extraire un seul canal, un seul point temporel comme exemple du milieu de la pile de séquences temporelles.
membranes_single_slice = image_timelapse[2]
membranes_single_slice.shape
(256, 256)
cle.imshow(membranes_single_slice)
Le plugin napari-segment-blobs-and-things-with-membrances est scriptable. Toutes les commandes qui peuvent être appelées depuis le menu peuvent également être appelées depuis Python. Par exemple, la ligne de partage des eaux avec germes de minima locaux peut être appelée comme ceci :
cell_labels_pre = nsbatwm.local_minima_seeded_watershed(membranes_single_slice, spot_sigma=7)
cle.imshow(cell_labels_pre, labels=True)
Évidemment, il y a trop d’objets segmentés et étiquetés après cette étape. Par conséquent, nous devrions explorer quelles propriétés de ces cellules nous permettent de différencier les vraies cellules et l’arrière-plan segmenté. Par exemple, comme les cellules ont également une membrane à l’avant et à l’arrière, nous percevons ce signal comme une intensité plus élevée dans les vraies cellules. Pour visualiser cela quantitativement, nous dessinons une carte d’intensité moyenne des cellules. Dans cette carte, chaque pixel appartenant à une cellule donnée reçoit l’intensité moyenne du signal de la cellule entière.
mean_intensity_map = cle.mean_intensity_map(membranes_single_slice, cell_labels_pre)
cle.imshow(mean_intensity_map, colorbar=True, colormap='jet')
À partir de cette vue et de la barre de couleurs sur le côté, nous pouvons deviner un seuil, par exemple 650, qui nous permet de séparer les vraies cellules de l’arrière-plan. Si une telle méthode de seuillage simple ne fonctionne pas et/ou qu’un seul paramètre tel que l’intensité moyenne ne permet pas cette différenciation, il peut être judicieux d’utiliser des classificateurs d’objets, également connus sous le nom d’apprentissage automatique. Par exemple, le plugin napari-accelerate-pixel-and-object-classification et la bibliothèque sous-jacente apoc peuvent aider.
cell_labels = cle.exclude_labels_with_map_values_within_range(mean_intensity_map, cell_labels_pre, maximum_value_range=700)
cle.imshow(cell_labels, labels=True)
Application du workflow à l’ensemble de la séquence temporelle#
Nous avons maintenant une idée approximative de la façon dont un workflow pour segmenter les cellules se présente. Nous réécrivons l’ensemble du workflow dans une fonction. La fonction dispose également d’une documentation appropriée afin que notre futur nous-même sache encore ce que fait la fonction.
def cell_segmentation_worflow(membranes_single_slice, cell_segmentation_spot_sigma=7, maximum_mean_intensity_per_cell=700):
"""Segmentation cellulaire basée sur le signal membranaire
Paramètres
----------
membranes_single_slice
Image à segmenter
cell_segmentation_spot_sigma: float, optionnel
Permet de configurer la segmentation. Plus sigma est élevé, moins il y aura de cellules.
maximum_mean_intensity_per_cell: float, optionnel
Seuil pour l'intensité moyenne par cellule. Les cellules avec un signal inférieur seront exclues
Retourne
-------
image d'étiquettes de cellules
"""
# étiqueter les candidats cellulaires
cell_labels_pre = nsbatwm.local_minima_seeded_watershed(
membranes_single_slice,
spot_sigma=cell_segmentation_spot_sigma)
# mesurer l'intensité dans tous les candidats
mean_intensity_map = cle.mean_intensity_map(
membranes_single_slice,
cell_labels_pre)
# exclure les candidats avec un faible signal
cell_labels = cle.exclude_labels_with_map_values_within_range(
mean_intensity_map, cell_labels_pre,
maximum_value_range=maximum_mean_intensity_per_cell)
return cell_labels
Cette fonction, nous pouvons l’appliquer à l’ensemble de la séquence temporelle et vérifier si la segmentation fonctionne également correctement dans tous les points temporels.
for t in range(len(image_timelapse)):
# extraire une seule tranche / point temporel
membranes_single_slice = image_timelapse[t]
# segmenter les cellules
cell_labels = cell_segmentation_worflow(membranes_single_slice)
# imprimer le titre
print("t = ", t, ", nombre de cellules : ", cell_labels.max())
# afficher trois images : image, étiquettes, superposition
fix, axs = plt.subplots(1, 3, figsize=(10,10))
cle.imshow(membranes_single_slice, plot=axs[0])
cle.imshow(cell_labels, plot=axs[1], labels=True)
cle.imshow(membranes_single_slice, plot=axs[2], alpha=0.5, continue_drawing=True)
cle.imshow(cell_labels, plot=axs[2], labels=True, alpha=0.5)
plt.show()
t = 0 , number of cells: 17.0
t = 1 , number of cells: 18.0
t = 2 , number of cells: 17.0
t = 3 , number of cells: 18.0
t = 4 , number of cells: 17.0
Exercice#
Au lieu de filtrer les cellules avec une faible intensité de signal, filtrez les cellules qui sont grandes et/ou touchent le bord de l’image. Appliquez le nouveau workflow à l’ensemble de la séquence temporelle.