В этой статье я расскажу, как я получил приложение OpenCV, работающее на STM32746G-Discovery и STM32F769I-Discovery. Если вам нужна только краткая инструкция по его воспроизведению, перейдите на соответствующую страницу wiki на Github.
Я один из разработчиков Embox. Эта ОСРВ позволяет запускать некоторое тяжелое программное обеспечение Linux (QT, OpenGL, PJSIP и т. Д.) Без ядра Linux (т.е. вам нужно гораздо меньше ресурсов и получить более прямой контроль над периферийными устройствами). Запуск OpenCV на микроконтроллерах кажется довольно популярным запросом, но кажется, что этого еще никто не делал (есть несколько видеороликов с такими именами, как OpenCV + STM32, но, насколько я вижу, они используют плату STM32 в качестве камеры, а фактическое изображение обработка ведется на настольном компьютере), поэтому я решил перенести его на плату STM32F7Discovery.
В чем проблема?
У OpenCV есть две основные проблемы, когда вы пытаетесь запустить его на MCU:
- Скомпилированный код занимает слишком много памяти (~ 4 МБ при минимальном включенном количестве модулей)
- OpenCV написан на C ++, поэтому вы не можете просто запустить его как простой код (требуются исключения и libstdc ++)
Перенос OpenCV на Embox
Когда вы переносите что-то на новую платформу, рекомендуется собрать это из исходного кода обычным способом, то есть скомпилировать для системы GNU / Linux. С OpenCV это не проблема: исходный код доступен на Github, и его легко собрать с помощью cmake.
Хорошие новости: OpenCV может быть статически связан, поэтому его будет намного проще перенести на MCU. Давайте создадим его с конфигурацией по умолчанию и посмотрим, сколько кода он производит:
> size lib/*so --totals
text data bss dec hex filename
1945822 15431 960 1962213 1df0e5 lib/libopencv_calib3d.so
17081885 170312 25640 17277837 107a38d lib/libopencv_core.so
10928229 137640 20192 11086061 a928ed lib/libopencv_dnn.so
842311 25680 1968 869959 d4647 lib/libopencv_features2d.so
423660 8552 184 432396 6990c lib/libopencv_flann.so
8034733 54872 1416 8091021 7b758d lib/libopencv_gapi.so
90741 3452 304 94497 17121 lib/libopencv_highgui.so
6338414 53152 968 6392534 618ad6 lib/libopencv_imgcodecs.so
21323564 155912 652056 22131532 151b34c lib/libopencv_imgproc.so
724323 12176 376 736875 b3e6b lib/libopencv_ml.so
429036 6864 464 436364 6a88c lib/libopencv_objdetect.so
6866973 50176 1064 6918213 699045 lib/libopencv_photo.so
698531 13640 160 712331 ade8b lib/libopencv_stitching.so
466295 6688 168 473151 7383f lib/libopencv_video.so
315858 6972 11576 334406 51a46 lib/libopencv_videoio.so
76510375 721519 717496 77949390 4a569ce (TOTALS)
Как вы можете видеть в последней строке, разделы .bss и .data занимают менее 1 МБ каждый, в то время как раздел кода составляет ~ 70 МБ (конечно, со статической компоновкой для конкретного приложения это займет гораздо меньше, но в любом случае это слишком много).
Теперь попробуем сделать минимальную сборку. Позвоните cmake .. -LA
, чтобы просмотреть список доступных опций и выключить как можно больше опций:
-DBUILD_opencv_java_bindings_generator=OFF \
-DBUILD_opencv_stitching=OFF \
-DWITH_PROTOBUF=OFF \
-DWITH_PTHREADS_PF=OFF \
-DWITH_QUIRC=OFF \
-DWITH_TIFF=OFF \
-DWITH_V4L=OFF \
-DWITH_VTK=OFF \
-DWITH_WEBP=OFF \
<...>
Размеры секции:
> size lib/libopencv_core.a --totals
text data bss dec hex filename
3317069 36425 17987 3371481 3371d9 (TOTALS)
Запустить OpenCV в QEMU
Лучше начать с эмулятора, чем с реального оборудования, поэтому давайте попробуем QEMU для запуска OpenCV на эмулированной плате Integrator / CP (это просто случайная плата ARM с поддержкой видео в QEMU).
Минимальный рабочий пример использования OpenCV выглядит так:
version.cpp:
#include <stdio.h>
#include <opencv2/core/utility.hpp>
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}
Эта программа печатает некоторую информацию OpenCV:
root@embox:/#opencv_version
OpenCV:
General configuration for OpenCV 4.0.1 =====================================
Version control: bd6927bdf-dirty
Platform:
Timestamp: 2019-06-21T10:02:18Z
Host: Linux 5.1.7-arch1-1-ARCH x86_64
Target: Generic arm-unknown-none
CMake: 3.14.5
CMake generator: Unix Makefiles
CMake build tool: /usr/bin/make
Configuration: Debug
CPU/HW features:
Baseline:
requested: DETECT
disabled: VFPV3 NEON
C/C++:
Built as dynamic libs?: NO
< other build info follows >
Следующим шагом является запуск базового примера реальной обработки изображения. На официальном сайте есть несколько примеров, я выбрал Canny edge Detector.
OpenCV поддерживает QT, GTK и Window API, которые слишком тяжелы для работы на микроконтроллерах, поэтому мне пришлось переписать некоторые части этого примера для прямого рисования в буфер кадра. После некоторой работы с внутренними форматами изображений OpenCV я получил следующие результаты:
Запустите OpenCV на STM32F7Discovery
32F746GDISCOVERY имеет следующие ресурсы памяти:
- 1 мегабайт флэш-памяти
- 340 КБ ОЗУ
- 128-мегабитная флэш-память Quad-SPI
- 128 Мбит SDRAM (доступно 64 Мбит)
- Разъем для карты microSD
MicroSD можно использовать для хранения изображений, но обрабатывать большой участок кода не очень удобно.
Разрешение дисплея 480x272, поэтому буфер кадра займет 522240 байт (с 32-битными цветами), то есть не умещается в оперативной памяти. Однако можно использовать SDRAM для буфера кучи и кадра; остальная часть оперативной памяти будет использована для других нужд ОС (стек, ресурсы процесса и т. д.).
Минимальная конфигурация Embox с OpenCV имеет следующие разделы:
text data bss dec hex filename
2876890 459208 312736 3648834 37ad42 build/base/bin/embox
Краткое отступление по разделам: разделы .text
и .rodata
содержат инструкции и константы (т.е. неизменяемые данные), .data
содержат изменяемые данные, .bss
содержат «обнуленные» переменные, которые фактически не помещаются в образ ядра, но эта память будет использоваться при выполнении время.
_12 _ / _ 13_ в порядке, они точно подойдут для RAM / SDRAM, но .text
слишком большой (код помещается во флеш-память - 1MiB).
Единственный способ справиться с такой большой частью кода - использовать флэш-память QSPI. Он имеет режим отображения памяти, который обеспечивает доступ только для чтения через системную шину, поэтому там можно разместить код. Однако с этим есть несколько проблем:
- QSPI недоступен после перезагрузки, т.е. перед выполнением кода из этой памяти необходимо выполнить некоторую инициализацию программного обеспечения.
- Как обычно, прошить с openocd и gdb не получится.
В конце концов я решил написать небольшой загрузчик, который получал данные с главного компьютера через TFTP и записывал данные в QSPI с помощью функций stm32cube.
Результаты
Наконец-то все заработало! Однако на обработку и отрисовку изображения уходит слишком много времени: 40 секунд.
Затем я попытался запустить ту же программу на плате STM32F769I-Discovery с немного другой конфигурацией (она использует другие контакты для UART и тому подобное). Эта плата имеет 2 МБ флеш-памяти, поэтому с -O2 она отлично работает без уловки QSPI и обрабатывает это изображение за 3 секунды.
Я надеюсь, что эта статья поможет вам запустить свои собственные проекты на основе OpenCV. Не стесняйтесь создавать проблемы в репозитории Embox или писать мне, если вам понадобится помощь.
Ресурсы Embox:
- Домашняя страница
- Основной список рассылки: [email protected]
- Telegram чат
- Новостной телеграмм канал