OpenCV op STM32F7-Discovery

OpenCV op STM32F7-Discovery Ik ben een van de ontwikkelaars van het besturingssysteem embox, en in dit artikel zal ik het hebben over hoe ik OpenCV op het STM32746G-bord heb kunnen uitvoeren.

Als je zoiets als "OpenCV op STM32-bord" in een zoekmachine typt, kun je nogal wat mensen vinden die geïnteresseerd zijn in het gebruik van deze bibliotheek op STM32-kaarten of andere microcontrollers.
Er zijn verschillende video's die, te oordelen naar de naam, zouden moeten laten zien wat er nodig is, maar meestal (in alle video's die ik zag) op het STM32-bord werd alleen het beeld van de camera ontvangen en werd het resultaat op het scherm weergegeven, en de beeldverwerking zelf gebeurde op een gewone computer of op krachtigere kaarten (bijvoorbeeld Raspberry Pi).

Waarom is het moeilijk?

De populariteit van zoekopdrachten wordt verklaard door het feit dat OpenCV de meest populaire computer vision-bibliotheek is, wat betekent dat meer ontwikkelaars er bekend mee zijn, en de mogelijkheid om desktop-ready code op een microcontroller uit te voeren, vereenvoudigt het ontwikkelingsproces aanzienlijk. Maar waarom zijn er nog steeds geen populaire kant-en-klare recepten om dit probleem op te lossen?

Het probleem van het gebruik van OpenCV op kleine sjaals houdt verband met twee kenmerken:

  • Als u de bibliotheek compileert, zelfs met een minimale set modules, past deze eenvoudigweg niet in het flashgeheugen van dezelfde STM32F7Discovery (zelfs zonder rekening te houden met het besturingssysteem) vanwege een zeer grote code (meerdere megabytes aan instructies)
  • De bibliotheek zelf is geschreven in C++, wat betekent
    • Ondersteuning nodig voor positieve runtime (uitzonderingen, etc.)
    • Weinig ondersteuning voor LibC/Posix, die meestal te vinden is in OS voor ingebedde systemen - je hebt een standaard plus-bibliotheek en een standaard STL-sjabloonbibliotheek nodig (vector, etc.)

Overzetten naar Embox

Zoals gewoonlijk is het een goed idee om, voordat u programma's naar het besturingssysteem overzet, te proberen het te bouwen in de vorm waarin de ontwikkelaars het bedoeld hebben. In ons geval zijn hier geen problemen mee - de broncode is te vinden op githabe, is de bibliotheek gebouwd onder GNU/Linux met de gebruikelijke cmake.

Het goede nieuws is dat OpenCV out-of-the-box kan worden gebouwd als een statische bibliotheek, wat porten eenvoudiger maakt. We verzamelen een bibliotheek met een standaardconfiguratie en kijken hoeveel ruimte ze innemen. Elke module wordt verzameld in een aparte bibliotheek.

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

Zoals je op de laatste regel kunt zien, nemen .bss en .data niet veel ruimte in beslag, maar de code is meer dan 70 MiB. Het is duidelijk dat als dit statisch gekoppeld wordt aan een specifieke applicatie, de code minder wordt.

Laten we proberen zoveel mogelijk modules weg te gooien zodat er een minimaal voorbeeld wordt samengesteld (dat bijvoorbeeld gewoon de OpenCV-versie zal uitvoeren), dus we kijken cmake .. -LA en schakel in de opties alles uit dat wordt uitgeschakeld.

        -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 compileroptimalisatie voor codegrootte (-Os). ~3 MiB aan code is nog best veel, maar geeft al hoop op succes.

Uitvoeren in emulator

Het is veel gemakkelijker om te debuggen op de emulator, dus zorg er eerst voor dat de bibliotheek werkt op qemu. Als geëmuleerd platform heb ik gekozen voor Integrator / CP, omdat ten eerste is het ook ARM, en ten tweede ondersteunt Embox grafische uitvoer voor dit platform.

