OpenCV na STM32F7-Discovery

OpenCV na STM32F7-Discovery Sem razvijalec operacijskega sistema Embox, v tem članku pa bom predstavil, kako sem uspel zagnati OpenCV na STM32746G plošči.

Če v iskalnik vpišete kaj takega kot »OpenCV na STM32 plošči«, lahko najdete kar nekaj ljudi, ki jih zanima uporaba te knjižnice na STM32 ploščah ali drugih mikrokrmilnikih.
Obstaja več videov, ki, sodeč po naslovu, naj bi prikazovali to, kar potrebujete, vendar običajno (v vseh videih, ki sem jih videl) na STM32 plošči samo pridobivajo sliko iz kamere in prikazujejo rezultat na ekranu, obdelava slik pa poteka bodisi na običajnem računalniku bodisi na zmogljivejših ploščah (na primer Raspberry Pi).

Zakaj je to zahtevno?

Priljubljenost iskalnih poizvedb je razložena s tem, da je OpenCV najbolj priljubljena knjižnica za računalniški vid, kar pomeni, da je z njo seznanjenih več razvijalcev, pa tudi možnost zagona kode, pripravljene za namizje, na mikrokrmilniku znatno poenostavi postopek razvoja. Ampak zakaj še vedno ni nekaterih popularnih rešitev za to težavo?

Težava pri uporabi OpenCV na manjših ploščah je povezana z dvema značilnostma:

  • Če knjižnico skompilirate, tudi z minimalnim naborom modulov, v flash pomnilnik STM32F7Discovery preprosto ne bo šla (tudi brez računanja operacijskega sistema) zaradi zelo velike kode (več megabajtov ukazov)
  • Sama knjižnica je napisana v C++, kar pomeni
    • Potrebna je podpora C++ runtime-a (izjeme itd.)
    • Pomanjkanje podpore za LibC/Posix, ki je običajno prisotna v operacijskih sistemih za vgrajene sisteme — potrebna je standardna knjižnica C++ in standardna knjižnica predlog STL (vector itd.)

Portiranje na Embox

Kot običajno, pred portiranjem kakršne koli programske opreme v operacijski sistem ni slabo poskusiti sestaviti v obliki, kot so jo načrtovali razvijalci. V našem primeru s tem ni težav — izvorne kode lahko najdete na GitHubu, knjižnico je mogoče sestaviti pod GNU/Linux z običajnim cmake-om.

Iz dobrih novic — OpenCV lahko iz škatlice sestavite kot statično knjižnico, kar olajša portiranje. Sestavimo knjižnico s standardno konfiguracijo in preverimo, koliko prostora zasedejo. Vsak modul se sestavi v ločeno knjižnico.

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

Kot je razvidno iz zadnjega vrstice, .bss in .data ne zavzemata veliko prostora, toda koda pa presega 70 MiB. Jasno je, da bo pri statični povezavi s specifično aplikacijo koda manjša.

Poskusimo odstraniti čim več modulov, da zgradimo minimalen primer (ki na primer preprosto izpiše različico OpenCV), zato poglejte cmake .. -LA in onemogočamo vse, kar se lahko onemogoči, v možnostih.

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

Po eni strani gre le za en modul knjižnice, po drugi strani pa je to brez optimizacije s strani kompajlerja za velikost kode (-Os). ~3 MiB kode – to je še vedno precej veliko, vendar že daje upanje na uspeh.

Zagon v emulatorju

Na emulatorju je debugiranje precej lažje, zato najprej preverimo, ali knjižnica deluje na qemu. Kot emulirana platforma sem izbral Integrator/CP, saj je to prav tako ARM, poleg tega pa Embox podpira izhod grafike za to platformo.

V Emboxu je mehanizem za gradnjo zunanjih knjižnic, s katerim dodamo OpenCV kot modul (predali smo vse iste možnosti za "minimalno" gradnjo v obliki statičnih knjižnic), nato dodam najpreprostejšo aplikacijo, ki izgleda tako:

version.cpp:

#include 
#include 

