IMA4 2017/2018 P14 : Différence entre versions
(→Feuille d'heures) |
(→Analyse du projet) |
||
(40 révisions intermédiaires par 2 utilisateurs non affichées) | |||
Ligne 20 : | Ligne 20 : | ||
Afin de réaliser ce projet, il faudra : | Afin de réaliser ce projet, il faudra : | ||
− | * Ajouter un Raspberry Pi sur chaque écran afin de le | + | * Ajouter un Raspberry Pi sur chaque écran afin de le rendre "intelligent". |
* Créer une application permettant de prendre un flux vidéo et de le diriger vers l'écran visé. | * Créer une application permettant de prendre un flux vidéo et de le diriger vers l'écran visé. | ||
Ligne 30 : | Ligne 30 : | ||
==Positionnement par rapport à l'existant== | ==Positionnement par rapport à l'existant== | ||
+ | Actuellement, le concept d'écran géant modulaire est surtout utilisé sur des façades pour diffuser des publicités ou simplement afficher des informations. | ||
− | + | On retrouve tout de même le principe du côté domestique/professionnel lorsqu'il s'agit de configuration à deux, trois ou plusieurs écrans sur un ordinateur pour obtenir un espace de travail permettant de visualiser plusieurs tâches à la fois. C'est toutefois entièrement géré par la ou les cartes graphiques. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
==Scénario d'usage du produit ou du concept envisagé== | ==Scénario d'usage du produit ou du concept envisagé== | ||
− | + | Pour ce projet, on envisage plutôt une utilisation personnelle/domestique afin de pouvoir regarder sur un écran géant un film par exemple. | |
==Réponse à la question difficile== | ==Réponse à la question difficile== | ||
Ligne 70 : | Ligne 64 : | ||
En agrandissant le procédé à plus d'écrans, on trouve facilement une méthode de calcul. | En agrandissant le procédé à plus d'écrans, on trouve facilement une méthode de calcul. | ||
− | + | =Préparation du projet= | |
+ | |||
+ | ==Cahier des charges== | ||
+ | |||
+ | Dans le cadre de ce projet où il s'agira d'être capable de fournir une solution matérielle à la problématique, on s'orientera vers un prototype n'incluant que 2 écrans. Ceci permet d'éviter un encombrement matériel. | ||
− | + | [Réalisé] : Mettre en place un moyen de transmettre un flux vidéo à chaque partie du système | |
− | + | [Non résolu] : Permettre de diffuser un fichier vidéo au choix de l'utilisateur | |
− | + | [Réalisé] : Le système doit pouvoir s'adapter selon les besoin de l'utilisateur (nombre d'écrans et positionnements) -> scripts et fichiers de configuration | |
− | + | [Non résolu] : Permettre au système de réaliser et communiquer les fichier de configuration de manière autonome. | |
==Choix techniques : matériel et logiciel== | ==Choix techniques : matériel et logiciel== | ||
− | * | + | * 3 Raspberry Pi (une pour chaque écran + une faisant office de serveur) |
+ | |||
+ | * 3 Câbles USB alimentation Raspberry | ||
+ | |||
+ | * 1 Commutateur (5 Ports ou +) | ||
− | * | + | * 3 Câbles Ethernet |
− | * | + | * 3 Câbles VGA |
− | * | + | * 3 Adaptateur VGA-femelle vers HDMI-mâle |
− | * | + | * 3 écrans |
+ | |||
+ | * 3 Câbles alimentation écrans | ||
==Liste des tâches à effectuer== | ==Liste des tâches à effectuer== | ||
Ligne 106 : | Ligne 110 : | ||
{| class="wikitable" | {| class="wikitable" | ||
− | !Tâche !! Prélude !! Heures S1 !! Heures S2 !! Heures S3 !! Heures S4 !! Heures S5 !! Heures S6 !! Heures S7 !! Heures S8 !! Heures S9 !! Heures S10 !! Total | + | !Tâche !! Prélude !! Heures S1 !! Heures S2 !! Heures S3 !! Heures S4 !! Heures S5 !! Heures S6 !! Heures S7 !! Heures S8 !! Heures S9 !! Heures S10 !! Heures S11 !! Heures S12 !! Heures S13 !! Total |
|- | |- | ||
− | | | + | | Déroulement du projet |
| 0 | | 0 | ||
| Lundi 15/01/18 : 3h30 | | Lundi 15/01/18 : 3h30 | ||
Ligne 124 : | Ligne 128 : | ||
|Lundi 12/03/18 : 2h | |Lundi 12/03/18 : 2h | ||
Mercredi 14/03/18 : 4h | Mercredi 14/03/18 : 4h | ||
+ | |Lundi 19/03/18 : 9h | ||
+ | Mercredi 21/03/18 : 4h | ||
+ | | | ||
+ | Mercredi 28/03/18 : 4h | ||
| | | | ||
+ | Mercredi 04/04/18 : 4h | ||
| | | | ||
+ | RdV stage | ||
| | | | ||
+ | Mercredi 18/04/18 : 4H | ||
|} | |} | ||
− | |||
==Semaine 1== | ==Semaine 1== | ||
Ligne 151 : | Ligne 161 : | ||
Comme prévu nous diffuserons le flux vidéo initial, ''envoyé par Ethernet par une Raspberry Pi à partir d'un fichier stocké sur une clé usb par exemple'', aux 4 Raspberry intégrées aux écrans via un switch donc également par Ethernet (le port HDMI des R-Pi étant uniquement une sortie) | Comme prévu nous diffuserons le flux vidéo initial, ''envoyé par Ethernet par une Raspberry Pi à partir d'un fichier stocké sur une clé usb par exemple'', aux 4 Raspberry intégrées aux écrans via un switch donc également par Ethernet (le port HDMI des R-Pi étant uniquement une sortie) | ||
− | |||
− | |||
− | |||
− | |||
Nous effectuerons ensuite grâce aux R-Pi la modification du flux en fonction des écrans servant à l'affichage avant de rediriger la partie du flux qui nous intéresse vers les écrans. | Nous effectuerons ensuite grâce aux R-Pi la modification du flux en fonction des écrans servant à l'affichage avant de rediriger la partie du flux qui nous intéresse vers les écrans. | ||
Ligne 389 : | Ligne 395 : | ||
* Installation d'OpenCV sur la R-pi maîtresse suivant le tutoriel [https://www.pyimagesearch.com/2016/04/18/install-guide-raspberry-pi-3-raspbian-jessie-opencv-3/] | * Installation d'OpenCV sur la R-pi maîtresse suivant le tutoriel [https://www.pyimagesearch.com/2016/04/18/install-guide-raspberry-pi-3-raspbian-jessie-opencv-3/] | ||
+ | |||
+ | Note : Lors de l'installation le nom de l'environnement du tutoriel est "cv" pour les deux exemples (python2 et python3) or nous avons déclaré "cv2" pour python2 ET "cv3" pour python3 | ||
+ | |||
+ | -> actuellement bloquée au point du make -j4 la compilation plante à environ 24-25% sur une erreur (même chose avec la commande make pour n'utiliser qu'un coeur de la R-pi) | ||
* Création d'un serveur de stream pour la R-pi maîtresse [https://github.com/cncjs/cncjs/wiki/Setup-Guide:-Raspberry-Pi-%7C-MJPEG-Streamer-Install-&-Setup-&-FFMpeg-Recording] | * Création d'un serveur de stream pour la R-pi maîtresse [https://github.com/cncjs/cncjs/wiki/Setup-Guide:-Raspberry-Pi-%7C-MJPEG-Streamer-Install-&-Setup-&-FFMpeg-Recording] | ||
+ | |||
+ | -> Le système est installé sur la R-pi maîtresse mais n'arrive pas à se lancer pour le moment -> en recherche de solution pour ce problème | ||
==Semaine 9== | ==Semaine 9== | ||
+ | |||
+ | ===Lundi 19/03/18=== | ||
+ | |||
+ | * Compilation d'OpenCV débloquée [https://stackoverflow.com/questions/40262928/error-compiling-opencv-fatal-error-stdlib-h-no-such-file-or-directory] | ||
+ | |||
+ | Réinstallation effectuée proprement en raison d'un problème avec stdlib.h dans les fichiers précompilés d'où l'ajout de la commande | ||
+ | |||
+ | >>> -DENABLE_PRECOMPILED_HEADERS=OFF | ||
+ | |||
+ | à la suite du cmake qui est à présent | ||
+ | <pre> | ||
+ | $ cd ~/opencv-3.1.0/ | ||
+ | $ mkdir build | ||
+ | $ cd build | ||
+ | $ cmake -D CMAKE_BUILD_TYPE=RELEASE \ | ||
+ | -D CMAKE_INSTALL_PREFIX=/usr/local \ | ||
+ | -D INSTALL_PYTHON_EXAMPLES=ON \ | ||
+ | -D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib-3.1.0/modules \ | ||
+ | -D BUILD_EXAMPLES=ON .. \ | ||
+ | -DENABLE_PRECOMPILED_HEADERS=OFF | ||
+ | |||
+ | </pre> | ||
+ | |||
+ | * Réinstallation complète du serveur de stream indiqué au tutoriel : [https://github.com/cncjs/cncjs/wiki/Setup-Guide:-Raspberry-Pi-%7C-MJPEG-Streamer-Install-&-Setup-&-FFMpeg-Recording] | ||
+ | |||
+ | Le serveur est fonctionnel (testé en local uniquement) localhost:8081/steam.html conformément au instructions. | ||
+ | |||
+ | L'une des raisons du non fonctionnement était que /dev/video0 n'était pas disponible malgré le fait qu'une caméra était présente sur la raspberry or une webcam branchée en USB permet l'apparition de /dev/video1 qui a été utilisée pour le test. | ||
+ | |||
+ | |||
+ | ===Mercredi 21/03/18=== | ||
+ | |||
+ | * Serveur de streaming R-pi-M à R-pi-1 et R-pi-2 fonctionnel | ||
+ | |||
+ | Il faut cependant lancer le script en root sinon le système renvoie un échec de lancement | ||
+ | <pre> | ||
+ | #./mjpg-streamer.sh start | ||
+ | </pre> | ||
+ | |||
+ | * Le script de lecture est fonctionnel et il ne reste qu'à implanter la détection de la position des écrans dans l'ensemble du système pour rendre l'implémentation du fichier '''position.conf''' "automatique" | ||
+ | |||
+ | <pre> | ||
+ | #!/bin/sh | ||
+ | |||
+ | X=`grep -m 1 X= position.conf | cut -d'=' -f2` | ||
+ | Y=`grep -m 1 Y= position.conf | cut -d'=' -f2` | ||
+ | |||
+ | NB_X=`grep -m 1 NB_X= position.conf | cut -d'=' -f2` | ||
+ | NB_Y=`grep -m 1 NB_Y= position.conf | cut -d'=' -f2` | ||
+ | |||
+ | X1_tmp=1280 | ||
+ | X1=`echo $X1_tmp"*"$X"/"$NB_X | bc` | ||
+ | Y1_tmp=720 | ||
+ | Y1=`echo $Y1_tmp"*"$Y"/"$NB_Y | bc` | ||
+ | |||
+ | X2_tmp=1280 | ||
+ | X2=`echo $X2_tmp"*(1+"$X")/"$NB_X | bc` | ||
+ | Y2_tmp=720 | ||
+ | Y2=`echo $Y2_tmp"*(1+"$Y")/"$NB_Y | bc` | ||
+ | |||
+ | STREAM="http://169.254.171.72:8081/?action=stream" | ||
+ | echo "Fichier de lecture = "$STREAM | ||
+ | omxplayer --aspect-mode stretch --crop $X1,$Y1,$X2,$Y2 --live $STREAM | ||
+ | </pre> | ||
+ | |||
+ | <pre> | ||
+ | $sudo ./play_stream.sh | ||
+ | </pre> | ||
+ | |||
+ | * Passage d'une source video webcam à un fichier vidéo | ||
+ | |||
+ | Pour le moment le système est fonctionnel pour une webcam (/dev/video0) or ça pourrait être intéressant de voir si nous pouvons envoyer un fichier vidéo sur /dev/video1 (par exemple) si tenté que l'on puisse le créer et l'utiliser | ||
+ | |||
+ | [https://www.raspberrypi.org/forums/viewtopic.php?t=121901] | ||
+ | |||
+ | AVLD est un exemple donné ici [http://allonlinux.free.fr/Projets/AVLD/] | ||
+ | v4l2loopback est un autre exemple [https://github.com/umlaeute/v4l2loopback] mais qui pose un problème au niveau des headers du kernel | ||
==Semaine 10== | ==Semaine 10== | ||
+ | |||
+ | ===Mercredi 28/03/18=== | ||
+ | |||
+ | Fonctionnement de v4l2loopback confirmé, il reste à configurer les points /dev/video10 et /dev/video11 créés pour pouvoir les utiliser dans le serveur de stream, il est déjà possible d'y ouvrir un fichier video MP4 mais l'ouverture du point par le serveur de stream reste problématique | ||
+ | |||
+ | <pre> | ||
+ | ffmpeg -re -i /home/pi/streams/dragons.mp4 -f v4l2 /dev/video10 | ||
+ | </pre> | ||
+ | |||
+ | Permet de charger le fichier mp4 en temps réel (-re) sur le dummy-point video10 | ||
+ | |||
+ | ==Semaine 11== | ||
+ | |||
+ | ===Mercredi 04/04/18=== | ||
+ | |||
+ | * Début de la conception de l'algorithme de traitement d'image pour la détection et le placement des écrans | ||
+ | |||
+ | Jusqu'à présent le tutoriel de détection de formes et de leur centre [https://www.pyimagesearch.com/2016/02/01/opencv-center-of-contour/] était limité à des conditions assez précises, à savoir '''des formes de couleurs claires sur fond sombre''' hors il faut permettre au système de reconnaître les écrans dans (à peu près) tout environnement (éclairage). | ||
+ | En effet, les variations d'environnements entraînent une différence d'éclairage et donc cause un problème lors du seuillage [https://docs.opencv.org/3.3.0/d7/d4d/tutorial_py_thresholding.html] pour la binarisation de l'image (pré-traitement de l'image). | ||
+ | |||
+ | Il y a néanmoins un avantage, la possibilité de choisir ce qui est affiché sur les écrans des R-pi d'affichage, ainsi il est donc envisageable de détecter une zone en particulier (ex: l'écran en lui même) puis travailler (l'image) dans la zone donnée pour y détecter le code formes-couleurs pour identifier à quel écran est associé quelle R-pi (codes d'identifications connus de la R-pi maîtresse) afin de pouvoir donner leurs emplacements relatifs. (nombre d'écrans sur telle ou telle ligne/colonne) | ||
+ | |||
+ | * Le code python pour la détection d'un écran sur une image : | ||
+ | |||
+ | (inspiré du tutoriel de détection de l'écran d'une console GameBoy pour un traitement de l'image affichée sur l'écran : | ||
+ | |||
+ | [https://www.pyimagesearch.com/2014/04/21/building-pokedex-python-finding-game-boy-screen-step-4-6/]) | ||
+ | |||
+ | [https://www.pyimagesearch.com/category/building-a-pokedex/] | ||
+ | |||
+ | <pre> | ||
+ | # import the necessary packages | ||
+ | import imutils | ||
+ | from skimage import exposure | ||
+ | import numpy as np | ||
+ | import argparse | ||
+ | import cv2 | ||
+ | |||
+ | # construct the argument parser and parse the arguments | ||
+ | ap = argparse.ArgumentParser() | ||
+ | ap.add_argument("-q", "--query", required = True, | ||
+ | help = "Path to the query image") | ||
+ | args = vars(ap.parse_args()) | ||
+ | |||
+ | # load the query image, compute the ratio of the old height | ||
+ | # to the new height, clone it, and resize it | ||
+ | image = cv2.imread(args["query"]) | ||
+ | ratio = image.shape[0] / 300.0 | ||
+ | orig = image.copy() | ||
+ | image = imutils.resize(image, height = 300) | ||
+ | |||
+ | # convert the image to grayscale, blur it, and find edges | ||
+ | # in the image | ||
+ | gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | ||
+ | gray = cv2.bilateralFilter(gray, 11, 17, 17) | ||
+ | edged = cv2.Canny(gray, 30, 200) | ||
+ | |||
+ | # find contours in the edged image, keep only the largest | ||
+ | # ones, and initialize our screen contour | ||
+ | (cnts, _) = cv2.findContours(edged.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) | ||
+ | cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:10] | ||
+ | screenCnt = None | ||
+ | |||
+ | # loop over our contours | ||
+ | for c in cnts: | ||
+ | # approximate the contour | ||
+ | peri = cv2.arcLength(c, True) | ||
+ | approx = cv2.approxPolyDP(c, 0.02 * peri, True) | ||
+ | |||
+ | # if our approximated contour has four points, then | ||
+ | # we can assume that we have found our screen | ||
+ | if len(approx) == 4: | ||
+ | screenCnt = approx | ||
+ | break | ||
+ | |||
+ | cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 3) | ||
+ | cv2.imshow("Screen", image) | ||
+ | cv2.waitKey(0) | ||
+ | </pre> | ||
+ | |||
+ | '''Remarque :''' | ||
+ | |||
+ | ==Semaine 12== | ||
+ | |||
+ | ===Mercredi 11/04/18=== | ||
+ | |||
+ | abs - rdv stage - cf fiche de présence | ||
+ | |||
+ | ==Semaine 13== | ||
+ | |||
+ | ===Mercredi 18/04/18=== | ||
+ | |||
+ | Tentative d'utiliser vlc pour stream | ||
+ | |||
+ | erreur coté lecture, à voir | ||
+ | |||
+ | stream [https://wiki.videolan.org/Documentation:Streaming_HowTo/Command_Line_Examples/] | ||
+ | |||
+ | lecture [https://wiki.videolan.org/Documentation:Streaming_HowTo/Receive_and_Save_a_Stream/] | ||
+ | |||
+ | |||
+ | |||
+ | <pre> | ||
+ | |||
+ | </pre> | ||
+ | |||
+ | ==Conclusion== | ||
+ | |||
+ | Malgré les efforts et le temps imparti, je n'ai pas été en mesure de mettre au point le système initialement attendu. | ||
+ | |||
+ | Seule une version qui date de la semaine 10 est "fonctionnelle", bien qu'elle soit ensuite resté bloqué en raison d'un manque de compétences sur l'utilisation des modules employés. | ||
+ | |||
+ | Ce système est uniquement capable de diffuser le flux vidéo d'une Caméra USB sur un serveur web intégré auquel il est possible de récupérer la vidéo en s'y connectant soit par internet soit avec le lecteur (script) utilisé dans le projet. | ||
+ | |||
+ | Les deux problèmes à résoudre pour finaliser ce projet seraient de réussir là où je n'avais pas les compétences nécessaires: | ||
+ | |||
+ | * Faire fonctionner v4l2loopback pour la simulation d'un point de montage, d'y charger le fichier vidéo pour ensuite utiliser ce point de montage dans le système déjà présent. (ou faire fonctionner VLC en mode streaming) | ||
+ | |||
+ | * Réussir à implémenter un script de traitement d'image (sous python si on reprend ce qui a été commencé) qui prends en compte la différence entre le modèle théorique et le système réel dont les paramètres sont variables. | ||
=Documents Rendus= | =Documents Rendus= | ||
+ | |||
+ | Rapport de projet : [[Média:Jade_Dupont_Rapport_P14.pdf]] |
Version actuelle datée du 16 mai 2018 à 13:13
Sommaire
- 1 Présentation générale
- 2 Analyse du projet
- 3 Préparation du projet
- 4 Réalisation du Projet
- 5 Documents Rendus
Présentation générale
Description
- Intitulé du projet : Proposer une solution permettant d'assembler des écrans de PC afin de créer un écran géant
Le renouvellement de matériel informatique continu entraîne un nombre grandissant d'écrans d'ordinateur qui sont difficilement réutilisables compte tenu de leur petite taille. (17")
Afin de leur donner une seconde vie, ce projet a pour but de construire une plateforme permettant d'assembler ces écrans en un écran géant d'une forme "quelconque"
Objectifs
Afin de réaliser ce projet, il faudra :
- Ajouter un Raspberry Pi sur chaque écran afin de le rendre "intelligent".
- Créer une application permettant de prendre un flux vidéo et de le diriger vers l'écran visé.
- Permettre de déplacer facilement les écrans tout en maintenant le flux, que ce soit sur des configurations pleines en 2D ou des configurations plus complexes avec des espaces vides ou en 3D.
Analyse du projet
Positionnement par rapport à l'existant
Actuellement, le concept d'écran géant modulaire est surtout utilisé sur des façades pour diffuser des publicités ou simplement afficher des informations.
On retrouve tout de même le principe du côté domestique/professionnel lorsqu'il s'agit de configuration à deux, trois ou plusieurs écrans sur un ordinateur pour obtenir un espace de travail permettant de visualiser plusieurs tâches à la fois. C'est toutefois entièrement géré par la ou les cartes graphiques.
Scénario d'usage du produit ou du concept envisagé
Pour ce projet, on envisage plutôt une utilisation personnelle/domestique afin de pouvoir regarder sur un écran géant un film par exemple.
Réponse à la question difficile
- Le positionnement des écrans et la communication entre ces derniers.
Pour une application telle qu'un écran géant composé de plusieurs écran, il est difficilement envisageable d'utiliser un système de coordonnées GPS pour repérer des écrans qui sont particulièrement proches les uns des autres. En revanche il est tout à fait réaliste de penser à un système de capteurs de contact/proximité, disposés sur les bords des écrans pour leur permettre de s’emboîter puis il ne resterait qu'à exploiter les possibilités proposées par les Raspberry Pi ainsi qu'un système de coordonnées réalisé dans ce but pour déterminer où l'écran se situe par rapport aux autres.
- La gestion du flux vidéo
Le système sera potentiellement réalisé de sorte que chaque écran soit autonome dans son fonctionnement, une fois sa position relative déterminée au préalable. En effet, pour le moment le problème était surtout dans le positionnement des écrans plus que dans la gestion du flux vidéo, à priori.
- "Est-ce que le système peut continuer de fonctionner si on retire l'un des écrans ?"
Pour pallier au problème d'un retrait d'écran, ou d'un disfonctionnement éventuel localisé, une solution possible est de fournir à chaque écran la totalité du flux vidéo. Ceci permet dans un premier temps de s'affranchir d'un traitement vidéo avant distribution mais surtout que les autres écrans puissent fonctionner sans interruption due à un écran en particulier
- "Peut-il y avoir des décalages d'affichage entre les écrans ?"
Malgré l'absence de possibilités de test pour le moment, on peut néanmoins supposer qu'il n'y ait pas de décalage. Chaque écran va recevoir le flux vidéo puis en fonction du positionnement des écrans, et on pourra utiliser le système de coordonnées pour effectuer une "selection" sur la partie à afficher.
Dans le cas d'une configuration de 4 écrans en rectangle, il faudrait donc que l'écran inférieur gauche n'affiche que l'image correspondante jusqu'à la moitié, ensuite l'écran inférieur droit s'occuperait de l'affichage de la partie inférieure droite, etc
En agrandissant le procédé à plus d'écrans, on trouve facilement une méthode de calcul.
Préparation du projet
Cahier des charges
Dans le cadre de ce projet où il s'agira d'être capable de fournir une solution matérielle à la problématique, on s'orientera vers un prototype n'incluant que 2 écrans. Ceci permet d'éviter un encombrement matériel.
[Réalisé] : Mettre en place un moyen de transmettre un flux vidéo à chaque partie du système
[Non résolu] : Permettre de diffuser un fichier vidéo au choix de l'utilisateur
[Réalisé] : Le système doit pouvoir s'adapter selon les besoin de l'utilisateur (nombre d'écrans et positionnements) -> scripts et fichiers de configuration
[Non résolu] : Permettre au système de réaliser et communiquer les fichier de configuration de manière autonome.
Choix techniques : matériel et logiciel
- 3 Raspberry Pi (une pour chaque écran + une faisant office de serveur)
- 3 Câbles USB alimentation Raspberry
- 1 Commutateur (5 Ports ou +)
- 3 Câbles Ethernet
- 3 Câbles VGA
- 3 Adaptateur VGA-femelle vers HDMI-mâle
- 3 écrans
- 3 Câbles alimentation écrans
Liste des tâches à effectuer
- 1) Régler le problème de la gestion du flux vidéo par le réseau ainsi qu'au niveau des Raspberry-Pi
Hypothèse: pour une configuration d'écran donnée, on suppose les écrans déjà intelligent et on dispose de leur emplacement relatif.
- 2) Une fois la première version fonctionnelle, s'orienter vers la mise en place du système intelligent
Calendrier prévisionnel
Réalisation du Projet
Feuille d'heures
Tâche | Prélude | Heures S1 | Heures S2 | Heures S3 | Heures S4 | Heures S5 | Heures S6 | Heures S7 | Heures S8 | Heures S9 | Heures S10 | Heures S11 | Heures S12 | Heures S13 | Total |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Déroulement du projet | 0 | Lundi 15/01/18 : 3h30
Mercredi 17/01/18 : 4h |
Mercredi 24/01/18 : 4h | Mercredi 31/01/18 : 4h | Lundi 05/02/18 : 2h
Mercredi 07/02/18 : 4h |
Mercredi 14/02/18 : 4h |
Absente (malade) |
Mercredi 07/03/18 : 4h |
Lundi 12/03/18 : 2h
Mercredi 14/03/18 : 4h |
Lundi 19/03/18 : 9h
Mercredi 21/03/18 : 4h |
Mercredi 28/03/18 : 4h |
Mercredi 04/04/18 : 4h |
RdV stage |
Mercredi 18/04/18 : 4H |
Semaine 1
Lundi 15/01/18
- Première séance ayant pour but de synthétiser l'idée théorique souhaitée par le projet.
On imagine un système d'ensemble d'écrans autonomes mais qui communiquent leur emplacement entre eux, dans le sens où il n'est pas nécessaire de reconfigurer à chaque modification sur l'emplacement des écrans.
Le système est pour ainsi dire évolutif puisqu'à chaque modification, exemple: passage d'un écran géant de 2*2 à 2*3 ou 3*2 (ou plus), les affichages peuvent modifier la zone du flux vidéo à afficher afin de conserver une image sur la totalité des écrans.
De ce fait on peut imaginer des structures plus complexes, cylindriques dans le cas d'un flux d'une caméra à 360° ou plus simplement pour des simulateurs où les écrans seraient inclinés sans pour autant décrire un cercle autour de l'utilisateur.
Cependant, pour des raisons évidentes d'effectif, de temps il est nécessaire de se réduire à une solution moins ambitieuse mais néanmoins acceptable.
Mercredi 17/01/18
- Mise en place d'un cahier des charge réalisable dans le temps imparti par rapport à la réponse théorique que l'on souhaiterai apporter au problème.
Suite aux raisons évoquées précédemment, nous allons donc nous orienter vers un système à échelle réduite que nous ferons évoluer progressivement.
Comme prévu nous diffuserons le flux vidéo initial, envoyé par Ethernet par une Raspberry Pi à partir d'un fichier stocké sur une clé usb par exemple, aux 4 Raspberry intégrées aux écrans via un switch donc également par Ethernet (le port HDMI des R-Pi étant uniquement une sortie)
Nous effectuerons ensuite grâce aux R-Pi la modification du flux en fonction des écrans servant à l'affichage avant de rediriger la partie du flux qui nous intéresse vers les écrans.
- Remarque: Dans un premier temps, pour une configuration donnée et avec les emplacement des écrans connues, il s'agit uniquement de réaliser la conversion d'affichage.
Le fait de rendre les écrans intelligents n'est cependant pas ignoré et viendra dans un second temps, le choix de la méthode à employer étant encore en suspend. Nous allons cependant effectuer des hypothèses avec des capteurs.
- Soit X et Y les dimensions du flux vidéo
- Soit U,R,D,L les états des capteurs sur un écran donné (respectivement, Up, Right, Down, Left) (1 pour actifs, 0 pour inactif)
- Soit I,J les indices des écrans (I = 0/1 et J = 0/1)
- Soit nb_X et nb_Y respectivement le nombre d'écrans en fonction des coordonnées i.e les dimensions de notre écran géant
La configuration d'écran est la suivante:
Schéma (I,J)
0,1 ; 1,1
0,0 ; 1,0
Quel que soit l'écran, le flux qu'il devra recevoir sera situé entre [X1,X2] et [Y1,Y2]. Évidemment ces valeurs de l'emplacement de l'écran
- Y2 = Y * ( D||U ) * (I+1) / nb_Y
- Y1 = Y * D * I / nb_Y
- X1 = X * L * J / nb_X
- X2 = X * ( L||R ) * (J+1) / nb_X
Semaine 2
Mercredi 24/01/18
Début de la gestion réseau du flux vidéo.
Pour la partie théorique, nous allons donc procéder de la manière suivante :
Une Raspberry s'occupera de diffuser le flux sur le réseau via un commutateur. À partir d'un fichier vidéo interne.
Les autres R-Pi disposeront d'un fichier de configuration qui contient les coordonnées à afficher en fonction de la position de l'écran, dans la première partie ce fichier sera directement implanté avec un contenu propre à chaque R-Pi mais dans la seconde il s'agira de les modifier automatiquement.
Configuration eth0 sur la Raspberry pi : iface eth0 inet static address 192.168.1.<2+numero_rpi> netmask 255.255.255.0
On commencera par créer un fichier fifo pour faire la transmission entre nc et OMXplayer
Lecture du réseau : nc -l -p <port> > /dev/shm/video.fifo
Affichage : omxplayer --crop x1,y1,x2,y2 /dev/shm/video.fifo
[1] : Documentation OMX player
Semaine 3
Mercredi 31/01/18
Tentatives de communication PC (Source flux) à Raspberry (Écran) via nc : Échec
- [PC source] : | nc
- [Raspberry Écran - Terminal 1] : nc -l -p 12345 > /dev/shm/video.fifo
- [Raspberry Écran - Terminal 2] : omxplayer --crop x1,y1,x1,y2 /dev/shm/video.fifo
Avec x1,y1,x2,y2 les coordonnées des coins de la fenêtre à afficher
Problème rencontré : la Raspberry n'arrive pas à obtenir une adresse joignable malgré une connexion directe avec le PC en ethernet
- sudo nano /etc/network/interfaces
-> iface eth0 inet dhcp
- ifdown eth0
- ifup eth0
- ifconfig
-> inet adr:169.254.68.185
Seconde tentative avec VLC en mode Streaming sur le PC source et VLC en écoute de stream avec VLC également : Échec (IP de la source = ?)
Ligne de commande pour VLC de lecture
- vlc -vvv http://IP_SOURCE:8080/PATH --crop X_RESxY_RES+LEFT_CROP+TOP_CROP -f
PATH: paramètre donné lors du lancement du stream sur la machine source
X_RES : Résolution X de l'affichage (en pixel)
Y_RES : Résolution Y de l'affichage (en pixel)
LEFT_CROP : Largeur de la zone coupée en partant de la gauche (en pixel)
TOP_CROP : Hauteur de la zone coupée en partant du haut (en pixel)
-f : Full screen
- Test avec le mode de stream (une fois le problème résolu) : le lancement de la lecture sous VLC du côté de la raspberry entraîne des pertes considérables au niveau du flux vidéo avec des coupures très fréquentes (écran noir) ainsi qu'une forte consommation des ressources
Semaine 4
Lundi 05/02/18
Omxplayer demeure plus adapté puisqu'il n'entraîne pas une sur-consommation.
Mercredi 07/02/18
Envoie avec succès d'un fichier vidéo via netcat du PC source vers la Raspberry
- $ nc -w 3 172.26.145.172 1234 < Downloads/dragons.mp4
Où 172.26.145.172 est l'adresse obtenue par ifconfig sur la Raspberry.
Réception avec :
- $ nc -l -p 1234 > Downloads/test.mp4
Commande de lecture :
- $ omxplayer --aspect-mode stretch --crop X1,Y1,X2,Y2 Downloads/test.mp4
Récupération de la résolution du flux vidéo (ou fichier dans le cas actuel) : exiftool
Nécessite un "exiftool" sur chaque Raspberry
- $ sudo apt-get install exiftool
- $ exiftool <file> | grep "Image Size" | cut -d' ' -f25 | cut -dx -f1
Observation d'événements particuliers sur des dossiers
- $ apt-get install inotify-tools
Script d'observation et d’exécution d'action sur la création d'un fichier dans un dossier
#!/bin/sh # CONFIGURATION DIR="~/Downloads" EVENTS="create" NB_X=1 NB_Y=1 X=0 Y=0 # MAIN inotifywait -m -e $EVENTS $DIR | while read file do X1=0 Y1=0 X2=exiftool /home/pifou/Downloads/hide-and-seek.mp4 | grep "Image Size" | cut -d' ' -f25 | cut -dx -f1 | awk '{print $1"*(1+X)/$NB_X"}' | bc Y2=exiftool /home/pifou/Downloads/hide-and-seek.mp4 | grep "Image Size" | cut -d' ' -f25 | cut -dx -f2 | awk '{print $1"*(1+Y)/$NB_Y"}' | bc echo "File recieved : $file"; omxplayer --aspect-mode stretch --crop X1,Y1,X2,Y2 $DIR/$file; PID=$!; wait $PID; rm -f $DIR/$file; echo "File removed $file"; done
Semaine 5
Mercredi 14/02/18
Récupération du temps de lecture (pour la commande wait)
- exiftool <video> | grep -m 1 "Track Duration" | cut -d' ' -f21 | cut -d':' -fX
X=1 -> heures ; 2 -> minutes ; 3 ; secondes
Début de la conception d'une méthode d'acquisition de la position de l'écran relative aux autres.
Un premier problème est présent pour la détection des coins dans une configuration d'écrans plus ou moins aléatoire
Dans le cas de notre prototype à 4 écrans. Qu'ils soient disposés en carré ou en ligne à l'horizontale ou à la verticale, ça ne pose pas de problème puisqu'il suffit de juger à l'état des (futurs) capteurs disposé cependant dans le cas où l'on supprime un écran, on se retrouve avec des incohérences
Semaine 6
absente pour maladie toute la semaine
Semaine 7
Jusqu'à présent nous nous sommes surtout intéressés à la partie lecture du flux vidéo (fichier jusqu'à présent) et nous alors donc nous orienter sur la partie essentielle, le streaming de la R-pi maîtresse.
Remarque : La R-pi maîtresse sera dotée d'une caméra afin de pouvoir effectuer la détection des écrans via un traitement d'image.
Le concept est simple, la R-pi envoie un signal aux autres qui doivent afficher un identifiant qui est sous la forme d'une image de 4 carrés de couleurs. Ensuite elle prend une photo des écrans puis après un traitement d'image elle peut ensuite attribuer les fichiers de configuration aux R-pi correspondantes pour les scripts de lecture
Pour le moment, seul le streaming par VLC sur la maîtresse et la lecture sous omxplayer sur la partie visualisation fonctionne mais souffre d'une latence particulièrement élevée (rappel: vlc consomme 97%+ de la raspberry).
Semaine 8
Lundi 12/03/18
- Problème avec la carte SD de la R-pi 1 théoriquement résolu en copiant via "dd if=<input_file> of=<output_file>"
- Début des recherches sur le traitement d'image par une raspberry utilisant une caméra.
OpenCV semble proposer plusieurs solution possibles qui sont à exploiter
Sources:
- Vidéo de présentation d'un projet de reconnaissance de formes colorées [2]
- Détermination de la couleur d'un objet avec OpenCV [3]
- Shape Detection avec OpenCV [4]
- Solution à examiner pour la gestion d'un stream par une R-pi puis lu par une R-pi via omxplayer [5]
Mercredi 14/03/18
- Installation d'OpenCV sur la R-pi maîtresse suivant le tutoriel [6]
Note : Lors de l'installation le nom de l'environnement du tutoriel est "cv" pour les deux exemples (python2 et python3) or nous avons déclaré "cv2" pour python2 ET "cv3" pour python3
-> actuellement bloquée au point du make -j4 la compilation plante à environ 24-25% sur une erreur (même chose avec la commande make pour n'utiliser qu'un coeur de la R-pi)
- Création d'un serveur de stream pour la R-pi maîtresse [7]
-> Le système est installé sur la R-pi maîtresse mais n'arrive pas à se lancer pour le moment -> en recherche de solution pour ce problème
Semaine 9
Lundi 19/03/18
- Compilation d'OpenCV débloquée [8]
Réinstallation effectuée proprement en raison d'un problème avec stdlib.h dans les fichiers précompilés d'où l'ajout de la commande
>>> -DENABLE_PRECOMPILED_HEADERS=OFF
à la suite du cmake qui est à présent
$ cd ~/opencv-3.1.0/ $ mkdir build $ cd build $ cmake -D CMAKE_BUILD_TYPE=RELEASE \ -D CMAKE_INSTALL_PREFIX=/usr/local \ -D INSTALL_PYTHON_EXAMPLES=ON \ -D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib-3.1.0/modules \ -D BUILD_EXAMPLES=ON .. \ -DENABLE_PRECOMPILED_HEADERS=OFF
- Réinstallation complète du serveur de stream indiqué au tutoriel : [9]
Le serveur est fonctionnel (testé en local uniquement) localhost:8081/steam.html conformément au instructions.
L'une des raisons du non fonctionnement était que /dev/video0 n'était pas disponible malgré le fait qu'une caméra était présente sur la raspberry or une webcam branchée en USB permet l'apparition de /dev/video1 qui a été utilisée pour le test.
Mercredi 21/03/18
- Serveur de streaming R-pi-M à R-pi-1 et R-pi-2 fonctionnel
Il faut cependant lancer le script en root sinon le système renvoie un échec de lancement
#./mjpg-streamer.sh start
- Le script de lecture est fonctionnel et il ne reste qu'à implanter la détection de la position des écrans dans l'ensemble du système pour rendre l'implémentation du fichier position.conf "automatique"
#!/bin/sh X=`grep -m 1 X= position.conf | cut -d'=' -f2` Y=`grep -m 1 Y= position.conf | cut -d'=' -f2` NB_X=`grep -m 1 NB_X= position.conf | cut -d'=' -f2` NB_Y=`grep -m 1 NB_Y= position.conf | cut -d'=' -f2` X1_tmp=1280 X1=`echo $X1_tmp"*"$X"/"$NB_X | bc` Y1_tmp=720 Y1=`echo $Y1_tmp"*"$Y"/"$NB_Y | bc` X2_tmp=1280 X2=`echo $X2_tmp"*(1+"$X")/"$NB_X | bc` Y2_tmp=720 Y2=`echo $Y2_tmp"*(1+"$Y")/"$NB_Y | bc` STREAM="http://169.254.171.72:8081/?action=stream" echo "Fichier de lecture = "$STREAM omxplayer --aspect-mode stretch --crop $X1,$Y1,$X2,$Y2 --live $STREAM
$sudo ./play_stream.sh
- Passage d'une source video webcam à un fichier vidéo
Pour le moment le système est fonctionnel pour une webcam (/dev/video0) or ça pourrait être intéressant de voir si nous pouvons envoyer un fichier vidéo sur /dev/video1 (par exemple) si tenté que l'on puisse le créer et l'utiliser
AVLD est un exemple donné ici [11] v4l2loopback est un autre exemple [12] mais qui pose un problème au niveau des headers du kernel
Semaine 10
Mercredi 28/03/18
Fonctionnement de v4l2loopback confirmé, il reste à configurer les points /dev/video10 et /dev/video11 créés pour pouvoir les utiliser dans le serveur de stream, il est déjà possible d'y ouvrir un fichier video MP4 mais l'ouverture du point par le serveur de stream reste problématique
ffmpeg -re -i /home/pi/streams/dragons.mp4 -f v4l2 /dev/video10
Permet de charger le fichier mp4 en temps réel (-re) sur le dummy-point video10
Semaine 11
Mercredi 04/04/18
- Début de la conception de l'algorithme de traitement d'image pour la détection et le placement des écrans
Jusqu'à présent le tutoriel de détection de formes et de leur centre [13] était limité à des conditions assez précises, à savoir des formes de couleurs claires sur fond sombre hors il faut permettre au système de reconnaître les écrans dans (à peu près) tout environnement (éclairage). En effet, les variations d'environnements entraînent une différence d'éclairage et donc cause un problème lors du seuillage [14] pour la binarisation de l'image (pré-traitement de l'image).
Il y a néanmoins un avantage, la possibilité de choisir ce qui est affiché sur les écrans des R-pi d'affichage, ainsi il est donc envisageable de détecter une zone en particulier (ex: l'écran en lui même) puis travailler (l'image) dans la zone donnée pour y détecter le code formes-couleurs pour identifier à quel écran est associé quelle R-pi (codes d'identifications connus de la R-pi maîtresse) afin de pouvoir donner leurs emplacements relatifs. (nombre d'écrans sur telle ou telle ligne/colonne)
- Le code python pour la détection d'un écran sur une image :
(inspiré du tutoriel de détection de l'écran d'une console GameBoy pour un traitement de l'image affichée sur l'écran :
[15])
# import the necessary packages import imutils from skimage import exposure import numpy as np import argparse import cv2 # construct the argument parser and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-q", "--query", required = True, help = "Path to the query image") args = vars(ap.parse_args()) # load the query image, compute the ratio of the old height # to the new height, clone it, and resize it image = cv2.imread(args["query"]) ratio = image.shape[0] / 300.0 orig = image.copy() image = imutils.resize(image, height = 300) # convert the image to grayscale, blur it, and find edges # in the image gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) gray = cv2.bilateralFilter(gray, 11, 17, 17) edged = cv2.Canny(gray, 30, 200) # find contours in the edged image, keep only the largest # ones, and initialize our screen contour (cnts, _) = cv2.findContours(edged.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:10] screenCnt = None # loop over our contours for c in cnts: # approximate the contour peri = cv2.arcLength(c, True) approx = cv2.approxPolyDP(c, 0.02 * peri, True) # if our approximated contour has four points, then # we can assume that we have found our screen if len(approx) == 4: screenCnt = approx break cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 3) cv2.imshow("Screen", image) cv2.waitKey(0)
Remarque :
Semaine 12
Mercredi 11/04/18
abs - rdv stage - cf fiche de présence
Semaine 13
Mercredi 18/04/18
Tentative d'utiliser vlc pour stream
erreur coté lecture, à voir
stream [17]
lecture [18]
Conclusion
Malgré les efforts et le temps imparti, je n'ai pas été en mesure de mettre au point le système initialement attendu.
Seule une version qui date de la semaine 10 est "fonctionnelle", bien qu'elle soit ensuite resté bloqué en raison d'un manque de compétences sur l'utilisation des modules employés.
Ce système est uniquement capable de diffuser le flux vidéo d'une Caméra USB sur un serveur web intégré auquel il est possible de récupérer la vidéo en s'y connectant soit par internet soit avec le lecteur (script) utilisé dans le projet.
Les deux problèmes à résoudre pour finaliser ce projet seraient de réussir là où je n'avais pas les compétences nécessaires:
- Faire fonctionner v4l2loopback pour la simulation d'un point de montage, d'y charger le fichier vidéo pour ensuite utiliser ce point de montage dans le système déjà présent. (ou faire fonctionner VLC en mode streaming)
- Réussir à implémenter un script de traitement d'image (sous python si on reprend ce qui a été commencé) qui prends en compte la différence entre le modèle théorique et le système réel dont les paramètres sont variables.
Documents Rendus
Rapport de projet : Média:Jade_Dupont_Rapport_P14.pdf