Som jedným z vývojárov operačného systému
Ak do vyhľadávača zadáte niečo ako „OpenCV na doske STM32“, môžete nájsť pomerne veľa ľudí, ktorí majú záujem o používanie tejto knižnice na doskách STM32 alebo iných mikrokontroléroch.
Existuje niekoľko videí, ktoré by podľa názvu mali demonštrovať, čo je potrebné, ale zvyčajne (vo všetkých videách, ktoré som videl) na doske STM32 bol prijatý iba obraz z kamery a výsledok sa zobrazil na obrazovke, a samotné spracovanie obrazu prebiehalo buď na bežnom počítači, alebo na výkonnejších doskách (napríklad Raspberry Pi).
Prečo je to ťažké?
Popularita vyhľadávacích dopytov sa vysvetľuje skutočnosťou, že OpenCV je najpopulárnejšou knižnicou počítačového videnia, čo znamená, že ju pozná viac vývojárov a možnosť spúšťať kód pripravený pre stolný počítač na mikrokontroléri výrazne zjednodušuje proces vývoja. Prečo však stále neexistujú obľúbené hotové recepty na riešenie tohto problému?
Problém používania OpenCV na malých šáloch súvisí s dvoma funkciami:
- Ak knižnicu skompilujete aj s minimálnou sadou modulov, jednoducho sa nezmestí do flash pamäte toho istého STM32F7Discovery (aj bez zohľadnenia OS) kvôli veľmi veľkému kódu (niekoľko megabajtov pokynov)
- Samotná knižnica je napísaná v C++, čo znamená
- Potrebujete podporu pre pozitívny runtime (výnimky atď.)
- Malá podpora pre LibC/Posix, ktorý sa zvyčajne nachádza v OS pre vstavané systémy - potrebujete štandardnú knižnicu plus a štandardnú knižnicu šablón STL (vektor atď.)
Prenos do Emboxu
Ako to už býva zvykom, pred portovaním akýchkoľvek programov do operačného systému je dobré skúsiť ho postaviť do podoby, v akej ho vývojári zamýšľali. V našom prípade s tým nie sú žiadne problémy - zdrojový kód nájdete na
Dobrou správou je, že OpenCV je možné postaviť ako statickú knižnicu hneď po vybalení, čo uľahčuje prenos. Zhromažďujeme knižnicu so štandardnou konfiguráciou a vidíme, koľko miesta zaberajú. Každý modul je zhromaždený v samostatnej knižnici.
> 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)
Ako môžete vidieť z posledného riadku, .bss a .data nezaberajú veľa miesta, no kód má viac ako 70 MiB. Je jasné, že ak je toto staticky prepojené s konkrétnou aplikáciou, kód bude menší.
Pokúsme sa vyhodiť čo najviac modulov, aby bol zostavený minimálny príklad (ktorý napríklad jednoducho vydá verziu OpenCV), takže sa pozrieme cmake .. -LA
a vypnite v možnostiach všetko, čo sa vypína.
-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)
Na jednej strane je to len jeden modul knižnice, na druhej strane je to bez optimalizácie kompilátora na veľkosť kódu (-Os
). ~3 MiB kódu je stále dosť veľa, ale už dáva nádej na úspech.
Spustite v emulátore
Na emulátore je oveľa jednoduchšie ladiť, takže sa najprv uistite, že knižnica funguje na qemu. Ako emulovanú platformu som si vybral Integrator / CP, pretože po prvé je to tiež ARM a po druhé Embox podporuje grafický výstup pre túto platformu.
Embox má mechanizmus na vytváranie externých knižníc, pomocou ktorého pridávame OpenCV ako modul (prechádzame všetkými rovnakými možnosťami pre „minimálne“ zostavenie vo forme statických knižníc), potom pridám jednoduchú aplikáciu, ktorá vyzerá takto:
version.cpp:
#include <stdio.h>
#include <opencv2/core/utility.hpp>
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}
Systém zostavíme, spustíme - dostaneme očakávaný výstup.
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 включены в сборку и т.п.>
Ďalším krokom je spustenie nejakého príkladu, najlepšie jedného zo štandardných, ktoré ponúkajú samotní vývojári.
Príklad musel byť mierne prepísaný, aby sa obrázok s výsledkom zobrazil priamo vo frame bufferi. Musel som to urobiť, pretože. funkciu imshow()
dokáže kresliť obrázky cez rozhrania QT, GTK a Windows, ktoré samozrejme v konfigurácii pre STM32 určite nebudú. V skutočnosti sa dá QT spustiť aj na STM32F7Discovery, ale o tom bude reč v inom článku 🙂
Po krátkom objasnení, v akom formáte je uložený výsledok detektora hrán, dostaneme obrázok.
pôvodný obrázok
Výsledok
Beží na STM32F7Discovery
Na 32F746GDISCOVERY je niekoľko sekcií hardvérovej pamäte, ktoré môžeme použiť tak či onak
- 320 kiB RAM
- 1MiB blesk pre obrázok
- 8MiB SDRAM
- 16MiB QSPI NAND Flash
- slot na microSD kartu
Na ukladanie obrázkov je možné použiť SD kartu, ale v kontexte spustenia minimálneho príkladu to nie je príliš užitočné.
Displej má rozlíšenie 480×272, čo znamená, že framebuffer pamäť bude 522 240 bajtov pri hĺbke 32 bitov, t.j. toto je viac ako veľkosť RAM, takže framebuffer a halda (ktorá bude potrebná, vrátane OpenCV, na ukladanie dát pre obrázky a pomocné štruktúry) budú umiestnené v SDRAM, všetko ostatné (pamäť pre zásobníky a iné systémové potreby ) prejde do pamäte RAM.
Ak vezmeme minimálnu konfiguráciu pre STM32F7Discovery (vyhodíme celú sieť, všetky príkazy, urobíme zásobníky čo najmenšie, atď.) a pridáme tam OpenCV s príkladmi, požadovaná pamäť bude nasledovná:
text data bss dec hex filename
2876890 459208 312736 3648834 37ad42 build/base/bin/embox
Pre tých, ktorí nie sú veľmi oboznámení s tým, ktoré úseky kam idú, vysvetlím: v .text
и .rodata
inštrukcie a konštanty (približne povedané, údaje len na čítanie) ležia v .data
údaje sú meniteľné, .bss
existujú "nulované" premenné, ktoré však potrebujú miesto (táto časť "prejde" do pamäte RAM).
Dobrá správa je, že .data
/.bss
by mal pasovať, ale s .text
problém je v tom, že na obrázok je len 1MiB pamäte. Dá sa vyhodiť .text
obrázok z príkladu a načítať ho napríklad z SD karty do pamäte pri štarte, ale fruits.png váži asi 330KiB, takže to problém nevyrieši: väčšina .text
pozostáva z kódu OpenCV.
Celkovo zostáva už len jedna vec – načítanie časti kódu na QSPI flash (má špeciálny režim prevádzky na mapovanie pamäte na systémovú zbernicu, takže procesor má priamy prístup k týmto údajom). V tomto prípade nastáva problém: po prvé, pamäť flash disku QSPI nie je k dispozícii ihneď po reštarte zariadenia (musíte samostatne inicializovať režim mapovania pamäte) a po druhé, nemôžete túto pamäť „flashovať“ pomocou známy bootloader.
V dôsledku toho bolo rozhodnuté prepojiť všetok kód v QSPI a flashovať ho pomocou vlastného bootloaderu, ktorý dostane požadovaný binárny súbor cez TFTP.
Výsledok
Myšlienka preniesť túto knižnicu do Emboxu sa objavila asi pred rokom, no znova a znova sa z rôznych dôvodov odkladala. Jednou z nich je podpora pre libstdc++ a štandardnú knižnicu šablón. Problém podpory C++ v Emboxe je nad rámec tohto článku, takže tu poviem len toľko, že sa nám podarilo dosiahnuť túto podporu v správnom množstve, aby táto knižnica fungovala 🙂
Nakoniec boli tieto problémy prekonané (aspoň dosť na to, aby príklad OpenCV fungoval) a príklad bežal. Doske pomocou filtra Canny trvá dlhých 40 sekúnd, kým vyhľadá hranice. To je, samozrejme, príliš dlhé (existujú úvahy o tom, ako túto záležitosť optimalizovať, v prípade úspechu o tom bude možné napísať samostatný článok).
Priebežným cieľom však bolo vytvorenie prototypu, ktorý ukáže zásadnú možnosť spustenia OpenCV na STM32, respektíve tento cieľ bol dosiahnutý, hurá!
tl;dr: pokyny krok za krokom
0: Stiahnite si zdroje Embox, napríklad takto:
git clone https://github.com/embox/embox && cd ./embox
1: Začnime zostavením bootloaderu, ktorý bude „flashovať“ QSPI flash disk.
make confload-arm/stm32f7cube
Teraz musíte nakonfigurovať sieť, pretože. Obrázok nahráme cez TFTP. Ak chcete nastaviť adresu IP dosky a hostiteľa, musíte upraviť súbor conf/rootfs/network.
Príklad konfigurácie:
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
- adresa hostiteľa, odkiaľ sa načíta obrázok, address
- adresa predstavenstva.
Potom zhromažďujeme bootloader:
make
2: Zvyčajné načítanie bootloaderu (prepáčte za slovnú hračku) na doske - tu nie je nič konkrétne, musíte to urobiť ako pre každú inú aplikáciu pre STM32F7Discovery. Ak neviete, ako na to, môžete si o tom prečítať
3: Kompilácia obrázka s konfiguráciou pre OpenCV.
make confload-platform/opencv/stm32f7discovery
make
4: Výpis z sekcií ELF, ktorý sa má zapísať do QSPI do 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
V adresári conf je skript, ktorý to robí, takže ho môžete spustiť
./conf/qspi_objcopy.sh # Нужный бинарник -- build/base/bin/qspi.bin
5: Pomocou tftp stiahnite qspi.bin.bin na flash disk QSPI. Na hostiteľovi skopírujte qspi.bin do koreňového priečinka tftp servera (zvyčajne /srv/tftp/ alebo /var/lib/tftpboot/; balíčky pre príslušný server sú dostupné vo väčšine populárnych distribúcií, zvyčajne tzv. tftpd alebo tftp-hpa, niekedy musíte urobiť systemctl start tftpd.service
začať).
# вариант для tftpd
sudo cp build/base/bin/qspi.bin /srv/tftp
# вариант для tftp-hpa
sudo cp build/base/bin/qspi.bin /var/lib/tftpboot
Na Emboxe (t. j. v bootloaderi) musíte vykonať nasledujúci príkaz (predpokladáme, že server má adresu 192.168.2.1):
embox> qspi_loader qspi.bin 192.168.2.1
6: S príkazom goto
musíte "skočiť" do pamäte QSPI. Konkrétne umiestnenie sa bude líšiť v závislosti od toho, ako je obrázok prepojený, túto adresu môžete vidieť pomocou príkazu mem 0x90000000
(začiatočná adresa sa zmestí do druhého 32-bitového slova obrázka); budete tiež musieť označiť zásobník -s
, adresa zásobníka je 0x90000000, príklad:
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
это адрес это адрес
стэка первой
инструкции
embox>goto -i 0x9000c27f -s 0x20023200 # Флаг -i нужен чтобы запретить прерывания во время инициализации системы
< Начиная отсюда будет вывод не загрузчика, а образа с OpenCV >
7: Spustenie
embox> edges 20
a užite si 40 sekundové hľadanie hraníc 🙂
Ak sa niečo pokazí - napíšte problém
Zdroj: hab.com