Ultralytics YOLO27: ウェイトリストに登録する。
Get Started
本番環境でのUltralytics YOLO·トラッキングとパイプラインの構築·レッスン 5/9
レッスン中級

ByteTrack、BoT-SORT、TrackTrackでオブジェクトを追跡する

すべてのオブジェクトにフレーム間で安定したIDを割り当て、トラッカーを選択し、検出器に合わせてしきい値を調整します。

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

成果

1本の動画で3種類のトラッカーを実行し、検出器の信頼度からID数を説明し、パイプラインでbox.idを使用して、調整済みのYAMLで不適合なトラッカーを修正します。

短期集中コース
すでに使い方をご存じの方は、こちらをご覧ください。
  1. model.track(source, stream=True, tracker="bytetrack.yaml")を使用します。各ボックスには安定したidが割り当てられます。

  2. フレームを自分で渡す場合は、呼び出しをまたいでIDを維持するためにpersist=Trueを渡します。カメラごとに1つのトラッカーを使用してください。

  3. デフォルトはtracktrack.yamlです。ByteTrackは高速で柔軟に対応し、BoT-SORTはカメラモーション補正を追加します。ReIDはオプトイン方式です。

  4. IDが少なすぎる場合は、検出器の信頼度を測定し、コピーしたYAML内のnew_track_threshとtrack_high_threshを下げます。

実践的

トラッカーが追加するもの#

Ultralytics YOLOによる高速道路の交通追跡。各車、バス、トラックに番号付きID(車#12、バス#21、トラック#16)が付与され、直近の移動経路に沿って色付きの線が表示されます

このフレームのラベルと軌跡は、トラッカーが生成したものです。各IDはトラック、つまり実在する1つのオブジェクトについての仮説であり、軌跡はその直近の履歴です。各フレームでトラッカーはカルマンフィルターを使って各トラックの位置を予測し、新しい検出結果をボックスの重なり(および任意で外観)に基づいて予測に対応付けます。

track_bufferフレームにわたって対応付けられなかったトラックは削除されます。仕組みはこれだけであり、しきい値がその後のすべてを決定します。

トラッカーを選択する#

Trackモードには6種類のトラッカーが用意されています。BoT-SORT、ByteTrack、OC-SORT、Deep OC-SORT、FastTracker、TrackTrack(trackerを渡さない場合のデフォルト)です。ほとんどの用途には、次の3種類で対応できます。

トラッカー照合に使う手がかり(デフォルト設定)次のような場合に適しています
ByteTrack動きとボックスの重なりに加え、信頼度の低いボックスを救済する2回目の照合固定カメラ、小規模または不確かな検出器、スループットが重要な場合
BoT-SORTByteTrack方式の照合にカメラモーション補正(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 IDs

persist=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_thresh0.25最初の照合で信頼度の低い検出結果を無視する信頼度の低い検出器を追跡する
new_track_thresh0.25短時間で消える誤トラックを減らすかすかなオブジェクトのトラックを開始する
track_buffer30フレームより長い遮蔽に対応する画面から消えたオブジェクトのIDを早く解放する
match_thresh0.8照合を緩やかにする(高速な動き、ボックスの揺れ)照合を厳密にする(人混み)

よくある落とし穴#

検出器の確信度を超えるしきい値

症状: 特に小さなモデルで、物体の数に比べてIDが大幅に少なくなります。 対処: 検出確信度の中央値を出力し(解決策を参照)、それより少し低い値にnew_track_threshとtrack_high_threshを設定します。

persistの指定を忘れる、または共有する

症状: 自分でmodel.track(frame)を呼び出すとフレームごとにIDがリセットされたり、2台のカメラから1つのモデルに入力するとIDが物体間で飛び移ったりします。 対処: 1つのストリームの連続するフレームにはpersist=Trueを渡し、カメラごとに独自のモデルインスタンスを用意します(または、ストリームごとに1つのトラッカーを保持する.streamsソースを使用します)。

低速の物体でIDがちらつく

症状: 駐車中の車のIDが数フレームごとに変わります。 対処: トラッカーが既存のトラックと照合せず、新しいトラックを開始しています。まず、その物体に対する検出器の確信度を確認し、次にmatch_threshとtrack_bufferを増やします。

試してみる

自分のドメインから30~60秒の動画を用意し、1クラスのオブジェクトを手作業で数えます。その動画にcompare_trackers.pyを実行し、ソリューションスクリプトで検出器の信頼度を測定して、ID数が手作業のカウントに近づくようにトラッカーのYAMLを1つ調整します。トラッカー、ユニークID数、手作業のカウントを表にまとめて提出します。

完了条件
以下のすべてを満たすと、レッスンは完了です。
  • 同じ動画について、ByteTrack、BoT-SORT、TrackTrackのユニークID数と手作業のカウントを比較できる状態です。

  • 最も少ないカウントについて、トラッカーのしきい値と検出器の信頼度中央値を使って説明できます。

  • 調整済みトラッカーのYAMLによって、実行前に予測した方向へカウントが変化しました。

解答を表示

まず検出器の信頼度を測定し、それより少し低い値にしきい値を設定します。

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を使うほうが簡単です。

次のステップ

安定したIDを使って、次のレッスンでは最も一般的な本番環境向けパイプライン、すなわちカウント、ゾーン、ヒートマップ、速度を構築します。