Unë jam një nga zhvilluesit e sistemit operativ
Nëse shkruani diçka si "OpenCV në tabelën STM32" në një motor kërkimi, mund të gjeni mjaft njerëz që janë të interesuar të përdorin këtë bibliotekë në bordet STM32 ose mikrokontrollues të tjerë.
Ka disa video që, duke gjykuar nga emri, duhet të demonstrojnë atë që nevojitet, por zakonisht (në të gjitha videot që pashë) në tabelën STM32, vetëm imazhi u mor nga kamera dhe rezultati shfaqej në ekran, dhe vetë përpunimi i imazhit është bërë ose në një kompjuter të rregullt, ose në borde më të fuqishme (për shembull, Raspberry Pi).
Pse është e vështirë?
Popullariteti i pyetjeve të kërkimit shpjegohet me faktin se OpenCV është biblioteka më e njohur e vizionit kompjuterik, që do të thotë se më shumë zhvillues janë të njohur me të dhe aftësia për të ekzekutuar kodin gati për desktop në një mikrokontrollues thjeshton shumë procesin e zhvillimit. Por pse nuk ka ende receta popullore të gatshme për zgjidhjen e këtij problemi?
Problemi i përdorimit të OpenCV në shalle të vogla lidhet me dy veçori:
- Nëse e përpiloni bibliotekën edhe me një grup minimal modulesh, ajo thjesht nuk do të futet në memorien flash të të njëjtit STM32F7Discovery (edhe pa marrë parasysh sistemin operativ) për shkak të një kodi shumë të madh (disa megabajt udhëzime)
- Vetë biblioteka është e shkruar në C++, që do të thotë
- Keni nevojë për mbështetje për kohëzgjatjen pozitive (përjashtimet, etj.)
- Mbështetje e vogël për LibC/Posix, e cila zakonisht gjendet në OS për sistemet e integruara - keni nevojë për një bibliotekë standarde plus dhe një bibliotekë standarde të shablloneve STL (vektor, etj.)
Transferimi në Embox
Si zakonisht, përpara se të transferoni ndonjë program në sistemin operativ, është mirë të përpiqeni ta ndërtoni atë në formën në të cilën zhvilluesit e synuan. Në rastin tonë, nuk ka probleme me këtë - kodi burim mund të gjendet në
Lajmi i mirë është se OpenCV mund të ndërtohet si një bibliotekë statike jashtë kutisë, gjë që e bën më të lehtë transferimin. Ne mbledhim një bibliotekë me një konfigurim standard dhe shohim se sa hapësirë zënë. Çdo modul është mbledhur 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ç mund ta shihni nga rreshti i fundit, .bss dhe .data nuk zënë shumë hapësirë, por kodi është më shumë se 70 MiB. Është e qartë se nëse kjo është e lidhur statikisht me një aplikacion specifik, kodi do të bëhet më i vogël.
Le të përpiqemi të hedhim sa më shumë module që të jetë e mundur në mënyrë që të mblidhet një shembull minimal (i cili, për shembull, thjesht do të nxjerrë versionin OpenCV), kështu që ne shikojmë cmake .. -LA
dhe fikni në opsionet gjithçka që 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 optimizim të përpiluesit për madhësinë e kodit (-Os
). ~ 3 MiB kod është ende shumë, por tashmë jep shpresë për sukses.
Ekzekutoni në emulator
Është shumë më e lehtë për të korrigjuar gabimet në emulator, kështu që së pari sigurohuni që biblioteka të funksionojë në qemu. Si një platformë emuluar, zgjodha Integrator / CP, sepse së pari, është gjithashtu ARM, dhe së dyti, Embox mbështet daljen grafike për këtë platformë.
Embox ka një mekanizëm për ndërtimin e bibliotekave të jashtme, duke e përdorur atë shtojmë OpenCV si modul (duke kaluar të gjitha opsionet e njëjta 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 <stdio.h>
#include <opencv2/core/utility.hpp>
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}
Ne mbledhim sistemin, e drejtojmë atë - marrim rezultatin e pritur.
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 включены в сборку и т.п.>
Hapi tjetër është të ekzekutoni disa shembuj, mundësisht një nga ato standarde të ofruara nga vetë zhvilluesit.
Shembulli duhej të rishkruhej pak për të shfaqur imazhin me rezultatin direkt në buferin e kornizës. Më duhej ta bëja këtë, sepse. funksionin imshow()
mund të vizatojë imazhe përmes ndërfaqeve QT, GTK dhe Windows, të cilat, natyrisht, definitivisht nuk do të jenë në konfigurimin për STM32. Në fakt, QT mund të ekzekutohet edhe në STM32F7Discovery, por kjo do të diskutohet në një artikull tjetër 🙂
Pas një sqarimi të shkurtër në cilin format ruhet rezultati i detektorit të skajit, marrim një imazh.
foto origjinale
Result
Punon në STM32F7Discovery
Në 32F746GDISCOVERY ka disa seksione të memories harduerike që mund t'i përdorim në një mënyrë ose në një tjetër
- RAM 320 KiB
- Blic 1 MiB për imazhin
- 8 MiB SDRAM
- Flash 16 MiB QSPI NAND
- fole për kartën microSD
Një kartë SD mund të përdoret për të ruajtur imazhet, por në kontekstin e ekzekutimit të një shembulli minimal, kjo nuk është shumë e dobishme.
Ekrani ka një rezolucion prej 480×272, që do të thotë se memoria framebuffer do të jetë 522 byte në një thellësi prej 240 bit, d.m.th. kjo është më shumë se madhësia e RAM-it, kështu që framebuffer dhe grumbulli (i cili do të kërkohet, duke përfshirë OpenCV, për të ruajtur të dhënat për imazhet dhe strukturat ndihmëse) do të vendosen në SDRAM, gjithçka tjetër (memoria për rafte dhe nevoja të tjera të sistemit ) do të shkojë në RAM.
Nëse marrim konfigurimin minimal për STM32F7Discovery (heqim të gjithë rrjetin, të gjitha komandat, bëjmë steka sa më të vogla, etj.) dhe shtojmë OpenCV me shembuj atje, memoria e kërkuar do të jetë si më poshtë:
text data bss dec hex filename
2876890 459208 312736 3648834 37ad42 build/base/bin/embox
Për ata që nuk janë shumë të njohur se cilat seksione shkojnë, unë do t'ju shpjegoj: në .text
и .rodata
udhëzimet dhe konstantet (përafërsisht, të dhëna vetëm për lexim) qëndrojnë brenda .data
të dhënat janë të ndryshueshme, .bss
ka variabla "të pavlefshëm", të cilët, megjithatë, kanë nevojë për një vend (ky seksion do të "shkojë" në RAM).
Lajmi i mirë është se .data
/.bss
duhet të përshtatet, por me .text
Problemi është se ka vetëm 1 MiB memorie për imazhin. Mund të hidhet jashtë .text
foton nga shembulli dhe lexoni atë, për shembull, nga karta SD në memorie në fillim, por fruits.png peshon rreth 330 KiB, kështu që kjo nuk do ta zgjidhë problemin: shumica .text
përbëhet nga kodi OpenCV.
Në përgjithësi, ka mbetur vetëm një gjë - ngarkimi i një pjese të kodit në një blic QSPI (ai ka një mënyrë të veçantë funksionimi për hartimin e kujtesës në autobusin e sistemit, në mënyrë që procesori të mund t'i qaset drejtpërdrejt këtyre të dhënave). Në këtë rast, lind një problem: së pari, kujtesa e një flash drive QSPI nuk është e disponueshme menjëherë pasi pajisja të rindizet (duhet të inicializoni veçmas modalitetin e hartës së kujtesës), dhe së dyti, nuk mund ta "flash" këtë memorie me një bootloader i njohur.
Si rezultat, u vendos që të lidhej i gjithë kodi në QSPI dhe ta ndezë atë me një ngarkues të vetë-shkruar që do të marrë binarin e kërkuar përmes TFTP.
Result
Ideja për ta transferuar këtë bibliotekë në Embox u shfaq rreth një vit më parë, por pa pushim u shty për arsye të ndryshme. Një prej tyre është mbështetja për libstdc++ dhe bibliotekën standarde të shablloneve. Problemi i mbështetjes së C++ në Embox është përtej qëllimit të këtij artikulli, kështu që këtu do të them vetëm se ne arritëm ta arrijmë këtë mbështetje në sasinë e duhur që kjo bibliotekë të funksionojë 🙂
Në fund, këto probleme u tejkaluan (të paktën mjafton që shembulli OpenCV të funksionojë) dhe shembulli u zhvillua. Duhen 40 sekonda të gjata që bordi të kërkojë për kufijtë duke përdorur filtrin Canny. Kjo, natyrisht, është shumë e gjatë (ka konsiderata se si të optimizohet kjo çështje, do të jetë e mundur të shkruhet një artikull i veçantë për këtë në rast suksesi).
Sidoqoftë, qëllimi i ndërmjetëm ishte krijimi i një prototipi që do të tregojë mundësinë themelore të ekzekutimit të OpenCV në STM32, respektivisht, ky qëllim u arrit, urrë!
tl;dr: udhëzime hap pas hapi
0: Shkarkoni burimet e Embox, si kjo:
git clone https://github.com/embox/embox && cd ./embox
1: Le të fillojmë duke montuar një bootloader që do të "flash" një flash drive QSPI.
make confload-arm/stm32f7cube
Tani ju duhet të konfiguroni rrjetin, sepse. Ne do të ngarkojmë imazhin përmes TFTP. Për të vendosur adresat IP të bordit dhe të hostit, duhet të modifikoni conf/rootfs/rrjetin.
Shembull 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:02
gateway
- adresa e hostit nga ku do të ngarkohet imazhi, address
- adresa e bordit.
Pas kësaj, ne mbledhim ngarkuesin:
make
2: Ngarkimi i zakonshëm i ngarkuesit (më falni për lojën e fjalës) në tabelë - nuk ka asgjë specifike këtu, duhet ta bëni atë si për çdo aplikacion tjetër për STM32F7Discovery. Nëse nuk dini si ta bëni, mund të lexoni për të
3: Përpilimi i një imazhi me një konfigurim për OpenCV.
make confload-platform/opencv/stm32f7discovery
make
4: Ekstrakt nga seksionet ELF për t'u shkruar 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=.data
Ekziston një skript në direktorinë conf që e bën këtë, kështu që ju mund ta ekzekutoni atë
./conf/qspi_objcopy.sh # Нужный бинарник -- build/base/bin/qspi.bin
5: Duke përdorur tftp, shkarkoni qspi.bin.bin në një flash drive QSPI. Në host, për ta bërë këtë, kopjoni qspi.bin në dosjen rrënjë të serverit tftp (zakonisht /srv/tftp/ ose /var/lib/tftpboot/; paketat për serverin përkatës janë të disponueshme në shpërndarjet më të njohura, zakonisht të quajtura tftpd ose tftp-hpa, ndonjëherë duhet të bëni systemctl start tftpd.service
të fillosh).
# вариант для tftpd
sudo cp build/base/bin/qspi.bin /srv/tftp
# вариант для tftp-hpa
sudo cp build/base/bin/qspi.bin /var/lib/tftpboot
Në Embox (d.m.th. në ngarkuesin e ngarkimit), duhet të ekzekutoni komandën e mëposhtme (supozojmë se serveri ka adresën 192.168.2.1):
embox> qspi_loader qspi.bin 192.168.2.1
6: Me komandë goto
ju duhet të "kërceni" në memorien QSPI. Vendndodhja specifike do të ndryshojë në varësi të mënyrës se si është lidhur imazhi, ju mund ta shihni këtë adresë me komandën mem 0x90000000
(adresa e fillimit përshtatet në fjalën e dytë 32-bit të figurës); do t'ju duhet gjithashtu të shënoni pirgun -s
, adresa e stivës është në 0x90000000, shembull:
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
это адрес это адрес
стэка первой
инструкции
embox>goto -i 0x9000c27f -s 0x20023200 # Флаг -i нужен чтобы запретить прерывания во время инициализации системы
< Начиная отсюда будет вывод не загрузчика, а образа с OpenCV >
7: Nisja
embox> edges 20
dhe shijoni kërkimin e kufirit prej 40 sekondash 🙂
Nëse diçka shkon keq - shkruani një problem në
Burimi: www.habr.com