OpenCV na STM32F7-Discovery

OpenCV na STM32F7-Discovery Sem eden od razvijalcev operacijskega sistema Embox, v tem članku pa bom govoril o tem, kako mi je uspelo zagnati OpenCV na plošči STM32746G.

Če v iskalnik vnesete nekaj takega kot "OpenCV na plošči STM32", lahko najdete kar nekaj ljudi, ki jih zanima uporaba te knjižnice na ploščah STM32 ali drugih mikrokontrolerjih.
Obstaja več videoposnetkov, ki bi po imenu sodeč morali pokazati, kaj je potrebno, vendar je običajno (v vseh videih, ki sem jih videl) na plošči STM32 s kamere prejeta samo slika in rezultat je bil prikazan na zaslonu, in sama obdelava slike je bila izvedena bodisi na običajnem računalniku bodisi na zmogljivejših ploščah (na primer Raspberry Pi).

Zakaj je težko?

Priljubljenost iskalnih poizvedb je razložena z dejstvom, da je OpenCV najbolj priljubljena knjižnica računalniškega vida, kar pomeni, da jo pozna več razvijalcev, zmožnost izvajanja namizne kode na mikrokontrolerju pa močno poenostavi razvojni proces. Toda zakaj še vedno ni priljubljenih pripravljenih receptov za rešitev te težave?

Težava uporabe OpenCV na majhnih šalih je povezana z dvema funkcijama:

  • Če knjižnico sestavite tudi z minimalnim naborom modulov, se zaradi zelo velike kode (več megabajtov navodil) preprosto ne bo prilegala bliskovnemu pomnilniku istega STM32F7Discovery (tudi brez upoštevanja OS)
  • Sama knjižnica je napisana v C++, kar pomeni
    • Potrebujem podporo za pozitivno delovanje (izjeme itd.)
    • Majhna podpora za LibC/Posix, ki je običajno v OS za vgrajene sisteme - potrebujete standardno knjižnico plus in standardno knjižnico predlog STL (vektor itd.)

Prenos v Embox

Kot običajno je dobro, da pred prenosom katerega koli programa v operacijski sistem poskusite zgraditi program v obliki, kot so si ga zamislili razvijalci. V našem primeru s tem ni težav - izvorno kodo najdete na github, je knjižnica zgrajena pod GNU/Linux z običajnim cmake.

Dobra novica je, da je OpenCV mogoče zgraditi kot statično knjižnico takoj, kar olajša prenos. Zberemo knjižnico s standardno konfiguracijo in vidimo, koliko prostora zavzamejo. Vsak modul je zbran v ločeni knjiž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)

Kot lahko vidite v zadnji vrstici, .bss in .data ne zavzameta veliko prostora, vendar je koda več kot 70 MiB. Jasno je, da bo koda manjša, če je to statično povezano z določeno aplikacijo.

Poskusimo vreči čim več modulov, da bo sestavljen minimalen primer (ki bo na primer preprosto izpisal različico OpenCV), tako da pogledamo cmake .. -LA in izklopi v opcijah vse, kar se izklopi.

        -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)

Po eni strani je to samo en modul knjižnice, po drugi strani pa je to brez optimizacije prevajalnika za velikost kode (-Os). ~3 MiB kode je še kar veliko, a že daje upanje na uspeh.

Zaženite v emulatorju

Na emulatorju je veliko lažje odpravljati napake, zato se najprej prepričajte, da knjižnica deluje na qemu. Kot emulirano platformo sem izbral Integrator / CP, ker prvič, je tudi ARM, in drugič, Embox podpira grafični izhod za to platformo.

Embox ima mehanizem za gradnjo zunanjih knjižnic, z njim dodamo OpenCV kot modul (prepustimo vse iste možnosti za "minimalno" gradnjo v obliki statičnih knjižnic), nato pa dodam preprosto aplikacijo, ki izgleda takole:

version.cpp:

#include <stdio.h>
#include <opencv2/core/utility.hpp>

int main() {
    printf("OpenCV: %s", cv::getBuildInformation().c_str());

    return 0;
}

