Som jedným z vývojárov operačného systému , a v tomto článku vám poviem, ako sa mi podarilo spustiť OpenCV na doske STM32746G.
Ak do vyhľadávača zadáte niečo ako „OpenCV na doske STM32“, môžete nájsť pomerne veľa ľudí, ktorí sa zaujímajú o používanie tejto knižnice na doskách STM32 alebo iných mikrokontroléroch.
Existuje niekoľko videí, ktoré, podľa názvu, by mali demonštrovať to, čo potrebujete, ale zvyčajne (vo všetkých videách, ktoré som videl) sa na doske STM32 iba získavali obrázky z kamery a zobrazoval sa výsledok na obrazovke, pričom samotné spracovanie obrazu sa vykonávalo buď na bežnom počítači, alebo na silnejších doskách (napríklad Raspberry Pi).
Prečo je to ťažké?
Popularita vyhľadávacích dopytov je spôsobená tým, že OpenCV je najobľúbenejšia knižnica počítačového videnia, a teda je s ňou oboznámených viac vývojárov, a navyše možnosť spúšťať kód pripravený pre desktop na mikrokontroléri výrazne zjednodušuje proces vývoja. Ale prečo stále neexistujú nejaké populárne hotové recepty na riešenie tohto problému?
Problém používania OpenCV na malých doskách súvisí s dvoma vlastnosťami:
- Ak skompilujete knižnicu aj s minimálnym počtom modulov, do flash pamäte STM32F7Discovery sa jednoducho nezmestí (aj bez ohľadu na OS) kvôli veľmi veľkému kódu (niekoľko megabajtov inštrukcií)
- Samotná knižnica je napísaná v C++, a teda
- Je potrebná podpora C++ runtime (výnimky a pod.)
- Malo je podpory LibC/Posix, ktoré sú zvyčajne k dispozícii v OS pre zabudované systémy — potrebná je štandardná knižnica C++ a štandardná knižnica šablón STL (vector a pod.)
Portovanie na Embox
Ako zvyčajne, pred portovaním akýchkoľvek programov do operačného systému nie je zlé pokúsiť sa ho zostaviť v takom formáte, aký zamýšľali vývojári. V našom prípade s tým nie sú žiadne problémy — zdrojové kódy môžete nájsť na , knižnica sa zostavuje pod GNU/Linux pomocou bežného cmake-a.
Z dobrých správ — OpenCV sa dá z vyžaduje zostaviť ako statická knižnica, čo uľahčuje portovanie. Zostavujeme knižnicu so štandardným konfiguráciou a sledujeme, koľko miesta zaberajú. Každý modul sa zostavuje do samostatnej knižnice.
> 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 vyplýva z posledného riadku, .bss a .data nezaberajú veľa miesta, avšak kód presahuje 70 MiB. Je jasné, že ak by to bolo staticky linkované s konkrétnou aplikáciou, veľkosť kódu by sa zmenšila.
Skúsime odstrániť čo najviac modulov, aby sme vytvorili minimálny príklad (ktorý napríklad jednoducho zobrazí verziu OpenCV), takže sa pozrime cmake .. -LA a v možnostiach vypneme všetko, čo je možné vypnúť.
-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 ide len o jeden modul knižnice, na druhej strane, to je bez optimalizácie kompilátorom podľa veľkosti kódu (-Os). ~3 MiB kódu — to je stále dosť veľa, ale už dáva nádej na úspech.
Spustenie v emulátore
Na emulátore sa odladí oveľa jednoduchšie, takže sa najprv uistíme, že knižnica funguje na qemu. Ako emulovanú platformu som si zvolil Integrator/CP, pretože jednak je to tiež ARM a jednak Embox podporuje grafický výstup pre túto platformu.
V Embox je mechanizmus na zostavenie externých knižníc, pomocou neho pridáme OpenCV ako modul (prevodom všetkých tých istých možností pre „minimálnu“ zostavu vo forme statických knižníc), po tom pridám najjednoduchšiu aplikáciu, ktorá vyzerá takto:
version.cpp:
#include
#include
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}Zostavíme systém, spustíme — a 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?: NODalším krokem je spustit nějaký příklad, nejlépe nějaký standardní od samotných vývojářů . Vybral jsem si .
Příklad jsem musel mírně přepsat, abych mohl zobrazit obrázek s výsledkem přímo ve frame bufferu. To bylo nutné, protože funkce imshow() dokáže vykreslovat obrázky přes rozhraní QT, GTK a Windows, které samozřejmě v konfiguraci pro STM32 určitě nebude. Ve skutečnosti je také možné spustit QT na STM32F7Discovery, ale to bude popsáno v jiném článku 🙂
Po krátkém zkoumání, v jakém formátu je uchováván výstup detektoru hranic, získáme obrázek.