int main() {
    printf("OpenCV: %s", cv::getBuildInformation().c_str());

    return 0;
}

Zgradimo sistem, zaženemo – dobimo pričakovani izhod.

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

Следующий шаг — запустить какой-нибудь пример, лучше всего какой-нибудь стандартный из тех, что предлагают сами разработчики у себя на сайте. Я выбрал детектор границ Кэнни.

Пример пришлось немного переписать, чтобы отображать картинку с результатом напрямую во фрэйм-буффер. Сделать это пришлось, т.к. функция imshow() умеет отрисовывать изображения через интерфейсы QT, GTK и Windows, которых, само собой, в конфиге для STM32 точно не будет. На самом деле, QT тоже можно запустить на STM32F7Discovery, но об этом будет рассказано уже в другой статье 🙂

После недолгого выяснения, в каком именно формате хранится результат работы детектора границ, получаем изображение.

OpenCV na STM32F7-Discovery

Оригинальная картинка

OpenCV na STM32F7-Discovery

Результат.

Запуск на STM32F7Discovery

На 32F746GDISCOVERY есть несколько аппаратных разделов памяти, которые мы можем так или иначе использовать

  1. 320KiB оперативной памяти
  2. 1MiB флэш-памяти для образа
  3. 8MiB SDRAM
  4. 16MiB QSPI NAND-флэшка
  5. Разъём для microSD-карточки

SD-карту можно использовать для хранения изображений, но в контексте запуска минимального примера это не очень полезно.
Дисплей имеет разрешение 480×272, а значит, память под фреймбуффер составит 522 240 байт при глубине 32 бита, т.е. это больше, чем размер оперативной памяти, так что фреймбуффер и кучу (которая потребуется в том числе для OpenCV, чтобы хранить данные для изображений и вспомогательных структур) будем располагать в SDRAM, всё остальное (память под стэки и прочие системные нужды) отправится в RAM.

Если взять минимальный конфиг для STM32F7Discovery (выкинуть всю сеть, все команды, сделать стэки как можно меньше и т.д.) и добавить туда OpenCV с примерами, с требуемой памятью будет следующее:

   text    data     bss     dec     hex filename
2876890  459208  312736 3648834  37ad42 build/base/bin/embox

Za tiste, ki niso ravno seznanjeni s tem, katere sekcije kam spadajo, naj pojasnim: v .text in .rodata leže navodila in konstante (preprosto rečeno, readonly-podatki), v .data leže podatki, ki jih je mogoče spreminjati, v .bss pa so »nulirane« spremenljivke, ki pa vseeno potrebujejo prostor (ta sekcija se bo »poslala« v RAM).

Dobre novice so, da .data/.bss morajo biti shranjene, medtem ko je .text težava — pod sliko je le 1MiB pomnilnika. Lahko odstranimo .text sliko iz primera in jo preberemo, na primer, s SD-kartice v pomnilnik ob zagonu, vendar fruits.png tehta približno 330KiB, tako da ta težave ne reši: velika večina .text sestavlja prav to koda OpenCV.

Na splošno ostane le še ena možnost — naložitev dela kode na QSPI-flash pomnilnik (ta ima poseben način delovanja za mapiranje pomnilnika na sistemsko vodilo, tako da lahko procesor neposredno dostopa do teh podatkov). Pri tem se pojavi težava: najprej, pomnilnik QSPI-flash ni takoj dostopen po ponovnem zagonu naprave (potrebno je posebej inicializirati način memory-mapped), drugič, te pomnilnike ni mogoče »naložiti« s običajnim zagonskim programom.

Na koncu je bilo odločeno, da se bo celotna koda povezala v QSPI, in se jo bo naložilo s samostojnim zagonskim programom, ki bo pridobival ustrezen binarni datoteko po TFTP.

Результат.

Ideja o prenosu te knjižnice na Embox se je pojavila že pred približno letom dni, vendar je bila večkrat odložena iz različnih razlogov. Eden od teh razlogov je podpora libstdc++ in standardni knjižnici predlogov. Problem s podporo C++ v Emboxu presega obseg te vsebine, zato naj povem le, da nam je uspelo doseči to podporo v potrebni meri za delovanje te knjižnice 🙂

