تتبّع الكائنات باستخدام ByteTrack وBoT-SORT وTrackTrack
امنح كل كائن معرّفًا ثابتًا عبر الإطارات، واختر متتبّعًا، واضبط عتباته بما يتوافق مع كاشفك.
يعتمد كل إحصاء ومدة مكوث وتقدير للسرعة على سؤال واحد: هل المعرّف 17 في هذا الإطار يعود إلى الكائن نفسه الذي يحمل المعرّف 17 في الإطار السابق؟ على المقطع نفسه المكوّن من 62 إطارًا، والذي يضم نحو 30 من المشاة، وباستخدام الكاشف نفسه، حدّد ByteTrack عدد 32 معرّفًا، بينما حدّد TrackTrack بالإعدادات الافتراضية 9 معرّفات. اختيار المتتبّع وضبطه جزء من النموذج، واختيار متتبّع غير مناسب يعطّل بصمت كل الأرقام اللاحقة.
عملي
ما الذي يضيفه المتتبّع#

التسميات والمسارات في هذا الإطار هي ما ينتجه المتتبّع. كل معرّف هو مسار، أي فرضية عن كائن حقيقي واحد، والمسار المرئي هو سجلّه الأخير. في كل إطار، يتنبأ المتتبّع بموضع كل مسار باستخدام مرشح كالمان، ثم يطابق الكشوف الجديدة مع تلك التنبؤات استنادًا إلى تداخل الصناديق (والمظهر اختياريًا):
تُحذف المسارات التي لا تُطابق أي كشف لمدة track_buffer من الإطارات. هذه هي الآلية بأكملها، وعتباتها هي التي تحدد كل ما يأتي بعدها.
اختر متتبّعًا#
توفّر وضع التتبّع ستة متتبّعات: BoT-SORT وByteTrack وOC-SORT وDeep OC-SORT وFastTracker وTrackTrack (وهو الافتراضي عند عدم تمرير tracker). وتلبي ثلاثة منها معظم الاحتياجات:
| أداة التتبع | إشارات المطابقة (الإعداد الافتراضي) | استخدمه عندما |
|---|---|---|
| ByteTrack | الحركة وتداخل الصناديق، بالإضافة إلى مرحلة مطابقة ثانية تستعيد الصناديق منخفضة الثقة | الكاميرات الثابتة، أو الكواشف الصغيرة أو غير اليقينية، أو عندما تكون الإنتاجية مهمة |
| BoT-SORT | مطابقة على نمط ByteTrack مع تعويض حركة الكاميرا (gmc_method: sparseOptFlow) | الكاميرات المتحركة أو المهتزة؛ اضبط with_reid: True للتعامل مع الحشود وحالات الاحتجاب الطويلة |
| TrackTrack | تكلفة متعددة الإشارات (شكل الصندوق، والثقة، والزاوية)، وتهيئة تراعي المسارات، وتعويض حركة الكاميرا | كشوف عالية الثقة من نموذج قوي ومدرّب جيدًا |
مطابقة المظهر في BoT-SORT (إعادة التعرّف) معطّلة افتراضيًا؛ وعند استخدام model: auto، يعيد المتتبّع استخدام ميزات الكاشف نفسه، لذا فإن تفعيلها لا يضيف إلا عبئًا ضئيلًا.
قارن المتتبّعات على مقطع واحد#
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يُظهر المقطع نحو 30 شخصًا يعبرون ساحة من منظور علوي، لذا تقترب نتائج ByteTrack وBoT-SORT من العدد الفعلي. أما TrackTrack فليس معطّلًا: فهو يبدأ مسارًا فقط عندما تتجاوز الثقة new_track_thresh: 0.7، ولا يجري المطابقة الأولية إلا عندما تتجاوز track_high_thresh: 0.6، في حين أن وسيط ثقة YOLO26n للأشخاص في هذا المقطع هو 0.53، ولا تبلغ سوى 17% من الكشوف قيمة 0.7. لذلك لا يحصل معظم الأشخاص على معرّف. قاعدة اتخاذ القرار: قارن عتبات المتتبّع بتوزيع درجات الثقة لدى كاشفك قبل الوثوق بأي إحصاء.
استخدم المعرّفات في مسار معالجة#
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تحتفظ مجموعة لكل فئة بكل معرّف مرة واحدة، بصرف النظر عن عدد الإطارات التي يظهر فيها، ولذلك تكون النتيجة عدد الكائنات لا عدد الكشوف. يعيد stream=True النتائج إطارًا تلو الآخر بدلًا من الاحتفاظ بالفيديو كله في الذاكرة. إن عدّ المناطق والخرائط الحرارية وحساب المسافة طبقات بسيطة مبنية فوق هذه الحلقة.
تتبّع الإطارات التي تقرؤها بنفسك#
يتولى تمرير مسار أو عنوان URL إدارة حالة المتتبّع نيابةً عنك. أما إذا جاءت الإطارات من وحدة فك ترميز خاصة بك، أو كاميرا ويب، أو كاميرا RTSP، فمرّر persist=True لكي يواصل كل استدعاء التسلسل نفسه (تسرد وثائق وضع التنبؤ جميع أنواع المصادر):
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النتيجة نفسها كما لو مرّرت المسار، لأن persist=True أبقى متتبّعًا واحدًا نشطًا خلال الاستدعاءات الـ62 جميعها.
اضبط المتتبّع ليتوافق مع كاشفك#
انسخ إعدادًا مضمّنًا، وخفّض العتبات التي لا تناسب كاشفك، ثم مرّر الملف الجديد:
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أدى ضبط العتبات لتكون أدنى بقليل من وسيط ثقة الكاشف إلى رفع عدد المعرّفات في TrackTrack من 9 إلى 27، وهو قريب من العدّ اليدوي. وفيما يلي عناصر الضبط المشتركة بين جميع المتتبّعات:
| الإعداد | الإعداد الافتراضي لـ ByteTrack | ارفعه من أجل | اخفضه من أجل |
|---|---|---|---|
track_high_thresh | 0.25 | تجاهل الكشوف الضعيفة في المطابقة الأولى | تتبّع كاشفًا منخفض الثقة |
new_track_thresh | 0.25 | استبعاد المسارات الخاطئة قصيرة الأمد | بدء مسارات للأجسام الخافتة |
track_buffer | 30 إطارًا | تحمّل حالات الاحتجاب الأطول | تحرير معرّفات الأجسام التي غادرت في وقت أقرب |
match_thresh | 0.8 | مطابقة أكثر تساهلًا (حركة سريعة، وصناديق متذبذبة) | مطابقة أكثر صرامة (الحشود) |
مواطن الخلل#
العَرَض: عدد المعرّفات أقل بكثير من عدد الأجسام، خصوصًا مع نموذج صغير. الحل: اطبع وسيط ثقة الكشف (انظر الحل)، واضبط new_track_thresh وtrack_high_thresh على قيم أدنى منه بقليل.
العَرَض: تُعاد تهيئة المعرّفات كل إطار عند استدعاء model.track(frame) بنفسك، أو تنتقل بين الأجسام عندما تغذي كاميرتان نموذجًا واحدًا. الحل: مرّر persist=True للإطارات المتتالية من تدفق واحد، وخصص نسخة نموذج لكل كاميرا (أو استخدم مصدر .streams، الذي يحتفظ بمتعقّب لكل تدفق).
العَرَض: يتغير معرّف سيارة متوقفة كل بضعة إطارات. الحل: ينشئ المتعقّب مسارات جديدة بدلًا من مطابقة المسارات القديمة. تحقّق أولًا من ثقة الكاشف في ذلك الجسم، ثم ارفع match_thresh وtrack_buffer.
عرض الحل
قِس درجات ثقة الكاشف أولًا، ثم اضبط العتبات لتكون أدنى منها بقليل:
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%في جدول مقطع الساحة، تكون النتائج: ByteTrack: 32، وBoT-SORT: 33، وTrackTrack: 9، وTrackTrack المضبوط: 27، والعدّ اليدوي: نحو 30. لا تتجاوز عتبة TrackTrack new_track_thresh: 0.7 سوى 17% من الكشوف، لذا فإن خفضها إلى 0.35 (باستخدام track_high_thresh: 0.3) هو الحل المناسب؛ وفي هذه الكاميرا العلوية الثابتة، يكون ByteTrack العادي الخيار الأبسط.