Mi estas unu el la programistoj de la operaciumo
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
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.
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.
originala bildo
rezulto
Funkcianta sur STM32F7Discovery
Sur 32F746GDISCOVERY estas pluraj aparataj memorsekcioj, kiujn ni povas uzi unumaniere aŭ alian.
- 320 KiB RAM
- 1MiB-fulmo por bildo
- 8 MiB SDRAM
- 16MiB QSPI NAND Flash
- 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).
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
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
fonto: www.habr.com