В статье рассматривается новый открытый фреймворк для потоковой видеоаналитики и демонстрируются его возможности на примере демонстрационного приложения, которое использует модель DeepStream’s PeopleNet для обнаружения людей и их лиц, размывает лица и отображает панель управления с помощью OpenCV CUDA.

Мы будем использовать Savant для обработки видео в реальном времени с протоколом RTSP и для обработки видеофайлов в пакетном режиме, чтобы продемонстрировать, как конвейер может достигать скорости 400 кадров в секунду на Nvidia Tesla T4.

Для тех, кто хочет сначала попробовать без подробностей, мы подготовили скрипты для быстрого старта на основе Docker Compose (раздел "Быстрый старт").

Savant на GitHub

Я директор продукта и представляю коллектив разработчиков фреймворка, буду рад ответить на любые вопросы. Статья является переводом моей же статьи на английском на Medium, я постарался адаптировать текст, но заранее прошу прощения за возможные киноляпы.

О Фреймворке Savant

Savant (от французского — «ученый») — это высокоуровневый фреймворк для видеоаналитики, созданный на основе Nvidia DeepStream. Он позволяет создавать пайплайны компьютерного зрения для видеоданных любого типа (файлы, потоковые трансляции и наборы изображений) с помощью YAML и дополнительных функций обработки на языке Python.

Savant предоставляет разработчикам несколько значимых функций, которых нет в базовом функционале Nvidia DeepStream; давайте кратко обсудим некоторые из них.

Адаптеры. В базовом DeepStream данные потоков тесно связаны с конвейером. Это означает, что если источник или назначение выходят из строя, то и весь конвейер прекращает работу; после этого может потребоваться несколько секунд, чтобы снова восстановить работу конвейера, поскольку перезапуск тяжеловесных конвейеров с моделями занимает значительное количество времени, и данные теряются за это время. В Savant адаптеры общаются с фреймворком через открытый потоковый API на основе ZeroMQ и Avro. Это позволяет адаптерам выходить из строя без влияния на работу конвейера.

Мультиплексирование данных. Savant позволяет автоматически обрабатывать несколько видеопотоков, которые могут динамически появляться и исчезать: вы запускаете модуль обработки и отправляете данные в него, указывая уникальный идентификатор источника. Фреймворк правильно обрабатывает данные, разделяя их на отдельные потоки.

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

Готовность к использованию в продуктиве. Savant общается с адаптерами через открытое API, а сам фреймворк работает в контейнере Docker. Это означает, что вы можете легко развернуть его в K8s или Docker Compose и использовать адаптеры для подключения к вашей инфраструктуре управления данными. Мы предоставляем несколько готовых к использованию адаптеров, которые могут служить основой для реализации ваших собственных.

Совместимость между Edge и серверным оборудованием. Мы преодолели ряд проблем, чтобы обеспечить совместимость с различным оборудованием Nvidia. Хотя Nvidia стремится предоставить единый интерфейс для всех устройств, значительные различия требуют адаптации под конкретные устройства. Мы предоставляем Docker‑контейнеры как для Jetson, так и для x86, которые уже совместимы с оборудованием.

Поддержка OpenCV CUDA. С этой интеграцией вы можете эффективно работать с кадрами видео в памяти GPU на Python без передачи их в память CPU. Эта функция определенно порадует тех, кто выполняет нетривиальные видео преобразования, рисует дашборды и использует сложные модели, которые требуют преобразований объектов (например, обработка лиц с лэндмарками). В то время как Nvidia предоставляет байндинги Python для мэппинга кадров видео в память CPU, OpenCV CUDA часто работает намного быстрее во многих случаях, например, при размытии лиц или номерных знаков.

Savant предлагает множество других полезных функций. Вы можете найти более подробную информацию о них на сайте проекта.

Почему просто не использовать DeepStream?

Мы разрабатываем приложения для фреймворка DeepStream уже несколько лет и за это время столкнулись с рядом проблем при реализации решений, подходящих для использования в продуктиве: от разработчика требуется глубокое понимание GStreamer — фреймворка обработки мультимедиа с открытым исходным кодом. Тем временем, GStreamer является относительно низкоуровневым фреймворком, построенным на экосистеме GObject, и часто требует реализации функционала на C/C++, а также обработки множества событий и сигналов.

Тем не менее, GStreamer оптимально подходит для создания высокопроизводительных систем обработки видео. Об этом свидетельствует тот факт, что ведущие производители железа, такие как Nvidia, AMD (Xilinx) и Intel, разрабатывают свои фреймворки на основе GStreamer в качестве плагинов.

