OpenCV på STM32F7-Discovery

OpenCV på STM32F7-Discovery Jag är en av utvecklarna av operativsystemet embox, och i den här artikeln kommer jag att prata om hur jag lyckades köra OpenCV på STM32746G-kortet.

Om du skriver något som "OpenCV på STM32-kort" i en sökmotor, kan du hitta en hel del personer som är intresserade av att använda detta bibliotek på STM32-kort eller andra mikrokontroller.
Det finns flera filmer som, av namnet att döma, ska visa vad som behövs, men vanligtvis (i alla videor som jag såg) på STM32-kortet togs bara bilden från kameran och resultatet visades på skärmen, och själva bildbehandlingen gjordes antingen på en vanlig dator eller på mer kraftfulla kort (till exempel Raspberry Pi).

Varför är det svårt?

Sökfrågornas popularitet förklaras av det faktum att OpenCV är det mest populära datorvisionsbiblioteket, vilket innebär att fler utvecklare känner till det, och möjligheten att köra skrivbordsklar kod på en mikrokontroller förenklar utvecklingsprocessen avsevärt. Men varför finns det fortfarande inga populära färdiga recept för att lösa detta problem?

Problemet med att använda OpenCV på små sjalar är relaterat till två funktioner:

  • Om du kompilerar biblioteket även med en minimal uppsättning moduler, kommer det helt enkelt inte att passa in i flashminnet i samma STM32F7Discovery (även utan att ta hänsyn till OS) på grund av en mycket stor kod (flera megabyte instruktioner)
  • Själva biblioteket är skrivet i C++, vilket betyder
    • Behöver stöd för positiv körtid (undantag, etc.)
    • Lite stöd för LibC/Posix, som vanligtvis finns i OS för inbyggda system - du behöver ett standard plus-bibliotek och ett standard STL-mallbibliotek (vektor, etc.)

Porterar till Embox

Som vanligt, innan du porterar några program till operativsystemet, är det en bra idé att försöka bygga det i den form som utvecklarna avsåg det. I vårt fall är det inga problem med detta - källkoden finns på github, är biblioteket byggt under GNU/Linux med den vanliga cmake.

Den goda nyheten är att OpenCV kan byggas som ett statiskt bibliotek ur lådan, vilket gör porteringen enklare. Vi samlar ett bibliotek med en standardkonfiguration och ser hur mycket plats de tar upp. Varje modul samlas i ett 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å sista raden tar .bss och .data inte mycket plats, men koden är mer än 70 MiB. Det är klart att om detta är statiskt kopplat till en specifik applikation kommer koden att bli mindre.

Låt oss försöka kasta ut så många moduler som möjligt så att ett minimalt exempel sätts ihop (som till exempel helt enkelt kommer att mata ut OpenCV-versionen), så vi tittar cmake .. -LA och stäng av i alternativen allt som stängs 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)

Å ena sidan är detta bara en modul i biblioteket, å andra sidan är detta utan kompilatoroptimering för kodstorlek (-Os). ~3 MiB kod är fortfarande ganska mycket, men ger redan hopp om framgång.

Kör i emulatorn

Det är mycket lättare att felsöka på emulatorn, så se först till att biblioteket fungerar på qemu. Som en emulerad plattform valde jag Integrator / CP, eftersom för det första är det också ARM, och för det andra stöder Embox grafikutgång för denna plattform.

Embox har en mekanism för att bygga externa bibliotek, med den lägger vi till OpenCV som en modul (som passerar alla samma alternativ för den "minimala" builden i form av statiska bibliotek), efter det lägger jag till en enkel applikation som ser ut så här:

version.cpp:

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

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

    return 0;
}

Vi monterar systemet, kör det - vi får den förväntade effekten.

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

Nästa steg är att köra något exempel, helst ett av de standard som erbjuds av utvecklarna själva. på din webbplats. jag valde gränsdetektor canny.

Exemplet behövde skrivas om något för att visa bilden med resultatet direkt i bildrutebufferten. Jag var tvungen att göra detta, eftersom. fungera imshow() kan rita bilder genom QT-, GTK- och Windows-gränssnitten, vilket naturligtvis inte kommer att finnas i konfigurationen för STM32. QT kan faktiskt också köras på STM32F7Discovery, men detta kommer att diskuteras i en annan artikel 🙂

Efter ett kort förtydligande i vilket format resultatet av kantdetektorn lagras får vi en bild.

OpenCV på STM32F7-Discovery

originalbild

OpenCV på STM32F7-Discovery

Resultat

Körs på STM32F7Discovery

På 32F746GDISCOVERY finns det flera hårdvaruminnessektioner som vi kan använda på ett eller annat sätt

  1. 320KiB RAM
  2. 1MiB blixt för bild
  3. 8MB SDRAM
  4. 16MiB QSPI NAND-blixt
  5. microSD-kortplats

Ett SD-kort kan användas för att lagra bilder, men i samband med att köra ett minimalt exempel är detta inte särskilt användbart.
Skärmen har en upplösning på 480×272, vilket innebär att framebufferminnet blir 522 240 byte på ett djup av 32 bitar, d.v.s. detta är mer än storleken på RAM, så framebuffern och heapen (som kommer att krävas, inklusive för OpenCV, för att lagra data för bilder och hjälpstrukturer) kommer att finnas i SDRAM, allt annat (minne för stackar och andra systembehov ) kommer att gå till RAM .

