Conteo de núcleos en mosaicos#
En este cuaderno procesaremos un gran conjunto de datos que ha sido guardado en formato zarr para contar células en mosaicos individuales. Para cada mosaico, escribiremos un píxel en una imagen de salida. Por lo tanto, estamos produciendo una imagen de conteo de células que es más pequeña que la imagen original por un factor que corresponde al tamaño del mosaico.
import zarr
import dask.array as da
import numpy as np
from skimage.io import imread
import pyclesperanto_prototype as cle
from pyclesperanto_prototype import imshow
from numcodecs import Blosc
Para fines de demostración, utilizamos un conjunto de datos proporcionado por Theresa Suckert, OncoRay, Hospital Universitario Carl Gustav Carus, TU Dresden. El conjunto de datos está licenciado bajo Licencia: CC-BY 4.0. Estamos utilizando aquí una versión recortada que fue guardada como imagen de 8 bits para poder proporcionarla con el cuaderno. Puede encontrar la imagen completa de 16 bits en formato de archivo CZI en línea.
image = imread('../../data/P1_H_C3H_M004_17-cropped.tif')[1]
# para propósitos de prueba, recortamos aún más la imagen.
# comente la siguiente línea para ejecutar en los 5000x2000 píxeles completos
image = image[1000:1500, 1000:1500]
#comprime Y cambia el array numpy a un array zarr
compressor = Blosc(cname='zstd', clevel=3, shuffle=Blosc.BITSHUFFLE)
# Convierte la imagen en un array zarr
chunk_size = (100, 100)
zarray = zarr.array(image, chunks=chunk_size, compressor=compressor)
# guarda zarr en el disco
zarr_filename = '../../data/P1_H_C3H_M004_17-cropped.zarr'
zarr.convenience.save(zarr_filename, zarray)
Cargando la imagen respaldada por zarr#
Dask trae soporte integrado para el formato de archivo zarr. Podemos crear arrays dask directamente desde un archivo zarr.
zarr_image = da.from_zarr(zarr_filename)
zarr_image
|
Podemos aplicar procesamiento de imágenes a este conjunto de datos en mosaico directamente.
Contando núcleos#
Para contar los núcleos, configuramos un flujo de trabajo simple de procesamiento de imágenes que aplica el Etiquetado Voronoi-Otsu al conjunto de datos. Después, contamos los objetos segmentados. Como los núcleos que tocan el borde del mosaico podrían contarse dos veces, tenemos que corregir el conteo para cada mosaico. Técnicamente, podríamos eliminar los objetos que tocan uno de los bordes verticales u horizontales del mosaico. Sin embargo, hay una forma más simple de corregir este error: Contamos el número de núcleos después de la segmentación. Luego, eliminamos todos los núcleos que tocan cualquier borde de la imagen y contamos los núcleos restantes nuevamente. Entonces podemos asumir que la mitad de los núcleos eliminados deberían contarse. Por lo tanto, sumamos los dos conteos, antes y después de la eliminación de bordes, y calculamos el promedio de estas dos mediciones. Especialmente en mosaicos grandes con muchos núcleos, el error restante debería ser insignificante. No se recomienda aplicar este método estimativo de conteo de células cuando cada mosaico contiene solo unos pocos núcleos.
def count_nuclei(image):
"""
Etiqueta objetos en una imagen binaria y produce una imagen de mapa de conteo de píxeles.
"""
print("Procesando imagen de tamaño", image.shape)
# Cuenta núcleos incluyendo aquellos que tocan el borde de la imagen
labels = cle.voronoi_otsu_labeling(image, spot_sigma=3.5)
label_intensity_map = cle.mean_intensity_map(image, labels)
high_intensity_labels = cle.exclude_labels_with_map_values_within_range(label_intensity_map, labels, maximum_value_range=20)
nuclei_count = high_intensity_labels.max()
# Cuenta núcleos incluyendo aquellos que tocan el borde de la imagen
labels_without_borders = cle.exclude_labels_on_edges(high_intensity_labels)
nuclei_count_excluding_borders = labels_without_borders.max()
# Tanto el conteo de núcleos incluyendo como excluyendo núcleos en los bordes de la imagen
# no son una buena aproximación. Deberíamos excluir los núcleos solo en
# la mitad de los bordes para obtener una buena estimación.
# Alternativamente, simplemente tomamos el promedio de ambos conteos.
result = np.asarray([[(nuclei_count + nuclei_count_excluding_borders) / 2]])
print(result.shape)
return result
Antes de poder iniciar el cálculo, necesitamos desactivar la ejecución asíncrona de operaciones en pyclesperanto. Ver también el problema relacionado.
cle.set_wait_for_kernel_finish(True)
Esta vez, no usamos superposición de mosaicos, porque no estamos midiendo propiedades de los núcleos y, por lo tanto, no necesitamos una segmentación perfecta de ellos.
tile_map = da.map_blocks(count_nuclei, zarr_image)
tile_map
Processing image of size (0, 0)
Processing image of size (1, 1)
(1, 1)
Processing image of size (0, 0)
|
Como la imagen resultante es mucho más pequeña que la original, podemos calcular todo el mapa de resultados.
result = tile_map.compute()
Processing image of size (100, 100)
Processing image of sizeProcessing image of size (100, 100)
Processing image of size (100, 100)
(100, 100)
Processing image of size (100, 100)
Processing image of size (100, 100)
Processing image of sizeProcessing image of size (100, 100)
Processing image of size(100, 100)
(100, 100)
Processing image of size (100, 100)
(1, 1)
(1, 1)
Processing image of size (100, 100)
(1, 1)
Processing image of size (100, 100)
Processing image of size (100, 100)
(1, 1)(1, 1)
Processing image of size (100, 100)
Processing image of size(1, 1)
(100, 100)
Processing image of size (100, 100)
(1, 1)
(1, 1)
Processing image of size (100, 100)
Processing image of size (100, 100)
(1, 1)
Processing image of size (100, 100)
(1, 1)
Processing image of size (100, 100)
(1, 1)
Processing image of size (100, 100)
(1, 1)
(1, 1)
Processing image of sizeProcessing image of size (100, 100)
(100, 100)
(1, 1)(1, 1)
(1, 1)
Processing image of size Processing image of size(100, 100) (1, 1)
(100, 100)
(1, 1)
(1, 1)
(1, 1)
(1, 1)
(1, 1)
(1, 1)
(1, 1)
(1, 1)
result.shape
(5, 5)
De nuevo, como el mapa de resultados es pequeño, podemos simplemente visualizarlo.
cle.imshow(result, colorbar=True)
Con una rápida comprobación visual en la imagen original, podemos ver que efectivamente en la esquina inferior izquierda de la imagen hay más células que en la superior derecha.
cle.imshow(cle.voronoi_otsu_labeling(image, spot_sigma=3.5), labels=True)