Mapear área de objetos en mosaicos#

En este notebook, segmentaremos núcleos en mosaicos y mediremos su área. Luego guardaremos el mapa de área resultante nuevamente como mosaicos en un archivo zarr. Esta estrategia se puede utilizar para procesar datos que en su totalidad no caben en la memoria de la computadora.

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

Con 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á bajo la Licencia: CC-BY 4.0. Estamos utilizando aquí una versión recortada que se volvió a guardar como imagen de 8 bits para poder proporcionarla con el notebook. 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.
# comenta 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 disco
zarr_filename = '../../data/P1_H_C3H_M004_17-cropped.zarr'
zarr.convenience.save(zarr_filename, zarray)

Mapas de área de objetos en mosaicos#

Dask trae soporte incorporado para el formato de archivo zarr. Podemos crear arrays dask directamente desde un archivo zarr.

zarr_image = da.from_zarr(zarr_filename)
zarr_image
Array Chunk
Bytes 244.14 kiB 9.77 kiB
Shape (500, 500) (100, 100)
Count 26 Tasks 25 Chunks
Type uint8 numpy.ndarray
500 500

Podemos aplicar procesamiento de imágenes a este conjunto de datos en mosaicos directamente.

def area_map(image):
    """
    Etiqueta objetos en una imagen binaria y produce una imagen de mapa de recuento de píxeles.
    """
    print("Procesando imagen de tamaño", image.shape)
    
    labels = cle.voronoi_otsu_labeling(image, spot_sigma=3.5)
    result = cle.pixel_count_map(labels)
    
    print(result.shape)
    
    return np.asarray(result)

Probando el procesamiento de imágenes en mosaicos#

Deberíamos probar nuestro algoritmo de mapeo de área en un solo mosaico. De hecho, en un escenario real, el flujo de trabajo de procesamiento de imágenes se desarrolla en mosaicos individuales, por ejemplo, en un notebook como este. Tan pronto como estemos seguros de que el algoritmo funciona, podemos aplicarlo a todos los mosaicos.

test_image = image[100:200,100:200]

imshow(test_image)

test_result = area_map(test_image)

imshow(test_result, colorbar=True)
../_images/7fce941cc2b9e407b5f3e6d98a0031d0628688b98c7476550029656dc5f4de03.png
Processing image of size (100, 100)
(100, 100)
../_images/335ca48e02120ec432331eb1833241e12114dad1999d55723e8bdf3b5e9fde32.png

Aplicando el procesamiento de imágenes en mosaicos a un conjunto de datos respaldado por zarr#

Aplicar la función a nuestro conjunto de datos zarr también resultará en un array dask.

overlap_width = 30

tile_map = da.map_overlap(area_map, zarr_image, depth=overlap_width, boundary=0)

tile_map
Processing image of size (0, 0)
Processing image of size (1, 1)
(1, 1)
Processing image of size (0, 0)
Array Chunk
Bytes 0.95 MiB 39.06 kiB
Shape (500, 500) (100, 100)
Count 632 Tasks 25 Chunks
Type float32 numpy.ndarray
500 500

Antes de poder iniciar el cálculo, necesitamos desactivar la ejecución asincrónica de operaciones en pyclesperanto. Ver también el problema relacionado.

cle.set_wait_for_kernel_finish(True)

Cuando invocamos guardar los resultados en el disco, el procesamiento ocurrirá en mosaicos individuales.

processed_zarr_filename = '../../data/P1_H_C3H_M004_17-processed.zarr'

tile_map.to_zarr(processed_zarr_filename, overwrite=True)
Processing image of size (160, 160)
Processing image of size (160, 160)
Processing image of size (160, 160)
Processing image of size (160, 160)
Processing image of size (160, 160)
Processing image of size Processing image of size (160, 160)
(160, 160)
Processing image of size (160, 160)
Processing image of size (160, 160)
Processing image of size (160, 160)
(160, 160)
Processing image of size (160, 160)
(160, 160)
(160, 160)
(160, 160)
Processing image of size Processing image of size (160, 160)
(160, 160)
(160, 160)
(160, 160)
(160, 160)
Processing image of size (160, 160)
Processing image of size(160, 160) 
(160, 160)
Processing image of size (160, 160)
(160, 160)
Processing image of size (160, 160)
Processing image of size (160, 160)
Processing image of size (160, 160)
(160, 160)
Processing image of size (160, 160)
(160, 160)
Processing image of size (160, 160)
(160, 160)
(160, 160)
(160, 160)
(160, 160)Processing image of sizeProcessing image of size(160, 160)
 (160, 160)
 
(160, 160)
Processing image of size (160, 160)
Processing image of size (160, 160)
(160, 160)
(160, 160)
(160, 160)
(160, 160)
(160, 160)
(160, 160)
(160, 160)
(160, 160)
(160, 160)

Cargando zarr#

Solo con fines de demostración, cargaremos la imagen en mosaicos respaldada por zarr y la visualizaremos. Al trabajar con big data, este paso podría no ser posible.

zarr_result = da.from_zarr(processed_zarr_filename)
zarr_result
Array Chunk
Bytes 0.95 MiB 39.06 kiB
Shape (500, 500) (100, 100)
Count 26 Tasks 25 Chunks
Type float32 numpy.ndarray
500 500
result = zarr_result.compute()

cle.imshow(result)
../_images/1340399ecadb71d0437e5783e77f0e4a20a913050db3bc49638531f1c048029c.png