В итоге, барьер входа для DeepStream высок. От разработчика требуются навыки програмирования GStreamer, умение писать плагины на C/C++, знания о том, как обрабатывать сигналы конвейера и понимание архитектуры системы. Если к предыдущим сложностям добавить дополнительные плагины, которые Nvidia разработала для оптимизации инференса на их оборудовании, то вы столкнетесь со многими препятствиями на пути к готовому к применению в продуктиве пайплайну.

Анализ видео с использованием PyTorch, OpenCV, TensorFlow

Мы знаем, что многие люди создают системы видеоаналитики с использованием других технологий. Они вычитывают видеофайлы или потоки с помощью OpenCV и передают растры изображений в PyTorch, и все как будто работает нормально.

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

Другими словами, вы можете потратить существенно большее количество денег и использовать в несколько раз больше аппаратного обеспечения, чтобы достичь той же производительности, которую вы могли бы достичь с помощью DeepStream. Стоит отметить, что некоторые конвейеры не могут работать в принципе или демонстрируют плохую производительность на Nvidia Jetson - широко используемых edge-устройствах - при использовании PyTorch или других неоптимизированных технологий.

В общем, каждый раз, когда вы делаете видео-аналитику с помощью PyTorch, TensorFlow или другого подобного фреймворка, где-то в мире плачет котик.

Почему стоит рассмотреть Savant

Вам стоит попробовать Savant, потому что:

  • с его помощью действительно легко создавать и запускать конвейеры обработки видео высокой производительности как для дискретных GPU, так и для периферийных устройств;

  • легко расширять и настраивать их под свои нужды;

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

Мы переходим к нашему демонстрационному приложению, которое поможет вам познакомиться с технологией.

Демонстрационное приложение

Теперь, когда у вас есть общее представление о Savant, мы приглашаем вас исследовать приложение, которое мы разработали, чтобы продемонстрировать возможности фреймворка.

Приложение с помощью модели PeopleNet в видеопотоке обнаруживает людей и их лиц, а затем эти лица размываются с помощью OpenCV CUDA. Дополнительно, чтобы продемонстрировать OpenCV CUDA, приложение отображает анимированную панель с количеством людей с обнаруженными лицами и без них. Кроме того, для снижения дрожания рамок используется трекер. И вся эта красота вам доступна на скорости ~ 400 FPS и выше для HD-видео на современном железе. Демонстрационная схема конвейера представлена ниже.

Мы покажем вам, как использовать Savant в реальных сценариях с видеопотоками с камер, транслировать результаты конвейера через RTSP и продемонстрируем производительность конвейера в режиме пакетной обработки на локальных видеофайлах.

Код для демонстрации находится в каталоге проекта с примерами на GitHub samples/peoplenet_detector.

Требования к рабочей среде

Хорошее интернет-соединение является важным: демонстрация воспроизводит видеофайлы непосредственно из Интернета, поэтому медленное интернет-соединение может вызвать залипание поткоа. Попробуйте открыть эту ссылку в браузере, чтобы проверить, воспроизводится ли видео без проблем.

Для запуска демонстрации вам понадобится корректно функционирующее окружение с поддержкой ускоренных вычислений на аппаратных средствах Nvidia. Пожалуйста, прочтите краткое руководство по настройке Ubuntu 22.04 для поддержки Savant.

Требования к среде на базе x86: Nvidia dGPU (Volta, Turing, Ampere, Ada Lovelace), ОС Linux с драйвером 525+, Docker с установленным и настроенным плагином Compose и Nvidia Container Runtime.

Требования к среде на базе Jetson: Nvidia Jetson (NX/AGX, Orin NX/Nano/AGX) с JetPack 5.1+ (фреймворк не поддерживает первое поколение Jetson Nano), Docker с установленным и настроенным плагином Compose и Nvidia Container Runtime.

Проверка совместимости окружения

Savant и адаптеры запускаются как контейнеры Docker. Поскольку мы используем ускоренные вычисления Nvidia, Nvidia Container Runtime должен быть настроен правильно. Устройства Jetson не должны иметь проблем, если вы установили и настроили последнюю операционную систему в соответствии с рекомендациями Nvidia.

Клонируйте проект Savant, чтобы получить доступ к исходному коду:

git clone https://github.com/insight-platform/Savant.git
cd Savant
git lfs pull
cd ..

Запустите следующую команду, чтобы убедиться, что среда совместима:

./Savant/utils/check-environment-compatible

