Skip to content

Clase 10 — Histogramas y Filtros Espaciales (Tema 7 final + Tema 8)

Resumen Ejecutivo

La clase completa el Tema 7 (introducción a OpenCV) y arranca el Tema 8 (filtros espaciales). Se cubren tres bloques: (1) histogramas de imágenes en escala de grises y color, normalización y ecualización; (2) dibujo básico en OpenCV (rectas, círculos, rectángulos, texto); y (3) filtros espaciales de suavizado, con el concepto de convolución y kernel, tratamiento de bordes y los tres primeros filtros: media (cv2.blur), aplicación genérica (cv2.filter2D) y preámbulo del gaussiano y la mediana que se verán la semana siguiente.

Aviso relevante: la profesora confirma que para el examen es muy importante saber dibujar en 3D con OpenGL.


Conceptos Clave

  • Histograma: gráfica que muestra el número de píxeles para cada nivel de intensidad (0–255) de una imagen. ⚠️ EXAMEN
  • Histograma normalizado: divide las frecuencias absolutas entre el total de píxeles → porcentaje de aparición de cada intensidad. Permite comparar imágenes de distinto tamaño. ⚠️ EXAMEN
  • Ecualización de histograma: técnica que "estira" el rango de grises concentrado para revelar detalles ocultos a simple vista.
  • Filtro puntual: opera píxel a píxel sin considerar el vecindario.
  • Filtro espacial: calcula el nuevo valor de cada píxel usando su vecindario. Es el enfoque que se estudia en la asignatura. ⚠️ EXAMEN
  • Filtro frecuencial: trabaja en el dominio de la frecuencia mediante la transformada de Fourier 2D (no se profundiza por su complejidad matemática).
  • Convolución: tipo de filtrado espacial que desplaza un kernel por toda la imagen y calcula el producto escalar entre el kernel y cada vecindario. ⚠️ EXAMEN
  • Kernel (máscara): matriz pequeña (típicamente 3×3, 5×5, …) de lado impar para que el píxel tratado quede centrado. Sus valores determinan el efecto del filtro. ⚠️ EXAMEN
  • Filtro de la media (cv2.blur): kernel con todos los valores iguales a \(\frac{1}{N^2}\); suaviza calculando la media del vecindario.
  • cv2.filter2D: función genérica de convolución que acepta cualquier kernel definido por el usuario.

Desarrollo del Temario

1. Histogramas de imágenes

Un histograma recorre la imagen y acumula el número de píxeles para cada valor de intensidad de 0 a 255. En escala de grises hay 256 niveles posibles.

Lectura del histograma:

Forma del histograma Interpretación
Pico en zona izquierda (0–50) Imagen oscura
Pico en zona derecha (200–255) Imagen clara
Distribución homogénea ("meseta") Imagen bien equilibrada / ecualizada
Solo 5 picos Imagen compuesta exactamente por 5 tonos

Histograma en color: se calcula por canal (B, G o R por separado). OpenCV lee en BGR, por lo que el canal 0 = azul, 1 = verde, 2 = rojo.

Normalización:

\[h_{norm}(i) = \frac{h(i)}{W \times H}\]

donde \(W \times H\) es el número total de píxeles.

Función OpenCV:

hist = cv2.calcHist(
    [img],       # lista de imágenes
    [0],         # canal(es): 0=gris/azul, 1=verde, 2=rojo
    None,        # máscara (None = imagen completa)
    [256],       # número de intervalos
    [0, 256]     # rango (límite superior excluido)
)
# Normalización
pixels = img.shape[0] * img.shape[1]
hist_norm = hist / pixels
  • Si se pasa una máscara (imagen binaria), solo los píxeles blancos de la máscara entran en el cálculo.
  • Se puede pasar más de un canal: [0, 1] → histograma combinado de los canales 0 y 1.

Ecualización: la profesora la introduce conceptualmente — iguala la distribución del histograma estirando el rango de grises. Se implementará en sesiones posteriores con cv2.equalizeHist.

2. Dibujo básico en OpenCV

OpenCV proporciona funciones para superponer primitivas gráficas sobre imágenes (útil para marcar lo detectado):

import numpy as np, cv2

# Crear imagen negra vacía
canvas = np.zeros((400, 600, 3), dtype=np.uint8)

# Línea
cv2.line(canvas, (50, 50), (300, 50), (0, 255, 0), 2)

# Rectángulo
cv2.rectangle(canvas, (50, 100), (200, 250), (255, 0, 0), 3)

# Círculo (grosor -1 = relleno)
cv2.circle(canvas, (400, 150), 60, (0, 0, 255), -1)

# Texto
cv2.putText(canvas, 'Hola OpenCV', (50, 350),
            cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)

cv2.imshow('canvas', canvas)
cv2.waitKey(0)
cv2.destroyAllWindows()

Parámetros comunes: (imagen, punto_inicio, punto_fin, color_BGR, grosor). Grosor -1 rellena la forma.

3. Filtros espaciales — Clasificación

Filtros
├── Puntuales        — operan píxel a píxel (sin vecindario)
├── Espaciales       — utilizan el vecindario de cada píxel  ← los de esta asignatura
│   ├── Convolucionales (media, gaussiano, Sobel, Laplaciano…)
│   └── No convolucionales (mediana)
└── Frecuenciales    — trabajan en dominio de la frecuencia (Fourier 2D)

