OpenCV op STM32F7-Discovery

OpenCV op STM32F7-Discovery Ik ben een van de ontwikkelaars van het besturingssysteem Embox, en in dit artikel zal ik uitleggen hoe het me gelukt is om OpenCV op de STM32746G ontwikkelplaat te draaien.

Als je iets zoekt zoals «OpenCV op STM32 bord», vind je behoorlijk veel mensen die geïnteresseerd zijn in het gebruik van deze bibliotheek op STM32 borden of andere microcontrollers.
Er zijn verschillende video's die, naar de titel te oordelen, moeten laten zien wat nodig is, maar meestal (in alle video's die ik heb gezien) werd op de STM32 bord alleen de afbeelding van de camera verkregen en het resultaat op het scherm weergeven, terwijl de beeldverwerking ofwel op een gewone computer of op krachtigere borden (bijvoorbeeld Raspberry Pi) plaatsvond.

Waarom is dat moeilijk?

De populariteit van zoekopdrachten kan worden verklaard door het feit dat OpenCV de meest populaire bibliotheek voor computer vision is, wat betekent dat meer ontwikkelaars ermee bekend zijn, en de mogelijkheid om kant-en-klare desktopcode op een microcontroller uit te voeren, vergemakkelijkt het ontwikkelingsproces aanzienlijk. Maar waarom zijn er tot nu toe geen populaire kant-en-klare recepten voor het oplossen van dit probleem?

Het probleem met het gebruik van OpenCV op kleine borden hangt samen met twee kenmerken:

  • Zelfs als je de bibliotheek met een minimale set modules compileert, past deze gewoon niet in het flashgeheugen van dezelfde STM32F7Discovery (zelfs zonder het besturingssysteem) vanwege de zeer grote code (enkele megabytes instructies)
  • De bibliotheek zelf is geschreven in C++, wat betekent
    • Dat er ondersteuning voor C++ runtime nodig is (exceptions enz.)
    • Er is weinig ondersteuning voor LibC/Posix, die meestal beschikbaar is in besturingssystemen voor embedded systemen — er is een standaard C++ bibliotheek en een standaard template bibliotheek STL (vector enz.) nodig.

Porting naar Embox

Zoals gebruikelijk, is het voor porting van programma's naar een besturingssysteem goed om te proberen deze te compileren in de vorm die de ontwikkelaars voor ogen hadden. In ons geval zijn er geen problemen hiermee — de source code kan gevonden worden op GitHub, de bibliotheek kan worden gebouwd met GNU/Linux met de gebruikelijke cmake.

Het goede nieuws is dat OpenCV standaard als een statische bibliotheek kan worden gebouwd, wat porting eenvoudiger maakt. We bouwen de bibliotheek met de standaardconfiguratie en kijken hoeveel ruimte ze innemen. Elke module wordt in een aparte bibliotheek gebouwd.

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

Uit de laatste regel blijkt dat .bss en .data niet veel ruimte innemen, maar de code meer dan 70 MiB is. Het is duidelijk dat als dit statisch met een specifieke applicatie wordt gekoppeld, de code kleiner wordt.

Laten we proberen zoveel mogelijk modules weg te laten om een minimale voorbeeld te creëren (dat bijvoorbeeld gewoon de versie van OpenCV weergeeft), dus laten we kijken. cmake .. -LA en schakel in de opties alles uit wat uitgeschakeld kan worden.

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

Aan de ene kant is dit slechts één module van de bibliotheek, aan de andere kant is dit zonder optimalisatie door de compiler op de grootte van de code (-Os). ~3 MiB code is nog steeds vrij veel, maar biedt al hoop op succes.

Uitvoering in een emulator

Het is veel gemakkelijker om in een emulator te debuggen, dus laten we eerst bevestigen dat de bibliotheek werkt op qemu. Als emulerend platform heb ik Integrator/CP gekozen, omdat dit ook ARM is en Embox grafische uitvoer voor dit platform ondersteunt.

In Embox is er een mechanisme voor het bouwen van externe bibliotheken, waarmee we OpenCV als module toevoegen (met dezelfde opties voor de "minimale" build in de vorm van statische bibliotheken), daarna voeg ik een eenvoudige applicatie toe die er als volgt uitziet:

version.cpp:

#include 
#include 

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

    return 0;
}

We bouwen het systeem, starten het op — en krijgen de verwachte uitvoer.