Embox heeft een mechanisme voor het bouwen van externe bibliotheken, door het te gebruiken voegen we OpenCV toe als een module (alle dezelfde opties doorgeven voor de "minimale" build in de vorm van statische bibliotheken), daarna voeg ik een eenvoudige applicatie toe die er zo uitziet:

version.cpp:

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

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

    return 0;
}

We assembleren het systeem, voeren het uit - we krijgen de verwachte 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 включены в сборку и т.п.>

De volgende stap is het uitvoeren van een voorbeeld, bij voorkeur een van de standaardvoorbeelden die door de ontwikkelaars zelf worden aangeboden. op uw site. ik kies grensdetector slim.

Het voorbeeld moest enigszins worden herschreven om de afbeelding met het resultaat direct in de framebuffer weer te geven. Ik moest dit doen, omdat. functie imshow() kan afbeeldingen tekenen via de QT-, GTK- en Windows-interfaces, die natuurlijk zeker niet in de configuratie voor STM32 zullen zitten. QT kan zelfs worden uitgevoerd op STM32F7Discovery, maar dit wordt in een ander artikel besproken 🙂

Na een korte verduidelijking in welk formaat het resultaat van de randdetector wordt opgeslagen, krijgen we een beeld.

OpenCV op STM32F7-Discovery

originele foto

OpenCV op STM32F7-Discovery

Resultaat

Draait op STM32F7Discovery

Op 32F746GDISCOVERY zijn er verschillende hardwaregeheugensecties die we op de een of andere manier kunnen gebruiken

  1. 320KiB RAM
  2. 1MiB flitser voor afbeelding
  3. 8 MB SDRAM
  4. 16MiB QSPI NAND-flitser
  5. microSD-kaartsleuf

Een SD-kaart kan worden gebruikt om afbeeldingen op te slaan, maar in het kader van het draaien van een minimaal voorbeeld is dit niet erg handig.
Het scherm heeft een resolutie van 480×272, wat betekent dat het framebuffergeheugen 522 bytes zal zijn op een diepte van 240 bits, d.w.z. dit is meer dan de grootte van RAM, dus de framebuffer en de heap (die nodig zijn, ook voor OpenCV, om gegevens voor afbeeldingen en hulpstructuren op te slaan) zullen zich in SDRAM bevinden, al het andere (geheugen voor stapels en andere systeembehoeften ) gaat naar RAM.