Вы должны увидеть сообщение о том, что среда в порядке:

Environment is OK

Если вывод соответствует ожиданиям, можно продолжать; иначе необходимо корректно настроить среду перед продолжением.

Быстрый старт

Если вы хотите сначала попробовать Savant, прежде чем потратить на него свое время используйте docker compose в директории samples/peoplenet_detector, чтобы запустить конвейер все-в-одном:

git clone https://github.com/insight-platform/Savant.git
cd Savant/samples/peoplenet_detector
git lfs pull

# если хотите поделиться своей геопозицией с нами, запустите
# следующую команду (это опционально), мы просто хотим узнать о 
# том, что вы попробовали Savant
#
curl --silent -O -- https://hello.savant.video/peoplenet.html

# if x86
../../utils/check-environment-compatible && \
   docker compose -f docker-compose.x86.yml up

# if Jetson
../../utils/check-environment-compatible && \
   docker compose -f docker-compose.l4t.yml up

# Ctrl+C to stop running the compose bundle

# to get back to the project root
cd ../..

Запуск конвейера в первый раз может занять несколько секунд из-за компиляции модели в формат TensorRT, что занимает время.

Теперь откройте URL rtsp://127.0.0.1:8554/city-traffic-processed в вашем любимом медиаплеере (мы рекомендуем VLC), и вы должны увидеть результирующее видео как представлено ниже:

В качестве альтернативы вы можете использовать ваш любимый браузер для доступа к потоку, открыв http://127.0.0.1:8888/city-traffic-processed/. Однако трансляция HLS добавляет задержку в несколько секунд.

Чтобы завершить демонстрацию, нажмите Ctrl+C. Теперь вы готовы углубиться в подробности о том, как выглядят и работают модули и адаптеры Savant.

Как модуль Savant взаимодействует с миром

В Savant, когда он используется в продуктиве, модуль аналитики взаимодействует с внешними системами, используя потоковый API, основанный на ZeroMQ и Apache Avro.

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

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

Адаптер — это специальное приложение, которое отправляет данные в модуль или получает результаты из модуля, используя протокол Savant. Такая архитектура делает систему устойчивой к отказам источника или приемника: адаптеры могут иметь проблемы, но модуль аналитики будет продолжать работать. Кроме того, концепция адаптера позволяет абстрагировать обработку от типа источника данных — вы можете отправлять файлы, непрерывные видеопотоки (например, RTSP), наборы изображений и т. д. в модуль.

В этой статье мы будем использовать три адаптера, которые поставляются с фреймворком:

  • Video File Source для обработки видеофайлов и коллекций видеофайлов;

  • RTSP Source для обработки входящих потоков RTSP;

  • Always-On RTSP Sink для трансляции результатов по RTSP.

Знакомство с адаптерами

Always‑On RTSP Sink — это выходной адаптер, который транслирует по RTSP видеопоток, состоящий либо из кадров, полученных из источника, либо из замещающего изображения с отметкой времени при ожидании новых кадров.

Таким образом, этот адаптер генерирует непрерывный RTSP-поток независимо от наличия правильно работающего источника, переключаясь между отображением кадров и замещающим изображением на лету.

Адаптер Always-On RTSP Sink требует поддержку аппаратных энкодеров и декодеров Nvidia, что связано с тем, что он всегда выполняет транскодинг видео внутри себя.

Архитектура адаптера для любопытных
Архитектура адаптера для любопытных

Мы создали этот адаптер, во многом, для демонстрационных целей, но поскольку на профессиональных GPU транскодирование по сути бесплатное и позволяет получить пару тысяч FPS, то его вполне можно использовать и в продуктиве.

Идея на миллион: Часто, люди, которым надо передать RTSP куда‑то далеко по публичной сети не знаю как это сделать, чтобы картинка не сыпалась. Вы можете использовать пару адаптеров RTSP Source и Always‑On RTSP Sink для передачи RTSP‑потоков по длинным публичным каналам с пляшущим джиттером — все будет проигрываться существенно лучше, чем получать доступ по RTSP напрямую — потоки не будут сыпаться, если будут задержки фреймов, будет показываться картинка.

Адаптер работает по принципу RTSP push и поэтому требует RTSP-сервер для широковещательной трансляции видео конечным пользователям; мы будем использовать надежный и открытый медиа-сервер MediaMTX.

# You are expected to be in Savant/ directory

git clone https://github.com/insight-platform/Fake-RTSP-Stream
cd Fake-RTSP-Stream && docker compose up -d && cd ..

