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

Какое-то время назад мы показали, что существует принципиальная возможность использовать OpenCV на STM32 (и других микроконтроллерах подобного класса). Тогда нашей целью было продемонстрировать возможность использования данной библиотеки на подобных аппаратных платформах. Поэтому, хотя мы и получили очень низкую производительность, мы не стали разбираться в ее причинах. На текущий момент мы исправили очевидные недостатки первого решения, что позволило добиться приемлемой производительности. В данной статье приведены результаты замеров производительности для различных примеров использования OpenCV на платформе STM32F7.

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

Edge detection


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

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

Пример анализируемого изображения:



Вывод для изображения 512x269

root@embox:(null)#edges fruits.png 20
Image: 512x269; Threshold=20
Detection time: 0 s 116 ms
Framebuffer: 800x480 32bpp

Вывод для изображения 512x480
root@embox:(null)#edges fruits.png 20
Image: 512x480; Threshold=20
Detection time: 0 s 254 ms
Framebuffer: 800x480 32bpp


Результаты
Image time from ROM (ms) time from QSPI (ms)
fruits.png 512x269 116 120
fruits.png 512x480 254 260




K-means


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

Для оценки плотности их распределения в OpenCV используется понятие “компактность”:
compactness: It is the sum of squared distance from each point to their corresponding centers.


Иными словами, compactness — это показатель того насколько близко точки сосредоточены к центру кластера.

В качестве входных данных kmeans.cpp генерирует картинку размером 480 x 480 c несколькими кластерами точек разных цветов. Центр каждого такого кластера выбирается случайным образом, а точки в кластер добавляются в соответствии с нормальным распределением.
Compactness time from ROM (ms) time from QSPI (ms)
733589 34 98
160406 6 18
331447 14 38
706280 13 36
399182 8 25




Squares


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

Пример анализируемого изображения:



Результаты для изображений 400x300:
Image time from ROM (ms) time from QSPI (ms)
pic1.png 1312 1668
pic2.png 4893 7268
pic3.png 1263 1571
pic4.png 2351 3590
pic5.png 1235 1515
pic6.png 1575 2202




Facedetect


Распознавание лиц было изначальной целью нашего исследования. Мы хотели оценить насколько подобные алгоритмы хорошо работают на аналогичных платах. Используем стандартный пример facedetect с набором из пяти изображений. В примерах используется Haar-cascade Detection (https://docs.opencv.org/4.5.2/db/d28/tutorial_cascade_classifier.html )

Пример анализируемого изображения:



Для изображений 256x256:

Image time from ROM (ms) time from QSPI (ms)
seq_256x256/img_000.png 3389 3801
seq_256x256/img_001.png 4015 4454
seq_256x256/img_002.png 4016 4464
seq_256x256/img_003.png 3315 3717
seq_256x256/img_004.png 3526 3952


Для изображений 480x480:
Image time from ROM (ms) time from QSPI (ms)
seq_256x256/img_000.png 14406 16149
seq_480x480/img_001.png 14784 16578
seq_480x480/img_002.png 15106 16904
seq_480x480/img_003.png 12695 14352
seq_480x480/img_004.png 14655 16446




Peopledetect


Повышая сложность мы решили попробовать как работает определение людей на изображении. Для этого можно использовать пример peopledetect.

Пример изображение:



Результаты
Image time from ROM (ms) time from QSPI (ms)
basketball2.png 640x480 40347 52587




QR code


QR коды является широко используемым примером распознавания образов.

Пример изображения взятый случайным образом из интернета:



Результаты
Данный пример не уместился во внутреннюю память, поэтому приведены результаты только из QSPI
Image time from ROM (ms) time from QSPI (ms)
qrcode_600x442.png 3092




Особенности работы на микроконтроллерах


Есть несколько примечательных моментов, которые мы обнаружили при работе с OpenCV на микроконтроллерах. Первый, код из внутренней памяти работает быстрее чем из внешней QSPI flash, даже при включенном кэше.

Второй, на наш взгляд, также связанные с кэшем, это зависимость производительности от размещения кода. Мы обнаружили, что незначительное изменение кода, например, добавление команды, которая не вызывается в основном алгоритме, может увеличить или уменьшить производительность на 5 или даже более процентов.

Третий, достаточно ограниченный объем внутренней памяти (2 MB). Нам не удалось быстро запустить пример с распознаванием QR кодов из внутренней памяти.

Еще одна важная особенность относится к ядрам ARM Cortex-M. Мы использовали ядра с поддержкой SIMD инструкций. Данная технология помогает увеличить производительность путем обработки нескольких арифметических команд на одном регистре. Для оценки того что это помогает для наших задач мы провели замеры на Linux c поддержкой SIMD инструкций и без и выяснили, что на некоторых примерах использование SIMD дает прирост производительности на 80 %.

Однако для нашего процессора, gcc не умеет автоматически генерить код с использованием SIMD инструкций. Существует поддержка только в виде Intrinsic functions. Иными словами нужно вставлять эти команды в ручном режиме. OpenCV поддерживает такой подход. Можно реализовать поддержку SIMD для специальной архитектуры. Но на текущий момент OpenCV рассчитан только на работу с длинными типами данных (128 бит и больше). Поэтому в рамках данной работы не было оценено улучшение производительности при использовании SIMD на STM32. Мы надеемся это будет направление будущих исследований.

Анализ результатов


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

Применение OpenCV на микроконтроллерах сильно зависит от задач, которые необходимо решить. Большинство базовых алгоритмов работают незаметно глазу. Тот же алгоритм выявления границ отрабатывал за доли секунды, такой производительности может быть вполне достаточно для автономного робота. Комплексные алгоритмы, например, обработка QR кода могут быть использованы, но необходимо оценивать плюсы и минусы решения. С одной стороны 3 секунды это много для распознавания, а с другой, для некоторых целей может быть достаточно.

Поэтому предположу, что для распознавания сложных объектов, например, выявления человека, подобные платформы, еще недостаточно мощные. Задержка сильно заметна по сравнению с распознаванием той же картинки на хосте. Но нужно учитывать и то что сравнивалось с 64 битным intel-i7 c 8 ядрами и принципиально другой частотой, а, следовательно, и потребление у данной платформы совсем другое. И к тому же в сравнении участвовал не самый мощный микроконтроллер. Даже у STM32 есть серия H7, которая в два раза мощнее.

Результаты можно увидеть на видео


Воспроизведение результатов


Вы можете воспроизвести результаты полученные в статье. Для этого понадобятся два репозитория. Основной репозиторий Embox и репозиторий с примерами изображений и готовыми конфигурациями для платы STM32F769i-discovery. Следуя инструкциям в README файле из репозитория и примерами вы можете воспроизвести результаты.

Вы можете также использовать другие платы, для этого вам необходимо собрать требуемую конфигурацию. Кроме того, вы можете поэкспериментировать с другими изображениями или разместить изображения на SD-карте, что также требует только изменения конфигурации Embox.

P. S. Впервые данныя статья была опубликована на английском языке на сайте emedded.com