Originální obrázek

Výsledok
Spuštění na STM32F7Discovery
Na 32F746GDISCOVERY je několik hardwarových oddílů paměti, které můžeme tak či onak využít
- 320KiB RAM
- 1MiB flash paměti pro image
- 8MiB SDRAM
- 16MiB QSPI NAND flash
- Slot pro microSD kartu
SD kartu lze použít pro ukládání obrázků, ale v kontextu spuštění minimálního příkladu to není příliš užitečné.
Displej má rozlišení 480×272, což znamená, že paměť pro frame buffer bude činit 522 240 bajtů při hloubce 32 bitů, t.j. to je více než velikost RAM, takže frame buffer a haldu (která bude potřebná, včetně pro OpenCV, aby uchovala data pro obrázky a pomocné struktury) umístíme do SDRAM, zbytek (paměť pro stacky a další systémové potřeby) půjde do RAM.
Pokud vezmeme minimální konfiguraci pro STM32F7Discovery (vyhodit celou síť, všechny příkazy, udělat stacky co nejmenší atd.) a přidáme tam OpenCV s příklady, bude požadovaná paměť následující:
text data bss dec hex filename
2876890 459208 312736 3648834 37ad42 build\/base\/bin\/emboxPre tých, ktorí nie sú veľmi oboznámení s tým, do ktorých sekcií čo patrí, vysvetlím: v .text a .rodata sú uložené inštrukcie a konštanty (hrubým spôsobom readonly-dáta), v .data sú uložené modifikovateľné dáta, v .bss sú umiestnené „nulové“ premenné, ktoré však potrebujú miesto (táto sekcia sa „presunie“ do RAM).
Dobrá správa je, že .data/.bss musí sa zmestiť, ale s .text problém — pre obrazec je len 1MiB pamäte. Môžete odstrániť z .text obrázka z príkladu a načítať ho, napríklad, z SD karty do pamäte pri spustení, ale fruits.png váži približne 330KiB, takže to problém nevyrieši: väčšia časť .text sa skladá práve z kódu OpenCV.
V podstate ostáva len jedno — nahratie časti kódu na QSPI-flash (má špeciálny režim práce pre mapovanie pamäte na systémovú zbernicu, takže procesor k týmto dátam môže pristupovať priamo). Pri tomto sa objavuje problém: po prvé, pamäť QSPI-flash nie je dostupná okamžite po reštartovaní zariadenia (je potrebné samostatne inicializovať memory-mapped-režim), po druhé, nie je možné „napáliť“ túto pamäť obvyklým nabíjačom.
Nakoniec sme sa rozhodli zlúčiť celý kód do QSPI a napáliť ho vlastným nabíjačom, ktorý získa potrebný binárny súbor pomocou TFTP.
Výsledok
Nápad portovať túto knižnicu na Embox sa objavil pred približne rokom, ale opakovane to bolo odložené z rôznych dôvodov. Jedným z nich je podpora libstdc++ a štandardnej šablónovej knižnice. Problém podpory C++ v Embox presahuje rámec tohto článku, takže tu len poviem, že sa nám podarilo dosiahnuť túto podporu v potrebnom rozsahu na prácu tejto knižnice 🙂
Nakoniec boli tieto problémy prekonané (aspoň v dostatočnej miere na fungovanie príkladu OpenCV) a príklad sa spustil. Hľadanie okrajov pomocou Cannyho filtra trvá dosť dlhých 40 sekúnd. To je samozrejme príliš dlho (existujú úvahy, ako tento proces optimalizovať, o tom by sa mohla napísať samostatná článok v prípade úspechu).

