OpenCV STM32F7-Discovery

OpenCV STM32F7-Discovery Esu vienas iš operacinės sistemos kūrėjų Embox, o šiame straipsnyje kalbėsiu apie tai, kaip man pavyko paleisti OpenCV STM32746G plokštėje.

Jei į paieškos sistemą įvesite kažką panašaus į „OpenCV STM32 plokštėje“, galite rasti nemažai žmonių, kurie domisi šia biblioteka STM32 plokštėse ar kituose mikrovaldikliuose.
Yra keletas vaizdo įrašų, kurie, sprendžiant iš pavadinimo, turėtų parodyti, ko reikia, tačiau paprastai (visuose vaizdo įrašuose, kuriuos mačiau) STM32 plokštėje buvo gautas tik vaizdas iš fotoaparato, o rezultatas buvo rodomas ekrane, o pats vaizdo apdorojimas buvo atliktas arba įprastu kompiuteriu, arba ant galingesnių plokščių (pavyzdžiui, Raspberry Pi).

Kodėl sunku?

Paieškos užklausų populiarumas paaiškinamas tuo, kad OpenCV yra pati populiariausia kompiuterinės vizijos biblioteka, o tai reiškia, kad daugiau kūrėjų yra susipažinę su ja, o galimybė paleisti darbalaukiui paruoštą kodą mikrovaldiklyje labai supaprastina kūrimo procesą. Bet kodėl vis dar nėra populiarių paruoštų receptų, kaip išspręsti šią problemą?

OpenCV naudojimo mažose skarose problema yra susijusi su dviem ypatybėmis:

  • Jei sudarysite biblioteką net su minimaliu modulių rinkiniu, ji tiesiog netilps į to paties STM32F7Discovery „flash“ atmintį (net neatsižvelgiant į OS) dėl labai didelio kodo (keli megabaitai instrukcijų)
  • Pati biblioteka parašyta C++, o tai reiškia
    • Reikia palaikyti teigiamą vykdymo laiką (išimtys ir pan.)
    • Mažai palaikoma LibC/Posix, kuri paprastai randama įterptųjų sistemų OS – jums reikia standartinės plius bibliotekos ir standartinės STL šablonų bibliotekos (vektoriaus ir kt.)

Perkėlimas į „Embox“.

Kaip įprasta, prieš perkeliant bet kokias programas į operacinę sistemą, pravartu pabandyti sukurti tokią formą, kokia ją numatė kūrėjai. Mūsų atveju su tuo problemų nėra – šaltinio kodą galima rasti adresu github, biblioteka sukurta naudojant GNU/Linux, naudojant įprastą cmake.

Geros naujienos yra tai, kad OpenCV gali būti sukurta kaip statinė biblioteka, kuri palengvina perkėlimą. Renkame biblioteką su standartine konfigūracija ir žiūrime, kiek vietos jie užima. Kiekvienas modulis yra surinktas atskiroje bibliotekoje.

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

Kaip matote iš paskutinės eilutės, .bss ir .data neužima daug vietos, tačiau kodas yra didesnis nei 70 MiB. Akivaizdu, kad jei tai bus statiškai susieta su konkrečia programa, kodas sumažės.

Pabandykime išmesti kuo daugiau modulių, kad būtų surinktas minimalus pavyzdys (kuris, pavyzdžiui, tiesiog išves OpenCV versiją), todėl žiūrime cmake .. -LA ir išjunkite parinktyse viską, kas išsijungia.

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

Viena vertus, tai tik vienas bibliotekos modulis, kita vertus, tai be kompiliatoriaus optimizavimo pagal kodo dydį (-Os). ~3 MiB kodo dar gana daug, bet jau suteikia sėkmės vilčių.

Paleiskite emuliatorių

Daug lengviau derinti emuliatoriuje, todėl pirmiausia įsitikinkite, kad biblioteka veikia su qemu. Kaip emuliuotą platformą pasirinkau Integrator / CP, nes pirma, tai taip pat yra ARM, antra, „Embox“ palaiko šios platformos grafikos išvestį.

„Embox“ turi išorinių bibliotekų kūrimo mechanizmą, naudodami jį kaip modulį pridedame OpenCV (perduodame visas tas pačias „minimalios“ kūrimo parinktis statinių bibliotekų pavidalu), po to pridedu paprastą programą, kuri atrodo taip:

