Saltar al contenido principal

Umbralización

info

Todo el código de ejemplo se encuentra en Google Colab para su ejecución directa. Si no puedes ejecutar OpenCV localmente, puedes usar Google Colab junto con matplotlib para visualizar imágenes. Además las imágenes que se utilizarán en los ejemplos son tobias_wilson.jpg y sudoku.jpg.

Objetivo

  • Aprender los principales métodos de umbralización de imágenes.
  • Comprender cómo se segmenta el primer plano del fondo mediante umbrales globales, adaptativos y automáticos (Otsu).
  • Aplicar técnicas de binarización en OpenCV para preparar imágenes para análisis y procesamiento posterior.

Conceptos clave

  • Umbral (threshold): Valor que define el límite entre fondo y objeto.
  • Primer plano: Región de interés de la imagen que queremos resaltar.
  • Fondo: Área de la imagen que se considera irrelevante para el análisis.
  • Binarización: Proceso de asignar solo dos valores a los píxeles (por ejemplo 0 y 255).
  • Umbral global: Mismo valor de umbral para toda la imagen.
  • Umbral adaptativo: Umbral calculado localmente para cada región de la imagen, útil cuando la iluminación no es uniforme.
  • Otsu: Método automático que calcula el umbral óptimo minimizando la varianza intra-clase.

Funciones principales en OpenCV

  • cv.threshold(): Aplica umbralización global y el tipo de umbral se define con cv.THRESH_*.
  • cv.adaptiveThreshold(): Aplica umbralización adaptativa, calculando el umbral localmente.
  • Tipos de umbralización global:
    • cv.THRESH_BINARY(): Píxeles > umbral → valor máximo; resto → 0.
    • cv.THRESH_BINARY_INV(): Binario inverso.
    • cv.THRESH_TRUNC(): Píxeles > umbral → umbral; resto se mantiene.
    • cv.THRESH_TOZERO(): Píxeles ≤ umbral → 0; resto se mantiene.
    • cv.THRESH_TOZERO_INV(): Binario inverso a TOZERO.
  • Tipos de umbralización adaptativa:
    • cv.ADAPTIVE_THRESH_MEAN_C: umbral = promedio local − C.
    • cv.ADAPTIVE_THRESH_GAUSSIAN_C: umbral = promedio ponderado (Gaussiano) − C.

Umbralización simple

La umbralización simple o global consiste en aplicar un único valor de umbral a toda la imagen. Es útil cuando la iluminación es uniforme y el contraste entre objeto y fondo es claro. Permite segmentar objetos de manera rápida y preparar la imagen para análisis posteriores, como detección de contornos o OCR.

import cv2 as cv
import matplotlib.pyplot as plt

img = cv.imread('tobias_wilson.jpg', cv.IMREAD_GRAYSCALE)

ret, thresh1 = cv.threshold(
img, # Imagen de entrada
40, # Valor del umbral
255, # Valor máximo a asignar si se cumple la condición
cv.THRESH_BINARY) # Umbral binario: píxeles > 40 -> 255, píxeles <= 40 -> 0

ret, thresh2 = cv.threshold(
img, # Imagen de entrada
40, # Umbral
255, # Valor máximo
cv.THRESH_BINARY_INV) # Binario inverso: píxeles > 40 -> 0, píxeles <= 40 -> 255

ret, thresh3 = cv.threshold(
img, # Imagen de entrada
40, # Umbral
255, # Valor máximo
cv.THRESH_TRUNC) # Truncamiento: píxeles >40 -> 40, píxeles <=40 -> se mantienen igual

ret, thresh4 = cv.threshold(
img, # Imagen de entrada
40, # Umbral
255, # Valor máximo
cv.THRESH_TOZERO) # TOZERO: píxeles >40 -> se mantienen igual, píxeles <=40 -> 0

ret, thresh5 = cv.threshold(
img, # Imagen de entrada
40, # Umbral
255, # Valor máximo
cv.THRESH_TOZERO_INV) # TOZERO inverso: píxeles > 40 -> 0, píxeles <= 40 -> se mantienen igual

