OpenCV sur STM32F7-Malkovro

OpenCV sur STM32F7-Malkovro Mi estas unu el la programistoj de la operaciumo Embox, kaj en ĉi tiu artikolo mi parolos pri kiel mi sukcesis ruli OpenCV sur la STM32746G-tabulo.

Se vi tajpas ion kiel "OpenCV sur STM32-tabulo" en serĉilon, vi povas trovi sufiĉe multajn homojn, kiuj interesiĝas pri uzi ĉi tiun bibliotekon sur STM32-tabuloj aŭ aliaj mikroregiloj.
Estas pluraj filmetoj, kiuj, se juĝante laŭ la nomo, devus montri tion, kio necesas, sed kutime (en ĉiuj videoj kiujn mi vidis) sur la STM32-tabulo, nur la bildo estis ricevita de la fotilo kaj la rezulto estis montrita sur la ekrano, kaj la bildprilaborado mem estis farita aŭ sur regula komputilo, aŭ sur pli potencaj tabuloj (ekzemple, Raspberry Pi).

Kial ĝi estas malfacila?

La populareco de serĉdemandoj estas klarigita per la fakto, ke OpenCV estas la plej populara komputilvida biblioteko, kio signifas, ke pli da programistoj konas ĝin, kaj la kapablo ruli labortablan kodon sur mikroregilo multe simpligas la evoluprocezon. Sed kial ankoraŭ ne ekzistas popularaj pretaj receptoj por solvi ĉi tiun problemon?

La problemo uzi OpenCV sur malgrandaj ŝaloj rilatas al du trajtoj:

  • Se vi kompilas la bibliotekon eĉ kun minimuma aro da moduloj, ĝi simple ne konvenos en la fulmmemoron de la sama STM32F7Discovery (eĉ sen konsideri la OS) pro tre granda kodo (kelkaj megabajtoj da instrukcioj)
  • La biblioteko mem estas skribita en C++, kio signifas
    • Bezonas subtenon por pozitiva rultempo (esceptoj, ktp.)
    • Malmulta subteno por LibC/Posix, kiu kutime troviĝas en OS por enkonstruitaj sistemoj - vi bezonas norman plus bibliotekon kaj norman STL-ŝablonbibliotekon (vektoro, ktp.)

Portado al Embox

Kiel kutime, antaŭ ol porti ajnajn programojn al la operaciumo, estas bona ideo provi konstrui ĝin en la formo en kiu la programistoj intencis ĝin. En nia kazo, ne estas problemoj pri tio - la fontkodo troviĝas sur github, la biblioteko estas konstruita sub GNU/Linukso kun la kutima cmake.

La bona novaĵo estas, ke OpenCV povas esti konstruita kiel senmova biblioteko el la skatolo, kio faciligas porti. Ni kolektas bibliotekon kun norma agordo kaj vidas kiom da spaco ili okupas. Ĉiu modulo estas kolektita en aparta biblioteko.

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

Kiel vi povas vidi de la lasta linio, .bss kaj .data ne okupas multe da spaco, sed la kodo estas pli ol 70 MiB. Estas klare, ke se ĉi tio estas statike ligita al specifa aplikaĵo, la kodo malpliiĝos.

Ni provu elĵeti kiel eble plej multajn modulojn por ke minimuma ekzemplo estu kunvenita (kiu, ekzemple, simple eligos la OpenCV-version), do ni rigardas cmake .. -LA kaj malŝaltu en la opcioj ĉion, kio malŝaltas.

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

Unuflanke, ĉi tio estas nur unu modulo de la biblioteko, aliflanke, ĉi tio estas sen kompilila optimumigo por koda grandeco (-Os). ~3 MiB da kodo estas ankoraŭ sufiĉe multe, sed jam donas esperon por sukceso.

Kuru en la emulilo

Estas multe pli facile sencimigi sur la emulilo, do unue certigu, ke la biblioteko funkcias sur qemu. Kiel emulita platformo, mi elektis Integrator / CP, ĉar unue, ĝi ankaŭ estas ARM, kaj due, Embox subtenas grafikan eligon por ĉi tiu platformo.

