Unë jam një nga zhvilluesit e sistemit operativ , dhe në këtë artikull do të ndaj me ju mënyrën se si e kam realizuar hapjen e OpenCV në pllakën STM32746G.
Nëse shkruani në motorin e kërkimit diçka si «OpenCV on STM32 board», mund të gjeni shumë njerëz që janë të interesuar në përdorimin e kësaj biblioteke në pllaka STM32 ose mikro-kontrollerë të tjerë.
Ekzistojnë disa video që, sipas emrit, duhet të demonstrojnë atë që është e nevojshme, por zakonisht (në të gjitha videot që kam parë) në pllakën STM32 është realizuar vetëm marrja e figurave nga kamera dhe shfaqja e rezultatit në ekran, ndërsa procesimi i imazhit është bërë ose në një kompjuter të zakonshëm, ose në pllaka më të fuqishme (siç është Raspberry Pi).
Pse është kjo e komplikuar?
Popullariteti i kërkesave në kërkimin shpjegohet me faktin se OpenCV është biblioteka më e njohur për vizionin kompjuterik, që do të thotë se shumë zhvillues e njohin atë, dhe gjithashtu mundësia për të ekzekutuar kodin e gatshëm për desktop në mikro-kontroller e thjeshton ndjeshëm procesin e zhvillimit. Por pse ende nuk ka receta të njohura të gatshme për zgjidhjen e kësaj probleme?
Problemi i përdorimit të OpenCV në pllaka të vogla lidhet me dy karakteristika:
- Nëse e kompilon bibliotekën edhe me një grup minimal modulash, ajo nuk do të hyjë në memorinë flash të STM32F7Discovery (madje pa marrë parasysh sistemin operativ) për shkak të kodit shumë të madh (disa megabajt instruksionesh).
- Biblioteka vetë është shkruar në C++, pra
- Kërkohet mbështetje për runtime-in e C++ (përjashtime dhe të tjera).
- Ka pak mbështetje për LibC/Posix, të cilat zakonisht janë të pranishme në sistemet operative për sisteme të embedded — është e nevojshme një bibliotekë standarde e C++ dhe biblioteka standarde e templating STL (vector etj.).
Portimi në Embox
Si zakonisht, para se të bëhet portimi i ndonjë programi në një sistem operative, është mirë ta provosh atë të ndërtosh në atë formë siç e kishin menduar zhvilluesit. Në rastin tonë, nuk ka probleme me këtë — burimet mund të gjenden në , biblioteka ndërtohet nën GNU/Linux me cmake të zakonshëm.
Nga lajmet e mira — OpenCV mund të ndërtohet si një bibliotekë statike nga kutia, që e bën portimin më të lehtë. Ndërtojmë bibliotekën me konfigurimin standard dhe shohim sa hapësirë zë ajo. Çdo modul ndërtohet si 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 mund të shihet nga rreshti i fundit, .bss dhe .data nuk zënë shumë vend, përkundrazi kodi është mbi 70 MiB. E qartë se nëse e çojmë këtë në lidhje statike me një aplikacion të caktuar, kodi do të bëhet më i vogël.
Do të përpiqemi të heqim sa më shumë module, në mënyrë që të ndërtojmë një shembull minimal (i cili, për shembull, thjesht do të tregojë versionin e OpenCV), kështu që le të shohim. cmake .. -LA dhe së pari çaktivizojmë në opsionet gjithçka që mund të çaktivizohet.
-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ë modull i bibliotekës, nga ana tjetër, ky është pa optimizim të kompilatorit për madhësinë e kodit (-Os). ~3 MiB kod – kjo ende është një sasi e konsiderueshme, por tashmë jep shpresë për sukses.
Ekzekutimi në emulator
Të debuguar në emulator është shumë më e lehtë, prandaj së pari të sigurohemi që biblioteka punon në qemu. Si platformë e emuluar kam zgjedhur Integrator/CP, sepse, përveç kësaj, është gjithashtu ARM, dhe për më tepë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 të ndihmojmë për të shtuar OpenCV si modull (duke kaluar të gjitha ato mundësi për 'ndërtimin minimal' në formën e bibliotekave statike), pas kësaj shtojmë 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ë – 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:
Koha: 2019-06-21T10:02:18Z
Kompjuteri: Linux 5.1.7-arch1-1-ARCH x86_64
Target: Generic arm-unknown-none
CMake: 3.14.5
Generatori CMake: Unix Makefiles
Mjeti i ndërtimit CMake: /usr/bin/make
Konfigurimi: Debug
Karakteristika CPU/HW:
Bazë:
e kërkuar: DETECT
e çaktivizuar: VFPV3 NEON
C/C++:
Ndërtuar si libra dinamik?: JOHapi tjetër është të ekzekutoni një shembull, më mirë ndonjë standard nga ata që ofrohen nga vetë zhvilluesit . Unë zgjodha .
Shembulli duhet të ri-shkruhej pak, për të shfaqur imazhin me rezultatin drejtpërdrejt në framebuffer. Këtë duhej ta bëja, pasi funksioni imshow() di të vizatojë imazhe përmes ndërfaqeve QT, GTK dhe Windows, të cilat, sigurisht, nuk do të jenë në konfigurimin për STM32. Në të vërtetë, QT gjithashtu mund të iniciohet në STM32F7Discovery, por për këtë do të flitet në një artikull tjetër 🙂
Pas një verifikim të shkurtër të formatit të ruajtjes së rezultatit të detektorit të skajit, ne marrim imazhin.

Imazhi origjinal

Rezultati
Një përfillje në STM32F7Discovery
Në 32F746GDISCOVERY ka disa segmente memorjeje që mund t'i përdorim në një farë mënyre.
- 320KiB memorie operative
- 1MiB memorie flash për imazhin
- 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ë shembujve minimalë, kjo nuk është shumë e dobishme.
Ekrani ka një rezolutë prej 480×272, që do të thotë se memoria për frame buffer do të jetë 522,240 byte me thellësi 32 bit, që është më shumë se madhësia e memorie operative, kështu që frame buffer dhe heap (të cilat do të nevojiten po ashtu për OpenCV për të mbajtur të dhëna për imazhet dhe strukturat ndihmëse) do të vendosen në SDRAM, ndërsa gjithçka tjetër (memoria për stekët dhe nevojat e tjera sistemike) do të dërgohet në RAM.
Nëse marrim konfigurimin minimal për STM32F7Discovery (heqim të gjithë rrjetin, të gjitha komandat, e bëjmë stekët sa më të vegjël dhe të tjera) dhe të shtojmë OpenCV me shembuj, atëherë kërkesat për memorie do të jenë si më poshtë:
teksti të dhëna bss dec hex emri i skedarit
2876890 459208 312736 3648834 37ad42 build/base/bin/emboxPër ata që nuk janë shumë të njohur me se cilat seksione i përkasin, le të shpjegoj: në .text dhe .rodata janë instruksionet dhe konstantet (në terma të thjeshta, të dhëna readonly), në .data janë të dhënat e ndryshueshme, në .bss janë variablat "të zeruara", të cilat, megjithatë, kërkojnë hapësirë (kjo seksion "do të dërgohet" në RAM).
Lajmi i mirë është se .data/.bss duhen vendosur, por këtu është .text problemi — për imazhin ka vetëm 1MiB të memories. Mund të hiqni imazhin nga shembulli dhe ta lexoni, për shembull, nga një SD kartë në memory gjatë nisjes, por fruits.png peshon rreth 330KiB, kështu që kjo nuk zgjidh problemin: pjesa më e madhe .text merrni imazhin nga shembulli dhe lexoni atë, për shembull, nga një kartë SD në kujtesë gjatë nisjes, por fruits.png peshon rreth 330KiB, kështu që kjo nuk do ta zgjidhë problemin: pjesa më e madhe .text është përbërë pikërisht nga kodi OpenCV.
Në thelb, ka mbetur vetëm një gjë — ngarkimi i një pjese të kodit në QSPI-flash (ajo ka një mod special për operimin e hartimit të memories në sistem, kështu që procesori mund t'i qaset këtyre të dhënave drejtpërdrejt). Megjithatë, ka një problem: së pari, memoria e QSPI-flash nuk është e qasshme menjëherë pas riparimit të pajisjes (duhet të inicializoni veçmas modin e memory-mapped), së dyti, nuk është e mundur të "programoni" këtë memory me një ngarkues të zakonshëm.
Përfundimisht, u vendos që të lidhim të gjithë kodin në QSPI dhe ta ngarkohet me një bootloader të shkruar vetë, i cili do të marrë binarin e nevojshëm përmes TFTP.
Rezultati
Idea për të portuar këtë bibliotekë në Embox ka lindur rreth një vit më parë, por vazhdimisht është shtyrë për shkak të arsyeve të ndryshme. Njëra prej tyre është mbështetje për libstdc++ dhe bibliotekën standarde të template. Problemi i mbështetjes së C++ në Embox kalon jashtë kësaj artikulli, prandaj këtu do të thosha vetëm se arritëm të sigurojmë këtë mbështetje në përmasat e nevojshme për funksionimin e kësaj biblioteke 🙂
Përfundimisht, këto probleme u përballën (të paktën, në masën e nevojshme për funksionimin e shembullit OpenCV), dhe shembulli filloi. 40 sekonda të gjatë i duhen bordit për të gjetur kufijtë me filtrin Canny. Kjo, sigurisht, është shumë e gjatë (ka disa propozime se si ta optimizojmë këtë, për të cilat mund të shkruajmë një artikull të veçantë në rast se kemi sukses).

Megjithatë, synimi ndërmjetës ishte krijimi i një prototipi që do të tregonte mundësinë themelore të aktivizimit të OpenCV në STM32, kështu që ky synim u arrit, urime!
tl;dr: udhëzime 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 bootloader-it që "do të ngarkojë" flash-in QSPI.
make confload-arm/stm32f7cubeTani, duhet të konfiguroni rrjetin, sepse do të ngarkojmë imazhin përmes TFTP. Për të caktuar adresat IP të bordit dhe hostit, duhet të ndryshoni skedarin conf/rootfs/network.
Shembulli i konfiguracionit:
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 e cila do të ngarkohet imazhi, adresë — adresa e bordit.
Pas kësaj, ne do të rindërtojmë bootloader-in:
make2: Ngarkimi normal i bootloader-it (falni për lojën me fjalë) në bord — këtu nuk ka asgjë specifike, duhet ta bëni si për çdo aplikacion tjetër për STM32F7Discovery. Nëse nuk e dini si ta bëni këtë, mund të lexoni rreth saj .
3: Kompilimi i imazhit me konfigurimin për OpenCV.
make confload-platform/opencv/stm32f7discovery
make4: Nxjerrja e sekzioneve nga ELF 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ë direktoriumin conf është skripti që e bën këtë, kështu që mund ta nisni atë
./conf/qspi_objcopy.sh # Binaries i nevojshëm -- build/base/bin/qspi.bin5: Me ndihmën e tftp ngarkojmë qspi.bin.bin në flash-in QSPI. Në host për këtë, ne duhet të kopjojmë qspi.bin në folderin rrënjë të serverit tftp (zakonisht është /srv/tftp/ ose /var/lib/tftpboot/; paketat për serverin përkatës janë në shumicën e distribucioneve të njohura, zakonisht quhet tftpd ose tftp-hpa, ndonjëherë duhet bërë 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 (dmth. në bootloader) duhet të ekzekutohet një komandë të tillë (të supozojmë që 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ë "këndohet" në memorien QSPI. Lokacioni konkret do të ndryshojë në varësi të mënyrës si lidhet imazhi, ky adresë mund të shikohet me komandën mem 0x90000000 (adresa e nisjes vendoset në fjalinë e dytë 32-bit të imazhit); gjithashtu do të kërkohet që të vendosim stek-un me flagun -s, adresa e stek-ut është në adresën 0x90000000, shembuj:
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
kjo është adresa kjo është adresa
e stek-ut e parë
instrukcioneve
embox>goto -i 0x9000c27f -s 0x20023200 # Flag-u -i është i nevojshë për të ndaluar ndërprerjet gjatë inicimit të sistemit7: Po fillojmë
embox> skaj 20dhe shijojmë një kërkim 40-sekondësh për kufij 🙂
Nëse diçka nuk shkon siç duhet — shkruani një issue në , ose në listën embox-devel@googlegroups.com, ose në komentet këtu.
Burimi: habr.com