Los filtros espaciales se ubican en la fase de preprocesado del pipeline de visión artificial, antes del análisis.

Usos del suavizado / blur: - Reducir ruido antes de aplicar detectores de bordes o contornos. - Efecto bokeh / modo retrato (suavizar el fondo manteniendo el sujeto nítido). - Ocultar zonas de una imagen (aplicar blur solo sobre una región con máscara). - Aumentación de datasets para redes neuronales (generar variantes de imágenes).

4. Convolución

La convolución desplaza un kernel \(K\) de tamaño \(m \times m\) por cada píxel \((x,y)\) de la imagen y calcula:

\[I'(x,y) = \sum_{i=-\lfloor m/2 \rfloor}^{\lfloor m/2 \rfloor} \sum_{j=-\lfloor m/2 \rfloor}^{\lfloor m/2 \rfloor} K(i,j) \cdot I(x+i,\, y+j)\]

Por qué el kernel debe tener lado impar: para que el píxel tratado quede centrado en la máscara. ⚠️ EXAMEN

Tratamiento de bordes: los píxeles del borde no tienen vecindario completo. Estrategias disponibles en OpenCV:

Constante OpenCV Estrategia
cv2.BORDER_CONSTANT Rellenar con un valor constante (por defecto 0)
cv2.BORDER_REPLICATE Replicar el píxel del borde más cercano
cv2.BORDER_REFLECT Reflexión especular en el borde
cv2.BORDER_WRAP Usar píxeles del lado opuesto

En la práctica el efecto es mínimo para imágenes grandes (solo afecta 1–2 píxeles de borde según tamaño del kernel).

Proceso general de la convolución:

  1. Definir el kernel (sus valores determinan el efecto).
  2. Desplazar el kernel centrado sobre cada píxel de la imagen.
  3. Calcular el producto escalar del kernel con la región cubierta.
  4. Asignar ese resultado como nuevo valor del píxel.

5. Filtro de la media (cv2.blur)

Kernel \(3 \times 3\):

\[K_{3\times3} = \frac{1}{9}\begin{pmatrix}1&1&1\\1&1&1\\1&1&1\end{pmatrix}\]

Kernel \(N \times N\) genérico: todos los valores iguales a \(\frac{1}{N^2}\).

Efecto: promedia el vecindario → suaviza bordes y reduce ruido. A mayor tamaño de kernel, mayor desenfoque. ⚠️ EXAMEN

img_blur = cv2.blur(img, (3, 3))   # kernel 3×3
img_blur = cv2.blur(img, (7, 7))   # kernel 7×7, más desenfoque

En imágenes color, blur aplica el filtro de manera independiente a cada canal (B, G, R) y los combina en la imagen resultante.

6. Función genérica de convolución (cv2.filter2D)

Permite aplicar cualquier kernel personalizado:

# Definir kernel de la media 5×5 manualmente
kernel = np.ones((5, 5), dtype=np.float32) / 25

# Aplicar convolución
# -1 en ddepth → misma profundidad que la imagen original
img_filtrada = cv2.filter2D(img, -1, kernel)

Parámetros: - src: imagen de entrada. - ddepth: profundidad de la imagen de salida; -1 = igual que la entrada (uint8). Se puede cambiar a float si el filtro produce valores fuera de [0, 255]. - kernel: matriz numpy con los coeficientes del filtro.

Efecto del tamaño del kernel sobre el desenfoque:

Al aumentar el kernel de 3×3 a 9×9 sobre una imagen con texto, los píxeles negros de las letras se mezclan con los píxeles claros del fondo, generando un gradiente progresivo (0 → 30 → 60 → 90) en lugar del cambio brusco original.

7. Aviso sobre el examen

La profesora confirma que la parte de OpenGL en el examen incluirá dibujo en 3D. Se dedicará una sesión próxima a corrección de la actividad grupal, que servirá también como repaso orientado al examen. ⚠️ EXAMEN


Preguntas de Autoevaluación

  1. ¿Qué mide el histograma de una imagen? ¿Qué diferencia hay entre el histograma absoluto y el normalizado?
  2. Dado el histograma de una imagen, ¿cómo distingues si la imagen es oscura, clara o equilibrada?
  3. ¿Para qué sirve la ecualización del histograma? ¿Qué problema visual resuelve?
  4. ¿Qué parámetros recibe cv2.calcHist? ¿Qué indica el parámetro de canal en una imagen BGR?
  5. ¿Qué diferencia hay entre un filtro puntual, uno espacial y uno frecuencial?
  6. ¿Qué es la convolución? Describe el proceso paso a paso.
  7. ¿Por qué el kernel de un filtro espacial debe tener lado impar?
  8. ¿Qué estrategias existen para tratar los píxeles del borde durante la convolución? ¿Tiene importancia práctica en imágenes grandes?
  9. Escribe el kernel 3×3 del filtro de la media. ¿Qué efecto produce sobre la imagen?
  10. ¿Qué hace cv2.filter2D que no hace cv2.blur? ¿Para qué se usa el parámetro ddepth=-1?
  11. ¿Por qué aumentar el tamaño del kernel produce más desenfoque?