root@embox:\/\#opencv_version                                                     \nOpenCV: \nAlgemene configuratie voor OpenCV 4.0.1 =====================================\n  Versiebeheer:               bd6927bdf-dirty\n\n  Platform:\n    Tijdstip:                   2019-06-21T10:02:18Z\n    Host:                        Linux 5.1.7-arch1-1-ARCH x86_64\n    Doel:                      Algemeen arm-unknown-none\n    CMake:                       3.14.5\n    CMake-generator:             Unix Makefiles\n    CMake-bouwtool:            \/usr\/bin\/make\n    Configuratie:               Debug\n\n  CPU\/HW-functies:\n    Basislijn:\n      aangevraagd:                 DETECT\n      uitgeschakeld:                  VFPV3 NEON\n\n  C\/C++:\n    Gebouwd als dynamische libs?:      NEE\n

De volgende stap is om een voorbeeld uit te voeren, bij voorkeur een standaardvoorbeeld dat door de ontwikkelaars zelf wordt aangeboden. op hun website.. Ik heb gekozen voor de Canny-randdetectie..

Het voorbeeld moest iets worden herschreven om het resultaat direct in de framebuffer weer te geven. Dit was nodig, aangezien de functie imshow() in staat is om afbeeldingen te tekenen via interfaces zoals QT, GTK en Windows, die absoluut niet in de configuratie voor STM32 zullen zitten. In feite kan QT ook worden uitgevoerd op STM32F7Discovery, maar daarover zal in een ander artikel worden gesproken 🙂

Na kort te hebben uitgezocht in welk formaat het resultaat van de randdetectie wordt opgeslagen, krijgen we de afbeelding.

OpenCV op STM32F7-Discovery

Oorspronkelijke afbeelding.

OpenCV op STM32F7-Discovery

Resultaat

Uitvoering op STM32F7Discovery.

Op de 32F746GDISCOVERY zijn er verschillende hardware-geheugensecties die we op een of andere manier kunnen gebruiken.

  1. 320KiB RAM.
  2. 1MiB flash-geheugen voor de afbeelding.
  3. 8MiB SDRAM.
  4. 16MiB QSPI NAND-flash.
  5. Slot voor microSD-kaart.

De SD-kaart kan worden gebruikt voor het opslaan van afbeeldingen, maar in de context van het uitvoeren van een minimaal voorbeeld is dit niet erg nuttig.
Het display heeft een resolutie van 480×272, wat betekent dat het geheugen voor de framebuffer 522240 byte zal bedragen bij een diepte van 32 bits, d.w.z. dit is meer dan de grootte van het RAM, zodat we de framebuffer en de heap (die ook nodig is voor OpenCV om gegevens voor afbeeldingen en ondersteunende structuren op te slaan) in SDRAM zullen plaatsen; de rest (geheugen voor stacks en andere systeembehoeften) gaat naar het RAM.

