Трекеры на основе признаков очень часто встречаются в индустрии глубокого обучения. Хорошо известные модели DNN с изучением признаков, такие как OSNet, генерируют однородные векторы повторной идентификации (Re-ID). Эти векторы можно оценить с помощью метрик расстояния, таких как евклидово, косинусное или другое. Векторы признаков, выведенные для одного объекта, генерируют результаты расстояния, которые лежат близко. В отличие от трекера IoU, показанного в моей предыдущей статье, трекеры на основе признаков Re-ID работают с однородными векторами, представляющими объект. Элемент вектора не имеет значения с точки зрения принятия решений, поэтому невозможно разработать алгоритм, анализирующий части векторов как самостоятельные элементы.

Чтобы продемонстрировать, как это работает в упрощенном 2D-пространстве, взгляните на следующую картинку.

На картинке вы можете видеть 3 объекта и их векторы признаков Re-ID. Модель Re-ID эффективна, когда результирующие векторы для одного и того же объекта лежат близко друг к другу.

Часто бывают выбросы — их можно увидеть за пределами эллиптических зон. Выбросы неизбежны и снижают качество Re-ID. Для уменьшения влияния выбросов применяются различные методы. Одна из них — оценка качества Re-ID — дополнительная модель, оценивающая качество Re-ID. Например, он может дать более высокий балл, когда вектор Re-ID выводится для большого, хорошо наблюдаемого объекта, и более низкий балл, когда объект наблюдается частично и в неопределенных условиях. Другой подход к уменьшению влияния выбросов — сравнение расстояний до последних N исторических наблюдений (не с последним) — это помогает реализовать взвешенное голосование, уменьшающее влияние выбросов.

Есть много трюков с Re-ID, но тема слишком широка, чтобы обсуждать ее здесь. Я хотел бы, чтобы читатели поняли интуицию о векторах признаков Re-ID. Приведенный выше пример функции демонстрирует Re-ID в 2D-пространстве. Положение точки показывает, насколько данная точка похожа на экземпляр X (1,0) и экземпляр Y (0,1). То же правило применяется к реальным векторам признаков Re-ID большой длины — 256, 512 или даже больше. Каждое значение в каждой позиции вектора показывает, насколько наблюдаемый объект похож на объект с вектором признаков Re-ID, состоящим только из нулей, за исключением 1.0 в этой позиции (вероятно, объект, используемый во время начального обучения DNN).

Попарные расстояния векторов можно вычислить с помощью известных методов, таких как Евклидов, Косинус, Махаланобиса и другие. Какое расстояние использовать, во многом зависит от поведения Re-ID и требует экспериментов. Евклидово расстояние используется часто, поэтому мы будем использовать его для демонстрации трекера.

Дизайн трекера

Я продемонстрирую пример трекера, который строит треки на основе подобия Re-ID. На каждом шаге трекер получает векторы признаков Re-ID для наблюдаемых объектов. Каждый новый вектор признаков сравнивается с векторами признаков треков, хранящихся в хранилище треков, с использованием евклидового расстояния. В хранилище дорожек хранятся 5 последних векторов признаков каждой дорожки, чтобы уменьшить влияние выбросов. Итак, для каждого нового вектора признаков и дорожки в магазине мы получаем 5 расстояний, которые показывают, насколько новый близок к таковым для дорожки. Мы получаем до 5 x N расстояний для всех гусениц, хранящихся в магазине.

Мы каким-то образом должны найти кандидата на слияние, когда расстояния собраны. Для этого мы сначала отфильтровываем результаты с дальним расстоянием, считаем их по треку и сортируем по убыванию количества голосов. Первый результат — это кандидат на дорожку, где мы объединяем новый вектор признаков. Если результат пуст, вектор признаков формирует новую дорожку.

