OpenCV sa STM32F7-Discovery

OpenCV sa STM32F7-Discovery Isa ako sa mga developer ng operating system Embox, at sa artikulong ito ay pag-uusapan ko kung paano ko pinatakbo ang OpenCV sa STM32746G board.

Kung nagta-type ka ng isang bagay tulad ng "OpenCV sa STM32 board" sa isang search engine, makakahanap ka ng ilang tao na interesadong gamitin ang library na ito sa mga STM32 board o iba pang microcontroller.
Mayroong ilang mga video na, sa paghusga sa pangalan, ay dapat magpakita kung ano ang kailangan, ngunit kadalasan (sa lahat ng mga video na nakita ko) sa STM32 board, ang imahe lamang ang natanggap mula sa camera at ang resulta ay ipinapakita sa screen, at ang mismong pagpoproseso ng imahe ay ginawa alinman sa isang regular na computer, o sa mas malakas na mga board (halimbawa, Raspberry Pi).

Bakit mahirap?

Ang katanyagan ng mga query sa paghahanap ay ipinaliwanag sa pamamagitan ng katotohanan na ang OpenCV ay ang pinakasikat na computer vision library, na nangangahulugan na mas maraming developer ang pamilyar dito, at ang kakayahang magpatakbo ng desktop-ready code sa isang microcontroller ay lubos na nagpapadali sa proseso ng pag-unlad. Ngunit bakit wala pa ring sikat na handa na mga recipe para sa paglutas ng problemang ito?

Ang problema sa paggamit ng OpenCV sa maliliit na shawl ay nauugnay sa dalawang tampok:

  • Kung pinagsama-sama mo ang library kahit na may kaunting hanay ng mga module, hindi ito magkasya sa flash memory ng parehong STM32F7Discovery (kahit na hindi isinasaalang-alang ang OS) dahil sa isang napakalaking code (ilang megabytes ng mga tagubilin)
  • Ang library mismo ay nakasulat sa C++, ibig sabihin
    • Kailangan ng suporta para sa positibong runtime (mga pagbubukod, atbp.)
    • Kaunting suporta para sa LibC/Posix, na kadalasang matatagpuan sa OS para sa mga naka-embed na system - kailangan mo ng standard plus library at isang standard na STL template library (vector, atbp.)

Pag-port sa Embox

Gaya ng dati, bago mag-port ng anumang mga program sa operating system, magandang ideya na subukang buuin ito sa anyo kung saan nilayon ito ng mga developer. Sa aming kaso, walang mga problema dito - ang source code ay matatagpuan sa github, ang library ay binuo sa ilalim ng GNU/Linux na may karaniwang cmake.

Ang magandang balita ay ang OpenCV ay maaaring itayo bilang isang static na library sa labas ng kahon, na ginagawang mas madali ang pag-port. Kinokolekta namin ang isang library na may karaniwang config at tingnan kung gaano karaming espasyo ang nagagamit nila. Ang bawat module ay kinokolekta sa isang hiwalay na aklatan.

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

Tulad ng nakikita mo mula sa huling linya, ang .bss at .data ay hindi kumukuha ng maraming espasyo, ngunit ang code ay higit sa 70 MiB. Ito ay malinaw na kung ito ay statically naka-link sa isang partikular na application, ang code ay magiging mas mababa.

Subukan nating itapon ang maraming mga module hangga't maaari upang ang isang maliit na halimbawa ay binuo (na, halimbawa, ay maglalabas lamang ng bersyon ng OpenCV), kaya tingnan natin cmake .. -LA at i-off sa mga opsyon ang lahat ng bagay na naka-off.

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

Sa isang banda, isa lang itong module ng library, sa kabilang banda, ito ay walang compiler optimization para sa laki ng code (-Os). ~3 MiB ng code ay marami pa rin, ngunit nagbibigay na ng pag-asa para sa tagumpay.

Patakbuhin sa emulator

Mas madaling mag-debug sa emulator, kaya siguraduhin muna na gumagana ang library sa qemu. Bilang isang emulated platform, pinili ko ang Integrator / CP, dahil una, ito ay ARM din, at pangalawa, sinusuportahan ng Embox ang graphics output para sa platform na ito.

Ang Embox ay may mekanismo para sa pagbuo ng mga panlabas na aklatan, gamit ito idinadagdag namin ang OpenCV bilang isang module (ipinapasa ang lahat ng parehong mga pagpipilian para sa "minimal" na build sa anyo ng mga static na aklatan), pagkatapos ay nagdagdag ako ng isang simpleng application na ganito ang hitsura:

version.cpp:

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

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

    return 0;
}

Binubuo namin ang system, patakbuhin ito - nakukuha namin ang inaasahang output.

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 Π²ΠΊΠ»ΡŽΡ‡Π΅Π½Ρ‹ Π² сборку ΠΈ Ρ‚.ΠΏ.>

