Mən əməliyyat sisteminin tərtibatçılarından biriyəm
Axtarış motoruna “STM32 lövhəsində OpenCV” kimi bir şey yazsanız, bu kitabxanadan STM32 lövhələrində və ya digər mikrokontrolörlərdə istifadə etməkdə maraqlı olan kifayət qədər adam tapa bilərsiniz.
Bir neçə video var ki, adından asılı olaraq nəyin lazım olduğunu nümayiş etdirməlidir, lakin adətən (gördüyüm bütün videolarda) STM32 lövhəsində yalnız kameradan görüntü alınır və nəticə ekranda göstərilirdi, və görüntünün işlənməsi ya adi kompüterdə, ya da daha güclü lövhələrdə (məsələn, Raspberry Pi) həyata keçirilirdi.
Niyə çətindir?
Axtarış sorğularının populyarlığı onunla izah olunur ki, OpenCV ən populyar kompüter görmə kitabxanasıdır, bu isə o deməkdir ki, onunla daha çox tərtibatçı tanışdır və mikrokontrollerdə iş masası üçün hazır kodu işlətmək imkanı inkişaf prosesini xeyli asanlaşdırır. Bəs niyə bu problemi həll etmək üçün hələ də populyar hazır reseptlər yoxdur?
Kiçik şallarda OpenCV-dən istifadə problemi iki xüsusiyyətlə bağlıdır:
- Kitabxananı hətta minimal modul dəsti ilə tərtib etsəniz, o, çox böyük kod (bir neçə meqabayt təlimat) səbəbindən eyni STM32F7Discovery-nin fləş yaddaşına (ƏS-ni nəzərə almadan belə) uyğun gəlməyəcək.
- Kitabxananın özü C++ dilində yazılmışdır, yəni
- Müsbət iş vaxtı üçün dəstəyə ehtiyac var (istisnalar və s.)
- Adətən quraşdırılmış sistemlər üçün ƏS-də tapılan LibC/Posix üçün az dəstək - sizə standart plus kitabxanası və standart STL şablon kitabxanası (vektor və s.) lazımdır.
Embox-a daşınır
Həmişə olduğu kimi, hər hansı bir proqramı əməliyyat sisteminə köçürməzdən əvvəl onu tərtibatçıların nəzərdə tutduğu formada qurmağa çalışmaq yaxşı olar. Bizim vəziyyətimizdə bununla bağlı heç bir problem yoxdur - mənbə kodu burada tapıla bilər
Yaxşı xəbər budur ki, OpenCV qutudan kənarda statik kitabxana kimi qurula bilər, bu da daşımağı asanlaşdırır. Standart konfiqurasiya ilə bir kitabxana toplayırıq və onların nə qədər yer tutduğunu görürük. Hər bir modul ayrıca kitabxanada toplanır.
> 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)
Sonuncu sətirdən göründüyü kimi, .bss və .data çox yer tutmur, lakin kod 70 MiB-dən çoxdur. Aydındır ki, bu, müəyyən bir tətbiqə statik olaraq bağlıdırsa, kod daha az olacaq.
Mümkün qədər çox modul atmağa çalışaq ki, minimal bir nümunə yığılsın (məsələn, sadəcə OpenCV versiyasını çıxaracaq), ona görə də baxırıq. cmake .. -LA
və sönən hər şeyi seçimlərdə söndürün.
-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)
Bir tərəfdən, bu, kitabxananın yalnız bir moduludur, digər tərəfdən, kod ölçüsü üçün kompilyator optimallaşdırması olmadan (-Os
). ~3 MiB kod hələ də kifayət qədər çoxdur, lakin artıq uğur üçün ümid verir.
Emulatorda işə salın
Emulatorda debug etmək daha asandır, ona görə də əvvəlcə kitabxananın qemu üzərində işlədiyinə əmin olun. Təqlid edilmiş bir platforma olaraq Integrator / CP-ni seçdim, çünki birincisi, o, həm də ARM-dir, ikincisi, Embox bu platforma üçün qrafik çıxışı dəstəkləyir.
Embox-da xarici kitabxanaların qurulması mexanizmi var, ondan istifadə edərək biz OpenCV-ni modul kimi əlavə edirik (statik kitabxanalar şəklində "minimal" qurmaq üçün bütün eyni variantları keçərək), bundan sonra bu kimi görünən sadə bir proqram əlavə edirəm:
version.cpp:
#include <stdio.h>
#include <opencv2/core/utility.hpp>
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}
Sistemi yığırıq, işə salırıq - gözlənilən nəticəni alırıq.
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 включены в сборку и т.п.>
Növbəti addım bəzi nümunələri, tercihen tərtibatçıların özləri tərəfindən təklif olunan standart nümunələrdən birini işə salmaqdır.
Nəticəni birbaşa çərçivə buferində göstərmək üçün nümunə bir qədər yenidən yazılmalıdır. Bunu etməli idim, çünki. funksiyası imshow()
QT, GTK və Windows interfeysləri vasitəsilə şəkillər çəkə bilər, bu, əlbəttə ki, STM32 üçün konfiqurasiyada olmayacaqdır. Əslində, QT STM32F7Discovery-də də işlədilə bilər, lakin bu başqa məqalədə müzakirə olunacaq 🙂
Kənar detektorun nəticəsinin hansı formatda saxlandığını qısa bir şəkildə aydınlaşdırdıqdan sonra bir şəkil alırıq.
orijinal şəkil
Nəticə
STM32F7Discovery üzərində işləyir
32F746GDISCOVERY-də bu və ya digər şəkildə istifadə edə biləcəyimiz bir neçə hardware yaddaş bölməsi var.
- 320KiB RAM
- Şəkil üçün 1MiB flaş
- 8MiB SDRAM
- 16MiB QSPI NAND Flash
- microSD kart yuvası
Şəkilləri saxlamaq üçün SD kartdan istifadə etmək olar, lakin minimal nümunəni işə salmaq kontekstində bu çox faydalı deyil.
Displey 480×272 təsvir ölçüsünə malikdir, yəni çərçivə buferinin yaddaşı 522 bit dərinlikdə 240 32 bayt olacaq, yəni. bu, RAM-ın ölçüsündən çoxdur, buna görə də çərçivə buferi və yığın (o cümlədən, OpenCV üçün təsvirlər və köməkçi strukturlar üçün məlumatların saxlanması üçün tələb olunacaq) SDRAM-da yerləşəcək, qalan hər şey (stoklar üçün yaddaş və digər sistem ehtiyacları) ) RAM-a gedəcək.
STM32F7Discovery üçün minimum konfiqurasiyanı götürsək (bütün şəbəkəni, bütün əmrləri atın, yığınları mümkün qədər kiçik edin və s.) və orada nümunələrlə OpenCV ə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/embox
Hansı bölmələrin hara getdiyi ilə çox tanış olmayanlar üçün izah edəcəyəm: in .text
и .rodata
təlimatlar və sabitlər (təxminən desək, yalnız oxunan məlumatlar) yatır .data
məlumat dəyişkəndir, .bss
"nulled" dəyişənlər var, buna baxmayaraq, bir yerə ehtiyac var (bu bölmə RAM-a "gedəcək").
Yaxşı xəbər budur .data
/.bss
uyğun olmalıdır, lakin ilə .text
problem ondadır ki, görüntü üçün cəmi 1MiB yaddaş var. Çölə atmaq olar .text
nümunədəki şəkli və onu oxuyun, məsələn, başlanğıc zamanı SD kartdan yaddaşa köçürün, lakin fruits.png təxminən 330KiB ağırlığındadır, buna görə də bu problemi həll etməyəcək: çoxu .text
OpenCV kodundan ibarətdir.
Ümumiyyətlə, yalnız bir şey qalır - kodun bir hissəsini QSPI flaşına yükləmək (o, yaddaşı sistem avtobusuna köçürmək üçün xüsusi iş rejiminə malikdir ki, prosessor bu məlumatlara birbaşa daxil ola bilsin). Bu halda problem yaranır: birincisi, QSPI fləş sürücüsünün yaddaşı cihaz yenidən işə salındıqdan dərhal sonra mövcud deyil (yaddaş xəritəli rejimini ayrıca işə salmalısınız), ikincisi, bu yaddaşı "yanıb-söndürə" bilməzsiniz. tanış bootloader.
Nəticədə, QSPI-də bütün kodu əlaqələndirmək və TFTP vasitəsilə tələb olunan ikili faylı alacaq öz-özünə yazılmış yükləyici ilə flaş etmək qərara alındı.
Nəticə
Bu kitabxananı Embox-a köçürmək ideyası təxminən bir il əvvəl ortaya çıxdı, lakin müxtəlif səbəblərə görə dəfələrlə təxirə salındı. Onlardan biri libstdc++ və standart şablon kitabxanasına dəstəkdir. Embox-da C++ dəstəyi problemi bu məqalənin əhatə dairəsi xaricindədir, ona görə də burada yalnız deyəcəm ki, biz bu dəstəyi bu kitabxananın işləməsi üçün lazımi miqdarda əldə edə bilmişik 🙂
Sonda bu problemlər aradan qaldırıldı (ən azı OpenCV nümunəsinin işləməsi üçün kifayətdir) və nümunə işə salındı. Lövhənin Canny filtrindən istifadə edərək sərhədləri axtarması 40 uzun saniyə çəkir. Bu, əlbəttə ki, çox uzundur (bu məsələnin optimallaşdırılmasına dair mülahizələr var, uğur qazanacağı təqdirdə bu barədə ayrıca məqalə yazmaq mümkün olacaq).
Bununla belə, aralıq məqsəd OpenCV-nin STM32-də işləməsinin fundamental imkanlarını göstərəcək prototip yaratmaq idi, müvafiq olaraq bu məqsədə nail olundu, yaş!
tl; dr: addım-addım təlimatlar
0: Embox mənbələrini bu kimi yükləyin:
git clone https://github.com/embox/embox && cd ./embox
1: QSPI flash sürücüsünü "yandıracaq" yükləyicini yığmaqla başlayaq.
make confload-arm/stm32f7cube
İndi şəbəkəni konfiqurasiya etməlisiniz, çünki. Şəkili TFTP vasitəsilə yükləyəcəyik. Lövhə və host IP ünvanlarını təyin etmək üçün siz conf/rootfs/şəbəkəni redaktə etməlisiniz.
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:02
gateway
- şəklin yüklənəcəyi host ünvanı, address
- şuranın ünvanı.
Bundan sonra yükləyicini toplayırıq:
make
2: Bootloaderin adi lövhədə yüklənməsi (söz üçün üzr istəyirik) - burada konkret heç nə yoxdur, bunu STM32F7Discovery üçün hər hansı digər proqram kimi etmək lazımdır. Bunu necə edəcəyinizi bilmirsinizsə, bu barədə oxuya bilərsiniz
3: OpenCV üçün konfiqurasiya ilə təsvirin tərtib edilməsi.
make confload-platform/opencv/stm32f7discovery
make
4: QSPI-yə qspi.bin-ə yazılacaq ELF bölmələrindən çıxarış
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
conf qovluğunda bunu edən bir skript var, ona görə də onu işə sala bilərsiniz
./conf/qspi_objcopy.sh # Нужный бинарник -- build/base/bin/qspi.bin
5: tftp istifadə edərək, qspi.bin.bin faylını QSPI flash sürücüsünə endirin. Bunu etmək üçün hostda qspi.bin faylını tftp serverinin kök qovluğuna kopyalayın (adətən /srv/tftp/ və ya /var/lib/tftpboot/; müvafiq server üçün paketlər adətən adlanan ən populyar paylamalarda mövcuddur. tftpd və ya tftp-hpa, bəzən etməlisən systemctl start tftpd.service
başlamaq).
# вариант для tftpd
sudo cp build/base/bin/qspi.bin /srv/tftp
# вариант для tftp-hpa
sudo cp build/base/bin/qspi.bin /var/lib/tftpboot
Embox-da (yəni yükləyicidə) aşağıdakı əmri yerinə yetirməlisiniz (biz güman edirik ki, server 192.168.2.1 ünvanına malikdir):
embox> qspi_loader qspi.bin 192.168.2.1
6: Əmrlə goto
siz QSPI yaddaşına "atlamalısınız". Xüsusi yer şəklin necə bağlanmasından asılı olaraq dəyişəcək, bu ünvanı əmrlə görə bilərsiniz mem 0x90000000
(başlanğıc ünvanı şəklin ikinci 32 bitlik sözünə uyğun gəlir); siz həmçinin yığını işarələməlisiniz -s
, yığın ünvanı 0x90000000-dadır, məsələn:
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
это адрес это адрес
стэка первой
инструкции
embox>goto -i 0x9000c27f -s 0x20023200 # Флаг -i нужен чтобы запретить прерывания во время инициализации системы
< Начиная отсюда будет вывод не загрузчика, а образа с OpenCV >
7: işə salın
embox> edges 20
və 40 saniyəlik sərhəd axtarışından həzz alın 🙂
Bir şey səhv olarsa - bir problem yazın
Mənbə: www.habr.com