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

В этой статье мы рассмотрим демонстрацию удаления фона с помощью алгоритма сегментации фона MOG2 с ускорением CUDA и Savant Video Analytics Framework. Демонстрация представляет собой высокопроизводительный конвейер, обеспечивающий обработку одного потока со скоростью 570 кадров в секунду на графическом процессоре NVIDIA QUADRO RTX4000 и 75 кадров в секунду на Jetson NX, обрабатывая видео с качеством HD. В результате получается мозаичное видео с разрешением 2560x720, в котором исходное видео и результат отображаются рядом друг с другом, как показано на следующем видео:

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

Исходный код демо можно найти на GitHub Savant.

Об используемых инструментах

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

Кроме того, Savant поставляет все механизмы для связи с внешним миром: расширяемые адаптеры источника/приемника, докеризованное развертывание и готовую модель масштабируемости; подробнее о Savant на сайте. Чтобы познакомиться с Savant, изучите руководство по началу работы.

OpenCV CUDA – это аппаратно-ускоренное расширение библиотеки OpenCV, позволяющее запускать алгоритмы с ускорением CUDA на изображениях, хранящихся в памяти графического процессора, без их загрузки в память ЦП. Это выгодно для модели обработки Savant, поскольку DeepStream SDK — основа Savant — обрабатывает кадры внутри графического процессора. В результате это помогает значительно увеличить скорость обработки изображений. Однако OpenCV CUDA поддерживает относительно ограниченное количество алгоритмов и операций OpenCV, но они очень эффективны. Узнайте больше об OpenCV CUDA на сайте.

Конвейер вычитания фона

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

Savant расширяется с помощью блоков pyfunc: мы реализуем короткий класс Python из 50 строк кода, который обращается к исходному изображению в левой части кадра, размывает фон, чтобы уменьшить мерцание, запускает CUDA- ускоренный алгоритм сегментации фона MOG2, и отрисовывает результирующее изображение в правой части кадра.

Объявление конвейера выполняется с помощью типичного манифеста Savant YAML. Давайте начнем с этого манифеста, чтобы понять, что доставляет конвейер:

# module name, required
name: ${oc.env:MODULE_NAME, 'demo'}

# base module parameters
parameters:
  # pipeline processing frame parameters
  frame:
    width: 1280
    height: 720
    # Add paddings to the frame before processing
    padding:
      # Paddings are kept on the output frame
      keep: true
      left: 0
      right: 1280
      top: 0
      bottom: 0
  output_frame:
    # Frame is output without any encoding
    # this is to circumvent 3 hardware decoding processes limit on NVIDIA consumer hardware
    codec: raw-rgba
  # PyFunc for drawing on frames
  batch_size: 1
  draw_func: {}


# pipeline definition
pipeline:
  # source definition is skipped, zeromq source is used by default to connect with source adapters

  # define pipeline's main elements
  elements:
    - element: pyfunc
      # specify the pyfunc's python module
      module: samples.opencv_cuda_bg_remover_mog2.bgremover
      # specify the pyfunc's python class from the module
      class_name: BgRemover
  # sink definition is skipped, zeromq sink is used by default to connect with sink adapters

Уточним интересные параметры. Этот конвейер работает на разрешении 1280x720. Это означает, что каждый входной видеопоток будет масштабироваться до этого разрешения. Мы также хотим, чтобы справа был неиспользуемый холст того же размера, поэтому мы настраиваем отступы:

    padding:
      # Paddings are kept on the output frame
      keep: true
      left: 0
      right: 1280
      top: 0
      bottom: 0

Эта конфигурация добавляет черную область того же размера справа, расширяя видеокадр от 1280x720 до 2560x720. Кроме того, мы хотели бы указать формат выходного кадра и функцию рисования:

  output_frame:
    # Frame is output without any encoding
    # this is to circumvent 3 hardware decoding processes limit on NVIDIA consumer hardware
    codec: raw-rgba
  # PyFunc for drawing on frames
  batch_size: 1
  draw_func: {}

Мы указали codec как raw-rgba , но в рабочей среде вы должны использовать h264 или h265. Однако графические процессоры серии GeForce ограничивают количество одновременно кодируемых потоков до 3, поэтому мы по возможности избегаем кодирования в наших примерах для обеспечения безопасности.

