OpenCV på STM32F7-Discovery

OpenCV på STM32F7-Discovery Jeg er en av utviklerne av operativsystemet embox, og i denne artikkelen vil jeg snakke om hvordan jeg klarte å kjøre OpenCV på STM32746G-kortet.

Hvis du skriver noe som "OpenCV on STM32 board" i en søkemotor, kan du finne ganske mange personer som er interessert i å bruke dette biblioteket på STM32 boards eller andre mikrokontrollere.
Det er flere videoer som, etter navnet å dømme, skal demonstrere hva som trengs, men vanligvis (i alle videoene jeg så) på STM32-tavlen var det bare bildet som ble mottatt fra kameraet og resultatet ble vist på skjermen, og selve bildebehandlingen ble gjort enten på en vanlig datamaskin, eller på kraftigere brett (for eksempel Raspberry Pi).

Hvorfor er det vanskelig?

Populariteten til søk forklares med at OpenCV er det mest populære datasynsbiblioteket, noe som betyr at flere utviklere er kjent med det, og muligheten til å kjøre desktop-klar kode på en mikrokontroller forenkler utviklingsprosessen betraktelig. Men hvorfor er det fortsatt ingen populære ferdige oppskrifter for å løse dette problemet?

Problemet med å bruke OpenCV på små sjal er relatert til to funksjoner:

  • Hvis du kompilerer biblioteket selv med et minimalt sett med moduler, vil det ganske enkelt ikke passe inn i flashminnet til den samme STM32F7Discovery (selv uten å ta hensyn til OS) på grunn av en veldig stor kode (flere megabyte med instruksjoner)
  • Selve biblioteket er skrevet i C++, som betyr
    • Trenger støtte for positiv kjøretid (unntak osv.)
    • Lite støtte for LibC/Posix, som vanligvis finnes i OS for innebygde systemer - du trenger et standard pluss-bibliotek og et standard STL-malbibliotek (vektor, etc.)

Portering til Embox

Som vanlig, før du porterer noen programmer til operativsystemet, er det en god idé å prøve å bygge det i den formen utviklerne hadde til hensikt. I vårt tilfelle er det ingen problemer med dette - kildekoden finner du på githabe, er biblioteket bygget under GNU/Linux med den vanlige cmake.

Den gode nyheten er at OpenCV kan bygges som et statisk bibliotek ut av esken, noe som gjør portering enklere. Vi samler et bibliotek med standard konfigurasjon og ser hvor mye plass de tar. Hver modul er samlet i et eget 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 ser på siste linje tar ikke .bss og .data mye plass, men koden er på mer enn 70 MiB. Det er klart at dersom dette er statisk knyttet til en spesifikk applikasjon, vil koden bli mindre.

La oss prøve å kaste ut så mange moduler som mulig slik at et minimalt eksempel blir satt sammen (som for eksempel ganske enkelt vil gi ut OpenCV-versjonen), så vi ser cmake .. -LA og slå av i alternativene alt som slår seg av.

        -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 siden er dette bare én modul i biblioteket, på den annen side er dette uten kompilatoroptimalisering for kodestørrelse (-Os). ~3 MiB kode er fortsatt ganske mye, men gir allerede håp om suksess.

Kjør i emulatoren

Det er mye lettere å feilsøke på emulatoren, så sørg først for at biblioteket fungerer på qemu. Som en emulert plattform valgte jeg Integrator / CP, fordi for det første er det også ARM, og for det andre støtter Embox grafikkutgang for denne plattformen.

Embox har en mekanisme for å bygge eksterne biblioteker, ved å bruke den legger vi til OpenCV som en modul (som passerer alle de samme alternativene for den "minimale" bygningen i form av statiske biblioteker), etter det legger jeg til en enkel applikasjon som ser slik ut:

version.cpp:

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

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

    return 0;
}

Vi monterer systemet, kjører det - vi får forventet utgang.

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 neste trinnet er å kjøre noen eksempler, fortrinnsvis et av de standard som tilbys av utviklerne selv. på nettstedet ditt. jeg velger grense detektor canny.

Eksemplet måtte skrives litt om for å vise bildet med resultatet direkte i rammebufferen. Jeg måtte gjøre dette, fordi. funksjon imshow() kan tegne bilder gjennom QT-, GTK- og Windows-grensesnittene, som selvfølgelig definitivt ikke vil være i konfigurasjonen for STM32. QT kan faktisk også kjøres på STM32F7Discovery, men dette vil bli diskutert i en annen artikkel 🙂

Etter en kort avklaring i hvilket format resultatet av kantdetektoren lagres, får vi et bilde.

OpenCV på STM32F7-Discovery

originalt bilde

OpenCV på STM32F7-Discovery

Resultat

Kjører på STM32F7Discovery

På 32F746GDISCOVERY er det flere maskinvareminneseksjoner som vi kan bruke på en eller annen måte

  1. 320KiB RAM
  2. 1MiB blits for bilde
  3. 8 MB SDRAM
  4. 16MiB QSPI NAND-blits
  5. microSD-kortspor

Et SD-kort kan brukes til å lagre bilder, men i sammenheng med å kjøre et minimalt eksempel er dette lite nyttig.
Skjermen har en oppløsning på 480×272, noe som betyr at rammebufferminnet vil være på 522 byte i en dybde på 240 bit, dvs. dette er mer enn størrelsen på RAM, så framebufferen og heapen (som vil være nødvendig, inkludert for OpenCV, for å lagre data for bilder og hjelpestrukturer) vil være plassert i SDRAM, alt annet (minne for stabler og andre systembehov) ) vil gå til RAM .

