STM32F7-Discovery'de OpenCV

STM32F7-Discovery'de OpenCV İşletim sisteminin geliştiricilerinden biriyim. em kutusu, ve bu yazıda STM32746G kartında OpenCV çalıştırmayı nasıl başardığımdan bahsedeceğim.

Bir arama motoruna “STM32 panosunda OpenCV” gibi bir şey yazarsanız, bu kitaplığı STM32 panolarında veya diğer mikrodenetleyicilerde kullanmakla ilgilenen epeyce insan bulabilirsiniz.
İsme bakılırsa neyin gerekli olduğunu göstermesi gereken birkaç video var, ancak genellikle (gördüğüm tüm videolarda) STM32 panosunda kameradan yalnızca görüntü alındı ​​​​ve sonuç ekranda gösterildi. ve görüntü işlemenin kendisi ya normal bir bilgisayarda ya da daha güçlü panolarda (örneğin, Raspberry Pi) yapıldı.

Neden zor?

Arama sorgularının popülaritesi, OpenCV'nin en popüler bilgisayar görme kitaplığı olması gerçeğiyle açıklanmaktadır; bu, daha fazla geliştiricinin ona aşina olduğu anlamına gelir ve bir mikrodenetleyicide masaüstüne hazır kodu çalıştırma yeteneği, geliştirme sürecini büyük ölçüde basitleştirir. Ama neden hala bu sorunu çözmek için popüler hazır tarifler yok?

Küçük şallarda OpenCV kullanma sorunu iki özellikle ilgilidir:

  • Kütüphaneyi minimum modül setiyle bile derlerseniz, çok büyük bir kod (birkaç megabayt talimat) nedeniyle aynı STM32F7Discovery'nin flash belleğine (işletim sistemini hesaba katmadan bile) sığmayacaktır.
  • Kütüphanenin kendisi C++ ile yazılmıştır, yani
    • Pozitif çalışma zamanı için desteğe ihtiyacınız var (istisnalar vb.)
    • Genellikle gömülü sistemler için işletim sisteminde bulunan LibC/Posix için çok az destek - standart bir plus kitaplığına ve standart bir STL şablon kitaplığına (vektör vb.) ihtiyacınız var.

Embox'a Taşıma

Her zaman olduğu gibi, herhangi bir programı işletim sistemine taşımadan önce, onu geliştiricilerin tasarladığı biçimde oluşturmaya çalışmak iyi bir fikirdir. Bizim durumumuzda bununla ilgili bir sorun yok - kaynak kodu şu adreste bulunabilir: github, kitaplık her zamanki cmake ile GNU/Linux altında oluşturulmuştur.

İyi haber şu ki, OpenCV kutudan çıkar çıkmaz statik bir kitaplık olarak oluşturulabilir, bu da taşımayı kolaylaştırır. Standart bir yapılandırmaya sahip bir kitaplık topluyoruz ve ne kadar yer kapladıklarını görüyoruz. Her modül ayrı bir kütüphanede 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)

Son satırdan da görebileceğiniz gibi .bss ve .data fazla yer kaplamaz ancak kod 70 MiB'den fazladır. Bu, statik olarak belirli bir uygulamaya bağlanırsa, kodun daha az olacağı açıktır.

Minimal bir örnek oluşturmak için mümkün olduğu kadar çok modülü atmaya çalışalım (örneğin, basitçe OpenCV sürümünün çıktısını alacaktır), bu yüzden bakalım cmake .. -LA ve kapanan her şeyi seçeneklerde kapatı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 yandan bu, kitaplığın yalnızca bir modülüdür, öte yandan bu, kod boyutu için derleyici optimizasyonu içermez (-Os). ~3 MiB kod hala oldukça fazla, ancak şimdiden başarı için umut veriyor.

Öykünücüde çalıştır

Öykünücüde hata ayıklamak çok daha kolaydır, bu nedenle önce kitaplığın qemu üzerinde çalıştığından emin olun. Öykünülmüş bir platform olarak Integrator / CP'yi seçtim, çünkü birincisi, aynı zamanda ARM'dir ve ikincisi, Embox bu platform için grafik çıktısını destekler.

Embox'ın harici kitaplıklar oluşturmak için bir mekanizması var, onu kullanarak OpenCV'yi bir modül olarak ekliyoruz ("minimal" yapı için aynı seçenekleri statik kitaplıklar biçiminde geçirerek), bundan sonra şuna benzeyen basit bir uygulama ekliyorum:

version.cpp:

#include <stdio.h>
#include <opencv2/core/utility.hpp>