# Crear listas de títulos y de imágenes
titles = ['Imagen original','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]

# Generar los plots de las imágenes
plt.figure(figsize=(16, 8))

for i in range(6):
plt.subplot(2,3,i+1),plt.imshow(images[i],'gray',vmin=0,vmax=255)
plt.title(titles[i])
plt.xticks([]),plt.yticks([])

plt.show()

Umbralización adaptativa

Características de la umbralización adaptativa con media:

  • Calcula un umbral diferente para cada píxel basado en la media de su vecindad.
  • Kernel (blockSize) determina el tamaño de la vecindad considerada. Un bloque más grande suaviza variaciones locales, uno más pequeño captura detalles finos.
  • La constante C ajusta el umbral hacia abajo (o hacia arriba si es negativa) para evitar que píxeles claros se pierdan o que píxeles oscuros se incluyan erróneamente. Características de la umbralización adaptativa con Gaussiana:
  • Similar a la adaptativa por media, pero cada píxel de la vecindad tiene un peso según una función Gaussiana (los píxeles cercanos pesan más).
  • Es útil cuando hay iluminación desigual o gradientes suaves, porque da más importancia a los píxeles cercanos al punto a umbralizar.
  • El kernel (blockSize) y la constante C funcionan igual que en la media, ajustando sensibilidad y alcance del umbral.
import cv2 as cv
from matplotlib import pyplot as plt

img = cv.imread('sudoku.jpg', cv.IMREAD_GRAYSCALE)

# Aplicar un filtro de mediana para reducir ruido
img = cv.medianBlur(
img, # Imagen de entrada
5) # Tamaño del kernel (vecindad de 5x5 píxeles para calcular la mediana)

# Umbralización global (Thresholding)
ret, th1 = cv.threshold(
img, # Imagen de entrada
127, # Umbral fijo
255, # Valor máximo asignado a los píxeles
cv.THRESH_BINARY) # Píxeles > 127 -> 255, píxeles <= 127 -> 0

# Umbralización adaptativa con media
th2 = cv.adaptiveThreshold(
img, # Imagen de entrada
255, # Valor máximo asignado a los píxeles
cv.ADAPTIVE_THRESH_MEAN_C, # Tipo de cálculo de umbral: Promedio de la vecindad
cv.THRESH_BINARY, # Tipo de umbral binario
11, # Tamaño del bloque de vecindad (11 x 11)
2) # Constante C que se resta del promedio

# Umbralización adaptativa con Gaussiana
th3 = cv.adaptiveThreshold(
img, # Imagen de entrada
255, # Valor máximo
cv.ADAPTIVE_THRESH_GAUSSIAN_C, # Tipo de cálculo: ponderación Gaussiana de la vecindad
cv.THRESH_BINARY, # Tipo de umbral binario
11, # Tamaño del bloque de vecindad (11 x 11)
2) # Constante C que se resta del promedio

titles = ['Original Image', 'Global Thresholding (v = 127)',
'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]

# Generar los plots de las imágenes
plt.figure(figsize=(16, 8))

for i in range(4):
plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])

plt.tight_layout()
plt.show()

Binarización Otsu's

La binarización de Otsu es un método automático para determinar el umbral óptimo para separar el primer plano del fondo en una imagen en escala de grises. Este método es especialmente útil cuando la imagen tiene un histograma bimodal, es decir, dos picos distintos que representan las intensidades del primer plano y del fondo.

¿Cómo funciona la binarización de Otsu?

  1. Cálculo del histograma: Se calcula el histograma de la imagen en escala de grises para obtener la distribución de las intensidades de píxeles.
  2. Iteración sobre posibles umbrales: Se itera sobre todos los posibles umbrales (0-255 para imágenes de 8 bits) y, para cada umbral, se divide la imagen en dos clases:
    • Clase 1: Píxeles con intensidades menores o iguales al umbral.
    • Clase 2: Píxeles con intensidades mayores al umbral.
  3. Cálculo de la varianza intra-clase: Para cada umbral, se calcula la varianza intra-clase, que mide la dispersión de las intensidades dentro de cada clase. La varianza intra-clase se define como la suma ponderada de las varianzas de las dos clases.
  4. Selección del umbral óptimo: El umbral que minimiza la varianza intra-clase es seleccionado como el umbral óptimo para la binarización.
import cv2 as cv
import matplotlib.pyplot as plt

# Cargar imagen en escala de grises
img = cv.imread('tobias_Wilson.jpg', 0)

# Binarización con Otsu
umbral, img_otsu = cv.threshold(
img, # Imagen de entrada
0, # Valor del umbral inicial - 0 -> calcula automáticamente
255, # Valor máximo a asignar a los píxeles
cv.THRESH_BINARY + cv.THRESH_OTSU # Tipo de umbral: Binario + Otsu
)

# Generar los plots de las imágenes
plt.figure(figsize=(16, 8))
plt.subplot(1, 2, 1)
plt.imshow(img, cmap='gray')
plt.title('Imagen original')
plt.axis('off')

plt.subplot(1, 2, 2)
plt.imshow(img_otsu, cmap='gray')
plt.title(f"Binarización Otsu con umbral {int(umbral)}")
plt.axis('off')

plt.show()