Clase 16 — Jerarquía de Contornos, ROIs y Corrección del Laboratorio (última clase)
Resumen Ejecutivo
Última sesión del curso. Tres partes:
- Cierre del Tema 11: jerarquía de contornos (estructura del vector de 4 posiciones que devuelve
findContours), y un repaso rápido de funciones de regiones de interés (ROIs): rectángulos envolventes (boundingRect,minAreaRect), círculo/elipse envolvente y casco convexo (convexHull). Nada de esto entra en el examen. - Corrección del laboratorio 2 (3 ejercicios): detección de rejilla, dirección predominante de rectas (con ecualización), y filtrado por área de contornos.
- Recomendaciones para el examen: estructura (7 pts OpenGL 3D + 3 pts OpenCV), gestión del tiempo y qué se valora.
⚠️ EXAMEN: el examen tiene un ejercicio de OpenGL (3D, 7 pts) y un ejercicio de OpenCV (3 pts), en 90 minutos. No entra OpenGL 2D. La métrica de los ejercicios de visión vendrá dada en el enunciado (no hay que diseñarla).
Conceptos Clave
- Jerarquía de contornos: para cada contorno, un vector
[siguiente, anterior, primer_hijo, padre](índices;-1= no existe). Se rellena solo conRETR_TREE. ⚠️ RETR_EXTERNAL/RETR_LIST/RETR_TREE: sin jerarquía interna / lista plana sin relaciones / jerarquía completa. ConLISTlos campos de padre/hijo van a-1(no se pidió jerarquía). ⚠️ EXAMEN- ROIs:
boundingRect(rectángulo recto),minAreaRect(rectángulo de área mínima, rotado), círculo/elipse envolvente,convexHull(casco convexo, ignora concavidades). - Métrica en visión clásica: extraer características (nº de rectas, áreas…) y definir un criterio para decidir automáticamente. Diseñar la métrica es lo difícil; en el examen viene dada. ⚠️ EXAMEN
- Convenio de ángulos en Hough: θ ∈ [0,180]; si la recta supera 180°, OpenCV devuelve ρ negativo. Se "deshace" negando ρ y sumando 180° a θ. ⚠️
equalizeHistvs CLAHE: ecualización global vs. ecualización adaptativa por regiones/canales.
Desarrollo del Temario
1. Jerarquía de contornos
findContours devuelve contours (lista) y hierarchy (otra lista, indexada igual). Para cada contorno, hierarchy[i] es un vector de 4 posiciones:
| Posición | Significado |
|---|---|
| 0 | Siguiente contorno al mismo nivel (hermano) |
| 1 | Anterior contorno al mismo nivel (hermano) |
| 2 | Primer hijo (contorno interior) |
| 3 | Padre (contorno exterior que lo rodea) |
-1 significa "no existe". La relación jerárquica es siempre por contenido geométrico (qué contorno está dentro de cuál).
Ejemplo (2 nueves rodeados por una elipse):
- RETR_EXTERNAL → solo el contorno externo (0); todo el resto a -1.
- RETR_LIST → los 6 contornos (interior+exterior de cada 9 y de la elipse), pero columnas de padre/hijo a -1 (no se pidió jerarquía).
- RETR_TREE → los 6 contornos con jerarquía completa (hermanos + padre/hijo).
No entra en examen; se explica por compromiso con la clase.
2. Regiones de interés (ROIs) — solo mención
boundingRect(verde): rectángulo recto (lados horizontales/verticales). Permite ver si una figura es más ancha que alta.minAreaRect(rojo): rectángulo de área mínima ajustado, puede estar rotado. Permite estimar área aproximada y morfología.- Círculo / elipse envolvente.
convexHull: casco convexo que rodea el objeto ignorando concavidades (ej.: una hoja dentada).
3. Corrección del laboratorio — Ejercicio 1: detección de rejilla
Pipeline: leer → copia → gris → (suavizado opcional) → Canny → HoughLines → clean_lines (tolerancia) → clasificar en horizontales/verticales con is_horizontal/is_vertical.
Funciones auxiliares del alumno:
- is_horizontal(recta, tol): una recta es horizontal si θ ≈ 90° (recordar: θ es el ángulo del segmento perpendicular a la recta).
- is_vertical(recta, tol): θ ≈ 0°/180°.
Métrica (deliberadamente simple): "hay rejilla si detecto ≥3 horizontales y ≥3 verticales". Reconoce que es pobre (no comprueba que se crucen ni que estén en la misma zona), pero diseñar una métrica robusta era demasiado complejo y no era el objetivo.
Parámetros a "probar, no calcular": - Umbrales de Canny (regla grosso modo: inferior ≈ mitad o tercio del superior). - Umbral de Hough = nº de votaciones (subir = más exigente; bajar = aparecen rectas falsas). - Tolerancia de limpieza en ρ → depende del tamaño de la imagen (15 px es poco en una imagen 2000×2000). - Tolerancia angular en grados → no depende del tamaño.
putText(img, texto, origen, fuente, escala, color, grosor, tipo_linea) para sobreimprimir el resultado en la imagen.
4. Corrección — Ejercicio 2: dirección predominante (con ecualización)
Imagen muy oscura → ecualizar (equalizeHist) tras pasar a gris y suavizar; se trabaja sobre la imagen ecualizada (copiarla y, si se quiere pintar en colores, convertirla a color con cvtColor).
Clasificar en 3 listas: horizontales, verticales, others (inclinadas). Pintar cada grupo de un color (azul/rojo/verde).
Métrica de predominancia: un grupo predomina si supera un porcentaje del total (p.ej. 40%). Cualquier métrica razonable vale — incluso comparar valores absolutos (el grupo con más rectas), como hizo un alumno.
Sobre la ecualización en color: se ecualiza por componentes y se fusiona;
splitsepara los 3 canales. CLAHE (createCLAHE) permite ecualización adaptativa por regiones y mezclar capas con distintos pesos. Para detectar colores no se usa ecualización: hay que cambiar a otro espacio de color (no RGB) y usar funciones de máscara por rango de color. Reflexión: el algoritmo asigna a cada píxel un color absoluto; la percepción humana es relativa (ejemplo del "vestido" y de la recta que "vemos" donde esperamos pero no existe).
5. Corrección — Ejercicio 3: filtrado por área (muy fácil)
Enunciado: una carta (de rombos) tiene buen diseño si el contorno más grande ocupa ≈7% ± 1% del área total de la imagen.
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# imagen sintética → sin suavizado
edges = cv2.Canny(img_gray, t1, t2)
contornos, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
areas = [cv2.contourArea(c) for c in contornos]
area_max = np.max(areas)
area_total = img.shape[0] * img.shape[1]
porc = area_max / area_total * 100
diseño_ok = 6 <= porc <= 8 # 7% ± 1%
Se usa RETR_EXTERNAL porque solo interesan los objetos externos (no los huecos internos del "as"). Resultado de la profesora: 7,03%; a los alumnos que aplicaron suavizado les dio ligeramente distinto (6,99%, 6,92%).
Si el enunciado necesita una función de NumPy (
np.max…), viene indicada en el enunciado — no se exige dominar NumPy.
Recomendaciones para el Examen ⚠️ EXAMEN
- Estructura: OpenGL 3D (7 pts) + OpenCV (3 pts), 90 min. No hay OpenGL 2D.
- OpenGL: construir el objeto por partes y luego componerlas; sacar las vistas (las vistas ya están hechas en la actividad). En perspectiva, subir la cámara para que se vea bien. La puntuación es parcial por partes del objeto (p.ej. los 3 ortoedros + las letras del "TNT" reparten puntos).
- OpenCV: ejercicios "muy fáciles", variantes de los 3 del laboratorio. No volverse loco ajustando parámetros; basta demostrar que se maneja el pipeline.
- Gestión del tiempo: cronometrarse con los ejercicios colgados de práctica. Afianzar primero aquello en lo que te sientas más seguro. Recomendación de la profesora: asegurar primero OpenGL (resultado más binario: si haces el objeto, está bien) y luego OpenCV.
- Entregable del laboratorio: solo el código Python; comentar el código es positivo. Se pueden llevar al examen los 3 ejercicios aunque solo se entregara uno.
Preguntas de Autoevaluación
- ¿Qué representa cada una de las 4 posiciones del vector de jerarquía de un contorno? ¿Qué significa
-1? - ¿Por qué con
RETR_LISTlas columnas de padre/hijo aparecen a-1? - ¿Para qué sirve
convexHull? ¿En qué se diferencia deboundingRectyminAreaRect? - En Hough, ¿por qué una recta horizontal tiene θ ≈ 90°? ¿Cómo se "deshace" el convenio del ρ negativo?
- ¿Por qué la tolerancia de limpieza en ρ depende del tamaño de la imagen y la angular no?
- Diseña una métrica simple para decidir si hay una rejilla. ¿Qué limitaciones tiene?
- ¿Cómo se ecualiza una imagen en color? ¿Qué aporta CLAHE frente a
equalizeHist? - ¿Por qué para detectar colores hay que cambiar de espacio de color en vez de ecualizar?
- Resuelve el ejercicio 3: ¿cómo decides si el contorno mayor ocupa el 7% ± 1% de la imagen?
- En el ejercicio de OpenGL del examen, ¿cómo se reparte la puntuación de un objeto compuesto?