Als we de minimale configuratie voor STM32F7Discovery nemen (gooi het hele netwerk weg, alle commando's, maak stapels zo klein mogelijk, enz.) en voeg daar OpenCV met voorbeelden toe, het vereiste geheugen is als volgt:

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

Voor degenen die niet zo bekend zijn met welke secties waar komen, zal ik uitleggen: in .text и .rodata instructies en constanten (grofweg alleen-lezen gegevens) liggen erin .data de gegevens zijn veranderlijk, .bss er zijn "nulled" variabelen, die niettemin een plaats nodig hebben (deze sectie "gaat" naar RAM).

Het goede nieuws is dat .data/.bss zou moeten passen, maar met .text het probleem is dat er slechts 1 MiB geheugen is voor de afbeelding. Kan worden weggegooid .text de afbeelding uit het voorbeeld en lees deze bijvoorbeeld van de SD-kaart in het geheugen bij het opstarten, maar fruits.png weegt ongeveer 330KiB, dus dit lost het probleem niet op: de meeste .text bestaat uit de OpenCV-code.

Over het algemeen blijft er nog maar één ding over: een deel van de code op een QSPI-flash laden (deze heeft een speciale werkingsmodus voor het toewijzen van geheugen aan de systeembus, zodat de processor rechtstreeks toegang heeft tot deze gegevens). In dit geval doet zich een probleem voor: ten eerste is het geheugen van een QSPI-flashdrive niet onmiddellijk beschikbaar nadat het apparaat opnieuw is opgestart (u moet de geheugentoegewezen modus afzonderlijk initialiseren), en ten tweede kunt u dit geheugen niet "flashen" met een bekende bootloader.

Daarom is besloten om alle code in QSPI te koppelen en te flashen met een zelfgeschreven bootloader die via TFTP de benodigde binary binnenkrijgt.

Resultaat

Het idee om deze bibliotheek over te zetten naar Embox ontstond ongeveer een jaar geleden, maar werd om verschillende redenen keer op keer uitgesteld. Een daarvan is ondersteuning voor libstdc++ en de standaard sjabloonbibliotheek. Het probleem van C++-ondersteuning in Embbox valt buiten het bestek van dit artikel, dus hier zal ik alleen zeggen dat we erin zijn geslaagd om deze ondersteuning in de juiste hoeveelheid te bereiken om deze bibliotheek te laten werken 🙂

Uiteindelijk werden deze problemen overwonnen (in ieder geval genoeg om het OpenCV-voorbeeld te laten werken), en het voorbeeld werkte. Het duurt 40 lange seconden voordat het bord naar grenzen zoekt met behulp van het Canny-filter. Dit is natuurlijk te lang (er zijn overwegingen om deze kwestie te optimaliseren, in geval van succes zal hier een apart artikel over kunnen worden geschreven).

OpenCV op STM32F7-Discovery

Het tussenliggende doel was echter om een ​​prototype te maken dat de fundamentele mogelijkheid laat zien om OpenCV op respectievelijk STM32 te draaien, dit doel is bereikt, hoera!

tl;dr: stap voor stap instructies

0: Download Embox-bronnen, zoals deze:

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

1: Laten we beginnen met het samenstellen van een bootloader die een QSPI-flashdrive zal "flashen".

    make confload-arm/stm32f7cube

Nu moet je het netwerk configureren, omdat. We zullen de afbeelding uploaden via TFTP. Om de board- en host-IP-adressen in te stellen, moet u de conf/rootfs/network bewerken.

Configuratievoorbeeld:

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 - hostadres van waaruit de afbeelding wordt geladen, address - adres van het bestuur.

Daarna verzamelen we de bootloader:

    make

2: Het gebruikelijke laden van de bootloader (sorry voor de woordspeling) op het bord - er is hier niets specifieks, je moet het doen zoals voor elke andere applicatie voor STM32F7Discovery. Als je niet weet hoe het moet, kun je erover lezen hier.
3: Een afbeelding samenstellen met een configuratie voor OpenCV.

    make confload-platform/opencv/stm32f7discovery
    make

4: Uittreksel van ELF-secties die naar QSPI moeten worden geschreven naar 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

Er is een script in de map conf dat dit doet, dus je kunt het uitvoeren

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

5: Download qspi.bin.bin met behulp van tftp naar een QSPI-flashstation. Kopieer hiervoor op de host qspi.bin naar de hoofdmap van de tftp-server (meestal /srv/tftp/ of /var/lib/tftpboot/; pakketten voor de corresponderende server zijn beschikbaar in de meeste populaire distributies, meestal genaamd tftpd of tftp-hpa, soms moet je het doen systemctl start tftpd.service beginnen).

    # вариант для 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 u de volgende opdracht uitvoeren (we gaan ervan uit dat de server het adres 192.168.2.1 heeft):

    embox> qspi_loader qspi.bin 192.168.2.1

6: Met commando goto u moet in het QSPI-geheugen "springen". De specifieke locatie is afhankelijk van hoe de afbeelding is gekoppeld, u kunt dit adres zien met de opdracht mem 0x90000000 (het startadres past in het tweede 32-bits woord van de afbeelding); je moet ook de stapel markeren -s, het stapeladres is 0x90000000, bijvoorbeeld:

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

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

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

7: lancering

    embox> edges 20

en geniet van de grenszoekactie van 40 seconden 🙂

Als er iets misgaat, schrijf dan een probleem in onze opslagplaats, of naar de mailinglijst [e-mail beveiligd], of in een reactie hier.

Bron: www.habr.com

Voeg een reactie