Én vagyok az operációs rendszer egyik fejlesztője , é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 , 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. . Én a .
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.

Eredeti kép

Eredmény
Futtatás STM32F7Discovery-n
A 32F746GDISCOVERY-n több hardveres memória szegmens van, amelyeket így vagy úgy felhasználhatunk.
- 320KiB RAM
- 1MiB flash memória a képmásnak
- 8MiB SDRAM
- 16MiB QSPI NAND flash
- 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/emboxAzok 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á!

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-tMost 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:02gateway — a hoszt címe, ahonnan a kép betöltődik, address — az alaplap címe.
Ezután összegyűjtjük a bootloadert:
make2: 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 .
3: A kép fordítása OpenCV konfigurációval.
make confload-platform/opencv/stm32f7discovery
make4: 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=.dataA 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.bin5: 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/tftpbootAz 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.16: 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 alatt7: 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 , vagy az embox-devel@googlegroups.com levelezőlistára, vagy kommentekbe itt.
Forrás: habr.com
