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) enFilter2Drecorta valores negativos a 0 y valores >255 a 255, perdiendo bordes de claro→oscuro. ⚠️ EXAMEN - Solución correcta: usar
cv2.CV_64Fpara 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 a255✓ - Valor de píxel de borde claro→oscuro:
−760→ truncado a0(se pierde el borde) ✗ convertScaleAbsaplica|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
- ¿Qué ocurre si aplicamos Sobel con tipo uint8 (
-1) en lugar deCV_64F? ¿Qué bordes se pierden y por qué? - ¿Qué hace
convertScaleAbs? ¿Por qué es necesaria tras trabajar en float? - Explica la diferencia entre un filtro direccional y uno omnidireccional. Da un ejemplo de cada uno.
- ¿Qué son los bordes débiles en Canny y cuándo se aceptan?
- ¿Por qué la detección de contornos necesita como entrada una imagen binaria (solo negro y blanco)?
- Da la fórmula del gradiente combinado de Sobel.