Unë jam një nga zhvilluesit e sistemit të operimit , dhe në këtë artikull do të flas për mënyrën se si arrita të aktivizoj OpenCV në pllakën STM32746G.
Nëse shkruani në motorin e kërkimit diçka si 'OpenCV on STM32 board', mund të gjeni mjaft njerëz që janë të interesuar për përdorimin e kësaj biblioteke në pllakat STM32 ose në mikrokontrollera të tjerë.
Ekzistojnë disa video që, sipas titullit, duhet të demonstrojnë atë që duhet, por zakonisht (në të gjitha videoto që kam parë) në pllakën STM32 është kryer vetëm marrja e pamjes nga Kamera dhe shfaqja e rezultatit në ekran, ndërsa vetë përpunimi i imazhit bëhej ose në një kompjuter të zakonshëm, ose në pllaka më të fuqishme (p.sh., Raspberry Pi).
Pse është e vështirë?
Popullariteti i kërkimeve shpjegohet me faktin se OpenCV është biblioteka më popullore e vizionit kompjuterik, dhe kjo do të thotë se shumë më shumë zhvillues janë të njohur me të, dhe mundësia për të aktivizuar kodin e gatshëm për desktop në mikrokontroller e thjeshton ndjeshëm procesin e zhvillimit. Por pse ende nuk ka ndonjë recetë të njohur të gatshme për zgjidhjen e kësaj problemi?
Problemi i përdorimit të OpenCV në pllaka të vogla është i lidhur me dy karakteristika:
- Nëse e kompiloni bibliotekën madje edhe me një grup minimal module, në memorien flash të po asaj STM32F7Discovery ajo thjesht nuk do të hyjë (madje pa marrë parasysh OS-në) për shkak të kodit shumë të madh (disa megabajt instrukcionesh)
- Biblioteka vetë është shkruar në C++, dhe kjo do të thotë
- Duhet mbështetje për runtime-in e C++ (përjashtime etj.)
- Ka pak mbështetje LibC/Posix, të cilat zakonisht janë në OS-të për sisteme të integruara — nevojitet biblioteka standarde e C++ dhe biblioteka standarde e templatit STL (vektori etj.)
Portimi në Embox
Si zakonisht, para se të portoni ndonjë program në një sistem operativ, nuk do të ishte keq të provoni ta ndërtoni atë në formën që e kanë menduar zhvilluesit. Në rastin tonë nuk ka probleme me këtë — burimet mund të gjenden në , biblioteka ndërtrohet nën GNU/Linux me cmake të zakonshëm.
Nga lajmet e mira — OpenCV nga kutia mund të ndërtohet në formën e një biblioteke statike, gjë që e bën portimin më të lehtë. Ndërtojmë bibliotekën me konfigurimin standard dhe shikojmë sa hapësirë po zë. Çdo modul ndërtohet në një bibliotekë të veçantë.
> 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)Siç duket nga rreshti i fundit, .bss dhe .data nuk zënë aq shumë hapësirë, por ka mbi 70 MiB kod. E qartë është se nëse kjo lidhet statikisht me një aplikacion të caktuar, kodi do të zvogëlohet.
Le të përpiqemi të heqim sa më shumë module që të jetë e mundur, në mënyrë që të krijojmë një shembull minimal (i cili, për shembull, thjesht do të tregojë versionin e OpenCV), kështu që po shohim cmake .. -LA dhe fikim në opsione gjithçka që mund të fiket.
-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)Nga njëra anë, ky është vetëm një modul i bibliotekës, nga ana tjetër, ky është pa optimizimin e kompilerit për madhësinë e kodit (-Os). ~3 MiB kod — kjo është ende e konsiderueshme, por tashmë jep shpresë për sukses.
Ekzekutimi në emulator
Në emulator është shumë më e lehtë të bësh debug, prandaj, fillimisht sigurohemi që biblioteka funksionon në qemu. Si platformë e emuluar kam zgjedhur Integrator/CP, sepse, nga njëra anë, është gjithashtu ARM dhe, nga ana tjetër, Embox mbështet daljen grafike për këtë platformë.
Në Embox ka një mekanizëm për ndërtimin e bibliotekave të jashtme, me ndihmën e tij e shtoj OpenCV si modul (duke kaluar të gjithë ato opsione për ndërtimin "minimal" në formën e bibliotekave statike), pas kësaj shtoj një aplikacion të thjeshtë që duket kështu:
version.cpp:
#include
#include
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}Ndërtojmë sistemin, e ekzekutojmë — dhe marrim rezultatin e pritur.
root@embox:/*opencv_version */
OpenCV:
Konfigurimi i përgjithshëm për OpenCV 4.0.1 =====================================
Kontrolli i versionit: bd6927bdf-dirty
Platforma:
Data e kohës: 2019-06-21T10:02:18Z
Host: Linux 5.1.7-arch1-1-ARCH x86_64
Objektivi: Generic arm-unknown-none
CMake: 3.14.5
Generatori i CMake: Unix Makefiles
Instrumenti për ndërtimin e CMake: /usr/bin/make
Konfigurimi: Debug
Tiparet e CPU/HW:
Bazë:
e kërkuar: DETECT
e ndaluar: VFPV3 NEON
C/C++:
Ndërtuar si biblioteka dinamike?: JOHapi tjetër është të ekzekutoni një shembull, më mirë ndonjë standard nga ata që ofrohen nga vetë zhvilluesit . Kam zgjedhur .
Shembulli ishte paksa e nevojshme ta rregulloja, për të shfaqur imazhin me rezultatin direkt në frame-buffer. Duhej ta bëja këtë, pasi funksioni imshow() di të vizatojë imazhe përmes interface-ve QT, GTK dhe Windows, të cilat, natyrisht, në konfigurimin për STM32 nuk do të jenë. Në të vërtetë, QT gjithashtu mund të ekzekutohet në STM32F7Discovery, por për këtë do të flitet në një artikull tjetër 🙂
Pas një sqarimi të shkurtër mbi se në cilin format ruhet rezultati i punës së detektorit të skajeve, marrim imazhin.

Imazhi origjinal

Rezultati
Ekzekutimi në STM32F7Discovery
Në 32F746GDISCOVERY ka disa seksione të ndryshme të memories që mund t'i përdorim në një mënyrë ose në një tjetër
- 320KiB memorie të përkohshme
- 1MiB memorie flash për figurën
- 8MiB SDRAM
- 16MiB QSPI NAND-flash
- Priza për kartën microSD
SD-karta mund të përdoret për ruajtjen e imazheve, por në kontekstin e ekzekutimit të një shembulli minimal nuk është shumë e dobishme.
Ekrani ka një resolutë prej 480×272, dhe kështu, memoria për frame-buffer do të ketë 522 240 byte në thellësi 32 bit, pra kjo është më shumë se sa sasia e memories së përkohshme, kështu që frame-buffer dhe heaps (të cilat do të kërkohen edhe për OpenCV, për të ruajtur të dhënat për imazhet dhe për strukturat ndihmëse) do të vendosen në SDRAM, çdo gjë tjetër (memoria për steka dhe nevojat e tjera sistemore) do të shkojë në RAM.
Nëse merrni konfigurimin minimal për STM32F7Discovery (hiqni të gjithë rrjetin, të gjitha komandat, bëni stekat sa më të vogla dhe etj.) dhe e shtoni OpenCV me shembuj, kjo do të ishte për sa i përket memories së kërkuar:
text data bss dec hex filename
2876890 459208 312736 3648834 37ad42 build/base/bin/emboxPër ata që nuk janë shumë të njohur me sekuencat që shkojnë ku, le të shpjegoj: në .text dhe .rodata janë ruajtur instruksionet dhe konstantet (për ta thënë thjesht, të dhënat pa lexim), në .data janë të dhënat e ndryshueshme, në .bss janë variablat "e zeruara", të cilat, megjithatë, kanë nevojë për hapësirë (kjo seksion "do të dërgohet" në RAM).
Lajmi i mirë është se .data/.bss duhen vendosur, por me .text problem — për imazhin ka vetëm 1MiB memorie. Mund ta heqim nga .text imazhin e shembujve dhe ta lexojmë, për shembull, nga karta SD në memorie gjatë aktivizimit, por fruits.png ka peshë rreth 330KiB, kështu që kjo problem nuk e zgjidh: pjesa më e madhe .text përbëhet pikërisht nga kodi OpenCV.
Në thelb, mbetet vetëm një zgjidhje — ngarkimi i pjesës së kodit në QSPI-flash (ajo ka një mod utilizimi të veçantë për mapimin e memories në autobusin sistemor, kështu që procesori mund t'i qaset këtyre të dhënave direkt). Megjithatë, lind problemi: së pari, memoria e QSPI-flash nuk është e arritshme menjëherë pas rinisjes së pajisjes (duhet të inizializoni veçmas modin e mapeuar të memories), së dyti, nuk mund të "flash" këtë memorie me boot loader-in e zakonshëm.
Në fund, u vendos të lidhet i gjithë kodi në QSPI, dhe ta flashojmë me një boot loader të shkruar nga vetë, i cili do të merrte binarin e nevojshëm përmes TFTP.
Rezultati
Ideja për të portuar këtë bibliotekë në Embox ka lindur rreth një viti më parë, por herë pas here është shtyrë për shkak të arsyeve të ndryshme. Njëra prej tyre është mbështetje për libstdc++ dhe standardin e bibliotekave të templatuara. Problemi i mbështetjes C++ në Embox del përtej këtij artikulli, ndaj këtu do të them vetëm se arritëm të sigurojmë këtë mbështetje në masën e nevojshme për funksionimin e kësaj biblioteke 🙂
Në fund, këto probleme u kapërcyen (të paktën, në masën e mjaftueshme për funksionimin e shembujve OpenCV), dhe shembulli u aktivizua. 40 sekonda të gjata kërkon blloku për të gjetur kufijtë me filtrin Canny. Kjo, natyrisht, është shumë e gjatë (ka propozime se si ta optimizojmë këtë proces, për këtë mund të shkruajmë një artikull të veçantë në rast suksesi).

Megjithatë, qëllimi ndërmjetës ishte krijimi i një prototipi që do të tregonte mundësinë themelore të aktivizimit të OpenCV në STM32, përkatësisht, ky qëllim u arrit, urime!
tl;dr: udhëzim hap pas hapi
0: Shkarkoni burimet e Embox, për shembull kështu:
git clone https://github.com/embox/embox && cd ./embox1: Le të fillojmë me ndërtimin e boot loader-it, i cili "do ta flash" QSPI-flash.
make confload-arm/stm32f7cubeTani duhet të konfiguroni rrjetin, sepse do ta ngarkojmë imazhin përmes TFTP. Për të caktuar adresat IP të kartës dhe hostit, duhet të modifikoni skedarin conf/rootfs/network.
Shembulli i konfigurimit:
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 — adresa e hostit nga ku do të ngarkohet imazhi, address — adresa e kartës.
Pasi të keni bërë këtë, ndjekim krijimin e ngarkuesit:
make2: Ngarkimi normal i ngarkuesit (falni për lojën me fjalë) në kartë — këtu nuk ka asgjë specifike, duhet bërë si për çdo tjetër aplikacion për STM32F7Discovery. Nëse nuk e dini se si bëhet, mund të lexoni për këtë .
3: Kompilimi i imazhit me konfigurimin për OpenCV.
make confload-platform/opencv/stm32f7discovery
make4: Nxjerrja nga ELF e seksioneve që duhet të shkruhen në QSPI, në 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=.dataNë direktorinë conf ndodhet një skript që e bën këtë, kështu që mund ta filloni atë
../conf/qspi_objcopy.sh # Binaries i nevojshëm -- build/base/bin/qspi.bin5: Me ndihmën e tftp ngarkoni qspi.bin.bin në QSPI-flash. Në host, për këtë duhet të kopjoni qspi.bin në dosjen rrënjësore të serverit tftp (zakonisht është /srv/tftp/ ose /var/lib/tftpboot/; paketat për serverin përkatës janë të disponueshme në shumicën e shpërndarjeve popullore, zakonisht quhen tftpd ose tftp-hpa, ndonjëherë duhet të bëni systemctl start tftpd.service për të filluar).
# вариант для tftpd
sudo cp build/base/bin/qspi.bin /srv/tftp
# вариант для tftp-hpa
sudo cp build/base/bin/qspi.bin /var/lib/tftpbootNë Embox (pra, në ngarkues) duhet të ekzekutoni këtë komandë (duke supozuar se adresa e serverit është 192.168.2.1):
embox> qspi_loader qspi.bin 192.168.2.16: Me ndihmën e komandës goto duhet "të hidheni" në memorien QSPI. Lokacioni specifik do të ndryshojë në varësi të mënyrës se si imazhi është lidhur, këtë adresë mund ta shihni me komandën mem 0x90000000 (adresa e fillimit përdoret në fjalën e dytë 32-bit të imazhit); gjithashtu do të kërkohet të vendosni flamurin e stack-ut -s, adresa e stack-ut ndodhet në adresën 0x90000000, shembull:
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
kjo është adresa kjo është adresa
e stack-ut e parë
e instrukcionit
embox>goto -i 0x9000c27f -s 0x20023200 # Flamuri -i është i nevojshëm për të ndaluar ndërprerjet gjatë inicimit të sistemit7: Nisim
embox> edges 20dhe shijoni kërkimin e kufijve për 40 sekonda 🙂
Nëse ndodhi ndonjë problem — shkruani një çështje në , ose në listën e postimeve embox-devel@googlegroups.com, ose në komentet këtu.
Burimi: habr.com
