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 expliquer comment j'ai réussi à faire fonctionner OpenCV sur la carte STM32746G.

Si vous tapez dans un moteur de recherche quelque chose comme « OpenCV sur carte STM32 », vous pouvez trouver pas mal de personnes intéressées par l'utilisation de cette bibliothèque sur des cartes STM32 ou d'autres microcontrôleurs.
Il y a plusieurs vidéos qui, d'après le titre, 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 il n'y avait que la capture d'image de la caméra et l'affichage du résultat sur l'écran, tandis que le traitement de l'image se faisait soit sur un ordinateur ordinaire, soit sur des cartes plus puissantes (comme un Raspberry Pi).

Pourquoi est-ce compliqué ?

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 plus de développeurs sont familiers avec elle, et la possibilité d'exécuter du code prêt à l'emploi pour les ordinateurs de bureau sur un microcontrôleur simplifie considérablement le processus de développement. Mais pourquoi n'existe-t-il toujours pas de recettes populaires prêtes à l'emploi pour résoudre ce problème ?

Le problème de l'utilisation d'OpenCV sur de petites cartes est lié à deux particularités :

  • Si l'on compile la bibliothèque même avec un ensemble minimal de modules, elle ne tiendra tout simplement pas dans la mémoire flash de la 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
    • Un support pour le runtime C++ (exceptions, etc.) est nécessaire.
    • Peu de soutien pour LibC/Posix, qui est généralement présent dans les systèmes d'exploitation pour systèmes embarqués — il faut la bibliothèque standard C++ et la bibliothèque standard de modèles STL (vector, etc.).

Portage sur Embox

Comme d'habitude, avant de porter des programmes sur un système d'exploitation, il est bon d'essayer de les assembler dans la forme que les développeurs avaient prévue. Dans notre cas, cela ne pose aucun problème — les sources peuvent être trouvées sur GitHub, la bibliothèque s'assemble sous GNU/Linux avec un cmake ordinaire.

D'une bonne nouvelle — OpenCV peut être construit en tant que bibliothèque statique dès la sortie de la boîte, ce qui facilite le portage. Assemblons la bibliothèque avec la configuration standard et voyons combien d'espace elle occupe. Chaque module est assemblé en une bibliothèque distincte.

