OpenCV

Tutorial OpenCV: Histograma y ecualización

OpenCV y Seaborn: cálculo y visualización de histogramas para una mejor comprensión de la distribución de píxeles en imágenes, hitogramas y ecualización en python.

Aprende OpenCV GRATIS y certifícate

Cálculo de histogramas y visualización con Seaborn

Un histograma es una herramienta fundamental en el procesamiento de imágenes que permite analizar la distribución de intensidades de los píxeles. En OpenCV, el cálculo de histogramas se realiza de manera eficiente mediante la función cv2.calcHist, ofreciendo información valiosa sobre el brillo y contraste de la imagen.

Para calcular el histograma de una imagen en escala de grises, primero debemos asegurarnos de que la imagen esté en ese formato. Utilizamos la función cv2.cvtColor para convertir una imagen de color a escala de grises:

import cv2

# Cargar la imagen en color
imagen_color = cv2.imread('ruta/a/la/imagen.jpg')

# Convertir a escala de grises
imagen_gris = cv2.cvtColor(imagen_color, cv2.COLOR_BGR2GRAY)

Una vez obtenida la imagen en escala de grises, procedemos a calcular su histograma:

# Calcular el histograma
histograma = cv2.calcHist([imagen_gris], [0], None, [256], [0, 256])

Aquí, [0] indica que estamos analizando el primer canal (en imágenes en escala de grises solo hay un canal). El parámetro None significa que no aplicamos ninguna máscara. [256] determina el número de bins o intervalos, y [0, 256] establece el rango de intensidades a considerar.

Podemos utilizar la biblioteca Seaborn, que proporciona herramientas avanzadas para gráficos estadísticos.

import seaborn as sns
import matplotlib.pyplot as plt

# Convertir el histograma a un array unidimensional
histograma_array = histograma.ravel()

# Crear un DataFrame con los datos del histograma
import pandas as pd
datos_histograma = pd.DataFrame({'intensidad': range(256), 'frecuencia': histograma_array})

# Visualizar el histograma con Seaborn
sns.barplot(data=datos_histograma, x='intensidad', y='frecuencia', color='blue')

plt.xlabel('Intensidad de píxel')
plt.ylabel('Frecuencia')
plt.title('Histograma de la Imagen en Escala de Grises')
plt.show()

En este ejemplo, utilizamos un gráfico de barras para representar la frecuencia de cada nivel de intensidad. Seaborn se encarga de manejar el estilo y la estética del gráfico, facilitando la interpretación de los datos.

Si trabajamos con imágenes en color, es útil calcular y visualizar los histogramas de cada uno de los canales de color por separado. Esto nos permite analizar la distribución de intensidades en los canales azul, verde y rojo:

# Separar los canales B, G y R
canal_b, canal_g, canal_r = cv2.split(imagen_color)

# Calcular histogramas para cada canal
hist_b = cv2.calcHist([canal_b], [0], None, [256], [0, 256])
hist_g = cv2.calcHist([canal_g], [0], None, [256], [0, 256])
hist_r = cv2.calcHist([canal_r], [0], None, [256], [0, 256])

# Visualizar los histogramas de los tres canales
plt.figure()

plt.plot(hist_b, color='blue', label='Canal Azul')
plt.plot(hist_g, color='green', label='Canal Verde')
plt.plot(hist_r, color='red', label='Canal Rojo')

plt.xlabel('Intensidad de píxel')
plt.ylabel('Frecuencia')
plt.title('Histogramas de los Canales de Color')
plt.legend()
plt.show()

Esta representación nos ofrece una visión detallada de cómo se distribuyen las intensidades en cada canal, lo cual es esencial para aplicaciones como la corrección de color y la detección de objetos.

La comprensión y visualización de histogramas nos prepara para técnicas más avanzadas como la ecualización de histograma, que mejora el contraste de las imágenes distribuyendo de manera más uniforme las intensidades de los píxeles.

Ecualización de histograma

La ecualización de histograma es una técnica fundamental en procesamiento de imágenes que mejora el contraste ajustando la intensidad de los píxeles. Este método redistribuye los valores de intensidad para cubrir todo el rango disponible, lo que resalta detalles ocultos en áreas oscuras o brillantes de la imagen.

En OpenCV, la función cv2.equalizeHist facilita la aplicación de esta técnica a imágenes en escala de grises. Veamos un ejemplo práctico:

import cv2
import matplotlib.pyplot as plt

# Cargar la imagen en escala de grises
imagen_gris = cv2.imread('ruta/a/la/imagen.jpg', cv2.IMREAD_GRAYSCALE)

# Aplicar ecualización de histograma
imagen_ecualizada = cv2.equalizeHist(imagen_gris)

# Mostrar la imagen original y la ecualizada
figura, ejes = plt.subplots(1, 2, figsize=(12, 6))

