OpenCV STM32F7-Discovery-n.

OpenCV STM32F7-Discovery-n. Én vagyok az operációs rendszer egyik fejlesztője Embox, és ebben a cikkben arról fogok mesélni, hogyan sikerült elindítanom az OpenCV-t az STM32746G lapon.

Ha az internetes keresőbe beírsz valami olyasmit, mint "OpenCV az STM32 lapon", akkor találhatsz jó néhány olyan embert, aki érdeklődik arról, hogyan lehet ezt a könyvtárat STM32 lapokon vagy más mikrokontrollereken használni.
Van néhány videó, amelyek címe alapján azt kellene bemutatniuk, amire szükség van, de általában (az összes videóban, amelyet láttam) az STM32 lapon csak a kamerából érkező kép megszerzése és az eredmény megjelenítése történt, míg a kép feldolgozását vagy egy sima számítógépen, vagy erősebb lapokon (például Raspberry Pi-n) végezték.

Miért olyan nehéz ez?

A népszerű keresési kifejezések népszerűsége azzal magyarázható, hogy az OpenCV a legnépszerűbb számítógépes látási könyvtár, így több fejlesztő ismeri, és a lehetőség, hogy az asztali kódot mikrokontrolleren futtassuk, jelentősen egyszerűsíti a fejlesztési folyamatot. De miért nincsenek eddig közismert megoldások erre a problémára?

Az OpenCV kis lapokon való használatának problémája két sajátossággal függ össze:

  • Ha a könyvtárat még a minimális modulokkal is lefordítjuk, az STM32F7Discovery flash-memóriájába egyszerűen nem fér bele (még az OS figyelembevételével sem) a rendkívül nagy kód miatt (több megabyte utasítás)
  • A könyvtár maga C++ nyelven íródott, ami azt jelenti,
    • Szükség van a C++ futtatókörnyezet támogatására (kivétel és hasonlók)
    • Kicsi a LibC/Posix támogatása, amely általában megtalálható beágyazott rendszerek operációs rendszereiben — szükség van a C++ standard könyvtárára és a STL sablon könyvtárra (vektor stb.)

Portálás az Embox-ra

Mint általában, mielőtt bármilyen programot portolnánk egy operációs rendszerbe, érdemes kipróbálni a fejlesztők által tervezett formában összeszerelni azt. Esetünkben ezzel nem merülnek fel problémák — az alapforrásokat megtalálhatod a GitHubon, a könyvtár pedig a GNU/Linux rendszer alatt a szokásos cmake segítségével építhető.

A jó hír az, hogy az OpenCV dobozból statikus könyvtárként is összeállítható, ami megkönnyíti a portálást. Összeszereljük a könyvtárat a standard konfigurációval, és megnézzük, mennyi helyet foglalnak el. Minden egyes modul külön könyvtárként épül.

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

Ahogy az utolsó sorból látszik, a .bss és .data nem foglal el túl sok helyet, viszont a kód több mint 70 MiB. Nyilvánvaló, hogy ha ezt statikusan linkeljük egy adott alkalmazással, a kód mennyisége csökkenni fog.

Próbáljuk meg minél több modult eltávolítani, hogy létrejöjjön a minimális példa (amely például csak az OpenCV verziót írja ki), tehát nézzük meg: cmake .. -LA és kikapcsoljuk az opciókban minden kikapcsolható dolgot.

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

Egyrészt ez csak egy modulfajta a könyvtárból, másrészt ez a kód optimalizálása nélküli méret szerint (-Os). ~3 MiB kód — ez még mindig sok, de már reményt ad a sikerre.

Futtatás emulátorban

Emulátorban sokkal könnyebb hibakeresni, ezért először győződjünk meg arról, hogy a könyvtár működik a qemu-n. Az emulált platformnak az Integrator/CP-t választottam, mert egyrészt ez is ARM, másrészt az Embox támogatja a grafikus kimenetet erre a platformra.

Az Embox rendelkezik egy mechanizmussal külső könyvtárak összeszerelésére, ezzel OpenCV-t modulként adunk hozzá (átadva az összes opciót a "minimalista" összeszereléshez statikus könyvtárak formájában), ezután hozzáadok egy egyszerű alkalmazást, ami így néz ki:

version.cpp:

#include 
#include 

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

    return 0;
}

Összeállítjuk a rendszert, elindítjuk — megkapjuk a várt kimenetet.