Вышеприведенный алгоритм является важной частью реальных алгоритмов отслеживания, которые сочетают подход на основе признаков, подход IoU и другие эвристики для обеспечения надежного отслеживания. В примере мы используем дорожку с одним новым вектором признаков для оценки кандидата на слияние. Вместо этого можно использовать собранный трек для объединения коротких треков в более длинный, когда трек объекта разбит на части из-за переключений или когда реализовано многокамерное отслеживание.

Вы можете задаться вопросом, где можно использовать такой трекер? Если вы можете использовать дополнительные эвристики, такие как IoU и другие атрибуты объекта, вам следует использовать их, так как это ведет к лучшему отслеживанию. Однако бывают случаи, когда задачу трекинга необходимо решать исключительно с помощью трекинга на основе признаков.

Один из очевидных случаев — когда нельзя часто наблюдать за объектом, поэтому IoU нельзя использовать. Например, вы используете Intel Movidius Myriad 2 для сбора биометрической информации — в реальных сценариях он может обрабатывать 4–5 кадров в секунду. Такой FPS приводит к плохому качеству IoU, в то время как вы все еще можете получить отличные результаты Re-ID.

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

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

Рекомендации по реализации

Вы, наверное, заметили, что трекер использует много вычислений. Чтобы реализовать это эффективно, трекер должен использовать многоядерную архитектуру и эффективно обрабатывать расчет расстояния. Эти функции встроены в Симилари — фреймворк, предназначенный в первую очередь для создания высокопроизводительных трекеров с малой задержкой. Similari разработан на Rust. Similari использует инструкции ЦП SIMD при работе с векторами признаков для быстрой обработки вычислений.

Репозиторий Similari содержит бенчмарк для трекера Re-ID, который мы будем реализовывать. Давайте посмотрим на цифры.

Контрольный показатель функции (256 @ f32) отслеживания для N одновременно наблюдаемых объектов выполняется на 4 ядрах процессора Intel(R) Core(TM) i5–7440HQ с тактовой частотой 2,80 ГГц. Бенчмарк не использует эвристики, которые разделяют наблюдаемые объекты на основе расстояния до объектов.

Бенчмарк находится по адресу benches/feature_tracker.rs.

10 objects:       101,465 ns/iter (+/- 10,056)         [9900 FPS]
100 objects:    4,020,673 ns/iter (+/- 877,444)        [ 250 FPS]
500 objects:   61,716,729 ns/iter (+/- 11,215,929)     [  16 FPS]
1000 objects: 235,187,877 ns/iter (+/- 89,734,978)     [   4 FPS]

Вы можете видеть, что до 500 объектов можно отслеживать в режиме реального времени на современном процессоре.

Реализация трекера

Давайте реализуем трекер поэтапно. Давайте начнем с основных концепций Similari, которые мы должны знать перед внедрением трекера.

Similari определяет следующие основные объекты:

  • Track — объект, представляющий трек объекта. Трек содержит атрибуты трека и наблюдения.
  • Атрибуты дорожки — настраиваемая часть дорожки, содержащая информацию о дорожке для конкретного домена. В нашем случае мы будем собирать ограничивающие рамки в атрибутах отслеживания.
  • Наблюдение — объект, представляющий наблюдаемые особенности объекта, помещенные на дорожку. Наблюдение содержит необязательный настраиваемый объект Атрибуты наблюдения и необязательный однородный вектор признаков. В нашем случае объект Атрибуты наблюдения будет представлен качеством Re-ID (F32), а Вектор признаков будет равен 2 @ Ф32.
  • Класс объектов — наблюдение связано с классом объектов. Трек может собирать различные наблюдения в различных классах объектов. В нашем случае мы будем использовать один класс объектов — FEAT0.
  • Метрика отслеживания — объект, выполняющий 3 роли: вычисляет метрики для Наблюдений, оптимизирует Наблюдения при построении отслеживания и отфильтровывает результаты после расчета метрик.
  • Track Store — эффективная параллельная база данных, которая выполняет поиск, расчет расстояния и объединение треков.
  • Механизм голосования — алгоритм, который выбирает кандидатов на слияние для вычисляемых показателей.