Ang susunod na hakbang ay magpatakbo ng ilang halimbawa, mas mabuti ang isa sa mga karaniwang inaalok ng mga developer mismo. sa iyong site. Pinili ko border detector canny.

Ang halimbawa ay kailangang bahagyang muling isulat upang ipakita ang imahe na may resulta nang direkta sa frame buffer. Kinailangan kong gawin ito, dahil. function imshow() maaaring gumuhit ng mga imahe sa pamamagitan ng mga interface ng QT, GTK at Windows, na, siyempre, ay tiyak na wala sa config para sa STM32. Sa katunayan, ang QT ay maaari ding patakbuhin sa STM32F7Discovery, ngunit ito ay tatalakayin sa ibang artikulo πŸ™‚

Pagkatapos ng maikling paglilinaw kung saan naka-imbak ang resulta ng edge detector, nakakakuha kami ng isang imahe.

OpenCV sa STM32F7-Discovery

orihinal na larawan

OpenCV sa STM32F7-Discovery

Resulta

Tumatakbo sa STM32F7Discovery

Sa 32F746GDISCOVERY mayroong ilang mga seksyon ng memorya ng hardware na maaari naming gamitin sa isang paraan o iba pa

  1. 320KiB RAM
  2. 1MiB flash para sa larawan
  3. 8MiB SDRAM
  4. 16MiB QSPI NAND Flash
  5. puwang ng microSD card

Maaaring gamitin ang isang SD card upang mag-imbak ng mga larawan, ngunit sa konteksto ng pagpapatakbo ng kaunting halimbawa, hindi ito masyadong kapaki-pakinabang.
Ang display ay may resolution na 480Γ—272, na nangangahulugan na ang framebuffer memory ay magiging 522 bytes sa lalim na 240 bits, i.e. ito ay higit pa sa laki ng RAM, kaya ang framebuffer at ang heap (na kakailanganin, kabilang ang para sa OpenCV, upang mag-imbak ng data para sa mga imahe at mga auxiliary na istruktura) ay matatagpuan sa SDRAM, lahat ng iba pa (memorya para sa mga stack at iba pang mga pangangailangan ng system ) ay mapupunta sa RAM .

