OpenCV na STM32F7-Discovery

OpenCV na STM32F7-Discovery Som jedným z vývojárov operačného systému Embox, a v tomto článku budem hovoriť o tom, 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í 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 github, knižnica je postavená pod GNU/Linux s obvyklým cmake.

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. na vašej stránke. vybral som si hraničný detektor šikovný.

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.

OpenCV na STM32F7-Discovery

pôvodný obrázok

OpenCV na STM32F7-Discovery

Výsledok

Beží na STM32F7Discovery

Na 32F746GDISCOVERY je niekoľko sekcií hardvérovej pamäte, ktoré môžeme použiť tak či onak

  1. 320 kiB RAM
  2. 1MiB blesk pre obrázok
  3. 8MiB SDRAM
  4. 16MiB QSPI NAND Flash
  5. 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).

OpenCV na STM32F7-Discovery

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ť tu.
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 naše úložiskoalebo do zoznamu adries [chránené e-mailom], alebo v komentári tu.

Zdroj: hab.com

Pridať komentár