root@embox: /#opencv_version                                                     
OpenCV: 
Általános konfiguráció az OpenCV 4.0.1-hez =====================================
  Verzió ellenőrzés:               bd6927bdf-dirty

  Platform:
    Időbélyeg:                   2019-06-21T10:02:18Z
    Gazda:                       Linux 5.1.7-arch1-1-ARCH x86_64
    Cél:                         Általános arm-unknown-none
    CMake:                       3.14.5
    CMake generátor:             Unix Makefiles
    CMake építőeszköz:            /usr/bin/make
    Konfiguráció:                Debug

  CPU/HW jellemzők:
    Alap:
      kért:                      DETECT
      letiltva:                  VFPV3 NEON

  C/C++:
    Dinamikus könyvtárként épült?:      NEM

A következő lépés, hogy futtassunk egy példát, lehetőleg egyet a fejlesztők által ajánlott standardok közül. a saját weboldalukon.. Én a Canny élszegmáló algoritmust.

választottam. A példát egy kicsit át kellett írnom, hogy közvetlenül a keretbufferben jelenítse meg az eredményt. Ezt azért kellett megtenni, mert a imshow() függvény képeket képes megjeleníteni QT, GTK és Windows interfészeken, amelyeket nyilvánvalóan a STM32 konfigurációban nem találunk. Valójában a QT-t is lehet futtatni az STM32F7Discovery-n, de erről egy másik cikkben lesz szó 🙂

Rövid tisztázás után, hogy a kontúrdetektor eredménye pontosan milyen formátumban tárolódik, megkapjuk a képet.

OpenCV STM32F7-Discovery-n.

Eredeti kép

OpenCV STM32F7-Discovery-n.

Eredmény

Futtatás STM32F7Discovery-n

A 32F746GDISCOVERY-n több hardveres memória szegmens van, amelyeket így vagy úgy felhasználhatunk.

  1. 320KiB RAM
  2. 1MiB flash memória a képmásnak
  3. 8MiB SDRAM
  4. 16MiB QSPI NAND flash
  5. microSD kártya csatlakozó

Az SD kártyát a képek tárolására használhatjuk, de a minimális példák futtatása szempontjából ez nem túl hasznos.
A kijelző 480×272 felbontású, így a keretbufferhez szükséges memória 522 240 bájt 32 bites mélység esetén, ami meghaladja a RAM méretét, ezért a keretbufferünket és a kupacot (amelyre az OpenCV-nek is szüksége lesz a képek és segédstruktúrák adataihoz) az SDRAM-ban helyezzük el, minden más (a veremhez és egyéb rendszerszükségletekhez szükséges memória) pedig a RAM-ba kerül.

Ha a minimális konfigurációt vesszük az STM32F7Discovery-hez (kizárva az összes hálózatot, az összes parancsot, a veremméretet a lehető legkisebbre állítva, stb.) és az OpenCV-t a példákkal együtt hozzáadjuk, a következő memóriaigény merül fel:

   text    data     bss     dec     hex fájlnév
2876890  459208  312736 3648834  37ad42 build/base/bin/embox

Azok számára, akik nem nagyon ismerik, milyen szekciók hová kerülnek, tisztázom: a .text és .rodata itt tárolódnak az utasítások és konstansok (nagyjából readonly adatok), a .data itt tárolódnak a változtatható adatok, a .bss itt találhatók a "nullázott" változók, amelyeknek azonban szükségük van helyre (ez a szekció "RAM-ba kerül").

A jó hír az, hogy .data/.bss itt kellene elhelyezkedniük, de a .text probléma az, hogy csak 1MiB memória áll rendelkezésre a szöveges képhez. Le lehetne dobni a példában szereplő képet, és például az SD-kártyáról beolvasni a memóriába indításkor, de a fruits.png körülbelül 330KiB-ot foglal el, tehát ez a probléma nem oldódik meg: a legtöbb .text ténylegesen az OpenCV kódjából áll. .text Nagyjából csak egy megoldás maradt — a kód egy részének betöltése a QSPI-flash memóriába (ennek van egy speciális üzemmódja a memória rendszersínre történő leképezésére, így a processzor közvetlenül hozzáférhet ezekhez az adatokhoz). Ekkor felmerül a probléma: egyrészt, a QSPI-flash memória nem elérhető az eszköz újraindítása után (külön inicializálni kell a memória-mapézési módot), másrészt, ezt a memóriát nem lehet "flashelni" a szokásos bootloaderrel.

Végül úgy határoztunk, hogy az összes kódot a QSPI-be linkeljük, és egy saját készítésű bootloader-rel töltjük be, amely a szükséges binárist TFTP-n keresztül kapja.

