OpenCV në STM32F7-Discovery

OpenCV në STM32F7-Discovery Unë jam një nga zhvilluesit e sistemit të operimit Embox, dhe në këtë artikull do të flas për mënyrën se si arrita të aktivizoj OpenCV në pllakën STM32746G.

Nëse shkruani në motorin e kërkimit diçka si 'OpenCV on STM32 board', mund të gjeni mjaft njerëz që janë të interesuar për përdorimin e kësaj biblioteke në pllakat STM32 ose në mikrokontrollera të tjerë.
Ekzistojnë disa video që, sipas titullit, duhet të demonstrojnë atë që duhet, por zakonisht (në të gjitha videoto që kam parë) në pllakën STM32 është kryer vetëm marrja e pamjes nga Kamera dhe shfaqja e rezultatit në ekran, ndërsa vetë përpunimi i imazhit bëhej ose në një kompjuter të zakonshëm, ose në pllaka më të fuqishme (p.sh., Raspberry Pi).

Pse është e vështirë?

Popullariteti i kërkimeve shpjegohet me faktin se OpenCV është biblioteka më popullore e vizionit kompjuterik, dhe kjo do të thotë se shumë më shumë zhvillues janë të njohur me të, dhe mundësia për të aktivizuar kodin e gatshëm për desktop në mikrokontroller e thjeshton ndjeshëm procesin e zhvillimit. Por pse ende nuk ka ndonjë recetë të njohur të gatshme për zgjidhjen e kësaj problemi?

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

  • Nëse e kompiloni bibliotekën madje edhe me një grup minimal module, në memorien flash të po asaj STM32F7Discovery ajo thjesht nuk do të hyjë (madje pa marrë parasysh OS-në) për shkak të kodit shumë të madh (disa megabajt instrukcionesh)
  • Biblioteka vetë është shkruar në C++, dhe kjo do të thotë
    • Duhet mbështetje për runtime-in e C++ (përjashtime etj.)
    • Ka pak mbështetje LibC/Posix, të cilat zakonisht janë në OS-të për sisteme të integruara — nevojitet biblioteka standarde e C++ dhe biblioteka standarde e templatit STL (vektori etj.)

Portimi në Embox

Si zakonisht, para se të portoni ndonjë program në një sistem operativ, nuk do të ishte keq të provoni ta ndërtoni atë në formën që e kanë menduar zhvilluesit. Në rastin tonë nuk ka probleme me këtë — burimet mund të gjenden në GitHub, biblioteka ndërtrohet nën GNU/Linux me cmake të zakonshëm.

Nga lajmet e mira — OpenCV nga kutia mund të ndërtohet në formën e një biblioteke statike, gjë që e bën portimin më të lehtë. Ndërtojmë bibliotekën me konfigurimin standard dhe shikojmë sa hapësirë po zë. Çdo modul ndërtohet 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ç duket nga rreshti i fundit, .bss dhe .data nuk zënë aq shumë hapësirë, por ka mbi 70 MiB kod. E qartë është se nëse kjo lidhet statikisht me një aplikacion të caktuar, kodi do të zvogëlohet.

Le të përpiqemi të heqim sa më shumë module që të jetë e mundur, në mënyrë që të krijojmë një shembull minimal (i cili, për shembull, thjesht do të tregojë versionin e OpenCV), kështu që po shohim cmake .. -LA dhe fikim në opsione gjithçka që mund të 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 optimizimin e kompilerit për madhësinë e kodit (-Os). ~3 MiB kod — kjo është ende e konsiderueshme, por tashmë jep shpresë për sukses.

Ekzekutimi në emulator

Në emulator është shumë më e lehtë të bësh debug, prandaj, fillimisht sigurohemi që biblioteka funksionon në qemu. Si platformë e emuluar kam zgjedhur Integrator/CP, sepse, nga njëra anë, është gjithashtu ARM dhe, nga ana tjetë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 ndihmën e tij e shtoj OpenCV si modul (duke kaluar të gjithë ato opsione 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 
#include 

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

    return 0;
}

