Umbralización o thresholding con Python y openCV

La umbralización o thresholding consiste en obtener una representación binaria de una imagen, por medio de la modificación de los píxeles. El valor de los píxeles se modificaran segun un valor umbral que establezcamos. En este post revisaremos tres tipos de umbralización: Umbralización simple, adaptativa y Otzu

Umbralización (thresholding) simple

La umbralización simple consiste el modificar el valor de un píxel si sobrepasa el umbral establecido. Existen varios tipos de umbralización simple establecidos en la documentación de openCV como:THRESH_BINARYTHRESH_BINARY_INVTHRESH_TRUNCTHRESH_TOZEROTHRESH_TOZERO_INV

Para describir la umbralización simple uso la imagen en escala de grises que matiza desde 0 a 255.

Los píxeles de tonalidad más oscura estarán cerca de 0 y los más claros cerca de 255. (Si no puedes hacer zoom el los pixeles, ejecuta tu código en un entorno virtual conda.

Para aplicar la umbralización simple se usa el siguiente comando en python:

ret,thresh = cv2.threshold(img, umbral, valorMax , tipo)

Los parámetros son los siguientes:

  1. img es la imagen gris que va a ser analizada
  2. umbral es el valor indicado a analizar en cada píxel
  3. valorMax Valor que se coloca a un píxel si sobrepasa el umbral
  4. tipo se elige un tipo de umbralización

La función devuelve:

  1. thresh imagen binarizada
  2. ret valor del umbral

En la siguente imagen, los píxeles por donde pasa la línea punteada tendrán el valor de 100. En los siguientes ejemplos considero que el umbral es 100, lo que significa que se verán cambios en la umbralización a la derecha e izquierda de la línea punteada.

THRESH_BINARY

Si el píxel (src(x,y)) supera el umbral (thresh), en la imagen binarizada dst(x,y) a los píxeles que superaron el umbral se les asigna el valor máximo establecido (maxval en este ejemplo es 255). A continuación el código.

import cv2

img = cv2.imread('matizGris.png',0)
ret,thresh = cv2.threshold(img,100,255,cv2.THRESH_BINARY)

cv2.imshow("Imagen", img)
cv2.imshow("THRESH", thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()

Los píxeles que superaron el valor de 100, ahora son todos 255 y los que no superaron el umbral ahora tiene el valor de 0

THRESH_BINARY_INV

Si el píxel (src(x,y)) supera el umbral (thresh), en la imagen binarizada dst(x,y) a los píxeles que superaron el umbral se les asigna cero 0 y a los que no superaron el umbral se les asigna el valor máximo establecido (maxval en este ejemplo es 255). Debemos añadir esta línea en el código.

ret,thresh = cv2.threshold(img,100,255,cv2.THRESH_BINARY)

THRESH_TRUNC

Si el píxel (src(x,y)) supera el umbral (thresh), en la imagen binarizada dst(x,y) a los píxeles que superaron el umbral se les asigna el mismo valor del umbral y a los que no superaron el umbral se les asigna los mismos valores que tenían originalmente. A continuación el código.

ret,thresh = cv2.threshold(img,100,255,cv2.THRESH_TRUNC)

THRESH_TOZERO

Si el píxel (src(x,y)) supera el umbral (thresh), en la imagen binarizada a los píxeles que superaron el umbral mantienen el valor de los pixeles originalmente, y cuando no superan el umbral se les asigna cero. A continuación el código.

ret,thresh = cv2.threshold(img,100,255,cv2.THRESH_TOZERO)

THRESH_TOZERO_INV

Si el píxel (src(x,y)) supera el umbral (thresh), en la imagen binarizada a los píxeles que superaron el umbral se les asigna cero, y a los píxeles que no superaron el umbral se les asigna el mismo valor que originalmente tenías. A continuación el código.

ret,thresh = cv2.threshold(img,100,255,cv2.THRESH_TOZERO_INV)

Umbralización (thresholding) adaptativa

Usare la imagen de una página para mostrar como puede mejorar la umbralización con un método adaptativo. En la imagen de la derecha vemos como la umbralización simple no da buenos resultados con variaciones de iluminación.

Página con variaciones de iluminación

Umbralización simple, THRESH_BINARY_INV

A diferencia de la umbralización simple, la umbralización adaptativa no tendrá un valor umbral establecido. Sino que el “algoritmo determina el umbral de un píxel en función de una pequeña región a su alrededor. Entonces obtenemos diferentes umbrales para diferentes regiones de la misma imagen, lo que brinda mejores resultados para imágenes con iluminación variable” 1.

Se puede seleccionar entre dos métodos adaptativos, que son los que deciden como se va a calcular el valor umbral:

  1. cv.ADAPTIVE_THRESH_MEAN_C
  2. cv.ADAPTIVE_THRESH_GAUSSIAN_C
threshold = cv2.adaptiveThreshold(img,maxValue,adaptiveMethod,thresholdType,blockSize,C)
  1. img es una imagen de un solo canal, debe ser en escala de grises
  2. maxValue valor asignado a los pixeles cuando cumplen la condición
  3. adaptiveMethod método adaptativo, se elige entre cv.ADAPTIVE_THRESH_MEAN_Ccv.ADAPTIVE_THRESH_GAUSSIAN_C
  4. thresholdType elegir un método de umbralización
  5. blockSize Tamaño de una vecindad de píxeles que se utiliza para calcular un valor de umbral para el píxel: 3, 5, 7, etc
  6. C Constante restada de la media o media ponderada

El código a continuación, muestra como la umbralización adaptativa funciona. El tamaño del bloque lo establezco como 11 y la contante C como 10 siguiendo el ejemplo de openCV documents, pero más adelante probaré con otros valores.

import cv2

img = cv2.imread('libro.jpg',0)
th1 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,11,10)
th2 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,11,10)

cv2.imshow("ADAPTIVE_THRESH_MEAN_C", th1)
cv2.imshow("ADAPTIVE_THRESH_GAUSSIAN_C", th2)

cv2.waitKey(0)
cv2.destroyAllWindows()

ADAPTIVE_THRESH_MEAN_C

ADAPTIVE_THRESH_GAUSSIAN_C

Método de Otsu

A diferencia del la umbralización simple, Otsu calcula automáticamente un umbral para toda la imagen. Y a diferencia de la umbralización adaptativa, el umbral es el mismo en toda la imagen y no varía por regiones.

import cv2

img = cv2.imread('libro.jpg',0)

ret1, th1 = cv2.threshold(img,0,255,cv2.THRESH_OTSU)
print('Umbral de th1:', ret1)

cv2.imshow("Imagen", img)
cv2.imshow("THRESH_OTSU", th1)
cv2.waitKey(0)
cv2.destroyAllWindows()

Inicialmente los valores del umbral los establecí aleatoriamente, y luego al ejecutar el método de Otsu, en la consola se imprime los valores del umbral que estableció automáticamente el algoritmo de Otsu. Puse 3 imágenes para ver como el algoritmo encuentra diferentes umbrales.

Umbral de th1: 150.0

El resultado de Otsu es el siguiente, para la imagen del libro:

Referencias

  1. OpenCV documentación. Image Thresholding. https://docs.opencv.org/4.5.2/d7/d4d/tutorial_py_thresholding.html 

2 comentarios en “Umbralización o thresholding con Python y openCV”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *