Deteksi per-frame menjadi lintasan: tracker BoT-SORT dan ByteTrack bawaan, persist=True untuk ID antar frame, praktik hitung objek melewati garis dengan centroid + state machine, plus batasan occlusion dan ID switch

Sebelas episode kita hidup di dunia gambar diam — deteksi per-frame yang saling lepas. Padahal mayoritas use case industri hidup di video: hitung kendaraan di tol, pantau orang di area sensitif, analisis pergerakan pemain. Dan pertanyaan video hampir selalu bukan "ada apa di frame ini?" melainkan "objek INI sedang ke mana?" — itulah pekerjaan tracking.
Bedanya fundamental: detection menjawab keberadaan per frame; tracking menjawab identitas kontinu — mobil dengan ID #7 di frame 100 harus tetap ID #7 di frame 101, walau posisinya bergeser. Tanpa ini, penghitung kendaraan akan menghitung mobil yang sama berkali-kali.
Tracking YOLO modern memakai pola tracking-by-detection: YOLO mendeteksi box per frame, lalu tracker terpisah mengaitkan deteksi antar frame:
Ultralytics membawa dua tracker state-of-the-art tanpa konfigurasi tambahan:
| Tracker | Mekanisme Asosiasi | Kekuatan |
|---|---|---|
| ByteTrack | Asosiasi dua tahap — deteksi confidence rendah pun dipakai | Robust terhadap deteksi goyah; default populer |
| BoT-SORT | IoU + motion (Kalman) + opsional appearance ReID | Lebih akurat untuk gerakan kompleks |
Intuisi ByteTrack yang elegan: tracker lain membuang deteksi low-confidence sehingga objek samar langsung "hilang". ByteTrack justru memakai deteksi lemah itu untuk menjaga jejak — inilah rahasia ID persistence-nya.
model.track() DasarAPI-nya nyaris identik dengan predict:
yolo track model=yolo26n.pt source=traffic.mp4 save=True tracker=bytetrack.yamlDua kata kunci yang tidak boleh salah:
persist=True — state tracker (Kalman filter + riwayat track) dipertahankan antar panggilan. Tanpa ini, setiap frame diperlakukan sebagai video baru dan ID terus berubah.r.boxes.id is None check — frame pertama atau frame kosong bisa saja tidak punya track; akses langsung akan error.Use case paling dicari: line-crossing counter. Logikanya murni geometri di atas track ID:
import cv2
from collections import defaultdict
from ultralytics import YOLO
LINE_Y = 400 # garis horizontal y=400
counted = set()
counts = defaultdict(int)
model = YOLO("yolo26n.pt")
results = model.track(source="traffic.mp4", stream=True, persist=True)
for r in results:
frame = r.orig_img
cv2.line(frame, (0, LINE_Y), (frame.shape[1], LINE_Y), (0, 255, 255), 2)
if r.boxes.id is not None:
for box in r.boxes:
if int(box.cls.item()) != 2: # kelas 2 = car di COCO
continue
tid = int(box.id.item())
cy = float(box.xywh[0][1])
prev_y = prev.get(tid, cy)
prev[tid] = cy
# lintas garis ke bawah: sebelumnya di atas, sekarang di bawah
if prev_y < LINE_Y <= cy and tid not in counted:
counted.add(tid)
counts["turun"] += 1
cv2.putText(frame, f"Count: {sum(counts.values())}",
(30, 60), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 255, 0), 3)
cv2.imshow("counter", frame)
if cv2.waitKey(1) == ord("q"):
break
cv2.destroyAllWindows()Tiga ide inti kode itu:
xywh center) dibandingkan terhadap garis referensi.counted menjamin satu ID dihitung tepat sekali seumur videonya.Pola ini langsung dipakai untuk: gate parkir, zebra cross analytics, conveyor counting di pabrik, sampai arah laju (in/out mall). Untuk zona area (bukan garis), ganti uji titik-dalam-poligon — logika state machine-nya sama.
Tip
Posisikan garis di area dengan okupansi rendah frame (tengah jalur kosong), bukan di area ramai. Crossing di area padat rawan salah asosiasi — dua ID bertukar posisi dalam satu frame membuat counter melompat.
Jujur soal keterbatasan agar harapan kalian realistis:
tracker=botsort.yaml dengan ReID aktif (appearance matching).bytetrack.yaml punya track_buffer (berapa frame track dipertahankan saat hilang) dan match_thresh — naikkan buffer untuk occlusion pendek.Rangkuman episode ini:
model.track(persist=True, stream=True) — dua flag wajib untuk video.track_buffer, match_thresh).Di episode 17 kita buka kontrol level dewa: custom training loop & callbacks — hook on_train_epoch_end, integrasi W&B/MLflow, dan cara memodifikasi perilaku Ultralytics tanpa menulis ulang library-nya. Sampai jumpa!