int main() {
    printf("OpenCV: %s", cv::getBuildInformation().c_str());

    return 0;
}

Sistemi kuruyoruz, çalıştırıyoruz - beklenen çıktıyı alıyoruz.

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 включены в сборку и т.п.>

Bir sonraki adım, tercihen geliştiricilerin kendileri tarafından sunulan standartlardan biri olan bir örnek çalıştırmaktır. sitenizde. Seçtim sınır dedektörü canny.

Görüntüyü doğrudan çerçeve arabelleğinde sonuçla birlikte görüntülemek için örneğin biraz yeniden yazılması gerekiyordu. Bunu yapmak zorundaydım çünkü. işlev imshow() QT, GTK ve Windows arayüzleri aracılığıyla, elbette kesinlikle STM32 yapılandırmasında olmayacak görüntüler çizebilir. Aslında QT, STM32F7Discovery üzerinde de çalıştırılabilir, ancak bu başka bir makalede ele alınacaktır 🙂

Kenar dedektörü sonucunun hangi formatta saklandığını kısaca açıkladıktan sonra karşımıza bir görüntü çıkıyor.

STM32F7-Discovery'de OpenCV

orijinal resim

STM32F7-Discovery'de OpenCV

sonuç

STM32F7Discovery'de çalışıyor

32F746GDISCOVERY'de öyle ya da böyle kullanabileceğimiz birkaç donanım belleği bölümü vardır.

  1. 320KiB RAM
  2. Görüntü için 1MiB flaş
  3. 8MiB SDRAM
  4. 16MiB QSPI NAND Flash
  5. microSD kart yuvası

Görüntüleri depolamak için bir SD kart kullanılabilir, ancak minimal bir örnek çalıştırma bağlamında bu pek kullanışlı değildir.
Ekran 480×272 çözünürlüğe sahiptir, yani çerçeve ara belleği 522 bit derinlikte 240 bayt olacaktır, yani. bu, RAM boyutundan daha fazladır, bu nedenle çerçeve arabelleği ve yığın (OpenCV dahil, görüntüler ve yardımcı yapılar için veri depolamak için gerekli olacaktır) SDRAM'de yer alacaktır, diğer her şey (yığınlar ve diğer sistem ihtiyaçları için bellek) ) RAM'e gidecektir.

STM32F7Discovery için minimum yapılandırmayı alıp (tüm ağı, tüm komutları atın, yığınları olabildiğince küçük yapın vb.) ve oradaki örneklerle OpenCV'yi eklersek, gerekli bellek aşağıdaki gibi olacaktır:

   text    data     bss     dec     hex filename
2876890  459208  312736 3648834  37ad42 build/base/bin/embox

Hangi bölümlerin nereye gittiğini pek bilmeyenler için açıklayayım: .text и .rodata talimatlar ve sabitler (kabaca söylemek gerekirse, salt okunur veriler) .data veriler değişebilir, .bss yine de bir yere ihtiyaç duyan "nulled" değişkenler vardır (bu bölüm RAM'e "gitecektir").

İyi haber şu ki .data/.bss uygun olmalı, ancak .text sorun şu ki, görüntü için yalnızca 1MiB bellek var. dışarı atılabilir .text örnekteki resmi okuyun ve örneğin başlangıçta SD karttan belleğe okuyun, ancak meyveler.png yaklaşık 330KiB ağırlığındadır, bu nedenle bu sorunu çözmez: çoğu .text OpenCV kodundan oluşur.

Genel olarak, geriye kalan tek bir şey var - kodun bir bölümünü bir QSPI flaşına yüklemek (işlemcinin bu verilere doğrudan erişebilmesi için belleği sistem veriyoluna eşlemek için özel bir çalışma modu vardır). Bu durumda, bir sorun ortaya çıkar: ilk olarak, bir QSPI flash sürücüsünün belleği, cihaz yeniden başlatıldıktan hemen sonra kullanılamaz (bellek eşlemeli modu ayrı olarak başlatmanız gerekir) ve ikincisi, bu belleği şu şekilde "flaşlayamazsınız": tanıdık bir önyükleyici.

Sonuç olarak, QSPI'deki tüm kodun bağlanmasına ve gerekli ikili dosyayı TFTP aracılığıyla alacak olan kendi kendine yazılan bir yükleyiciye yüklemeye karar verildi.

sonuç

Bu kütüphaneyi Embox'a taşıma fikri yaklaşık bir yıl önce ortaya çıktı, ancak çeşitli nedenlerle defalarca ertelendi. Bunlardan biri libstdc++ ve standart şablon kitaplığı desteğidir. Embox'ta C++ desteği sorunu bu makalenin kapsamı dışındadır, bu yüzden burada sadece bu desteği bu kitaplığın çalışması için doğru miktarda sağlamayı başardığımızı söyleyeceğim 🙂

Sonunda bu sorunlar aşıldı (en azından OpenCV örneğinin çalışmasına yetecek kadar) ve örnek çalıştı. Panonun Canny filtresini kullanarak sınırları araması 40 uzun saniye sürer. Bu elbette çok uzun (bu konunun nasıl optimize edileceğine dair düşünceler var, başarı durumunda bununla ilgili ayrı bir makale yazmak mümkün olacak).

STM32F7-Discovery'de OpenCV

Bununla birlikte, ara hedef, sırasıyla STM32'de OpenCV çalıştırmanın temel olasılığını gösterecek bir prototip oluşturmaktı, bu hedefe ulaşıldı, yaşasın!

tl; dr: adım adım talimatlar

0: Embox kaynaklarını şu şekilde indirin:

    git clone https://github.com/embox/embox && cd ./embox

1: Bir QSPI flash sürücüsünü "yanıp söndürecek" bir önyükleyici kurarak başlayalım.

    make confload-arm/stm32f7cube

Şimdi ağı yapılandırmanız gerekiyor, çünkü. Görüntüyü TFTP aracılığıyla yükleyeceğiz. Pano ve ana bilgisayar IP adreslerini ayarlamak için, conf/rootfs/network dosyasını düzenlemeniz gerekir.

Yapılandırma örneği:

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 - görüntünün yükleneceği ana bilgisayar adresi, address - kurulun adresi.

Bundan sonra, önyükleyiciyi topluyoruz:

    make

2: Önyükleyicinin tahtaya normal yüklenmesi (kelime oyunu için özür dilerim) - burada özel bir şey yok, bunu STM32F7Discovery için diğer herhangi bir uygulama gibi yapmanız gerekiyor. Bunu nasıl yapacağınızı bilmiyorsanız, bu konuda okuyabilirsiniz burada.
3: Bir görüntüyü OpenCV için bir yapılandırma ile derlemek.

    make confload-platform/opencv/stm32f7discovery
    make

4: QSPI'ye qspi.bin'e yazılacak ELF bölümlerinden alıntı

    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 dizininde bunu yapan bir betik var, onu çalıştırabilirsiniz.

    ./conf/qspi_objcopy.sh # Нужный бинарник -- build/base/bin/qspi.bin

5: tftp kullanarak qspi.bin.bin dosyasını bir QSPI flash sürücüsüne indirin. Ana bilgisayarda, bunu yapmak için qspi.bin dosyasını tftp sunucusunun kök klasörüne kopyalayın (genellikle /srv/tftp/ veya /var/lib/tftpboot/; karşılık gelen sunucu için paketler en popüler dağıtımlarda bulunur, genellikle tftpd veya tftp-hpa, bazen yapmanız gerekir systemctl start tftpd.service başlamak).

    # вариант для tftpd
    sudo cp build/base/bin/qspi.bin /srv/tftp
    # вариант для tftp-hpa
    sudo cp build/base/bin/qspi.bin /var/lib/tftpboot

Embox'ta (yani önyükleyicide), aşağıdaki komutu uygulamanız gerekir (sunucunun 192.168.2.1 adresine sahip olduğunu varsayıyoruz):

    embox> qspi_loader qspi.bin 192.168.2.1

6: Komut ile goto QSPI belleğine "atlamanız" gerekir. Belirli konum, görüntünün nasıl bağlandığına bağlı olarak değişecektir, bu adresi komutla görebilirsiniz. mem 0x90000000 (başlangıç ​​adresi görüntünün ikinci 32 bitlik kelimesine sığar); ayrıca yığını işaretlemeniz gerekecek -s, yığın adresi 0x90000000 konumundadır, örnek:

    embox>mem 0x90000000
    0x90000000:     0x20023200  0x9000c27f  0x9000c275  0x9000c275
                      ↑           ↑
              это адрес    это  адрес 
                стэка        первой
                           инструкции

    embox>goto -i 0x9000c27f -s 0x20023200 # Флаг -i нужен чтобы запретить прерывания во время инициализации системы

    < Начиная отсюда будет вывод не загрузчика, а образа с OpenCV >

7: Başlat

    embox> edges 20

ve 40 saniyelik sınır aramanın keyfini çıkarın 🙂

Bir şeyler ters giderse - şuraya bir sorun yazın: depomuzveya posta listesine [e-posta korumalı], veya burada bir yorumda.

Kaynak: habr.com

Yorum ekle