
Proyecto BermejaPi — Entrada 17: Misión de reconocimiento (Aislamiento de la ROI)
En la entrada anterior, los algoritmos estadísticos de BermejaPi detectaron una alerta crítica: el Sector [3, 7] presentaba una desviación de +8.41\sigma respecto a la media del planeta. Pasar volando a $7{,}66\text{ km/s}$ fotografiando todo el horizonte no nos permite entender qué ocurre en ese punto concreto.
En esta misión, BermejaPi activa el modo de Misión de Reconocimiento: un algoritmo de visión por computador que localiza dinámicamente las coordenadas de la anomalía, recorta la Región de Interés (ROI, Region of Interest) y extrae su perfil espectral a alta resolución optimizando el uso de memoria.
1. La Matemática del Recorte Dinámico (Bounding Box)
Para aislar una anomalía dentro de un lienzo global de píxeles sin saturar la memoria RAM ni el disco de la Raspberry Pi (cumpliendo estrictamente las cuotas de almacenamiento de la ESA), transformamos las coordenadas de la rejilla discreta (i, j) en una ventana de recorte \text{ROI} en píxeles:
x_{\text{min}} = j \cdot w_{\text{sector}}, \quad x_{\text{max}} = (j + 1) \cdot w_{\text{sector}} y_{\text{min}} = i \cdot h_{\text{sector}}, \quad y_{\text{max}} = (i + 1) \cdot h_{\text{sector}}Donde w_{\text{sector}} y h_{\text{sector}} son el ancho y el alto en píxeles de cada celda de la rejilla.
El submuestreo de la imagen recortada I_{\text{ROI}}(x, y) nos permite calcular los momentos estadísticos de canal (Azul, Verde, Infrarrojo/Rojo) exclusivamente sobre la zona sospechosa.
2. Código en Python: Extracción de la ROI y Firma Espectral
El siguiente script carga la captura global, calcula automáticamente la caja delimitadora de la anomalía en el Sector [3, 7], guarda la foto ampliada de reconocimiento (anomalia_sector_3_7.jpg) y analiza la firma de color de la región:
Python
import cv2
import numpy as np
from pathlib import Path
def aislar_y_analizar_roi(ruta_imagen="mapa_planetario.jpg", sector_anomalia=(3, 7), divisiones=(10, 10)):
"""Aísla la Región de Interés (ROI) de la anomalía y extrae su perfil multiespectral."""
ruta = Path(ruta_imagen)
# Si no existe el mapa previo, genera una imagen de prueba
if not ruta.exists():
mapa_dummy = np.random.randint(50, 180, (1080, 1920, 3), dtype=np.uint8)
# Inyectar anomalía rojiza/NIR en sector 3,7
mapa_dummy[324:432, 1344:1536] = (40, 60, 230)
cv2.imwrite(str(ruta), mapa_dummy)
img = cv2.imread(str(ruta))
h_total, w_total, _ = img.shape
# Dimensiones de cada celda de la rejilla
filas, columnas = divisiones
h_sec = h_total // filas
w_sec = w_total // columnas
# Calcular coordenadas en píxeles para el Sector [fila, columna]
r, c = sector_anomalia
y1, y2 = r * h_sec, (r + 1) * h_sec
x1, x2 = c * w_sec, (c + 1) * w_sec
# Recorte de la Región de Interés (ROI)
roi = img[y1:y2, x1:x2]
# Guardar la imagen de reconocimiento
nombre_roi = f"anomalia_sector_{r}_{c}.jpg"
cv2.imwrite(nombre_roi, roi)
# Análisis espectral por canales en la ROI (Formato BGR en OpenCV)
media_b = float(np.mean(roi[:, :, 0])) # Canal Azul
media_g = float(np.mean(roi[:, :, 1])) # Canal Verde
media_r = float(np.mean(roi[:, :, 2])) # Canal NIR (Infrarrojo en Cámara NoIR)
# Relación Infrarrojo/Azul específica de la anomalía
ratio_nir_blue = media_r / (media_b + 1e-6)
print(f"🎯 ROI aislada con éxito: {roi.shape[1]}x{roi.shape[0]} px -> '{nombre_roi}'")
print(f"📊 Firma de Color ROI -> Azul: {round(media_b, 1)} | Verde: {round(media_g, 1)} | NIR (Rojo): {round(media_r, 1)}")
print(f"⚡ Ratio de Firma Infrarroja (NIR/Blue): {round(ratio_nir_blue, 2)}")
return nombre_roi, ratio_nir_blue
# Ejecutar la Misión de Reconocimiento
archivo_roi, ratio = aislar_y_analizar_roi()
Resultados de la Misión: Inspección Cercana del Sector [3, 7]
Al aislar el sector anómalo y analizar sus componentes espectrales:
Plaintext
RECONNAISSANCE MODE — TARGET LOCATED
┌───────────────────────────────────────┐
│ ANOMALY BOUNDING BOX [SECTOR 3,7] │
│ Coordenadas Píxel: X[1344:1536] │
│ Y[324:432] │
│ ─────────────────────────────────── │
│ Respuesta NIR / Azul = 2.84 │
│ Estructura: Concentrada / No Nubes │
└───────────────────────────────────────┘
- Aislamiento Eficiente: BermejaPi generó el archivo de alta resolución
anomalia_sector_3_7.jpgutilizando apenas 18 KB de disco, optimizando al máximo la cuota exigida por la ESA. - Perfil no Identificado: El ratio Infrarrojo/Azul (\text{NIR}/\text{Blue} = 2.84) es notablemente superior al de la vegetación estándar del planeta, descartando que se trate de simple nubosidad o agua de océano.


Reto para la clase: ¿Qué es la anomalía? (Machine Learning)
Ya tenemos aislada la región y extraídos sus datos espectrales. Sin embargo, un algoritmo condicional estándar (if/else) no puede clasificar un patrón que nunca ha sido programado de forma explícita.
En la Entrada 18 implementaremos BermejaPi 2.0, entrenando un modelo de Machine Learning en Scikit-Learn con categorías de superficie para determinar si la anomalía es una formación biológica masiva o una Superficie Desconocida.