Als we de minimale configuratie voor STM32F7Discovery nemen (alle netwerken, alle commando's verwijderen, de stacks zo klein mogelijk maken, enz.) en daar OpenCV met voorbeelden aan toevoegen, zal het benodigde geheugen als volgt zijn:

   text    data     bss     dec     hex bestandsnaam\n2876890  459208  312736 3648834  37ad42 build\/base\/bin\/embox

Voor degenen die niet goed bekend zijn met welke secties waarheen gaan, laat me het uitleggen: in .text en .rodata staan instructies en constanten (grofweg, readonly-gegevens), in .data staan veranderlijke gegevens, in .bss staan de 'geannuleerde' variabelen, waarvoor echter geheugen nodig is (deze sectie 'wordt verzonden' naar RAM).

Het goede nieuws is dat .data/.bss moeten worden geplaatst, maar het probleem is dat .text er slechts 1MiB geheugen voor de afbeelding is. Je kunt de afbeelding uit het voorbeeld verwijderen en deze bijvoorbeeld vanaf een SD-kaart in het geheugen lezen bij het opstarten, maar fruits.png is ongeveer 330KiB, dus dat lost het probleem niet op: het grootste deel .text bestaat inderdaad uit OpenCV-code. .text Uiteindelijk resteert er nog maar één optie - een deel van de code op de QSPI-flash op te laden (deze heeft een speciale werkmodus voor geheugenmapping op de systeembus, zodat de processor direct toegang kan krijgen tot deze gegevens). In dit geval ontstaat er een probleem: ten eerste is het geheugen van de QSPI-flash niet onmiddellijk toegankelijk na het opnieuw opstarten van het apparaat (je moet de memory-mapped-modus afzonderlijk initialiseren), en ten tweede kan je dit geheugen niet 'flitsen' met een gebruikelijke bootloader.

Uiteindelijk werd besloten om alle code in QSPI te linken en deze te flashen met een zelfgeschreven bootloader die het benodigde binaire bestand via TFTP zal ophalen.

Het idee om deze bibliotheek naar Embox te porten is ongeveer een jaar geleden ontstaan, maar het werd keer op keer uitgesteld om verschillende redenen. Een daarvan is de ondersteuning van libstdc++ en de standaardtemplatebibliotheek. Het probleem van C++-ondersteuning in Embox valt buiten het bestek van dit artikel, dus ik zal hier alleen zeggen dat we erin geslaagd zijn deze ondersteuning in de benodigde mate voor het functionele gebruik van deze bibliotheek te realiseren 🙂

Resultaat

Uiteindelijk werden deze problemen overwonnen (tenminste, in voldoende mate voor het uitvoeren van het OpenCV-voorbeeld), en het voorbeeld werd uitvoerig. Het duurt 40 lange seconden voor de board om de randen met de Canny-filter te vinden. Dit is natuurlijk te lang (er zijn ideeën om dit te optimaliseren, hierover kan in geval van succes een apart artikel worden geschreven).

Desondanks was de tussenliggende doelstelling het maken van een prototype dat de principiële mogelijkheid aantoont om OpenCV op STM32 uit te voeren; deze doelstelling is bereikt, hoera!

OpenCV op STM32F7-Discovery

tl;dr: stap-voor-stap instructies

0: Download de Embox-bronbestanden, bijvoorbeeld zo:

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

    1: We beginnen met het bouwen van de bootloader, die de QSPI-flash zal 'flitsen'.

make confload-arm/stm32f7cube

    maak confload-arm/stm32f7cube

Je moet nu het netwerk configureren, omdat we de afbeelding via TFTP zullen laden. Om de IP-adressen van de kaart en de host in te stellen, moet je het bestand conf/rootfs/network aanpassen.

Voorbeeldconfiguratie:

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 — het adres van de host waarvan de afbeelding wordt geladen, address — het adres van de kaart.

Daarna bouwen we de bootloader:

    make

2: Gewone installatie van de bootloader (excuses voor het woordgrapje) op de kaart — hier is niets speciaals aan, doe dit zoals voor elke andere toepassing voor de STM32F7Discovery. Als je niet weet hoe dit moet, kun je erover lezen here.
3: Compileren van de afbeelding met de configuratie voor OpenCV.

    make confload-platform/opencv/stm32f7discovery
    make

4: Extraheren van secties uit ELF, die moeten worden geschreven naar QSPI, in 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

In de directory conf staat een script dat dit doet, dus je kunt het uitvoeren

    ./conf/qspi_objcopy.sh # De benodigde binaire is -- build/base/bin/qspi.bin

5: Laad qspi.bin.bin naar de QSPI-flash met tftp. Daartoe moet je qspi.bin naar de rootdirectory van de tftp-server kopiëren (dit is meestal /srv/tftp/ of /var/lib/tftpboot/; de pakketten voor de juiste server zijn beschikbaar in de meeste populaire distributies, meestal genaamd tftpd of tftp-hpa, soms is het nodig om systemctl start tftpd.service om te starten).

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

Op Embox (d.w.z. in de bootloader) moet je het volgende commando uitvoeren (we veronderstellen dat het adres van de server 192.168.2.1 is):

    embox> qspi_loader qspi.bin 192.168.2.1

6: Met het commando goto moet je "springen" naar het QSPI-geheugen. De specifieke locatie varieert afhankelijk van hoe de afbeelding is gelinkt, deze adres kan je vinden met het commando mem 0x90000000 (het startadres ligt in het tweede 32-bits woord van de afbeelding); je moet ook de stack met het vlaggetje instellen -s, het stackadres ligt op het adres 0x90000000, voorbeeld:

    embox>mem 0x90000000
    0x90000000:     0x20023200  0x9000c27f  0x9000c275  0x9000c275
                      ↑           ↑
              dit is het adres    dit is het adres 
                van de stack        van de eerste
                           instructie

    embox>goto -i 0x9000c27f -s 0x20023200 # De vlag -i is nodig om onderbrekingen tijdens de systeeminitialisatie te voorkomen

7: Start

    embox> edges 20

en geniet van de 40 seconden durende randdetectie 🙂

Als er iets misgaat - schrijf dan een issue in onze repository, of op de mailinglijst embox-devel@googlegroups.com, of in de opmerkingen hier.

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers 🔥 Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster