Когда я разговариваю с ребятами, которые совсем недавно начали разрабатывать компьютерное зрение на периферии, я часто сталкиваюсь с любопытным заблуждением:
«Если я возьму NPU (GPU|TPU|CPU), который в два раза быстрее, мое решение будет работать вдвое быстрее.
Ха-ха! Давайте поговорим о том, почему это обычно неверно. И найдите, где скрывается максимальная производительность!

Почему это происходит? Основная причина в том, что узкое место не в ускорителе. Рассмотрим общую схему обработки данных для задач компьютерного зрения:

  1. Получение изображения
  2. Подготовка изображения к загрузке в нейросеть
  3. Отправка изображения в калькулятор
  4. Получение результата из калькулятора
  5. Постобработка
  6. По результатам постобработки возможен возврат к шагу 2 (для части задач)

Рассмотрим процесс подробно.

Получение изображения

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

По интерфейсу CSI. Стандарт обеспечивает скорость почти до 10 Гбит/с. Это почти 5 тысяч кадров для черно-белых изображений с разрешением 640*480 в секунду!

Но реальность зависит от контроллера камеры и контроллера на плате, битрейта изображения и т.д. И от генерации CSI, конечно. На RPi CSI передает 90–200 кадров в секунду. В зависимости от версии камеры:

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

Если вы решите поставить две камеры — это будет невозможно ни на одной системе. Из массовых плат знаю только Jetson. В остальном готовых решений на рынке нет. И там скорость большинства камер CSI будет ограничена 60fps:

Как итог — для масштабирования такой системы самой большой проблемой является подключение новой камеры/повышение FPS. ЦП также может быть проблемой, но для CSI это редкость.

USB, GigE. В отличие от интерфейса CSI, проблема USB заключается в большем потреблении процессора (см. сравнение здесь). Можно найти современную USB-камеру, выдающую 500 кадров в секунду при разрешении 640*480. Но вы, наверное, не сможете подключить к системе две отдельные камеры. Обычно разные порты USB находятся на одном контроллере. При подключении камеры скорость доступа может сильно упасть. Вы должны использовать PCI-e для создания дополнительных контроллеров для подключения новой камеры.
Для камеры gigE потребуется специальный порт, но функциональность примерно такая же, как у USB.

Как итог — для масштабирования такой системы наиболее опасны две проблемы:

  • Недостаточно ЦП
  • Аппаратное обеспечение не сможет подключить другую камеру при сохранении FPS

Локальная сеть. Вы можете получать поток с камеры и отдельные изображения по локальной сети. В среднем локальная сеть обеспечивает более низкий FPS, чем другие камеры (сопоставимые настройки качества). При использовании локальной сети может возникнуть следующее узкое место:

  • Сетевая нагрузка
  • Контроллер не может получить несколько потоков.
  • В отличие от CSI и USB, LAN обычно передает сжатый поток. Большинство современных процессоров используют аппаратную реализацию для распаковки. И это тоже может быть узким местом. Несколько раз система, обрабатывающая десятки камер, получала перегрузку при распаковке.

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

Я не упомянул еще несколько протоколов, вроде CameraLink и т. д. Но на практике я их либо вообще не встречал, либо встречал много лет назад.

Следует также упомянуть, что вы можете получать изображения из постоянной памяти. Это может быть жесткий диск/флеш-память. И часто существуют ограничения на производительность памяти или контроллера. Не забывайте это учитывать! Вот, например, пожалуй, этот частный случай.

Подготовка данных и постобработка

Мы получили кадр на память. Теперь нам нужно его подготовить. Подготовка может включать в себя следующее:

  • Декодирование кадров (обсуждалось выше для сетевых камер)
  • Изменение разрешения (например, интерполяция)
  • Нормализация входных данных для нейронной сети (-1..1, 0..1, 0..255 и т.д.)

При этом, если используется каскадный подход (например, Обнаружение-›Обрезка-›Обработка-›Передача в Нейросеть), то возможны дополнительные операции по Обрезке/Подготовке/Предобработке.
Могут быть дополнительные операции по обработке используется, если используются камеры глубины, стерео и тепловизионные камеры (подготовка с высоты птичьего полета и т. д.).
При постобработке появятся почти такие же узкие места:

  • Немаксимальное подавление
  • Аналитика тепловых карт
  • e.t.c.

То же самое может случиться и с более сложными алгоритмами (оптический поток, SLM и т.д.).
И, конечно, чем слабее процессор, тем сложнее алгоритм и тем дольше будут выполняться эти операции.
Часто оптимизация кода процессора дает лучший прирост производительности, чем упрощение нейронной сети.

Особо следует отметить платформу NVIDIA (и другие графические процессоры). Вы можете выполнять для них всю предварительную/постобработку на графическом процессоре. Когда мы работали с первой версией OpenPose в 2016–2017 годах, мы могли почти пять раз ломать алгоритм, портируя код. При этом снижается FPS для нейросети.

Отправка изображения в ускоритель

Существует множество различных подходов к вычислениям в нейронных сетях. Есть CPU, GPU, NPU и так далее. Отправка может быть реализована по-разному. Например, если это GPU, данные сначала отправляются в память GPU, а затем запускается расчет. Некоторые NPU имеют внутреннюю память; некоторые напрямую связаны. Посмотрите, например, насколько различаются скорости вывода для нескольких быстрых и медленных плат:

Есть ли способ оптимизировать его? Да, для некоторых платформ.

Например, для платформ на базе GPU. С NVIDIA вы можете писать код в CUDA или в некоторых фреймворках, таких как DeepStream. И исключите процедуру копирования данных без необходимости (CPU-›GPU-›CPU-›GPU).

Или вы можете решить это аппаратно. Например, камеры серии OAK напрямую настраивают конвейеры в Movidius, чтобы избежать ненужных передач.

Вот небольшие хитрости, которые могут улучшить скорость:

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

****

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

Послесловие

Я надеюсь, что эта статья поможет вам!
Вы можете подписаться на мой канал на youtube или на мой LinkedIn.

А если вы хотите спросить больше или у вас есть интересный проект — смело пишите мне и нашей команде!