OpenCV na STM32F7-Discovery

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

Ako u tražilicu upišete nešto poput "OpenCV na STM32 ploči", možete pronaći dosta ljudi koji su zainteresirani za korištenje ove biblioteke na STM32 pločama ili drugim mikrokontrolerima.
Postoji nekoliko videa koji bi, sudeći po nazivu, trebali demonstrirati što je potrebno, ali obično (u svim videima koje sam vidio) na STM32 ploči samo se prima slika s kamere i rezultat se prikazuje na ekranu, a sama obrada slike radila se ili na običnom računalu, ili na moćnijim pločama (primjerice Raspberry Pi).

Zašto je teško?

Popularnost upita za pretraživanje objašnjava se činjenicom da je OpenCV najpopularnija biblioteka računalnog vida, što znači da je više programera upoznato s njom, a mogućnost pokretanja koda spremnog za desktop na mikrokontroleru uvelike pojednostavljuje razvojni proces. Ali zašto još uvijek nema popularnih gotovih recepata za rješavanje ovog problema?

Problem korištenja OpenCV-a na malim šalovima povezan je s dvije značajke:

  • Ako kompajlirate biblioteku čak i s minimalnim skupom modula, ona jednostavno neće stati u flash memoriju istog STM32F7Discovery (čak i bez uzimanja u obzir OS) zbog vrlo velikog koda (nekoliko megabajta uputa)
  • Sama biblioteka je napisana u C++, što znači
    • Potrebna podrška za pozitivno vrijeme izvođenja (iznimke, itd.)
    • Mala podrška za LibC/Posix, koji se obično nalazi u OS-u za ugrađene sustave - potrebna vam je standardna biblioteka plus i standardna biblioteka STL predložaka (vektor, itd.)

Prijelaz na Embox

Kao i obično, prije prijenosa bilo kojeg programa na operativni sustav, dobra je ideja pokušati ga izgraditi u obliku u kojem su ga programeri zamislili. U našem slučaju s tim nema problema - izvorni kod možete pronaći na githabe, biblioteka je izgrađena pod GNU/Linuxom s uobičajenim cmakeom.

Dobra vijest je da se OpenCV može napraviti kao statička biblioteka odmah po korištenju, što olakšava prijenos. Prikupljamo biblioteku sa standardnom konfiguracijom i vidimo koliko prostora zauzimaju. Svaki modul se prikuplja u zasebnoj biblioteci.

> 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 možete vidjeti iz posljednjeg retka, .bss i .data ne zauzimaju puno prostora, ali je kod veći od 70 MiB. Jasno je da ako je ovo statički povezano s određenom aplikacijom, kod će biti manje.

Pokušajmo izbaciti što je više moguće modula tako da se sastavi minimalni primjer (koji će, na primjer, jednostavno ispisati OpenCV verziju), tako da gledamo cmake .. -LA i isključite u opcijama sve što se isključi.

        -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, ovo je samo jedan modul biblioteke, s druge strane, ovo je bez optimizacije prevoditelja za veličinu koda (-Os). ~3 MiB koda je još uvijek dosta, ali već daje nadu za uspjeh.

Pokrenite u emulatoru

Puno je lakše ispravljati pogreške na emulatoru, pa prvo provjerite radi li biblioteka na qemu. Kao emuliranu platformu odabrao sam Integrator / CP, jer prvo, također je ARM, a drugo, Embox podržava grafički izlaz za ovu platformu.

Embox ima mehanizam za izgradnju vanjskih biblioteka, pomoću njega dodajemo OpenCV kao modul (propuštajući sve iste opcije za "minimalnu" izgradnju u obliku statičkih biblioteka), nakon toga dodam jednostavnu aplikaciju koja izgleda ovako:

version.cpp:

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

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

    return 0;
}

Sastavljamo sustav, pokrećemo ga - dobivamo očekivani 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 включены в сборку и т.п.>

Sljedeći korak je pokretanje nekog primjera, po mogućnosti jednog od standardnih koje nude sami programeri. na vašoj stranici. izabrao sam granični detektor canny.

Primjer je morao biti malo prepisan kako bi se slika s rezultatom prikazala izravno u međuspremniku okvira. Morao sam ovo učiniti, jer. funkcija imshow() može crtati slike kroz QT, GTK i Windows sučelja, što, naravno, definitivno neće biti u konfiguraciji za STM32. Zapravo, QT se također može pokrenuti na STM32F7Discovery, ali o tome će biti riječi u drugom članku 🙂

Nakon kratkog pojašnjenja u kojem je formatu pohranjen rezultat detektora rubova, dobivamo sliku.

OpenCV na STM32F7-Discovery

originalna slika

OpenCV na STM32F7-Discovery

Rezultirati

Radi na STM32F7Discovery

Na 32F746GDISCOVERY postoji nekoliko odjeljaka hardverske memorije koje možemo koristiti na ovaj ili onaj način

  1. 320KiB RAM
  2. 1MiB flash za sliku
  3. 8 MiB SDRAM
  4. 16MiB QSPI NAND Flash
  5. utor za microSD karticu