version.cpp:

#include <stdio.h>
#include <opencv2/core/utility.hpp>

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

    return 0;
}

Surenkame sistemą, paleidžiame – gauname laukiamą produkciją.

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 включены в сборку и т.п.>

Kitas žingsnis – paleisti kokį nors pavyzdį, pageidautina vieną iš standartinių, kuriuos siūlo patys kūrėjai. jūsų svetainėje. aš pasirinkau šaunus sienų detektorius.

Pavyzdį reikėjo šiek tiek perrašyti, kad vaizdas su rezultatu būtų rodomas tiesiai kadro buferyje. Aš turėjau tai padaryti, nes. funkcija imshow() gali piešti vaizdus per QT, GTK ir Windows sąsajas, kurių, žinoma, STM32 konfigūracijoje tikrai nebus. Tiesą sakant, QT taip pat gali būti paleistas naudojant STM32F7Discovery, tačiau tai bus aptarta kitame straipsnyje 🙂

Po trumpo patikslinimo, kokiu formatu saugomas krašto detektoriaus rezultatas, gauname vaizdą.

OpenCV STM32F7-Discovery

originalus paveikslas

OpenCV STM32F7-Discovery

Rezultatas

Veikia naudojant STM32F7Discovery

32F746GDISCOVERY yra keletas aparatinės įrangos atminties skyrių, kuriuos galime naudoti vienaip ar kitaip

  1. 320 KB RAM
  2. 1 MB blykstė vaizdui
  3. 8 MB SDRAM
  4. 16 MiB QSPI NAND blykstė
  5. microSD kortelės lizdas

SD kortelė gali būti naudojama vaizdams saugoti, tačiau minimalaus pavyzdžio kontekste tai nėra labai naudinga.
Ekrano skiriamoji geba yra 480×272, o tai reiškia, kad kadrų buferio atmintis bus 522 240 baitų 32 bitų gylyje, t.y. tai yra daugiau nei RAM dydis, todėl kadrų buferis ir krūva (kurios reikės, įskaitant OpenCV, vaizdų ir pagalbinių struktūrų duomenims saugoti) bus SDRAM, visa kita (atmintis stekams ir kitiems sistemos poreikiams ) pateks į RAM.

Jei paimsime minimalią STM32F7Discovery konfigūraciją (išmeskime visą tinklą, visas komandas, padarysime kuo mažesnius krūvelius ir pan.) ir pridėsime ten OpenCV su pavyzdžiais, reikiama atmintis bus tokia:

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

Tiems, kurie nelabai žino, kurios skiltys kur eina, paaiškinsiu: į .text и .rodata instrukcijos ir konstantos (grubiai tariant, tik skaitomi duomenys). .data duomenys yra keičiami, .bss yra „nuliudyti“ kintamieji, kuriems vis dėlto reikia vietos (ši dalis „pateks“ į RAM).

Gera žinia ta .data/.bss turėtų tikti, bet su .text bėda ta, kad vaizdui yra tik 1MiB atminties. Galima išmesti .text paveikslėlį iš pavyzdžio ir perskaitykite jį, pavyzdžiui, iš SD kortelės į atmintį paleidžiant, bet fruits.png sveria apie 330KiB, todėl problemos tai neišspręs: dauguma .text susideda iš OpenCV kodo.

Apskritai, belieka tik vienas dalykas - įkelti dalį kodo į QSPI blykstę (ji turi specialų veikimo režimą atminties susiejimui su sistemos magistrale, kad procesorius galėtų tiesiogiai pasiekti šiuos duomenis). Tokiu atveju iškyla problema: pirma, QSPI „flash drive“ atmintis nepasiekiama iš karto po įrenginio paleidimo iš naujo (reikia atskirai inicijuoti atminties susiejimo režimą), ir, antra, negalite „flash“ šios atminties pažįstama įkrovos programa.

Dėl to buvo nuspręsta susieti visą kodą QSPI ir paleisti jį su savaime parašytu įkrovikliu, kuris per TFTP gaus reikiamą dvejetainį failą.

Rezultatas

Mintis perkelti šią biblioteką į „Embox“ kilo maždaug prieš metus, tačiau vis dėl įvairių priežasčių ji buvo atidėta. Vienas iš jų yra libstdc++ ir standartinės šablonų bibliotekos palaikymas. C++ palaikymo problema Embox nepatenka į šio straipsnio taikymo sritį, todėl čia pasakysiu tik tiek, kad mums pavyko pasiekti reikiamą palaikymą, kad ši biblioteka veiktų 🙂