# если хотите поделиться своей геопозицией с нами, запустите
# следующую команду (это опционально), мы просто хотим узнать о 
# том, что вы попробовали Savant
#
curl --silent -O -- https://hello.savant.video/peoplenet.html

Сейчас мы протестируем адаптер Always-On RTSP в связке с адаптером Video File Source, устанавливая прямое соединение между ними без промежуточного аналитического модуля (протокол Savant позволяет осуществить это):

Теперь давайте запустим адаптер и убедимся, что он работает:

# You are expected to be in Savant/ directory

docker run --gpus=all --rm -it \
--add-host=gw:host-gateway \
-e ZMQ_ENDPOINT=ipc:///tmp/zmq-sockets/video.ipc \
-e ZMQ_TYPE=ROUTER \
-e ZMQ_BIND=True \
-e SOURCE_ID=camera1 \
-e STUB_FILE_LOCATION=/stub_img/smpte100_1280x900.jpeg \
-e RTSP_URI=rtsp://gw:8554/stream1 \
-v `pwd`/samples/stub_imgs/:/stub_img/ \
-v /tmp/zmq-sockets:/tmp/zmq-sockets \
ghcr.io/insight-platform/savant-adapters-deepstream:0.2.0-6.2 \
python -m adapters.ds.sinks.always_on_rtsp

Заметка для Jetson: Если вы запускаете код на Jetson, измените имя образа на “*-l4t”: “savant-adapters-deepstream” должен быть заменен на “savant-adapters-deepstream-l4t”.

Обратите внимание на следующие переменные:

  • ZMQ_ENDPOINT, ZMQ_TYPE и ZMQ_BIND определяют сокет, используемый для соединения; значения, в сочетании с соответствующими параметрами входного адаптера, должны формировать допустимую схему подключения;

  • SOURCE_ID определяет какой поток будет транслироваться RTSP; на практике, в некоторых случаях, кадры из разных источников могут приходить на сокет одновременно;

  • STUB_FILE_LOCATION задает путь к замещающему изображению;

  • RTSP_URI указывает URI, по которому будет публиковаться видео-поток.

После запуска адаптера можно уже наблюдать поток RTSP, открыв URL rtsp://127.0.0.1:8554/stream1. Так как в потоке пока еще нет реальных данных, вы должны увидеть замещающее изображение.

В качестве альтернативы доступа к потоку можно использовать браузер, открыв http://127.0.0.1:8888/stream1/; однако, HLS вносит задержку в несколько секунд - не рекомендуем.

Теперь давайте отправим видеофайл в Always-On RTSP Sink, используя адаптер Video File Source, чтобы продемонстрировать, как Always-On RTSP Sink отобразит его содержимое.

Перед отправкой откройте плеер VLC (или браузер) на видимой части экрана, чтобы увидеть, как адаптер будет транслировать содержимое файла через RTSP.

Запустите трансляцию содержимого видео-файла с помощью адаптера Video File Source командой:

# You are expected to be in Savant/ directory

docker run --rm -it \
--entrypoint /opt/app/adapters/gst/sources/media_files.sh \
-e ZMQ_ENDPOINT=ipc:///tmp/zmq-sockets/video.ipc \
-e ZMQ_TYPE=DEALER \
-e ZMQ_BIND=False \
-e READ_METADATA=False \
-e SYNC_OUTPUT=True \
-e SOURCE_ID=camera1 \
-e LOCATION=https://eu-central-1.linodeobjects.com/savant-data/demo/Free_City_Street_Footage.mp4 \
-e FILE_TYPE=video \
-v /tmp/zmq-sockets:/tmp/zmq-sockets \
ghcr.io/insight-platform/savant-adapters-gstreamer:0.2.0

Заметка для Jetson: Если вы работаете на устройстве Jetson, измените имя образа на "*-l4t": "savant-adapters-gstreamer" должен быть изменен на "savant-adapters-gstreamer-l4t".

Обратите внимание на следующие переменные среды:

  • SYNC_OUTPUT указывает, что необходимо синхронизировать скорость отправки данных со скоростью FPS источника; если для вывода используется адаптер Always-On RTSP Sink, то без синхронизации адаптер просто "проглотит" часть фреймов, потому что они будут обрабатываться слишком быстро; чтобы этого не произошло, мы на стороне источника данных замедляем поток до требуемого FSP;

  • SOURCE_ID идентификатор источника; в данном случае он должен соответствовать соответствующему параметру, заданному для Always-On RTSP Sink;

  • LOCATION URL видео;

  • FILE_TYPE тип файлов, используя значения video или picture.