Om vi ​​tar minimikonfigurationen för STM32F7Discovery (kastar ut hela nätverket, alla kommandon, gör stackar så små som möjligt, etc.) och lägger till OpenCV med exempel där, kommer minnet som krävs att vara som följer:

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

För dem som inte är så bekanta med vilka avsnitt som går vart kommer jag att förklara: in .text и .rodata instruktioner och konstanter (grovt sett, skrivskyddade data) ligger i .data uppgifterna är föränderliga, .bss det finns "nullade" variabler, som ändå behöver en plats (det här avsnittet kommer att "gå" till RAM).

Den goda nyheten är det .data/.bss ska passa, men med .text Problemet är att det bara finns 1MiB minne för bilden. Kan kastas ut .text bilden från exemplet och läs den till exempel från SD-kortet till minnet vid start, men fruits.png väger cirka 330KiB, så det här löser inte problemet: de flesta .text består av OpenCV-koden.

I stort sett är det bara en sak kvar - ladda en del av koden på en QSPI-blixt (den har ett speciellt driftläge för att mappa minne till systembussen, så att processorn kan komma åt dessa data direkt). I det här fallet uppstår ett problem: för det första är minnet på en QSPI-flashenhet inte tillgängligt omedelbart efter att enheten har startat om (du måste initiera det minnesmappade läget separat), och för det andra kan du inte "flasha" detta minne med en välbekant bootloader.

Som ett resultat beslutades det att länka all kod i QSPI, och flasha den med en självskriven loader som kommer att ta emot den erforderliga binära filen via TFTP.

Resultat

Idén att porta detta bibliotek till Embox dök upp för ungefär ett år sedan, men om och om igen sköts det upp på grund av olika anledningar. En av dem är stöd för libstdc++ och standardmallbiblioteket. Problemet med C++-stöd i Embox ligger utanför ramen för denna artikel, så här vill jag bara säga att vi lyckades uppnå detta stöd i rätt mängd för att det här biblioteket skulle fungera 🙂

Till slut övervanns dessa problem (åtminstone tillräckligt för att OpenCV-exemplet skulle fungera), och exemplet kördes. Det tar 40 långa sekunder för brädan att söka efter gränser med Canny-filtret. Detta är naturligtvis för långt (det finns överväganden om hur man kan optimera denna fråga, det kommer att vara möjligt att skriva en separat artikel om detta i händelse av framgång).

OpenCV på STM32F7-Discovery

Delmålet var dock att skapa en prototyp som ska visa den fundamentala möjligheten att köra OpenCV på respektive STM32, detta mål uppnåddes, hurra!

tl;dr: steg för steg instruktioner

0: Ladda ner Embox-källor, så här:

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

1: Låt oss börja med att montera en bootloader som kommer att "flasha" en QSPI-flashenhet.

    make confload-arm/stm32f7cube

Nu måste du konfigurera nätverket, eftersom. Vi laddar upp bilden via TFTP. För att ställa in kortet och värd-IP-adresserna måste du redigera conf/rootfs/nätverket.

Konfigurationsexempel:

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ärdadress varifrån bilden kommer att laddas, address - styrelsens adress.

Efter det samlar vi in ​​bootloadern:

    make

2: Den vanliga laddningen av starthanteraren (ursäkta ordleken) på tavlan - det finns inget specifikt här, du måste göra det som för alla andra applikationer för STM32F7Discovery. Om du inte vet hur du gör kan du läsa om det här.
3: Kompilera en bild med en konfiguration för OpenCV.

    make confload-platform/opencv/stm32f7discovery
    make

4: Utdrag från ELF-sektioner som ska skrivas till QSPI till 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 finns ett skript i conf-katalogen som gör detta, så du kan köra det

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

5: Använd tftp, ladda ner qspi.bin.bin till en QSPI-flashenhet. På värden, för att göra detta, kopiera qspi.bin till rotmappen på tftp-servern (vanligtvis /srv/tftp/ eller /var/lib/tftpboot/; paket för motsvarande server är tillgängliga i de flesta populära distributioner, vanligtvis kallade tftpd eller tftp-hpa, ibland måste man göra systemctl start tftpd.service att börja).

    # вариант для 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 bootloadern) måste du köra följande kommando (vi antar att servern har adressen 192.168.2.1):

    embox> qspi_loader qspi.bin 192.168.2.1

6: Med kommando goto du måste "hoppa" in i QSPI-minnet. Den specifika platsen kommer att variera beroende på hur bilden är länkad, du kan se denna adress med kommandot mem 0x90000000 (startadressen passar in i det andra 32-bitarsordet i bilden); du måste också flagga stacken -s, stackadressen är 0x90000000, exempel:

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

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

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

7: Lansering

    embox> edges 20

och njut av den 40 sekunder långa gränssökningen 🙂

Om något går fel - skriv ett ärende i vårt förråd, eller till e-postlistan [e-postskyddad], eller i en kommentar här.

Källa: will.com

Lägg en kommentar