Embox havas mekanismon por konstrui eksterajn bibliotekojn, uzante ĝin ni aldonas OpenCV kiel modulon (pasante ĉiujn samajn eblojn por la "minimuma" konstruo en formo de statikaj bibliotekoj), post tio mi aldonas simplan aplikaĵon, kiu aspektas jene:

version.cpp:

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

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

    return 0;
}

Ni muntas la sistemon, rulas ĝin - ni ricevas la atendatan eligon.

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

La sekva paŝo estas ekzekuti iun ekzemplon, prefere unu el la normaj ofertitaj de la programistoj mem. sur via retejo. mi elektis limdetektilo canny.

La ekzemplo devis esti iomete reverkita por montri la bildon kun la rezulto rekte en la kadra bufro. Mi devis fari ĉi tion, ĉar. funkcio imshow() povas desegni bildojn per la interfacoj QT, GTK kaj Windows, kiuj, kompreneble, certe ne estos en la agordo por STM32. Fakte, QT ankaŭ povas ruliĝi per STM32F7Discovery, sed ĉi tio estos diskutita en alia artikolo 🙂

Post mallonga klarigo, en kiu formato la rezulto de la randa detektilo estas stokita, ni ricevas bildon.

OpenCV sur STM32F7-Malkovro

originala bildo

OpenCV sur STM32F7-Malkovro

rezulto

Funkcianta sur STM32F7Discovery

Sur 32F746GDISCOVERY estas pluraj aparataj memorsekcioj, kiujn ni povas uzi unumaniere aŭ alian.

  1. 320 KiB RAM
  2. 1MiB-fulmo por bildo
  3. 8 MiB SDRAM
  4. 16MiB QSPI NAND Flash
  5. fendo por kartoj microSD

SD-karto povas esti uzata por stoki bildojn, sed en la kunteksto de kurado de minimuma ekzemplo, ĉi tio ne estas tre utila.
La ekrano havas rezolucion de 480×272, kio signifas, ke la frambuffer-memoro estos 522 bajtoj je profundo de 240 bitoj, t.e. ĉi tio estas pli ol la grandeco de RAM, do la framebuffer kaj la amaso (kiu estos postulata, inkluzive por OpenCV, por stoki datumojn por bildoj kaj helpaj strukturoj) troviĝos en SDRAM, ĉio alia (memoro por stakoj kaj aliaj sistemoj bezonoj). ) iros al RAM .

Se ni prenas la minimuman agordon por STM32F7Discovery (forĵetu la tutan reton, ĉiujn komandojn, faru stakojn kiel eble plej malgrandajn, ktp.) kaj aldonas OpenCV kun ekzemploj tie, la bezonata memoro estos jena:

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

Por tiuj, kiuj ne tre konas, kiuj sekcioj iras kien, mi klarigos: en .text и .rodata instrukcioj kaj konstantoj (malglate parolante, nur legeblaj datumoj) kuŝas en .data la datumoj estas ŝanĝeblaj, .bss estas "nuligitaj" variabloj, kiuj tamen bezonas lokon (ĉi tiu sekcio "iros" al RAM).

La bona novaĵo estas tio .data/.bss devus konveni, sed kun .text la problemo estas, ke ekzistas nur 1MiB da memoro por la bildo. Povas esti elĵetita .text la bildon el la ekzemplo kaj legu ĝin, ekzemple, de la SDa karto en memoron ĉe ekfunkciigo, sed fruits.png pezas ĉirkaŭ 330KiB, do ĉi tio ne solvos la problemon: plej .text konsistas el la OpenCV-kodo.

Ĝenerale, restas nur unu afero - ŝarĝi parton de la kodo sur QSPI-fulmo (ĝi havas specialan operacimanieron por mapado de memoro al la sistema buso, por ke la procesoro povu rekte aliri ĉi tiujn datumojn). En ĉi tiu kazo, problemo ekestas: unue, la memoro de QSPI-memoro ne disponeblas tuj post kiam la aparato estas rekomencita (vi devas aparte pravalorigi la memor-mapitan reĝimon), kaj due, vi ne povas "fulmi" ĉi tiun memoron per konata ekŝargilo.

