|
Professor Seleznov
|
 Всем привет, на связи команда NeuroCore. Сегодня расскажем про кейс разработки системы видеоаналитики для магазинов самообслуживания: почему fisheye-камеры - настоящее проклятие, почему SORT и DeepSORT не справились с задачей, как мы выстроили конвейер от детекции до бизнес-событий, и какие инженерные решения позволили добиться стабильной работы в продакшене. Дано: магазины самообслуживания, которые работают без кассиров и продавцов. Покупатель входит по QR, выбирает товары, рассчитывается и выходит. Заказчику нужна автоматизированная система отслеживания: кто находится внутри, сколько времени, в каких зонах, а также распознает несанкционированный доступ и вход группами. В случае нарушений, система должна генерировать алерты по 7 типам событий. Что есть: одна потолочная fisheye-камера, которая покрывает весь зал. Это идеальный выбор для ритейла: угол обзора 180 градусов, не нужно ставить десятки обычных камер, не нужно сшивать панорамы. Но за этот комфорт приходится платить. Что не так с “рыбьим глазом” Fisheye-объектив проецирует сферическую сцену на плоский сенсор через нелинейное отображение. Модель искажения OpenCV описывается формулой: θ_dist = θ × (1 + k1×θ² + k2×θ⁴ + k3×θ⁶ + k4×θ⁸) На практике это означает:
