Ultralytics YOLO27: Встать в лист ожидания.
Get Started
Ultralytics YOLO в продакшене·Трекинг объектов и создание конвейеров·Урок 5/9
Уроксредний

Трекинг объектов с ByteTrack, BoT-SORT и TrackTrack

Назначай каждому объекту стабильный идентификатор на всех кадрах, выбирай трекер и подбирай его пороги под свой детектор.

Каждый подсчёт, время пребывания и оценка скорости зависят от одного вопроса: является ли ID 17 на этом кадре тем же объектом, что и ID 17 на предыдущем? На одном и том же ролике из 62 кадров примерно с 30 пешеходами и с одним и тем же детектором ByteTrack нашёл 32 ID, а TrackTrack с настройками по умолчанию — 9. Выбор и настройка трекера — часть модели, а ошибка в них незаметно искажает все последующие показатели.

Результат

Запусти три трекера на одном видео, объясни их количество ID уверенностью детектора, используй box.id в конвейере и исправь несоответствие трекера с помощью настроенного YAML-файла.

Ускоренное прохождение
Если ты уже освоился, вот краткая версия.
  1. model.track(source, stream=True, tracker="bytetrack.yaml"); каждому боксу присваивается стабильный id.

  2. Подаёшь кадры самостоятельно? Передай persist=True, чтобы ID сохранялись между вызовами; для каждой камеры используй отдельный трекер.

  3. По умолчанию используется tracktrack.yaml; ByteTrack работает быстро и надёжно; BoT-SORT добавляет компенсацию движения камеры, а ReID включается по желанию.

  4. Слишком мало ID: измерь уверенность детектора и снизь new_track_thresh и track_high_thresh в копии YAML-файла.

Практические занятия

Что добавляет трекер#

Трекинг дорожного движения с Ultralytics YOLO: у каждого автомобиля, автобуса и грузовика есть пронумерованный ID (автомобиль #12, автобус #21, грузовик #16), а за ним тянется цветная линия недавнего пути

Метки и траектории на этом кадре — результат работы трекера. Каждый ID — это трек, гипотеза об одном реальном объекте, а траектория показывает его недавнюю историю. На каждом кадре трекер предсказывает положение каждого трека с помощью фильтра Калмана, а затем сопоставляет новые обнаружения с этими предсказаниями по перекрытию боксов (и, при необходимости, по внешнему виду):

Треки, которые не сопоставляются с обнаружениями в течение track_buffer кадров, удаляются. Таков весь механизм, и его пороги определяют всё, что происходит дальше.

Выбери трекер#

В режиме Track доступны шесть трекеров: 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. Большинство людей так и не получают 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 один раз, сколько бы кадров он ни появлялся, поэтому результат показывает количество объектов, а не обнаружений. 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 вызовов.

Настрой трекер под свой детектор#

Скопируй готовую конфигурацию, снизь пороги, которые не подходят твоему детектору, и передай новый файл:

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

Установка порогов чуть ниже медианной уверенности детектора увеличила количество ID в TrackTrack с 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 или использовать его совместно

Симптом: ID сбрасываются на каждом кадре, когда ты сам вызываешь model.track(frame), или перескакивают между объектами, когда две камеры передают данные одной модели. Решение: передавай persist=True для последовательных кадров одного потока и создай отдельный экземпляр модели для каждой камеры (или используй источник .streams, который хранит отдельный трекер для каждого потока).

Дрожание ID у медленно движущихся объектов

Симптом: ID припаркованного автомобиля меняется каждые несколько кадров. Решение: трекер создаёт новые треки вместо сопоставления с существующими. Сначала проверь уверенность детектора для этого объекта, затем увеличь match_thresh и track_buffer.

Попробовать

Возьми видео длительностью 30–60 секунд из своей предметной области и вручную подсчитай объекты одного класса. Запусти на нём compare_trackers.py, измерь уверенность детектора с помощью скрипта для решения задачи и настрой YAML-файл одного трекера так, чтобы количество ID приблизилось к ручному подсчёту. Подготовь таблицу: трекер, уникальные ID, ручной подсчёт.

Критерии готовности
Урок считается завершённым, если выполнены все условия ниже.
  • У тебя есть количество уникальных ID для ByteTrack, BoT-SORT и TrackTrack на одном и том же видео и рядом — ручной подсчёт.

  • Ты можешь объяснить наименьшее количество ID порогами трекера и медианной уверенностью детектора.

  • Настройка YAML-файла трекера изменила количество ID в том направлении, которое ты предсказал до запуска.

Показать решение

Сначала измерь уверенность детектора, затем установи пороги чуть ниже этих значений:

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. Только 17% обнаружений превышают new_track_thresh: 0.7 TrackTrack, поэтому правильное решение — снизить его до 0.35 (с track_high_thresh: 0.3); для этой неподвижной камеры с видом сверху проще использовать обычный ByteTrack.

Что дальше

При наличии стабильных ID следующий урок посвящён самым распространённым производственным конвейерам на их основе: подсчёту, зонам, тепловым картам и скорости.

Войди, чтобы сохранять прогресс