Jeg er en af udviklerne af styresystemet
Hvis du skriver noget som "OpenCV on STM32 board" i en søgemaskine, kan du finde en del mennesker, der er interesserede i at bruge dette bibliotek på STM32 boards eller andre mikrocontrollere.
Der er adskillige videoer, der efter navnet at dømme burde demonstrere, hvad der er brug for, men normalt (i alle de videoer, jeg så) på STM32-kortet, var det kun billedet, der blev modtaget fra kameraet, og resultatet blev vist på skærmen, og selve billedbehandlingen foregik enten på en almindelig computer eller på mere kraftfulde boards (for eksempel Raspberry Pi).
Hvorfor er det svært?
Populariteten af søgeforespørgsler forklares med, at OpenCV er det mest populære computervision-bibliotek, hvilket betyder, at flere udviklere kender det, og muligheden for at køre desktop-klar kode på en mikrocontroller forenkler udviklingsprocessen i høj grad. Men hvorfor er der stadig ingen populære færdige opskrifter til at løse dette problem?
Problemet med at bruge OpenCV på små sjaler er relateret til to funktioner:
- Hvis du kompilerer biblioteket selv med et minimalt sæt moduler, vil det simpelthen ikke passe ind i flashhukommelsen på den samme STM32F7Discovery (selv uden at tage OS i betragtning) på grund af en meget stor kode (flere megabyte instruktioner)
- Selve biblioteket er skrevet i C++, hvilket betyder
- Har brug for support til positiv kørselstid (undtagelser osv.)
- Lidt understøttelse af LibC/Posix, som normalt findes i OS til indlejrede systemer - du har brug for et standard plus-bibliotek og et standard STL-skabelonbibliotek (vektor osv.)
Portering til Embox
Som sædvanlig, før du porterer programmer til operativsystemet, er det en god idé at prøve at bygge det i den form, som udviklerne havde tiltænkt det. I vores tilfælde er der ingen problemer med dette - kildekoden kan findes på
Den gode nyhed er, at OpenCV kan bygges som et statisk bibliotek ud af boksen, hvilket gør portering lettere. Vi samler et bibliotek med en standardkonfiguration og ser hvor meget plads de fylder. Hvert modul er samlet i et 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å sidste linje, fylder .bss og .data ikke meget, men koden er mere end 70 MiB. Det er klart, at hvis dette er statisk knyttet til en specifik applikation, vil koden blive mindre.
Lad os prøve at smide så mange moduler ud som muligt, så der samles et minimalt eksempel (som f.eks. blot vil udskrive OpenCV-versionen), så vi ser cmake .. -LA
og sluk i indstillingerne alt, hvad der slukker.
-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)
På den ene side er dette kun et modul i biblioteket, på den anden side er dette uden compiler optimering for kodestørrelse (-Os
). ~3 MiB kode er stadig ret meget, men giver allerede håb om succes.
Kør i emulatoren
Det er meget nemmere at debugge på emulatoren, så sørg først for, at biblioteket fungerer på qemu. Som en emuleret platform valgte jeg Integrator / CP, fordi for det første er det også ARM, og for det andet understøtter Embox grafikoutput til denne platform.
Embox har en mekanisme til at bygge eksterne biblioteker, ved at bruge den tilføjer vi OpenCV som et modul (passer alle de samme muligheder for den "minimale" build i form af statiske biblioteker), derefter tilføjer jeg en simpel applikation, der ser sådan ud:
version.cpp:
#include <stdio.h>
#include <opencv2/core/utility.hpp>
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}
Vi samler systemet, kører det - vi får det forventede 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 включены в сборку и т.п.>
Det næste trin er at køre nogle eksempler, helst et af de standard, der tilbydes af udviklerne selv.
Eksemplet skulle omskrives lidt for at vise billedet med resultatet direkte i rammebufferen. Jeg var nødt til at gøre dette, fordi. fungere imshow()
kan tegne billeder gennem QT-, GTK- og Windows-grænseflader, som selvfølgelig ikke vil være i konfigurationen til STM32. Faktisk kan QT også køres på STM32F7Discovery, men det vil blive diskuteret i en anden artikel 🙂
Efter en kort afklaring i hvilket format resultatet af kantdetektoren er gemt, får vi et billede.
originalt billede
Outcome
Kører på STM32F7Discovery
På 32F746GDISCOVERY er der flere hardwarehukommelsessektioner, som vi kan bruge på den ene eller anden måde
- 320KiB RAM
- 1MiB flash til billede
- 8MB SDRAM
- 16MiB QSPI NAND Flash
- microSD-kortslot
Et SD-kort kan bruges til at gemme billeder, men i forbindelse med at køre et minimalt eksempel er dette ikke særlig nyttigt.
Skærmen har en opløsning på 480×272, hvilket betyder, at framebuffer-hukommelsen bliver på 522 bytes i en dybde på 240 bit, dvs. dette er mere end størrelsen af RAM, så framebufferen og heapen (som vil være påkrævet, inklusive for OpenCV, for at gemme data til billeder og hjælpestrukturer) vil være placeret i SDRAM, alt andet (hukommelse til stakke og andre systembehov) ) vil gå til RAM .
Hvis vi tager minimumskonfigurationen for STM32F7Discovery (smid hele netværket ud, alle kommandoer, gør stakke så små som muligt osv.) og tilføjer OpenCV med eksempler der, vil den nødvendige hukommelse være som følger:
text data bss dec hex filename
2876890 459208 312736 3648834 37ad42 build/base/bin/embox
For dem, der ikke er særlig bekendt med, hvilke sektioner der går hvor, vil jeg forklare: in .text
и .rodata
instruktioner og konstanter (groft sagt, skrivebeskyttede data) ligger i .data
dataene kan ændres, .bss
der er "nullede" variabler, som ikke desto mindre har brug for en plads (denne sektion vil "gå" til RAM).
Den gode nyhed er det .data
/.bss
skulle passe, men med .text
problemet er, at der kun er 1MiB hukommelse til billedet. Kan smides ud .text
billedet fra eksemplet og læs det f.eks. fra SD-kortet ind i hukommelsen ved opstart, men fruits.png vejer omkring 330KiB, så dette løser ikke problemet: de fleste .text
består af OpenCV-koden.
I det store og hele er der kun én ting tilbage - at indlæse en del af koden på en QSPI-flash (den har en speciel driftstilstand til at kortlægge hukommelsen til systembussen, så processoren kan få direkte adgang til disse data). I dette tilfælde opstår der et problem: For det første er hukommelsen på et QSPI-flashdrev ikke tilgængelig umiddelbart efter, at enheden er genstartet (du skal separat initialisere den hukommelseskortede tilstand), og for det andet kan du ikke "flash" denne hukommelse med en velkendt bootloader.
Som et resultat blev det besluttet at linke al koden i QSPI og flashe den med en selvskrevet loader, der vil modtage den nødvendige binære via TFTP.
Outcome
Ideen om at portere dette bibliotek til Embox dukkede op for omkring et år siden, men igen og igen blev det udskudt af forskellige årsager. En af dem er understøttelse af libstdc++ og standardskabelonbiblioteket. Problemet med C++-understøttelse i Embox ligger uden for rammerne af denne artikel, så her vil jeg kun sige, at det lykkedes os at opnå denne support i den rigtige mængde for, at dette bibliotek kunne fungere 🙂
I sidste ende blev disse problemer overvundet (i det mindste nok til at OpenCV-eksemplet virkede), og eksemplet kørte. Det tager 40 lange sekunder for brættet at søge efter grænser ved hjælp af Canny-filteret. Dette er selvfølgelig for langt (der er overvejelser om, hvordan man optimerer denne sag, det vil være muligt at skrive en separat artikel om dette i tilfælde af succes).
Det mellemliggende mål var dog at skabe en prototype, der skal vise den fundamentale mulighed for at køre OpenCV på henholdsvis STM32, dette mål blev nået, hurra!
tl;dr: trin for trin instruktioner
0: Download Embox-kilder som dette:
git clone https://github.com/embox/embox && cd ./embox
1: Lad os starte med at samle en bootloader, der vil "flashe" et QSPI-flashdrev.
make confload-arm/stm32f7cube
Nu skal du konfigurere netværket, fordi. Vi uploader billedet via TFTP. For at indstille boardet og værtens IP-adresser skal du redigere conf/rootfs/netværket.
Konfigurationseksempel:
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ærtsadresse, hvorfra billedet vil blive indlæst, address
- bestyrelsens adresse.
Derefter samler vi bootloaderen:
make
2: Den sædvanlige indlæsning af bootloaderen (undskyld ordspillet) på tavlen - der er ikke noget specifikt her, du skal gøre det som for enhver anden applikation til STM32F7Discovery. Hvis du ikke ved, hvordan du gør det, kan du læse om det
3: Kompilere et billede med en config til OpenCV.
make confload-platform/opencv/stm32f7discovery
make
4: Uddrag fra 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=.data
Der er et script i conf-mappen, der gør dette, så du kan køre det
./conf/qspi_objcopy.sh # Нужный бинарник -- build/base/bin/qspi.bin
5: Brug tftp til at downloade qspi.bin.bin til et QSPI-flashdrev. For at gøre dette på værten skal du kopiere qspi.bin til rodmappen på tftp-serveren (normalt /srv/tftp/ eller /var/lib/tftpboot/; pakker til den tilsvarende server er tilgængelige i de fleste populære distributioner, normalt kaldet tftpd eller tftp-hpa, nogle gange er du nødt til at gøre systemctl start tftpd.service
at begynde).
# вариант для 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 bootloaderen) skal du udføre følgende kommando (vi antager, at serveren har adressen 192.168.2.1):
embox> qspi_loader qspi.bin 192.168.2.1
6: Med kommando goto
du skal "hoppe" ind i QSPI-hukommelsen. Den specifikke placering vil variere afhængigt af hvordan billedet er linket, du kan se denne adresse med kommandoen mem 0x90000000
(startadressen passer ind i billedets andet 32-bit ord); du skal også markere stakken -s
, stakadressen er på 0x90000000, eksempel:
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
это адрес это адрес
стэка первой
инструкции
embox>goto -i 0x9000c27f -s 0x20023200 # Флаг -i нужен чтобы запретить прерывания во время инициализации системы
< Начиная отсюда будет вывод не загрузчика, а образа с OpenCV >
7: Lancering
embox> edges 20
og nyd den 40 sekunder lange grænsesøgning 🙂
Hvis noget går galt - skriv et problem ind
Kilde: www.habr.com