SD kartica se može koristiti za pohranu slika, ali u kontekstu pokretanja minimalnog primjera, to nije baš korisno.
Zaslon ima rezoluciju od 480×272, što znači da će memorija framebuffera biti 522 bajta na dubini od 240 bita, tj. ovo je više od veličine RAM-a, tako da će međuspremnik okvira i gomila (koji će biti potrebni, uključujući i za OpenCV, za pohranu podataka za slike i pomoćne strukture) biti smješteni u SDRAM, sve ostalo (memorija za hrpe i druge potrebe sustava ) će ići u RAM .

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

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

Za one koji nisu baš upoznati s tim koji dijelovi idu gdje, objasnit ću: in .text и .rodata upute i konstante (grubo rečeno, podaci samo za čitanje) leže u .data podaci su promjenjivi, .bss postoje "nulirane" varijable, koje ipak trebaju mjesto (ovaj odjeljak će "ići" u RAM).

Dobra vijest je da .data/.bss trebao bi odgovarati, ali sa .text problem je što postoji samo 1MiB memorije za sliku. Može se izbaciti .text sliku iz primjera i pročitajte je, na primjer, sa SD kartice u memoriju pri pokretanju, ali fruits.png teži oko 330KiB, tako da to neće riješiti problem: većina .text sastoji se od OpenCV koda.

Uglavnom, preostaje samo jedno - učitavanje dijela koda na QSPI flash (ima poseban način rada za mapiranje memorije na sistemsku sabirnicu, tako da procesor može izravno pristupiti tim podacima). U ovom slučaju nastaje problem: prvo, memorija QSPI flash pogona nije dostupna odmah nakon ponovnog pokretanja uređaja (morate posebno inicijalizirati način mapiranja memorije), i drugo, ne možete "bljeskati" ovu memoriju s poznati bootloader.

Kao rezultat toga, odlučeno je da se sav kod poveže u QSPI, i da se flešuje s programom za učitavanje koji je sam napisao i koji će primiti potrebnu binarnu datoteku putem TFTP-a.

Rezultirati

Ideja o prijenosu ove biblioteke na Embox pojavila se prije otprilike godinu dana, ali je uvijek iznova odgađana iz raznih razloga. Jedan od njih je podrška za libstdc++ i standardnu ​​biblioteku predložaka. Problem C++ podrške u Emboxu je izvan okvira ovog članka, pa ću ovdje samo reći da smo uspjeli postići tu podršku u pravoj količini da ova biblioteka radi 🙂

Na kraju su ti problemi prevladani (barem dovoljno da OpenCV primjer radi) i primjer je pokrenut. Ploči je potrebno 40 dugih sekundi da traži granice pomoću Canny filtera. Ovo je, naravno, predugo (ima razmišljanja kako ovu stvar optimizirati, o tome će se u slučaju uspjeha moći napisati poseban članak).

OpenCV na STM32F7-Discovery

Međutim, srednji cilj je bio stvoriti prototip koji će pokazati temeljnu mogućnost pokretanja OpenCV-a na STM32, odnosno, ovaj cilj je postignut, hura!

tl;dr: upute korak po korak

0: Preuzmite Embox izvore, poput ovog:

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

1: Počnimo s sastavljanjem bootloadera koji će "flashati" QSPI flash pogon.

    make confload-arm/stm32f7cube

Sada morate konfigurirati mrežu, jer. Sliku ćemo učitati putem TFTP-a. Za postavljanje IP adrese ploče i hosta, trebate urediti 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 - host adresa s koje će se slika učitavati, address - adresa odbora.

Nakon toga prikupljamo bootloader:

    make

2: Uobičajeno učitavanje bootloadera (oprostite na dosjetki) na ploči - ovdje nema ničeg posebnog, morate to učiniti kao i za bilo koju drugu aplikaciju za STM32F7Discovery. Ako ne znate kako to učiniti, možete pročitati o tome ovdje.
3: Sastavljanje slike s konfiguracijom za OpenCV.

    make confload-platform/opencv/stm32f7discovery
    make

4: Izvadak iz ELF odjeljaka za pisanje 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

Postoji skripta u conf direktoriju koja to radi, tako da je možete pokrenuti

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

5: Koristeći tftp, preuzmite qspi.bin.bin na QSPI flash pogon. Na glavnom računalu, da biste to učinili, kopirajte qspi.bin u korijensku mapu tftp poslužitelja (obično /srv/tftp/ ili /var/lib/tftpboot/; paketi za odgovarajući poslužitelj dostupni su u većini popularnih distribucija, obično se nazivaju tftpd ili tftp-hpa, ponekad morate učiniti systemctl start tftpd.service poč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. u bootloaderu) trebate izvršiti sljedeću naredbu (pretpostavljamo da poslužitelj ima adresu 192.168.2.1):

    embox> qspi_loader qspi.bin 192.168.2.1

6: Uz naredbu goto potrebno je "skočiti" u QSPI memoriju. Specifična lokacija će se razlikovati ovisno o tome kako je slika povezana, ovu adresu možete vidjeti pomoću naredbe mem 0x90000000 (početna adresa stane u drugu 32-bitnu riječ slike); također ćete morati označiti stog -s, adresa steka je 0x90000000, primjer:

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

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

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

7: Lansiranje

    embox> edges 20

i uživajte u traženju granice od 40 sekundi 🙂

Ako nešto pođe po zlu - upišite problem naše spremište, ili na mailing listu [e-pošta zaštićena], ili u komentaru ovdje.

Izvor: www.habr.com

Dodajte komentar