我是操作系統的開發者之一
如果你在搜索引擎中輸入“OpenCV on STM32 board”之類的東西,你會發現很多人有興趣在 STM32 板或其他微控制器上使用這個庫。
有幾個視頻,從名字來看,應該演示需要什麼,但通常(在我看到的所有視頻中)在 STM32 板上,只有從相機接收到圖像並將結果顯示在屏幕上,圖像處理本身要么在普通計算機上完成,要么在功能更強大的電路板(例如 Raspberry Pi)上完成。
為什麼很難?
搜索查詢的流行是因為 OpenCV 是最流行的計算機視覺庫,這意味著更多的開發人員熟悉它,並且能夠在微控制器上運行桌面就緒代碼大大簡化了開發過程。 但是為什麼仍然沒有解決這個問題的流行的現成食譜呢?
在小披肩上使用OpenCV的問題與兩個特徵有關:
- 如果您使用最少的模塊集編譯庫,由於代碼非常大(幾兆字節的指令),它根本不適合同一個 STM32F7Discovery 的閃存(即使不考慮操作系統)
- 該庫本身是用 C++ 編寫的,這意味著
- 需要支持正運行時(異常等)
- 對 LibC/Posix 的支持很少,這通常在嵌入式系統的操作系統中找到——你需要一個標準的 plus 庫和一個標準的 STL 模板庫(vector 等)
移植到 Embox
像往常一樣,在將任何程序移植到操作系統之前,最好嘗試以開發人員想要的形式構建它。 在我們的例子中,這沒有問題 - 源代碼可以在
好消息是 OpenCV 可以構建為開箱即用的靜態庫,這使得移植更加容易。 我們收集了一個標準配置的庫,看看它們佔用了多少空間。 每個模塊都收集在一個單獨的庫中。
> 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並沒有佔用多少空間,但是代碼卻有70多MiB。 很明顯,如果將此靜態鏈接到特定應用程序,代碼將變得更少。
讓我們嘗試拋出盡可能多的模塊,以便組裝一個最小的示例(例如,將簡單地輸出 OpenCV 版本),所以我們看 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)
一方面,這只是庫的一個模塊,另一方面,這沒有針對代碼大小進行編譯器優化(-Os
). ~3 MiB 的代碼還是不少,但已經有了成功的希望。
在模擬器中運行
在模擬器上調試要容易得多,所以首先要確保庫在 qemu 上工作。 作為仿真平台,我選擇了Integrator/CP,因為一來也是ARM,二來Embox支持這個平台的圖形輸出。
Embox 有一個構建外部庫的機制,使用它我們將 OpenCV 添加為一個模塊(以靜態庫的形式為“最小”構建傳遞所有相同的選項),之後我添加一個簡單的應用程序,如下所示:
version.cpp:
#include <stdio.h>
#include <opencv2/core/utility.hpp>
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}
我們組裝系統,運行它——我們得到了預期的輸出。
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
< Дальше идут прочие параметры сборки -- с какими флагами компилировалось,
какие модули OpenCV включены в сборку и т.п.>
下一步是運行一些示例,最好是開發人員自己提供的標準示例之一。
該示例必須稍微重寫以直接在幀緩衝區中顯示圖像和結果。 我不得不這樣做,因為。 功能 imshow()
可以通過QT、GTK、Windows接口畫圖,當然STM32的config里肯定沒有。 事實上,QT 也可以在 STM32F7Discovery 上運行,但這將在另一篇文章中討論🙂
在簡短說明邊緣檢測器結果的存儲格式後,我們得到了一張圖像。
原圖
導致
在 STM32F7Discovery 上運行
在 32F746GDISCOVERY 上有幾個硬件內存部分,我們可以以一種或另一種方式使用
- 320KiB 內存
- 1MiB 閃存用於圖像
- 8MiB 內存
- 16MiB QSPI NAND閃存
- microSD 卡槽
SD 卡可用於存儲圖像,但在運行最小示例的上下文中,這不是很有用。
顯示器的分辨率為 480×272,這意味著幀緩衝存儲器在 522 位深度下為 240 字節,即這超過了 RAM 的大小,因此幀緩衝區和堆(需要,包括 OpenCV,用於存儲圖像和輔助結構的數據)將位於 SDRAM 中,其他所有內容(堆棧和其他系統需要的內存) ) 將轉到 RAM 。
如果我們採用 STM32F7Discovery 的最小配置(丟棄整個網絡、所有命令、使堆棧盡可能小等)並在其中添加帶有示例的 OpenCV,則所需內存如下:
text data bss dec hex filename
2876890 459208 312736 3648834 37ad42 build/base/bin/embox
對於那些不太了解哪些部分去哪裡的人,我將解釋一下:在 .text
и .rodata
指令和常量(粗略地說,只讀數據)位於 .data
數據是可變的, .bss
有一些“空”變量,但它們需要一個位置(本節將“轉到”RAM)。
好消息是 .data
/.bss
應該適合,但與 .text
問題是圖像只有 1MiB 的內存。 可以扔出去 .text
示例中的圖片並讀取它,例如,在啟動時從 SD 卡讀取到內存,但是 fruits.png 重約 330KiB,所以這不能解決問題: most .text
由 OpenCV 代碼組成。
總的來說,只剩下一件事了——將一部分代碼加載到 QSPI 閃存上(它有一種特殊的操作模式,可以將內存映射到系統總線,這樣處理器就可以直接訪問這些數據)。 這樣一來,問題就來了:一是QSPI閃存盤的內存在設備重啟後不能立即使用(需要單獨初始化內存映射模式),二是無法“刷入”這塊內存熟悉的引導加載程序。
因此,決定鏈接 QSPI 中的所有代碼,並使用自寫的加載程序對其進行閃存,該加載程序將通過 TFTP 接收所需的二進製文件。
導致
將這個庫移植到 Embox 的想法大約在一年前就出現了,但由於各種原因一再推遲。 其中之一是支持 libstdc++ 和標準模板庫。 Embox 中的 C++ 支持問題超出了本文的範圍,所以在這裡我只會說我們設法實現了適當數量的這種支持,以使該庫正常工作🙂
最終,這些問題都被攻克了(至少足夠讓OpenCV的例子可以運行了),例子跑起來了。 電路板使用 Canny 過濾器搜索邊界需要 40 秒。 這當然是太長了(有關於如何優化這件事的考慮,如果成功的話可以單獨寫一篇關於這個的文章)。
然而,中間目標是創建一個原型,分別展示在 STM32 上運行 OpenCV 的基本可能性,這個目標已經實現,萬歲!
tl;dr:分步說明
0: 下載 Embox 資源,像這樣:
git clone https://github.com/embox/embox && cd ./embox
1:讓我們首先組裝一個將“閃存”QSPI 閃存驅動器的引導加載程序。
make confload-arm/stm32f7cube
現在你需要配置網絡,因為。 我們將通過 TFTP 上傳圖像。 要設置開發板和主機 IP 地址,您需要編輯 conf/rootfs/network。
配置示例:
iface eth0 inet static
address 192.168.2.2
netmask 255.255.255.0
gateway 192.168.2.1
hwaddress aa:bb:cc:dd:ee:02
gateway
- 將加載圖像的主機地址, address
- 董事會地址。
之後,我們收集引導加載程序:
make
2:在電路板上通常加載引導加載程序(抱歉是雙關語)——這裡沒有什麼特別的,您需要像 STM32F7Discovery 的任何其他應用程序一樣執行此操作。 如果你不知道該怎麼做,你可以閱讀它
3:使用 OpenCV 的配置編譯圖像。
make confload-platform/opencv/stm32f7discovery
make
4:提取ELF段寫入QSPI到qspi.bin
arm-none-eabi-objcopy -O binary build/base/bin/embox build/base/bin/qspi.bin
--only-section=.text --only-section=.rodata
--only-section='.ARM.ex*'
--only-section=.data
conf 目錄中有一個腳本可以執行此操作,因此您可以運行它
./conf/qspi_objcopy.sh # Нужный бинарник -- build/base/bin/qspi.bin
5:使用 tftp,將 qspi.bin.bin 下載到 QSPI 閃存驅動器。 在主機上,為此,將 qspi.bin 複製到 tftp 服務器的根文件夾(通常是 /srv/tftp/ 或 /var/lib/tftpboot/;相應服務器的軟件包在大多數流行的發行版中都可用,通常稱為tftpd 或 tftp-hpa,有時你必須做 systemctl start tftpd.service
開始)。
# вариант для tftpd
sudo cp build/base/bin/qspi.bin /srv/tftp
# вариант для tftp-hpa
sudo cp build/base/bin/qspi.bin /var/lib/tftpboot
在 Embox 上(即在引導加載程序中),您需要執行以下命令(我們假設服務器的地址為 192.168.2.1):
embox> qspi_loader qspi.bin 192.168.2.1
6:帶命令 goto
您需要“跳轉”到 QSPI 內存中。 具體位置會根據圖片的鏈接方式不同,可以通過命令看到這個地址 mem 0x90000000
(起始地址適合圖像的第二個 32 位字); 您還需要標記堆棧 -s
, 堆棧地址在 0x90000000, 例如:
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
это адрес это адрес
стэка первой
инструкции
embox>goto -i 0x9000c27f -s 0x20023200 # Флаг -i нужен чтобы запретить прерывания во время инициализации системы
< Начиная отсюда будет вывод не загрузчика, а образа с OpenCV >
7:啟動
embox> edges 20
並享受 40 秒的邊界搜索🙂
如果出現問題 - 在中寫一個問題
來源: www.habr.com