PDI+VC · Ejercicio de Programación

EP07_05 — 🔴 Histograma de Orientaciones de una Célula HOG

7.18.5 EP07_05 🔴 Histograma de Orientaciones de una Célula HOG

La función hog de scikit-image, empleada en el proyecto de clasificación de dígitos, divide la imagen en pequeñas células y, para cada una, construye un histograma de las orientaciones del gradiente ponderado por la magnitud — exactamente la etapa central descrita en la sección sobre el descriptor HOG del capítulo.

Se te ha encargado implementar este cálculo para una única célula, a partir de los valores de magnitud y orientación del gradiente ya calculados para cada píxel de la célula (prescindiendo del cálculo de las derivadas parciales).

7.18.5.1 📋 Directrices de Implementación

  1. Dimensiones: Leer los enteros \(n\) (la célula tiene \(n \times n\) píxeles) y \(B\) (número de compartimentos del histograma).
  2. Magnitudes: Leer \(n\) líneas con \(n\) valores reales cada una, que representan \(|\nabla f(x,y)|\) para cada píxel de la célula.
  3. Orientaciones: Leer otras \(n\) líneas con \(n\) valores reales cada una, que representan \(\theta(x,y)\) en grados, ya convertidos al intervalo no signado \([0^\circ, 180^\circ)\), como se utiliza convencionalmente en HOG.
  4. Compartimentos: Los \(B\) compartimentos cubren \([0^\circ, 180^\circ)\) en bandas iguales de ancho \(180/B\) grados. Un píxel con orientación \(\theta\) pertenece al compartimento \(\lfloor \theta / (180/B) \rfloor\); si ese índice es igual a \(B\) (posible solo cuando \(\theta\) es exactamente \(180^\circ\), lo cual no debería ocurrir según la directriz del punto 3), utiliza el compartimento \(B-1\).
  5. Histograma bruto: Para cada píxel, acumula su magnitud (no su conteo) en el compartimento correspondiente: \[ H[b] = \sum_{(x,y)\, :\, \text{bin}(\theta(x,y)) = b} |\nabla f(x,y)|. \]
  6. Normalización L2: Tras construir \(H\), normalízalo para obtener \(\hat H\): \[ \hat H[b] = \frac{H[b]}{\sqrt{\sum_{j=0}^{B-1} H[j]^2 + \epsilon}}, \qquad \epsilon = 10^{-6}. \]
  7. Salida: Imprimir el histograma bruto \(H\) (redondeado a 2 decimales) en una línea, seguido del histograma normalizado \(\hat H\) (redondeado a 4 decimales) en otra línea, ambos con los \(B\) valores separados por espacios, en el orden de los compartimentos.

7.18.5.2 📌 Restricciones Computacionales

  • Binning no signado: el intervalo de orientaciones es \([0,180)\), no \([0,360)\) — los gradientes en direcciones opuestas (diferencia de \(180^\circ\)) contribuyen al mismo compartimento, convención estándar de HOG para detección de objetos.
  • Acumulación por magnitud, no por conteo: el histograma pondera cada píxel por su magnitud de gradiente, no simplemente cuenta cuántos píxeles caen en cada compartimento.
  • Constante de estabilización: el \(\epsilon = 10^{-6}\) en el denominador de la normalización evita la división por cero cuando la célula es completamente homogénea (todas las magnitudes nulas).

7.18.5.3 📐 De dónde provienen las matrices de entrada

Antes de este EP, cada píxel \((x,y)\) de la imagen pasa por:

\[ G_x = f(x+1,y)-f(x-1,y), \qquad G_y = f(x,y+1)-f(x,y-1) \]

\[ |\nabla f| = \sqrt{G_x^2+G_y^2}, \qquad \theta_{\text{signado}} = \operatorname{atan2}(G_y,G_x) \]

Como HOG ignora la polaridad del contraste, el ángulo se pliega al intervalo no signado:

\[ \theta = \theta_{\text{signado}} \bmod 180° \]

Repitiendo esto para todos los píxeles de una célula \(n\times n\), se obtienen las dos matrices de entrada de este ejercicio: magnitudes \(|\nabla f|\) y orientaciones \(\theta \in [0°,180°)\).

7.18.5.4 🧠 Fundamentación Teórica

Etapa Papel
Magnitud del gradiente Pondera la contribución de cada píxel — los bordes fuertes pesan más que el ruido débil
Orientación no signada Hace que el descriptor sea invariante a la polaridad del contraste (claro→oscuro vs. oscuro→claro)
Histograma por célula Resume la distribución local de bordes en un vector compacto
Normalización L2 Reduce la sensibilidad del descriptor a variaciones globales de iluminación y contraste

La concatenación de los histogramas normalizados de todas las células de la imagen — no implementada en este ejercicio — forma el vector de características HOG completo, utilizado como entrada del clasificador k-NN en el proyecto del capítulo.

7.18.5.5 📦 Especificación de Entrada y Salida (VPL)

Entrada:

  • Línea 1: Enteros \(n\) y \(B\).
  • Siguientes \(n\) líneas: \(n\) magnitudes reales cada una.
  • Siguientes \(n\) líneas: \(n\) orientaciones reales (grados, \([0,180)\)) cada una.

Salida:

  • Línea 1: los \(B\) valores del histograma bruto, redondeados a 2 decimales.
  • Línea 2: los \(B\) valores del histograma normalizado, redondeados a 4 decimales.

7.18.5.6 📌 Ejemplos

Entrada Salida Observación
2 2
1.0 2.0
3.0 4.0
10 100
170 20
5.00 5.00
0.7071 0.7071
Bin de ancho 90°: \([0,90)\) y \([90,180)\); magnitudes 1 y 4 caen en el bin 0, 2 y 3 en el bin 1.
2 4
0.0 0.0
0.0 0.0
0 0
0 0
0.00 0.00 0.00 0.00
0.0000 0.0000 0.0000 0.0000
Célula homogénea: \(\epsilon\) evita división por cero.
🎮 Simulador EP07_05: Histograma de Orientaciones de una Célula 🔴 célula 3×3 fija

Ajusta B y observa cómo la matriz de orientaciones (independiente de la de magnitudes) se mapea a los compartimentos mediante bin = floor(θ / (180/B)), y cómo las magnitudes se suman en cada bin.

2
📄 Entrada (exactamente como el programa lee por stdin)

🔢 Matriz de magnitudes |∇f|
📐 Matriz de orientaciones θ (grados) — coloreada por el bin
📏 Dónde cae cada θ en la regla [0°, 180°) — bin = floor(θ / ancho)
📊 Rango de cada compartimento (ancho = 180° / B)
🧩 Cada píxel: magnitud + orientación → bin
🧮 Cálculo paso a paso (floor de la división + suma de magnitudes por bin)
Figura 7.25: Simulador EP07_05: Histograma HOG de una Celda (mapeo de ángulos a bins)
%%writefile EP07_05.py
# Código Python
Overwriting EP07_05.py
TestSuite("EP07_05.py").run()
✔️ EP07_05.cases ya existe en casos/
📋 5 caso(s) cargado(s) de casos/EP07_05.cases

🔍 Probando Python: EP07_05.py
⚠️ EP07_05.py: archivo vacío (menos de 3 líneas). Pruebas omitidas.