OpenCV på STM32F7-Discovery

OpenCV på STM32F7-Discovery Jeg er en af ​​udviklerne af styresystemet Embox, og i denne artikel vil jeg tale om, hvordan jeg formåede at køre OpenCV på STM32746G-kortet.

Hvis du skriver noget som "OpenCV on STM32 board" i en søgemaskine, kan du finde en del mennesker, der er interesserede i at bruge dette bibliotek på STM32 boards eller andre mikrocontrollere.
Der er adskillige videoer, der efter navnet at dømme burde demonstrere, hvad der er brug for, men normalt (i alle de videoer, jeg så) på STM32-kortet, var det kun billedet, der blev modtaget fra kameraet, og resultatet blev vist på skærmen, og selve billedbehandlingen foregik enten på en almindelig computer eller på mere kraftfulde boards (for eksempel Raspberry Pi).

Hvorfor er det svært?

Populariteten af ​​søgeforespørgsler forklares med, at OpenCV er det mest populære computervision-bibliotek, hvilket betyder, at flere udviklere kender det, og muligheden for at køre desktop-klar kode på en mikrocontroller forenkler udviklingsprocessen i høj grad. Men hvorfor er der stadig ingen populære færdige opskrifter til at løse dette problem?

Problemet med at bruge OpenCV på små sjaler er relateret til to funktioner:

  • Hvis du kompilerer biblioteket selv med et minimalt sæt moduler, vil det simpelthen ikke passe ind i flashhukommelsen på den samme STM32F7Discovery (selv uden at tage OS i betragtning) på grund af en meget stor kode (flere megabyte instruktioner)
  • Selve biblioteket er skrevet i C++, hvilket betyder
    • Har brug for support til positiv kørselstid (undtagelser osv.)
    • Lidt understøttelse af LibC/Posix, som normalt findes i OS til indlejrede systemer - du har brug for et standard plus-bibliotek og et standard STL-skabelonbibliotek (vektor osv.)

Portering til Embox

Som sædvanlig, før du porterer programmer til operativsystemet, er det en god idé at prøve at bygge det i den form, som udviklerne havde tiltænkt det. I vores tilfælde er der ingen problemer med dette - kildekoden kan findes på github, biblioteket er bygget under GNU/Linux med den sædvanlige cmake.

Den gode nyhed er, at OpenCV kan bygges som et statisk bibliotek ud af boksen, hvilket gør portering lettere. Vi samler et bibliotek med en standardkonfiguration og ser hvor meget plads de fylder. Hvert modul er samlet i et separat bibliotek.

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

Som du kan se på sidste linje, fylder .bss og .data ikke meget, men koden er mere end 70 MiB. Det er klart, at hvis dette er statisk knyttet til en specifik applikation, vil koden blive mindre.

Lad os prøve at smide så mange moduler ud som muligt, så der samles et minimalt eksempel (som f.eks. blot vil udskrive OpenCV-versionen), så vi ser cmake .. -LA og sluk i indstillingerne alt, hvad der slukker.

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

På den ene side er dette kun et modul i biblioteket, på den anden side er dette uden compiler optimering for kodestørrelse (-Os). ~3 MiB kode er stadig ret meget, men giver allerede håb om succes.

Kør i emulatoren

Det er meget nemmere at debugge på emulatoren, så sørg først for, at biblioteket fungerer på qemu. Som en emuleret platform valgte jeg Integrator / CP, fordi for det første er det også ARM, og for det andet understøtter Embox grafikoutput til denne platform.

Embox har en mekanisme til at bygge eksterne biblioteker, ved at bruge den tilføjer vi OpenCV som et modul (passer alle de samme muligheder for den "minimale" build i form af statiske biblioteker), derefter tilføjer jeg en simpel applikation, der ser sådan ud:

version.cpp:

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

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

    return 0;
}

Vi samler systemet, kører det - vi får det forventede output.

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

Det næste trin er at køre nogle eksempler, helst et af de standard, der tilbydes af udviklerne selv. på dit websted. jeg vælger grænsedetektor canny.

Eksemplet skulle omskrives lidt for at vise billedet med resultatet direkte i rammebufferen. Jeg var nødt til at gøre dette, fordi. fungere imshow() kan tegne billeder gennem QT-, GTK- og Windows-grænseflader, som selvfølgelig ikke vil være i konfigurationen til STM32. Faktisk kan QT også køres på STM32F7Discovery, men det vil blive diskuteret i en anden artikel 🙂

Efter en kort afklaring i hvilket format resultatet af kantdetektoren er gemt, får vi et billede.

OpenCV på STM32F7-Discovery

originalt billede

OpenCV på STM32F7-Discovery

Outcome

Kører på STM32F7Discovery

På 32F746GDISCOVERY er der flere hardwarehukommelsessektioner, som vi kan bruge på den ene eller anden måde

  1. 320KiB RAM
  2. 1MiB flash til billede
  3. 8MB SDRAM
  4. 16MiB QSPI NAND Flash
  5. microSD-kortslot

Et SD-kort kan bruges til at gemme billeder, men i forbindelse med at køre et minimalt eksempel er dette ikke særlig nyttigt.
Skærmen har en opløsning på 480×272, hvilket betyder, at framebuffer-hukommelsen bliver på 522 bytes i en dybde på 240 bit, dvs. dette er mere end størrelsen af ​​RAM, så framebufferen og heapen (som vil være påkrævet, inklusive for OpenCV, for at gemme data til billeder og hjælpestrukturer) vil være placeret i SDRAM, alt andet (hukommelse til stakke og andre systembehov) ) vil gå til RAM .

