ByteTrack、BoT-SORT、TrackTrackでオブジェクトを追跡する
すべてのオブジェクトにフレーム間で安定したIDを割り当て、トラッカーを選択し、検出器に合わせてしきい値を調整します。
カウント、滞在時間、速度の推定はすべて、ある問いに左右されます。このフレームのID 17は、前のフレームのID 17と同じオブジェクトでしょうか。同じ検出器を使い、約30人の歩行者が映る同じ62フレームのクリップで比較したところ、ByteTrackは32個のIDを検出し、デフォルトのTrackTrackは9個を検出しました。トラッカーの選択と調整はモデルの一部であり、誤った選択は後続の数値すべてを気づかないうちに損ないます。
実践的
トラッカーが追加するもの#

このフレームのラベルと軌跡は、トラッカーが生成したものです。各IDはトラック、つまり実在する1つのオブジェクトについての仮説であり、軌跡はその直近の履歴です。各フレームでトラッカーはカルマンフィルターを使って各トラックの位置を予測し、新しい検出結果をボックスの重なり(および任意で外観)に基づいて予測に対応付けます。
track_bufferフレームにわたって対応付けられなかったトラックは削除されます。仕組みはこれだけであり、しきい値がその後のすべてを決定します。
トラッカーを選択する#
Trackモードには6種類のトラッカーが用意されています。BoT-SORT、ByteTrack、OC-SORT、Deep OC-SORT、FastTracker、TrackTrack(trackerを渡さない場合のデフォルト)です。ほとんどの用途には、次の3種類で対応できます。
| トラッカー | 照合に使う手がかり(デフォルト設定) | 次のような場合に適しています |
|---|---|---|
| ByteTrack | 動きとボックスの重なりに加え、信頼度の低いボックスを救済する2回目の照合 | 固定カメラ、小規模または不確かな検出器、スループットが重要な場合 |
| BoT-SORT | ByteTrack方式の照合にカメラモーション補正(gmc_method: sparseOptFlow)を追加 | カメラが移動または振動する場合。人混みや長時間の遮蔽に対応するにはwith_reid: Trueを設定します。 |
| TrackTrack | 複数の手がかり(ボックス形状、信頼度、角度)に基づくコスト、トラックを考慮した初期化、カメラモーション補正 | 強力で十分に学習されたモデルによる、信頼度の高い検出結果 |
BoT-SORTの外観照合(再識別)はデフォルトで無効です。model: autoを使うと検出器自体の特徴量を再利用するため、有効にしてもオーバーヘッドはほとんど増えません。
1本のクリップでトラッカーを比較する#
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で、検出結果のうち0.7に達するのはわずか17%です。そのため、ほとんどの人にIDが割り当てられません。判断の指針は、カウントを信頼する前に、トラッカーのしきい値と検出器の信頼度分布を比較することです。
パイプラインでIDを使用する#
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クラスごとにIDをセットで管理すると、何フレームに現れても各IDを1回だけ数えられるため、検出数ではなくオブジェクト数が得られます。stream=Trueは動画全体をメモリに保持せず、フレームごとに結果を返します。領域カウント、ヒートマップ、距離計算は、このループの上に構築する薄いレイヤーです。
自分で読み込んだフレームを追跡する#
パスまたはURLを渡せば、トラッカーの状態は自動的に管理されます。独自のデコーダー、ウェブカメラ、RTSPカメラからフレームを取得する場合は、各呼び出しで同じシーケンスを継続するためにpersist=Trueを渡します(Predictモードのドキュメントに、すべてのソースタイプが記載されています)。
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 IDspersist=Trueが62回の呼び出しすべてで1つのトラッカーを維持するため、パスを渡した場合と同じ結果になります。
検出器に合わせてトラッカーを調整する#
同梱の設定ファイルをコピーし、検出器に合わないしきい値を下げて、新しいファイルを渡します。
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のID数は9から27に増え、手作業で数えた数に近づきました。すべてのトラッカーに共通する調整項目は次のとおりです。
| 設定項目 | ByteTrackのデフォルト値 | 上げる場合 | 下げる場合 |
|---|---|---|---|
track_high_thresh | 0.25 | 最初の照合で信頼度の低い検出結果を無視する | 信頼度の低い検出器を追跡する |
new_track_thresh | 0.25 | 短時間で消える誤トラックを減らす | かすかなオブジェクトのトラックを開始する |
track_buffer | 30フレーム | より長い遮蔽に対応する | 画面から消えたオブジェクトのIDを早く解放する |
match_thresh | 0.8 | 照合を緩やかにする(高速な動き、ボックスの揺れ) | 照合を厳密にする(人混み) |
よくある落とし穴#
症状: 特に小さなモデルで、物体の数に比べてIDが大幅に少なくなります。 対処: 検出確信度の中央値を出力し(解決策を参照)、それより少し低い値にnew_track_threshとtrack_high_threshを設定します。
症状: 自分でmodel.track(frame)を呼び出すとフレームごとにIDがリセットされたり、2台のカメラから1つのモデルに入力するとIDが物体間で飛び移ったりします。 対処: 1つのストリームの連続するフレームにはpersist=Trueを渡し、カメラごとに独自のモデルインスタンスを用意します(または、ストリームごとに1つのトラッカーを保持する.streamsソースを使用します)。
症状: 駐車中の車のIDが数フレームごとに変わります。 対処: トラッカーが既存のトラックと照合せず、新しいトラックを開始しています。まず、その物体に対する検出器の確信度を確認し、次に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を使うほうが簡単です。