После запуска вы увидите содержимое видеофайла, проигрываемое в плеере. Когда файл будет отправлен полностью, вы снова увидите заглушку с изображением. Вы можете повторять этот эксперимент несколько раз, отправляя разные файлы в кодеках H.264 или HEVC.

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

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

Далее мы рассмотрим структуру модуля обработки для Savant.

Структура модуля обработки видео

Модуль обработки видео в рамках фреймворка Savant описывается файлом конфигурации в формате YAML (можно и с помощью Python его сконструировать, но мы здесь это не рассматриваем). Модуль запускается в контейнере Docker и взаимодействует с внешним миром через адаптеры.

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

Давайте кратко рассмотрим содержание каждого раздела файла конфигурации.

Раздел "parameters"

Конфигурационный файл начинается с названия конвейера и общих параметров. Они определяют разрешение, до которого масштабируются обработываемые кадры из всех потоков, с возможностью расширения размера "холста" кадра путем указания отступов.

name: demo
parameters:
  frame:
    width: 1280
    height: 720
    padding:
      keep: true
      left: 0
      right: 0
      top: 180
      bottom: 0
  output_frame:
    codec: raw-rgba
  draw_func:
    module: samples.peoplenet_detector.overlay
    class_name: Overlay
    kwargs:
      # kwargs are omitted for the sake of briefness

В демонстрации в верхней части кадра отображается информационная панель-дашборд, для которой определен отступ сверху в 180 пикселей. В результате этой манипуляции выходной кадр будет иметь разрешение 1280x900.

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

Рисование в Savant не использует низкоуровневый API DeepStream, поддерживает больше примитивов (в том числе повернутые прямоугольные рамки и произвольные многоугольники) и, благодаря реализации через OpenCV CUDA, сохраняет эффективность, работая непосредственно с видеопамятью и обеспечивая доступ к эффективно реализованным операциям, таким как гауссово размытие. Кроме того, возможно расширение функции рисования, если требуются дополнительные операции, которые мы не предусмотрели.

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

Кроме того, в этой секции задается кодек выходного кадра. Если он не задан, то кадры не отправляются на выход, только метаданные — это используется в пайплайнах, где конструирование выходного видео не требуется.

В продуктовой среде следует использовать тип кодирования hevc или h264, однако здесь мы используем raw-rgba для обеспечения совместимости с видеокартами GeForce, которые имеют ограничение на количество одновременно кодируемых видеопотоков равное трем.

В общем, у нас тут тоже плачет котенок, потому что передача raw-rgba из памяти GPU в CPU легко снижает скорость работы на 40-50%. Однако, поскольку у большинства людей на рабочих машинах GeForce, а не Quadro или Tesla, мы оставили raw-rgba. В бенчмарке производительности мы используем кодирование в h264.

Раздел "pipeline"

Раздел "pipeline" содержит элементы конвейера, включая источники данных, выходные элементы и элементы обработки. Если источник и выход не заданы, используются элементы по умолчанию на основе ZeroMQ и Apache Avro.

Давайте рассмотрим следующую диаграмму, чтобы вспомнить структуру конвейера:

Функциональные блоки (желтые и синие) задаются в разделе pipeline.elements, как показано в следующем примере кода:

pipeline:
  elements:
    - element: nvinfer@detector
      ...
    - element: nvtracker
      ...
    - element: pyfunc
      ...

Таким образом, упомянутый выше конвейер включает в себя детектор людей и лиц, за которым следует трекер людей, а затем настраиваемая функция Python.

Каждый элемент имеет свои специфические параметры конфигурации и отвечает за выполнение определенной задачи, такой как обнаружение объектов, отслеживание или обработка изображений. Для создания полного конвейера обработки видео вы задаете несколько элементов в разделе pipeline.elements, а фреймворк обеспечивает продвижение элементов потоков данных между ними.

Давайте более подробно рассмотрим эти элементы обработки.

Детектор на основе NvInfer

Первым в последовательности элементов является элемент Nvinfer из DeepStream, который обеспечивает детектцию лиц и людей с помощью модели PeopleNet. Параметры модели могут быть указаны непосредственно в файле YAML.

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

- element: nvinfer@detector
  name: peoplenet
  model:
    format: etlt
    remote:
      # where to download model
      ...
    model_file: resnet34_peoplenet_pruned.etlt

