Optimización de parámetros para la segmentación celular basada en imágenes de membrana#

Los flujos de trabajo para segmentar células a partir de imágenes de tinción de membrana suelen ser difíciles de optimizar. En este cuaderno demostramos cómo optimizar automáticamente el algoritmo de cuenca hidrográfica con semillas, un enfoque común para este tipo de datos de imagen.

from napari_workflow_optimizer import JaccardLabelImageOptimizer, Workflow

from skimage.io import imread
import napari_segment_blobs_and_things_with_membranes as nsbatwm
import pyclesperanto_prototype as cle
import matplotlib.pyplot as plt
from the_segmentation_game.metrics import jaccard_index_sparse

Configuramos un flujo de trabajo e insertamos una única operación, Cuenca hidrográfica con semillas usando mínimos locales como puntos de inicio utilizando el complemento Napari napari-segment-blobs-and-things-with-membranes. El algoritmo tiene dos parámetros: spot_sigma para ajustar qué tan cerca pueden estar los puntos de semilla y outline_sigma para ajustar qué tan precisas deben ser las membranas segmentadas.

w = Workflow()
w.set("labeled", # result image name
      nsbatwm.thresholded_local_minima_seeded_watershed, # operation
      "input", spot_sigma=2, outline_sigma=2) # parameters
# image data source: scikit-image cells3d example, slice 28
w.set("input", imread("../../data/membranes_2d.tif"))
input_image = w.get("input")
cle.imshow(input_image)
../_images/e582bd9aaa1d965ac4bbf1c6dc70e9c071f0818c0cfc99ea62e7e963029484a6.png

Producimos un primer resultado de segmentación que está sobresegmentado, obviamente se encuentran demasiadas células.

result = w.get("labeled")
cle.imshow(result, labels=True)
../_images/94cbfdd60d9e0413d52cad0a1633de52542aa84530ed4b3fb916eeeb3a0855bb.png

Para darle al algoritmo de segmentación alguna verdad fundamental con la que comparar los resultados de segmentación, usamos esta imagen de anotación dispersa. Típicamente es suficiente anotar con precisión algunas células de ejemplo. Es mejor invertir tiempo en hacer buenas segmentaciones y no dibujar tantas.

ground_truth = imread("../../data/membranes_2d_sparse_labels.tif")
cle.imshow(ground_truth, labels=True)
../_images/9db50b653fefde0c26cfabf08d8f73b6e8a880aad8a14aaf0cebcdce1fc1342e.png

Luego podemos iniciar el JaccardLabelImageOptimizer. Solo para probar, inspeccionamos el punto de inicio actual para la optimización.

jlio = JaccardLabelImageOptimizer(w)
jlio.get_numeric_parameters()
[2, 2, 500]

Luego iniciamos la optimización y después imprimimos el conjunto de parámetros optimizados.

best_param = jlio.optimize("labeled", ground_truth, maxiter=100)
best_param
array([  2.34307473,   5.6861856 , -74.78749191])

También podemos pedir al optimizador que establezca estos parámetros por nosotros e inspeccionar la imagen de etiquetas resultante.

jlio.set_numeric_parameters(best_param)
cle.imshow(w.get("labeled"), labels=True)
../_images/85473129a1e7a7ea7e18a6c348231691b1ff249ea3ce6aecb9e2c2b70cc179c4.png

La calidad de esta imagen se puede medir promediando el Índice de Jaccard de los tres objetos de verdad fundamental. La biblioteca The Segmentation Game tiene una función para esto.

jaccard_index_sparse(ground_truth, w.get("labeled"))
0.8300891581238193

A veces, el resultado no es perfecto y es posible que queramos cambiar un parámetro y ver si se puede mejorar el resultado.

new_starting_point = best_param.copy()
new_starting_point[0] = 8

new_starting_point
array([  8.        ,   5.6861856 , -74.78749191])
jlio2 = JaccardLabelImageOptimizer(w)
jlio2.set_numeric_parameters(new_starting_point)
cle.imshow(w.get("labeled"), labels=True)
../_images/1af543a541e502ab71e23fa598db3fdce5e138f7e7e8044945fa209c9839631e.png

Luego podemos iniciar un segundo intento.

best_param = jlio.optimize("labeled", ground_truth, maxiter=100)
best_param
array([  9.50775939,   1.39446381, -83.9132378 ])
jlio.set_numeric_parameters(best_param)
cle.imshow(w.get("labeled"), labels=True)
../_images/38c6a0a6aa934ecea8c6ee139e0f341375e2a41ff16f565e519997e3d335ae87.png
jaccard_index_sparse(ground_truth, w.get("labeled"))
0.8905848327576197