Ndërtojmë sistemin, e ekzekutojmë — dhe 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:
    Data e kohës:                   2019-06-21T10:02:18Z
    Host:                        Linux 5.1.7-arch1-1-ARCH x86_64
    Objektivi:                      Generic arm-unknown-none
    CMake:                       3.14.5
    Generatori i CMake:             Unix Makefiles
    Instrumenti për ndërtimin e CMake:            /usr/bin/make
    Konfigurimi:               Debug

  Tiparet e CPU/HW:
    Bazë:
      e kërkuar:                 DETECT
      e ndaluar:                  VFPV3 NEON

  C/C++:
    Ndërtuar si biblioteka dinamike?:      JO

Hapi tjetër është të ekzekutoni një shembull, më mirë ndonjë standard nga ata që ofrohen nga vetë zhvilluesit në faqen tuaj. Kam zgjedhur detektor të skajeve Këni.

Shembulli ishte paksa e nevojshme ta rregulloja, për të shfaqur imazhin me rezultatin direkt në frame-buffer. Duhej ta bëja këtë, pasi funksioni imshow() di të vizatojë imazhe përmes interface-ve QT, GTK dhe Windows, të cilat, natyrisht, në konfigurimin për STM32 nuk do të jenë. Në të vërtetë, QT gjithashtu mund të ekzekutohet në STM32F7Discovery, por për këtë do të flitet në një artikull tjetër 🙂

Pas një sqarimi të shkurtër mbi se në cilin format ruhet rezultati i punës së detektorit të skajeve, marrim imazhin.

OpenCV në STM32F7-Discovery

Imazhi origjinal

OpenCV në STM32F7-Discovery

Rezultati

Ekzekutimi në STM32F7Discovery

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

  1. 320KiB memorie të përkohshme
  2. 1MiB memorie flash për figurën
  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ë një shembulli minimal nuk është shumë e dobishme.
Ekrani ka një resolutë prej 480×272, dhe kështu, memoria për frame-buffer do të ketë 522 240 byte në thellësi 32 bit, pra kjo është më shumë se sa sasia e memories së përkohshme, kështu që frame-buffer dhe heaps (të cilat do të kërkohen edhe për OpenCV, për të ruajtur të dhënat për imazhet dhe për strukturat ndihmëse) do të vendosen në SDRAM, çdo gjë tjetër (memoria për steka dhe nevojat e tjera sistemore) do të shkojë në RAM.

Nëse merrni konfigurimin minimal për STM32F7Discovery (hiqni të gjithë rrjetin, të gjitha komandat, bëni stekat sa më të vogla dhe etj.) dhe e shtoni OpenCV me shembuj, kjo do të ishte për sa i përket memories së kërkuar:

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

Për ata që nuk janë shumë të njohur me sekuencat që shkojnë ku, le të shpjegoj: në .text dhe .rodata janë ruajtur instruksionet dhe konstantet (për ta thënë thjesht, të dhënat pa lexim), në .data janë të dhënat e ndryshueshme, në .bss janë variablat "e zeruara", të cilat, megjithatë, kanë nevojë për hapësirë (kjo seksion "do të dërgohet" në RAM).

Lajmi i mirë është se .data/.bss duhen vendosur, por me .text problem — për imazhin ka vetëm 1MiB memorie. Mund ta heqim nga .text imazhin e shembujve dhe ta lexojmë, për shembull, nga karta SD në memorie gjatë aktivizimit, por fruits.png ka peshë rreth 330KiB, kështu që kjo problem nuk e zgjidh: pjesa më e madhe .text përbëhet pikërisht nga kodi OpenCV.

