OpenCV në STM32F7-Discovery

OpenCV në STM32F7-Discovery Unë jam një nga zhvilluesit e sistemit operativ Embox, dhe në këtë artikull do të flas se si arrita të ekzekutoj OpenCV në bordin STM32746G.

Nëse shkruani diçka si "OpenCV në tabelën STM32" në një motor kërkimi, mund të gjeni mjaft njerëz që janë të interesuar të përdorin këtë bibliotekë në bordet STM32 ose mikrokontrollues të tjerë.
Ka disa video që, duke gjykuar nga emri, duhet të demonstrojnë atë që nevojitet, por zakonisht (në të gjitha videot që pashë) në tabelën STM32, vetëm imazhi u mor nga kamera dhe rezultati shfaqej në ekran, dhe vetë përpunimi i imazhit është bërë ose në një kompjuter të rregullt, ose në borde më të fuqishme (për shembull, Raspberry Pi).

Pse është e vështirë?

Popullariteti i pyetjeve të kërkimit shpjegohet me faktin se OpenCV është biblioteka më e njohur e vizionit kompjuterik, që do të thotë se më shumë zhvillues janë të njohur me të dhe aftësia për të ekzekutuar kodin gati për desktop në një mikrokontrollues thjeshton shumë procesin e zhvillimit. Por pse nuk ka ende receta popullore të gatshme për zgjidhjen e këtij problemi?

Problemi i përdorimit të OpenCV në shalle të vogla lidhet me dy veçori:

  • Nëse e përpiloni bibliotekën edhe me një grup minimal modulesh, ajo thjesht nuk do të futet në memorien flash të të njëjtit STM32F7Discovery (edhe pa marrë parasysh sistemin operativ) për shkak të një kodi shumë të madh (disa megabajt udhëzime)
  • Vetë biblioteka është e shkruar në C++, që do të thotë
    • Keni nevojë për mbështetje për kohëzgjatjen pozitive (përjashtimet, etj.)
    • Mbështetje e vogël për LibC/Posix, e cila zakonisht gjendet në OS për sistemet e integruara - keni nevojë për një bibliotekë standarde plus dhe një bibliotekë standarde të shablloneve STL (vektor, etj.)

Transferimi në Embox

Si zakonisht, përpara se të transferoni ndonjë program në sistemin operativ, është mirë të përpiqeni ta ndërtoni atë në formën në të cilën zhvilluesit e synuan. Në rastin tonë, nuk ka probleme me këtë - kodi burim mund të gjendet në github, biblioteka është ndërtuar nën GNU/Linux me cmake-në e zakonshme.

Lajmi i mirë është se OpenCV mund të ndërtohet si një bibliotekë statike jashtë kutisë, gjë që e bën më të lehtë transferimin. Ne mbledhim një bibliotekë me një konfigurim standard dhe shohim se sa hapësirë ​​zënë. Çdo modul është mbledhur në 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 ta shihni nga rreshti i fundit, .bss dhe .data nuk zënë shumë hapësirë, por kodi është më shumë se 70 MiB. Është e qartë se nëse kjo është e lidhur statikisht me një aplikacion specifik, kodi do të bëhet më i vogël.

Le të përpiqemi të hedhim sa më shumë module që të jetë e mundur në mënyrë që të mblidhet një shembull minimal (i cili, për shembull, thjesht do të nxjerrë versionin OpenCV), kështu që ne shikojmë cmake .. -LA dhe fikni në opsionet gjithçka që fiket.

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

Ekzekutoni në emulator

Është shumë më e lehtë për të korrigjuar gabimet në emulator, kështu që së pari sigurohuni që biblioteka të funksionojë në qemu. Si një platformë emuluar, zgjodha Integrator / CP, sepse së pari, është gjithashtu ARM, dhe së dyti, Embox mbështet daljen grafike për këtë platformë.

Embox ka një mekanizëm për ndërtimin e bibliotekave të jashtme, duke e përdorur atë shtojmë OpenCV si modul (duke kaluar të gjitha opsionet e njëjta për ndërtimin "minimal" në formën e bibliotekave statike), pas kësaj shtoj një aplikacion të thjeshtë që duket kështu:

