Блог

Как обучить ИИ распознавать товары на полках, часть 2: Детекция объектов

Продолжаем цикл наших статей про классификацию товаров в ритейле! Предыдущую статью можно найти здесь. Ее полезно прочитать для полного понимания контекста. Ниже - краткий обзор происходящего.

Задача

Перед нами стоит следующая задача: по фотографиям с камер, установленных в супермаркете, определить продукты на каждой полке. Зачем? Для аналитики, поиска пропавших товаров и повышения on-shelf availability.
Также раз в несколько дней одно фото с каждой камеры попадает в ручную разметку.
Пример фото с камеры ниже:

Типичное фото к с камеры и детекции от YOLO. В самом низу - пример камеры.

Пайплайн v0 (эмбеддер + search space) и v1 (v0 + realgram)

Самое первое решение v0 состояло из эмбеддера и search space:
  1. По детекциям YOLO вырезаем кропы.
  2. Каждый кроп пропускаем через эмбеддер и получаем векторное представление.
  3. Ищем ближайший вектор в Qdrant, где хранится search space с эмбеддингами и метаданными размеченных кропов.
  4. Берём top-1 по cosine similarity и присваиваем класс продукта.
Большой минус — мы учитываем только визуальный контекст, но не учитываем пространственный. Тем не менее, даже такой простой подход давал метрику классификации около 85%.

Решение v1 добавляло поверх поиска в Qdrant ещё и realgram — алгоритм, который учитывает то, что было на этой же полке в последних разметках. Если вкратце: для всех товаров из разметки считаем коэффициент устаревания и коэффициент пересечения по координатам, объединяем их и добавляем к cosine similarity из пайплайна v0. В итоге получаем более устойчивый алгоритм, учитывающий больше контекста и повышающий метрику до 92%.

Оставшиеся проблемы

Среди оставшихся проблем:
  • Качество всё ещё далеко от идеала: на сложных кропах, где плохое освещение или часть товара не видна, мы всё ещё предсказываем почти случайно, опираясь в основном на контекст разметок на полке;
  • Большое количество «миганий»: когда на соседних фото с одной камеры товар в каком-то месте не изменился, но предсказание для него меняется из-за волатильности топа search space. В основном эту проблему мы и будем решать в этой статье.

Object tracking

Что такое object tracking и зачем это нужно в нашей задаче

На первый взгляд object tracking плохо вписывается в нашу задачу: мы работаем не с видеопотоком, а со снимками. Давайте же разберемся, что это такое, а потом объясним, как это можно применить в нашей задаче.
Object tracking (отслеживание объектов) — это задача сопоставления одних и тех же объектов между разными кадрами. В классическом виде она решается на видеопотоках с высокой частотой кадров, где объект плавно перемещается, и алгоритм может опираться на два типа сигналов:
  • визуальные признаки объекта;
  • динамику его движения.

Главная цель трекинга — понять, какой объект в текущем кадре соответствует объекту из предыдущего кадра, даже если он немного изменил положение, масштаб или внешний вид.

С визуальными дескрипторами или без?

Существует два основных подхода: трекинг без визуальных дескрипторов и трекинг с ними.
В первую категорию входят алгоритмы вроде SORT, которые используют только детекции и простую модель движения (чаще всего фильтр Калмана). Такие методы хорошо работают, когда между кадрами происходят небольшие изменения и объект можно предсказать по его прошлой траектории. Они быстрые, лёгкие и отлично подходят для видеопотоков в реальном времени.
Когда визуальной или временной информации недостаточно, на помощь приходят методы с эмбеддингами, например DeepSORT. Добавление визуальных дескрипторов позволяет устойчиво сопоставлять детекции между кадрами даже при длительных пропусках, сложных перекрытиях или в ситуациях, когда модель движения даёт недостаточно сигнала. По сути, трекер сравнивает объекты не только по координатам, но и по «похожести» их внешнего вида.
Интересный вызов возникает в задачах, где кадры появляются редко, — например, раз в несколько минут или даже раз в полчаса. В таких условиях стандартные модели движения дают меньше полезного сигнала, и основной опорой становятся визуальные признаки.
Трекинг превращается в задачу сопоставления независимых снимков с похожими координатами: нужно правильно связать объекты, которые могли немного сдвинуться, исчезнуть или появиться вновь.
Поэтому особенно важными становятся качественные дескрипторы, устойчивые к изменению ракурса, освещения и положения объектов.
Что такое визуальные дескрипторы?
Визуальный дескриптор — это способ «перевести» изображение объекта в набор чисел, с которым можно работать математически.
Нейросеть берёт картинку и превращает её в вектор — компактное описание внешнего вида объекта.
Идея простая:
  • похожие объекты → похожие векторы,
  • разные объекты → разные векторы.