ejes[0].imshow(imagen_gris, cmap='gray')
ejes[0].set_title('Imagen Original')
ejes[0].axis('off')

ejes[1].imshow(imagen_ecualizada, cmap='gray')
ejes[1].set_title('Imagen Ecualizada')
ejes[1].axis('off')

plt.show()

En este código, se carga una imagen en escala de grises y se aplica la ecualización de histograma. Al comparar ambas imágenes, se observa un incremento en el contraste, destacando características previamente imperceptibles.

Para entender mejor el impacto, es útil visualizar los histogramas antes y después de la ecualización:

# Calcular los histogramas
histograma_original = cv2.calcHist([imagen_gris], [0], None, [256], [0, 256])
histograma_ecualizado = cv2.calcHist([imagen_ecualizada], [0], None, [256], [0, 256])

# Graficar los histogramas
figura, ejes = plt.subplots(1, 2, figsize=(12, 6))

ejes[0].plot(histograma_original, color='black')
ejes[0].set_title('Histograma Original')
ejes[0].set_xlabel('Intensidad')
ejes[0].set_ylabel('Frecuencia')

ejes[1].plot(histograma_ecualizado, color='black')
ejes[1].set_title('Histograma Ecualizado')
ejes[1].set_xlabel('Intensidad')
ejes[1].set_ylabel('Frecuencia')

plt.show()

Los histogramas revelan cómo la ecualización redistribuye las intensidades para abarcar todo el espectro de valores, creando una distribución más uniforme.

Cuando se trabaja con imágenes en color, la ecualización se aplica de manera diferente. Ecualizar cada canal RGB por separado puede alterar los colores originales. Una solución es convertir la imagen al espacio de color YCrCb o LAB y aplicar la ecualización únicamente al canal de luminancia. Aquí se muestra cómo hacerlo con el espacio YCrCb:

# Cargar la imagen en color
imagen_color = cv2.imread('ruta/a/la/imagen.jpg')

# Convertir al espacio YCrCb
imagen_ycrcb = cv2.cvtColor(imagen_color, cv2.COLOR_BGR2YCrCb)

# Separar los canales
canal_y, canal_cr, canal_cb = cv2.split(imagen_ycrcb)

# Ecualizar el canal de luminancia
canal_y_ecualizado = cv2.equalizeHist(canal_y)

# Recomponer la imagen
imagen_ycrcb_ecualizada = cv2.merge([canal_y_ecualizado, canal_cr, canal_cb])

# Convertir de vuelta al espacio BGR
imagen_ecualizada = cv2.cvtColor(imagen_ycrcb_ecualizada, cv2.COLOR_YCrCb2BGR)

# Mostrar las imágenes
figura, ejes = plt.subplots(1, 2, figsize=(12, 6))

ejes[0].imshow(cv2.cvtColor(imagen_color, cv2.COLOR_BGR2RGB))
ejes[0].set_title('Imagen Original')
ejes[0].axis('off')

ejes[1].imshow(cv2.cvtColor(imagen_ecualizada, cv2.COLOR_BGR2RGB))
ejes[1].set_title('Imagen Ecualizada')
ejes[1].axis('off')

plt.show()

Este enfoque preserva la integridad de los colores mientras mejora el contraste. Al ecualizar solo el canal de luminancia, evitamos distorsiones cromáticas que podrían surgir al modificar los canales de color directamente.

La ecualización de histograma es especialmente útil en aplicaciones donde es necesario realzar detalles en imágenes con iluminación deficiente o contrastes bajos. Sin embargo, es importante tener en cuenta que esta técnica puede aumentar el ruido en zonas con baja relación señal-ruido, por lo que se debe aplicar con precaución en imágenes sensibles.

En situaciones donde la ecualización global no es suficiente o puede introducir artefactos, se recomienda explorar la ecualización adaptativa, que ajusta el contraste de forma local y se abordará en la siguiente sección.

Ecualización adaptativa (CLAHE)

La ecualización adaptativa de histograma, conocida como CLAHE (Contrast Limited Adaptive Histogram Equalization), es una técnica avanzada que mejora el contraste de una imagen de forma local. A diferencia de la ecualización global, CLAHE divide la imagen en pequeñas regiones llamadas mosaicos y aplica la ecualización en cada una de ellas individualmente. Esto permite realzar detalles en zonas específicas sin amplificar excesivamente el ruido ni crear artefactos.

En OpenCV, la implementación de CLAHE se realiza mediante la clase cv2.createCLAHE, que proporciona parámetros ajustables para controlar el proceso. Los dos parámetros más importantes son:

  • clipLimit: define el límite de contraste para evitar que el histograma se sobrepase y generar así una ecualización más natural.
  • tileGridSize: especifica el tamaño en píxeles de los mosaicos en los que se divide la imagen.