version.cpp:

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

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

    return 0;
}

Ne mbledhim sistemin, e drejtojmë atë - marrim rezultatin e pritur.

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

Hapi tjetër është të ekzekutoni disa shembuj, mundësisht një nga ato standarde të ofruara nga vetë zhvilluesit. në faqen tuaj. une zgjodha detektor kufitar i zgjuar.

Shembulli duhej të rishkruhej pak për të shfaqur imazhin me rezultatin direkt në buferin e kornizës. Më duhej ta bëja këtë, sepse. funksionin imshow() mund të vizatojë imazhe përmes ndërfaqeve QT, GTK dhe Windows, të cilat, natyrisht, definitivisht nuk do të jenë në konfigurimin për STM32. Në fakt, QT mund të ekzekutohet edhe në STM32F7Discovery, por kjo do të diskutohet në një artikull tjetër 🙂

Pas një sqarimi të shkurtër në cilin format ruhet rezultati i detektorit të skajit, marrim një imazh.

OpenCV në STM32F7-Discovery

foto origjinale

OpenCV në STM32F7-Discovery

Result

Punon në STM32F7Discovery

Në 32F746GDISCOVERY ka disa seksione të memories harduerike që mund t'i përdorim në një mënyrë ose në një tjetër

  1. RAM 320 KiB
  2. Blic 1 MiB për imazhin
  3. 8 MiB SDRAM
  4. Flash 16 MiB QSPI NAND
  5. fole për kartën microSD

Një kartë SD mund të përdoret për të ruajtur imazhet, por në kontekstin e ekzekutimit të një shembulli minimal, kjo nuk është shumë e dobishme.
Ekrani ka një rezolucion prej 480×272, që do të thotë se memoria framebuffer do të jetë 522 byte në një thellësi prej 240 bit, d.m.th. kjo është më shumë se madhësia e RAM-it, kështu që framebuffer dhe grumbulli (i cili do të kërkohet, duke përfshirë OpenCV, për të ruajtur të dhënat për imazhet dhe strukturat ndihmëse) do të vendosen në SDRAM, gjithçka tjetër (memoria për rafte dhe nevoja të tjera të sistemit ) do të shkojë në RAM.

Nëse marrim konfigurimin minimal për STM32F7Discovery (heqim të gjithë rrjetin, të gjitha komandat, bëjmë steka sa më të vogla, etj.) dhe shtojmë OpenCV me shembuj atje, memoria e kërkuar do të jetë si më poshtë:

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

Për ata që nuk janë shumë të njohur se cilat seksione shkojnë, unë do t'ju shpjegoj: në .text и .rodata udhëzimet dhe konstantet (përafërsisht, të dhëna vetëm për lexim) qëndrojnë brenda .data të dhënat janë të ndryshueshme, .bss ka variabla "të pavlefshëm", të cilët, megjithatë, kanë nevojë për një vend (ky seksion do të "shkojë" në RAM).

Lajmi i mirë është se .data/.bss duhet të përshtatet, por me .text Problemi është se ka vetëm 1 MiB memorie për imazhin. Mund të hidhet jashtë .text foton nga shembulli dhe lexoni atë, për shembull, nga karta SD në memorie në fillim, por fruits.png peshon rreth 330 KiB, kështu që kjo nuk do ta zgjidhë problemin: shumica .text përbëhet nga kodi OpenCV.

