OpenCV uz STM32F7-Discovery

OpenCV uz STM32F7-Discovery Es esmu viens no operētājsistēmas izstrādātājiem Embox, 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 GitHub, 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 savas vietnes. Es izvēlējos Kēnija robežu detektoru.

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.

OpenCV uz STM32F7-Discovery

Oriģinālā bilde

OpenCV uz STM32F7-Discovery

Rezultāts

Palaist uz STM32F7Discovery

Uz 32F746GDISCOVERY ir vairāki aparatūras atmiņas segmenti, kurus mēs varam kaut kā izmantot

  1. 320KiB operatīvās atmiņas
  2. 1MiB zibspējas atmiņas attēlam
  3. 8MiB SDRAM
  4. 16MiB QSPI NAND zibatmiņa
  5. 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/embox

Lai 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).

OpenCV uz STM32F7-Discovery

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 ./embox

1: Sāksim ar ielādētāja izveidi, kurš «ierakstīs» QSPI zibspuldzi.

    make confload-arm/stm32f7cube

Tagad 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:02

gateway — hosta adrese, no kuras tiks ielādēts attēls, address — plates adrese.

Pēc tam sastādām ielādētāju:

    make

2: 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 šeit.
3: Attēla kompilācija ar konfigurāciju OpenCV.

    make confload-platform/opencv/stm32f7discovery
    make

4: 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=.data

Kopā 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.bin

5: 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/tftpboot

Emboxā (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.1

6: 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 20

un izbaudām 40 sekunžu robežizgūšanas procesu 🙂

Ja kaut kas noiet greizi — rakstiet jautājumu mūsu repozitorijā, vai embox-devel@googlegroups.com sarakstē, vai šeit komentāros.

Avots: habr.com

Iegādājieties uzticamu tīmekļa hostingu ar DDoS aizsardzību, VPS VDS serveriem 🔥 Iegādājieties uzticamu tīmekļa hostingu ar DDoS aizsardzību, VPS VDS serveriem | ProHoster