OpenCV sur STM32F7-Discovery

OpenCV sur STM32F7-Discovery Je suis l'un des développeurs du système d'exploitation Embox, et dans cet article, je vais parler de la façon dont j'ai réussi à exécuter OpenCV sur la carte STM32746G.

Si vous tapez quelque chose comme "OpenCV sur la carte STM32" dans un moteur de recherche, vous pouvez trouver un certain nombre de personnes intéressées par l'utilisation de cette bibliothèque sur les cartes STM32 ou d'autres microcontrôleurs.
Il existe plusieurs vidéos qui, à en juger par le nom, devraient démontrer ce qui est nécessaire, mais généralement (dans toutes les vidéos que j'ai vues) sur la carte STM32, seule l'image a été reçue de la caméra et le résultat a été affiché à l'écran, et le traitement d'image lui-même a été effectué soit sur un ordinateur ordinaire, soit sur des cartes plus puissantes (par exemple, Raspberry Pi).

Pourquoi est-ce difficile?

La popularité des requêtes de recherche s'explique par le fait qu'OpenCV est la bibliothèque de vision par ordinateur la plus populaire, ce qui signifie que davantage de développeurs la connaissent, et la possibilité d'exécuter du code prêt pour le bureau sur un microcontrôleur simplifie grandement le processus de développement. Mais pourquoi n'y a-t-il toujours pas de recettes prêtes à l'emploi populaires pour résoudre ce problème?

Le problème d'utilisation d'OpenCV sur de petits châles est lié à deux fonctionnalités :

  • Si vous compilez la bibliothèque même avec un ensemble minimal de modules, elle ne rentrera tout simplement pas dans la mémoire flash du même STM32F7Discovery (même sans tenir compte du système d'exploitation) en raison d'un code très volumineux (plusieurs mégaoctets d'instructions)
  • La bibliothèque elle-même est écrite en C++, ce qui signifie
    • Besoin de support pour l'exécution positive (exceptions, etc.)
    • Peu de support pour LibC/Posix, qui se trouve généralement dans les systèmes d'exploitation pour les systèmes embarqués - vous avez besoin d'une bibliothèque standard plus et d'une bibliothèque de modèles STL standard (vecteur, etc.)

Portage vers Embox

Comme d'habitude, avant de porter un programme sur le système d'exploitation, c'est une bonne idée d'essayer de le construire sous la forme dans laquelle les développeurs l'ont prévu. Dans notre cas, cela ne pose aucun problème - le code source se trouve sur githabé, la bibliothèque est construite sous GNU/Linux avec le cmake habituel.

La bonne nouvelle est qu'OpenCV peut être construit comme une bibliothèque statique prête à l'emploi, ce qui facilite le portage. Nous collectons une bibliothèque avec une configuration standard et voyons combien d'espace ils occupent. Chaque module est collecté dans une bibliothèque séparée.

> 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)

Comme vous pouvez le voir sur la dernière ligne, .bss et .data ne prennent pas beaucoup de place, mais le code fait plus de 70 MiB. Il est clair que si cela est lié statiquement à une application spécifique, le code deviendra moins.

Essayons de jeter autant de modules que possible afin qu'un exemple minimal soit assemblé (qui, par exemple, produira simplement la version OpenCV), alors nous regardons cmake .. -LA et désactivez dans les options tout ce qui s'éteint.

        -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)

D'une part, ce n'est qu'un module de la bibliothèque, d'autre part, c'est sans optimisation du compilateur pour la taille du code (-Os). ~3 Mo de code, c'est encore beaucoup, mais cela donne déjà de l'espoir de succès.

Exécuter dans l'émulateur

Il est beaucoup plus facile de déboguer sur l'émulateur, alors assurez-vous d'abord que la bibliothèque fonctionne sur qemu. En tant que plate-forme émulée, j'ai choisi Integrator / CP, car premièrement, c'est aussi ARM, et deuxièmement, Embox prend en charge la sortie graphique pour cette plate-forme.

Embox a un mécanisme pour construire des bibliothèques externes, en l'utilisant nous ajoutons OpenCV en tant que module (passant toutes les mêmes options pour la construction "minimale" sous forme de bibliothèques statiques), après cela j'ajoute une application simple qui ressemble à ceci :

version.cpp:

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

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

    return 0;
}

