Phase 04 - Lesson 27

Rastreamento de Multiplos Objetos e Memoria de Video

This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.

Rastreamento e deteccao mais associacao. Detecte cada quadro. Associe as deteccoes deste quadro as faixas do quadro anterior por ID.

Tipo: Build Linguagens: Python Pre-requisitos: Fase 4 Licao 06 (Deteccao com YOLO), Fase 4 Licao 08 (Mask R-CNN), Fase 4 Licao 24 (SAM 3) Tempo: ~60 minutos

Objetivos de Aprendizagem

  • Distinguir o rastreamento por deteccao do rastreamento baseado em consultas e nomear as familias de algoritmos (SORT, DeepSORT, ByteTrack, BoT-SORT, rastreador por memoria SAM 2, SAM 3.1 Object Multiplex)
  • Implementar a atribuicao IoU + Hungaro do zero para o classico rastreamento por deteccao
  • Explicar o banco de memoria do SAM 2 e por que ele lida melhor com oclusao do que a associacao baseada em IoU
  • Ler as tres metricas de rastreamento (MOTA, IDF1, HOTA) e escolher qual delas importa para um determinado caso de uso

O Problema

Um detector diz onde os objetos estao em um unico quadro. Um rastreador diz qual deteccao no quadro t e o mesmo objeto que uma deteccao no quadro t-1. Sem isso, voce nao consegue contar objetos cruzando uma linha, seguir uma bola atraves de uma oclusao ou saber que "o carro #4 esta na faixa ha 8 segundos".

O rastreamento e essencial para todo produto voltado a video: analise esportiva, vigilancia, direcao autonoma, analise de video medico, monitoramento de fauna, contagem de marcas. Os blocos de construcao centrais sao compartilhados: um detector por quadro, um modelo de movimento (filtro de Kalman ou algo mais sofisticado), uma etapa de associacao (algoritmo Hungaro sobre IoU / cosseno / atributos aprendidos) e um ciclo de vida da faixa (nascimento, atualizacao, morte).

2026 trouxe dois novos padroes: rastreamento baseado em memoria do SAM 2 (associacao por memoria de atributos em vez de modelo de movimento) e SAM 3.1 Object Multiplex (memoria compartilhada para muitas instancias do mesmo conceito). Esta licao percorre primeiro a pilha classica e depois a abordagem baseada em memoria.

O Conceito

Rastreamento por deteccao