Фреймворк поддерживает два способа работы с файлами моделей:

  1. Локальная упаковка в образ во время сборки контейнера или мэппинг с помощью отображения каталогов хоста в образ;

  2. Загрузка моделей удаленно при первом запуске контейнера (в этом случае требуется постоянный том Docker, чтобы загруженные модели не исчезали при перезапуске контейнера).

В демонстрации используется метод удаленной загрузки (AWS S3), поэтому настраивается раздел remote, где указываются параметры загрузки.

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

# continuing PeopleNet config
input:
  layer_name: input_1
  shape: [3, 544, 960]
  scale_factor: 0.0039215697906911373

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

# continuing peoplenet config
output:
  layer_names: [output_bbox/BiasAdd, output_cov/Sigmoid]
  num_detected_classes: 3
  objects:
    - class_id: 0
      label: person
      selector:
        kwargs:
          min_width: 32
          min_height: 32
    - class_id: 2
      label: face
      selector:
        kwargs:
          confidence_threshold: 0.1

Элемент selector выполняет дополнительную фильтрацию внутри класса: для класса person исключаются объекты с высотой или шириной менее 32 пикселей.

Разработчик конвейера может переопределить selector с помощью функции Python. Реализация по умолчанию доступна по ссылке.

Обнаруженные объекты передаются следующему элементу в конвейере, в нашем случае, трекеру людей.

Элемент трекера на основе NvTracker

Savant поддерживает стандартные трекеры DeepStream, но также возможно реализовать свой трекер с помощью pyfunc(обратите внимание на наш фреймворк трекеров Similari). В этом приложении мы будем использовать стандартный трекер Nvidia из DeepStream:

- element: nvtracker
  properties:
    tracker-width: 640
    tracker-height: 384
    ll-lib-file: /opt/nvidia/deepstream/deepstream/lib/libnvds_nvmultiobjecttracker.so
    ll-config-file: ${oc.env:APP_PATH}/samples/peoplenet_detector/config_tracker_NvSORT.yml

Далее, конвейер выполняет аналитические операции на Python, а именно сопоставление лиц с человеческими телами и подсчет количества людей с обнаруженными лицами и без для панели инструментов. Для реализации этой функциональности будет использован элемент pyfunc.

Элемент Pyfunc

Pyfunc - это класс Python, который имеет доступ к метаданным кадра, накопленным конвейером на текущий момент, и к самому кадру:

- element: pyfunc
  module: samples.peoplenet_detector.analytics
  class_name: Analytics
  kwargs:
    counters_smoothing_period: 0.25

Как и draw_func, pyfunc является одним из существенных преимуществ Savant по сравнению с базовым Deepstream. Такие элементы позволяют вставлять пользовательский Python-код в конвейер без необходимости понимания архитектуры GStreamer и необходимости писать большое количество шаблонного кода.

Мы не приводим анализ кода функции для краткости, но вы можете изучить его самостоятельно для лучшего понимания.

Запуск модуля фреймворка

Перейдем к запуску модуля вместе с адаптерами. Для начала работы запустим контейнер с аналитическим модулем. В отличие от адаптеров, модуль использует два сокета: один для связи с источником, а другой для связи с адаптером вывода.

Запустим модуль:

# You are expected to be in Savant/ directory

docker run --rm -it --gpus=all \
-e ZMQ_SRC_ENDPOINT=ipc:///tmp/zmq-sockets/input-video.ipc \
-e ZMQ_SRC_TYPE=ROUTER \
-e ZMQ_SRC_BIND=True \
-e ZMQ_SINK_ENDPOINT=ipc:///tmp/zmq-sockets/output-video.ipc \
-e ZMQ_SINK_TYPE=PUB \
-e ZMQ_SINK_BIND=True \
-v /tmp/zmq-sockets:/tmp/zmq-sockets \
-v `pwd`/samples:/opt/app/samples \
-v `pwd`/downloads/peoplenet_detector:/downloads \
-v `pwd`/models/peoplenet_detector:/models \
ghcr.io/insight-platform/savant-deepstream:0.2.0-6.2-samples \
samples/peoplenet_detector/demo.yml

Примечание для Jetson: Если вы запускаете на Jetson, измените имя образа на "*-l4t": "savant-deepstream" должен быть заменен на "savant-deepstream-l4t".

После запуска модуль выведет на консоль сообщение, указывающее на успешный запуск (при первом запуске конвейера может потребоваться некоторое время — из‑за компиляции модели в engine, что может занять пару десятков секунд; в продуктиве, если есть возможность работать с готовыми engine, лучше так и делать):

2023-03-30 16:41:38,867 [savant.gstreamer.runner] [INFO] Pipeline starting 
ended after 0:00:02.267221.