- Нелинейный масштаб. Один и тот же человек у центра кадра и у края выглядит по-разному: у центра — нормальный силуэт сверху, у края — вытянутый и искривлённый. Смещение на 50 пикселей в центре и у края — это разные реальные расстояния.
- Форма детекций “плывёт”. Когда человек идёт от центра к краю, его bounding box меняет пропорции от почти квадратного до вытянутого прямоугольника. Трекер, ориентирующийся на IoU, воспринимает это как потерю объекта.
- Полные окклюзии при виде сверху. Камера смотрит строго вниз, люди ходят друг за другом. Один полностью перекрывает другого, IoU между детекциями падает до нуля за один кадр.
Почему SORT и DeepSORT не справляются SORT и DeepSORT для трекинга - логичный выбор в данном классе задач. Это стандартные алгоритмы, которые используют для отслеживания людей в видеопотоке. SORT опирается на IoU между предсказанной и обнаруженной рамкой плюс модель движения на основе фильтра Калмана. На fisheye оба механизма ломаются одновременно: IoU ненадёжен из-за геометрических искажений, модель движения некорректна в неевклидовом пространстве. DeepSORT добавляет ReID-эмбеддинги, но по-прежнему опирается на IoU и предсказание позиции. При длительных окклюзиях — а на fisheye они норма— ID теряется, потому что трекер не ждёт возвращения человека достаточно долго. Прогнали оба на реальной записи с торгового зала. При первом же скоплении людей — через 30–40 секунд — ID начали перемешиваться. Счётчик посетителей после этого не восстанавливался. Архитектура системы Мы построили pipeline из шести компонентов, каждый из которых решает свою задачу:
| Компонент |
Технология |
Задача |
| Детектор |
YOLO |
Обнаружение людей на fisheye кадре |
| Трекер |
DeepOcSort (BoxMOT) |
Связывание детекций между кадрами |
| ReID-модель |
OSNet / ResNet50 |
Извлечение вектора внешности (эмбеддинга) |
| ReID менеджер |
Галерея эмбеддингов + EMA |
Восстановление потерянных ID |
| Менеджер треков |
Алгоритмы |
Подсчёт посетителей, слияния/разделения |
| Оценщик скорости |
FisheyeUndistorter и VelocityEstimator |
Расчёт реальной скорости в м/с |
Детекция и трекинг работают на сыром fisheye-кадре, без выпрямления геометрии.Тестировали undistort для компенсации геометрических искажений объектива, которая переводит координаты из искажённого fisheye-пространства в евклидово, чтобы “выпрямить” кадр перед подачей в детектор. На практике это не дало особого выигрыша. Нейросети детектора и ReID устойчиво работали на искажённом кадре — они обучены достаточно, чтобы справляться с перспективными искажениями. Выпрямление не улучшало качество детекций. Undistort остался только в одном месте — в расчёте скорости, где он действительно нужен: пиксельное смещение на fisheye нелинейно, и перевести его в метры без выпрямления координат нельзя. Но это точечная трансформация координат, а не рендер всего кадра. Детекция: YOLO на fisheye Детектор на базе YOLO фильтрует по классу — из всего, что есть в кадре, нужны только люди. Мелкие детекции отсекаются: на краях fisheye-кадра артефакты искажения дают ложные срабатывания, и без фильтрации по размеру они засоряют трекер. Проблемой стала форма рамок. ReID-модели обучены на датасетах пешеходов с уличных камер, где человек в кадре вертикальный. Fisheye смотрит сверху, и силуэт человека там часто шире, чем выше. Если подавать такой bbox в ReID без предобработки, модель получает на входе то, на чём не обучалась, и эмбеддинги выходят малоинформативными. Метод normalize_boxes_for_reid приводит рамку к квадратной форме с центрировкой по вертикали — после этого качество ассоциаций заметно выросло. Трекер: почему OC-SORT, а не DeepSORT Использовали OC-SORT с ReID-эмбеддингами из библиотеки BoxMOT. Главное отличие от классического SORT: OC-SORT обновляет состояние трека, отталкиваясь от факта наблюдения, а не от предсказания модели движения. В толпе, где траектория непредсказуема, это принципиально. Параметры, подобранные под fisheye-условия: w_association_emb = 0.85 - 85% решения об ассоциации принимается на основе сходства внешности. Формула: cost = (1 - 0.85) × motion_cost + 0.85 × (1 - cosine_similarity) iou_thresh = 0.05 - крайне низкий порог IoU. Обычно ставят 0.3–0.5. У нас 0.05, потому что в толпе люди стоят вплотную, их детекты перекрываются и при высоком пороге трекер склеивает двух разных людей в один трек. max_age = 100 кадров время жизни трека без ассоциации. При 10-15 FPS это примерно 7–10 секунд позволяет удерживать ID, даже если человек надолго пропал. EMA-обновление эмбеддинга с alpha ≈ 0.985 плавное обновление внешности сглаживает случайные выбросы ReID-менеджер ReidManager работает поверх трекера и решает три задачи:
- Галерея эмбеддингов. Для каждого активного трека хранится очередь из последних 30 эмбеддингов. На их основе рассчитывается прототип — усреднённый EMA-вектор, представляющий типичную внешность. Чем больше наблюдений, тем стабильнее прототип.
- ReLink. Когда BoxMOT создаёт новый трек, менеджер проверяет, не тот ли это человек, которого потеряли раньше. Эмбеддинг нового трека сравнивается с прототипами недавно потерянных. Порог восстановления relink_dist_thresh = 0.25 — если косинусное расстояние ниже, старому треку возвращается его ID. Дополнительно учитывается близость к точке исчезновения: если новый трек появился рядом с тем местом, где пропал старый, порог смягчается. Один ID не может быть присвоен нескольким трекам в одном кадре.
- Защита от ID-swap. ID-swap — когда два человека проходят мимо друг друга, их bounding box'ы перекрываются, и трекер меняет их идентификаторы местами. На fisheye это происходит регулярно. Механизм защиты: если новый эмбеддинг сильно отличается от прототипа, он помечается как подозрительный. После двух подозрительных эмбеддингов подряд система отказывается добавлять их в галерею и перекалибрует прототип по последним стабильным наблюдениям.
Защита от подмены ID Коварная ошибка системы: когда два человека подходят друг к другу, их bounding box'ы сливаются в один, один трек поглощает другой. Затем они расходятся — нужно восстановить потерянный ID. TrackManager обрабатывает это через двухфазную логику:
- Детекция слияния. Трек пропал, рядом с ним появился другой трек с заметно увеличенным bbox. Критерии: расстояние центров не превышает заданного порога, площадь bbox выросла больше чем в 1.5 раза.
- Детекция разделения. После зафиксированного слияния рядом с поглотившим треком появляется новая небольшая детекция — система пытается присвоить ей ID поглощённого трека. Восстановление по минимальному расстоянию между центрами с проверкой размера бокса.
Расчёт скорости Пиксельное смещение на fisheye нельзя прямо переводить в метры — пространство нелинейно. Для расчёта скорости нужно евклидово пространство, поэтому здесь и только здесь применяется undistort — не полный рендер кадра, а точечная трансформация координат. Конвейер расчёта:
- Сглаживание bbox — отдельно позиция центра и размер. Разделение убирает дрожание детекций, но сохраняет отзывчивость на реальное перемещение.
- Undistort bbox — четыре угла сглаженного бокса переводятся в выпрямленное пространство.
- Расчёт footpoint — при виде сверху через fisheye ноги человека смещены к центру кадра. Используется алгоритм с экспоненциальным затуханием смещения от центра.
- Сглаживание footpoint через EMA, чтобы скорость не прыгала от кадра к кадру.
- Расчёт скорости — смещение footpoint между кадрами, умноженное на коэффициент пиксель/метр и делённое на dt.
- Обратная проекция — footpoint возвращается в искажённые координаты для проверки попадания в зоны.
События: от координат к бизнес-логике Финальное звено конвейера это бизнес логика. Оно получает список детекций с координатами и скоростью, проверяет попадание в зоны и генерирует семь типов событий:
| Событие |
Описание |
| visitor_enter |
Посетитель вошёл в зону магазина |
| visitor_exit |
Посетитель покинул зону |
| visitor_at_checkout |
Посетитель вошёл в зону кассы |
| rapid_movement |
Резкие действия посетителя (скорость более 5 м/с) |
| slow_movement |
Длительная остановка (скорость < 0.005 м/с за 5 сек) |
| long_stay |
Длительное нахождение в зоне (> 30 сек) |
| group_entry |
Групповой вход (3+ человек за 5 сек) |
Выход из зоны не генерируется мгновенно — через механизм PendingExit с временной задержкой. Если человек ушел за границу полигона и вернулся, это не засчитывается как выход. Без этой логики любое касание края зоны генерировало ложное событие. Проблемы Две сложности проявились сразу при работе с реальным потоком. Первая — счётчик посетителей. Короткие треки, когда человек зашёл в кадр и сразу скрылся за стеллажом, засчитывались как новые при каждом появлении. Добавили задержку: трек фиксируется как новый посетитель только после нескольких секунд устойчивого присутствия. Остаточный эффект — задержка до 5 секунд между фактическим входом и записью в базу. Вторая — настройка зон. Алгоритм теряет людей чаще, если граница зоны обрезает часть траектории: человек выходит за полигон на несколько кадров и трекер засчитывает это как выход. Для операторов на стороне клиента это оказалось неочевидным. Закрыли документацией с конкретными правилами расстановки зон под типовые планировки — зона должна перекрывать траекторию с запасом, не обрезая её по краю. Резюме Вместо того чтобы чинить fisheye-искажения для старых алгоритмов, мы выстроили архитектуру, которая умеет работать с искажениями:
- ReID-first подход. 85% решения об ассоциации по внешности. Даже если человек полностью пропал из виду и появился в другой части кадра, система узнаёт его по эмбеддингу.
- Адаптация к геометрии. Детекция и трекинг в искажённом пространстве. Выпрямление точечное, только для скорости. YOLO и ReID работают с теми данными, на которых обучались.
- Продвинутое управление ID. ReLink восстанавливает потерянные идентификаторы, Anti-Swap защищает от подмены, merge/split обрабатывает слияние и разделение людей.
- Observation-centric трекинг. OC-SORT не полагается на модель движения — он обновляет состояние трека по факту наблюдения. В толпе, где траектория непредсказуема, это работает надёжнее.
- Семь типов бизнес-событий с продуманной логикой задержек, warmup-периодов и дедупликации — превращают сырые координаты в полезную аналитику для ритейла.
Какой эффект дала система для заказчика? В рабочей конфигурации система снимает с оператора просмотр видео для подсчёта посетителей, определения времени пребывания у кассы и фиксации событий долгого стояния. Данные пишутся в базу в структурированном виде и подтягиваются в аналитику магазина. Что по-прежнему требует человека: первичная калибровка зон под конкретную камеру и планировку зала, разбор нестандартных инцидентов — система генерирует событие slow_movement, но интерпретация остаётся на операторе. Если вы работали с трекингом на нестандартных камерах — потолочных, панорамных, подводных — интересно услышать, какие решения использовали для удержания ID при длительных окклюзиях.-Источник
|