Например, одна и та же пачка молока, снятая под разным углом или при другом освещении, будет иметь близкие дескрипторы. А молоко и шоколадка — уже нет.

Решение v2: v1 + tracking

Применяем трекинг в нашей задаче

У нас возникла следующая гипотеза: на соседних кадрах с одной и той же камеры товары в одних и тех же координатах, как правило, меняются не сильно. Это усреднённое наблюдение — на разных камерах динамика отличается: например, в категории молочных продуктов движение товаров заметно выше, чем в других категориях.
Соответственно, если объект совпадает по координатам и визуально выглядит похоже, то с высокой вероятностью это тот же товар, что и на предыдущем кадре. Значит, мы можем добавить в пайплайн алгоритм сравнения координат и эмбеддингов на соседних изображениях и получить «трекинг на минималках».
Это должно:
  • повысить стабильность предсказаний,
  • уменьшить количество «миганий»,
  • потенциально улучшить качество классификации.
Пример того, как товары меняются в одних и тех же координатах на разных кадрах, показан ниже.

Столбец - одно фото с камеры, строка - один набор координат. Конкретно на этой камере товары со временем почти не меняются и выглядят почти идентично, так, что трекинг будет работать хорошо. Но так хорошо дела обстоят далеко не на всех камерах.

Модифицированный DeepSORT

Не хотелось бы изобретать велосипед, когда есть столько разных реализаций трекинга в open-source, поэтому по популярности и простоте интеграции мы остановили свой выбор на алгоритме DeepSORT.
В оригинальном DeepSORT сначала выполняется матчинг треков (уже существующих детекций на прошлых кадрах) по эмбеддингам (с предварительной фильтрацией нереалистичных детекций по координатам), а затем для оставшихся детекций применяется матчинг по IoU.
В нашем случае это не очень подходит по двум причинам:
  • Во-первых, на полке часто стоят несколько идентичных товаров с почти одинаковыми эмбеддингами, из-за чего легко сматчить неправильные объекты (например, первую бутылку колы на одном кадре со второй бутылкой колы на другом).
  • Во-вторых, товары в целом мало перемещаются, а требования к надёжности матчинга достаточно высокие, поэтому разделение IoU-матчинга и матчинга по эмбеддингам оказалось неэффективным — лучше учитывать оба сигнала одновременно.
В итоге мы адаптировали DeepSORT следующим образом: сначала выполняется матчинг по IoU, и только для детекций, которые совпали по IoU, дополнительно рассчитывается матчинг по эмбеддингам. Таким образом мы объединяем преимущества обоих подходов.
Разумеется, ложные срабатывания полностью не исчезли (например, вместо бутылки газированной воды поставили негазированную → IoU большой, cosine similarity высокая → итоговый скор высокий). Поэтому мы повысили пороги, чтобы матчить только действительно уверенные совпадения.
Ниже приведены блок-схемы оригинального DeepSORT (сверху) и модифицированного варианта (снизу).

Оригинальный DeepSORT: сначала матчинг по эмбеддингам (убирая нереалистичные фильтром Калмана), а для тех, что не сматчились по эмбеддингам - матчинг по IoU.