A continuación, se muestra cómo aplicar CLAHE a una imagen en escala de grises:

import cv2
import matplotlib.pyplot as plt

# Cargar la imagen en escala de grises
imagen_gris = cv2.imread('ruta/a/la/imagen.jpg', cv2.IMREAD_GRAYSCALE)

# Crear el objeto CLAHE con los parámetros deseados
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))

# Aplicar CLAHE a la imagen
imagen_clahe = clahe.apply(imagen_gris)

# Mostrar la imagen original y la procesada
figura, ejes = plt.subplots(1, 2, figsize=(12, 6))

ejes[0].imshow(imagen_gris, cmap='gray')
ejes[0].set_title('Imagen Original')
ejes[0].axis('off')

ejes[1].imshow(imagen_clahe, cmap='gray')
ejes[1].set_title('Imagen con CLAHE')
ejes[1].axis('off')

plt.show()

En este ejemplo, se establece un clipLimit de 2.0 y un tileGridSize de (8, 8). El límite de contraste controla cuánto se extiende el histograma en cada mosaico, evitando la sobresaturación. El tamaño de mosaico determina el equilibrio entre el contraste local y global: mosaicos más pequeños potencian detalles locales, mientras que mosaicos más grandes aportan una apariencia más uniforme.

Para aplicar CLAHE a imágenes en color, es recomendable transformar la imagen al espacio de color LAB y operar sobre el canal de luminosidad. Esto preserva la integridad cromática mientras se mejora el contraste:

# Cargar la imagen en color
imagen_color = cv2.imread('ruta/a/la/imagen.jpg')

# Convertir al espacio LAB
imagen_lab = cv2.cvtColor(imagen_color, cv2.COLOR_BGR2LAB)

# Separar los canales L, A y B
canal_l, canal_a, canal_b = cv2.split(imagen_lab)

# Aplicar CLAHE al canal de luminosidad
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
canal_l_clahe = clahe.apply(canal_l)

# Recomponer la imagen LAB con el canal L ecualizado
imagen_lab_clahe = cv2.merge([canal_l_clahe, canal_a, canal_b])

# Convertir de vuelta al espacio BGR
imagen_clahe = cv2.cvtColor(imagen_lab_clahe, cv2.COLOR_LAB2BGR)

# Mostrar las imágenes original y ecualizada
figura, ejes = plt.subplots(1, 2, figsize=(12, 6))

# Convertir a RGB para visualización con Matplotlib
imagen_color_rgb = cv2.cvtColor(imagen_color, cv2.COLOR_BGR2RGB)
imagen_clahe_rgb = cv2.cvtColor(imagen_clahe, cv2.COLOR_BGR2RGB)

ejes[0].imshow(imagen_color_rgb)
ejes[0].set_title('Imagen Original')
ejes[0].axis('off')

ejes[1].imshow(imagen_clahe_rgb)
ejes[1].set_title('Imagen con CLAHE')
ejes[1].axis('off')

plt.show()

Al aplicar la ecualización adaptativa al canal de luminosidad, se mejora el contraste sin alterar los colores originales de la imagen. El espacio de color LAB separa la información de luminosidad de la cromática, lo que es esencial para mantener la fidelidad de color en el procesamiento.

Es importante ajustar los parámetros de CLAHE según las características de la imagen. Un clipLimit más alto incrementa el contraste pero puede introducir artefactos, mientras que un valor más bajo produce cambios sutiles. El tileGridSize afecta la escala a la que se mejora el contraste: tamaños más pequeños enfatizan detalles locales, y tamaños más grandes proporcionan un efecto más global.

La ecualización adaptativa es especialmente útil en áreas como la medicina, la astronomía y la visión por computadora, donde las imágenes pueden tener variaciones significativas de iluminación y contraste. CLAHE permite extraer detalles en regiones oscuras o brillantes, mejorando la calidad y la información que se puede obtener de la imagen.

Al integrar CLAHE en flujos de procesamiento, se facilita el análisis y se potencian las capacidades de algoritmos posteriores, como la detección de bordes o la segmentación. Esta técnica es una herramienta valiosa para optimizar la apariencia y la utilidad de las imágenes en diversos campos.

Aprende OpenCV GRATIS online

Todas las lecciones de OpenCV

Accede a todas las lecciones de OpenCV y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a OpenCV y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  1. Aprender a manipular imágenes y convertirlas a escala de grises.
  2. Calcular histogramas con OpenCV para representar distribuciones de píxeles.
  3. Visualizar histogramas mediante representaciones gráficas con Seaborn.
  4. Diseñar gráficos de barras que ilustren intensidades de píxeles.
  5. Diferenciar entre histogramas de imágenes en escala de grises y en color.