> taille lib/*so --totals
   texte    données     bss     déc     hex fichier
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 on peut le voir dans la dernière ligne, .bss et .data occupent peu d'espace, mais le code dépasse 70 Mo. Il est évident que si cela est lié statiquement à une application spécifique, la taille du code sera réduite.

Tentrons d'éliminer le plus de modules possible pour créer un exemple minimal (qui, par exemple, affichera simplement la version d'OpenCV), donc observons cmake .. -LA et désactivons dans les options tout ce qui peut être désactivé.

        -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

> taille lib/libopencv_core.a --totals
   texte    données     bss     déc     hex fichier
3317069   36425   17987 3371481  3371d9 (TOTALS)

D'un côté, c'est seulement un module de la bibliothèque, de l'autre côté, c'est sans optimisation du compilateur pour la taille du code (-Os). ~3 Mo de code — cela reste beaucoup, mais donne déjà de l'espoir pour le succès.

Exécution dans un émulateur

Le débogage sur un émulateur est beaucoup plus simple, donc commençons par vérifier que la bibliothèque fonctionne sur qemu. J'ai choisi la plateforme émulée Integrator/CP, car premièrement, c'est aussi ARM, et deuxièmement, Embox supporte l'affichage graphique pour cette plateforme.

Dans Embox, il y a un mécanisme pour construire des bibliothèques externes, grâce auquel nous ajoutons OpenCV en tant que module (en transmettant toutes les mêmes options pour une 'construction minimale' sous forme de bibliothèques statiques), après cela, j'ajoute une application très simple qui se présente comme suit :

version.cpp:

#include 
#include 

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

    return 0;
}

Nous compilons le système, le démarrons — nous obtenons la sortie attendue.

root@embox:\/ #opencv_version
OpenCV:
Configuration générale pour OpenCV 4.0.1 =====================================
  Contrôle de version :               bd6927bdf-dirty

  Plateforme:
    Horodatage :                   2019-06-21T10:02:18Z
    Hôte :                        Linux 5.1.7-arch1-1-ARCH x86_64
    Cible :                      Generic arm-unknown-none
    CMake :                       3.14.5
    Générateur CMake :             Unix Makefiles
    Outil de construction CMake :            \/usr\/bin\/make
    Configuration :               Debug

  Fonctionnalités CPU/HW :
    Baseline :
      demandé :                 DETECT
      désactivé :                  VFPV3 NEON

  C/C++:
    Construit comme des bibliothèques dynamiques ? :      NON

La prochaine étape consiste à exécuter un exemple, de préférence l'un des exemples standards proposés par les développeurs eux-mêmes sur leur site. J'ai choisi le détecteur de bords de Canny.

L'exemple a dû être légèrement modifié pour afficher l'image avec le résultat directement dans le buffer de trame. Cela a été nécessaire, car la fonction imshow() peut dessiner des images via les interfaces QT, GTK et Windows, qui, bien sûr, ne seront pas présentes dans la configuration pour STM32. En réalité, QT peut effectivement être exécuté sur STM32F7Discovery, mais cela sera abordé dans un autre article 🙂

Après avoir passé un certain temps à déterminer dans quel format le résultat du détecteur de bords est stocké, obtenons l'image.

OpenCV sur STM32F7-Discovery

L'image originale

OpenCV sur STM32F7-Discovery

Résultat

Lancement sur STM32F7Discovery

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

  1. 320KiB de RAM
  2. 1MiB de mémoire flash pour l'image
  3. 8MiB de SDRAM
  4. 16MiB de NAND-flash QSPI
  5. Connecteur pour carte microSD

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

Si nous prenons une configuration minimale pour STM32F7Discovery (en enlevant tout le réseau, toutes les commandes, en rendant les piles aussi petites que possible, etc.) et que nous 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 sont pas très familiers avec les sections, je vais expliquer : dans .text et .rodata se trouvent les instructions et les constantes (en gros, les données en lecture seule), dans .data se trouvent les données modifiables, dans .bss se trouvent les variables « nulles » qui, cependant, ont besoin d'espace (cette section « ira » dans la RAM).

La bonne nouvelle est que .data/.bss doivent tenir, mais le problème est que .text il n'y a qu'1MiB de mémoire disponible pour l'image. On pourrait supprimer l'image de l'exemple et la lire, par exemple, depuis une carte SD en mémoire au démarrage, mais fruits.png pèse environ 330KiB, donc cela ne résoudra pas le problème : la majeure partie .text est composée précisément du code OpenCV. .text En gros, il ne reste qu'une solution : charger une partie du code sur une mémoire flash QSPI (elle a un mode de fonctionnement spécial pour le mappage de la mémoire sur le bus système, donc le processeur pourra accéder directement à ces données). Cependant, le problème se pose : d'une part, la mémoire de la flash QSPI n'est pas accessible immédiatement après le redémarrage de l'appareil (il faut initialiser séparément le mode de mémoire mappée), d'autre part, nous ne pouvons pas « flasher » cette mémoire avec un chargeur classique.

Au final, il a été décidé de lier tout le code dans la QSPI et de le flasher avec un chargeur personnalisé qui recevra le bon binaire par TFTP.

L'idée de porter cette bibliothèque sur Embox est née il y a environ un an, mais cela a été repoussé plusieurs fois pour diverses raisons. L'une des raisons est le support de libstdc++ et de la bibliothèque de modèles standard. Le problème du support de C++ dans Embox dépasse le cadre de cet article, donc je dirai juste ici que nous avons réussi à obtenir ce support dans l'ampleur nécessaire pour faire fonctionner cette bibliothèque 🙂

Résultat

Finalement, ces problèmes ont été surmontés (du moins, dans une mesure suffisante pour faire fonctionner l'exemple OpenCV), et l'exemple s'est lancé. La carte met 40 secondes à rechercher les contours avec le filtre de Canny. C'est, bien sûr, trop long (il y a des idées pour optimiser cela, de quoi on pourra parler dans un article séparé en cas de succès).

Néanmoins, l'objectif intermédiaire était de créer un prototype qui montrera la possibilité fondamentale de faire fonctionner OpenCV sur STM32, donc cet objectif a été atteint, hourra !

OpenCV sur STM32F7-Discovery

tl;dr : guide étape par étape

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

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

    1 : Commençons par compiler le chargeur qui « flashera » la mémoire flash QSPI.

make confload-arm/stm32f7cube

    make confload-arm/stm32f7cube

Il est maintenant nécessaire de configurer le réseau, car nous allons charger l'image via TFTP. Pour définir les adresses IP de la carte et de l'hôte, il faut 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

passerelle — adresse de l'hôte à partir duquel l'image sera chargée, address — adresse de la carte.

Après cela, nous construisons le chargeur :

    make

2 : Chargement classique du chargeur (désolé pour le jeu de mots) sur la carte — il n'y a rien de spécifique ici, il faut le faire comme pour toute autre application pour STM32F7Discovery. Si vous ne savez pas comment procéder, vous pouvez lire à ce sujet. ici.
3 : Compilation de l'image avec la configuration pour OpenCV.

    make confload-platform/opencv/stm32f7discovery
    make

4 : Extraction des sections ELF à écrire dans le QSPI, dans 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

Dans le répertoire conf se trouve un script qui s'en occupe, vous pouvez donc l'exécuter.

    ./conf/qspi_objcopy.sh # Le binaire requis -- build/base/bin/qspi.bin

5 : Avec tftp, téléchargez qspi.bin.bin sur la clé QSPI. Sur l'hôte, il faut copier qspi.bin dans le répertoire racine du serveur tftp (généralement /srv/tftp/ ou /var/lib/tftpboot/ ; les paquets correspondants pour le serveur se trouvent dans la plupart des distributions populaires, souvent appelés tftpd ou tftp-hpa, il peut parfois être nécessaire de faire systemctl start tftpd.service pour démarrer).

    # вариант для 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), il faut exécuter cette commande (en supposant que l'adresse du serveur est 192.168.2.1) :

    embox> qspi_loader qspi.bin 192.168.2.1

6 : Avec la commande goto il faut "sauter" dans la mémoire QSPI. L'emplacement précis 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épart est placée dans le deuxième mot de 32 bits de l'image) ; il faudra également définir le drapeau de pile -s, l'adresse de la pile se trouve à l'adresse 0x90000000, exemple :

    embox>mem 0x90000000
    0x90000000:     0x20023200  0x9000c27f  0x9000c275  0x9000c275
                      ↑           ↑
              c'est l'adresse    c'est  l'adresse 
                de la pile      de la première
                           instruction

    embox>goto -i 0x9000c27f -s 0x20023200 # Le drapeau -i est nécessaire pour interdire les interruptions pendant l'initialisation du système

7 : Démarrez

    embox> edges 20

et profitez d'une recherche des contours de 40 secondes 🙂

Si quelque chose ne va pas, écrivez un problème dans notre dépôt, ou dans la mailing list embox-devel@googlegroups.com, ou dans les commentaires ici.

Source : habr.com

Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster