OpenCV na STM32F7-Discovery

OpenCV na STM32F7-Discovery Ja sam jedan od programera operativnog sustava Embox, a u ovom ću članku objasniti kako sam uspio pokrenuti OpenCV na STM32746G ploči.

Ako u pretraživač upišete nešto poput „OpenCV na STM32 ploči“, možete pronaći priličan broj ljudi koji su zainteresirani za korištenje ove biblioteke na STM32 pločama ili drugim mikrokontrolerima.
Postoji nekoliko videa koji, sudeći po naslovu, trebaju pokazati ono što nam treba, ali obično (u svim videima koje sam vidio) na STM32 ploči se samo prikazivala slika s kamere i rezultat na ekranu, dok se sama obrada slike odvijala ili na običnom računalu ili na snažnijim pločama (na primjer, Raspberry Pi).

Zašto je to teško?

Popularnost pretraživačkih upita objašnjava se time što je OpenCV najpopularnija biblioteka za računalni vid, što znači da su mnogi programeri upoznati s njom, a mogućnost pokretanja gotovog koda za radnu površinu na mikrokontroleru značajno olakšava proces razvoja. No, zašto još uvijek nema nekih popularnih gotovih recepata za rješavanje ovog problema?

Problem korištenja OpenCV-a na malim pločama povezan je s dvije osobitosti:

  • Ako se biblioteka kompajlira čak i s minimalnim skupom modula, jednostavno neće stati u flash memoriju iste STM32F7Discovery (čak i bez uzimanja u obzir OS) zbog jako velikog koda (nekoliko megabajta instrukcija).
  • Sama biblioteka napisana je na C++, što znači
    • Potrebna je podrška za plus runtime (izuzeci i sl.)
    • Nedovoljna je podrška za LibC/Posix, koja obično postoji u operativnim sustavima za ugrađene sustave — potrebna je standardna biblioteka plus i standardna biblioteka predloška STL (vector i sl.).

Portiranje na Embox

Kao i obično, prije portiranja bilo kakvih programa u operativni sustav, dobro je pokušati ga sastaviti u obliku u kojem su ga zamislili programeri. U našem slučaju s ovim nema problema — izvorni kodovi mogu se pronaći na githubu, biblioteka se sastavlja s GNU/Linux uobičajenim cmake-om.

Od dobrih vijesti — OpenCV se inače može sastaviti kao statička biblioteka, što olakšava portiranje. Sastavljamo biblioteku sa standardnom konfiguracijom i gledamo koliko prostora zauzimaju. Svaki modul se sastavlja u zasebnu biblioteku.

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

Kao što se može vidjeti iz posljednjeg retka, .bss i .data ne zauzimaju toliko puno prostora, ali kod ima više od 70 MiB. Jasno je da će, ako se to statički poveže s određenom aplikacijom, koda biti manje.

Pokušat ćemo ukloniti što je više modula moguće da bismo dobili minimalni primjer (koji, na primjer, jednostavno ispisuje verziju OpenCV), pa da vidimo. cmake .. -LA i isključujemo sve što se može isključiti u opcijama.

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

S jedne strane, to je samo jedan modul biblioteke, ali s druge strane, to je bez optimizacije kompajlera za veličinu koda (-Os). ~3 MiB koda — to je još uvijek dovoljno puno, ali već pruža nadu za uspjeh.

Pokretanje u emulatoru

Debugging na emulatoru je puno jednostavnije, pa se najprije uvjeravamo da biblioteka radi na qemu. Kao emulirana platforma odabrao sam Integrator/CP, jer, prvo, to je također ARM, a drugo, Embox podržava ispis grafike za ovu platformu.

U Emboxu postoji mehanizam za izradu vanjskih biblioteka, njegovom pomoći dodajemo OpenCV kao modul (proslijedivši sve iste opcije za 'minimalnu' izradu u obliku statičkih biblioteka), a nakon toga dodajem najjednostavniju aplikaciju koja izgleda ovako:

version.cpp:

#include 
#include 

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

    return 0;
}

Gradimo sustav, pokrećemo — dobijamo očekivani izlaz.

