Phase 04 - Lesson 27
Seguimiento de Multiples Objetos y Memoria de Video
This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.
El seguimiento es deteccion mas asociacion. Detecta cada fotograma. Empareja las detecciones de este fotograma con las pistas del fotograma anterior por ID.
Tipo: Build Lenguajes: Python Requisitos previos: Fase 4 Leccion 06 (Deteccion con YOLO), Fase 4 Leccion 08 (Mask R-CNN), Fase 4 Leccion 24 (SAM 3) Tiempo: ~60 minutos
Objetivos de Aprendizaje
- Distinguir el seguimiento por deteccion del seguimiento basado en consultas y nombrar las familias de algoritmos (SORT, DeepSORT, ByteTrack, BoT-SORT, rastreador por memoria SAM 2, SAM 3.1 Object Multiplex)
- Implementar la asignacion IoU + Hungaro desde cero para el clasico seguimiento por deteccion
- Explicar el banco de memoria de SAM 2 y por que maneja mejor la oclusion que la asociacion basada en IoU
- Leer las tres metricas de seguimiento (MOTA, IDF1, HOTA) y elegir cual importa para un caso de uso dado
El Problema
Un detector te dice donde estan los objetos en un solo fotograma. Un rastreador te dice que deteccion en el fotograma t es el mismo objeto que una deteccion en el fotograma t-1. Sin eso, no puedes contar objetos que cruzan una linea, seguir una pelota a traves de una oclusion ni saber que "el auto #4 lleva 8 segundos en el carril".
El seguimiento es esencial para todo producto orientado a video: analitica deportiva, vigilancia, conduccion autonoma, analisis de video medico, monitoreo de fauna, conteo de marcas. Los bloques de construccion centrales son compartidos: un detector por fotograma, un modelo de movimiento (filtro de Kalman o algo mas rico), un paso de asociacion (algoritmo Hungaro sobre IoU / coseno / atributos aprendidos) y un ciclo de vida de la pista (nacimiento, actualizacion, muerte).
2026 trajo dos nuevos patrones: seguimiento basado en memoria de SAM 2 (asociacion por memoria de atributos en lugar de modelo de movimiento) y SAM 3.1 Object Multiplex (memoria compartida para muchas instancias del mismo concepto). Esta leccion recorre primero la pila clasica y luego el enfoque basado en memoria.
El Concepto
Seguimiento por deteccion
flowchart LR
F1["Fotograma t"] --> DET["Detector"] --> D1["Detecciones en t"]
PREV["Pistas hasta t-1"] --> PREDICT["Prediccion de movimiento<br/>(Kalman)"]
PREDICT --> PRED["Pistas predichas en t"]
D1 --> ASSOC["Asignacion Hungara<br/>(IoU / coseno / movimiento)"]
PRED --> ASSOC
ASSOC --> UPDATE["Actualizar pistas emparejadas"]
ASSOC --> NEW["Crear nuevas pistas"]
ASSOC --> DEAD["Envejecer pistas sin emparejar; eliminar tras N"]
UPDATE --> NEXT["Pistas en t"]
NEW --> NEXT
DEAD --> NEXT
style DET fill:#dbeafe,stroke:#2563eb
style ASSOC fill:#fef3c7,stroke:#d97706
style NEXT fill:#dcfce7,stroke:#16a34a
Todo rastreador que encontraras en 2026 es una variacion de este bucle. Las diferencias:
- SORT (2016): filtro de Kalman + IoU Hungaro. Simple, rapido, sin modelo de apariencia.
- DeepSORT (2017): SORT + un atributo de apariencia basado en CNN por pista (embedding de ReID). Maneja mejor los cruces.
- ByteTrack (2021): asocia detecciones de baja confianza como una segunda etapa; no necesita atributos de apariencia, pero es el de mejor desempeno en MOT17.
- BoT-SORT (2022): Byte + compensacion de movimiento de camara + ReID.
- StrongSORT / OC-SORT — descendientes de ByteTrack con mejor movimiento y apariencia.
Filtro de Kalman en un parrafo
Un filtro de Kalman mantiene un estado por pista (x, y, w, h, dx, dy, dw, dh) con una covarianza. En cada fotograma, predice el estado usando un modelo de velocidad constante y luego actualiza con la deteccion emparejada. La actualizacion confia mas en la deteccion cuando la incertidumbre de la prediccion es alta. Esto da trayectorias suaves y la capacidad de continuar una pista a traves de una oclusion corta (1-5 fotogramas).
Todo rastreador clasico usa un filtro de Kalman en el paso de prediccion de movimiento.
El algoritmo Hungaro
Dada una matriz de costo M x N (pistas x detecciones), encuentra la asignacion uno-a-uno que minimiza el costo total. El costo suele ser 1 - IoU(track_bbox, detection_bbox) o la similitud de coseno negativa de atributos de apariencia. El tiempo de ejecucion es O((M+N)^3); para M, N de hasta ~1000 es suficientemente rapido en Python via scipy.optimize.linear_sum_assignment.
La idea clave de ByteTrack
Los rastreadores estandar descartan las detecciones de baja confianza (< 0.5). ByteTrack las mantiene como candidatas de segunda etapa: tras emparejar las pistas con las detecciones de alta confianza, las pistas sin emparejar intentan coincidir con detecciones de baja confianza usando un umbral de IoU un poco mas flexible. Recupera oclusiones cortas y cambios de ID cerca de multitudes.
Seguimiento basado en memoria de SAM 2
SAM 2 maneja video manteniendo un banco de memoria de atributos espacio-temporales por instancia. Dado un prompt (clic, caja, texto) en un fotograma, codifica la instancia en la memoria. En los fotogramas siguientes, la memoria recibe atencion cruzada contra los atributos del nuevo fotograma, y el decodificador produce una mascara para la misma instancia en el nuevo fotograma.
Sin filtro de Kalman, sin asignacion Hungara. La asociacion es implicita en la operacion de atencion a la memoria.
Pros:
- Robusto ante grandes oclusiones (la memoria lleva la identidad de la instancia a traves de muchos fotogramas).
- Vocabulario abierto cuando se combina con los prompts de texto de SAM 3.
- Funciona sin un modelo de movimiento separado.
Contras:
- Mas lento que ByteTrack para seguimiento de muchos objetos.
- El banco de memoria crece; limita la ventana de contexto.
SAM 3.1 Object Multiplex
El seguimiento previo de SAM 2 / SAM 3 mantiene un banco de memoria separado por instancia. Para 50 objetos, 50 bancos de memoria. Object Multiplex (marzo de 2026) los colapsa en una unica memoria compartida con tokens de consulta por instancia. El costo escala de forma sublineal en el numero de instancias.
Multiplex es el nuevo estandar para el seguimiento de multitudes en 2026: multitudes en conciertos, trabajadores de almacen, intersecciones de trafico.
Tres metricas que debes conocer
- MOTA (Multi-Object Tracking Accuracy) — 1 - (FN + FP + cambios de ID) / GT. Ponderada por tipo de error; una sola metrica que mezcla fallas de deteccion y de asociacion.
- IDF1 (ID F1) — media armonica entre precision y exhaustividad de ID. Se enfoca especificamente en que tan bien cada pista de referencia mantiene su ID a lo largo del tiempo. Mejor que MOTA para tareas sensibles a los cambios de ID.
- HOTA (Higher Order Tracking Accuracy) — se descompone en precision de deteccion (DetA) y precision de asociacion (AssA). El estandar de la comunidad desde 2020; el mas completo.
Para vigilancia (quien es quien): IDF1 es lo que reportas. Para analitica deportiva (contar pases): HOTA. Para comparacion academica general: HOTA.
Construyelo
Paso 1: Matriz de costo basada en IoU
import numpy as np
def bbox_iou(a, b):
"""
a, b: (N, 4) arrays of [x1, y1, x2, y2].
Returns (N_a, N_b) IoU matrix.
"""
ax1, ay1, ax2, ay2 = a[:, 0], a[:, 1], a[:, 2], a[:, 3]
bx1, by1, bx2, by2 = b[:, 0], b[:, 1], b[:, 2], b[:, 3]
inter_x1 = np.maximum(ax1[:, None], bx1[None, :])
inter_y1 = np.maximum(ay1[:, None], by1[None, :])
inter_x2 = np.minimum(ax2[:, None], bx2[None, :])
inter_y2 = np.minimum(ay2[:, None], by2[None, :])
inter = np.clip(inter_x2 - inter_x1, 0, None) * np.clip(inter_y2 - inter_y1, 0, None)
area_a = (ax2 - ax1) * (ay2 - ay1)
area_b = (bx2 - bx1) * (by2 - by1)
union = area_a[:, None] + area_b[None, :] - inter
return inter / np.clip(union, 1e-8, None)
Paso 2: Rastreador minimo al estilo SORT
El Kalman de velocidad constante fija se omite por brevedad — aqui usamos una asociacion simple por IoU; en produccion la prediccion de Kalman es esencial. El paquete Python sort proporciona la version completa.
from scipy.optimize import linear_sum_assignment
class Track:
def __init__(self, tid, bbox, frame):
self.id = tid
self.bbox = bbox
self.last_frame = frame
self.hits = 1
def update(self, bbox, frame):
self.bbox = bbox
self.last_frame = frame
self.hits += 1
class SimpleTracker:
def __init__(self, iou_threshold=0.3, max_age=5):
self.tracks = []
self.next_id = 1
self.iou_threshold = iou_threshold
self.max_age = max_age
def step(self, detections, frame):
if not self.tracks:
for d in detections:
self.tracks.append(Track(self.next_id, d, frame))
self.next_id += 1
return [(t.id, t.bbox) for t in self.tracks]
track_boxes = np.array([t.bbox for t in self.tracks])
det_boxes = np.array(detections) if len(detections) else np.empty((0, 4))
iou = bbox_iou(track_boxes, det_boxes) if len(det_boxes) else np.zeros((len(track_boxes), 0))
cost = 1 - iou
cost[iou < self.iou_threshold] = 1e6
matched_track = set()
matched_det = set()
if cost.size > 0:
row, col = linear_sum_assignment(cost)
for r, c in zip(row, col):
if cost[r, c] < 1.0:
self.tracks[r].update(det_boxes[c], frame)
matched_track.add(r); matched_det.add(c)
for i, d in enumerate(det_boxes):
if i not in matched_det:
self.tracks.append(Track(self.next_id, d, frame))
self.next_id += 1
self.tracks = [t for t in self.tracks if frame - t.last_frame <= self.max_age]
return [(t.id, t.bbox) for t in self.tracks]
60 lineas. Recibe detecciones por fotograma, devuelve IDs de pista por fotograma. Los sistemas reales agregan la prediccion de Kalman, la re-asociacion de segunda etapa de ByteTrack y atributos de apariencia.
Paso 3: Prueba de trayectoria sintetica
def synthetic_frames(num_frames=20, num_objects=3, H=240, W=320, seed=0):
rng = np.random.default_rng(seed)
starts = rng.uniform(20, 200, size=(num_objects, 2))
velocities = rng.uniform(-5, 5, size=(num_objects, 2))
frames = []
for f in range(num_frames):
dets = []
for i in range(num_objects):
cx, cy = starts[i] + f * velocities[i]
dets.append([cx - 10, cy - 10, cx + 10, cy + 10])
frames.append(dets)
return frames
tracker = SimpleTracker()
for f, dets in enumerate(synthetic_frames()):
tracks = tracker.step(dets, f)
Tres objetos que se mueven en lineas rectas deben conservar sus IDs a lo largo de los 20 fotogramas.
Paso 4: Metrica de cambio de ID
def count_id_switches(tracks_per_frame, gt_per_frame):
"""
tracks_per_frame: list of list of (track_id, bbox)
gt_per_frame: list of list of (gt_id, bbox)
Returns number of ID switches.
"""
prev_assignment = {}
switches = 0
for tracks, gts in zip(tracks_per_frame, gt_per_frame):
if not tracks or not gts:
continue
t_boxes = np.array([b for _, b in tracks])
g_boxes = np.array([b for _, b in gts])
iou = bbox_iou(g_boxes, t_boxes)
for g_idx, (gt_id, _) in enumerate(gts):
j = iou[g_idx].argmax()
if iou[g_idx, j] > 0.5:
t_id = tracks[j][0]
if gt_id in prev_assignment and prev_assignment[gt_id] != t_id:
switches += 1
prev_assignment[gt_id] = t_id
return switches
Esta es una metrica simplificada cercana al IDF1: cuenta cuantas veces un objeto de referencia cambia el ID de la pista predicha asignada a el. Las herramientas reales de MOTA / IDF1 / HOTA viven en py-motmetrics y TrackEval.
Usalo
Rastreadores de produccion en 2026:
ultralytics— YOLOv8 + ByteTrack / BoT-SORT integrados.results = model.track(source, tracker="bytetrack.yaml"). El estandar.supervision(Roboflow) — wrappers de ByteTrack mas utilidades de anotacion.- SAM 2 / SAM 3.1 — seguimiento basado en memoria via
processor.track(). - Pila personalizada: detector (YOLOv8 / RT-DETR) +
sort-tracker/OC-SORT/StrongSORT.
Eligiendo:
- Peatones / autos / cajas a 30+ fps: ByteTrack con ultralytics.
- Muchas instancias de una clase en una multitud: SAM 3.1 Object Multiplex.
- Oclusiones intensas con apariencia identificable: DeepSORT / StrongSORT (atributos de ReID).
- Deportes / interacciones complejas: BoT-SORT o rastreadores aprendidos (MOTRv3).
Entregalo
Esta leccion produce:
outputs/prompt-tracker-picker.md— elige SORT / ByteTrack / BoT-SORT / SAM 2 / SAM 3.1 segun el tipo de escena, los patrones de oclusion y el presupuesto de latencia.outputs/skill-mot-evaluator.md— escribe un harness de evaluacion completo para MOTA / IDF1 / HOTA contra pistas de referencia.
Ejercicios
- (Facil) Ejecuta el rastreador sintetico anterior con 3, 10 y 30 objetos. Reporta el conteo de cambios de ID en cada caso. Identifica donde la asociacion simple solo por IoU comienza a fallar.
- (Medio) Agrega un paso de prediccion de Kalman de velocidad constante antes de la asociacion. Muestra que las oclusiones cortas (2-3 fotogramas) ya no causan cambios de ID.
- (Dificil) Integra el rastreador basado en memoria de SAM 2 (via
transformers) como un backend de seguimiento alternativo. Ejecuta tanto SimpleTracker como SAM 2 en un clip de 30 segundos de una multitud y compara los conteos de cambios de ID, etiquetando manualmente los IDs de referencia de 5 personas destacadas.
Terminos Clave
| Termino | Lo que la gente dice | Lo que realmente significa |
|---|---|---|
| Seguimiento por deteccion | "Detectar y luego asociar" | Detector por fotograma + asignacion Hungara sobre IoU / apariencia |
| Filtro de Kalman | "Prediccion de movimiento" | Dinamica lineal + covarianza para predicciones suaves de pista y manejo de oclusion |
| Algoritmo Hungaro | "Asignacion optima" | Resuelve el problema de emparejamiento bipartito de costo minimo; scipy.optimize.linear_sum_assignment |
| ByteTrack | "Segunda pasada de baja confianza" | Re-asocia pistas sin emparejar a detecciones de baja confianza para recuperar oclusiones cortas |
| DeepSORT | "SORT + apariencia" | Agrega un atributo de ReID para emparejamiento entre fotogramas; mejor para la preservacion de ID |
| Banco de memoria | "Truco de SAM 2" | Atributos espacio-temporales por instancia almacenados a lo largo de los fotogramas; la atencion cruzada reemplaza la asociacion explicita |
| Object Multiplex | "Memoria compartida de SAM 3.1" | Memoria unica compartida con consultas por instancia para seguimiento rapido de muchos objetos |
| HOTA | "Metrica moderna de seguimiento" | Se descompone en precision de deteccion y de asociacion; estandar de la comunidad |
Lectura Adicional
- SORT (Bewley et al., 2016) — el articulo minimo de seguimiento por deteccion
- DeepSORT (Wojke et al., 2017) — agrega atributo de apariencia
- ByteTrack (Zhang et al., 2022) — segunda pasada de baja confianza
- BoT-SORT (Aharon et al., 2022) — compensacion de movimiento de camara
- HOTA (Luiten et al., 2020) — metrica de seguimiento descompuesta
- Segmentacion de video SAM 2 (Meta, 2024) — rastreador basado en memoria
- SAM 3.1 Object Multiplex (Meta, marzo de 2026)