Наша версия DeepSORT: сначала матчинг по IoU, и только для тех, которые сматчились по IoU - матчинг по эмбеддингам (поскольку товары у нас особо не двигаются и нам важнее визуальные дескрипторы).

Пайплайн v2: добавляем трекинг

Пайплайн v1 оставляем без изменений: строим эмбеддинги, идём в Qdrant за ближайшими кропами, считаем realgram и получаем промежуточное предсказание.
Следующим этапом мы ищем последнюю неиспользованную разметку с этой камеры (каждую разметку используем только один раз) и выполняем трекинг между детекциями на размеченном изображении и текущими детекциями.
Про «последнюю неиспользованную разметку».
Делать трекинг между кадрами — хорошая идея, но если источником для трекинга становится обычное предсказание пайплайна (с качеством, например, 90%), то в 10% случаев трекинг будет «тащить» ошибку дальше. Чтобы уменьшить этот эффект, мы используем разметку как более надёжный источник.
На каждом кадре мы проверяем наличие разметки. Когда появляется новая разметка, мы один раз запускаем трекинг от неё и затем продолжаем трекинг на промежуточных кадрах. Да, с каждым кадром число треков, созданных из разметки (мы называем их «якорными»), уменьшается, но в зависимости от порогов между разметками сохраняется от 20% до 50% «якорных» треков.
Далее в пайплайне возникает развилка:

  • Если скор трекинга (IoU между боксами × cosine similarity между эмбеддингами) выше, чем скор промежуточного предсказания минус константа (гиперпараметр, определяющий уровень доверия к трекингу), мы берём предсказание из предыдущего кадра.
  • Иначе берём промежуточное предсказание и инициализируем трек этим предсказанием.

Пайплайн v1 выглядит примерно так: из search space и realgram получаем промежуточные предсказания; получаем предсказания из трекинга; выбираем из них по скору с threshold.

Как видим, помимо одного гиперпараметра для выбора между промежуточным предсказанием и результатом трекинга, в самом трекинге появляются дополнительные параметры (IoU threshold, IoU × cosine threshold и другие). В итоге число гиперпараметров продолжает расти.

Тем не менее результат есть: метрика (способ её расчёта описан в предыдущей статье) выросла с 92% до 94%, а количество «миганий» снизилось примерно вдвое.

Что касается метрики «миганий», на тот момент мы не вводили отдельную формальную метрику, поэтому оценка их снижения носит качественный характер. Однако эффект хорошо объясним: после добавления трекинга предсказание на текущем кадре значительно чаще наследуется от предыдущего, тогда как раньше соседние кадры обрабатывались независимо. В результате совпадение предсказаний между соседними кадрами стало происходить заметно чаще, что визуально снижает количество «миганий».

Результаты

Мы продолжили развивать baseline с эмбеддером и realgram и добавили трекинг. В результате качество выросло с 92% до 94%, а предсказания стали заметно стабильнее: количество «миганий» снизилось примерно на 50%.
Однако у версии v2 остаются существенные проблемы:
  • Рост числа гиперпараметров. У realgram их было около 10, трекинг добавил ещё примерно 5. Даже Optuna перестала эффективно справляться с поиском, а риск оверфиттинга вырос, поскольку подбор параметров каждый раз выполняется на ограниченном датасете.
  • False positive трекинга. Даже при высоком скоре трекинг может «тащить» ошибочные предсказания. Например, в тех же координатах один товар может смениться другим вкусом или ароматом, визуально почти не отличимым. В такой ситуации трекер легко подхватывает неправильный класс.
  • Усложнение выбора между кандидатами. С добавлением трекинга мы ещё больше усложнили проблему выбора: теперь у нас есть множество кандидатов — из top search space, realgram и трекинга. Их может быть один, а может быть двадцать. Как выбирать между ними?
Часть этих ограничений решается добавлением классификатора второго уровня. Именно о нём мы расскажем в следующей статье.
Огромное спасибо нашим инженерам, Александру Коротаевскому и Артему Сметанину, за подготовленный материал.
Компьютерное зрение ИИ Видео