Jsem jedním z vývojářů operačního systému
Pokud zadáte do vyhledávače něco jako „OpenCV on STM32 board“, můžete najít nemálo lidí, kteří mají zájem tuto knihovnu používat na deskách STM32 nebo jiných mikrokontrolérech.
Existuje několik videí, která by podle názvu měla demonstrovat, co je potřeba, ale obvykle (ve všech videích, která jsem viděl) na desce STM32 byl přijat pouze obraz z kamery a výsledek se zobrazil na obrazovce, a samotné zpracování obrazu probíhalo buď na běžném počítači, nebo na výkonnějších deskách (například Raspberry Pi).
Proč je to těžké?
Popularita vyhledávacích dotazů se vysvětluje skutečností, že OpenCV je nejoblíbenější knihovnou počítačového vidění, což znamená, že ji zná více vývojářů, a možnost spouštět kód připravený pro stolní počítače na mikrokontroléru značně zjednodušuje proces vývoje. Proč ale stále neexistují oblíbené hotové recepty na řešení tohoto problému?
Problém používání OpenCV na malých šálách souvisí se dvěma funkcemi:
- Pokud zkompilujete knihovnu i s minimální sadou modulů, jednoduše se nevejde do flash paměti stejného STM32F7Discovery (i bez zohlednění OS) kvůli velmi velkému kódu (několik megabajtů instrukcí)
- Samotná knihovna je napsána v C++, což znamená
- Potřebujete podporu pro pozitivní běhové prostředí (výjimky atd.)
- Malá podpora pro LibC/Posix, který se obvykle nachází v OS pro vestavěné systémy - potřebujete standardní knihovnu plus a standardní knihovnu šablon STL (vektor atd.)
Portování do Emboxu
Jako obvykle je před portováním jakýchkoli programů do operačního systému dobré zkusit jej postavit do podoby, v jaké jej vývojáři zamýšleli. V našem případě s tím nejsou žádné problémy - zdrojový kód najdete na
Dobrou zprávou je, že OpenCV lze sestavit jako statickou knihovnu ihned po vybalení, což usnadňuje přenos. Shromažďujeme knihovnu se standardní konfigurací a vidíme, kolik místa zabírají. Každý modul je shromážděn v samostatné knihovně.
> 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)
Jak můžete vidět z posledního řádku, .bss a .data nezabírají mnoho místa, ale kód má více než 70 MiB. Je jasné, že pokud je toto staticky propojeno s konkrétní aplikací, bude kód méně.
Zkusme vyhodit co nejvíce modulů, aby se sestavil minimální příklad (který například jednoduše vydá verzi OpenCV), takže se podíváme cmake .. -LA
a v možnostech vypněte vše, co se vypíná.
-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 jednu stranu se jedná pouze o jeden modul knihovny, na druhou stranu bez optimalizace kompilátoru na velikost kódu (-Os
). ~3 MiB kódu je stále poměrně hodně, ale už dává naději na úspěch.
Spusťte v emulátoru
Na emulátoru je mnohem jednodušší ladit, takže se nejprve ujistěte, že knihovna funguje na qemu. Jako emulovanou platformu jsem zvolil Integrator / CP, protože za prvé je to také ARM a za druhé Embox podporuje grafický výstup pro tuto platformu.
Embox má mechanismus pro vytváření externích knihoven, pomocí něj přidáme OpenCV jako modul (předání všech stejných možností pro „minimální“ sestavení ve formě statických knihoven), poté přidám jednoduchou aplikaci, která vypadá takto:
version.cpp:
#include <stdio.h>
#include <opencv2/core/utility.hpp>
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}
Sestavíme systém, spustíme jej – dostaneme očeká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 включены в сборку и т.п.>
Dalším krokem je spuštění nějakého příkladu, nejlépe jednoho ze standardních nabízených samotnými vývojáři.
Příklad musel být mírně přepsán, aby se obrázek s výsledkem zobrazil přímo ve frame bufferu. Musel jsem to udělat, protože. funkce imshow()
umí kreslit obrázky přes rozhraní QT, GTK a Windows, což samozřejmě v configu pro STM32 rozhodně nebude. Ve skutečnosti lze QT spustit i na STM32F7Discovery, ale o tom bude řeč v jiném článku 🙂
Po krátkém upřesnění, v jakém formátu je výsledek detektoru hran uložen, dostaneme obrázek.
originální obrázek
Výsledek
Běží na STM32F7Discovery
Na 32F746GDISCOVERY je několik sekcí hardwarové paměti, které můžeme použít tak či onak
- 320 kiB RAM
- 1MiB blesk pro obrázek
- 8MiB SDRAM
- 16MiB QSPI NAND Flash
- slot na microSD kartu
Pro ukládání snímků lze použít SD kartu, 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ěť framebufferu bude 522 240 bytů při hloubce 32 bitů, tzn. to je více než velikost RAM, takže framebuffer a halda (která bude vyžadována, včetně OpenCV, pro ukládání dat pro obrázky a pomocné struktury) budou umístěny v SDRAM, vše ostatní (paměť pro zásobníky a další systémové potřeby ) přejde do paměti RAM.
Pokud vezmeme minimální konfiguraci pro STM32F7Discovery (vyhodíme celou síť, všechny příkazy, uděláme zásobníky 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/embox
Pro ty, kteří nejsou příliš obeznámeni s tím, které úseky kam jdou, vysvětlím: v .text
и .rodata
instrukce a konstanty (zhruba řečeno data pouze pro čtení) leží v .data
data jsou proměnlivá, .bss
existují "nulované" proměnné, které však potřebují místo (tato sekce "přejde" do RAM).
Dobrá zpráva je, že .data
/.bss
by měl pasovat, ale s .text
problém je v tom, že pro obraz je pouze 1MiB paměti. Lze vyhodit .text
obrázek z příkladu a načíst ho např. z SD karty do paměti při startu, ale fruits.png váží cca 330KiB, takže to problém nevyřeší: většina .text
se skládá z kódu OpenCV.
Celkově zbývá jediné - načtení části kódu na QSPI flash (má speciální režim provozu pro mapování paměti na systémovou sběrnici, takže procesor má k těmto datům přímý přístup). V tomto případě nastává problém: za prvé, paměť flash disku QSPI není k dispozici ihned po restartu zařízení (je třeba samostatně inicializovat režim mapování paměti) a za druhé nemůžete tuto paměť „flashovat“ pomocí známý bootloader.
V důsledku toho bylo rozhodnuto propojit veškerý kód v QSPI a flashovat jej pomocí samonapsaného zavaděče, který obdrží požadovaný binární soubor přes TFTP.
Výsledek
Myšlenka portovat tuto knihovnu na Embox se objevila asi před rokem, ale znovu a znovu byla z různých důvodů odkládána. Jedním z nich je podpora pro libstdc++ a standardní knihovnu šablon. Problém podpory C++ v Emboxu je nad rámec tohoto článku, takže zde řeknu pouze to, že se nám podařilo dosáhnout této podpory ve správné míře, aby tato knihovna fungovala 🙂
Nakonec byly tyto problémy překonány (alespoň natolik, aby příklad OpenCV fungoval) a příklad běžel. Desce trvá dlouhých 40 sekund, než pomocí filtru Canny vyhledá hranice. To je samozřejmě příliš dlouhé (existují úvahy, jak tuto záležitost optimalizovat, o tom bude možné v případě úspěchu napsat samostatný článek).
Prozatímním cílem však bylo vytvořit prototyp, který ukáže zásadní možnost provozování OpenCV na STM32, respektive tohoto cíle bylo dosaženo, hurá!
tl;dr: pokyny krok za krokem
0: Stáhněte si zdroje Embox, jako je tento:
git clone https://github.com/embox/embox && cd ./embox
1: Začněme sestavením bootloaderu, který bude „flashovat“ QSPI flash disk.
make confload-arm/stm32f7cube
Nyní musíte nakonfigurovat síť, protože. Obrázek nahrajeme přes TFTP. Chcete-li nastavit adresu IP desky a hostitele, musíte upravit soubor conf/rootfs/network.
Příklad konfigurace:
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 hostitele, odkud bude obrázek načten, address
- adresa představenstva.
Poté shromáždíme bootloader:
make
2: Obvyklé načítání bootloaderu (omlouvám se za slovní hříčku) na desku - zde není nic konkrétního, musíte to udělat jako u každé jiné aplikace pro STM32F7Discovery. Pokud nevíte, jak na to, můžete si o tom přečíst
3: Kompilace obrázku s konfigurací pro OpenCV.
make confload-platform/opencv/stm32f7discovery
make
4: Výpis ze sekcí ELF, které mají být zapsány 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áři conf je skript, který to dělá, takže ho můžete spustit
./conf/qspi_objcopy.sh # Нужный бинарник -- build/base/bin/qspi.bin
5: Pomocí tftp stáhněte qspi.bin.bin na QSPI flash disk. Chcete-li to provést, zkopírujte na hostiteli soubor qspi.bin do kořenové složky serveru tftp (obvykle /srv/tftp/ nebo /var/lib/tftpboot/; balíčky pro odpovídající server jsou dostupné ve většině oblíbených distribucí, obvykle nazvaných tftpd nebo tftp-hpa, někdy musíte udělat systemctl start tftpd.service
začít).
# вариант для tftpd
sudo cp build/base/bin/qspi.bin /srv/tftp
# вариант для tftp-hpa
sudo cp build/base/bin/qspi.bin /var/lib/tftpboot
Na Emboxu (tj. v bootloaderu) musíte provést následující příkaz (předpokládáme, že server má adresu 192.168.2.1):
embox> qspi_loader qspi.bin 192.168.2.1
6: S příkazem goto
musíte "skočit" do paměti QSPI. Konkrétní umístění se bude lišit v závislosti na tom, jak je obrázek propojen, tuto adresu můžete vidět pomocí příkazu mem 0x90000000
(počáteční adresa se vejde do druhého 32bitového slova obrázku); budete také muset označit zásobník -s
, adresa zásobníku je 0x90000000, příklad:
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
это адрес это адрес
стэка первой
инструкции
embox>goto -i 0x9000c27f -s 0x20023200 # Флаг -i нужен чтобы запретить прерывания во время инициализации системы
< Начиная отсюда будет вывод не загрузчика, а образа с OpenCV >
7: Spuštění
embox> edges 20
a užijte si 40sekundové hledání hranic 🙂
Pokud se něco pokazí - napište problém
Zdroj: www.habr.com