Në përgjithësi, ka mbetur vetëm një gjë - ngarkimi i një pjese të kodit në një blic QSPI (ai ka një mënyrë të veçantë funksionimi për hartimin e kujtesës në autobusin e sistemit, në mënyrë që procesori të mund t'i qaset drejtpërdrejt këtyre të dhënave). Në këtë rast, lind një problem: së pari, kujtesa e një flash drive QSPI nuk është e disponueshme menjëherë pasi pajisja të rindizet (duhet të inicializoni veçmas modalitetin e hartës së kujtesës), dhe së dyti, nuk mund ta "flash" këtë memorie me një bootloader i njohur.

Si rezultat, u vendos që të lidhej i gjithë kodi në QSPI dhe ta ndezë atë me një ngarkues të vetë-shkruar që do të marrë binarin e kërkuar përmes TFTP.

Result

Ideja për ta transferuar këtë bibliotekë në Embox u shfaq rreth një vit më parë, por pa pushim u shty për arsye të ndryshme. Një prej tyre është mbështetja për libstdc++ dhe bibliotekën standarde të shablloneve. Problemi i mbështetjes së C++ në Embox është përtej qëllimit të këtij artikulli, kështu që këtu do të them vetëm se ne arritëm ta arrijmë këtë mbështetje në sasinë e duhur që kjo bibliotekë të funksionojë 🙂

Në fund, këto probleme u tejkaluan (të paktën mjafton që shembulli OpenCV të funksionojë) dhe shembulli u zhvillua. Duhen 40 sekonda të gjata që bordi të kërkojë për kufijtë duke përdorur filtrin Canny. Kjo, natyrisht, është shumë e gjatë (ka konsiderata se si të optimizohet kjo çështje, do të jetë e mundur të shkruhet një artikull i veçantë për këtë në rast suksesi).

OpenCV në STM32F7-Discovery

Sidoqoftë, qëllimi i ndërmjetëm ishte krijimi i një prototipi që do të tregojë mundësinë themelore të ekzekutimit të OpenCV në STM32, respektivisht, ky qëllim u arrit, urrë!

tl;dr: udhëzime hap pas hapi

0: Shkarkoni burimet e Embox, si kjo:

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

1: Le të fillojmë duke montuar një bootloader që do të "flash" një flash drive QSPI.

    make confload-arm/stm32f7cube

Tani ju duhet të konfiguroni rrjetin, sepse. Ne do të ngarkojmë imazhin përmes TFTP. Për të vendosur adresat IP të bordit dhe të hostit, duhet të modifikoni conf/rootfs/rrjetin.

Shembull i konfigurimit:

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 ku do të ngarkohet imazhi, address - adresa e bordit.

Pas kësaj, ne mbledhim ngarkuesin:

    make

2: Ngarkimi i zakonshëm i ngarkuesit (më falni për lojën e fjalës) në tabelë - nuk ka asgjë specifike këtu, duhet ta bëni atë si për çdo aplikacion tjetër për STM32F7Discovery. Nëse nuk dini si ta bëni, mund të lexoni për të këtu.
3: Përpilimi i një imazhi me një konfigurim për OpenCV.

    make confload-platform/opencv/stm32f7discovery
    make

4: Ekstrakt nga seksionet ELF për t'u shkruar 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

Ekziston një skript në direktorinë conf që e bën këtë, kështu që ju mund ta ekzekutoni atë

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

5: Duke përdorur tftp, shkarkoni qspi.bin.bin në një flash drive QSPI. Në host, për ta bërë këtë, kopjoni qspi.bin në dosjen rrënjë të serverit tftp (zakonisht /srv/tftp/ ose /var/lib/tftpboot/; paketat për serverin përkatës janë të disponueshme në shpërndarjet më të njohura, zakonisht të quajtura tftpd ose tftp-hpa, ndonjëherë duhet të bëni systemctl start tftpd.service të fillosh).

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

Në Embox (d.m.th. në ngarkuesin e ngarkimit), duhet të ekzekutoni komandën e mëposhtme (supozojmë se serveri ka adresën 192.168.2.1):

    embox> qspi_loader qspi.bin 192.168.2.1

6: Me komandë goto ju duhet të "kërceni" në memorien QSPI. Vendndodhja specifike do të ndryshojë në varësi të mënyrës se si është lidhur imazhi, ju mund ta shihni këtë adresë me komandën mem 0x90000000 (adresa e fillimit përshtatet në fjalën e dytë 32-bit të figurës); do t'ju duhet gjithashtu të shënoni pirgun -s, adresa e stivës është në 0x90000000, shembull:

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

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

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

7: Nisja

    embox> edges 20

dhe shijoni kërkimin e kufirit prej 40 sekondash 🙂

Nëse diçka shkon keq - shkruani një problem në depoja jonë, ose në listën e postimeve [email mbrojtur], ose në një koment këtu.

Burimi: www.habr.com

Shto një koment