Ultralytics YOLO27: Únete a la lista de espera.
Get Started
Ultralytics YOLO en producción·Seguimiento y creación de pipelines·Lección 5/9
Lecciónintermedio

Sigue objetos con ByteTrack, BoT-SORT y TrackTrack

Asigna a cada objeto un ID estable entre fotogramas, elige un rastreador y ajusta sus umbrales a tu detector.

Cada recuento, tiempo de permanencia y estimación de velocidad depende de una pregunta: ¿el ID 17 de este fotograma corresponde al mismo objeto que el ID 17 del anterior? En el mismo clip de 62 fotogramas, con unos 30 peatones y el mismo detector, ByteTrack encontró 32 ID y TrackTrack con la configuración predeterminada encontró 9. Elegir y ajustar el rastreador forma parte del modelo, y equivocarse rompe silenciosamente todos los valores posteriores.

Resultado

Ejecuta tres rastreadores en un vídeo, explica sus recuentos de ID a partir de las confianzas del detector, usa box.id en un pipeline y corrige un rastreador que no encaja con un YAML ajustado.

Vía rápida
Si ya tienes experiencia, aquí tienes un resumen.
  1. model.track(source, stream=True, tracker="bytetrack.yaml"); cada caja obtiene un id estable.

  2. ¿Alimentas tú mismo los fotogramas? Pasa persist=True para que los ID se mantengan entre llamadas; usa un rastreador por cámara.

  3. El valor predeterminado es tracktrack.yaml; ByteTrack es rápido y tolerante; BoT-SORT añade compensación del movimiento de cámara, con ReID opcional.

  4. ¿Hay muy pocos ID? Mide las confianzas del detector y reduce new_track_thresh y track_high_thresh en una copia del YAML.

Práctico

Qué aporta un rastreador#

