Az operációs rendszer egyik fejlesztője vagyok
Ha valami olyasmit ír be a keresőbe, hogy „OpenCV az STM32 kártyán”, akkor jó néhány embert találhat, aki érdeklődik a könyvtár STM32 kártyákon vagy más mikrokontrollereken való használata iránt.
Több videó is van, aminek a névből ítélve bemutatnia kell, hogy mire van szükség, de általában (az összes videóban, amit láttam) az STM32 táblán csak a kép érkezett a kamerából, és az eredmény megjelent a képernyőn, maga a képfeldolgozás pedig vagy normál számítógépen, vagy erősebb táblákon (például Raspberry Pi) történt.
Miért nehéz?
A keresési lekérdezések népszerűségét az magyarázza, hogy az OpenCV a legnépszerűbb számítógépes látáskönyvtár, ami azt jelenti, hogy több fejlesztő ismeri, és az asztali számítógépre alkalmas kód mikrokontrolleren való futtatása nagyban leegyszerűsíti a fejlesztési folyamatot. De miért nincsenek még mindig népszerű kész receptek a probléma megoldására?
Az OpenCV kis kendőkön való használatának problémája két jellemzőhöz kapcsolódik:
- Ha a könyvtárat még minimális modulkészlettel is összeállítja, egyszerűen nem fog beleférni ugyanazon STM32F7Discovery flash memóriájába (még az operációs rendszer figyelembevétele nélkül sem) a nagyon nagy kód miatt (több megabájt utasítás)
- Maga a könyvtár C++ nyelven van írva, ami azt jelenti
- Támogatás szükséges a pozitív futásidőhöz (kivételek stb.)
- Kevés támogatás a LibC/Posix-hoz, amely általában megtalálható a beágyazott rendszerek operációs rendszerében – szüksége van egy szabványos plusz könyvtárra és egy szabványos STL sablonkönyvtárra (vektor stb.)
Portolás az Emboxba
Szokás szerint, mielőtt bármilyen programot portolnánk az operációs rendszerre, érdemes megpróbálni a fejlesztők által tervezett formában felépíteni. A mi esetünkben ezzel nincs probléma - a forráskód megtalálható
A jó hír az, hogy az OpenCV statikus könyvtárként is felépíthető a dobozból, ami megkönnyíti a portolást. Összegyűjtünk egy könyvtárat egy szabványos konfigurációval, és megnézzük, mennyi helyet foglalnak el. Minden modul külön könyvtárban van gyűjtve.
> 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 is látszik, a .bss és a .data nem foglal sok helyet, de a kód több mint 70 MiB. Nyilvánvaló, hogy ha ez statikusan kapcsolódik egy adott alkalmazáshoz, akkor a kód kevesebb lesz.
Próbáljunk meg minél több modult kidobni, hogy egy minimális példa kerüljön összeállításra (ami például egyszerűen kiadja az OpenCV verziót), így nézzük cmake .. -LA
és kapcsoljon ki mindent, ami kikapcsol.
-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)
Ez egyrészt csak egy modulja a könyvtárnak, másrészt ez a fordítóprogram kódméretre való optimalizálása nélkül (-Os
). ~3 MiB kód még elég sok, de már reményt ad a sikerre.
Futtassa az emulátort
Sokkal egyszerűbb a hibakeresés az emulátoron, ezért először győződjön meg arról, hogy a könyvtár működik qemu-n. Emulált platformként az Integrator / CP-t választottam, mert egyrészt ez is ARM, másrészt az Embox támogatja a grafikus kimenetet ezen a platformon.
Az Embox rendelkezik egy külső könyvtárak felépítésének mechanizmusával, ennek használatával az OpenCV-t modulként adjuk át (a "minimális" felépítéshez ugyanazokat a lehetőségeket adjuk át statikus könyvtárak formájában), majd hozzáadok egy egyszerű alkalmazást, amely így néz ki:
version.cpp:
#include <stdio.h>
#include <opencv2/core/utility.hpp>
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}
Összeállítjuk a rendszert, lefuttatjuk - megkapjuk a várt kimenetet.
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 включены в сборку и т.п.>
A következő lépés egy példa futtatása, lehetőleg a fejlesztők által kínált szabványos példák egyike.
A példát kissé át kellett írni, hogy a kép közvetlenül a keretpufferben jelenjen meg az eredménnyel. Ezt meg kellett tennem, mert. funkció imshow()
képes képeket rajzolni a QT, GTK és Windows felületeken keresztül, ami természetesen az STM32 konfigurációjában biztosan nem lesz benne. Valójában a QT STM32F7Discovery-n is futtatható, de erről egy másik cikkben lesz szó 🙂
Rövid tisztázás után, hogy az éldetektor eredményét milyen formátumban tároljuk, képet kapunk.
eredeti kép
Eredmény
STM32F7Discovery-n fut
A 32F746GDISCOVERY-n több hardveres memóriarész található, amelyeket így vagy úgy használhatunk
- 320KiB RAM
- 1 MiB vaku a képhez
- 8 MiB SDRAM
- 16 MiB QSPI NAND vaku
- microSD kártya nyílás
Az SD-kártya használható képek tárolására, de egy minimális példa futtatása kapcsán ez nem túl hasznos.
A kijelző felbontása 480×272, ami azt jelenti, hogy a framebuffer memória 522 240 bájt lesz 32 bites mélységben, pl. ez több, mint a RAM mérete, így a framebuffer és a kupac (amelyre szükség lesz, beleértve az OpenCV-t is, a képek és a segédstruktúrák adatainak tárolására) az SDRAM-ban lesz, minden más (memória a veremekhez és egyéb rendszerigényekhez) ) a RAM-ba kerül.
Ha vesszük az STM32F7Discovery minimális konfigurációját (kidobjuk a teljes hálózatot, az összes parancsot, a veremeket a lehető legkisebbre állítjuk stb.) és hozzáadjuk az OpenCV-t példákkal, akkor a szükséges memória a következő lesz:
text data bss dec hex filename
2876890 459208 312736 3648834 37ad42 build/base/bin/embox
Azok számára, akik nem nagyon ismerik, hogy melyik szakasz hova megy, elmagyarázom: be .text
и .rodata
utasítások és konstansok (nagyjából szólva csak olvasható adatok) rejlenek .data
az adatok változtathatók, .bss
vannak "nullált" változók, amelyeknek azonban hely kell (ez a rész "megy" a RAM-ba).
A jó hír az .data
/.bss
illeszkednie kell, de azzal .text
az a baj, hogy csak 1MiB memória van a képhez. Ki lehet dobni .text
a képet a példából és olvassa be például az SD kártyáról a memóriába indításkor, de a fruits.png körülbelül 330KiB súlyú, így ez nem oldja meg a problémát: a legtöbb .text
OpenCV kódból áll.
Összességében csak egy dolog van hátra - a kód egy részének betöltése egy QSPI flash-re (speciális működési módja van a memória rendszerbuszhoz való hozzárendeléséhez, így a processzor közvetlenül hozzáférhet ezekhez az adatokhoz). Ebben az esetben egy probléma adódik: egyrészt a QSPI flash meghajtó memóriája nem érhető el azonnal az eszköz újraindítása után (külön kell inicializálni a memória-leképezett módot), másodszor pedig ezt a memóriát nem tudja „flash” egy ismerős bootloader.
Ennek eredményeként úgy döntöttek, hogy az összes kódot QSPI-ben kapcsolják össze, és egy saját írású betöltővel frissítik, amely megkapja a szükséges bináris fájlt TFTP-n keresztül.
Eredmény
Körülbelül egy éve jelent meg az ötlet, hogy ezt a könyvtárat áthelyezzük az Emboxra, de különféle okok miatt újra és újra elhalasztották. Az egyik a libstdc++ és a szabványos sablonkönyvtár támogatása. Az Embox C++ támogatásának problémája túlmutat ennek a cikknek a keretein, így itt csak annyit mondok el, hogy ezt a támogatást sikerült elérni a megfelelő mennyiségben ahhoz, hogy ez a könyvtár működjön 🙂
Végül ezeket a problémákat sikerült megoldani (legalább ahhoz, hogy az OpenCV példa működjön), és a példa futott. 40 hosszú másodpercig tart, amíg a tábla a Canny szűrővel határokat keres. Ez persze túl hosszú (vannak megfontolások, hogyan lehet ezt az ügyet optimalizálni, erről lehet majd külön cikket írni, ha sikerül).
A köztes cél azonban egy olyan prototípus létrehozása volt, amely megmutatja az OpenCV STM32-n való futtatásának alapvető lehetőségét, illetve ez a cél teljesült, hurrá!
tl;dr: lépésről lépésre
0: Töltse le az Embox-forrásokat, például:
git clone https://github.com/embox/embox && cd ./embox
1: Kezdjük egy bootloader összeállításával, ami "flash" egy QSPI flash meghajtót.
make confload-arm/stm32f7cube
Most konfigurálnia kell a hálózatot, mert. A képet TFTP-n keresztül töltjük fel. Az alaplap és a gazdagép IP-címének beállításához módosítania kell a conf/rootfs/network fájlt.
Konfigurációs példa:
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 gazdagép címe, ahonnan a kép betöltődik, address
- a testület címe.
Ezt követően összegyűjtjük a rendszerbetöltőt:
make
2: A rendszerbetöltő szokásos betöltése (elnézést a szójátékért) a fórumon - itt nincs semmi konkrét, ezt úgy kell megtenni, mint bármely más STM32F7Discovery alkalmazásnál. Ha nem tudod, hogyan kell csinálni, akkor olvashatsz róla
3: Kép fordítása OpenCV konfigurációval.
make confload-platform/opencv/stm32f7discovery
make
4: Kivonat az ELF szakaszokból a QSPI-be írandó 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 van egy szkript, ami ezt megteszi, így futtatható
./conf/qspi_objcopy.sh # Нужный бинарник -- build/base/bin/qspi.bin
5: Tftp használatával töltse le a qspi.bin.bin fájlt egy QSPI flash meghajtóra. A gazdagépen ehhez másolja a qspi.bin fájlt a tftp szerver gyökérmappájába (általában /srv/tftp/ vagy /var/lib/tftpboot/; a megfelelő kiszolgálóhoz csomagok elérhetők a legtöbb népszerű disztribúcióban, általában ún. tftpd vagy tftp-hpa, néha meg kell tennie systemctl start tftpd.service
kezdeni).
# вариант для tftpd
sudo cp build/base/bin/qspi.bin /srv/tftp
# вариант для tftp-hpa
sudo cp build/base/bin/qspi.bin /var/lib/tftpboot
Az Emboxon (azaz a rendszerbetöltőben) a következő parancsot kell végrehajtania (feltételezzük, hogy a szerver címe 192.168.2.1):
embox> qspi_loader qspi.bin 192.168.2.1
6: paranccsal goto
be kell "ugrani" a QSPI memóriába. Az adott hely a kép hivatkozási módjától függően változik, ezt a címet láthatja a paranccsal mem 0x90000000
(a kezdőcím a kép második 32 bites szavába illeszkedik); meg kell jelölnie a verem is -s
, a verem címe 0x90000000, például:
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
это адрес это адрес
стэка первой
инструкции
embox>goto -i 0x9000c27f -s 0x20023200 # Флаг -i нужен чтобы запретить прерывания во время инициализации системы
< Начиная отсюда будет вывод не загрузчика, а образа с OpenCV >
7: Indítás
embox> edges 20
és élvezze a 40 másodperces határkeresést 🙂
Ha valami elromlik - írja be a problémát
Forrás: will.com