root@embox: /#opencv_version                                                     
OpenCV: 
Opća konfiguracija za OpenCV 4.0.1 =====================================
  Kontrola verzije:               bd6927bdf-dirty

  Platforma:
    Vrijeme:                      2019-06-21T10:02:18Z
    Host:                        Linux 5.1.7-arch1-1-ARCH x86_64
    Cilj:                        Opći arm-unknown-none
    CMake:                       3.14.5
    CMake generator:             Unix Makefiles
    CMake alat za izgradnju:     /usr/bin/make
    Konfiguracija:               Debug

  CPU/HW značajke:
    Osnovno:
      zatraženo:                 DETECT
      onemogućeno:               VFPV3 NEON

  C/C++:
    Izgrađeno kao dinamičke biblioteke?:      NE

Sljedeći korak — pokrenuti neki primjer, najbolje neki standardni od onih koji nude sami programeri na svojoj web stranici. Odabrao sam detektor rubova Canny.

Primjer je trebalo malo prepraviti kako bi se slika s rezultatom direktno prikazivala u frame bufferu. To je bilo potrebno jer funkcija imshow() može prikazivati slike putem QT-a, GTK-a i Windows-a, kojih, naravno, neće biti u konfiguraciji za STM32. U stvari, QT se može pokrenuti na STM32F7Discovery, ali o tome će biti riječi u drugom članku 🙂

Nakon kratkog istraživanja u kojem formatu se čuva rezultat rada detektora rubova, dobivamo sliku.

OpenCV na STM32F7-Discovery

Izvorna slika

OpenCV na STM32F7-Discovery

Rezultat

Pokretanje na STM32F7Discovery

Na 32F746GDISCOVERY postoji nekoliko hardverskih dijelova memorije koje možemo koristiti

  1. 320KiB radne memorije
  2. 1MiB flash memorije za sliku
  3. 8MiB SDRAM-a
  4. 16MiB QSPI NAND-flash
  5. Utikač za microSD karticu

SD-karticu možemo koristiti za pohranu slika, ali u kontekstu pokretanja minimalnog primjera, to nije previše korisno.
Zaslon ima rezoluciju 480×272, što znači da će memorija za frame buffer iznositi 522 240 bajtova pri dubini od 32 bita, tj. to je više od veličine radne memorije, tako da ćemo frame buffer i hrpu (koja će biti potrebna, između ostalog, za OpenCV da drži podatke za slike i pomoćne strukture) smjestiti u SDRAM, dok će sve ostalo (memorija za stekove i ostale sistemske potrebe) otići u RAM.

Ako uzmemo minimalnu konfiguraciju za STM32F7Discovery (izbacimo cijelu mrežu, sve naredbe, napravimo stekove što manjim itd.) i dodamo OpenCV s primjerima, potrebna će memorija biti sljedeća:

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

Za one koji nisu upoznati s tim koja se odjeljenja gdje nalaze, objašnjavam: u .text i .rodata se nalaze upute i konstante (grobno rečeno, readonly-podaci), u .data se nalaze promjenjivi podaci, u .bss se nalaze «nula» varijable kojima je ipak potrebno mjesto (ovo odjeljenje će «otići» u RAM).

Dobra vijest je da .data/.bss moraju se smjestiti, ali .text problem - za sliku ima samo 1MiB memorije. Može se izbaciti iz .text primjera i čitati je, na primjer, s SD kartice u memoriju pri pokretanju, ali fruits.png teži otprilike 330KiB, tako da to ne rješava problem: veći dio .text se sastoji upravo od koda OpenCV.

U suštini, ostaje samo jedno - učitati dio koda na QSPI-flash (ima poseban način rada za mapiranje memorije na sistemsku sabirnicu, tako da procesor može izravno pristupiti tim podacima). Međutim, javlja se problem: prvo, QSPI-flash memorija nije dostupna odmah nakon ponovnog pokretanja uređaja (potrebno je zasebno inicijalizirati mode mapirane memorije), drugo, ovu memoriju nije moguće «programirati» uobičajenim bootloaderom.

Na kraju je odlučeno da se cijeli kod poveže u QSPI, a da se ugradi vlastitim bootloaderom koji će dobiti potrebni binarni fajl putem TFTP-a.

Rezultat

Ideja o portovanju ove biblioteke na Embox pojavila se još prije otprilike godinu dana, ali se to iz različitih razloga neprekidno odgađalo. Jedan od njih je podrška za libstdc++ i standardnu biblioteku predložaka. Problem podrške za C++ u Emboxu prelazi okvire ovog članka, stoga ću ovdje samo reći da smo uspjeli postići tu podršku u potrebnoj mjeri za rad ove biblioteke 🙂

Na kraju su ti problemi prevaziđeni (barem u dovoljnoj mjeri za rad primjera OpenCV), i primjer je pokrenut. 40 dugih sekundi traje pretraživanje granica filtriranjem Canny-ja. To je, naravno, predugo (postoje razmišljanja o tome kako to optimizirati, o tome bi se moglo napisati poseban članak u slučaju uspjeha).

OpenCV na STM32F7-Discovery

Ipak, među ciljevima je bio razvoj prototipa koji će pokazati principijelnu mogućnost pokretanja OpenCV-a na STM32, i stoga je taj cilj postignut, hura!

tl;dr: upute korak po korak

0: Preuzimamo izvorne kodove Embox, na primjer ovako:

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

1: Počinjemo sa izradom bootloadera koji će «programirati» QSPI-flash.

    make confload-arm/stm32f7cube

Sada je potrebno konfigurirati mrežu, jer ćemo učitati sliku putem TFTP-a. Da bismo postavili IP adrese za ploču i domaćina, potrebno je promijeniti datoteku conf/rootfs/network.

Primjer 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 — adresa domaćina sa kojeg će se slika učitati, address — adresa ploče.

Nakon toga sastavljamo učitavač:

    make

2: Uobičajeno učitavanje učitavača (ispričavam se na igri riječi) na ploču — ovdje nema ništa specifično, potrebno je to učiniti kao za bilo koju drugu aplikaciju za STM32F7Discovery. Ako ne znate kako to učiniti, možete pročitati o tome ovdje.
3: Kompilacija slike s konfiguracijom za OpenCV.

    make confload-platform/opencv/stm32f7discovery
    make

4: Izdvajanje iz ELF sekcija koje je potrebno zapisati u QSPI, u 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

U direktoriju conf nalazi se skripta koja to radi, stoga je možete pokrenuti

    ./conf/qspi_objcopy.sh # Potreban binarni -- build/base/bin/qspi.bin

5: Putem tftp učitajte qspi.bin na QSPI-flash. Na domaćinu je potrebno kopirati qspi.bin u root mapu tftp poslužitelja (to je obično /srv/tftp/ ili /var/lib/tftpboot/; paketi za odgovarajući poslužitelj postoje u većini popularnih distribucija, obično se zove tftpd ili tftp-hpa, ponekad je potrebno systemctl start tftpd.service za pokretanje).

    # вариант для 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. u učitavaču) potrebno je izvršiti ovakav naredbu (pretpostavljamo da je adresa poslužitelja 192.168.2.1):

    embox> qspi_loader qspi.bin 192.168.2.1

6: Pomoću naredbe goto potrebno je 'skočiti' u QSPI memoriju. Konkretna lokacija će varirati ovisno o tome kako se slika spaja, taj adresa možete vidjeti naredbom mem 0x90000000 (adresa početka nalazi se u drugom 32-bitnom wordu slike); također, potrebno je postaviti stack s oznakom -s, adresa stacka se nalazi na adresi 0x90000000, primjer:

    embox>mem 0x90000000
    0x90000000:     0x20023200  0x9000c27f  0x9000c275  0x9000c275
                      ↑           ↑
              to je adresa    to je  adresa 
                stacka        prve
                           instrukcije

    embox>goto -i 0x9000c27f -s 0x20023200 # Oznaka -i je potrebna da se prekidanja onemoguće tijekom inicijalizacije sustava

7: Pokrećemo

    embox> edges 20

i uživamo u 40-sekundnoj potrazi za rubovima 🙂

Ako nešto pođe po zlu — pišite issue u naš repozitorij, ili na mailing listu embox-devel@googlegroups.com, ili u komentarima ovdje.

Izvor: habr.com

Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere 🔥 Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere | ProHoster