Në thelb, mbetet vetëm një zgjidhje — ngarkimi i pjesës së kodit në QSPI-flash (ajo ka një mod utilizimi të veçantë për mapimin e memories në autobusin sistemor, kështu që procesori mund t'i qaset këtyre të dhënave direkt). Megjithatë, lind problemi: së pari, memoria e QSPI-flash nuk është e arritshme menjëherë pas rinisjes së pajisjes (duhet të inizializoni veçmas modin e mapeuar të memories), së dyti, nuk mund të "flash" këtë memorie me boot loader-in e zakonshëm.

Në fund, u vendos të lidhet i gjithë kodi në QSPI, dhe ta flashojmë me një boot loader të shkruar nga vetë, i cili do të merrte binarin e nevojshëm përmes TFTP.

Rezultati

Ideja për të portuar këtë bibliotekë në Embox ka lindur rreth një viti më parë, por herë pas here është shtyrë për shkak të arsyeve të ndryshme. Njëra prej tyre është mbështetje për libstdc++ dhe standardin e bibliotekave të templatuara. Problemi i mbështetjes C++ në Embox del përtej këtij artikulli, ndaj këtu do të them vetëm se arritëm të sigurojmë këtë mbështetje në masën e nevojshme për funksionimin e kësaj biblioteke 🙂

Në fund, këto probleme u kapërcyen (të paktën, në masën e mjaftueshme për funksionimin e shembujve OpenCV), dhe shembulli u aktivizua. 40 sekonda të gjata kërkon blloku për të gjetur kufijtë me filtrin Canny. Kjo, natyrisht, është shumë e gjatë (ka propozime se si ta optimizojmë këtë proces, për këtë mund të shkruajmë një artikull të veçantë në rast suksesi).

OpenCV në STM32F7-Discovery

Megjithatë, qëllimi ndërmjetës ishte krijimi i një prototipi që do të tregonte mundësinë themelore të aktivizimit të OpenCV në STM32, përkatësisht, ky qëllim u arrit, urime!

tl;dr: udhëzim 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 boot loader-it, i cili "do ta flash" QSPI-flash.

    make confload-arm/stm32f7cube

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

Shembulli 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 kartës.

Pasi të keni bërë këtë, ndjekim krijimin e ngarkuesit:

    make

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

    make confload-platform/opencv/stm32f7discovery
    make

4: Nxjerrja nga ELF e seksioneve 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ë direktorinë conf ndodhet një skript që e bën këtë, kështu që mund ta filloni atë

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

5: Me ndihmën e tftp ngarkoni qspi.bin.bin në QSPI-flash. Në host, për këtë duhet të kopjoni qspi.bin në dosjen rrënjësore të serverit tftp (zakonisht është /srv/tftp/ ose /var/lib/tftpboot/; paketat për serverin përkatës janë të disponueshme në shumicën e shpërndarjeve popullore, zakonisht quhen tftpd ose tftp-hpa, ndonjëherë duhet të bëni 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 (pra, në ngarkues) duhet të ekzekutoni këtë komandë (duke supozuar se 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ë hidheni" në memorien QSPI. Lokacioni specifik do të ndryshojë në varësi të mënyrës se si imazhi është lidhur, këtë adresë mund ta shihni me komandën mem 0x90000000 (adresa e fillimit përdoret në fjalën e dytë 32-bit të imazhit); gjithashtu do të kërkohet të vendosni flamurin e stack-ut -s, adresa e stack-ut ndodhet në adresën 0x90000000, shembull:

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

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

7: Nisim

    embox> edges 20

dhe shijoni kërkimin e kufijve për 40 sekonda 🙂

Nëse ndodhi ndonjë problem — shkruani një çështje në repositori ynë, ose në listën e postimeve embox-devel@googlegroups.com, ose në komentet këtu.

Burimi: habr.com

Blini hostim të besueshëm për faqe interneti me mbrojtje DDoS, serverë VPS VDS 🔥 Blini hostim të besueshëm për faqe interneti me mbrojtje DDoS, serverë VPS VDS - ProHoster