Kiel rezulto, oni decidis ligi la tutan kodon en QSPI, kaj ekbriligi ĝin per memskribita ŝargilo, kiu ricevos la bezonatan binaron per TFTP.

rezulto

La ideo porti ĉi tiun bibliotekon al Embox aperis antaŭ proksimume unu jaro, sed ree kaj ree ĝi estis prokrastita pro diversaj kialoj. Unu el ili estas subteno por libstdc++ kaj la norma ŝablono-biblioteko. La problemo de C++-subteno en Embox estas preter la amplekso de ĉi tiu artikolo, do ĉi tie mi nur diros, ke ni sukcesis atingi ĉi tiun subtenon en la ĝusta kvanto por ke ĉi tiu biblioteko funkciu 🙂

Fine, ĉi tiuj problemoj estis venkitaj (almenaŭ sufiĉe por ke la ekzemplo OpenCV funkciu), kaj la ekzemplo kuris. La estraro bezonas 40 longajn sekundojn por serĉi limojn per la Canny-filtrilo. Ĉi tio kompreneble estas tro longa (estas konsideroj pri kiel optimumigi ĉi tiun aferon, pri tio eblos verki apartan artikolon en kazo de sukceso).

OpenCV sur STM32F7-Malkovro

Tamen, la meza celo estis krei prototipon kiu montros la fundamentan eblecon ruli OpenCV sur STM32, respektive, ĉi tiu celo estis atingita, hure!

tl;dr: instrukcioj paŝo post paŝo

0: Elŝutu Embox-fontojn, jene:

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

1: Ni komencu kunmetante ekŝargilon, kiu "flumigos" QSPI-memorilon.

    make confload-arm/stm32f7cube

Nun vi devas agordi la reton, ĉar. Ni alŝutos la bildon per TFTP. Por agordi la tabulon kaj gastigantajn IP-adresojn, vi devas redakti la conf/rootfs/network.

Ekzemplo de agordo:

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 - gastiga adreso de kie la bildo estos ŝarĝita, address - adreso de la estraro.

Post tio, ni kolektas la ekŝargilon:

    make

2: La kutima ŝarĝo de la ekŝargilo (pardonu la vortludon) sur la tabulo - estas nenio specifa ĉi tie, vi devas fari ĝin kiel por iu ajn alia aplikaĵo por STM32F7Discovery. Se vi ne scias kiel fari ĝin, vi povas legi pri ĝi tie.
3: Kompilante bildon kun agordo por OpenCV.

    make confload-platform/opencv/stm32f7discovery
    make

4: Eltiraĵo de ELF-sekcioj skribenda al QSPI al 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

Estas skripto en la dosierujo conf kiu faras tion, do vi povas ruli ĝin

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

5: Uzante tftp, elŝutu qspi.bin.bin al QSPI-poŝmemoro. Sur la gastiganto, por fari tion, kopiu qspi.bin al la radika dosierujo de la tftp-servilo (kutime /srv/tftp/ aŭ /var/lib/tftpboot/; pakoj por la responda servilo disponeblas en plej popularaj distribuoj, kutime nomataj. tftpd aŭ tftp-hpa, foje vi devas fari systemctl start tftpd.service komenci).

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

Sur Embox (t.e. en la ekŝargilo), vi devas ekzekuti la sekvan komandon (ni supozas, ke la servilo havas la adreson 192.168.2.1):

    embox> qspi_loader qspi.bin 192.168.2.1

6: Kun ordono goto vi devas "salti" en QSPI-memoron. La specifa loko varias depende de kiel la bildo estas ligita, vi povas vidi ĉi tiun adreson per la komando mem 0x90000000 (la komenca adreso taŭgas en la dua 32-bita vorto de la bildo); vi ankaŭ devos marki la stakon -s, la stakadreso estas ĉe 0x90000000, ekzemplo:

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

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

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

7: Lanĉo

    embox> edges 20

kaj ĝuu la 40-sekundan randserĉon 🙂

Se io misfunkcias - skribu aferon enen nia deponejo, aŭ al la dissendolisto [retpoŝte protektita], aŭ en komento ĉi tie.

fonto: www.habr.com

Aldoni komenton