Hvis vi tager minimumskonfigurationen for STM32F7Discovery (smid hele netværket ud, alle kommandoer, gør stakke så små som muligt osv.) og tilføjer OpenCV med eksempler der, vil den nødvendige hukommelse være som følger:

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

For dem, der ikke er særlig bekendt med, hvilke sektioner der går hvor, vil jeg forklare: in .text и .rodata instruktioner og konstanter (groft sagt, skrivebeskyttede data) ligger i .data dataene kan ændres, .bss der er "nullede" variabler, som ikke desto mindre har brug for en plads (denne sektion vil "gå" til RAM).

Den gode nyhed er det .data/.bss skulle passe, men med .text problemet er, at der kun er 1MiB hukommelse til billedet. Kan smides ud .text billedet fra eksemplet og læs det f.eks. fra SD-kortet ind i hukommelsen ved opstart, men fruits.png vejer omkring 330KiB, så dette løser ikke problemet: de fleste .text består af OpenCV-koden.

I det store og hele er der kun én ting tilbage - at indlæse en del af koden på en QSPI-flash (den har en speciel driftstilstand til at kortlægge hukommelsen til systembussen, så processoren kan få direkte adgang til disse data). I dette tilfælde opstår der et problem: For det første er hukommelsen på et QSPI-flashdrev ikke tilgængelig umiddelbart efter, at enheden er genstartet (du skal separat initialisere den hukommelseskortede tilstand), og for det andet kan du ikke "flash" denne hukommelse med en velkendt bootloader.

Som et resultat blev det besluttet at linke al koden i QSPI og flashe den med en selvskrevet loader, der vil modtage den nødvendige binære via TFTP.

Outcome

Ideen om at portere dette bibliotek til Embox dukkede op for omkring et år siden, men igen og igen blev det udskudt af forskellige årsager. En af dem er understøttelse af libstdc++ og standardskabelonbiblioteket. Problemet med C++-understøttelse i Embox ligger uden for rammerne af denne artikel, så her vil jeg kun sige, at det lykkedes os at opnå denne support i den rigtige mængde for, at dette bibliotek kunne fungere 🙂

I sidste ende blev disse problemer overvundet (i det mindste nok til at OpenCV-eksemplet virkede), og eksemplet kørte. Det tager 40 lange sekunder for brættet at søge efter grænser ved hjælp af Canny-filteret. Dette er selvfølgelig for langt (der er overvejelser om, hvordan man optimerer denne sag, det vil være muligt at skrive en separat artikel om dette i tilfælde af succes).

OpenCV på STM32F7-Discovery

Det mellemliggende mål var dog at skabe en prototype, der skal vise den fundamentale mulighed for at køre OpenCV på henholdsvis STM32, dette mål blev nået, hurra!

tl;dr: trin for trin instruktioner

0: Download Embox-kilder som dette:

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

1: Lad os starte med at samle en bootloader, der vil "flashe" et QSPI-flashdrev.

    make confload-arm/stm32f7cube

Nu skal du konfigurere netværket, fordi. Vi uploader billedet via TFTP. For at indstille boardet og værtens IP-adresser skal du redigere conf/rootfs/netværket.

Konfigurationseksempel:

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 - værtsadresse, hvorfra billedet vil blive indlæst, address - bestyrelsens adresse.

Derefter samler vi bootloaderen:

    make

2: Den sædvanlige indlæsning af bootloaderen (undskyld ordspillet) på tavlen - der er ikke noget specifikt her, du skal gøre det som for enhver anden applikation til STM32F7Discovery. Hvis du ikke ved, hvordan du gør det, kan du læse om det her.
3: Kompilere et billede med en config til OpenCV.

    make confload-platform/opencv/stm32f7discovery
    make

4: Uddrag fra ELF-sektioner, der skal skrives til QSPI til 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

Der er et script i conf-mappen, der gør dette, så du kan køre det

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

5: Brug tftp til at downloade qspi.bin.bin til et QSPI-flashdrev. For at gøre dette på værten skal du kopiere qspi.bin til rodmappen på tftp-serveren (normalt /srv/tftp/ eller /var/lib/tftpboot/; pakker til den tilsvarende server er tilgængelige i de fleste populære distributioner, normalt kaldet tftpd eller tftp-hpa, nogle gange er du nødt til at gøre systemctl start tftpd.service at begynde).

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

På Embox (dvs. i bootloaderen) skal du udføre følgende kommando (vi antager, at serveren har adressen 192.168.2.1):

    embox> qspi_loader qspi.bin 192.168.2.1

6: Med kommando goto du skal "hoppe" ind i QSPI-hukommelsen. Den specifikke placering vil variere afhængigt af hvordan billedet er linket, du kan se denne adresse med kommandoen mem 0x90000000 (startadressen passer ind i billedets andet 32-bit ord); du skal også markere stakken -s, stakadressen er på 0x90000000, eksempel:

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

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

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

7: Lancering

    embox> edges 20

og nyd den 40 sekunder lange grænsesøgning 🙂

Hvis noget går galt - skriv et problem ind vores depot, eller til mailinglisten [e-mail beskyttet], eller i en kommentar her.

Kilde: www.habr.com

Tilføj en kommentar