Napriek tomu, medzičasovým cieľom bolo vytvorenie prototypu, ktorý preukáže zásadnú možnosť spustenia OpenCV na STM32, a preto bol tento cieľ dosiahnutý, hurá!
tl;dr: krok-za-krokom návod
0: Stiahneme zdrojové kódy Embox, napríklad takto:
git clone https://github.com/embox/embox && cd ./embox1: Začneme so zostavením nabíjača, ktorý „napáli“ QSPI-flash.
make confload-arm/stm32f7cubeTeraz je potrebné nastaviť sieť, pretože budeme načítavať obraz cez TFTP. Aby bolo možné nastaviť IP adresy dosky a hostiteľa, je potrebné zmeniť 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:02gateway — adresa hostiteľa, z ktorého sa bude načítavať obraz, address — adresa dosky.
Po tomto zostavíme zavádzač:
make2: Bežné načítanie zavádzača (prepáčte za slovnú hračku) na dosku — tu nie je nič špecifické, treba to urobiť ako pre akúkoľvek inú aplikáciu pre STM32F7Discovery. Ak neviete, ako to urobiť, môžete si o tom prečítať. .
3: Kompilácia obrazu s konfiguráciou pre OpenCV.
make confload-platform/opencv/stm32f7discovery
make4: Extrakcia z ELF sekcií, ktoré je potrebné 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=.dataV adresári conf je skript, ktorý to robí, takže ho môžete spustiť
./conf/qspi_objcopy.sh # Potrebný binárny súbor -- build/base/bin/qspi.bin5: Pomocou tftp načítame qspi.bin na QSPI flash disk. Na hostiteľovi je potrebné skopírovať qspi.bin do koreňového priečinka tftp servera (obe lokácie sú zvyčajne /srv/tftp/ alebo /var/lib/tftpboot/; balíky pre príslušný server sú v väčšine populárnych distribúcií, zvyčajne sa nazývajú tftpd alebo tftp-hpa, niekedy je potrebné systemctl start tftpd.service na spustenie).
# вариант для tftpd
sudo cp build/base/bin/qspi.bin /srv/tftp
# вариант для tftp-hpa
sudo cp build/base/bin/qspi.bin /var/lib/tftpbootNa Emboxe (t.j. v zavádzači) je potrebné vykonať tento príkaz (predpokladáme, že adresa servera je 192.168.2.1):
embox> qspi_loader qspi.bin 192.168.2.16: Pomocou príkazu goto je potrebné "preskočiť" do QSPI pamäte. Konkrétna lokalita sa bude líšiť v závislosti od toho, ako sa obraz prepojí, tento adresu môžete skontrolovať príkazom mem 0x90000000 (adresa začiatku sa nachádza v druhom 32-bitovom slove obrazu); tiež bude potrebné nastaviť zásobník s praporkom -s, adresa zásobníka je na adrese 0x90000000, príklad:
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
to je adresa to je adresa
zásobníka prvej
inštrukcie
embox>goto -i 0x9000c27f -s 0x20023200 # Prapor -i je potrebný na zakázanie prerušenia počas inicializácie systému7: Spúšťame
embox> edges 20a užívame si 40-sekundové hľadanie hraníc 🙂
Ak sa niečo pokazí — napíšte issue do , alebo do mailing listu embox-devel@googlegroups.com, alebo do komentárov tu.
Zdroj: habr.com
