Umbralización#

La umbralización es una técnica de segmentación de imágenes. Separa una imagen de un solo canal dada (o pila) en dos regiones: Píxeles con intensidad por debajo de un umbral dado, también llamado “fondo” y píxeles con intensidad por encima de un umbral dado, “primer plano”. Típicamente, estos algoritmos resultan en imágenes binarias donde la intensidad de fondo es 0 y la intensidad de primer plano es 1. Al aplicar tales algoritmos en ImageJ, los píxeles de primer plano son 255. En scikit-image, los píxeles de fondo son False y los píxeles de primer plano son True.

Ver también

from skimage.io import imread
from pyclesperanto_prototype import imshow
import pyclesperanto_prototype as cle

from skimage import filters
from skimage.filters import try_all_threshold
from matplotlib import pyplot as plt
import napari_simpleitk_image_processing as nsitk
image = imread("../../data/blobs.tif")
imshow(image)
../_images/697332ed931701bd777704b279b47e121b548544b81f7a635c3783b21c04fb53.png

Segmentación de imágenes por umbralización#

La operación threshold_otsu, también conocida como método de Otsu (Otsu et al., IEEE Transactions on Systems, Man, and Cybernetics, Vol. 9 (1), 1979), proporciona un número - el umbral a aplicar.

threshold = filters.threshold_otsu(image)

Cuando se utilizan métodos como la umbralización en notebooks, se recomienda imprimir el resultado para ver qué devuelve realmente. Aquí, estamos utilizando el método de scikit-image, que devuelve el umbral que se aplica. Imprimir ese umbral puede ser útil más adelante al reproducir el flujo de trabajo, también si otros quieren aplicar el mismo umbral al conjunto de datos en otro software.

threshold
120

Usando arrays de numpy, podemos aplicar el umbral utilizando el operador >=. El resultado será una imagen binaria.

binary_image = image >= threshold

imshow(binary_image)
../_images/a7b2159f4f800a1618c3d6082c124079d4f7e3116111a1a4a4a67982e4eecc1e.png

También podemos determinar en qué tipo se procesa la imagen binaria imprimiendo el mínimo y el máximo de la imagen:

binary_image.max()
True
binary_image.min()
False

Como se mostró anteriormente, matplotlib nos permite dibujar un contorno sobre una imagen visualizada usando imshow mediante el comando contour.

# crear un nuevo gráfico
fig, axes = plt.subplots(1,1)

# añadir dos imágenes
axes.imshow(image, cmap=plt.cm.gray)
axes.contour(binary_image, [0.5], linewidths=1.2, colors='r')
<matplotlib.contour.QuadContourSet at 0x2b57076dc70>
../_images/06c7022a7454d1160b496fab99037089bf65b12b028c885551cc71daf820b4ae.png

Hay una lista de algoritmos de umbralización disponibles. Es posible aplicarlos todos a tus datos y ver las diferencias:

fig, ax = try_all_threshold(image, figsize=(10, 8), verbose=False)
plt.show()
../_images/a5bde9d6498b8293b645bd6971e1c424b09bc31486fc161743a8bd2a4341cf99.png

Umbralización usando pyclesperanto#

Además, otras bibliotecas como pyclesperanto también ofrecen algoritmos de umbralización. La implementación aquí no devuelve el umbral, sino que devuelve directamente la imagen binaria.

binary_image2 = cle.threshold_otsu(image)
imshow(binary_image2)
../_images/aac24fde3fd260040b5033ca92c02ff51dae5ae320de2449b8e8e86ef339dc9d.png

Aquí también podemos ver que diferentes bibliotecas almacenan imágenes binarias de diferentes maneras. pyclesperanto, por ejemplo, almacena los píxeles positivos en imágenes binarias no como True sino con un 1 en su lugar:

binary_image2.max()
1.0
binary_image2.min()
0.0

Umbralización usando SimpleITK#

SimpleITK también ofrece algoritmos de umbralización que se pueden encontrar en la lista de filtros. Para facilitar la escritura de scripts, aquí usamos napari-simpleitk-image-processing, un plugin de napari programable que ofrece algunas funciones de SimpleITK de una manera más accesible. Podemos programar un pequeño bucle for que pruebe todos los algoritmos de umbralización en SimpleITK y nos muestre los resultados:

threshold_algorithms = [
    nsitk.threshold_huang,
    nsitk.threshold_intermodes,
    nsitk.threshold_isodata,
    nsitk.threshold_kittler_illingworth,
    nsitk.threshold_li,
    nsitk.threshold_maximum_entropy,
    nsitk.threshold_moments,
    nsitk.threshold_otsu,
    nsitk.threshold_renyi_entropy,
    nsitk.threshold_shanbhag,
    nsitk.threshold_triangle,
    nsitk.threshold_yen
]

for algorithm in threshold_algorithms:
    # mostrar el nombre del algoritmo encima de la imagen
    print(algorithm.__name__)
    
    # binarizar la imagen usando el algoritmo dado
    binary_image = algorithm(image)
    
    # mostrar el resultado de la segmentación
    imshow(binary_image)
threshold_huang
../_images/45e3c8efd769b88b4fc58e4d5c1a50a912d9366ec57f499833c7d4818a16d68f.png
threshold_intermodes
../_images/474585bfc41a7150477a90c9ae4f2ce918a7ed0041e2e5f2eea18e8ace4ae1bb.png
threshold_isodata
../_images/474585bfc41a7150477a90c9ae4f2ce918a7ed0041e2e5f2eea18e8ace4ae1bb.png
threshold_kittler_illingworth
../_images/8d4c59070474247eb02c19ca4d21208dca15778cc0c5e13fadd3e195e8885ce0.png
threshold_li
../_images/ea3d20faef42e8488598e1c914b90d031aefa3b733b82e7d5690f7cbfac67525.png
threshold_maximum_entropy
../_images/a7b2159f4f800a1618c3d6082c124079d4f7e3116111a1a4a4a67982e4eecc1e.png
threshold_moments
../_images/474585bfc41a7150477a90c9ae4f2ce918a7ed0041e2e5f2eea18e8ace4ae1bb.png
threshold_otsu
../_images/aac24fde3fd260040b5033ca92c02ff51dae5ae320de2449b8e8e86ef339dc9d.png
threshold_renyi_entropy
../_images/fccedafce8680667fc01673c4f353031a8b0ce3a2762337531c13b06b405af7e.png
threshold_shanbhag
../_images/507204442c9b905ad28921ef50996a9879695302aaa3686ab31ef89b2579de14.png
threshold_triangle
../_images/aac24fde3fd260040b5033ca92c02ff51dae5ae320de2449b8e8e86ef339dc9d.png
threshold_yen
../_images/134aa11339889cf89aba337af18d894502fe56f28ac0dc73bc0e4d0d8af43abe.png

Ejercicio#

Segmenta blobs.tif usando el algoritmo de Yen. Usa matplotlib para dibujar un contorno verde de los objetos segmentados alrededor de las regiones en la imagen original.

Segmenta la imagen usando un umbral calculado según esta ecuación:

threshold = mean + 2 * standard_deviation

Visualiza la segmentación resultante con un contorno rojo sobre la imagen original y el contorno verde de arriba.

Alternativamente, coloca ambos resultados de segmentación en napari y compáralos visualmente allí.