IMA4 2017/2018 P14 : Différence entre versions

De Wiki de Projets IMA
(Feuille d'heures)
(Cahier des charges)
Ligne 78 : Ligne 78 :
 
==Cahier des charges==
 
==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 4 écrans. Ceci permet à la fois d'éviter un encombrement matériel mais aussi .
+
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 4 écrans. Ceci permet à la fois d'éviter un encombrement matériel.
  
 
==Choix techniques : matériel et logiciel==
 
==Choix techniques : matériel et logiciel==

Version du 12 mai 2018 à 09:11


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"

Exemple d'un cas d'écran carré

Combinaison ecrans.jpg

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

Analyse du premier concurrent

Analyse du second concurrent

Scénario d'usage du produit ou du concept envisagé

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.

Exemple

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 4 écrans. Ceci permet à la fois d'éviter un encombrement matériel.

Choix techniques : matériel et logiciel

  • 5 Raspberry Pi 3 [1] lien 2 [2]
  • Un Commutateur (5 Ports +)
  • 5 Câbles Ethernet
  • 4 Câbles VGA
  • 4 Adaptateur VGA-femelle vers HDMI-mâle

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

Prologue

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)

Schéma

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

[3] : 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

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
<photo 95%>

Semaine 4

Lundi 05/02/18

Omxplayer demeure plus adapté puisqu'il n'entraîne pas une sur-consommation.

<photo 0-3%>

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 [4]

- Détermination de la couleur d'un objet avec OpenCV [5]

- Shape Detection avec OpenCV [6]

  • Solution à examiner pour la gestion d'un stream par une R-pi puis lu par une R-pi via omxplayer [7]

Mercredi 14/03/18

  • Installation d'OpenCV sur la R-pi maîtresse suivant le tutoriel [8]

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 [9]

-> 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 [10]

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 : [11]

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

[12]

AVLD est un exemple donné ici [13] v4l2loopback est un autre exemple [14] 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 [15] é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 [16] 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 :

[17])

[18]

# 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


Semaine 13

Mercredi 18/04/18

Tentative d'utiliser vlc pour stream

erreur coté lecture, à voir

stream [19]

lecture [20]



Documents Rendus