Обработка видеопотока из файла

После запуска модуля вы можете запустить адаптер Always-On RTSP Sink. Мы уже демонстрировали, как правильно запустить Always-On RTSP Sink с MediaMTX. Теперь мы запустим его с немного другими параметрами для корректного взаимодействия с модулем:

# You are expected to be in Savant/ directory

docker run --gpus=all --rm -it \
--add-host=gw:host-gateway \
-e ZMQ_ENDPOINT=ipc:///tmp/zmq-sockets/output-video.ipc \
-e ZMQ_TYPE=SUB \
-e ZMQ_BIND=False \
-e SOURCE_ID=camera1 \
-e STUB_FILE_LOCATION=/stub_img/smpte100_1280x900.jpeg \
-e RTSP_URI=rtsp://gw:8554/stream1 \
-v `pwd`/samples/stub_imgs/:/stub_img/ \
-v /tmp/zmq-sockets:/tmp/zmq-sockets \
ghcr.io/insight-platform/savant-adapters-deepstream:0.2.0-6.2 \
python -m adapters.ds.sinks.always_on_rtsp

Примечание для Jetson: Если вы запускаете на Jetson, измените имя образа на "*-l4t": "savant-adapters-deepstream" должен быть заменен на "savant-adapters-deepstream-l4t".

После запуска адаптера проверьте доступность потока RTSP, открыв адрес rtsp://127.0.0.1:8554/stream1. Оставьте видеоплеер открытым на видном месте, чтобы увидеть воспроизведение потока после получения данных.

Теперь, когда конвейер готов к получению данных, давайте отправим данные в модуль с помощью адаптера Video File Source:

# You are expected to be in Savant/ directory

docker run --rm -it \
--entrypoint /opt/app/adapters/gst/sources/media_files.sh \
-e ZMQ_ENDPOINT=ipc:///tmp/zmq-sockets/input-video.ipc \
-e ZMQ_TYPE=DEALER \
-e ZMQ_BIND=False \
-e READ_METADATA=False \
-e SYNC_OUTPUT=True \
-e SOURCE_ID=camera1 \
-e LOCATION=https://eu-central-1.linodeobjects.com/savant-data/demo/Free_City_Street_Footage.mp4 \
-e FILE_TYPE=video \
-v /tmp/zmq-sockets:/tmp/zmq-sockets \
ghcr.io/insight-platform/savant-adapters-gstreamer:0.2.0

Примечание для Jetson: Если вы запускаете на Jetson, измените имя образа на "*-l4t": "savant-adapters-gstreamer" должен быть заменен на "savant-adapters-gstreamer-l4t".

В плеере вы должны увидеть результаты работы аналитического модуля, как показано в видео ниже:

Желаемый результат достигнут!

Обработка потока RTSP

Когда модуль был протестирован на видеофайле, давайте подключим его к потоку RTSP от IP-камеры. Для этого изменим адаптер источника.

В качестве замены IP-камеры будем использовать поток RTSP, созданный из зацикленного видеофайла с помощью репозитория Fake-RTSP-Stream:

Убедитесь, что вы можете получить доступ к зацикленному потоку rtsp://127.0.0.1:8554/city-traffic с помощью VLC. Затем запустите адаптер RTSP Source, передав ему URI данного RTSP-потока:

# you are expected to be in Savant/ directory

docker run --rm -it \
--add-host=gw:host-gateway \
--entrypoint /opt/app/adapters/gst/sources/rtsp.sh \
-e SOURCE_ID=camera1 \
-e RTSP_URI=rtsp://gw:8554/city-traffic \
-e SYNC_OUTPUT=True \
-e ZMQ_ENDPOINT=ipc:///tmp/zmq-sockets/input-video.ipc \
-e ZMQ_TYPE=DEALER \
-e ZMQ_BIND=False \
-v /tmp/zmq-sockets:/tmp/zmq-sockets \
ghcr.io/insight-platform/savant-adapters-gstreamer:0.2.0

Примечание для Jetson: если вы запускаете на Jetson, измените имя образа на "*-l4t": "savant-adapters-gstreamer" должен быть заменен на "savant-adapters-gstreamer-l4t".

Сейчас кадры из потока RTSP city-traffic отправляются в модуль и должны отображаться в плейере по адресу rtsp://127.0.0.1:8554/stream1.

Несколько потоков RTSP

Наконец, стоит отметить, как легко масштабировать построенный конвейер до нескольких, например, трех источников. Для этого запустите еще два контейнера Always-On RTSP Sink и два контейнера RTSP Source, присвоив каждому источнику свой собственный SOURCE_ID и URI потока. В самом модуле ничего менять не надо - обработка будет выполняться параллельно для трех источников.

У GPU GeForce есть аппаратное ограничение на количество одновременно кодируемых потоков, равное трём, поэтому нельзя запустить более трёх независимых потоков на этих картах с использованием адаптера Always-On RTSP Sink.

Если вам нужно передавать более трех различных потоков, вам нужно использовать профессиональные карты Nvidia, карты для центров обработки данных или устройства Jetson Edge, поскольку на них нет таких ограничений.

Завершение работы демонстрации

Для освобождения ресурсов необходимо завершить все запущенные контейнеры (Ctrl+C, если контейнер запущен в интерактивном режиме) и вспомогательные контейнеры, которые использовались для эмуляции потоковой передачи RTSP:

# You are expected to be in Savant/ directory

cd Fake-RTSP-Stream
docker compose down 
cd ..

Измерение производительности

Давайте оценим какую пиковую производительность конвейера мы сможем получить. Для повышения производительности мы включем кодирование выходных фреймов в h.264 , так как передавать кадры raw-rgba из GPU в CPU и далее в 0MQ из-за их большого размера не эффективно - это существенно снижает производительность обруботки данных.

output_frame:
  codec: h264

Кроме того, мы исключим адаптеры из тестирования, чтобы измерить только производительность модуля: заменим источник на локальный файл и выход на devnull_sink. Для этого мы добавим раздел источника в блок конвейера:

pipeline:
  source:
    element: uridecodebin
    properties:
      uri: file:///data/Free_City_Street_Footage.mp4

А раздел вывода конвейера станет таким:

pipeline:
  sink:
    - element: devnull_sink

В результате вы получите конфигурацию, которую можно найти в файле demo_performance.yml.

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

# you are expected to be in Savant/ directory
mkdir -p var/data_in/demo

curl -o var/data_in/demo/Free_City_Street_Footage.mp4 \
   https://eu-central-1.linodeobjects.com/savant-data/demo/Free_City_Street_Footage.mp4

Все готово к запуску теста производительности с помощью следующей команды:

# You are expected to be in Savant/ directory

docker run --rm -it --gpus=all \
-v `pwd`/samples:/opt/app/samples \
-v `pwd`/downloads/peoplenet_detector:/downloads \
-v `pwd`/models/peoplenet_detector:/models \
-v `pwd`/var/data_in/demo:/data:ro \
ghcr.io/insight-platform/savant-deepstream:0.2.0-6.2-samples \
samples/peoplenet_detector/demo_performance.yml

Примечание для Jetson: Если вы запускаете на Jetson, измените имя образа на "*-l4t": "savant-deepstream" должен быть заменен на "savant-deepstream-l4t".

После завершения обработки модуль выведет количество обработанных кадров и FPS в журнале. На виртуальной машине AWS c 4мя ядрами Xeon Platinum 8259CL и Nvidia Tesla T4 результат выглядит следующим образом:

2023-04-08 14:31:04,755 [savant.demo_performance] [INFO] Processed 3478 frames, 
359.75 FPS.

Пользуясь случаем передаю привет GIL!

Данный пайплайн требователен к производительности ядра CPU, потому что в функции на Python выполняется существенная для Python вычислительная нагрузка, что на слабых CPU может влиять на снижение производительности пайплайна и слабой загрузке GPU (наш инженер реализовал обработку данных с помощью Scikit и NumPy, чтобы здоровья читателей меньше потратить - можно упороться и сделать быстрее, конечно).

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

Таким образом, в режиме обработки потокового видео в реальном времени конвейер с одной картой уровня Tesla T4 может обрабатывать до 14 камер с частотой кадров 25 FPS.

Заключение

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

Решение требует минимального количества кода и имеет высокую производительность благодаря использованию стека NVIDIA Deepstream; оно обладает устойчивостью к сбоям источников данных и масштабируемостью для параллельной обработки нескольких потоков одновременно. Конвейер может быть легко развернут на аппаратном обеспечении NVIDIA Edge, таком как Jetson, без каких-либо модификаций.

Это была первая демонстрация фреймворка Savant, сфокусированная на поведении и взаимодействии, а не на внутренних API. Мы будем благодарны за ваш интерес к нашим будущим публикациям. У нас есть Telegram канал GitHub Discussions и Discord. Если вам понравилось, поставьте ???? на GitHub.

Комментарии (0)