OpenCV në STM32F7-Discovery

OpenCV në STM32F7-Discovery Unë jam një nga zhvilluesit e sistemit operativ Embox, dhe në këtë artikull do të ndaj me ju mënyrën se si e kam realizuar hapjen e OpenCV në pllakën STM32746G.

Nëse shkruani në motorin e kërkimit diçka si «OpenCV on STM32 board», mund të gjeni shumë njerëz që janë të interesuar në përdorimin e kësaj biblioteke në pllaka STM32 ose mikro-kontrollerë të tjerë.
Ekzistojnë disa video që, sipas emrit, duhet të demonstrojnë atë që është e nevojshme, por zakonisht (në të gjitha videot që kam parë) në pllakën STM32 është realizuar vetëm marrja e figurave nga kamera dhe shfaqja e rezultatit në ekran, ndërsa procesimi i imazhit është bërë ose në një kompjuter të zakonshëm, ose në pllaka më të fuqishme (siç është Raspberry Pi).

Pse është kjo e komplikuar?

Popullariteti i kërkesave në kërkimin shpjegohet me faktin se OpenCV është biblioteka më e njohur për vizionin kompjuterik, që do të thotë se shumë zhvillues e njohin atë, dhe gjithashtu mundësia për të ekzekutuar kodin e gatshëm për desktop në mikro-kontroller e thjeshton ndjeshëm procesin e zhvillimit. Por pse ende nuk ka receta të njohura të gatshme për zgjidhjen e kësaj probleme?

Problemi i përdorimit të OpenCV në pllaka të vogla lidhet me dy karakteristika:

  • Nëse e kompilon bibliotekën edhe me një grup minimal modulash, ajo nuk do të hyjë në memorinë flash të STM32F7Discovery (madje pa marrë parasysh sistemin operativ) për shkak të kodit shumë të madh (disa megabajt instruksionesh).
  • Biblioteka vetë është shkruar në C++, pra
    • Kërkohet mbështetje për runtime-in e C++ (përjashtime dhe të tjera).
    • Ka pak mbështetje për LibC/Posix, të cilat zakonisht janë të pranishme në sistemet operative për sisteme të embedded — është e nevojshme një bibliotekë standarde e C++ dhe biblioteka standarde e templating STL (vector etj.).

Portimi në Embox

Si zakonisht, para se të bëhet portimi i ndonjë programi në një sistem operative, është mirë ta provosh atë të ndërtosh në atë formë siç e kishin menduar zhvilluesit. Në rastin tonë, nuk ka probleme me këtë — burimet mund të gjenden në github, biblioteka ndërtohet nën GNU/Linux me cmake të zakonshëm.

Nga lajmet e mira — OpenCV mund të ndërtohet si një bibliotekë statike nga kutia, që e bën portimin më të lehtë. Ndërtojmë bibliotekën me konfigurimin standard dhe shohim sa hapësirë zë ajo. Çdo modul ndërtohet si 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 të shihet nga rreshti i fundit, .bss dhe .data nuk zënë shumë vend, përkundrazi kodi është mbi 70 MiB. E qartë se nëse e çojmë këtë në lidhje statike me një aplikacion të caktuar, kodi do të bëhet më i vogël.

Do të përpiqemi të heqim sa më shumë module, në mënyrë që të ndërtojmë një shembull minimal (i cili, për shembull, thjesht do të tregojë versionin e OpenCV), kështu që le të shohim. cmake .. -LA dhe së pari çaktivizojmë në opsionet gjithçka që mund të çaktivizohet.

        -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ë modull i bibliotekës, nga ana tjetër, ky është pa optimizim të kompilatorit për madhësinë e kodit (-Os). ~3 MiB kod – kjo ende është një sasi e konsiderueshme, por tashmë jep shpresë për sukses.

Ekzekutimi në emulator

Të debuguar në emulator është shumë më e lehtë, prandaj së pari të sigurohemi që biblioteka punon në qemu. Si platformë e emuluar kam zgjedhur Integrator/CP, sepse, përveç kësaj, është gjithashtu ARM, dhe për më tepër, Embox mbështet daljen grafike për këtë platformë.

Në Embox ka një mekanizëm për ndërtimin e bibliotekave të jashtme, me të ndihmojmë për të shtuar OpenCV si modull (duke kaluar të gjitha ato mundësi për 'ndërtimin minimal' në formën e bibliotekave statike), pas kësaj shtojmë një aplikacion të thjeshtë që duket kështu:

version.cpp:

#include 
#include 

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

    return 0;
}

Ndërtojmë sistemin, e ekzekutojmë – marrim rezultatin e pritur.

