Rastreia objetos com ByteTrack, BoT-SORT e TrackTrack
Atribui a cada objeto um ID estável entre fotogramas, escolhe um rastreador e ajusta os respetivos limites ao teu detetor.
Cada contagem, tempo de permanência e estimativa de velocidade depende de uma pergunta: o ID 17 neste fotograma é o mesmo objeto que o ID 17 no anterior? No mesmo clipe de 62 fotogramas, com cerca de 30 peões e o mesmo detetor, o ByteTrack encontrou 32 IDs e o TrackTrack predefinido encontrou 9. Escolher e ajustar o rastreador faz parte do modelo, e uma escolha errada compromete silenciosamente todos os números seguintes.
Prático
O que um rastreador acrescenta#

As etiquetas e os rastos neste fotograma são o resultado do rastreador. Cada ID é um rasto, uma hipótese sobre um objeto real, e o rasto visual mostra o histórico recente desse objeto. Em cada fotograma, o rastreador prevê onde cada rasto deverá estar usando um filtro de Kalman e, em seguida, associa as novas deteções a essas previsões com base na sobreposição das caixas (e, opcionalmente, na aparência):
Os rastos que não correspondem a uma deteção durante track_buffer fotogramas são eliminados. Esse é todo o mecanismo, e os respetivos limites determinam tudo o que se segue.
Escolhe um rastreador#
O modo de rastreamento inclui seis rastreadores: BoT-SORT, ByteTrack, OC-SORT, Deep OC-SORT, FastTracker e TrackTrack (o predefinido quando não passas tracker). Três cobrem a maioria das necessidades:
| Rastreador | Critérios de associação (configuração predefinida) | Usa-o quando |
|---|---|---|
| ByteTrack | Movimento e sobreposição das caixas, além de uma segunda passagem que recupera caixas com baixa confiança | Câmaras fixas, detetores pequenos ou incertos, quando o débito é importante |
| BoT-SORT | Associação ao estilo ByteTrack, mais compensação do movimento da câmara (gmc_method: sparseOptFlow) | Câmaras em movimento ou com trepidação; define with_reid: True para multidões e oclusões prolongadas |
| TrackTrack | Custo com vários critérios (forma da caixa, confiança, ângulo), inicialização consciente dos rastos e compensação do movimento da câmara | Deteções confiantes de um modelo robusto e bem treinado |
A associação por aparência do BoT-SORT (reidentificação) está desativada por predefinição; com model: auto, reutiliza as próprias características do detetor, pelo que ativá-la acrescenta pouca sobrecarga.
Compara rastreadores num único clipe#
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 IDsO clipe mostra cerca de 30 pessoas a atravessar uma praça, filmada de cima, por isso o ByteTrack e o BoT-SORT aproximam-se da contagem real. O TrackTrack não está avariado: só inicia um rasto acima de new_track_thresh: 0.7 e só faz a primeira associação acima de track_high_thresh: 0.6, enquanto a confiança mediana de YOLO26n para pessoas neste clipe é 0,53 e apenas 17% das deteções chegam a 0,7. A maioria das pessoas nunca recebe um ID. Regra de decisão: compara os limites do rastreador com a distribuição de confiança do teu detetor antes de confiares em qualquer contagem.
Usa IDs num 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 objectsUm conjunto por classe conta cada ID uma única vez, independentemente do número de fotogramas em que aparece, pelo que o resultado corresponde a objetos e não a deteções. stream=True produz resultados fotograma a fotograma, em vez de manter o vídeo inteiro na memória. Contagem por região, mapas de calor e cálculo de distâncias são camadas simples sobre este ciclo.
Rastreia os fotogramas que lês diretamente#
Ao passar um caminho ou URL, o estado do rastreador é gerido automaticamente. Quando os fotogramas vêm do teu próprio descodificador, de uma webcam ou de uma câmara RTSP, passa persist=True para que cada chamada continue a mesma sequência (a documentação do modo de previsão enumera todos os tipos de origem):
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 IDsO resultado é o mesmo que ao passar o caminho, porque persist=True manteve um rastreador ativo ao longo das 62 chamadas.
Ajusta um rastreador ao teu detetor#
Copia uma configuração incluída no pacote, reduz os limites incompatíveis com o teu detetor e passa o novo ficheiro:
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 IDsDefinir os limites ligeiramente abaixo da confiança mediana do detetor fez o TrackTrack passar de 9 para 27 IDs, aproximando-se da contagem manual. Os parâmetros comuns a todos os rastreadores:
| Parâmetro | Predefinição do ByteTrack | Aumenta para | Reduz para |
|---|---|---|---|
track_high_thresh | 0.25 | Ignorar deteções fracas na primeira associação | Rastrear com um detetor de baixa confiança |
new_track_thresh | 0.25 | Eliminar rastos falsos de curta duração | Iniciar rastos para objetos pouco visíveis |
track_buffer | 30 fotogramas | Manter os rastos durante oclusões prolongadas | Libertar mais cedo os IDs de objetos que saíram de cena |
match_thresh | 0.8 | Fazer associações mais permissivas (movimento rápido, caixas instáveis) | Fazer associações mais rigorosas (multidões) |
Armadilhas#
Sintoma: muito menos IDs do que objetos, principalmente com um modelo pequeno. Solução: mostre a confiança mediana das detecções (veja a solução) e defina new_track_thresh e track_high_thresh logo abaixo dela.
Sintoma: os IDs são redefinidos a cada quadro quando você chama model.track(frame) por conta própria, ou alternam entre objetos quando duas câmeras alimentam um modelo. Solução: passe persist=True para quadros consecutivos de um único fluxo e atribua uma instância própria do modelo a cada câmera (ou use uma origem .streams, que mantém um rastreador por fluxo).
Sintoma: o ID de um carro estacionado muda a cada poucos quadros. Solução: o rastreador está iniciando novas trajetórias em vez de corresponder às antigas. Primeiro, verifique a confiança do detector nesse objeto e, em seguida, aumente match_thresh e track_buffer.
Mostrar solução
Mede primeiro as confianças do detetor e, em seguida, define os limites ligeiramente abaixo delas:
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 o clipe da praça, a tabela indica: ByteTrack 32, BoT-SORT 33, TrackTrack 9, TrackTrack ajustado 27, contagem manual de cerca de 30. Apenas 17% das deteções ultrapassam new_track_thresh: 0.7 do TrackTrack, por isso reduzi-lo para 0,35 (com track_high_thresh: 0.3) é a solução certa; para esta câmara fixa, montada por cima, o ByteTrack simples é a opção mais direta.