Nous assemblons le système, l'exécutons - nous obtenons le résultat attendu.

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

L'étape suivante consiste à exécuter un exemple, de préférence l'un des standards proposés par les développeurs eux-mêmes. sur votre site. J'ai choisi détecteur de frontière canny.

L'exemple a dû être légèrement réécrit pour afficher l'image avec le résultat directement dans le frame buffer. Je devais le faire, parce que. fonction imshow() peut dessiner des images via les interfaces QT, GTK et Windows, qui, bien sûr, ne seront certainement pas dans la configuration de STM32. En fait, QT peut également être exécuté sur STM32F7Discovery, mais cela sera discuté dans un autre article 🙂

Après une brève clarification dans quel format le résultat du détecteur de bord est stocké, nous obtenons une image.

OpenCV sur STM32F7-Discovery

image originale

OpenCV sur STM32F7-Discovery

Résultat

Exécution sur STM32F7Discovery

Sur 32F746GDISCOVERY, il existe plusieurs sections de mémoire matérielle que nous pouvons utiliser d'une manière ou d'une autre

  1. 320 Ko de RAM
  2. Flash de 1 Mo pour l'image
  3. SDRAM 8 Mo
  4. Flash NAND QSPI 16 Mo
  5. Fente pour carte microSD

Une carte SD peut être utilisée pour stocker des images, mais dans le cadre de l'exécution d'un exemple minimal, cela n'est pas très utile.
L'affichage a une résolution de 480 × 272, ce qui signifie que la mémoire du framebuffer sera de 522 240 octets à une profondeur de 32 bits, c'est-à-dire c'est plus que la taille de la RAM, donc le framebuffer et le tas (qui seront nécessaires, y compris pour OpenCV, pour stocker les données des images et des structures auxiliaires) seront situés dans la SDRAM, tout le reste (mémoire pour les piles et autres besoins du système ) ira dans la RAM.

Si nous prenons la configuration minimale pour STM32F7Discovery (jetons tout le réseau, toutes les commandes, rendons les piles aussi petites que possible, etc.) et y ajoutons OpenCV avec des exemples, la mémoire requise sera la suivante :

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

Pour ceux qui ne savent pas très bien quelles sections vont où, je vais expliquer : dans .text и .rodata les instructions et les constantes (en gros, données en lecture seule) résident dans .data les données sont modifiables, .bss il y a des variables "nullées", qui ont néanmoins besoin d'une place (cette section "ira" dans la RAM).

La bonne nouvelle est que .data/.bss devrait convenir, mais avec .text le problème est qu'il n'y a que 1 Mo de mémoire pour l'image. Peut être jeté .text l'image de l'exemple et lisez-la, par exemple, de la carte SD dans la mémoire au démarrage, mais fruits.png pèse environ 330KiB, donc cela ne résoudra pas le problème : la plupart .text se compose du code OpenCV.

Dans l'ensemble, il ne reste qu'une seule chose - charger une partie du code sur un flash QSPI (il a un mode de fonctionnement spécial pour mapper la mémoire sur le bus système, afin que le processeur puisse accéder directement à ces données). Dans ce cas, un problème survient: premièrement, la mémoire d'un lecteur flash QSPI n'est pas disponible immédiatement après le redémarrage de l'appareil (vous devez initialiser séparément le mode mappé en mémoire), et deuxièmement, vous ne pouvez pas "flasher" cette mémoire avec un chargeur de démarrage familier.

En conséquence, il a été décidé de lier tout le code dans QSPI et de le flasher avec un chargeur auto-écrit qui recevra le binaire requis via TFTP.

Résultat

L'idée de porter cette bibliothèque sur Embox est apparue il y a environ un an, mais elle a été maintes et maintes fois reportée pour diverses raisons. L'un d'eux est la prise en charge de libstdc++ et de la bibliothèque de modèles standard. Le problème de la prise en charge de C++ dans Embox dépasse le cadre de cet article, donc ici je dirai seulement que nous avons réussi à obtenir cette prise en charge dans la bonne quantité pour que cette bibliothèque fonctionne 🙂

En fin de compte, ces problèmes ont été surmontés (au moins suffisamment pour que l'exemple OpenCV fonctionne) et l'exemple a fonctionné. Il faut 40 longues secondes au tableau pour rechercher les limites à l'aide du filtre Canny. Ceci, bien sûr, est trop long (il y a des considérations sur la façon d'optimiser cette matière, il sera possible d'écrire un article séparé à ce sujet en cas de succès).

OpenCV sur STM32F7-Discovery

Cependant, l'objectif intermédiaire était de créer un prototype qui montrera la possibilité fondamentale d'exécuter OpenCV sur STM32, respectivement, cet objectif a été atteint, hourra !

tl;dr : instructions étape par étape

0 : Téléchargez les sources d'Embox, comme ceci :

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

1 : Commençons par assembler un bootloader qui "flashera" un lecteur flash QSPI.

    make confload-arm/stm32f7cube

Maintenant, vous devez configurer le réseau, car. Nous allons télécharger l'image via TFTP. Pour définir les adresses IP de la carte et de l'hôte, vous devez modifier le fichier conf/rootfs/network.

Exemple de configuration:

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 - l'adresse de l'hôte à partir duquel l'image sera chargée, address - adresse du conseil.

Après cela, nous récupérons le bootloader :

    make

2 : Le chargement habituel du chargeur de démarrage (désolé pour le jeu de mots) sur la carte - il n'y a rien de spécifique ici, vous devez le faire comme pour toute autre application pour STM32F7Discovery. Si vous ne savez pas comment faire, vous pouvez lire à ce sujet ici.
3 : Compiler une image avec une configuration pour OpenCV.

    make confload-platform/opencv/stm32f7discovery
    make

4 : Extrait des sections ELF à écrire sur QSPI vers 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

Il y a un script dans le répertoire conf qui fait cela, vous pouvez donc l'exécuter

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

5 : À l'aide de tftp, téléchargez qspi.bin.bin sur un lecteur flash QSPI. Sur l'hôte, pour ce faire, copiez qspi.bin dans le dossier racine du serveur tftp (généralement /srv/tftp/ ou /var/lib/tftpboot/ ; les packages pour le serveur correspondant sont disponibles dans les distributions les plus populaires, généralement appelées tftpd ou tftp-hpa, il faut parfois faire systemctl start tftpd.service commencer).

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

Sur Embox (c'est-à-dire dans le chargeur de démarrage), vous devez exécuter la commande suivante (nous supposons que le serveur a l'adresse 192.168.2.1) :

    embox> qspi_loader qspi.bin 192.168.2.1

6 : Avec commande goto vous devez "sauter" dans la mémoire QSPI. L'emplacement spécifique variera en fonction de la façon dont l'image est liée, vous pouvez voir cette adresse avec la commande mem 0x90000000 (l'adresse de début rentre dans le second mot de 32 bits de l'image) ; vous devrez également marquer la pile -s, l'adresse de la pile est à 0x90000000, exemple :

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

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

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

7 : Lancement

    embox> edges 20

et profitez de la recherche frontalière de 40 secondes 🙂

Si quelque chose ne va pas - écrivez un problème dans notre référentiel, ou à la liste de diffusion [email protected], ou dans un commentaire ici.

Source: habr.com

Ajouter un commentaire