Creación de mosaicos de imágenes con superposición#

Cuando procesamos imágenes en mosaicos, podemos observar artefactos en los bordes de los mosaicos en la imagen resultante. Una estrategia para prevenir estos artefactos es procesar mosaicos con cierta superposición. dask también admite esto. De nuevo, con fines demostrativos, usamos imshow para mostrar las imágenes resultantes. Si estos fueran datos grandes, la función imshow no funcionaría.

import dask
import dask.array as da
from skimage.filters import gaussian
from skimage.data import cells3d
from pyclesperanto_prototype import imshow

De manera similar al ejemplo en la última lección, definimos un procedimiento que aplica un desenfoque gaussiano a una imagen e imprime el tamaño de la imagen, solo para que lo sepamos:

def procedure(image):
    print("proceduring", image.shape)
    return gaussian(image, sigma=5)
image = cells3d()[30,1]
imshow(image)
../_images/e6d2393f7c605c769fb82b772552ade1566917e2a294f57aa9e4f747b0c78793.png

Después de cargar la imagen, la dividimos en mosaicos como de costumbre.

tiles = da.from_array(image, chunks=(128, 128))
tiles
Array Chunk
Bytes 128.00 kiB 32.00 kiB
Shape (256, 256) (128, 128)
Count 4 Tasks 4 Chunks
Type uint16 numpy.ndarray
256 256

A continuación, le decimos a dask qué hacer con nuestros mosaicos: Queremos mapear la función procedure a los mosaicos con una superposición definida.

overlap_width = 1
tile_map = da.map_overlap(procedure, tiles, depth=overlap_width)
proceduring (0, 0)
proceduring (1, 1)
/Users/haase/opt/anaconda3/envs/bio_39/lib/python3.9/site-packages/dask/array/overlap.py:642: FutureWarning: Default 'boundary' argument value will change from 'reflect' to 'none' in future versions from 2022.03.0 onwards. Use 'boundary="none"' to opt into the future behavior now or set 'boundary="reflect"' to maintain the current behavior going forward.
  warnings.warn(

La función se ejecutó dos veces con imágenes muy pequeñas (0x0 y 1x1 píxeles) para comprobar si funciona. A continuación, calculamos realmente el resultado.

result = tile_map.compute() # Advertencia: Esto carga todos los datos de la imagen en la memoria
proceduringproceduring (130, 130)
proceduring (130, 130)
 (130, 130)
proceduring (130, 130)

A partir del tamaño de imagen impreso, podemos ver que el tamaño de la imagen procesada es 2 píxeles más grande que el tamaño del mosaico. Esa es la superposición de 1 píxel en todas las direcciones.

Minimizando los efectos de borde#

A continuación, compararemos el resultado al procesar la imagen completa con la imagen procesada en mosaicos con diferentes superposiciones. Esto nos da la oportunidad de determinar el ancho mínimo necesario de superposición para eliminar los efectos de borde. Primero, calculamos el resultado para la imagen completa.

untiled_result = procedure(image)
proceduring (256, 256)

Luego, ejecutamos un bucle for con diferentes border_widths.

for overlap_width in range(0, 25, 5):
    print("Ancho de superposición", overlap_width)
    tile_map = da.map_overlap(procedure, tiles, depth=overlap_width, boundary='nearest')
    result = tile_map.compute()
    difference = result - untiled_result
    imshow(difference)
    print("suma de diferencia", difference.sum())
    print("-----------------------------------")
Overlap width 0
proceduring (0, 0)
proceduring (1, 1)
proceduringproceduring (128, 128)
proceduring (128, 128) (128, 128)

proceduring (128, 128)
../_images/e4126918c50e4b551767159a05ace9b17cb60db4891f34ce4000369b3cfe9c54.png
sum difference 1.528818863147824
-----------------------------------
Overlap width 5
proceduring (0, 0)
proceduring (1, 1)
proceduring (138, 138)
proceduring (138, 138)
proceduring (138, 138)
proceduring (138, 138)
../_images/13eb6b058a8c32ec6d6cdd255cc7627467c4e18c5cc7345049e9843d1ec85552.png
sum difference 2.098167990865754
-----------------------------------
Overlap width 10
proceduring (0, 0)
proceduring (1, 1)
proceduringproceduring (148, 148)
 (148, 148)
proceduring (148, 148)
proceduring (148, 148)
../_images/4ba9572136695137b5fa129099e0fd00fbf9be14726ad27fc105eb7ac77396b6.png
sum difference -0.18132395183423158
-----------------------------------
Overlap width 15
proceduring (0, 0)
proceduring (1, 1)
proceduring (158, 158)
proceduring (158, 158)
proceduring (158, 158)
proceduring (158, 158)
../_images/475a541b79caf5ff201e8f11ef3f1c1d04afbb786c252c87e5ee31d2d560ac53.png
sum difference -0.005761703866654207
-----------------------------------
Overlap width 20
proceduring (0, 0)
proceduring (1, 1)
proceduring (168, 168)
proceduring (168, 168)
proceduring (168, 168)
proceduring (168, 168)
../_images/48eebdd7f37bda714d1326c6ccf8d5b9a96db531693e290ee45bd898b2277a4e.png
sum difference 0.0
-----------------------------------

Como puedes ver, para eliminar completamente el efecto de borde, necesitamos usar una superposición de 25 píxeles. Esto está obviamente relacionado con el procedure que aplicamos. En nuestro caso, el desenfoque gaussiano utilizado en procedure se configuró con sigma=5. Como regla general, podemos decir que en el caso de un desenfoque gaussiano, el ancho del borde debe ser al menos cuatro veces el sigma configurado. Sin embargo, cuando se utilizan algoritmos más complicados, no existen tales reglas. En general, se recomienda probar el procesamiento de imágenes en mosaicos en imágenes pequeñas como se demuestra aquí y averiguar si aparecen artefactos y qué error pueden causar en un flujo de trabajo de procesamiento de imágenes más largo.