Kung kukuha tayo ng pinakamababang config para sa STM32F7Discovery (itapon ang buong network, lahat ng mga command, gumawa ng mga stack bilang maliit hangga't maaari, atbp.) at magdagdag ng OpenCV na may mga halimbawa doon, ang kinakailangang memorya ay ang mga sumusunod:

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

Para sa mga hindi masyadong pamilyar kung saan pupunta ang mga seksyon, ipapaliwanag ko: sa .text ΠΈ .rodata ang mga tagubilin at constants (halos pagsasalita, readonly data) ay nasa .data ang data ay nababago, .bss may mga "nulled" na mga variable, na, gayunpaman, ay nangangailangan ng isang lugar (ang seksyong ito ay "pumunta" sa RAM).

Ang magandang balita ay iyon .data/.bss dapat magkasya, ngunit may .text ang problema ay mayroon lamang 1MiB ng memorya para sa imahe. Maaaring itapon .text ang larawan mula sa halimbawa at basahin ito, halimbawa, mula sa SD card papunta sa memorya sa pagsisimula, ngunit ang fruits.png ay tumitimbang ng humigit-kumulang 330KiB, kaya hindi nito malulutas ang problema: karamihan .text binubuo ng OpenCV code.

Sa pangkalahatan, mayroon lamang isang bagay na natitira - ang pag-load ng isang bahagi ng code sa isang QSPI flash (ito ay may isang espesyal na mode ng operasyon para sa pagmamapa ng memorya sa bus ng system, upang ang processor ay direktang ma-access ang data na ito). Sa kasong ito, lumitaw ang isang problema: una, ang memorya ng isang QSPI flash drive ay hindi magagamit kaagad pagkatapos na i-reboot ang device (kailangan mong hiwalay na simulan ang memory-mapped mode), at pangalawa, hindi mo maaaring "i-flash" ang memorya na ito gamit ang isang pamilyar na bootloader.

Bilang resulta, napagpasyahan na i-link ang lahat ng code sa QSPI, at i-flash ito gamit ang self-written loader na makakatanggap ng kinakailangang binary sa pamamagitan ng TFTP.

Resulta

Ang ideya na i-port ang library na ito sa Embox ay lumitaw halos isang taon na ang nakalipas, ngunit paulit-ulit itong ipinagpaliban dahil sa iba't ibang dahilan. Ang isa sa mga ito ay suporta para sa libstdc++ at ang karaniwang template library. Ang problema ng suporta sa C++ sa Embox ay lampas sa saklaw ng artikulong ito, kaya dito ko lang sasabihin na nagawa naming makamit ang suportang ito sa tamang halaga para gumana ang library na ito πŸ™‚

Sa huli, nalampasan ang mga problemang ito (kahit sapat para gumana ang halimbawa ng OpenCV), at tumakbo ang halimbawa. Tumatagal ng 40 mahabang segundo para maghanap ang board ng mga hangganan gamit ang Canny filter. Ito, siyempre, ay masyadong mahaba (may mga pagsasaalang-alang kung paano i-optimize ang bagay na ito, posible na magsulat ng isang hiwalay na artikulo tungkol dito kung sakaling magtagumpay).

OpenCV sa STM32F7-Discovery

Gayunpaman, ang intermediate na layunin ay lumikha ng isang prototype na magpapakita ng pangunahing posibilidad ng pagpapatakbo ng OpenCV sa STM32, ayon sa pagkakabanggit, ang layuning ito ay nakamit, hooray!

tl;dr: sunud-sunod na mga tagubilin

0: Mag-download ng mga mapagkukunan ng Embox, tulad nito:

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

1: Magsimula tayo sa pamamagitan ng pag-assemble ng isang bootloader na "mag-flash" ng isang QSPI flash drive.

    make confload-arm/stm32f7cube

Ngayon ay kailangan mong i-configure ang network, dahil. I-upload namin ang larawan sa pamamagitan ng TFTP. Upang itakda ang board at host ng mga IP address, kailangan mong i-edit ang conf/rootfs/network.

Halimbawa ng configuration:

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 - address ng host kung saan ilo-load ang larawan, address - address ng board.

Pagkatapos nito, kinokolekta namin ang bootloader:

    make

2: Ang karaniwang paglo-load ng bootloader (paumanhin para sa pun) sa board - walang tiyak dito, kailangan mong gawin ito tulad ng para sa anumang iba pang application para sa STM32F7Discovery. Kung hindi mo alam kung paano gawin ito, maaari mong basahin ang tungkol dito dito.
3: Pag-compile ng isang imahe na may config para sa OpenCV.

    make confload-platform/opencv/stm32f7discovery
    make

4: I-extract mula sa mga seksyon ng ELF na isusulat sa QSPI sa 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

Mayroong isang script sa direktoryo ng conf na gumagawa nito, para mapatakbo mo ito

    ./conf/qspi_objcopy.sh # НуТный Π±ΠΈΠ½Π°Ρ€Π½ΠΈΠΊ -- build/base/bin/qspi.bin

5: Gamit ang tftp, i-download ang qspi.bin.bin sa isang QSPI flash drive. Sa host, para magawa ito, kopyahin ang qspi.bin sa root folder ng tftp server (karaniwan ay /srv/tftp/ o /var/lib/tftpboot/; ang mga package para sa kaukulang server ay available sa pinakasikat na distribusyon, karaniwang tinatawag tftpd o tftp-hpa, minsan kailangan mong gawin systemctl start tftpd.service upang simulan ang).

    # Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ для tftpd
    sudo cp build/base/bin/qspi.bin /srv/tftp
    # Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ для tftp-hpa
    sudo cp build/base/bin/qspi.bin /var/lib/tftpboot

Sa Embox (i.e. sa bootloader), kailangan mong isagawa ang sumusunod na command (ipagpalagay namin na ang server ay may address na 192.168.2.1):

    embox> qspi_loader qspi.bin 192.168.2.1

6: Sa utos goto kailangan mong "tumalon" sa memorya ng QSPI. Mag-iiba-iba ang partikular na lokasyon depende sa kung paano naka-link ang larawan, makikita mo ang address na ito gamit ang command mem 0x90000000 (ang panimulang address ay umaangkop sa pangalawang 32-bit na salita ng larawan); kakailanganin mo ring i-flag ang stack -s, ang stack address ay nasa 0x90000000, halimbawa:

    embox>mem 0x90000000
    0x90000000:     0x20023200  0x9000c27f  0x9000c275  0x9000c275
                      ↑           ↑
              это адрСс    это  адрСс 
                стэка        ΠΏΠ΅Ρ€Π²ΠΎΠΉ
                           инструкции

    embox>goto -i 0x9000c27f -s 0x20023200 # Π€Π»Π°Π³ -i Π½ΡƒΠΆΠ΅Π½ Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π·Π°ΠΏΡ€Π΅Ρ‚ΠΈΡ‚ΡŒ прСрывания Π²ΠΎ врСмя ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ систСмы

    < Начиная ΠΎΡ‚ΡΡŽΠ΄Π° Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹Π²ΠΎΠ΄ Π½Π΅ Π·Π°Π³Ρ€ΡƒΠ·Ρ‡ΠΈΠΊΠ°, Π° ΠΎΠ±Ρ€Π°Π·Π° с OpenCV >

7: Ilunsad

    embox> edges 20

at tamasahin ang 40 segundong paghahanap sa hangganan πŸ™‚

Kung may mali - sumulat ng isyu sa ang aming imbakan, o sa mailing list [protektado ng email], o sa isang komento dito.

Pinagmulan: www.habr.com

Magdagdag ng komento