Mən əməliyyat sistemi mühəndislərindən biriyəm , və bu yazıda STM32746G platasında OpenCV-ni necə işlətməyə müvəffəq olduğum barədə danışacağam.
Axtarış sisteminə "OpenCV on STM32 board" yazanda, bu kitabxananın STM32 platalarında və ya digər mikroidarəedicilərdə istifadəsi ilə maraqlananların sayı olduqca çoxdur.
Bir neçə video var ki, adından belə demək olar ki, bunu nümayiş etdirməlidir, amma adətən (gördüyüm bütün videolarda) STM32 platasında yalnız kamera ilə görüntü alınır və nəticə ekrana verilir, şəkil emalı ya adi bir kompüterdə, ya da daha güclü platalarda (məsələn, Raspberry Pi) edilir.
Niyə bu çətindir?
Axtarış sorğularının populyarlığı, OpenCV-nin ən məşhur kompüter görmə kitabxanası olmasıyla izah olunur, yəni bu kitabxana daha çox inkişaf etdirici tərəfindən tanınır və desktop üçün hazırlanmış kodu mikroidarəedici üzərində işlətmək inkişaf prosesini əhəmiyyətli dərəcədə asanlaşdırır. Amma hələ də niyə bu problemi həll etmək üçün məşhur hazır reseptlər yoxdur?
OpenCV-nin kiçik platalarda istifadəsi iki xüsusiyyətlə bağlıdır:
- Əgər kitabxananı minimal modullar dəsti ilə tərtib etsəniz, STM32F7Discovery-nin flash yaddaşına sadəcə sığmayacaq (hətta OS nəzərə alınmadan) çox böyük kodu (bir neçə megabayt göstərişləri) olduğu üçün.
- Kitabxana özü C++-da yazılıb, yəni
- C++ runtime dəstəyinə (istisnalar və s.) ehtiyac var.
- LibC/Posix üçün dəstəyin azlığı, adətən, inteqrasiya edilmiş sistemlər üçün OS-lərdə var — standart C++ kitabxanası və STL şablon kitabxanası (vector və s.) lazımdır.
Embox-a portlama
Adətən, hər hansı proqramı əməliyyat sisteminə portlamaqdan əvvəl, onu inkişaf etdiricilərin düşündüyü şəkildə toplamağa çalışmaq yaxşıdır. Bizim halda bununla bağlı bir problem olmur — mənbələri tapmaq olur, kitabxana adi cmake vasitəsilə GNU/Linux üçün tərtib edilir.
Yaxşı xəbər odur ki, OpenCV qutudan işlək vəziyyətdə statik kitabxana şəklində tərtib edilə bilər, bu da portlanmanı asanlaşdırır. Kitabxananı standart konfiqurasiyaya uyğun toplayırıq və nə qədər yer tutduğuna baxırıq. Hər bir modul ayrı bir kitabxana olaraq tərtib edilir.
> ölçü lib/*so --toplamlar
mətn məlumat bss dec hex fayl adı
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 (TOPLAMLAR)Son satırdan göründüyü kimi, .bss və .data çox yer tutmur, amma kod 70 MiB-dən çoxdur. Aydındır ki, bu, konkret bir tətbiqlə statik bağlama zamanı daha az kod olmasına səbəb olur.
Mümkün qədər çox modulu çıxarmağa çalışaq ki, minimal bir nümunə toplaya bilək (məsələn, sadəcə OpenCV versiyasını göstərəcək), buna görə diqqət edək. cmake .. -LA və seçimlərdə deaktiv edilən hər şeyi söndürürük.
-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> ölçü lib/libopencv_core.a --toplamlar
mətn məlumat bss dec hex fayl adı
3317069 36425 17987 3371481 3371d9 (TOPLAMLAR)Bir tərəfdən, bu yalnız bir moduldur, digər tərəfdən, bu tərtib olunan kod üçün optimallaşdırmadan irəlidədir (-Os). ~3 MiB kod — hələ də çoxdur, lakin uğur üçün ümid verməyə başlayır.
Emulyatorda işə salma
Emulyatorda debuq etmək daha asandır, buna görə əvvəlcə kitabxananın qemu-da işlədiyini təsdiq edək. Emulyasiya edilən platforma olaraq Integrator/CP-ni seçdim, çünki birincisi, bu da ARM-dir, ikincisi isə, Embox bu platforma üçün görüntü çıxışını dəstəkləyir.
Embox-da xarici kitabxanaların yığılması üçün bir mexanizm var, onun köməyi ilə OpenCV-ni modul kimi əlavə edirik (bütün eyni seçimləri istifadə edərək
version.cpp:
#include
#include
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}Sistemi yığıb işə salırıq — gözlənilən çıxışı alırıq.
root@embox:/#opencv_version
OpenCV:
OpenCV 4.0.1 üçün Ümumi konfiqurasiya =====================================
Versiya idarəsi: bd6927bdf-dirty
Platforma:
Zaman damğası: 2019-06-21T10:02:18Z
Ev sahibi: Linux 5.1.7-arch1-1-ARCH x86_64
Hədəf: Generic arm-unknown-none
CMake: 3.14.5
CMake generator: Unix Makefiles
CMake yığma aləti: /usr/bin/make
Konfiqurasiya: Debug
CPU/HW xüsusiyyətləri:
Əsas xətt:
istəklər: DETECT
deaktiv edilmiş: VFPV3 NEON
C/C++:
Dinamik kitabxana kimi yığıldımı?: HƏNSonrakı addım, hansısa bir nümunəni işə salmaqdır, ən yaxşısı isə, inkişaf etdiricilərin təklif etdiyi standart nümunələrdən biri olsun. . Mən seçdim .
Nümunəni bir az yenidən yazmaq lazım gəldi ki, nəticə görüntüsünü birbaşa frame buffer-da göstərsin. Bunu etmək lazım gəldi, çünki imshow() QT, GTK və Windows interfeysləri vasitəsilə görüntüləri çəkməyi bacarır ki, bunlar STM32 konfiqurasiyasında qurulmayacaq. Əslində, QT-nin də STM32F7Discovery-də çalışdırılması mümkündür, amma bu artıq başqa bir məqalədə müzakirə olunacaq 🙂
Qısa bir araşdırmadan sonra, sərhəd detektorunun iş nəticəsinin hansı formatda saxlandığını tədqiq etdik, görüntünü alırıq.

Əsas görüntü

Nəticə
STM32F7Discovery-də işə salmaq
32F746GDISCOVERY-də istifadə edə biləcəyimiz bir neçə aparat yaddaş bölməsi var.
- 320KiB RAM
- Şəkil üçün 1MiB flash yaddaş
- 8MiB SDRAM
- 16MiB QSPI NAND flaş
- microSD kartı üçün girişi
SD kartını şəkillərin saxlanması üçün istifadə etmək olar, lakin minimal nümunənin işə salınması kontekstində bu çox da faydalı deyil.
Ekranın çözünürlüğü 480×272-dir, bu isə frame buffer üçün 522 240 bayt yaddaş tələb edir, 32 bit dərinlikdə, yəni bu, RAM ölçüsündən daha çoxdur, beləliklə frame buffer və heap (OpenCV üçün məlumatları və köməkçi strukturları saxlamaq üçün lazım olacaq) SDRAM-da yerləşəcək, qalan hər şey (stack və digər sistem ehtiyacları üçün yaddaş) RAM-a yönləndiriləcək.
STM32F7Discovery üçün minimal konfiqurasiyanı (bütün şəbəkəni çıxarmaq, bütün komandaları, stakları mümkün qədər kiçik etmək və s.) götürsək və oraya OpenCV nümunələrini əlavə etsək, tələb olunan yaddaş aşağıdakı kimi olacaq:
text data bss dec hex filename
2876890 459208 312736 3648834 37ad42 build/base/bin/emboxHansı bölmələrin haraya yerləşdiyinə çox da aşina olmayanlar üçün izah edim: burada .text və .rodata instruction və konstantaların (qısaca, oxunmaz verilərin) olduğu yerdir, burada .data dəyişdirilə bilən verilərin olduğu yerdir, burada .bss "sıfırlanmış" dəyişkənlər yerləşir ki, buna baxmayaraq yerə ehtiyac var (bu bölmə RAM-a "gedəcək").
Yaxşı xəbər odur ki, .data/.bss yerləşməlidirlər, amma .text burası çətinlikdir — imic üçün yalnız 1MiB yaddaş var. Nümunəni istifadə edilən şəkli çıxarmaq və onu SD kartdan yükləmək mümkündür, amma fruits.png təxminən 330KiB-dir, buna görə bu problem çözülmür: əksər hissəsi .text OpenCV kodundan ibarətdir. .text Ümumi olaraq, yalnız bir şey qalır — kodun bir hissəsini QSPI flaşına yükləmək (onun sistem avtobusuna yaddaşı xəritələmə üçün xüsusi iş rejimi var, beləliklə prosessor bu məlumatlara birbaşa müraciət edə bilər). Bu zaman bir problem ortaya çıxır: birincisi, QSPI flaş yaddaşı cihazın yenidən başladılmasından dərhal sonra istifadə edilə bilmir (xüsusi memory-mapped rejimini ayrıca işə salmaq lazımdır), ikincisi, bu yaddaşı adi yükləyici ilə "yazmaq" olmaz.
По большому счёту, остаётся только одно — загрузка части кода на QSPI-флэшку (у неё есть спец. режим работы для мэпирования памяти на системную шину, так что процессор сможет обращаться к этим данным напрямую). При этом возникает проблема: во-первых, память QSPI-флэшки недоступна сразу после перезагрузки устройства (нужно отдельно инициализировать memory-mapped-режим), во-вторых, нельзя «прошить» эту память привычным загрузчиком.
Nəticədə, bütün kodu QSPI-yə bağlamaq qərarına gəldik və onu TFTP-dən lazımi binar faylı alacaq öz yükləyici proqramımızla yükləyəcəyik.
Nəticə
Bu kitabxananı Embox-a port etmək ideyası təxminən bir il əvvəl ortaya çıxdı, amma bir neçə səbəbə görə başdan-ayağa təxirə salındı. Bunlardan biri - libstdc++ və standart şablon kitabxanasının dəstəyi. C++-ın Embox-da dəstəklənməsi bu yazının çərçivəsindən kənardır, ona görə də yalnız deyim ki, bu kitabxana üçün lazım olan səviyyədə bu dəstəyi qazana bildik 🙂
Nəticədə, bu problemlər həll olundu (ən azı OpenCV nümunəsinin işləməsi üçün kifayət qədər), və nümunə işə düşdü. 40 uzun saniyə platanın Kənni filtrindən sərhədləri tapması üçün sərf olunur. Bu, əlbəttə ki, çox uzundur (bu məsələni optimallaşdırmaq üçün fikirlər var, uğur qazanarsaq, bununla bağlı ayrıca bir yazı yazmaq olar).

Yəni, müvəqqəti məqsəd OpenCV-nin STM32-də işlədiyini göstərən prototip yaratmaq idi, buna görə də bu məqsəd əldə edildi, yaşasın!
tl;dr: addım-addım təlimat
0: Embox-un mənbələrini yükləyirik, məsələn belə:
git clone https://github.com/embox/embox && cd ./embox1: QSPI-flash üçün yükləyicinin yığılmasına başlayacağıq.
make confload-arm/stm32f7cubeİndi şəbəkəni tənzimləmək lazımdır, çünki imici TFTP ilə yükləyəcəyik. Platanın və hostun IP ünvanlarını ayarlamaq üçün conf/rootfs/network faylını dəyişdirmək lazımdır.
Konfiqurasiya nümunəsi:
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 — imicin yüklənəcəyi hostun ünvanı, address — platanın ünvanı.
Bundan sonra yükləyicini yığırıq:
make2: Yükləyicinin (bu ifadəyə görə üzr istəyirəm) platanın üzərinə göndərilməsi — burada xüsusi bir şey yoxdur, bunu STM32F7Discovery üçün hər hansı digər tətbiq kimi etmək lazımdır. Əgər bunu necə edə biləcəyinizi bilmirsinizsə, bununla bağlı oxumaq olar. .
3: OpenCV üçün konfiqurasiya ilə imicin tərtibi.
make confload-platform/opencv/stm32f7discovery
make4: QSPI-yə yazılacaq ELF bölmələrinin çıxarılması, qspi.bin faylına.
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=.dataconf qovluğunda bunu edən bir skript var, ona görə də onu işə sala bilərsiniz.
./conf/qspi_objcopy.sh # Lazım olan binar fayl -- build/base/bin/qspi.bin5: TFTP ilə qspi.bin.bin faylını QSPI-flash-a yükləyirik. Hostda, bunun üçün qspi.bin faylını tftp serverinin kök qovluğuna (adi olaraq /srv/tftp/ və ya /var/lib/tftpboot/; müvafiq server üçün paketlər bir çox populyar distributivdə mövcuddur, adətən tftpd ya da tftp-hpa adlanır, bəzən başlamaq üçün bu əmri vermək lazımdır: systemctl start tftpd.service bunun üçün).
# вариант для tftpd
sudo cp build/base/bin/qspi.bin /srv/tftp
# вариант для tftp-hpa
sudo cp build/base/bin/qspi.bin /var/lib/tftpbootEmbox-da (yəni yükləyicidə) belə bir əmri yerinə yetirmək lazımdır (serverin ünvanının 192.168.2.1 olduğunu güman edirik):
embox> qspi_loader qspi.bin 192.168.2.16: Bu əmrlə goto QSPI-memoriya ilə "atlamaq" lazımdır. Konkret yer, şəkilin necə bağlandığına bağlı olaraq dəyişəcək, bu adresə baxmaq üçün komandanı istifadə edin. mem 0x90000000 (başlanğıc adresi görüntünün ikinci 32-bit sözünə yerləşir); həmçinin, yığın bayrağını təyin etmək lazım olacaq. -s, yığın adresi 0x90000000 ünvanında yerləşir, nümunə:
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
bu adres bu adres
yığın ilk
əmrin
embox>goto -i 0x9000c27f -s 0x20023200 # -i bayrağı, sistemi başlatarkən kəsilmələri qadağan etmək üçün lazımdır.7: Başlasaq
embox> edges 20və 40 saniyəlik sərhədlərin axtarışından həzz alırıq 🙂
Əgər bir şey səhv olarsa — bizim məsələlər sistemimizdə yaza bilərsiniz. , ya embox-devel@googlegroups.com ünvanına, ya da burada şərhlərdə.
Mənbə: habr.com