Sestavimo sistem, ga zaženemo - dobimo pričakovan rezultat.

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 включены в сборку и т.п.>

Naslednji korak je zagon nekega primera, po možnosti enega od standardnih, ki jih ponujajo razvijalci sami. na vašem spletnem mestu. izberem mejni detektor canny.

Primer je bilo treba nekoliko prepisati, da se slika z rezultatom prikaže neposredno v medpomnilniku okvirja. To sem moral storiti, ker. funkcijo imshow() zna risati slike preko QT, GTK in Windows vmesnikov, ki jih seveda zagotovo ne bo v konfiguraciji za STM32. Pravzaprav je QT mogoče izvajati tudi na STM32F7Discovery, vendar bo o tem razpravljal v drugem članku 🙂

Po kratkem pojasnilu, v katerem formatu je shranjen rezultat detektorja robov, dobimo sliko.

OpenCV na STM32F7-Discovery

izvirna slika

OpenCV na STM32F7-Discovery

Rezultat

Deluje na STM32F7Discovery

Na 32F746GDISCOVERY obstaja več delov pomnilnika strojne opreme, ki jih lahko uporabimo tako ali drugače

  1. 320KiB RAM
  2. 1MiB flash za sliko
  3. 8 MB SDRAM
  4. 16MiB QSPI NAND Flash
  5. reža za kartico microSD

Kartico SD lahko uporabite za shranjevanje slik, vendar v kontekstu izvajanja minimalnega primera to ni zelo uporabno.
Zaslon ima ločljivost 480×272, kar pomeni, da bo pomnilnik framebuffer velik 522 bajtov pri globini 240 bitov, tj. to je več kot velikost RAM-a, zato bosta okvirni medpomnilnik in kopica (ki bosta potrebna, tudi za OpenCV, za shranjevanje podatkov za slike in pomožne strukture) v SDRAM-u, vse ostalo (pomnilnik za sklade in druge sistemske potrebe ) bo šel v RAM .

Če vzamemo minimalno konfiguracijo za STM32F7Discovery (izločimo celotno omrežje, vse ukaze, čim manjše sklade itd.) in tam dodamo OpenCV s primeri, bo potreben pomnilnik naslednji:

   text    data     bss     dec     hex filename
2876890  459208  312736 3648834  37ad42 build/base/bin/embox

Za tiste, ki niso dobro seznanjeni s tem, kateri odseki kam gredo, bom pojasnil: v .text и .rodata navodila in konstante (grobo rečeno, podatki samo za branje) ležijo v .data podatki so spremenljivi, .bss obstajajo "nulled" spremenljivke, ki pa kljub temu potrebujejo mesto (ta razdelek bo "šel" v RAM).

Dobra novica je ta .data/.bss bi se moral prilegati, a z .text težava je v tem, da je za sliko na voljo le 1 MB pomnilnika. Lahko se vrže ven .text sliko iz primera in jo preberete na primer s kartice SD v pomnilnik ob zagonu, vendar fruits.png tehta približno 330 KB, tako da to ne bo rešilo težave: večina .text sestoji iz kode OpenCV.

Na splošno ostane le še ena stvar - nalaganje dela kode na QSPI flash (ima poseben način delovanja za preslikavo pomnilnika v sistemsko vodilo, tako da lahko procesor neposredno dostopa do teh podatkov). V tem primeru se pojavi težava: prvič, pomnilnik bliskovnega pogona QSPI ni na voljo takoj po ponovnem zagonu naprave (ločeno morate inicializirati način preslikave pomnilnika), in drugič, tega pomnilnika ne morete »flash« z znan zagonski nalagalnik.

Posledično je bilo odločeno, da se vsa koda poveže v QSPI in jo utripa s samonapisanim nalagalnikom, ki bo prejel zahtevano binarno datoteko prek TFTP.

Rezultat