Seguimiento del tráfico en una autopista con Ultralytics YOLO: cada coche, autobús y camión conserva un ID numerado (coche #12, autobús #21, camión #16) y deja una línea de color a lo largo de su trayectoria reciente

Las etiquetas y los rastros de este fotograma son el resultado del rastreador. Cada ID es una trayectoria, una hipótesis sobre un objeto real, y el rastro es su historial reciente. En cada fotograma, el rastreador predice dónde debería estar cada trayectoria con un filtro de Kalman y, a continuación, empareja las nuevas detecciones con esas predicciones según el solapamiento de las cajas (y, opcionalmente, la apariencia):

Las trayectorias que no se emparejan durante track_buffer fotogramas se eliminan. Ese es todo el mecanismo, y sus umbrales determinan todo lo que viene después.

Elige un rastreador#

El modo de seguimiento incluye seis rastreadores: BoT-SORT, ByteTrack, OC-SORT, Deep OC-SORT, FastTracker y TrackTrack (el predeterminado si no pasas tracker). Tres cubren la mayoría de las necesidades:

RastreadorCriterios de emparejamiento (configuración predeterminada)Cuándo elegirlo
ByteTrackMovimiento y solapamiento de cajas, más una segunda pasada que recupera las cajas con baja confianzaCámaras fijas, detectores pequeños o poco fiables; cuando importa el rendimiento
BoT-SORTEmparejamiento al estilo ByteTrack más compensación del movimiento de cámara (gmc_method: sparseOptFlow)Cámaras en movimiento o con vibraciones; establece with_reid: True para multitudes y oclusiones prolongadas
TrackTrackCoste basado en varios criterios (forma de la caja, confianza, ángulo), inicialización consciente de las trayectorias y compensación del movimiento de cámaraDetecciones fiables de un modelo potente y bien entrenado

La coincidencia por apariencia de BoT-SORT (reidentificación) está desactivada de forma predeterminada; con model: auto reutiliza las propias características del detector, por lo que activarla apenas añade sobrecarga.

Compara rastreadores en un mismo clip#

from ultralytics import YOLO

model = YOLO("yolo26n.pt")
video = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"

for tracker in ("bytetrack.yaml", "botsort.yaml", "tracktrack.yaml"):
    ids, frames = set(), 0
    for r in model.track(video, stream=True, tracker=tracker, classes=[0], verbose=False):
        frames += 1
        if r.boxes.is_track:
            ids.update(r.boxes.id.int().tolist())
    print(f"{tracker:16s} {frames} frames, {len(ids)} unique person IDs")
bytetrack.yaml   62 frames, 32 unique person IDs
botsort.yaml     62 frames, 33 unique person IDs
tracktrack.yaml  62 frames, 9 unique person IDs

En el clip se ven unas 30 personas cruzando una plaza desde arriba, por lo que ByteTrack y BoT-SORT se acercan al recuento real. TrackTrack no está roto: solo inicia una trayectoria por encima de new_track_thresh: 0.7 y la empareja por primera vez por encima de track_high_thresh: 0.6, mientras que la confianza mediana de YOLO26n para las personas en este clip es 0,53 y solo el 17 % de las detecciones llega a 0,7. La mayoría de las personas nunca obtiene un ID. Regla para decidir: compara los umbrales del rastreador con la distribución de confianza de tu detector antes de fiarte de ningún recuento.

Usa los ID en un pipeline#

from collections import defaultdict

from ultralytics import YOLO

model = YOLO("yolo26n.pt")
video = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"

counts = defaultdict(set)
for r in model.track(video, stream=True, tracker="bytetrack.yaml", verbose=False):
    if not r.boxes.is_track:  # no confirmed tracks in this frame yet
        continue
    for cls, tid in zip(r.boxes.cls.int().tolist(), r.boxes.id.int().tolist()):
        counts[r.names[cls]].add(tid)

for cls, ids in counts.items():
    print(f"{cls}: {len(ids)} unique objects")
person: 32 unique objects

Un conjunto por clase cuenta cada ID una sola vez, independientemente del número de fotogramas en los que aparezca, así que el resultado son objetos, no detecciones. stream=True genera resultados de uno en uno, por fotograma, en lugar de mantener todo el vídeo en memoria. El recuento por regiones, los mapas de calor y el cálculo de distancias son capas ligeras sobre este bucle.

Sigue los fotogramas que lees por tu cuenta#

Al pasar una ruta o una URL, el estado del rastreador se gestiona automáticamente. Si los fotogramas proceden de tu propio decodificador, una webcam o una cámara RTSP, pasa persist=True para que cada llamada continúe la misma secuencia (la documentación del modo Predict enumera todos los tipos de origen):

import cv2

from ultralytics import YOLO
from ultralytics.utils.downloads import safe_download

model = YOLO("yolo26n.pt")
video = safe_download("https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4")
cap = cv2.VideoCapture(str(video))  # or 0 for a webcam, or an rtsp:// URL

seen = set()
while True:
    ok, frame = cap.read()
    if not ok:
        break
    r = model.track(frame, persist=True, tracker="bytetrack.yaml", classes=[0], verbose=False)[0]
    if r.boxes.is_track:
        seen.update(r.boxes.id.int().tolist())  # your logic: dwell time, trajectories, alerts
cap.release()
print(f"{len(seen)} unique IDs")
32 unique IDs

La respuesta es la misma que al pasar la ruta, porque persist=True mantuvo activo un único rastreador durante las 62 llamadas.

Ajusta un rastreador a tu detector#

Copia una configuración incluida, reduce los umbrales que no se ajusten a tu detector y pasa el nuevo archivo:

from pathlib import Path

from ultralytics import YOLO
from ultralytics.utils import ROOT

cfg = (ROOT / "cfg/trackers/tracktrack.yaml").read_text()  # packaged defaults
for old, new in (
    ("track_high_thresh: 0.6", "track_high_thresh: 0.3"),
    ("track_low_thresh: 0.25", "track_low_thresh: 0.1"),
    ("new_track_thresh: 0.7", "new_track_thresh: 0.35"),
):
    cfg = cfg.replace(old, new)
Path("tracktrack_tuned.yaml").write_text(cfg)

model = YOLO("yolo26n.pt")
video = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"
for tracker in ("tracktrack.yaml", "tracktrack_tuned.yaml"):
    ids = set()
    for r in model.track(video, stream=True, tracker=tracker, classes=[0], verbose=False):
        if r.boxes.is_track:
            ids.update(r.boxes.id.int().tolist())
    print(f"{tracker:22s} {len(ids)} unique person IDs")
tracktrack.yaml        9 unique person IDs
tracktrack_tuned.yaml  27 unique person IDs

Al establecer los umbrales justo por debajo de la confianza mediana del detector, TrackTrack pasó de 9 a 27 ID, cerca del recuento manual. Los parámetros comunes a todos los rastreadores:

ParámetroValor predeterminado de ByteTrackAuméntalo paraRedúcelo para
track_high_thresh0.25Ignorar detecciones débiles en el primer emparejamientoSeguir con un detector de baja confianza
new_track_thresh0.25Eliminar trayectorias falsas de corta duraciónIniciar trayectorias para objetos poco visibles
track_buffer30 fotogramasMantenerse durante oclusiones más prolongadasLiberar antes los ID de los objetos que se han marchado
match_thresh0.8Emparejar con más flexibilidad (movimiento rápido, cajas inestables)Emparejar de forma más estricta (multitudes)

Errores habituales#

Umbrales por encima de la confianza del detector

Síntoma: hay muchos menos ID que objetos, especialmente con un modelo pequeño. Solución: muestra la confianza mediana de las detecciones (consulta la solución) y establece new_track_thresh y track_high_thresh justo por debajo de ese valor.

Olvidar persist o compartirlo

Síntoma: los ID se reinician en cada fotograma cuando llamas a model.track(frame) por tu cuenta, o saltan entre objetos cuando dos cámaras alimentan un mismo modelo. Solución: pasa persist=True para los fotogramas consecutivos de una misma secuencia y asigna a cada cámara su propia instancia del modelo (o usa una fuente .streams, que mantiene un rastreador por secuencia).

Parpadeo de ID en objetos lentos

Síntoma: el ID de un coche aparcado cambia cada pocos fotogramas. Solución: el rastreador está iniciando nuevas trayectorias en lugar de asociar las antiguas. Comprueba primero la confianza del detector para ese objeto y, después, aumenta match_thresh y track_buffer.

Pruébalo

Graba un vídeo de entre 30 y 60 segundos de tu propio entorno y cuenta a mano los objetos de una clase. Ejecuta compare_trackers.py sobre el vídeo, mide las confianzas del detector con el script de soluciones y ajusta un YAML del rastreador para que su recuento de ID se acerque al recuento manual. Entrega una tabla con el rastreador, los ID únicos y el recuento manual.

Cuándo se considera terminado
Has terminado la lección cuando se cumplen todas estas condiciones.
  • Tienes los recuentos de ID únicos de ByteTrack, BoT-SORT y TrackTrack en el mismo vídeo, junto al recuento manual.

  • Puedes explicar el recuento más bajo a partir de los umbrales del rastreador y de la confianza mediana del detector.

  • Un YAML del rastreador ajustado cambió el recuento en la dirección que habías predicho antes de ejecutarlo.

Mostrar solución

Mide primero las confianzas del detector y, después, establece los umbrales justo por debajo de ellas:

import torch

from ultralytics import YOLO

model = YOLO("yolo26n.pt")
video = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"
conf = torch.cat([r.boxes.conf for r in model.predict(video, stream=True, classes=[0], verbose=False)])
print(f"median {conf.median():.2f}, share >= 0.7: {(conf >= 0.7).float().mean():.0%}")
median 0.53, share >= 0.7: 17%

Para el clip de la plaza, la tabla indica: ByteTrack, 32; BoT-SORT, 33; TrackTrack, 9; TrackTrack ajustado, 27; recuento manual, unos 30. Solo el 17 % de las detecciones supera new_track_thresh: 0.7 de TrackTrack, así que reducirlo a 0,35 (con track_high_thresh: 0.3) es la solución adecuada; para esta cámara fija situada en alto, ByteTrack sin ajustes es la opción más sencilla.

Qué viene después

Con los ID estables, en la próxima lección crearás sobre ellos los pipelines de producción más habituales: recuentos, zonas, mapas de calor y velocidades.