Az ötlet, hogy ezt a könyvtárat portolni kell az Embox-ra, körülbelül egy évvel ezelőtt merült fel, de különböző okok miatt rendre elhalasztották. Az egyik ok a libstdc++ és a standard template library támogatása volt. A C++ támogatása az Embox-ban túlmutat ennek a cikknek a keretein, ezért csak annyit mondok, hogy sikerült elérnünk a szükséges szintű támogatást ehhez a könyvtárhoz 🙂

Eredmény

Végül ezeket a problémákat sikerült megoldani (legalábbis elegendő mértékben az OpenCV példájának működéséhez), és a példa elindult. A panel 40 hosszú másodpercet tölt a Canny szűrő határainak keresésére. Ez természetesen túl hosszú (vannak ötleteim, hogyan lehetne optimalizálni, erről egy külön cikkben írok, ha sikerül).

Mindazonáltal, a köztes cél egy prototípus létrehozása volt, amely bemutatja az OpenCV STM32-n történő futtatásának elvi lehetőségét, így ezt a célt elértük, hurrá!

OpenCV STM32F7-Discovery-n.

tl;dr: lépésről lépésre útmutató

0: Töltsük le az Embox forráskódját, például így:

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

    1: Kezdjük a bootloader összegyűjtésével, amely "flasheli" a QSPI-flash memóriát.

make confload-arm/stm32f7cube

    készítsd el a confload-arm/stm32f7cube-t

Most beállítani a hálózatot, mivel a képet TFTP-n keresztül fogjuk betölteni. Az alaplap és a hoszt IP-címének megadásához módosítani kell a conf/rootfs/network fájlt.

Példa konfiguráció:

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 — a hoszt címe, ahonnan a kép betöltődik, address — az alaplap címe.

Ezután összegyűjtjük a bootloadert:

    make

2: A bootloader szokásos betöltése (elnézést a szóviccért) az alaplapra — itt semmi speciális, ezt meg kell tenni, mint bármely más STM32F7Discovery alkalmazás esetében. Ha nem tudja, hogyan kell ezt megtenni, olvasson utána itt.
3: A kép fordítása OpenCV konfigurációval.

    make confload-platform/opencv/stm32f7discovery
    make

4: Az ELF szekciók kinyerése, amelyeket a QSPI-be kell írni, a qspi.bin fájlba

    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

A conf könyvtárban található egy script, ami ezt végzi, így futtathatja azt

    ./conf/qspi_objcopy.sh # A szükséges bináris -- build/base/bin/qspi.bin

5: TFTP segítségével betöltjük a qspi.bin fájlt a QSPI flash memóriába. A hoszton ehhez másolni kell a qspi.bin fájlt a tftp-szerver gyökérmappájába (ez általában /srv/tftp/ vagy /var/lib/tftpboot/; a megfelelő csomagok a legtöbb népszerű disztribúcióban megtalálhatók, általában tftpd vagy tftp-hpa néven, időnként el kell indítani systemctl start tftpd.service a kezdéshez).

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

Az Embox-ban (azaz a bootloaderben) végre kell hajtani egy ilyen parancsot (feltehetően, hogy a szerver címe 192.168.2.1):

    embox> qspi_loader qspi.bin 192.168.2.1

6: A goto parancs segítségével "ugrani" kell a QSPI memóriába. A konkrét hely változni fog a kép linkelésének módjától függően, ezt a címet a mem 0x90000000 paranccsal lehet megnézni. goto A kezdő cím az kép második 32-bites szavában található; emellett be kell állítani a stack-et is a -s flaggel. mem 0x90000000 (a kezdő cím a kép második 32-bites szavában található); szükség lesz a stack címének 0x90000000-ra történő megadására, példa: -s, a stack címe a 0x90000000 címen található, példa:

    embox>mem 0x90000000
    0x90000000:     0x20023200  0x9000c27f  0x9000c275  0x9000c275
                      ↑           ↑
              ez a cím    ez a cím 
                a stack-nek        az első
                           utasításnak

    embox>goto -i 0x9000c27f -s 0x20023200 # Az -i flag szükséges az interruptok letiltásához a rendszer inicializálása alatt

7: Indítsuk el

    embox> edges 20

és élvezzük a 40 másodperces élfeltárást 🙂

Ha valami nem működik, írjon problémát a repository-nkba, vagy az embox-devel@googlegroups.com levelezőlistára, vagy kommentekbe itt.

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster