OpenCV STM32F7-Discoveryssa

OpenCV STM32F7-Discoveryssa Olen yksi käyttöjärjestelmän kehittäjistä Embox, ja tässä artikkelissa puhun siitä, kuinka onnistuin ajamaan OpenCV:tä STM32746G-kortilla.

Jos kirjoitat hakukoneeseen esimerkiksi "OpenCV STM32-kortilla", voit löytää useita ihmisiä, jotka ovat kiinnostuneita käyttämään tätä kirjastoa STM32-korteilla tai muilla mikrokontrollereilla.
On olemassa useita videoita, joiden nimestä päätellen pitäisi osoittaa, mitä tarvitaan, mutta yleensä (kaikissa videoissa, jotka näin) STM32-kortilla vain kuva vastaanotettiin kamerasta ja tulos näytettiin näytöllä, ja itse kuvankäsittely tehtiin joko tavallisella tietokoneella tai tehokkaammilla levyillä (esim. Raspberry Pi).

Miksi se on vaikeaa?

Hakukyselyiden suosio selittyy sillä, että OpenCV on suosituin tietokonenäkökirjasto, mikä tarkoittaa, että useammat kehittäjät tuntevat sen, ja kyky ajaa työpöytävalmiutta koodia mikro-ohjaimella yksinkertaistaa kehitysprosessia huomattavasti. Mutta miksi vieläkään ei ole suosittuja valmiita reseptejä tämän ongelman ratkaisemiseksi?

Ongelma OpenCV:n käyttämisessä pienissä huiveissa liittyy kahteen ominaisuuteen:

  • Jos käännät kirjaston jopa minimaalisella moduulijoukolla, se ei yksinkertaisesti mahdu saman STM32F7Discoveryn flash-muistiin (edes ottamatta huomioon käyttöjärjestelmää) erittäin suuren koodin vuoksi (useita megatavuja ohjeita)
  • Itse kirjasto on kirjoitettu C++:lla, mikä tarkoittaa
    • Tarvitsevat tukea positiiviselle suoritusajalle (poikkeukset jne.)
    • Pieni tuki LibC/Posixille, jota yleensä löytyy sulautettujen järjestelmien käyttöjärjestelmästä - tarvitset tavallisen plus-kirjaston ja standardin STL-mallikirjaston (vektori jne.)

Siirtäminen Emboxiin

Kuten tavallista, ennen ohjelmien siirtämistä käyttöjärjestelmään, on hyvä idea yrittää rakentaa se siinä muodossa, jossa kehittäjät sen suunnittelivat. Meidän tapauksessamme tässä ei ole ongelmia - lähdekoodi löytyy osoitteesta github, kirjasto on rakennettu GNU/Linuxin alle tavallisella cmakella.

Hyvä uutinen on, että OpenCV voidaan rakentaa staattisena kirjastona, joka tekee siirtämisestä helpompaa. Keräämme kirjaston vakiokonfiguraatiolla ja katsomme kuinka paljon tilaa ne vievät. Jokainen moduuli on koottu erilliseen kirjastoon.

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

Kuten viimeiseltä riviltä näet, .bss ja .data eivät vie paljon tilaa, mutta koodi on yli 70 MiB. On selvää, että jos tämä on staattisesti linkitetty tiettyyn sovellukseen, koodista tulee vähemmän.

Yritetään heittää ulos mahdollisimman monta moduulia, jotta kootaan minimaalinen esimerkki (joka esimerkiksi yksinkertaisesti tulostaa OpenCV-version), joten katsomme cmake .. -LA ja sammuta vaihtoehdoista kaikki, mikä sammuu.

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

Toisaalta tämä on vain yksi kirjaston moduuli, toisaalta tämä on ilman kääntäjän optimointia koodin koon (-Os). ~3 MiB koodia on vielä aika paljon, mutta antaa jo toivoa onnistumisesta.

Suorita emulaattori

On paljon helpompaa tehdä virheenkorjaus emulaattorissa, joten varmista ensin, että kirjasto toimii qemussa. Emuloiduksi alustaksi valitsin Integrator / CP, koska Ensinnäkin se on myös ARM, ja toiseksi Embox tukee tämän alustan grafiikkaa.

Emboxissa on mekanismi ulkoisten kirjastojen rakentamiseen, sen avulla lisäämme OpenCV:n moduuliksi (välitämme kaikki samat vaihtoehdot "minimaaliselle" rakennukselle staattisten kirjastojen muodossa), sen jälkeen lisään yksinkertaisen sovelluksen, joka näyttää tältä:

version.cpp:

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

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

    return 0;
}

Kokoamme järjestelmän, käytämme sitä - saamme odotetun tuloksen.

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

Seuraava askel on suorittaa jokin esimerkki, mieluiten jokin kehittäjien itsensä tarjoamista vakiomalleista. sivustollasi. minä valitsin näppärä rajanilmaisin.

Esimerkkiä piti kirjoittaa hieman uudelleen, jotta kuva näkyisi tuloksen kanssa suoraan kehyspuskurissa. Minun oli tehtävä tämä, koska. toiminto imshow() voi piirtää kuvia QT-, GTK- ja Windows-rajapintojen kautta, mikä ei tietenkään ole STM32:n konfiguraatiossa. Itse asiassa QT voidaan suorittaa myös STM32F7Discoveryllä, mutta tästä keskustellaan toisessa artikkelissa 🙂

Lyhyen selvennyksen jälkeen, missä muodossa reunatunnistimen tulos on tallennettu, saadaan kuva.

OpenCV STM32F7-Discoveryssa

alkuperäinen kuva

OpenCV STM32F7-Discoveryssa

Tulos

Toimii STM32F7Discoveryllä

32F746GDISCOVERYssä on useita laitteistomuistiosioita, joita voimme käyttää tavalla tai toisella

  1. 320KiB RAM-muistia
  2. 1 MiB:n salama kuvaa varten
  3. 8 MiB SDRAM
  4. 16 MiB QSPI NAND -salama
  5. microSD-korttipaikka

SD-korttia voidaan käyttää kuvien tallentamiseen, mutta minimaalisen esimerkin yhteydessä tämä ei ole kovin hyödyllistä.
Näytön resoluutio on 480×272, mikä tarkoittaa, että kehyspuskurimuisti tulee olemaan 522 240 tavua 32 bitin syvyydessä, ts. tämä on enemmän kuin RAM-muistin koko, joten kehyspuskuri ja kasa (jotka tarvitaan myös OpenCV:ssä kuvien ja apurakenteiden tietojen tallentamiseen) sijoitetaan SDRAM:iin, kaikki muu (muisti pinoille ja muille järjestelmätarpeille) ) siirtyy RAM-muistiin.

Jos otamme STM32F7Discoveryn vähimmäiskonfiguraation (heitetään pois koko verkko, kaikki komennot, tehdään pinot mahdollisimman pieneksi jne.) ja lisätään siihen esimerkkejä sisältävä OpenCV, tarvittava muisti on seuraava:

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

Selitän niille, jotka eivät ole kovin perillä siitä, mitkä osiot menevät minne .text и .rodata käskyt ja vakiot (karkeasti sanottuna vain luku -tiedot) ovat sisällä .data data on muuttuva, .bss on "nollattuja" muuttujia, jotka kuitenkin tarvitsevat paikan (tämä osio "menee" RAM:iin).

Hyvä uutinen on se .data/.bss pitäisi sopia, mutta kanssa .text Ongelmana on, että kuvalle on vain 1 MiB muistia. Voidaan heittää pois .text kuva esimerkistä ja lue se esimerkiksi SD-kortilta muistiin käynnistyksen yhteydessä, mutta fruits.png painaa noin 330KiB, joten tämä ei ratkaise ongelmaa: useimmat .text koostuu OpenCV-koodista.

Yleisesti ottaen on jäljellä vain yksi asia - ladata osan koodista QSPI-flashille (sillä on erityinen toimintatapa muistin yhdistämiseksi järjestelmäväylään, jotta prosessori pääsee suoraan näihin tietoihin). Tässä tapauksessa syntyy ongelma: ensinnäkin QSPI-flash-aseman muisti ei ole käytettävissä heti laitteen uudelleenkäynnistyksen jälkeen (sinun on alustettava muistikartoitettu tila erikseen), ja toiseksi et voi "flash"a tätä muistia tuttu käynnistyslatain.

Tämän seurauksena päätettiin linkittää kaikki koodi QSPI:ssä ja flash se itse kirjoitetulla latauslaitteella, joka vastaanottaa vaaditun binaarin TFTP:n kautta.

Tulos

Ajatus tämän kirjaston siirtämisestä Emboxiin syntyi noin vuosi sitten, mutta kerta toisensa jälkeen sitä lykättiin eri syistä. Yksi niistä on tuki libstdc++:lle ja vakiomallikirjastolle. Emboxin C++-tuen ongelma ei kuulu tämän artikkelin piiriin, joten sanon tässä vain, että onnistuimme saavuttamaan tämän tuen oikean määrän, jotta tämä kirjasto toimisi 🙂

Lopulta nämä ongelmat voitettiin (ainakin tarpeeksi, jotta OpenCV-esimerkki toimisi), ja esimerkki juoksi. Lautalla kestää 40 pitkää sekuntia etsiä rajoja Canny-suodattimen avulla. Tämä on tietysti liian pitkä (on pohdintoja tämän asian optimoinnista, tästä on mahdollista kirjoittaa erillinen artikkeli onnistumisen yhteydessä).

OpenCV STM32F7-Discoveryssa

Välitavoitteena oli kuitenkin luoda prototyyppi, joka näyttää perustavanlaatuisen mahdollisuuden käyttää OpenCV:tä STM32:lla, tämä tavoite saavutettiin, hurraa!

tl;dr: vaiheittaiset ohjeet

0: Lataa Embox-lähteitä, kuten tämä:

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

1: Aloitetaan kokoamalla käynnistyslatain, joka "vilkkaa" QSPI-flash-aseman.

    make confload-arm/stm32f7cube

Nyt sinun on määritettävä verkko, koska. Lataamme kuvan TFTP:n kautta. Jotta voit asettaa kortin ja isännän IP-osoitteet, sinun on muokattava conf/rootfs/network.

Esimerkki kokoonpanosta:

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 - isäntäosoite, josta kuva ladataan, address - hallituksen osoite.

Tämän jälkeen keräämme käynnistyslataimen:

    make

2: Tavallinen käynnistyslataimen lataus (anteeksi sanapeli) laudalla - tässä ei ole mitään erityistä, sinun on tehtävä se kuten minkä tahansa muun STM32F7Discoveryn sovelluksen kohdalla. Jos et tiedä miten se tehdään, voit lukea siitä täällä.
3: Kuvan kokoaminen konfiguraatiolla OpenCV:tä varten.

    make confload-platform/opencv/stm32f7discovery
    make

4: Ote ELF-osioista, jotka kirjoitetaan QSPI:hen qspi.biniin

    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

Conf-hakemistossa on komentosarja, joka tekee tämän, joten voit suorittaa sen

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

5: Lataa qspi.bin.bin QSPI-muistitikulle tftp:n avulla. Kopioi isännässä tiedosto qspi.bin tftp-palvelimen juurikansioon (yleensä /srv/tftp/ tai /var/lib/tftpboot/; vastaavan palvelimen paketit ovat saatavilla suosituimmissa jakeluissa, yleensä ns. tftpd tai tftp-hpa, joskus sinun on tehtävä systemctl start tftpd.service aloittaa).

    # вариант для tftpd
    sudo cp build/base/bin/qspi.bin /srv/tftp
    # вариант для tftp-hpa
    sudo cp build/base/bin/qspi.bin /var/lib/tftpboot

Emboxissa (eli käynnistyslataimessa) sinun on suoritettava seuraava komento (oletamme, että palvelimen osoite on 192.168.2.1):

    embox> qspi_loader qspi.bin 192.168.2.1

6: Komennolla goto sinun täytyy "hyppää" QSPI-muistiin. Tarkka sijainti vaihtelee sen mukaan, miten kuva on linkitetty, näet tämän osoitteen komennolla mem 0x90000000 (aloitusosoite sopii kuvan toiseen 32-bittiseen sanaan); sinun tulee myös merkitä pino -s, pinon osoite on 0x90000000, esimerkki:

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

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

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

7: Käynnistä

    embox> edges 20

ja nauti 40 sekunnin rajahausta 🙂

Jos jokin menee pieleen - kirjoita ongelma varastomme, tai postituslistalle [sähköposti suojattu], tai tässä kommentissa.

Lähde: will.com

Lisää kommentti