Galiausiai šios problemos buvo įveiktos (bent jau pakankamai, kad OpenCV pavyzdys veiktų), ir pavyzdys paleistas. Užtrunka 40 ilgų sekundžių, kol lenta ieško ribų naudojant Canny filtrą. Tai, žinoma, per ilga (yra svarstymų, kaip optimizuoti šį reikalą, sėkmės atveju apie tai bus galima parašyti atskirą straipsnį).

OpenCV STM32F7-Discovery

Tačiau tarpinis tikslas buvo sukurti prototipą, kuris parodytų esminę galimybę paleisti OpenCV STM32, šis tikslas buvo pasiektas, urr!

tl;dr: žingsnis po žingsnio instrukcijos

0: Atsisiųskite „Embox“ šaltinius, pavyzdžiui:

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

1: Pradėkime nuo įkrovos įkroviklio surinkimo, kuris „užves“ QSPI „flash drive“.

    make confload-arm/stm32f7cube

Dabar reikia sukonfigūruoti tinklą, nes. Įkelsime vaizdą per TFTP. Norėdami nustatyti plokštės ir pagrindinio kompiuterio IP adresus, turite redaguoti conf/rootfs/network.

Konfigūracijos pavyzdys:

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 - pagrindinio kompiuterio adresas, iš kurio bus įkeltas vaizdas, address - valdybos adresas.

Po to renkame įkrovos įkroviklį:

    make

2: Įprastas įkrovos įkėlimo programos įkėlimas (atsiprašau už kalambūrą) lentoje - čia nėra nieko konkretaus, turite tai padaryti kaip ir bet kurią kitą STM32F7Discovery programą. Jei nežinote, kaip tai padaryti, galite paskaityti apie tai čia.
3: Vaizdo kompiliavimas su OpenCV konfigūracija.

    make confload-platform/opencv/stm32f7discovery
    make

4: Ištrauka iš ELF sekcijų, kuri turi būti įrašyta į QSPI į 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

Conf kataloge yra scenarijus, kuris tai atlieka, todėl galite jį paleisti

    ./conf/qspi_objcopy.sh # Нужный бинарник -- build/base/bin/qspi.bin

5: Naudodami tftp, atsisiųskite qspi.bin.bin į QSPI atmintinę. Norėdami tai padaryti, pagrindiniame kompiuteryje nukopijuokite qspi.bin į tftp serverio šakninį aplanką (paprastai /srv/tftp/ arba /var/lib/tftpboot/; atitinkamo serverio paketai yra prieinami daugelyje populiariausių paskirstymų, paprastai vadinamų tftpd arba tftp-hpa, kartais jūs turite tai padaryti systemctl start tftpd.service pradėti).

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

„Embox“ (t. y. įkrovos įkrovikoje) turite vykdyti šią komandą (manome, kad serverio adresas yra 192.168.2.1):

    embox> qspi_loader qspi.bin 192.168.2.1

6: Su komanda goto reikia „peršokti“ į QSPI atmintį. Konkreti vieta skirsis priklausomai nuo to, kaip vaizdas susietas, šį adresą galite pamatyti naudodami komandą mem 0x90000000 (pradžios adresas telpa antrame 32 bitų vaizdo žodyje); taip pat reikės pažymėti kaminą -s, kamino adresas yra 0x90000000, pavyzdys:

    embox>mem 0x90000000
    0x90000000:     0x20023200  0x9000c27f  0x9000c275  0x9000c275
                      ↑           ↑
              это адрес    это  адрес 
                стэка        первой
                           инструкции

    embox>goto -i 0x9000c27f -s 0x20023200 # Флаг -i нужен чтобы запретить прерывания во время инициализации системы

    < Начиная отсюда будет вывод не загрузчика, а образа с OpenCV >

7: paleisti

    embox> edges 20

ir mėgaukitės 40 sekundžių pasienio paieška 🙂

Jei kas nors negerai - parašykite problemą mūsų saugykla, arba į adresų sąrašą [apsaugotas el. paštu], arba komentare čia.

Šaltinis: www.habr.com

Добавить комментарий