root@embox:/#opencv_version                                                     
OpenCV: 
Konfigurimi i përgjithshëm për OpenCV 4.0.1 =====================================
  Kontrolli i versionit:               bd6927bdf-dirty

  Platforma:
    Koha:                   2019-06-21T10:02:18Z
    Kompjuteri:                        Linux 5.1.7-arch1-1-ARCH x86_64
    Target:                      Generic arm-unknown-none
    CMake:                       3.14.5
    Generatori CMake:             Unix Makefiles
    Mjeti i ndërtimit CMake:            /usr/bin/make
    Konfigurimi:               Debug

  Karakteristika CPU/HW:
    Bazë:
      e kërkuar:                 DETECT
      e çaktivizuar:                  VFPV3 NEON

  C/C++:
    Ndërtuar si libra dinamik?:      JO

Hapi tjetër është të ekzekutoni një shembull, më mirë ndonjë standard nga ata që ofrohen nga vetë zhvilluesit në faqen e tyre. Unë zgjodha detektorin e kufijve Kënni.

Shembulli duhet të ri-shkruhej pak, për të shfaqur imazhin me rezultatin drejtpërdrejt në framebuffer. Këtë duhej ta bëja, pasi funksioni imshow() di të vizatojë imazhe përmes ndërfaqeve QT, GTK dhe Windows, të cilat, sigurisht, nuk do të jenë në konfigurimin për STM32. Në të vërtetë, QT gjithashtu mund të iniciohet në STM32F7Discovery, por për këtë do të flitet në një artikull tjetër 🙂

Pas një verifikim të shkurtër të formatit të ruajtjes së rezultatit të detektorit të skajit, ne marrim imazhin.

OpenCV në STM32F7-Discovery

Imazhi origjinal

OpenCV në STM32F7-Discovery

Rezultati

Një përfillje në STM32F7Discovery

Në 32F746GDISCOVERY ka disa segmente memorjeje që mund t'i përdorim në një farë mënyre.

  1. 320KiB memorie operative
  2. 1MiB memorie flash për imazhin
  3. 8MiB SDRAM
  4. 16MiB QSPI NAND flash
  5. Priza për kartën microSD

SD karta mund të përdoret për ruajtjen e imazheve, por në kontekstin e ekzekutimit të shembujve minimalë, kjo nuk është shumë e dobishme.
Ekrani ka një rezolutë prej 480×272, që do të thotë se memoria për frame buffer do të jetë 522,240 byte me thellësi 32 bit, që është më shumë se madhësia e memorie operative, kështu që frame buffer dhe heap (të cilat do të nevojiten po ashtu për OpenCV për të mbajtur të dhëna për imazhet dhe strukturat ndihmëse) do të vendosen në SDRAM, ndërsa gjithçka tjetër (memoria për stekët dhe nevojat e tjera sistemike) do të dërgohet në RAM.

Nëse marrim konfigurimin minimal për STM32F7Discovery (heqim të gjithë rrjetin, të gjitha komandat, e bëjmë stekët sa më të vegjël dhe të tjera) dhe të shtojmë OpenCV me shembuj, atëherë kërkesat për memorie do të jenë si më poshtë:

   teksti    të dhëna     bss     dec     hex emri i skedarit
2876890  459208  312736 3648834  37ad42 build/base/bin/embox

Për ata që nuk janë shumë të njohur me se cilat seksione i përkasin, le të shpjegoj: në .text dhe .rodata janë instruksionet dhe konstantet (në terma të thjeshta, të dhëna readonly), në .data janë të dhënat e ndryshueshme, në .bss janë variablat "të zeruara", të cilat, megjithatë, kërkojnë hapësirë (kjo seksion "do të dërgohet" në RAM).

Lajmi i mirë është se .data/.bss duhen vendosur, por këtu është .text problemi — për imazhin ka vetëm 1MiB të memories. Mund të hiqni imazhin nga shembulli dhe ta lexoni, për shembull, nga një SD kartë në memory gjatë nisjes, por fruits.png peshon rreth 330KiB, kështu që kjo nuk zgjidh problemin: pjesa më e madhe .text merrni imazhin nga shembulli dhe lexoni atë, për shembull, nga një kartë SD në kujtesë gjatë nisjes, por fruits.png peshon rreth 330KiB, kështu që kjo nuk do ta zgjidhë problemin: pjesa më e madhe .text është përbërë pikërisht nga kodi OpenCV.