Na koncu so bile te težave premagane (vsaj v zadostni meri za delovanje primera OpenCV) in primer se je zagnal. Iskanje meja s Canny filtri traja 40 dolgotrajnih sekund. To je seveda predolgo (obstajajo ideje, kako to optimizirati, o tem pa bi lahko napisali poseben članek v primeru uspeha).

OpenCV na STM32F7-Discovery

Kljub temu je bila vmesna naloga ustvariti prototip, ki bi pokazal načelno možnost uporabe OpenCV na STM32, zato je ta cilj dosežen, hura!

tl;dr: korak za korakom navodila

0: Prenesemo vire Embox, na primer tako:

    git clone https://github.com/embox/embox && cd ./embox

1: Začeli bomo s sestavo zagonskega programa, ki bo »naložil« QSPI-flash.

    make confload-arm/stm32f7cube

Sedaj je potrebno nastaviti omrežje, ker bomo prenesli sliko preko TFTP. Da bi dodelili IP-naslove kartici in gostitelju, je treba spremeniti datoteko conf/rootfs/network.

Primer konfiguracije:

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

prehod — naslov gostitelja, s katerega bo slika naložena, naslov — naslov kartice.

Po tem zberemo zaganjalnik:

    make

2: Običajno nalaganje zaganjalnika (oprosti za besedno igro) na kartico — tukaj ni nič posebnega, morate to storiti kot pri kateri koli drugi aplikaciji za STM32F7Discovery. Če ne veste, kako to narediti, lahko preberete o tem. tukaj.
3: Kompilacija slike s konfiguracijo za OpenCV.

    make confload-platform/opencv/stm32f7discovery
    make

4: Izvlečenje iz ELF sekcij, ki jih je treba zapisati v QSPI, v 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

V direktoriju conf se nahaja skripta, ki to stori, tako da jo lahko zaženete.

    ./conf/qspi_objcopy.sh # Potreben binar — build/base/bin/qspi.bin

5: S pomočjo tftp naložimo qspi.bin na QSPI-pomnilnik. Na gostitelju je treba qspi.bin kopirati v korensko mapo tftp-strežnika (ponavadi je to /srv/tftp/ ali /var/lib/tftpboot/; paketi za ustrezen strežnik so na voljo v večini priljubljenih distribucij, običajno se imenuje tftpd ali tftp-hpa, včasih je potrebno zagnati systemctl start tftpd.service za začetek).

    # вариант для tftpd
    sudo cp build/base/bin/qspi.bin /srv/tftp
    # вариант для tftp-hpa
    sudo cp build/base/bin/qspi.bin /var/lib/tftpboot

Na Embox-u (t.j. v zaganjalniku) je treba izvesti ukaz (predvidevamo, da je naslov strežnika 192.168.2.1):

    embox> qspi_loader qspi.bin 192.168.2.1

6: S pomočjo ukaza goto morate 'skočiti' v QSPI-pomnilnik. Konkretna lokacija se bo razlikovala glede na to, kako se povezuje slika, ta naslov lahko preverite z ukazom mem 0x90000000 (naslov začetka je zapisan v druge 32-bitne besede slike); prav tako bo potrebno nastaviti sklad s flagom -s, naslov sklada leži na naslovu 0x90000000, primer:

    embox>mem 0x90000000
    0x90000000:     0x20023200  0x9000c27f  0x9000c275  0x9000c275
                      ↑           ↑
              to je naslov    to je  naslov 
                sklada        prve
                           instrukcije

    embox>goto -i 0x9000c27f -s 0x20023200 # Flag -i je potreben, da prepreči prekinitve med inicializacijo sistema

7: Začnite

    embox> edges 20

in uživajte v 40-sekundnem iskanju meja 🙂

Če bo prišlo do težav, pišite na težave v našem repozitoriju, ali na mailing listo embox-devel@googlegroups.com, ali v komentarje tukaj.

Izvor: habr.com

Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki 🔥 Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki | ProHoster