Ik ben een van de ontwikkelaars van het besturingssysteem , 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 , 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\nDe volgende stap is om een voorbeeld uit te voeren, bij voorkeur een standaardvoorbeeld dat door de ontwikkelaars zelf wordt aangeboden. . Ik heb gekozen voor .
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.

Oorspronkelijke afbeelding.

Resultaat
Uitvoering op STM32F7Discovery.
Op de 32F746GDISCOVERY zijn er verschillende hardware-geheugensecties die we op een of andere manier kunnen gebruiken.
- 320KiB RAM.
- 1MiB flash-geheugen voor de afbeelding.
- 8MiB SDRAM.
- 16MiB QSPI NAND-flash.
- 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\/emboxVoor 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!

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/stm32f7cubeJe 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:02gateway — het adres van de host waarvan de afbeelding wordt geladen, address — het adres van de kaart.
Daarna bouwen we de bootloader:
make2: 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 .
3: Compileren van de afbeelding met de configuratie voor OpenCV.
make confload-platform/opencv/stm32f7discovery
make4: 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=.dataIn 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.bin5: 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/tftpbootOp 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.16: 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 voorkomen7: Start
embox> edges 20en geniet van de 40 seconden durende randdetectie 🙂
Als er iets misgaat - schrijf dan een issue in , of op de mailinglijst embox-devel@googlegroups.com, of in de opmerkingen hier.
Bron: habr.com
