Theo dõi nhiều đối tượng & trí nhớ video
Type: Build
Languages: Python
Prerequisites: Phase 4 Lesson 06 (YOLO Detection), Phase 4 Lesson 08 (Mask R-CNN), Phase 4 Lesson 24 (SAM 3)
Time: ~60 minutes
Mục tiêu học tập
- Hóa ra phân biệt theo dõi theo dò từ theo dõi dựa trên truy vấn và đặt tên cho các gia đình thuật toán (SORT, DeepSORT, ByteTrack, BoT-SORT, SAM 2 bộ nhớ theo dõi, SAM 3.1 Object Multiplex)
- Thực hiện IoU + Hungary assignment từ đầu cho theo dõi theo dò
- Giải thích ngân hàng bộ nhớ của SAM 2 và lý do tại sao nó xử lý sự bịt kín tốt hơn so với liên kết dựa trên IoU
- Đọc ba số liệu theo dõi (MOTA, IDF1, HOTA) và chọn một trong số đó quan trọng cho một trường hợp sử dụng nhất định
Vấn đề
Một máy dò cho bạn biết các đối tượng ở đâu trong một khung hình.tlà cùng một đối tượng như một phát hiện trong khungt-1Nếu không có nó, bạn không thể đếm được những vật vượt qua một đường, theo dõi một quả bóng qua một sự bịt kín, hoặc biết "cỗ xe #4 đã ở trong làn đường trong 8 giây".
Theo dõi là điều cần thiết cho mọi sản phẩm đối diện với video: phân tích thể thao, giám sát, lái xe tự động, phân tích video y tế, giám sát động vật hoang dã, đếm dấu từ. Các khối xây dựng cốt lõi được chia sẻ: một bộ phát hiện mỗi khung, một mô hình chuyển động (trình lọc Kalman hoặc một cái gì đó giàu hơn), một bước liên kết (đồ pháp Hungary về IoU / cosine / các tính năng học), và một vòng đời đường (sự sinh, cập nhật, cái chết).
Năm 2026 đã mang lại hai mô hình mới: SAM 2 memory-based tracking(tức nhớ tính năng thay vì kết hợp mô hình chuyển động) và SAM 3.1 Object MultiplexBài học này đi theo các phương pháp cổ điển trước, sau đó là phương pháp dựa trên bộ nhớ.
Khái niệm
Theo dõi bằng phát hiện
flowchart LR
F1["Frame t"] --> DET["Detector"] --> D1["Detections at t"]
PREV["Tracks up to t-1"] --> PREDICT["Motion predict<br/>(Kalman)"]
PREDICT --> PRED["Predicted tracks at t"]
D1 --> ASSOC["Hungarian assignment<br/>(IoU / cosine / motion)"]
PRED --> ASSOC
ASSOC --> UPDATE["Update matched tracks"]
ASSOC --> NEW["Birth new tracks"]
ASSOC --> DEAD["Age unmatched tracks; delete after N"]
UPDATE --> NEXT["Tracks at t"]
NEW --> NEXT
DEAD --> NEXT
style DET fill:#dbeafe,stroke:#2563eb
style ASSOC fill:#fef3c7,stroke:#d97706
style NEXT fill:#dcfce7,stroke:#16a34aMỗi bộ theo dõi mà bạn sẽ gặp vào năm 2026 là một biến thể trong vòng lặp này.
- SORT(2016): Kalman filter + IoU Hungarian. đơn giản, nhanh chóng, không có mô hình ngoại hình.
- DeepSORT(2017): SORT + một tính năng xuất hiện dựa trên CNN cho mỗi bài hát (ReID).
- ByteTrack(2021): liên kết các phát hiện độ tự tin thấp với giai đoạn thứ hai; không cần thiết các tính năng xuất hiện nhưng hiệu suất cao nhất trên MOT17.
- BoT-SORT(2022): Byte + camera motion compensation + ReID.
- StrongSORT / OC-SORT Những người theo dõi ByteTrack có chuyển động và ngoại hình tốt hơn.
Bộ lọc Kalman trong một đoạn
Một bộ lọc Kalman duy trì trạng thái theo dõi .(x, y, w, h, dx, dy, dw, dh)với một sự đồng hóa.predicttrạng thái sử dụng mô hình tốc độ liên tục, sau đó updateCác bản cập nhật này tin tưởng vào việc phát hiện nhiều hơn khi sự không chắc chắn dự đoán cao. Điều này cung cấp quỹ đạo trơn tru và khả năng tiếp tục theo dõi thông qua một sự đóng kín ngắn (1-5 khung).
Mỗi bộ theo dõi cổ điển sử dụng bộ lọc Kalman trong bước dự đoán chuyển động.
Algoritm Hungary
Với một M x NMatrix chi phí (tracks x detections), tìm việc giao dịch một đối với một để giảm thiểu tổng chi phí.1 - IoU(track_bbox, detection_bbox)hoặc tương đồng âm tính của các tính năng xuất hiện. thời gian chạy là O(((M+N) ^ 3); cho M, N lên đến ~ 1000 nó đủ nhanh trong Python thông qua scipy.optimize.linear_sum_assignment- Tôi không biết.
Ý tưởng chính của ByteTrack
Các máy theo dõi tiêu chuẩn cho thấy độ tự tin thấp (< 0, 5).second-stage candidates: sau khi kết hợp các đường ray với các phát hiện độ tin cậy cao, các đường ray không sánh được cố gắng kết hợp các phát hiện độ tin cậy thấp với ngưỡng IoU nhẹ nhàng hơn.
SAM 2 theo dõi dựa trên bộ nhớ
SAM 2 xử lý video bằng cách giữ một memory bankCác bộ nhớ được phân tích với các tính năng của khung mới, và bộ giải mã tạo ra một mặt nạ cho cùng một bản trong khung mới.
Không bộ lọc Kalman, không có bài tập tiếng Hungary.
Lợi thế:
- Đứng vững đến các sự che giấu lớn (tưởng thức mang lại danh tính trường hợp trên nhiều khung).
- Từ khóa mở khi kết hợp với các lời nhắn văn bản của SAM 3.
- Nó hoạt động mà không có mô hình chuyển động riêng biệt.
Khối tác:
- Hạt chậm hơn ByteTrack để theo dõi nhiều vật thể.
- Ngân hàng bộ nhớ tăng lên; hạn chế cửa sổ ngữ cảnh.
SAM 3.1 Object Multiplex
SAM 2 / SAM 3 theo dõi trước giữ một ngân hàng bộ nhớ riêng biệt mỗi lần. Đối với 50 đối tượng, 50 ngân hàng bộ nhớ. Object Multiplex (March 2026) sụp đổ chúng thành một bộ nhớ chung với per-instance query tokens- Giá cả tăng theo đường thẳng dưới số trường hợp.
Multiplex là tiêu chuẩn mặc định mới cho việc theo dõi đám đông vào năm 2026: đám đông biểu diễn, nhân viên kho, giao thông giao thông.
Ba số liệu cần biết
- MOTA (Multi-Object Tracking Accuracy) 1 - (FN + FP + ID switch) / GT. Đánh nặng theo loại lỗi; một số liệu đơn lẻ kết hợp các lỗi phát hiện và liên kết.
- IDF1 (ID F1) trung bình hợp đồng của độ chính xác và nhớ ID. Tập trung cụ thể vào mức độ tốt của mỗi đường dẫn thực tại giữ ID của mình theo thời gian.
- HOTA (Higher Order Tracking Accuracy) phân hủy thành độ chính xác phát hiện (DetA) và độ chính xác liên kết (AssA).
Đối với giám sát (còn là ai): IDF1 là những gì bạn báo cáo. Đối với phân tích thể thao (tài đếm): HOTA. Đối với so sánh học thuật chung: HOTA.
Hãy xây dựng nó
Bước 1: Matrix chi phí dựa trên IoU
pythonimport 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)Bước 2: Trình theo dõi kiểu SORT tối thiểu
Calman bỏ qua cho ngắn gọn chúng ta sử dụng một liên kết IoU đơn giản ở đây; trong sản xuất dự đoán Kalman là thiết yếu.sortPhạm Python cung cấp phiên bản đầy đủ.
pythonfrom 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 dòng. lấy các phát hiện trên mỗi khung, trả lại ID theo dõi trên mỗi khung. Hệ thống thực thêm dự đoán Kalman, sự phù hợp lại của ByteTrack giai đoạn hai, và tính năng xuất hiện.
Bước 3: Kiểm tra đường mòn tổng hợp
pythondef 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)Ba vật di chuyển thẳng phải giữ thẻ ID của họ trên tất cả 20 khung hình.
Bước 4: Métric chuyển đổi ID
pythondef 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Đây là một số liệu đơn giản hóa IDF1 lân cận: đếm bao nhiêu lần một đối tượng thực tại mặt đất thay đổi ID đường theo dõi dự đoán được gán.py-motmetricsvà TrackEval- Tôi không biết.
Sử dụng nó
Các máy theo dõi sản xuất vào năm 2026:
ultralyticsYOLOv8 + ByteTrack / BoT-SORT tích hợp.results = model.track(source, tracker="bytetrack.yaml")- Đáng mặc định.supervision(Roboflow) Bị trùm ByteTrack cộng với tiện ích ghi chú.- SAM 2 / SAM 3.1 theo dõi dựa trên bộ nhớ qua
processor.track()- Tôi không biết. - Dòng tùy chỉnh: máy dò (YOLOv8 / RT-DETR) +
sort-tracker-OC-SORT-StrongSORT- Tôi không biết.
Chọn:
- Người đi bộ / xe hơi / hộp ở tốc độ 30+ fps: ByteTrack with ultralytics- Tôi không biết.
- Nhiều trường hợp của một lớp trong đám đông:SAM 3.1 Object Multiplex- Tôi không biết.
- Các vết lấn nặng với hình dạng có thể xác định được: DeepSORT / StrongSORT(Các tính năng ReID).
- Thể thao / tương tác phức tạp: BoT-SORThoặc các máy theo dõi học (MOTRv3).
Chuyển nó
Bài học này mang lại:
outputs/prompt-tracker-picker.mdchọn SORT / ByteTrack / BoT-SORT / SAM 2 / SAM 3.1 cho các loại cảnh, mô hình bịt kín và ngân sách thời gian trễ.outputs/skill-mot-evaluator.mdviết một vòng đánh giá đầy đủ cho MOTA / IDF1 / HOTA chống lại các đường mòn thực tại mặt đất.
Các bài tập
- (Easy)Hãy chạy bộ theo dõi tổng hợp ở trên với 3, 10 và 30 đối tượng. báo cáo số lượng chuyển đổi ID trong mỗi trường hợp. xác định nơi liên kết đơn giản chỉ với IoU bắt đầu thất bại.
- (Medium)Thêm một bước dự đoán tốc độ liên tục Kalman trước khi kết hợp.
- (Hard)Thêm vào bộ theo dõi dựa trên bộ nhớ của SAM 2 (via
transformersThử cả SimpleTracker và SAM 2 trên một clip 30 giây của đám đông và so sánh số lượng chuyển đổi ID, bằng cách dán nhãn bằng tay các ID thực tại cho 5 người nổi bật.
Các điều khoản chính
| Term | What people say | What it actually means |
|---|---|---|
| Tracking-by-detection | "Detect then associate" | Per-frame detector + Hungarian assignment on IoU / appearance |
| Kalman filter | "Motion predict" | Linear dynamics + covariance for smooth track predictions and occlusion handling |
| Hungarian algorithm | "Optimal assignment" | Solves the minimum-cost bipartite matching problem; scipy.optimize.linear_sum_assignment |
| ByteTrack | "Low-confidence second pass" | Re-match unmatched tracks to low-confidence detections to recover short occlusions |
| DeepSORT | "SORT + appearance" | Adds a ReID feature for cross-frame matching; better for ID preservation |
| Memory bank | "SAM 2 trick" | Per-instance spatio-temporal features stored across frames; cross-attention replaces explicit association |
| Object Multiplex | "SAM 3.1 shared memory" | Single shared memory with per-instance queries for fast many-object tracking |
| HOTA | "Modern tracking metric" | Decomposes into detection and association accuracy; community standard |
Đọc thêm
- SORT (Bewley et al., 2016) giấy theo dõi bằng phát hiện tối thiểu
- DeepSORT (Wojke et al., 2017) thêm tính năng xuất hiện
- ByteTrack (Zhang et al., 2022) Tiếp tục thông qua thứ hai có độ tự tin thấp
- BoT-SORT (Aharon et al., 2022) Khấu trừ chuyển động của máy ảnh
- HOTA (Luiten et al., 2020) Metric theo dõi phân hủy
- SAM 2 video segmentation (Meta, 2024) bộ theo dõi dựa trên bộ nhớ
- SAM 3.1 Object Multiplex (Meta, March 2026)
This free lesson is part of the AI Engineering from Scratch curriculum. Read the full explanation, run the lesson code, and verify the result in the interactive reader or from the repository source.
Browse the complete course catalog or open this lesson on GitHub.