Мы также устанавливаем batch_size на 1 . Этот параметр удобен при обработке нескольких видеопотоков с помощью конвейеров вывода на основе DNN, но в нашем примере он не влияет на производительность.

Так как художником Саванта мы не рисуем на кадре, мы отключили этот элемент.

Раздел pipeline также тривиален: мы реализуем только один pyfunc, который обрабатывает кадры с кодом удаления фона:

# pipeline definition
pipeline:
  # source definition is skipped, zeromq source is used by default to connect with source adapters

  # define pipeline's main elements
  elements:
    - element: pyfunc
      # specify the pyfunc's python module
      module: samples.opencv_cuda_bg_remover_mog2.bgremover
      # specify the pyfunc's python class from the module
      class_name: BgRemover
  # sink definition is skipped, zeromq sink is used by default to connect with sink adapters

Вы можете заметить, что pyfunc называется BgRemover и находится в модуле bgremover .

Pyfuncs имеют доступ к обработанным кадрам и их метаинформации, накопленной конвейером вплоть до вызова функции. В нашем случае BgRemover pyfunc удаляет фон. Исследуем его код:

"""Background remover module."""
from savant.gstreamer import Gst
from savant.deepstream.meta.frame import NvDsFrameMeta
from savant.deepstream.pyfunc import NvDsPyFuncPlugin
from savant.utils.artist import Artist
from savant.deepstream.opencv_utils import (
    nvds_to_gpu_mat,
)
import cv2

class BgRemover(NvDsPyFuncPlugin):
    """Background remover pyfunc.
    The class is designed to process video frame metadata and remove the background from the frame.
    MOG2 method from openCV is used to remove background.
    """

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.stream = cv2.cuda.Stream_Null()
        self.back_subtractors = {}

        self.gaussian_filter = cv2.cuda.createGaussianFilter(
            cv2.CV_8UC4, cv2.CV_8UC4, (9, 9), 2
        )


    def on_source_eos(self, source_id: str):
        """On source EOS event callback."""
        if source_id is self.back_subtractors:
            self.back_subtractors.pop(source_id)

    def process_frame(self, buffer: Gst.Buffer, frame_meta: NvDsFrameMeta):
        """Process frame metadata.
        :param buffer: Gstreamer buffer with this frame's data.
        :param frame_meta: This frame's metadata.
        """
        with nvds_to_gpu_mat(buffer, frame_meta.frame_meta) as frame_mat:
            with Artist(frame_mat) as artist:
                if frame_meta.source_id in self.back_subtractors:
                    back_sub = self.back_subtractors[frame_meta.source_id]
                else:
                    back_sub = cv2.cuda.createBackgroundSubtractorMOG2()
                    self.back_subtractors[frame_meta.source_id] = back_sub
                ref_frame = cv2.cuda_GpuMat(frame_mat, (0, 0, int(frame_meta.roi.width), int(frame_meta.roi.height)))
                cropped = ref_frame.clone()
                self.gaussian_filter.apply(cropped, cropped, stream=self.stream)
                cu_mat_fg = back_sub.apply(cropped, -1, self.stream)
                res_image = ref_frame.copyTo(cu_mat_fg, self.stream)
                artist.add_graphic(res_image, (int(frame_meta.roi.width), 0))

Экземпляр pyfunc создается при запуске конвейера и выделяется до тех пор, пока конвейер не будет запущен.

Конструктор

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

Обработчик EOS

Иногда потоки обрываются, отправляя сигнал EOS. Мы используем это событие, чтобы удалить вычитатель для завершенного потока. Если поток возвращается, сегментатор будет создан для него снова по запросу (см. далее).

Метод процессора кадров

Метод вызывается для каждого кадра каждого обрабатываемого потока. В этом методе мы получаем доступ к фрейму DeepStream как к GpuMat OpenCV с помощью служебной функции:

nvds_to_gpu_mat(buffer, frame_meta.frame_meta) as frame_mat

Наконец, мы запускаем фильтр Гаусса и сегментацию фона и применяем результаты к правой области кадра с помощью объекта artist, предоставленного Savant:

ref_frame = cv2.cuda_GpuMat(frame_mat, (0, 0, int(frame_meta.roi.width), int(frame_meta.roi.height)))
cropped = ref_frame.clone()
self.gaussian_filter.apply(cropped, cropped, stream=self.stream)
cu_mat_fg = back_sub.apply(cropped, -1, self.stream)
res_image = ref_frame.copyTo(cu_mat_fg, self.stream)
artist.add_graphic(res_image, (int(frame_meta.roi.width), 0))

Запуск кода

Демонстрацию можно запустить на периферийном устройстве на базе Jetson или на платформе ПК с дискретным графическим процессором. Убедитесь, что ваша среда выполнения настроена правильно. Мы создали небольшое руководство, в котором показано, как настроить среду выполнения Ubuntu 22.04.

Требования к среде на базе 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.

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

# if you want to share with us where are you from
# run the following command, it is completely optional
curl --silent -O -- https://hello.savant.video/opencv_cuda_bg_remover_mog2.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

# open 'rtsp://127.0.0.1:554/stream' in your player
# or visit 'https://127.0.0.1:888/stream/' (LL-HLS)

# Ctrl+C to stop running the compose bundle

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

Конвейер использует адаптер Video Loop Source и адаптер Always-On RTSP Sink. Рекомендуем прочитать статью, чтобы познакомиться с адаптерами и тем, как Savant общается с внешним миром.

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

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

# download
# you are expected to be in Savant/ directory

mkdir -p data && curl -o data/road_traffic.mp4 \
   https://eu-central-1.linodeobjects.com/savant-data/demo/road_traffic.mp4

Теперь вы можете запустить конвейер с файлом конфигурации, оптимизированным для производительности, и получить FPS, характерный для вашей вычислительной среды:

PC:

# you are expected to be in Savant/ directory

docker run --rm -it --gpus=all \
-v $(pwd)/samples:/opt/savant/samples \
-v $(pwd)/data:/data:ro \
ghcr.io/insight-platform/savant-deepstream:latest \
samples/opencv_cuda_bg_remover_mog2/demo_performance.yml

Джетсон:

docker run --rm -it --gpus=all \
-v $(pwd)/samples:/opt/savant/samples \
-v $(pwd)/data:/data:ro \
ghcr.io/insight-platform/savant-deepstream-l4t:latest \
samples/opencv_cuda_bg_remover_mog2/demo_performance.yml

После завершения обработки модуль выведет в лог количество обработанных кадров и FPS.

На рабочей станции с Core i5–6400 и Nvidia Quadro RTX4000 результат следующий:

2023-04-07 12:24:57,212 [savant.demo] [INFO] Processed 9184 frames, 
572.34 FPS.

В экземпляре AWS Tesla T4 с 4-ядерным процессором Intel(R) Xeon(R) Platinum 8259CL с тактовой частотой 2,50 ГГц конвейер приводит к следующему:

2023-04-08 14:41:17,924 [savant.demo] [INFO] Processed 9184 frames, 
464.28 FPS.

Это не ресурсоемкий конвейер, поэтому производительность GPU влияет больше, чем CPU.

Таким образом, в режиме обработки потока в реальном времени одна карта Quadro RTX4000 может обрабатывать до 22 камер с частотой кадров 25 FPS.

На Jetson NX вы можете ожидать 75 кадров в секунду в однопоточном режиме и до 130 кадров в секунду при запуске двух экземпляров конвейера.

Заключение

Мы продемонстрировали, как запустить алгоритм удаления фона MOG2 на графическом процессоре с помощью OpenCV CUDA и среды Savant. В демонстрации мы использовали исходное разрешение кадра для запуска MOG2.

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

Чтобы сделать это возможным, вы можете использовать правую сторону рамки, чтобы:

  • Шаг 1: уменьшите размер кадра в два раза (640 x 360) или в четыре раза (320 x 180) с помощью OpenCV CUDA и нарисуйте его с правой стороны кадра;
  • Шаг 2. Запустите сегментацию фона на полученном правом изображении, полученном на Шаге 1.

Мы ценим ваш интерес к технологии Savant и будем рады ответить на ваши вопросы. Присоединяйтесь к нам на GitHub в разделах Обсуждения и Discord.