IMA3/IMA4 2019/2021 P6+

De Wiki de Projets IMA


Présentation générale

Description

Un boitier Raspberry Pi et deux cameras permettent de monitorer le positionnement d'une fibre optique avec précision.

Objectifs

L'objectif de ce projet est de redévelopper une interface permettant de visualiser simultanément les images de deux caméras. L'interface de visualisation actuelle présente de nombreux défauts. Les deux caméras sont connectées à une Raspberry Pi grâce à un multiplexeur sur le bus CSI.

Préparation du projet

Choix techniques : matériel et logiciel

Multiplexeur de caméra : https://www.arducam.com/product/multi-camera-v2-1-adapter-raspberry-pi/ Dépôt git : https://github.com/ArduCAM/RaspberryPi/tree/master/Multi_Camera_Adapter/Multi_Adapter_Board_4Channel

Liste des tâches à effectuer

  • Installation de Raspberry OS
  • Installation software caméra multiplexer
  • Compréhension du logiciel constructeur
  • Recherche d'un bottleneck dans le programme
  • Etude des technologies disponibles pour redévelopper l'interface
  • Amélioration des performance avec affichage OpenCV
  • Création interface Qt
  • Insertion de la vidéo OpenCV dans l'interface

Cahier des charges

  • Maximiser les performances d'affichage de deux caméra simultanément via le module multiplexeur.
  • Concevoir une interface utilisable pour l'affichage vidéo.

Réalisation du Projet

Prologue

Le sujet de mon projet est de redévelopper un logiciel qui permettra de monitorer le positionnement d’une fibre optique avec deux caméras. Pour cela je devrais utiliser le module Multi-Camera Adapter Board Arducam, un multiplexeur de caméra permettant de connecter jusqu'à 4 caméras simultanément sur une Raspberry Pi. Pour notre application il n’y a besoin que de deux caméras.

Je devrais donc en comprendre le fonctionnement du module pour développer le logiciel puisque le logiciel fournit par le constructeur a des performances insuffisantes et une interface inutilisable.

La difficulté principale de ce projet est de travailler sur une carte sans aucune documentation, celle-ci agit donc comme une boîte noire sur laquelle je dois influer.

Semaine 1

Installation de Raspberry OS Installation du script fourni avec la carte => Suivre le git, attention à activer la caméra (CSI) et le bus I2C dans le raspi-config.

Le logiciel constructeur fonctionne. Problème :

  • Impossible de fermer la fenêtre sans kill le programme.
  • Faible fps (~6)
  • Impossible de redimensionner

Fonctionnement du logiciel de la carte multiplexeur : Le choix de la camera relié au bus CSI se fait commande I2C. Le logiciel commence par l'analyse des caméras connectées (1 à 4) en interrogeant les capteurs sur la carte à travers le bus I2C. Pour afficher les deux caméras simultanément le logiciel va switch très rapidement entre les caméras détecté et appeler l'affichage OpenCV pour chaque caméra à chaque switch.

Toutes mes tentatives d'amélioration se sont soldées par une instabilité de l'image, que ce soit en augmentant la résolution ou en diminuant la période entre chaque switch (augmentation FPS).

Pour déterminer d'où venait le blocage j'ai décidé d'essayer une camera connecté au bus CSI sans le multiplexeur : On atteint des performances bien supérieures avec un script de lecture du flux vidéo avec un OpenCV (960x720 30 fps).

Le switch rapide des caméras entraine forcément une réduction des FPS mais cela n'explique pas la faible résolution imposée.

Un essais avec une seule caméra placée sur la carte multiplexeur (pas encore effectué) permettrait de déterminer si le module empêche une meilleure qualité. (Une caméra sur le module avec le code constructeur est à 6 fps)

Recherches effectuées :

  • Choix de l'interface : la combinaison OpenCV pour la gestion de la vidéo et Qt pour l'interface semble tout indiqué. Je me suis donc renseigné sur le fonctionnement de ces deux bibliothèques.
  • Remise à niveau en C++
  • Faibles performances des caméras Raspberry pi de manière générale : Diviser l'action en deux thread différents semblerait grandement améliorer les performances ? (Sources peu fiables : Forum et Blogs personnels)

Semaine 2

J'ai décidé de travailler en Python pour continuer d'effectuer différents tests plus simplements sur le materiel, dans l'idée que si un developpement C++ est necessaire par la suite, la retranscription du code reste possible.

J'ai donc crée dans le dossier git /python différents scripts permettant de faire fonctionner les caméras sur le module multiplexeur. On obtient des performances similaires à celles du code constructeur ( ~6 fps + latence)

Le changement de résolution semble n'avoir que très peu d'impact sur les performances. Pour avoir des images stables on est obligé de forcer une baisse des FPS, pour laisser le temps au module de changer de caméra. Il semblerait donc que ce changement de caméra soit le goulot d'étranglement du système.

Afin de vérifier qu'il ne s'agit pas d'un problème de puissance de calcul je vais essayer de diviser les opérations en différents threads. Si il s'agit d'un problème de puissance sur le GPU alors un essai sur Raspberry Pi 4 pourrait obtenir de bien meilleures performances. --> Les tests sur raspberry pi 4 n'ont pas donné de résultats significativement meilleurs.