Ideja o prenosu te knjižnice na Embox se je pojavila pred približno enim letom, vendar je bila zaradi različnih razlogov vedno znova odložena. Eden od njih je podpora za libstdc++ in standardno knjižnico predlog. Problem podpore za C++ v Emboxu je izven obsega tega članka, zato bom tukaj povedal samo to, da smo to podporo uspeli doseči v pravi meri, da ta knjižnica deluje 🙂

Na koncu so bile te težave premagane (vsaj toliko, da je primer OpenCV deloval) in primer je stekel. Za iskanje meja s pomočjo filtra Canny tabla potrebuje dolgih 40 sekund. To je seveda predolgo (obstajajo premisleki, kako to zadevo optimizirati, o tem bo mogoče napisati poseben članek v primeru uspeha).

OpenCV na STM32F7-Discovery

Vendar je bil vmesni cilj ustvariti prototip, ki bo pokazal temeljno možnost izvajanja OpenCV na STM32, oziroma ta cilj je bil dosežen, hura!

tl;dr: navodila po korakih

0: Prenesite vire Embox, kot je ta:

    git clone https://github.com/embox/embox && cd ./embox

1: Začnimo s sestavljanjem zagonskega nalagalnika, ki bo "flashnil" bliskovni pogon QSPI.

    make confload-arm/stm32f7cube

Zdaj morate konfigurirati omrežje, ker. Sliko bomo naložili prek TFTP. Če želite nastaviti naslove IP plošče in gostitelja, morate urediti conf/rootfs/network.

Primer konfiguracije:

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 - naslov gostitelja, od koder se bo slika naložila, address - naslov plošče.

Po tem zberemo zagonski nalagalnik:

    make

2: Običajno nalaganje zagonskega nalagalnika (oprostite za besedno igro) na ploščo - tukaj ni nič posebnega, to morate storiti kot pri kateri koli drugi aplikaciji za STM32F7Discovery. Če ne veste, kako to storiti, lahko preberete o tem tukaj.
3: Prevajanje slike s konfiguracijo za OpenCV.

    make confload-platform/opencv/stm32f7discovery
    make

4: Izvleček iz odsekov ELF, ki bodo zapisani v QSPI v 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 imeniku conf je skript, ki to naredi, tako da ga lahko zaženete

    ./conf/qspi_objcopy.sh # Нужный бинарник -- build/base/bin/qspi.bin

5: Prek tftp prenesite qspi.bin.bin na bliskovni pogon QSPI. Če želite to narediti, na gostitelju kopirajte qspi.bin v korensko mapo strežnika tftp (običajno /srv/tftp/ ali /var/lib/tftpboot/; paketi za ustrezen strežnik so na voljo v večini priljubljenih distribucij, običajno imenovanih tftpd ali tftp-hpa, včasih morate narediti systemctl start tftpd.service začeti).

    # вариант для 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 zagonskem nalagalniku) morate izvesti naslednji ukaz (predvidevamo, da ima strežnik naslov 192.168.2.1):

    embox> qspi_loader qspi.bin 192.168.2.1

6: Z ukazom goto morate "skočiti" v pomnilnik QSPI. Določena lokacija se bo razlikovala glede na to, kako je slika povezana, ta naslov lahko vidite z ukazom mem 0x90000000 (začetni naslov se prilega drugi 32-bitni besedi slike); sklad boste morali tudi označiti -s, je naslov sklada 0x90000000, primer:

    embox>mem 0x90000000
    0x90000000:     0x20023200  0x9000c27f  0x9000c275  0x9000c275
                      ↑           ↑
              это адрес    это  адрес 
                стэка        первой
                           инструкции

    embox>goto -i 0x9000c27f -s 0x20023200 # Флаг -i нужен чтобы запретить прерывания во время инициализации системы

    < Начиная отсюда будет вывод не загрузчика, а образа с OpenCV >

7: Zagon

    embox> edges 20

in uživajte v 40-sekundnem iskanju meje 🙂

Če gre kaj narobe - napišite težavo naše skladišče, ali na poštni seznam [e-pošta zaščitena], ali v komentarju tukaj.

Vir: www.habr.com

Dodaj komentar