Jeg er en af udviklerne af operativsystemet , og i denne artikel vil jeg fortælle, hvordan jeg har fået OpenCV til at køre på STM32746G boardet.
Hvis man søger efter noget som «OpenCV on STM32 board», kan man finde en del, der er interesseret i at bruge dette bibliotek på STM32 boards eller andre mikrokontrollere.
Der er flere videoer, som efter titlen at dømme burde demonstrere, hvad der er nødvendigt, men normalt (i alle videoerne, jeg har set) blev der kun udført billedoptagelse fra et kamera og visning af resultatet på skærmen. Selve billedbehandlingen blev enten foretaget på en almindelig computer eller på kraftigere boards (for eksempel Raspberry Pi).
Hvorfor er det svært?
Populariteten af søgningerne skyldes, at OpenCV er det mest populære bibliotek til computer vision, hvilket betyder, at flere udviklere kender det. Desuden gør muligheden for at køre desktop-kode på en mikrokontroller udviklingsprocessen betydeligt nemmere. Men hvorfor findes der stadig ikke nogle populære opskrifter til at løse dette problem?
Problemet med at bruge OpenCV på små boards er relateret til to egenskaber:
- Selv hvis man kompilere biblioteket med det minimale sæt moduler, vil det simpelthen ikke kunne være i flashhukommelsen på STM32F7Discovery (endda uden at tage hensyn til OS) på grund af den meget store kode (flere megabyte instruktioner)
- Selve biblioteket er skrevet i C++, hvilket betyder
- Der er behov for support til C++ runtime (undtagelser osv.)
- Der er lidt support til LibC/Posix, som normalt findes i operativsystemer til indlejrede systemer — der er brug for en standard C++ bibliotek og standard template library STL (vector osv.)
Portering til Embox
Som sædvanligt, før man porterer programmer til et operativsystem, er det en god idé at forsøge at bygge det i den form, som udviklerne havde til hensigt. I vores tilfælde er der ikke problemer med dette — kildefilerne kan findes på , biblioteket kan bygges til GNU/Linux med det almindelige cmake.
Fra de gode nyheder – OpenCV kan fra starten bygges som et statisk bibliotek, hvilket gør porteringen lettere. Vi bygger biblioteket med standardkonfigurationen og ser, hvor meget plads de optager. Hvert modul bygges som et separat bibliotek.
> størrelse lib/*so --totals
tekst data bss dec hex filnavn
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 det ses fra den sidste linje, optager .bss og .data ikke så meget plads, men koden er over 70 MiB. Det står klart, at hvis dette er linket statisk til en specifik applikation, vil koden fylde mindre.
Lad os prøve at fjerne så mange moduler som muligt for at sammenstille et minimalt eksempel (som f.eks. blot vil udskrive versionen af OpenCV), så her ser vi. cmake .. -LA og deaktiverer alle muligheder, der kan deaktiveres.
-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> størrelse lib/libopencv_core.a --totals
tekst data bss dec hex filnavn
3317069 36425 17987 3371481 3371d9 (TOTALS)På den ene side er det kun én modul af biblioteket, men på den anden side er det uden kompilatoroptimering for kodes størrelse (-Os). ~3 MiB kode — det er stadig en del, men giver allerede håb om succes.
Kørsel i emulator
Det er langt lettere at debugge i emulatoren, så lad os først sikre os, at biblioteket fungerer på qemu. Som den emulerede platform har jeg valgt Integrator/CP, fordi det for det første også er ARM, og for det andet, at Embox understøtter grafikudgang til denne platform.
I Embox er der et mekanisme til at bygge eksterne biblioteker, ved hjælp af dette tilføjer vi OpenCV som et modul (ved at videregive alle de samme muligheder til den "minimale" bygning som statiske biblioteker), hvorefter jeg tilføjer en simpel applikation, der ser sådan ud:
version.cpp:
#include
#include
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}Vi bygger systemet, kører — og får den forventede udgang.
root@embox: /#opencv_version
OpenCV:
Generel konfiguration for OpenCV 4.0.1 =====================================
Versionskontrol: bd6927bdf-dirty
Platform:
Tidsstempel: 2019-06-21T10:02:18Z
Vært: Linux 5.1.7-arch1-1-ARCH x86_64
Mål: Generisk arm-unknown-none
CMake: 3.14.5
CMake-generator: Unix Makefiles
CMake bygningsværktøj: /usr/bin/make
Konfiguration: Debug
CPU/HW funktioner:
Grundlæggende:
anmodet: DETECT
deaktiveret: VFPV3 NEON
C/C++:
Bygget som dynamiske biblioteker?: NEJNæste skridt er at køre et eksempel, helst et standard eksempel fra udviklerne selv. . Jeg valgte .
Eksemplet måtte skrives om en smule for at vise billedet med resultaterne direkte i framebufferen. Dette var nødvendigt, da funktionen imshow() kan tegne billeder gennem QT, GTK og Windows interfaces, som selvfølgelig ikke vil være i konfigurationen for STM32. Faktisk kan QT også køres på STM32F7Discovery, men det vil blive beskrevet i en anden artikel 🙂
Efter kort at finde ud af, i hvilket format grænse detektorens resultater gemmes, får vi billedet.

Originalbilledet.

Resultat
Kørsel på STM32F7Discovery.
På 32F746GDISCOVERY er der flere hardwareminnesektioner, som vi kan bruge på forskellige måder.
- 320KiB RAM.
- 1MiB flashhukommelse til billedet.
- 8MiB SDRAM.
- 16MiB QSPI NAND-flash.
- Stik til microSD-kort.
SD-kortet kan bruges til at gemme billeder, men i konteksten af at køre et minimalt eksempel er det ikke særligt nyttigt.
Skærmen har en opløsning på 480×272, hvilket betyder, at hukommelsen til framebufferen vil være 522 240 bytes ved en dybde på 32 bit, dvs. det er mere end størrelsen på RAM, så framebufferen og hukommelsen (som også er nødvendig for OpenCV til at gemme data til billeder og hjælpe strukturer) vil blive placeret i SDRAM, alt andet (hukommelse til stakke og andre systembehov) vil gå til RAM.
Hvis man tager den minimale konfiguration for STM32F7Discovery (fjerne alt netværk, alle kommandoer, gøre stakke så små som muligt osv.) og tilføjer OpenCV med eksempler, vil hukommelseskravene være følgende:
text data bss dec hex filename
2876890 459208 312736 3648834 37ad42 build/base/bin/emboxFor those who are not very familiar with where the sections go, let me explain: in .text og .rodata are the instructions and constants (roughly speaking, readonly data), in .data are the modifiable data, in .bss are the 'zeroed' variables that still need space (this section will be 'sent' to RAM).
The good news is that .data/.bss should fit, but with .text there's a problem — there is only 1MiB of memory available for the image. You could throw away the image from the example and read it, for instance, from an SD card into memory at startup, but fruits.png weighs about 330KiB, so that doesn’t solve the problem: most of it .text is made up of OpenCV code. .text Essentially, there remains only one option — to load part of the code onto the QSPI flash chip (it has a special operating mode for memory mapping to the system bus, so the processor can directly access this data). However, there is a problem: first, the QSPI flash memory is not available immediately after rebooting the device (you need to separately initialize the memory-mapped mode), and second, you cannot 'flash' this memory with the usual loader.
As a result, it was decided to link all code into QSPI and flash it with a custom bootloader that would retrieve the necessary binary via TFTP.
The idea of porting this library to Embox came about about a year ago, but it was postponed time and again for various reasons. One of them was support for libstdc++ and the standard template library. The issue of C++ support in Embox goes beyond the scope of this article, so I will only say here that we managed to achieve this support to the extent necessary for this library to work 🙂
Resultat
As a result, these problems were overcome (at least sufficiently for the OpenCV example to work), and the example ran. The board takes 40 long seconds to find edges with the Canny filter. This is, of course, too long (there are considerations on how to optimize this, a separate article can be written if successful).
Nevertheless, the intermediate goal was to create a prototype that would demonstrate the fundamental possibility of running OpenCV on STM32; accordingly, this goal was achieved, hurray!

tl;dr: step-by-step guide
0: Download the Embox source code, for example like this:
git clone https://github.com/embox/embox && cd ./embox
1: Let's start with building the bootloader that will 'flash' the QSPI flash.make confload-arm/stm32f7cube
lav confload-arm/stm32f7cubeNu skal netværket konfigureres, da vi skal downloade billedet via TFTP. For at angive IP-adresserne for kortet og værten skal du ændre filen conf/rootfs/network.
Eksempel på konfiguration:
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 — adressen til værten, hvorfra billedet vil blive hentet, address — adressen til kortet.
Efter dette samler vi bootloaderen:
make2: Normal opstart af bootloaderen (undskyld for ordspillet) på kortet — der er ikke noget specifikt her, det skal gøres som for enhver anden applikation til STM32F7Discovery. Hvis du ikke ved, hvordan man gør dette, kan du læse om det .
3: Kompilering af billedet med konfiguration til OpenCV.
make confload-platform/opencv/stm32f7discovery
make4: Uddragning af ELF-sektioner, der 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=.dataI mappen conf ligger der et script, som gør dette, så det er muligt at køre det
./conf/qspi_objcopy.sh # Den nødvendige binær -- build/base/bin/qspi.bin5: Ved hjælp af tftp uploader vi qspi.bin.bin til QSPI-flash-drevet. På værten skal du kopiere qspi.bin til roden af tftp-serverens mappe (normalt er det /srv/tftp/ eller /var/lib/tftpboot/; pakker til den tilsvarende server findes i de fleste populære distributioner, som regel heter tftpd eller tftp-hpa, nogle gange skal du køre systemctl start tftpd.service for at starte).
# вариант для tftpd
sudo cp build/base/bin/qspi.bin /srv/tftp
# вариант для tftp-hpa
sudo cp build/base/bin/qspi.bin /var/lib/tftpbootPå Embox (dvs. i bootloaderen) skal du køre følgende kommando (vi antager, at serverens adresse er 192.168.2.1):
embox> qspi_loader qspi.bin 192.168.2.16: Ved hjælp af kommandoen goto skal du "hoppe" ind i QSPI-hukommelsen. Den specifikke placering vil variere afhængigt af, hvordan billedet linkes, og du kan se denne adresse med kommandoen mem 0x90000000 (startadressen ligger i det andet 32-bits ord i billedet); det vil også være nødvendigt at indstille stack-flaget -s, stakens adresse findes på adressen 0x90000000, eksempel:
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
dette er adressen dette er adressen
til stakken til den første
instruktion
embox>goto -i 0x9000c27f -s 0x20023200 # Flag -i er nødvendigt for at forhindre afbrydelser under systeminitialisering7: Vi starter
embox> edges 20og nyder en 40-sekunders kantdetektering 🙂
Hvis noget går galt — skriv en issue i , eller til mailinglisten embox-devel@googlegroups.com, eller i kommentarerne her.
Kilde: habr.com