J'ai ensuite chercher à confirme l'hypothèse que le temps de changement de camera est trop lent pour obtenir de belles performances pour l'affichage des deux caméras simultanement : Pour cela j'ai crée un programme nommé meaure_min_swap_time.py, celui-ci permet de faire varier le temps d'attente entre chaque changement de camera avant d'afficher l'image. Ce pourrait permettre de nous donner une idée plus precise de la fréquence maximale pour maintenir une stabilité d'image.

Semaine 3

Finalisation du programe mesure : Il semblerait que la période minimale de changement de caméra soit autour de 0.05s, ce qui laisse penser que nous pourrions atteindre 10 fps par caméra. Peut être plus puisque les performances de ce programme de sont pas optimisées (python, appels systeme...)

Bien que la bibliothèque OpenCV soit reconnu pour avoir de bonnes performances j'ai tout de même voulu verifier que le problème ne venait pas de là non plus (Problème à l'installation ? Mauvaise optoin de compilation ? mauvaise utilisation, etc). Pour cela j'ai installé droidcam sur mon télephone et sur la Rapsberry pour simuler une webcam passant l'USB. J'ai affiché l'image de la caméra USB et de la caméra CSI avec OpenCV. Les performances étaient très bonnes.

Semaine 4

Maintenant que le fonctionnement du materiel est plus ou moins compris, je vais réecrire le programme en C++ afin de ne pas perdre de performance à cause du langage de programation. Les bibliothèques sont similaires pour OpenCV et pour le reste je m'inspirerai du programme fourni par le constructeur. Afin de simplifier le programme je ne verifierai pas si les caméras sont présentes sur le module avec des interrogations I2C; je considère donc que les deux caméras sont toujours presente sur le port A et le port C. C'est en cherchant dans la documentation de la bibliothèque i2c du kernel linux que je me suis rendu compte que le bus i2c n'est utile que pour la detection des caméras (test sur code Python + test sur code constructeur modifié). En effet, il semblerait que pour le changement de caméra, le controle de GPIO suffit.

J'ai ensuite rencontré un bug pour l'instant inexplicable. Parfois mes programmes fonctionnent parfaitement, aussi bien les programmes en C++, en python ou le programme constructeur, mais parfois aucun d'entre eux ne fonctionnent. Je n'ai pas encore trouvé de lien avec une quelconque action. Le problème pourrais donc venir de plusieurs endroit :

- Module Linux
- Problème système; mais une nouvelle installation fraiche de Raspberry OS n'a pas reglé le soucis.
- Problème materiel survenu pendant le développement du projet (peu plausible, module manipulé avec grand soin et fonctionne parfois).
- Problème materiel sur la raspberry Pi; aucun test sur une autre n'a été effectué pour l'instant.


Ce bug est bloquant pour le développement du projet. Je recherche donc activement une solution, la documentation étant inexistante sur ce module cela complique beaucoup les recherches.

Semaine 5

Le bug de la semaine précédente est toujours présent, je recherche donc encore une solution.

Pour ne pas rester sans rien faire, bloqué sur ce bug, je commence à utiliser la bibliothèque Qt pour développer l'interface plus proprement en parallèle de la recherche de solution.

Le problème a fini par se résoudre de lui-même avec le temps et sans aucune explication ou indice.

J’ai également fini par reprendre le code fournis par le constructeur comme base, en l’adaptant à mes besoins. La raison à cela est assez simple, bien que je perde en clarté dans le code, que j’adapterai du mieux que je peux, je m’assure qu’il ne manque aucun élément, afin de minimiser les possibilités de bug puisque le programme constructeur semble stable. Par exemple, toute la partie I2C pouvait jusque-là être ignorée puisque je considère que deux caméras sont toujours présente sur le module. Mais j’ai pu observer une meilleure stabilité lorsque j'ai laissé toute la partie I2C constructeur. Rien de certain puisqu’aucune documentation n’est disponible, mais cela permet de gagner du temps et de la sérénité sur le fonctionnement du programme. De plus, cela ne coute que des performances à l’initialisation, donc quelques dizaines de millisecondes par utilisation.

Le code était donc fonctionnel pour afficher sur une frame OpenCV les flux vidéo des deux caméra.


Conception de l'interface :

L’interface dont j’avais besoin n’avait rien de bien compliqué, la difficulté résiderait principalement dans l'intégration d’une image OpenCV dans l'environnement Qt.

Bien que Qt propose également son module de gestion de vidéo, similaire à celui d’OpenCV, je souhaitais garder OpenCV puisque jusque là j’avais travaillé avec, donc moins de perte de temps sur l’adaptation à une nouvelle bibliothèque et un nouveau fonctionnement. Mais surtout OpenCV peut permettre d’apporter de nombreuses fonctions de traitement d’image et de reconnaissance d’image, ce qui, à mon sens, pourrait s'avérer être un bon point pour la suite du projet, pour le positionnement de la fibre optique.

Afin d’éviter des complications avec la cross-compilation j’ai développé le projet depuis la Raspberry. Cela à mener à une petite subtilité lors de l’installation de Qt, en effet Qt utilisait de base gtk2 ce qui empêchait la compilation de mes programmes, la solution que j’ai trouvé (sur un forum) est de forcer les bonnes variables Qt de la manière suivante dans le ~/.bashrc :

export QT_QPA_PLATFORMETHEME=gtk3

export QT_STYLE_OVERRIDE=gtk3

Afin de forcer l'utilisation de gtk3 par Qt.

Probablement pas la meilleure solution mais je n'ai pas trouvé l’option lors de la compilation de la bibliothèque. De plus, cette option permet d’utiliser la bibliothèque installée depuis le dépôt APT.

Pour intégrer une vidéo OpenCV j’ai fini par trouver, au cours de mes recherches, un site expliquant parfaitement ce que je cherchais à faire.

J’utilisais jusque là la bibliothèque OpenCV installée via le dépôt APT, mais ici je l’ai installée et compilée statiquement pour avoir les options souhaitées. Je ne sais donc pas si la bibliothèque du dépôt apt pourrait suffir.

Semaine 6

Pour cette dernière semaine je me suis concentré sur la création de l'interface via Qt :


J’ai donc créé la forme de la fenêtre de base en utilisant le logiciel Qt Creator, il s'agit d’un outil permettant de placer sur un plan les éléments que l'on souhaite intégrer à notre programme. Ainsi, ce logiciel se charge de créer le code et les différents fichiers nécessaires à la création de ce programme. Le programme se compose donc d’un bouton start/stop, de cinq boutons radio et d’une surface QGraphicsView qui permettra d’afficher la vidéo.

  • Les fichiers générés sont donc les suivants :
  • un fichier .ui permettant de gérer le positionnement sur le plan les éléments, un fichier main.cpp, permettant le lancement du programme.
  • Un fichier mainwindow.cpp qui gère la fenêtre affichée
  • Un fichier .pro qui est très similaire à un fichier cmake, adapté aux projets Qt. Ce dernier permet donc de créer le Makefile, grâce à la commande qmake, nécessaire à la compilation. C’est dans le .pro qu’on viendra ajouter l'utilisation de la bibliothèque openCV. Pour l’ajout de la bibliothèque WiringPi je n’ai réussi à l’ajouter à la compilation que dans le Makefile. Il en faut donc, à chaque régénération du Makefile avec la commande cmake, ajouter à nouveau dans le Makefile le “-lwiringPi” à la ligne correspondante à la compilation de mainwindow.cpp.
  • Et les fichiers headers .h

Les boutons radios serviront simplement à sélectionner l’option d’affichage souhaitée, les fonctions liées à ces boutons radios influent sur des variables globales. Ce n’est pas le moyen le plus optimal de faire cela, Qt utilise un système de signal pour que les fonctions communiquent entre elles, je n’ai pas pris le temps de m’y plonger, surtout qu’à nouveau, il s’agit de pertes de performance uniquement à l'initialisation et ici c’est largement négligeable.

Pour afficher la vidéo on va placer dans la fonction liée au bouton start/stop, l'initialisation du module et caméras, comme dans nos précédents programmes, puis placer une boucle infinie qui viendra :

  • Changer de caméra si nécessaire
  • Lire la frame avec OpenCV, puis la fusionner sur la grande frame comme dans les précédent programmes
  • Convertir l’image OpenCV en image Qt (Mat -> QPixMap) en adaptant la forme, le type et les couleurs :
  • On vient ensuite ajouter l’image Qt à l'écran en actualisant la frame
  • Pour que tout s’actualise bien on vient actualiser le l’interface Qt

Il suffit ensuite d'adapter tout cela aux différentes options des boutons radio.

Il y a 3 résolutions disponibles lorsque l’on utilise les deux caméras simultanément, pour permettre, cela permet de privilégier soit la stabilité, soit la qualité, même si on préfèrera généralement la stabilité, qui est sélectionnée par défaut.

De plus, il y a la possibilité de visualiser une caméra à la fois, avec une bien meilleure qualité.

Rendus final

lien projet gitlab

Le résultat final est utilisable mais ne correspond pas à ce que l'on pourrait attendre d’un produit fini et ergonomique. En effet il arrive que le programme plante, rarement mais cela arrive encore, et surtout, parfois l’image peut être amenée à avoir des flash violet, si ces flash sont trop présent il faut alors relancer le programme jusqu'à ce que ceux-ci aient disparu, le résultat est différent à chaque exécution. L’interface est maintenant utilisable, différentes options sont disponibles, de plus la fenêtre peut être maximisée, ce qui permet d’avoir un affichage plus grand.

Amélioration possible

  • Bien utiliser les fonctions de signaux de Qt pour stabiliser l’interface.
  • Placer les fonctions de lecture, d’affichage de l’image et de gestion de l’interface sur différents threads.
  • Ajout de traitement d’image ou de reconnaissance d’image pour aider au positionnement de la fibre optique.