flowchart LR
    F1["Quadro t"] --> DET["Detector"] --> D1["Deteccoes em t"]
    PREV["Faixas ate t-1"] --> PREDICT["Previsao de movimento<br/>(Kalman)"]
    PREDICT --> PRED["Faixas previstas em t"]
    D1 --> ASSOC["Atribuicao Hungara<br/>(IoU / cosseno / movimento)"]
    PRED --> ASSOC
    ASSOC --> UPDATE["Atualizar faixas associadas"]
    ASSOC --> NEW["Criar novas faixas"]
    ASSOC --> DEAD["Envelhecer faixas sem associacao; excluir apos N"]
    UPDATE --> NEXT["Faixas em 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 voce encontrara em 2026 e uma variacao desse loop. As diferencas:

  • SORT (2016): filtro de Kalman + IoU Hungaro. Simples, rapido, sem modelo de aparencia.
  • DeepSORT (2017): SORT + um atributo de aparencia baseado em CNN por faixa (embedding de ReID). Lida melhor com cruzamentos.
  • ByteTrack (2021): associa deteccoes de baixa confianca como uma segunda etapa; nao precisa de atributos de aparencia, mas e o melhor no MOT17.
  • BoT-SORT (2022): Byte + compensacao de movimento de camera + ReID.
  • StrongSORT / OC-SORT — descendentes do ByteTrack com melhor movimento e aparencia.

Filtro de Kalman em um paragrafo

Um filtro de Kalman mantem um estado por faixa (x, y, w, h, dx, dy, dw, dh) com uma covariancia. A cada quadro, preve o estado usando um modelo de velocidade constante e depois atualiza com a deteccao associada. A atualizacao confia mais na deteccao quando a incerteza da previsao e alta. Isso da trajetorias suaves e a capacidade de continuar uma faixa atraves de uma curta oclusao (1-5 quadros).

Todo rastreador classico usa um filtro de Kalman na etapa de previsao de movimento.

O algoritmo Hungaro

Dada uma matriz de custo M x N (faixas x deteccoes), encontre a atribuicao um-para-um que minimiza o custo total. O custo geralmente e 1 - IoU(track_bbox, detection_bbox) ou a similaridade de cosseno negativa de atributos de aparencia. O tempo de execucao e O((M+N)^3); para M, N de ate ~1000 e rapido o suficiente em Python via scipy.optimize.linear_sum_assignment.

A ideia central do ByteTrack

Rastreadores padrao descartam deteccoes de baixa confianca (< 0.5). O ByteTrack as mantem por perto como candidatas de segunda etapa: depois de associar as faixas as deteccoes de alta confianca, as faixas sem associacao tentam casar com deteccoes de baixa confianca usando um limiar de IoU um pouco mais frouxo. Recupera oclusoes curtas e trocas de ID perto de multidoes.

Rastreamento baseado em memoria do SAM 2

O SAM 2 lida com video mantendo um banco de memoria de atributos espaco-temporais por instancia. Dado um prompt (clique, caixa, texto) em um quadro, ele codifica a instancia na memoria. Nos quadros seguintes, a memoria recebe atencao cruzada contra os atributos do novo quadro, e o decodificador produz uma mascara para a mesma instancia no novo quadro.

Sem filtro de Kalman, sem atribuicao Hungara. A associacao e implicita na operacao de atencao a memoria.

Pros:

  • Robusto a grandes oclusoes (a memoria carrega a identidade da instancia atraves de muitos quadros).
  • Vocabulario aberto quando combinado com os prompts de texto do SAM 3.
  • Funciona sem um modelo de movimento separado.

Contras:

  • Mais lento que o ByteTrack para rastreamento de muitos objetos.
  • O banco de memoria cresce; limita a janela de contexto.

SAM 3.1 Object Multiplex

O rastreamento anterior do SAM 2 / SAM 3 mantem um banco de memoria separado por instancia. Para 50 objetos, 50 bancos de memoria. O Object Multiplex (marco de 2026) os colapsa em uma unica memoria compartilhada com tokens de consulta por instancia. O custo escala de forma sub-linear no numero de instancias.

O Multiplex e o novo padrao para rastreamento de multidoes em 2026: multidoes em shows, trabalhadores de armazem, cruzamentos de transito.

Tres metricas para conhecer

  • MOTA (Multi-Object Tracking Accuracy) — 1 - (FN + FP + trocas de ID) / GT. Ponderada por tipo de erro; uma metrica unica que mistura falhas de deteccao e de associacao.
  • IDF1 (ID F1) — media harmonica entre precisao e revocacao de ID. Foca especificamente em quao bem cada faixa de referencia mantem seu ID ao longo do tempo. Melhor que MOTA para tarefas sensiveis a troca de ID.
  • HOTA (Higher Order Tracking Accuracy) — decompoe em precisao de deteccao (DetA) e precisao de associacao (AssA). O padrao da comunidade desde 2020; o mais abrangente.

Para vigilancia (quem e quem): IDF1 e o que voce reporta. Para analise esportiva (contar passes): HOTA. Para comparacao academica geral: HOTA.

Construa

Passo 1: Matriz de custo baseada em 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)

Passo 2: Rastreador minimo no estilo SORT

O Kalman de velocidade constante fixa foi omitido por brevidade — usamos aqui uma associacao simples por IoU; em producao a previsao de Kalman e essencial. O pacote Python sort fornece a versao 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 linhas. Recebe deteccoes por quadro, retorna IDs de faixa por quadro. Sistemas reais adicionam a previsao de Kalman, a re-associacao de segunda etapa do ByteTrack e atributos de aparencia.

Passo 3: Teste de trajetoria 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 movendo-se em linha reta devem manter seus IDs ao longo dos 20 quadros.

Passo 4: Metrica de troca 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 e uma metrica simplificada proxima do IDF1: conta quantas vezes um objeto de referencia muda o ID da faixa prevista atribuida a ele. As ferramentas reais de MOTA / IDF1 / HOTA vivem em py-motmetrics e TrackEval.

Use

Rastreadores de producao em 2026:

  • ultralytics — YOLOv8 + ByteTrack / BoT-SORT integrados. results = model.track(source, tracker="bytetrack.yaml"). O padrao.
  • supervision (Roboflow) — wrappers de ByteTrack mais utilitarios de anotacao.
  • SAM 2 / SAM 3.1 — rastreamento baseado em memoria via processor.track().
  • Pilha personalizada: detector (YOLOv8 / RT-DETR) + sort-tracker / OC-SORT / StrongSORT.

Escolhendo:

  • Pedestres / carros / caixas a 30+ fps: ByteTrack com ultralytics.
  • Muitas instancias de uma classe em uma multidao: SAM 3.1 Object Multiplex.
  • Oclusoes pesadas com aparencia identificavel: DeepSORT / StrongSORT (atributos de ReID).
  • Esportes / interacoes complexas: BoT-SORT ou rastreadores aprendidos (MOTRv3).

Entregue

Esta licao produz:

  • outputs/prompt-tracker-picker.md — escolhe SORT / ByteTrack / BoT-SORT / SAM 2 / SAM 3.1 dado o tipo de cena, os padroes de oclusao e o orcamento de latencia.
  • outputs/skill-mot-evaluator.md — escreve um harness de avaliacao completo para MOTA / IDF1 / HOTA contra faixas de referencia.

Exercicios

  1. (Facil) Execute o rastreador sintetico acima com 3, 10 e 30 objetos. Reporte a contagem de trocas de ID em cada caso. Identifique onde a associacao simples apenas por IoU comeca a falhar.
  2. (Medio) Adicione um passo de previsao de Kalman de velocidade constante antes da associacao. Mostre que oclusoes curtas (2-3 quadros) nao causam mais trocas de ID.
  3. (Dificil) Integre o rastreador baseado em memoria do SAM 2 (via transformers) como um backend de rastreamento alternativo. Execute tanto o SimpleTracker quanto o SAM 2 em um clipe de 30 segundos de uma multidao e compare as contagens de trocas de ID, rotulando manualmente os IDs de referencia de 5 pessoas em destaque.

Termos-chave

Termo O que as pessoas dizem O que realmente significa
Rastreamento por deteccao "Detectar e depois associar" Detector por quadro + atribuicao Hungara sobre IoU / aparencia
Filtro de Kalman "Previsao de movimento" Dinamica linear + covariancia para previsoes suaves de faixa e tratamento de oclusao
Algoritmo Hungaro "Atribuicao otima" Resolve o problema de correspondencia bipartida de custo minimo; scipy.optimize.linear_sum_assignment
ByteTrack "Segunda passagem de baixa confianca" Re-associa faixas sem correspondencia a deteccoes de baixa confianca para recuperar oclusoes curtas
DeepSORT "SORT + aparencia" Adiciona um atributo de ReID para correspondencia entre quadros; melhor para preservacao de ID
Banco de memoria "Truque do SAM 2" Atributos espaco-temporais por instancia armazenados ao longo dos quadros; a atencao cruzada substitui a associacao explicita
Object Multiplex "Memoria compartilhada do SAM 3.1" Memoria unica compartilhada com consultas por instancia para rastreamento rapido de muitos objetos
HOTA "Metrica moderna de rastreamento" Decompoe em precisao de deteccao e de associacao; padrao da comunidade

Leitura Adicional

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).