Hvis vi tar minimumskonfigurasjonen for STM32F7Discovery (kaster ut hele nettverket, alle kommandoer, gjør stabler så små som mulig, etc.) og legger til OpenCV med eksempler der, vil det nødvendige minnet være som følger:

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

For de som ikke er veldig kjent med hvilke seksjoner som går hvor, vil jeg forklare: in .text и .rodata instruksjoner og konstanter (grovt sett, skrivebeskyttede data) ligger i .data dataene kan endres, .bss det er "nullede" variabler, som likevel trenger en plass (denne delen vil "gå" til RAM).

Den gode nyheten er det .data/.bss skal passe, men med .text Problemet er at det bare er 1MiB minne for bildet. Kan kastes ut .text bildet fra eksemplet og les det for eksempel fra SD-kortet inn i minnet ved oppstart, men fruits.png veier omtrent 330KiB, så dette vil ikke løse problemet: de fleste .text består av OpenCV-koden.

I det store og hele er det bare én ting igjen - å laste en del av koden på en QSPI-flash (den har en spesiell driftsmodus for å kartlegge minne til systembussen, slik at prosessoren kan få tilgang til disse dataene direkte). I dette tilfellet oppstår et problem: for det første er ikke minnet til en QSPI-flash-stasjon tilgjengelig umiddelbart etter at enheten er startet på nytt (du må initialisere den minnetilordnede modusen separat), og for det andre kan du ikke "flash" dette minnet med en kjent bootloader.

Som et resultat ble det besluttet å koble all koden i QSPI, og flashe den med en selvskrevet loader som vil motta den nødvendige binære filen via TFTP.

Resultat

Ideen om å portere dette biblioteket til Embox dukket opp for omtrent et år siden, men om og om igjen ble det utsatt på grunn av ulike årsaker. En av dem er støtte for libstdc++ og standard malbibliotek. Problemet med C++-støtte i Embox er utenfor rammen av denne artikkelen, så her vil jeg bare si at vi klarte å oppnå denne støtten i riktig mengde for at dette biblioteket skulle fungere 🙂

Til slutt ble disse problemene overvunnet (i det minste nok til at OpenCV-eksemplet fungerte), og eksemplet kjørte. Det tar 40 lange sekunder for brettet å søke etter grenser ved å bruke Canny-filteret. Dette er selvfølgelig for langt (det er vurderinger om hvordan man kan optimalisere denne saken, det vil være mulig å skrive en egen artikkel om dette i tilfelle suksess).

OpenCV på STM32F7-Discovery

Imidlertid var delmålet å lage en prototype som skal vise den grunnleggende muligheten for å kjøre OpenCV på henholdsvis STM32, dette målet ble nådd, hurra!

tl;dr: trinnvise instruksjoner

0: Last ned Embox-kilder, som dette:

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

1: La oss starte med å sette sammen en bootloader som vil "flashe" en QSPI flash-stasjon.

    make confload-arm/stm32f7cube

Nå må du konfigurere nettverket, fordi. Vi laster opp bildet via TFTP. For å angi tavle- og verts-IP-adresser, må du redigere conf/rootfs/nettverket.

Konfigurasjonseksempel:

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 - vertsadresse hvor bildet skal lastes inn, address - adresse til styret.

Etter det samler vi oppstartslasteren:

    make

2: Den vanlige lastingen av bootloader (beklager ordspillet) på brettet - det er ikke noe spesifikt her, du må gjøre det som for alle andre applikasjoner for STM32F7Discovery. Hvis du ikke vet hvordan du gjør det, kan du lese om det her.
3: Kompilere et bilde med en konfigurasjon for OpenCV.

    make confload-platform/opencv/stm32f7discovery
    make

4: Utdrag fra ELF-seksjoner som 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

Det er et skript i conf-katalogen som gjør dette, så du kan kjøre det

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

5: Bruk tftp, last ned qspi.bin.bin til en QSPI flash-stasjon. På verten, for å gjøre dette, kopier qspi.bin til rotmappen til tftp-serveren (vanligvis /srv/tftp/ eller /var/lib/tftpboot/; pakker for den tilsvarende serveren er tilgjengelige i de fleste populære distribusjoner, vanligvis kalt tftpd eller tftp-hpa, noen ganger må du gjøre systemctl start tftpd.service å starte).

    # вариант для 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 oppstartslasteren), må du utføre følgende kommando (vi antar at serveren har adressen 192.168.2.1):

    embox> qspi_loader qspi.bin 192.168.2.1

6: Med kommando goto du må "hoppe" inn i QSPI-minnet. Den spesifikke plasseringen vil variere avhengig av hvordan bildet er koblet, du kan se denne adressen med kommandoen mem 0x90000000 (startadressen passer inn i det andre 32-bits ordet i bildet); du må også flagge stabelen -s, stabeladressen er 0x90000000, eksempel:

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

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

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

7: Lansering

    embox> edges 20

og nyt det 40 sekunder lange grensesøket 🙂

Hvis noe går galt - skriv en sak i vårt depot, eller til e-postlisten [e-postbeskyttet], eller i en kommentar her.

Kilde: www.habr.com

Legg til en kommentar