Skip to content

Clase 12 — Tema 9: Filtros de Detección de Bordes. Revisión Actividad Grupal OpenGL

Resumen Ejecutivo

Sesión doble (1h 42m). Primera hora: Tema 9 completo — filtros de detección de bordes en OpenCV. Se trabajan los filtros direccionales (Prewitt, Sobel, Scharr) con Filter2D, se explica el problema crítico del truncamiento en uint8, la solución con CV_64F + convertScaleAbs, la combinación de bordes horizontal y vertical, la umbralización (threshold) y el filtro omnidireccional de Canny con doble umbral. Segunda hora: corrección de la actividad grupal de OpenGL, recomendaciones de cara al examen y estructura del directorio de examen. El Tema 12 se elimina del temario (no entrará en el examen).


Conceptos Clave

  • Borde en imagen digital: cambio brusco de intensidad entre píxeles vecinos. Las librerías de visión siempre trabajan con escala de grises para detección de bordes.
  • Filtros direccionales: detectan bordes en una dirección (X = vertical, Y = horizontal). Prewitt, Sobel, Scharr son convolucionales.
  • Filtro de Sobel: el más usado. Kernel 3×3 con pesos que amplifican el gradiente. ⚠️ EXAMEN
  • Problema del truncamiento: usar -1 (uint8) en Filter2D recorta valores negativos a 0 y valores >255 a 255, perdiendo bordes de claro→oscuro. ⚠️ EXAMEN
  • Solución correcta: usar cv2.CV_64F para trabajar en flotante + convertScaleAbs (valor absoluto + conversión a uint8).
  • Gradiente combinado: \(G = \sqrt{G_x^2 + G_y^2}\) combina bordes horizontales y verticales en una sola imagen.
  • Umbralización (cv2.threshold): convierte la imagen de bordes a binaria (negro/blanco). El umbral define qué bordes se conservan. ⚠️ EXAMEN
  • Filtro de Canny: omnidireccional, doble umbral (alto + bajo). Los bordes débiles entre ambos umbrales se aceptan solo si están conectados a un borde fuerte. ⚠️ EXAMEN
  • Recomendación umbrales Canny: umbral bajo = mitad o un tercio del umbral alto.
  • Tema 12 eliminado: no entra en el examen.

Desarrollo del Temario

1. Filtros direccionales — Prewitt, Scharr y Sobel

Los tres filtros usan filter2D con sus respectivos kernels 3×3.

Filtro Característica
Prewitt Pesos uniformes (±1)
Scharr Pesos mayores en el centro (±10/3)
Sobel Pesos intermedios (±1, ±2); el más equilibrado

Por qué usamos filter2D en vez de cv2.Sobel directamente: para controlar explícitamente el tipo de datos de salida y comprender qué ocurre por dentro.

2. El problema del truncamiento (el error más común)

# MAL: tipo uint8, se pierden bordes de claro→oscuro
resultado = cv2.filter2D(img_gray, -1, kernel_sobel_x)

# BIEN: trabajar en float, luego convertir
resultado_float = cv2.filter2D(img_gray, cv2.CV_64F, kernel_sobel_x)
resultado = cv2.convertScaleAbs(resultado_float)
  • Valor de píxel de borde oscuro→claro: +760 → truncado a 255
  • Valor de píxel de borde claro→oscuro: −760 → truncado a 0 (se pierde el borde) ✗
  • convertScaleAbs aplica |x| antes de convertir, recuperando los bordes perdidos.

Los valores tras Sobel no son intensidades sino gradientes (magnitud del cambio). Cuanto mayor, más pronunciado el borde.

3. Combinación de bordes X e Y

gx = cv2.filter2D(img, cv2.CV_64F, kernel_x)
gy = cv2.filter2D(img, cv2.CV_64F, kernel_y)
g  = np.sqrt(gx**2 + gy**2)       # o: np.abs(gx) + np.abs(gy)
resultado = cv2.convertScaleAbs(g)

4. Umbralización

_, result = cv2.threshold(img_bordes, umbral, 255, cv2.THRESH_BINARY)
  • Píxeles > umbral → 255 (blanco = borde)
  • Píxeles ≤ umbral → 0 (negro = no borde)
  • Subir el umbral = más estricto, menos ruido; bajarlo = más bordes débiles incluidos.
  • El siguiente paso en el pipeline (detección de contornos) necesita fondo negro + bordes blancos.

5. Filtro de Canny — doble umbral

result = cv2.Canny(img, threshold_low, threshold_high)

Motivación: con un único umbral, un borde real puede perderse si pasa localmente por debajo. Canny lo resuelve con dos umbrales:

Zona Valor Tratamiento
> threshold_high Borde fuerte Se acepta siempre
Entre ambos Borde débil Se acepta si conecta con borde fuerte
< threshold_low No borde Se descarta

Recomendación: threshold_low = threshold_high / 2 (o /3). Si hay mucho ruido → subir el umbral alto.


Revisión Actividad Grupal — OpenGL

Feedback general de la profesora:

  • Escalado incorrecto: el enunciado pedía no escalar los objetos, sino coordinarse para que todos los miembros usaran la misma escala de cubo unitario. El escalado posterior fue penalizado informativamente pero no en nota.
  • Proyección perspectiva: si la escena queda cortada hay que elevar más la cámara. La profesora lo puso como ejemplo en el enunciado deliberadamente.
  • Buenas prácticas vistas: reutilización de modelos (coche en 4 colores + semáforos animados), esferas con ecuación paramétrica, uso de módulos Python separados.
  • Para el examen: crear objetos con cubos unitarios en OpenGL en ~40 minutos. Hay que dominar la técnica, no solo conocerla.

Material permitido en el examen

  • Todos los cuadernos de clase subidos al aula.
  • Las actividades entregadas por el alumno.
  • No se pueden llevar plantillas propias preparadas fuera del aula.
  • Directorio examen/ en el aula: módulos OpenGL y OpenCV + imágenes de práctica.

Preguntas Tipo Examen

  1. ¿Qué ocurre si aplicamos Sobel con tipo uint8 (-1) en lugar de CV_64F? ¿Qué bordes se pierden y por qué?
  2. ¿Qué hace convertScaleAbs? ¿Por qué es necesaria tras trabajar en float?
  3. Explica la diferencia entre un filtro direccional y uno omnidireccional. Da un ejemplo de cada uno.
  4. ¿Qué son los bordes débiles en Canny y cuándo se aceptan?
  5. ¿Por qué la detección de contornos necesita como entrada una imagen binaria (solo negro y blanco)?
  6. Da la fórmula del gradiente combinado de Sobel.