Начнем с кодирования. Во-первых, давайте определим атрибуты отслеживания.

Чтобы обновить атрибуты дорожки, мы должны определить объект обновления:

Пока объекты создаются, они не имеют определенного поведения и не могут использоваться в Similari. Чтобы это исправить, давайте реализуем для них необходимые трейты. Во-первых, черта TrackAttributesUpdate для BBoxAttributesUpdate . Черта определяет метод apply, который используется для обновления атрибутов дорожки. Он реализован следующим образом:

Далее давайте определим требуемый трейт для BBoxAttributes, чтобы он работал с Similari:

Здесь есть 3 метода:

  • compatible используется, когда треки выбраны для расчета расстояния. Несовместимые треки пропускаются;
  • merge используется для объединения атрибутов при объединении дорожек — в нашем случае мы объединяем их ограничивающие рамки;
  • baked используется для указания хранилища, что трек, хранящийся в хранилище, может быть выбран для расчета расстояния, если вызывающему абоненту нужны только baked треков; добавление трека может быть потрачено впустую, если каким-то образом он стал непригодным для использования, например, он не смог собрать достаточно наблюдений во время последних итераций.

Следующим шагом является определение объекта Track Metric:

Это немного сложно, но давайте рассмотрим метод за методом, чтобы понять, что происходит.

Первый метод metric . Он вычисляет метрики (расстояния) между наблюдениями двух сравниваемых треков. Он может вернуться

  • None если наблюдения несопоставимы или
  • Some((Option<ObservationAttributes::MetricObject>, Option<f32>)) если сравнение произошло.

В нашем случае ObservationAttributes — это F32, и они предопределены — представьте, что это параметр качества Re-ID. Однако на самом деле мы не используем его в голосовании, ObservationAttributes::MetricObject также является F32. А f32::calculate_metric_object всего лишь abs(x-y).

Мы ищем расчет евклидова расстояния для векторов Re-ID, и он тоже есть.

Следующий метод — optimize. Он используется после объединения двух дорожек или добавления наблюдения к дорожке. Цель оптимизации состоит в том, чтобы сохранить треки в здравом уме — в нашем случае мы просто оставляем до 5 последних наблюдений.

Последний postprocess_distances . Этот метод используется для фильтрации расстояний от выбросов перед передачей данных в механизм голосования. Это метод оптимизации, который более эффективен, чем выполнение его позже в механизме голосования. В нашем случае мы просто убираем расстояния, которые точно не подходят.

Все необходимые объекты определены. Перейдем к образцу трекера. Во-первых, давайте посмотрим на весь демо-трекер:

То, что там происходит, довольно просто, но давайте рассмотрим важные части отдельно:

Здесь вы можете увидеть генерацию Re-ID и ограничивающую рамку. Далее создается новый трек, и в него добавляется наблюдение. Шаги повторяются для obj1t и obj2t.

Следующая важная часть — голосование:

Обратите внимание на строку 6 — если нет кандидатов на слияние, то трек добавляется в магазин как новый, иначе трек объединяется с кандидатом.

Полный код доступен в репозитории Similari.

Заключение

Мы создали очень простой трекер, который использует сходство признаков для сопоставления объектов. Трекер можно значительно улучшить, добавив оценку IoU из предыдущей статьи.

Другие образцы трекеров вы можете найти в разделе примеры репозитория Similari:

  • Частичный трек двигатель слияния на основе сходства признаков (евклидово расстояние);
  • Пример простой поисковой системы (но лучше использовать для этого HNSW).

Кроме того, в репозитории Similari есть тесты производительности, которые можно запустить с помощью ночного компилятора Rust.

Если вам нравится Similari, пожалуйста, оцените репозиторий :). Как автор, я был бы очень признателен за отзывы и вопросы о Similari.