Es esmu viens no operētājsistēmas izstrādātājiem , un šajā rakstā es pastāstīšu, kā man izdevās palaist OpenCV uz STM32746G plates.
Ja meklētājā ieraksta kaut ko līdzīgu "OpenCV on STM32 board", var atrast diezgan daudz cilvēku, kuri interesējas par šīs bibliotēkas izmantošanu STM32 vai citu mikrokontrolieru platformās.
Ir vairāki video, kas, spriežot pēc nosaukuma, demonstrē to, kas nepieciešams, taču parasti (visos redzētajos video) uz STM32 plates tika veikta tikai attēla iegūšana no kameras un rezultāta rādīšana ekrānā, bet paša attēla apstrāde notika vai nu uz parasta datora, vai uz jaudīgākām platēm (piemēram, Raspberry Pi).
Kāpēc tas ir sarežģīti?
Meklēšanas pieprasījumu popularitāti izskaidro tas, ka OpenCV ir vispopulārākā datorspēka bibliotēka, tādējādi ar to ir iepazinušies vairāk izstrādātāju, kā arī iespēja palaist gatavu kodu, kas paredzēts darbvirsmām, mikrokontrolierī ievērojami atvieglo izstrādes procesu. Bet kāpēc joprojām nav populāru risinājumu receptes šai problēmai?
Problēma, izmantojot OpenCV uz mazām plātnēm, ir saistīta ar divām iezīmēm:
- Ja bibliotēka tiek kompilēta pat ar minimālo moduļu kopu, tai vienkārši nav vietas STM32F7Discovery zibatmiņā (pat neskatoties uz operētājsistēmu) tā lielā koda dēļ (vairāki megabaiti instrukciju)
- Pati bibliotēka ir uzrakstīta C++, kas nozīmē
- Nepieciešama atbalsta C++ laikā (izņēmumi utt.)
- Ierobežots LibC/Posix atbalsts, kas parasti ir iebūvētajām operētājsistēmām — nepieciešama standarta C++ bibliotēka un standarta veidņu bibliotēka STL (vektori utt.)
Pārvietošana uz Embox
Kā parasti, pirms jebkuru programmu pārvietošanas operētājsistēmā ir labi izmēģināt tās salikšanu tādā veidā, kā to domāja izstrādātāji. Mūsu gadījumā ar to nav problēmu — avots var atrast , bibliotēka tiek salikta GNU/Linux parastajā cmake.
No labajām ziņām — OpenCV no kastes var salikt statiskās bibliotēkas veidā, kas atvieglo pārvietošanu. Saliekam bibliotēku ar standarta konfigurāciju un skatāmies, cik daudz vietas tās aizņem. Katrs modulis tiek salikts atsevišķā bibliotēkā.
> izmērs lib/*so --totals
teksts dati bss dec hex fails
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)Kā redzams pēdējā rindā, .bss un .data neaizņem tik daudz vietas, taču koda ir vairāk nekā 70 MiB. Ir skaidrs, ka, ja to stingri sasaistām ar konkrētu lietojumprogrammu, koda būs mazāk.
Mēģināsim izmest pēc iespējas vairāk moduļu, lai izveidotu minimālu piemēru (kas, piemēram, vienkārši izvadīs OpenCV versiju), tāpēc skatāmies. cmake .. -LA un atslēdzam visus iespējamos opcijas.
-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> izmērs lib/libopencv_core.a --totals
teksts dati bss dec hex fails
3317069 36425 17987 3371481 3371d9 (TOTALS)No vienas puses, tas ir tikai viens bibliotēkas modulis, no otras puses, tas ir bez optimizācijas kompilatorā pēc koda izmēra (-Os). ~3 MiB koda — tas joprojām ir diezgan daudz, bet jau dod cerību uz panākumiem.
Palaišana emulatorā
Emulatorā atkļūdošana ir daudz vienkāršāka, tāpēc vispirms pārliecināmies, ka bibliotēka darbojas uz qemu. Kā emulatoram izvēlējos Integrator/CP, jo, pirmkārt, tas arī ir ARM, un, otrkārt, Embox atbalsta grafikas izvadīšanu šai platformai.
Embox ir mehānisms, lai veidotu ārējās bibliotēkas, ar tā palīdzību pievienojam OpenCV kā moduli (pārsūtot visas tās pašas opcijas "minimālai" izveidei kā statiskas bibliotēkas), pēc tam pievienoju vienkāršāko lietojumprogrammu, kas izskatās šādi:
version.cpp:
#include
#include
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}Sagatavojam sistēmu, palaižam — iegūstam gaidīto izvadi.
root@embox:/$opencv_version
OpenCV:
Vispārējā konfigurācija OpenCV 4.0.1 =====================================
Versijas kontrole: bd6927bdf-dirty
Platforma:
Laiks: 2019-06-21T10:02:18Z
Host: Linux 5.1.7-arch1-1-ARCH x86_64
Mērķis: Vispārīgs arm-unknown-none
CMake: 3.14.5
CMake ģenerators: Unix Makefiles
CMake veidošanas rīks: /usr/bin/make
Konfigurācija: Debug
CPU/HW funkcijas:
Bāzes līmenis:
pieprasīts: DETECT
izslēgts: VFPV3 NEON
C/C++:
Veidots kā dinamiskas bibliotēkas?: NĒ
< Pārējie kompilācijas parametri -- ar kādiem karogiem kompileja,
kādi OpenCV moduļi iekļauti kompilācijā utt.>Nākamais solis — palaist kādu piemēru, labāk kādu standarta no tiem, ko piedāvā paši izstrādātāji . Es izvēlējos .
Piemēru nācās nedaudz pārrakstīt, lai attēlotu rezultāta bildi tieši frēma buferī. To nācās izdarīt, jo funkcija imshow() spēj attēlot bildes caur QT, GTK un Windows interfeisiem, kuru, protams, konfigurācijā STM32 noteikti nebūs. Patiesībā, QT arī var palaist uz STM32F7Discovery, bet par to jau būs runāts citā rakstā 🙂
Pēc īsas noskaidrošanas, kādā tieši formātā tiek glabāts detektora robežu darbības rezultāts, iegūstam attēlu.

Oriģinālā bilde

Rezultāts
Palaist uz STM32F7Discovery
Uz 32F746GDISCOVERY ir vairāki aparatūras atmiņas segmenti, kurus mēs varam kaut kā izmantot
- 320KiB operatīvās atmiņas
- 1MiB zibspējas atmiņas attēlam
- 8MiB SDRAM
- 16MiB QSPI NAND zibatmiņa
- Savienojums microSD kartei
SD karti var izmantot attēlu uzglabāšanai, bet minimālā piemēra palaišanā tas nav īpaši noderīgi.
Displejam ir izšķirtspēja 480×272, un tāpēc atmiņa frēma buferim sastādīs 522 240 baitus ar dziļumu 32 biti, t.i., tas ir vairāk nekā operatīvās atmiņas apjoms, tāpēc frēma buferis un kaudze (kas būs nepieciešama arī OpenCV, lai uzglabātu datus attēliem un palīgstruktūrām) tiks izvietoti SDRAM, viss pārējais (atmiņa stekiem un citiem sistēmas mērķiem) tiks nosūtīts uz RAM.
Ja ņem simtprocentīgu konfigurāciju STM32F7Discovery (izmetot visu tīklu, visas komandas, padarot stekus pēc iespējas mazākus utt.) un pievienojam tur OpenCV ar piemēriem, nepieciešamā atmiņa būs sekojoša:
text data bss dec hex faila nosaukums
2876890 459208 312736 3648834 37ad42 build/base/bin/emboxLai precizētu, kuras sadaļas kur iet, skaidrošu: .text un .rodata atrodas instrukcijas un konstantes (vienkāršoti sakot, readonly dati), .data atrodas maināmie dati, .bss atrodas «nullēti» mainīgie, kuriem, tomēr, ir nepieciešama vieta (šī sadaļa «pāries» uz RAM).
Laba ziņa ir tā, ka .data/.bss jāierodas, bet ar .text ir problēma — attēlam ir pieejama tikai 1MiB atmiņa. Var izmest no .text piemēra attēlu un lasīt to, piemēram, no SD kartes atmiņā startējot, bet fruits.png svars ir aptuveni 330KiB, tāpēc tas problēmu atrisinās: lielākā daļa .text sastāv tieši no OpenCV koda.
Lai nu kā, paliek tikai viena iespēja — noloadēt daļu koda uz QSPI zibspuldzi (tai ir īpašs darba režīms, lai kartētu atmiņu uz sistēmas autobusu, tāpēc procesors varētu piekļūt šiem datiem tieši). Tomēr rodas problēmas: pirmkārt, QSPI zibspuldzes atmiņa nav pieejama uzreiz pēc ierīces restartēšanas (jāveic atsevišķa atmiņas kartes inicializācija), un, otrkārt, šo atmiņu nevar «ierakstīt» ar ierasto ielādētāju.
Tāpēc tika nolemts sagrupēt visu kodu QSPI un veikt tā uzrakstīšanu ar pašu rakstītu ielādētāju, kurš saņems nepieciešamos bināros datus pa TFTP.
Rezultāts
Ideja portēt šo bibliotēku uz Embox radās vēl apmēram pirms gada, bet tā tika atkārtoti atlikta dažādu iemeslu dēļ. Viens no tiem bija libstdc++ un standartā šablona bibliotēkas atbalsts. Problēma ar C++ atbalstu Embox iziet ārpus šī raksta, tāpēc šeit tikai teikšu, ka mums izdevās sasniegt šo atbalstu nepieciešamajā apmērā, lai šī bibliotēka darbotos 🙂
Galu galā šīs problēmas tika pārvarētas (vismaz pietiekamā līmenī, lai OpenCV piemērs darbotos), un piemērs izpildījās. 40 ilgstošas sekundes plati meklē robežas ar Kēnija filtrāciju. Tas, protams, ir par ilgu (ir idejas, kā to optimizēt, par ko varētu uzrakstīt atsevišķu rakstu, ja tas būs izdevies).

Tomēr starpposma mērķis bija prototipa izveide, kas parādītu principālu iespēju palaist OpenCV uz STM32, attiecīgi šis mērķis tika sasniegts, hura!
tl;dr: soli pa solim instrukcija
0: Lejupielādējam Embox avota kodu, piemēram, šādi:
git clone https://github.com/embox/embox && cd ./embox1: Sāksim ar ielādētāja izveidi, kurš «ierakstīs» QSPI zibspuldzi.
make confload-arm/stm32f7cubeTagad ir jāiestata tīkls, jo attēlu ielādēsim pa TFTP. Lai norādītu plates un hosta IP adreses, jāpārveido fails conf/rootfs/network.
Konfigurācijas piemērs:
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 — hosta adrese, no kuras tiks ielādēts attēls, address — plates adrese.
Pēc tam sastādām ielādētāju:
make2: Standarta ielādētāja (atvainojiet par spēlēm ar vārdiem) augšupielāde uz plates — šeit nav nekā specifiska, šis process jāveic tāpat kā jebkura cita lietojuma gadījumā STM32F7Discovery. Ja nesaprotat, kā to izdarīt, varat izlasīt par to .
3: Attēla kompilācija ar konfigurāciju OpenCV.
make confload-platform/opencv/stm32f7discovery
make4: Sekciju izvilkšana no ELF, kuras jāieraksta QSPI, failā 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=.dataKopā ar conf direktorijā ir skripts, kas to veic, tāpēc varat to palaist
./conf/qspi_objcopy.sh # Nepieciešamais binārais fails -- build/base/bin/qspi.bin5: Ar tftp palīdzību augšupielādējam qspi.bin uz QSPI zibatmiņu. Hostā jākopē qspi.bin uz tftp-servera saknes mapi (parasti tas ir /srv/tftp/ vai /var/lib/tftpboot/; attiecīgie paketes ir pieejami lielākajā daļā populāro izplatīšanas versiju, parasti to dēvē par tftpd vai tftp-hpa, dažreiz ir jāveic systemctl start tftpd.service lai uzsāktu).
# вариант для tftpd
sudo cp build/base/bin/qspi.bin /srv/tftp
# вариант для tftp-hpa
sudo cp build/base/bin/qspi.bin /var/lib/tftpbootEmboxā (t.i., ielādētājā) šāda komanda jāizpilda (pieņemot, ka servera adrese ir 192.168.2.1):
embox> qspi_loader qspi.bin 192.168.2.16: Ar komandu goto jāizdara 'lēciena' uz QSPI atmiņu. Konkrētā atrašanās vieta var mainīties atkarībā no tā, kā attēls ir saslēgts, šo adresi var pārbaudīt ar komandu mem 0x90000000 (starta adrese ir otrajā 32 bitu vārdā no attēla); būs arī jāiestata steka karogs -s, steka adrese atrodas adresē 0x90000000, piemērs:
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
tā ir adrese tā ir adrese
stekam pirmās
instrukcijas
embox>goto -i 0x9000c27f -s 0x20023200 # Karogs -i nepieciešams, lai nepieļautu pārtraukumus sistēmas inicializācijas laikā
7: Uzsākam
embox> edges 20un izbaudām 40 sekunžu robežizgūšanas procesu 🙂
Ja kaut kas noiet greizi — rakstiet jautājumu mūsu , vai embox-devel@googlegroups.com sarakstē, vai šeit komentāros.
Avots: habr.com
