Jag är en av utvecklarna av operativsystemet
Om du skriver något som "OpenCV på STM32-kort" i en sökmotor, kan du hitta en hel del personer som är intresserade av att använda detta bibliotek på STM32-kort eller andra mikrokontroller.
Det finns flera filmer som, av namnet att döma, ska visa vad som behövs, men vanligtvis (i alla videor som jag såg) på STM32-kortet togs bara bilden från kameran och resultatet visades på skärmen, och själva bildbehandlingen gjordes antingen på en vanlig dator eller på mer kraftfulla kort (till exempel Raspberry Pi).
Varför är det svårt?
Sökfrågornas popularitet förklaras av det faktum att OpenCV är det mest populära datorvisionsbiblioteket, vilket innebär att fler utvecklare känner till det, och möjligheten att köra skrivbordsklar kod på en mikrokontroller förenklar utvecklingsprocessen avsevärt. Men varför finns det fortfarande inga populära färdiga recept för att lösa detta problem?
Problemet med att använda OpenCV på små sjalar är relaterat till två funktioner:
- Om du kompilerar biblioteket även med en minimal uppsättning moduler, kommer det helt enkelt inte att passa in i flashminnet i samma STM32F7Discovery (även utan att ta hänsyn till OS) på grund av en mycket stor kod (flera megabyte instruktioner)
- Själva biblioteket är skrivet i C++, vilket betyder
- Behöver stöd för positiv körtid (undantag, etc.)
- Lite stöd för LibC/Posix, som vanligtvis finns i OS för inbyggda system - du behöver ett standard plus-bibliotek och ett standard STL-mallbibliotek (vektor, etc.)
Porterar till Embox
Som vanligt, innan du porterar några program till operativsystemet, är det en bra idé att försöka bygga det i den form som utvecklarna avsåg det. I vårt fall är det inga problem med detta - källkoden finns på
Den goda nyheten är att OpenCV kan byggas som ett statiskt bibliotek ur lådan, vilket gör porteringen enklare. Vi samlar ett bibliotek med en standardkonfiguration och ser hur mycket plats de tar upp. Varje modul samlas i ett 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å sista raden tar .bss och .data inte mycket plats, men koden är mer än 70 MiB. Det är klart att om detta är statiskt kopplat till en specifik applikation kommer koden att bli mindre.
Låt oss försöka kasta ut så många moduler som möjligt så att ett minimalt exempel sätts ihop (som till exempel helt enkelt kommer att mata ut OpenCV-versionen), så vi tittar cmake .. -LA
och stäng av i alternativen allt som stängs av.
-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)
Å ena sidan är detta bara en modul i biblioteket, å andra sidan är detta utan kompilatoroptimering för kodstorlek (-Os
). ~3 MiB kod är fortfarande ganska mycket, men ger redan hopp om framgång.
Kör i emulatorn
Det är mycket lättare att felsöka på emulatorn, så se först till att biblioteket fungerar på qemu. Som en emulerad plattform valde jag Integrator / CP, eftersom för det första är det också ARM, och för det andra stöder Embox grafikutgång för denna plattform.
Embox har en mekanism för att bygga externa bibliotek, med den lägger vi till OpenCV som en modul (som passerar alla samma alternativ för den "minimala" builden i form av statiska bibliotek), efter det lägger jag till en enkel applikation som ser ut så här:
version.cpp:
#include <stdio.h>
#include <opencv2/core/utility.hpp>
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}
Vi monterar systemet, kör det - vi får den förväntade effekten.
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 включены в сборку и т.п.>
Nästa steg är att köra något exempel, helst ett av de standard som erbjuds av utvecklarna själva.
Exemplet behövde skrivas om något för att visa bilden med resultatet direkt i bildrutebufferten. Jag var tvungen att göra detta, eftersom. fungera imshow()
kan rita bilder genom QT-, GTK- och Windows-gränssnitten, vilket naturligtvis inte kommer att finnas i konfigurationen för STM32. QT kan faktiskt också köras på STM32F7Discovery, men detta kommer att diskuteras i en annan artikel 🙂
Efter ett kort förtydligande i vilket format resultatet av kantdetektorn lagras får vi en bild.
originalbild
Resultat
Körs på STM32F7Discovery
På 32F746GDISCOVERY finns det flera hårdvaruminnessektioner som vi kan använda på ett eller annat sätt
- 320KiB RAM
- 1MiB blixt för bild
- 8MB SDRAM
- 16MiB QSPI NAND-blixt
- microSD-kortplats
Ett SD-kort kan användas för att lagra bilder, men i samband med att köra ett minimalt exempel är detta inte särskilt användbart.
Skärmen har en upplösning på 480×272, vilket innebär att framebufferminnet blir 522 240 byte på ett djup av 32 bitar, d.v.s. detta är mer än storleken på RAM, så framebuffern och heapen (som kommer att krävas, inklusive för OpenCV, för att lagra data för bilder och hjälpstrukturer) kommer att finnas i SDRAM, allt annat (minne för stackar och andra systembehov ) kommer att gå till RAM .
Om vi tar minimikonfigurationen för STM32F7Discovery (kastar ut hela nätverket, alla kommandon, gör stackar så små som möjligt, etc.) och lägger till OpenCV med exempel där, kommer minnet som krävs att vara som följer:
text data bss dec hex filename
2876890 459208 312736 3648834 37ad42 build/base/bin/embox
För dem som inte är så bekanta med vilka avsnitt som går vart kommer jag att förklara: in .text
и .rodata
instruktioner och konstanter (grovt sett, skrivskyddade data) ligger i .data
uppgifterna är föränderliga, .bss
det finns "nullade" variabler, som ändå behöver en plats (det här avsnittet kommer att "gå" till RAM).
Den goda nyheten är det .data
/.bss
ska passa, men med .text
Problemet är att det bara finns 1MiB minne för bilden. Kan kastas ut .text
bilden från exemplet och läs den till exempel från SD-kortet till minnet vid start, men fruits.png väger cirka 330KiB, så det här löser inte problemet: de flesta .text
består av OpenCV-koden.
I stort sett är det bara en sak kvar - ladda en del av koden på en QSPI-blixt (den har ett speciellt driftläge för att mappa minne till systembussen, så att processorn kan komma åt dessa data direkt). I det här fallet uppstår ett problem: för det första är minnet på en QSPI-flashenhet inte tillgängligt omedelbart efter att enheten har startat om (du måste initiera det minnesmappade läget separat), och för det andra kan du inte "flasha" detta minne med en välbekant bootloader.
Som ett resultat beslutades det att länka all kod i QSPI, och flasha den med en självskriven loader som kommer att ta emot den erforderliga binära filen via TFTP.
Resultat
Idén att porta detta bibliotek till Embox dök upp för ungefär ett år sedan, men om och om igen sköts det upp på grund av olika anledningar. En av dem är stöd för libstdc++ och standardmallbiblioteket. Problemet med C++-stöd i Embox ligger utanför ramen för denna artikel, så här vill jag bara säga att vi lyckades uppnå detta stöd i rätt mängd för att det här biblioteket skulle fungera 🙂
Till slut övervanns dessa problem (åtminstone tillräckligt för att OpenCV-exemplet skulle fungera), och exemplet kördes. Det tar 40 långa sekunder för brädan att söka efter gränser med Canny-filtret. Detta är naturligtvis för långt (det finns överväganden om hur man kan optimera denna fråga, det kommer att vara möjligt att skriva en separat artikel om detta i händelse av framgång).
Delmålet var dock att skapa en prototyp som ska visa den fundamentala möjligheten att köra OpenCV på respektive STM32, detta mål uppnåddes, hurra!
tl;dr: steg för steg instruktioner
0: Ladda ner Embox-källor, så här:
git clone https://github.com/embox/embox && cd ./embox
1: Låt oss börja med att montera en bootloader som kommer att "flasha" en QSPI-flashenhet.
make confload-arm/stm32f7cube
Nu måste du konfigurera nätverket, eftersom. Vi laddar upp bilden via TFTP. För att ställa in kortet och värd-IP-adresserna måste du redigera conf/rootfs/nätverket.
Konfigurationsexempel:
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ärdadress varifrån bilden kommer att laddas, address
- styrelsens adress.
Efter det samlar vi in bootloadern:
make
2: Den vanliga laddningen av starthanteraren (ursäkta ordleken) på tavlan - det finns inget specifikt här, du måste göra det som för alla andra applikationer för STM32F7Discovery. Om du inte vet hur du gör kan du läsa om det
3: Kompilera en bild med en konfiguration för OpenCV.
make confload-platform/opencv/stm32f7discovery
make
4: Utdrag från ELF-sektioner som ska skrivas till QSPI till 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
Det finns ett skript i conf-katalogen som gör detta, så du kan köra det
./conf/qspi_objcopy.sh # Нужный бинарник -- build/base/bin/qspi.bin
5: Använd tftp, ladda ner qspi.bin.bin till en QSPI-flashenhet. På värden, för att göra detta, kopiera qspi.bin till rotmappen på tftp-servern (vanligtvis /srv/tftp/ eller /var/lib/tftpboot/; paket för motsvarande server är tillgängliga i de flesta populära distributioner, vanligtvis kallade tftpd eller tftp-hpa, ibland måste man göra systemctl start tftpd.service
att börja).
# вариант для 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 bootloadern) måste du köra följande kommando (vi antar att servern har adressen 192.168.2.1):
embox> qspi_loader qspi.bin 192.168.2.1
6: Med kommando goto
du måste "hoppa" in i QSPI-minnet. Den specifika platsen kommer att variera beroende på hur bilden är länkad, du kan se denna adress med kommandot mem 0x90000000
(startadressen passar in i det andra 32-bitarsordet i bilden); du måste också flagga stacken -s
, stackadressen är 0x90000000, exempel:
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
это адрес это адрес
стэка первой
инструкции
embox>goto -i 0x9000c27f -s 0x20023200 # Флаг -i нужен чтобы запретить прерывания во время инициализации системы
< Начиная отсюда будет вывод не загрузчика, а образа с OpenCV >
7: Lansering
embox> edges 20
och njut av den 40 sekunder långa gränssökningen 🙂
Om något går fel - skriv ett ärende i
Källa: will.com