Në thelb, ka mbetur vetëm një gjë — ngarkimi i një pjese të kodit në QSPI-flash (ajo ka një mod special për operimin e hartimit të memories në sistem, kështu që procesori mund t'i qaset këtyre të dhënave drejtpërdrejt). Megjithatë, ka një problem: së pari, memoria e QSPI-flash nuk është e qasshme menjëherë pas riparimit të pajisjes (duhet të inicializoni veçmas modin e memory-mapped), së dyti, nuk është e mundur të "programoni" këtë memory me një ngarkues të zakonshëm.

Përfundimisht, u vendos që të lidhim të gjithë kodin në QSPI dhe ta ngarkohet me një bootloader të shkruar vetë, i cili do të marrë binarin e nevojshëm përmes TFTP.

Rezultati

Idea për të portuar këtë bibliotekë në Embox ka lindur rreth një vit më parë, por vazhdimisht është shtyrë për shkak të arsyeve të ndryshme. Njëra prej tyre është mbështetje për libstdc++ dhe bibliotekën standarde të template. Problemi i mbështetjes së C++ në Embox kalon jashtë kësaj artikulli, prandaj këtu do të thosha vetëm se arritëm të sigurojmë këtë mbështetje në përmasat e nevojshme për funksionimin e kësaj biblioteke 🙂

Përfundimisht, këto probleme u përballën (të paktën, në masën e nevojshme për funksionimin e shembullit OpenCV), dhe shembulli filloi. 40 sekonda të gjatë i duhen bordit për të gjetur kufijtë me filtrin Canny. Kjo, sigurisht, është shumë e gjatë (ka disa propozime se si ta optimizojmë këtë, për të cilat mund të shkruajmë një artikull të veçantë në rast se kemi sukses).

OpenCV në STM32F7-Discovery

Megjithatë, synimi ndërmjetës ishte krijimi i një prototipi që do të tregonte mundësinë themelore të aktivizimit të OpenCV në STM32, kështu që ky synim u arrit, urime!

tl;dr: udhëzime hap pas hapi

0: Shkarkoni burimet e Embox, për shembull kështu:

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

1: Le të fillojmë me ndërtimin e bootloader-it që "do të ngarkojë" flash-in QSPI.

    make confload-arm/stm32f7cube

Tani, duhet të konfiguroni rrjetin, sepse do të ngarkojmë imazhin përmes TFTP. Për të caktuar adresat IP të bordit dhe hostit, duhet të ndryshoni skedarin conf/rootfs/network.

Shembulli i konfiguracionit:

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 e cila do të ngarkohet imazhi, adresë — adresa e bordit.

Pas kësaj, ne do të rindërtojmë bootloader-in:

    make

2: Ngarkimi normal i bootloader-it (falni për lojën me fjalë) në bord — këtu nuk ka asgjë specifike, duhet ta bëni si për çdo aplikacion tjetër për STM32F7Discovery. Nëse nuk e dini si ta bëni këtë, mund të lexoni rreth saj këtu.
3: Kompilimi i imazhit me konfigurimin për OpenCV.

    make confload-platform/opencv/stm32f7discovery
    make

4: Nxjerrja e sekzioneve nga ELF që duhet të shkruhen 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

Në direktoriumin conf është skripti që e bën këtë, kështu që mund ta nisni atë

    ./conf/qspi_objcopy.sh # Binaries i nevojshëm -- build/base/bin/qspi.bin

5: Me ndihmën e tftp ngarkojmë qspi.bin.bin në flash-in QSPI. Në host për këtë, ne duhet të kopjojmë qspi.bin në folderin rrënjë të serverit tftp (zakonisht është /srv/tftp/ ose /var/lib/tftpboot/; paketat për serverin përkatës janë në shumicën e distribucioneve të njohura, zakonisht quhet tftpd ose tftp-hpa, ndonjëherë duhet bërë systemctl start tftpd.service për të filluar).

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

Në Embox (dmth. në bootloader) duhet të ekzekutohet një komandë të tillë (të supozojmë që adresa e serverit është 192.168.2.1):

    embox> qspi_loader qspi.bin 192.168.2.1

6: Me ndihmën e komandës goto duhet të "këndohet" në memorien QSPI. Lokacioni konkret do të ndryshojë në varësi të mënyrës si lidhet imazhi, ky adresë mund të shikohet me komandën mem 0x90000000 (adresa e nisjes vendoset në fjalinë e dytë 32-bit të imazhit); gjithashtu do të kërkohet që të vendosim stek-un me flagun -s, adresa e stek-ut është në adresën 0x90000000, shembuj:

    embox>mem 0x90000000
    0x90000000:     0x20023200  0x9000c27f  0x9000c275  0x9000c275
                      ↑           ↑
              kjo është adresa    kjo është  adresa 
                e stek-ut        e parë
                           instrukcioneve

    embox>goto -i 0x9000c27f -s 0x20023200 # Flag-u -i është i nevojshë për të ndaluar ndërprerjet gjatë inicimit të sistemit

7: Po fillojmë

    embox> skaj 20

dhe shijojmë një kërkim 40-sekondësh për kufij 🙂

Nëse diçka nuk shkon siç duhet — shkruani një issue në depon tonë, ose në listën embox-devel@googlegroups.com, ose në komentet këtu.

Burimi: habr.com

Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS 🔥 Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS | ProHoster