Сегодня мы возьмём две задачи, которые выглядят как чистая теория графов — паросочетание минимального веса и поток минимальной стоимости, — и заставим их следить за людьми на видео. Ни одной строчки не написано заранее: всё, что вы увидите, считается прямо в этой вкладке.
Настоящее видео с камеры наблюдения. По нему прогнали настоящий детектор — YOLOv8s, класс «человек»: 795 кадров, 4717 рамок. Нейросеть отработала прекрасно. Ответьте на простой вопрос: сколько всего человек прошло через кадр?
Парадигма называется tracking-by-detection. Детектор — не наша забота, он уже есть. Наша забота — то, что между кадрами.
| метрика | что считает |
|---|---|
| MOTA | пропуски + ложные + перепутывания |
| IDSW | сколько раз объект сменил номер |
| MOTP | точность рамок |
| MT / ML | доля треков, пройденных до конца |
| FM | сколько раз трек рвался |
«Свяжем каждый трек с ближайшей рамкой». Берём самую дешёвую пару, потом следующую, и так далее — жадный алгоритм. Работает. Ровно до первой встречи двух людей.
Та же сцена, что и минуту назад, — заглянем внутрь. Выпишем расстояния «трек → рамка» в таблицу. Задача обрела форму: выбрать по одной клетке в каждой строке и каждом столбце так, чтобы сумма была минимальна.
Это классическая задача о назначениях. Ей полтора века, у неё есть точный алгоритм — венгерский, — и он работает за O(n³).
scipy.optimize.linear_sum_assignment.
Одна строчка.Вот и весь ответ на вопрос из заголовка лекции. Задача не изменилась — изменилось только то, как мы её решаем. Перебор честен, но столько времени нет ни у кого.
Человек зашёл за столб — рамок нет. Просто подождать его? Попробуйте: поднимите «терпение» и выключите предсказание.
Всё. Ни одной обучаемой детали. Двести строк на Python.
| метод | режим | MOTA ↑ | IDSW ↓ | Гц ↑ |
|---|---|---|---|---|
| SORT | онлайн | 59.8 | 1423 | 60 |
| Deep SORT | онлайн | 61.4 | 781 | 40 |
| POI | онлайн | 66.1 | 805 | 10 |
| EAMTT | онлайн | 52.5 | 910 | 12 |
| LMP | офлайн | 71.0 | 434 | 0.5 |
| NOMTwSDP16 | офлайн | 62.2 | 406 | 3 |
| MCMOT HDM | офлайн | 62.4 | 1394 | 35 |
Синий зашёл за столб и там притормозил. Калман об этом не знает и продолжает вести его вправо. А из-за столба выходит совсем другой человек — и оказывается ровно там, где Калман ждал синего. Геометрия уверенно указывает на самозванца.
Изменилось только содержимое клеток. Двудольный граф тот же,
венгерский алгоритм тот же, строчка linear_sum_assignment та же.
И отдельная деталь, ради которой стоит читать статьи целиком. Авторы сообщают, что при заметном движении камеры разумным выбором оказалось λ = 0 — то есть в стоимости остаётся только внешность, а от геометрии остаётся лишь право вето.
Голубой надолго скрылся за опорой — его эллипс неуверенности растёт, и чем он больше, тем «ближе» потерянному треку кажется всё вокруг. Жёлтый просто стоит рядом. Досмотрите до конца: потерянный трек дотянется до чужой рамки — и украдёт её.
Всё, что было до сих пор, решало задачу
по одному кадру за раз.
Видео уже записано. Оно целиком лежит на диске. Мы имеем полное право посмотреть, что будет дальше, — и только потом решить, кто есть кто в текущем кадре. Это меняет не решение. Это меняет задачу.
Строим один граф на всё видео сразу.
Тогда единица потока из S в T — это ровно одна траектория: цепочка детекций от рождения до смерти. Пропускная способность 1 гарантирует, что треки не поделят рамку между собой.
Разворачиваем видео в график: по горизонтали — кадры, по вертикали — где объект был в кадре. Точки — детекции. Траектория превращается в линию, а поиск треков — в протягивание k путей слева направо.
Те же 4717 рамок из первого слайда. Всё видео развёрнуто в пространство и время: по горизонтали — кадры, по вертикали — где человек был в кадре.
Те же кадры, тот же детектор, что двадцать минут назад. Изменилось одно: поверх рамок поработали алгоритмы. Теперь на вопрос «сколько человек прошло» есть ответ.
Было бы нечестно закончить на том, что поток всё решает. У него есть врождённый изъян, и он виден прямо из формулировки.
Стоимость ребра зависит только от двух соседних детекций. Граф не помнит, откуда объект пришёл. Если двое встретятся в одной точке, поток с равным удовольствием протянет линии насквозь или развернёт их обратно — для него это одна и та же цена.
| SORT | Deep SORT | k-поток | |
|---|---|---|---|
| задача | паросочет. | паросочет. | поток |
| область | 1 кадр | 1 кадр | всё видео |
| алгоритм | венгерский | венгерский | k кратч. путей |
| что меняли | — | клетки матрицы | граф |
| онлайн | да | да | нет |
| IDSW | 1423 | 781 | меньше |
Так зачем нужны алгоритмы?
Не чтобы их писать. Готовый венгерский лежит в scipy, готовый min-cost flow — в networkx и OR-Tools. Написать их вы, скорее всего, не будете никогда.
Алгоритмы нужны, чтобы их узнавать. Человек, который знает про паросочетание, при виде мигающих рамок думает «а это же задача о назначениях» — и дальше дело техники. Человек, который не знает, пишет пятьсот строк эвристик с порогами и потом год их подкручивает.
| → пробел | следующий слайд |
| ← | предыдущий |
| Home End | в начало / в конец |
| F | полный экран |
| T | таймер: пауза / пуск |
| R | сбросить таймер |
| ? | эта справка |
Всё считается вживую. Ползунки можно крутить при аудитории — ничего не сломается, все результаты настоящие.