mirror of
https://git.roussel.pro/telecom-paris/pact.git
synced 2026-02-09 02:20:17 +01:00
Merge branch 'quentin' of https://gitlab.telecom-paris.fr/pact/2022-2023/pact71 into quentin
This commit is contained in:
@@ -1,59 +0,0 @@
|
||||
import cv2
|
||||
import mediapipe as mp
|
||||
|
||||
mp_drawing = mp.solutions.drawing_utils
|
||||
mp_face_mesh = mp.solutions.face_mesh
|
||||
|
||||
drawing_spec = mp_drawing.DrawingSpec(thickness=1, circle_radius=1)
|
||||
cap = cv2.VideoCapture(0)
|
||||
with mp_face_mesh.FaceMesh(
|
||||
max_num_faces=1,
|
||||
refine_landmarks=True,
|
||||
min_detection_confidence=0.5,
|
||||
min_tracking_confidence=0.5) as face_mesh:
|
||||
while cap.isOpened():
|
||||
success, image = cap.read()
|
||||
if not success:
|
||||
print("Ignoring empty camera frame.")
|
||||
continue
|
||||
# Initialize the face mesh model
|
||||
face_mesh = mp_face_mesh.FaceMesh(static_image_mode=False, max_num_faces=1, min_detection_confidence=0.5)
|
||||
|
||||
# Load the input image
|
||||
# lecture de la vidéo
|
||||
ret, frame = cap.read()
|
||||
# conversion de l'image en RGB
|
||||
image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
||||
# Process the image and extract the landmarks
|
||||
results = face_mesh.process(image)
|
||||
if results.multi_face_landmarks:
|
||||
landmarks = results.multi_face_landmarks[0]
|
||||
|
||||
# Define the landmark indices for the corners of the eyes and the tip of the nose
|
||||
left_eye = [33, 133, 246, 161, 160, 159, 158, 157, 173, 133]
|
||||
right_eye = [362, 263, 373, 380, 381, 382, 384, 385, 386, 362]
|
||||
nose_tip = 4
|
||||
|
||||
# Calculate the distance between the eyes and the nose tip
|
||||
left_eye_x = landmarks.landmark[left_eye[0]].x * image.shape[1]
|
||||
right_eye_x = landmarks.landmark[right_eye[0]].x * image.shape[1]
|
||||
nose_x = landmarks.landmark[nose_tip].x * image.shape[1]
|
||||
eye_distance = abs(left_eye_x - right_eye_x)
|
||||
nose_distance = abs(nose_x - (left_eye_x + right_eye_x) / 2)
|
||||
|
||||
# Determine the gender based on the eye and nose distances
|
||||
if eye_distance > 1.5 * nose_distance:
|
||||
gender = "Female"
|
||||
else:
|
||||
gender = "Male"
|
||||
|
||||
# Draw the landmarks on the image
|
||||
cv2.putText(image, gender, (10, 50),cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
|
||||
# affichage de la vidéo
|
||||
cv2.imshow('Video', cv2.cvtColor(image, cv2.COLOR_RGB2BGR))
|
||||
if cv2.waitKey(10) & 0xFF == ord('q'):
|
||||
break
|
||||
|
||||
# libération de la caméra et des ressources
|
||||
cap.release()
|
||||
cv2.destroyAllWindows()
|
||||
@@ -1,88 +0,0 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
import mediapipe as mp
|
||||
mp_drawing = mp.solutions.drawing_utils
|
||||
mp_drawing_styles = mp.solutions.drawing_styles
|
||||
mp_hands = mp.solutions.hands
|
||||
|
||||
def prodScalaire(V1,V2):
|
||||
return V1[0]*V2[0]+V1[1]*V2[1]/(np.sqrt(V1[0]**2+V1[1]**2)*np.sqrt(V2[0]**2+V2[1]**2))
|
||||
|
||||
def reconnaissancePouce(handLandmarks):
|
||||
etatDuPouce=["neutre","baissé","levé"]
|
||||
i=0
|
||||
j=0
|
||||
for cpt in range (0,4):
|
||||
V1=[handLandmarks[(4*cpt)+6][0]-handLandmarks[(4*cpt)+5][0],handLandmarks[(4*cpt)+6][1]-handLandmarks[(4*cpt)+5][1]]
|
||||
V2=[handLandmarks[(4*cpt)+8][0]-handLandmarks[(4*cpt)+6][0],handLandmarks[(4*cpt)+8][1]-handLandmarks[(4*cpt)+6][1]]
|
||||
j=np.dot(V1,V2)
|
||||
if (j>0.005):
|
||||
return etatDuPouce[0]
|
||||
V1=[handLandmarks[4][0]-handLandmarks[1][0],handLandmarks[4][1]-handLandmarks[1][1]]
|
||||
V2=[handLandmarks[2][0]-handLandmarks[1][0],handLandmarks[2][1]-handLandmarks[1][1]]
|
||||
if((np.dot(V1,V2))>0 and (handLandmarks[4][1]>handLandmarks[2][1])):
|
||||
i=1
|
||||
elif(np.dot(V1,V2)>0 and handLandmarks[4][1]<handLandmarks[2][1]):
|
||||
i=2
|
||||
return etatDuPouce[i]
|
||||
|
||||
|
||||
cap = cv2.VideoCapture(0)
|
||||
with mp_hands.Hands(
|
||||
model_complexity=0,
|
||||
min_detection_confidence=0.5,
|
||||
min_tracking_confidence=0.5) as hands:
|
||||
while cap.isOpened():
|
||||
success, image = cap.read()
|
||||
if not success:
|
||||
print("Ignoring empty camera frame.")
|
||||
# If loading a video, use 'break' instead of 'continue'.
|
||||
continue
|
||||
|
||||
# To improve performance, optionally mark the image as not writeable to
|
||||
# pass by reference.
|
||||
image.flags.writeable = False
|
||||
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
||||
results = hands.process(image)
|
||||
|
||||
# Draw the hand annotations on the image.
|
||||
image.flags.writeable = True
|
||||
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
|
||||
if results.multi_hand_landmarks:
|
||||
for hand_landmarks in results.multi_hand_landmarks:
|
||||
mp_drawing.draw_landmarks(
|
||||
image,
|
||||
hand_landmarks,
|
||||
mp_hands.HAND_CONNECTIONS,
|
||||
mp_drawing_styles.get_default_hand_landmarks_style(),
|
||||
mp_drawing_styles.get_default_hand_connections_style())
|
||||
|
||||
# Set variable to keep landmarks positions (x and y)
|
||||
handLandmarks = []
|
||||
if results.multi_hand_landmarks:
|
||||
for hand_landmarks in results.multi_hand_landmarks:
|
||||
# Fill list with x and y positions of each landmark
|
||||
for landmarks in hand_landmarks.landmark:
|
||||
handLandmarks.append([landmarks.x, landmarks.y])
|
||||
|
||||
cv2.putText(image, reconnaissancePouce(handLandmarks), (50, 450), cv2.FONT_HERSHEY_SIMPLEX, 3, (255, 0, 0), 10)
|
||||
|
||||
# Flip the image horizontally for a selfie-view display.
|
||||
cv2.imshow('MediaPipe Hands', cv2.flip(image, 1))
|
||||
if cv2.waitKey(5) & 0xFF == 27:
|
||||
break
|
||||
cap.release()
|
||||
|
||||
|
||||
|
||||
|
||||
""" etatDuPouce=["neutre","baissé","levé"]
|
||||
i=0
|
||||
|
||||
if results.multi_hand_landmarks:
|
||||
|
||||
if(results.multi_hand_landmarks.gestures.categories[0].categoryName==Thumb_Up):
|
||||
cv2.putText(image, str(results.multi_hand_landmarks.gestures.categories[0].categoryName), (50, 450), cv2.FONT_HERSHEY_SIMPLEX, 3, (255, 0, 0), 10)
|
||||
else:
|
||||
cv2.putText(image, "raté", (50, 450), cv2.FONT_HERSHEY_SIMPLEX, 3, (255, 0, 0), 10)
|
||||
"""
|
||||
@@ -1,113 +0,0 @@
|
||||
Arrivée à 10h30 nous avons été accueillis très grossièrement une agente d'accueil de Securitas
|
||||
(au niveau escalator montant vers richelieu) qui n'a pas regardé la carte handicapé de mon épouse
|
||||
heureusement qui il y avait un responsable au dessus qui lui c'est rendu compte et est venu nous
|
||||
faire accéder. Travaillant moi même dans la sécurité d'un public je trouve ça inadmissible
|
||||
et lamentable à 1 an des JO de Paris.
|
||||
|
||||
//Beau musée,mais ne se renouvelle pas.
|
||||
Ça fait 15 fois que j y vais depuis des années.
|
||||
Belles pièces mais l orientation toujours aussi bordélique. Heureusement que des surveillants sympa sont là.
|
||||
Ne pas oublier que le Louvre est un ancien Palais Royal et son histoire est un peu laissée de côté… dommage.
|
||||
Parking un peu cher sous le Louvre.
|
||||
Déçue dès boutique du carrousel.
|
||||
|
||||
//7h de visite non stop pour un musée extraordinaire. On apprécié les tableaux bien sûr mais aussi les plafonds,
|
||||
les escaliers, les objets, les sculptures. Quelle grandeur ! Le musée reste très abordable pour ce qu'il renferme
|
||||
comparé à d'autres musées dans le monde. Vive la France et vive Le Louvre !
|
||||
|
||||
//J'ai beaucoup apprécié les expositions de ce musée si réputé.Il n'a pas été possible de tout visiter en un seul jour.
|
||||
Il y a trop d'escaliers et pas mal de pas pour accéder aux salles de visites,du coup trop de perte de temps.
|
||||
Si non tout est fabuleux et émouvant.J'espère pouvoir y revenir.
|
||||
|
||||
//Je n'ai jamais pu visiter le musée du Louvre, et, en week-end avec des amies, on en a profiter,
|
||||
surtout que c'était le premier week-end du mois, donc entrée gratuite.
|
||||
Arrivée devant, incroyable, les pyramides sont magnifiques et le bâtiment les entourant l'est encore plus.
|
||||
Malheureusement, on a vite déchanter quand un membre du personnel nous a annoncé que c'était gratuit uniquement jusqu'à 26 ans.
|
||||
Quitte à ce que ce soit gratuit une fois dans le mois, autant que ça soit gratuit pour tout le monde,
|
||||
ça changera pas grand chose au chiffre d'affaires.
|
||||
Très déçue, c'est dommage.
|
||||
|
||||
//Le musée est magnifique, c'est un peu compliqué de s'orienter dedans. Plus d'indications ne serait pas de trop.
|
||||
Ce qui a diminué la note est : le personnel. Très désagréable, s'attend à ce qu'on comprenne ce qu'ils veulent
|
||||
sans ouvrir la bouche et formuler leur demande. Pas de bonjour, que des gestes ou ordres hurlés. De quoi miner une visite...
|
||||
|
||||
//J’ai payé 2 entrées adultes au Musée du Louvre. Après maintes mails avec confirmation de la réservation,
|
||||
nous n’avons pas reçu les billets par mail.
|
||||
Nous avons dû payer encore une fois les billets pour réaliser la visite! Entrées 2 fois moins chères sur place!
|
||||
Remboursement demandé, on va voir si la demande va aboutir !
|
||||
|
||||
//La multitude de touristes n'hésitant pas à faire longuement la queue avant d'entrer dans ce musée prestigieux
|
||||
ne fait que confirmer, à juste titre, son incroyable attractivité, sans cesse renouvelée au fil des ans !
|
||||
Plusieurs visites sont indispensables pour en profiter !
|
||||
Une pure merveille !
|
||||
|
||||
//Expérience très désagréable au Louvre début janvier 2023 : beaucoup, beaucoup trop de monde, admis puisqu’il
|
||||
faut réserver ( et payer) préalablement. File d’attente interminable avant de passer le contrôle de sécurité,
|
||||
file pour obtenir un casier, file pour valider son ticket, file pour voir certaines œuvres.
|
||||
Deux adultes avec 3 enfants entre 5 et 11 ans et impossible de profiter un minimum car il faut se frayer un chemin,
|
||||
ne pas perdre les enfants, admirer les œuvres d’art. Nos enfants n’ont quasi rien retenu si ce n’est la foule.
|
||||
Dommage que ce haut lieu de culture cultive surtout la rentabilité !
|
||||
|
||||
//Quelques expositions- pas très bien indiqué- la seconde partie introuvable!
|
||||
Il serait bon de mieux indiquer toutes les parties de l’exposition- la recherche était fatigante!
|
||||
|
||||
//il est mon musée favori, j'adore ce lieu, vous pouvez très bien passer des heures devants un tableau
|
||||
loin de tout ce qui est touristique tels que Mona lissa, il est franchement agréable a visiter.
|
||||
|
||||
//Des soucis pour s'orienter (changer de bâtiment est parfois compliqué). Grande richesse de la collection.
|
||||
Personnel dans les salles très serviable.
|
||||
|
||||
//Tout simplement grandiose ! Allez-y même si vous ne connaissez rien à la culture… peintures, sculptures,
|
||||
objets d’arts, rien ne manque
|
||||
|
||||
//Le titre se suffit à lui-même, l'art est précieux et admirable, mais il fait assez chaud et c'est très grand,
|
||||
du genre, très très très grand: un conseil ne faites pas la queue devant la pyramide, entrez par le carroussel du louvre <3
|
||||
|
||||
//Superbe musée, passionnant , ce voyage dans l art et dans l histoire est un pure moment de poésie !
|
||||
De nombreuses collections, avec une partie sur la Grèce antique et l Égypte très attractives pour les
|
||||
jeunes enfants à partir de 4ans . Le musée est très grand , avec des enfants en bas âge ciblez ce que vous voulez
|
||||
visiter car le faire entièrement sera difficile.
|
||||
Un fabuleux moment
|
||||
|
||||
//Il faut plusieurs jours pour visiter ce musée, à moins de choisir une ou deux sections par visite.
|
||||
Le musée est très bien entretenu.
|
||||
Les plans communiqués sont compréhensibles.
|
||||
Les collections vues sont tres intéressante.
|
||||
|
||||
//Magnifique musée.
|
||||
Le seul souci est la désorganisation pour y rentrer.
|
||||
Bien que ayant pris bien à l' avance nos billets sur le site, nous avons du faire presque 1h de queue malgres tout pour pouvoir y entrer.
|
||||
|
||||
C' est la raison pour laquelle, je ne mets pas la note maximale ainsi que le fait qu' à l interieur,
|
||||
il y a peu d' indication pour s' orienter.
|
||||
|
||||
Mais la visite du musée était très positive pour nous.
|
||||
|
||||
//Un musée d’une richesse exceptionnelle.
|
||||
|
||||
Il est difficile de se repérer malgré les plans papier fournis. Des plans précis affichés plus régulièrement
|
||||
ainsi qu’une numérotation des salles plus lisibles seraient appréciés et faciliteraient les déplacements des visiteurs.
|
||||
|
||||
Il serait également opportun de disposer plus de bancs pour les enfants et personnes âgées.
|
||||
|
||||
//Lieu exceptionnel, collections très riches, à visiter.
|
||||
Accessibilité sans faille mais quel dédale pour trouver les ascenseurs on doit bien faire le double de
|
||||
distance que les valides pour la même visite. Le charme des vieilles pierres.
|
||||
|
||||
//Première visite au Louvre, nous avions réservé à 9h30, nous avons patienté environs 15/20min avant de rentrer,
|
||||
il y avait énormément de monde. Les oeuvres et les structures sont magnifique, je pense que
|
||||
c'est à visiter au moins une fois dans une vie. Il est a des restaurations à l'intérieur à un prix vraiment raisonnable.
|
||||
Cependant, si on veut vraiment observer toute les oeuvres, il faut prévoir toute une moitié de journée voir plus,
|
||||
car il y a énormément de chose à regarder et d'endroit à visiter.
|
||||
|
||||
//une très bonne expérience surtout pour une personne qui s'intéresse a l'art et aux cultures. je recommande.
|
||||
|
||||
//Première visite au Louvre, arrivé avec des billet pour 9h à l'entrée rue de Rivoli. Nous sommes entrée
|
||||
directement sans faire de queue et avons pu profiter un cours instant d'une visite du musée entièrement vide.
|
||||
Quel plaisir de pouvoir des œuvres comme la Vénus de Milo ou la Victoire de Samothrace sans une foule de touristes
|
||||
devant ! Nous avons pu effectuer la suite de notre visite sans blocage dû à la masse des visiteurs et avons même
|
||||
pu admirer la Joconde en même pas 5min de file d'attente. Merci le personnel du Louvre!
|
||||
|
||||
//Magnifique bâtiment. Un musée extraordinaire de part les œuvres et l’intérieur des bâtiments
|
||||
Une double visite
|
||||
Pas de file d’attente ( billet acheté avant sur internet) prévoir la journée ou en plusieurs fois pour arriver à tout voir
|
||||
@@ -1,160 +0,0 @@
|
||||
from vaderSentiment_fr.vaderSentiment import SentimentIntensityAnalyzer
|
||||
|
||||
#Emplacements des fichiers contenants le lexique et les avis
|
||||
lexiconPath = r"C:\Users\kesha\Desktop\TelecomParis\PACT\fr_lexicon.txt"
|
||||
reviewPath = r"C:\Users\kesha\Desktop\TelecomParis\PACT\LouvreAvis.txt"
|
||||
|
||||
#Création d'une liste de listes ordonnée alphabétiquement pour ne pas
|
||||
#avoir à chercher un mot d'un avis dans le lexique en entier à chaque fois.
|
||||
#La dernière case correspond aux expressions n'étant pas des mots.
|
||||
scoreWords = open(lexiconPath, "r")
|
||||
scoreTable = [[] for i in range(27)]
|
||||
line = scoreWords.readline()
|
||||
|
||||
#Fonction d'ajout d'une paire mot-score par ordre alphabétique avec les
|
||||
#expressions n'étant pas des mots à la dernière case.
|
||||
#L'indice de la bonne case est trouvée avec le code ASCII en minuscule
|
||||
#(a vaut 97 et z vaut 122)
|
||||
def add(scoreword):
|
||||
if (ord(scoreword[0][0]) < 97 or ord(scoreword[0][0]) > 122):
|
||||
scoreTable[26].append(scoreword)
|
||||
else:
|
||||
scoreTable[ord(scoreword[0][0])-97].append(scoreword)
|
||||
|
||||
#Ajout des paires mot-score dans scoreTable
|
||||
while (line != ''):
|
||||
line = line.strip().split("->")
|
||||
add([line[0].lower(), float(line[1])])
|
||||
line = scoreWords.readline()
|
||||
scoreWords.close()
|
||||
|
||||
### Partie analyse d'avis ###
|
||||
|
||||
|
||||
file = open(reviewPath, "r", encoding='UTF-8')
|
||||
reviews = (file.read()).split('//')
|
||||
|
||||
|
||||
#liste (partielle) de mots-clé pertinents pour un musée
|
||||
keys=['attente', "d'attente", 'queue', 'patienter', 'patience', 'patient',
|
||||
'patients', 'patiente', 'patientes', 'file', 'files',
|
||||
'impolitesse' ,'impolie', 'impolies', 'impoli', 'impolis',
|
||||
'gentillesse', 'amabilité', 'aimable', 'aimables','gentil', 'gentils',
|
||||
'gentille', 'gentilles', 'personnel',
|
||||
'sales', 'sale', 'saleté', 'propre', 'propres', 'propreté',
|
||||
'acceuil', 'prix', 'cher', 'chers', 'chère', 'chères',
|
||||
'onéreux', 'onéreuse', 'onéreuses', 'abordable',
|
||||
'raisonnable', 'raisonnables', 'accessible', 'accessibilité',
|
||||
'handicapé', 'handicapée', 'handicapés', 'handicapées', 'orienter','employé',
|
||||
'employés', 'employées', 'employée', 'agent', 'agente',
|
||||
'orientation', 'orienté', "s'orienter", 'dédale',
|
||||
'désorienter', 'désorienté', 'désorientée', 'désorientés', 'désorientées',
|
||||
'panneau', 'panneaux', 'signalétique', 'labyrinthe',
|
||||
'perdu', 'perdus', 'perdue', 'perdues',
|
||||
'toilettes',
|
||||
'restaurant', 'restaurants', 'restauration', 'manger', 'mangé', 'déjeuner', 'déjeuné']
|
||||
|
||||
#Tableau de paires mots-clé, score associé
|
||||
keyWords = []
|
||||
|
||||
#Score moyen d'un avis
|
||||
averageScore = 0
|
||||
|
||||
vaderScore = {'neg': 0, 'neu': 0, 'pos': 0, 'compound': 0}
|
||||
|
||||
SIA = SentimentIntensityAnalyzer()
|
||||
|
||||
|
||||
#Fonction de recherche d'un mot d'un avis parmis le lexique
|
||||
def search(word):
|
||||
if (len(word) != 0):
|
||||
if (ord(word[0]) < 97 or ord(word[0]) > 122):
|
||||
mots = list(e[0] for e in scoreTable[26])
|
||||
if (word in mots):
|
||||
return([word, scoreTable[26][mots.index(word)][1]])
|
||||
else:
|
||||
return(-1)
|
||||
mots = list(e[0] for e in scoreTable[ord(word[0])-97])
|
||||
if (word in mots):
|
||||
return([word, scoreTable[ord(word[0])-97][mots.index(word)][1]])
|
||||
return(-1)
|
||||
|
||||
#Fonction déterminant si une phrase contient une négation et renvoyant un booléen
|
||||
def isNegative(sentence):
|
||||
if (('ne' in sentence) or ("n'" in sentence) or ("pas" in sentence)):
|
||||
return True
|
||||
return False
|
||||
|
||||
vaderCriteria = ['neg', 'neu', 'pos', 'compound']
|
||||
|
||||
def ReviewAnalyzer(review):
|
||||
reviewScore = 0
|
||||
|
||||
#miniKey donne les mots-clé contenus dans l'avis en train d'être analysé
|
||||
miniKey = []
|
||||
|
||||
### Recherche de mots positifs/négatifs ###
|
||||
|
||||
#On découpe l'avis en une liste de phrases
|
||||
sentences = Review.split('.')
|
||||
for sent in sentences:
|
||||
|
||||
#On découpe la phrase en une liste de mots
|
||||
sentence = list(e.strip(',.') for e in sent.split())
|
||||
for Word in sentence:
|
||||
|
||||
#On met tout les mots en minuscule pour ne pas prendre en compte les majuscules
|
||||
word = Word.lower()
|
||||
temp = search(word)
|
||||
|
||||
#recherche d'un éventuel mot-clé associé à ce caractère positif/négatif
|
||||
if (temp != -1):
|
||||
|
||||
#On change la valeur du score associé au mot-clé
|
||||
#si la phrase contient une négation
|
||||
if (isNegative(sentence)):
|
||||
temp[1] = -temp[1]
|
||||
|
||||
for key in keys:
|
||||
if (key in sentence):
|
||||
|
||||
#Récupération des mots-clés déjà détectés dans tous les avis
|
||||
cles = list(e[0] for e in keyWords)
|
||||
|
||||
#En fonction de si key a déjà été détecté auparavant,
|
||||
#et donc qu'il est dans keyWords avec un score associé,
|
||||
#on augmente le score ou sinon, on crée une paire mot-clé score
|
||||
if (key in cles):
|
||||
keyWords[cles.index(key)][1] += temp[1]
|
||||
else:
|
||||
keyWords.append([key, temp[1]])
|
||||
|
||||
#Rajout du mot-clé key dans la liste des mots-clé trouvés dans cet avis
|
||||
miniKey.append(key)
|
||||
|
||||
#Mise à jour du score de l'avis
|
||||
reviewScore += temp[1]
|
||||
|
||||
#Caractéristiques de l'avis analysé
|
||||
miniKey = set(miniKey)
|
||||
return(miniKey, reviewScore)
|
||||
|
||||
for Review in reviews:
|
||||
ReviewAnalayzer(Review)
|
||||
|
||||
#Ces 3 lignes sont pour comparer avec le score de VADER
|
||||
score = SIA.polarity_scores(sent)
|
||||
for criteria in vaderCriteria:
|
||||
vaderScore[criteria] += score[criteria]
|
||||
|
||||
averageScore /= len(reviews)
|
||||
print("Format: [[Mot-clé, score associé]]")
|
||||
for e in keyWords:
|
||||
print(e)
|
||||
print('')
|
||||
print("Nombre d'avis: ", len(reviews))
|
||||
print("Score moyen d'un avis: ", averageScore)
|
||||
for criteria in vaderCriteria:
|
||||
vaderScore[criteria] /= len(reviews)
|
||||
print("Score moyen de vader d'un avis: ", vaderScore)
|
||||
file.close()
|
||||
@@ -1,268 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 62,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#pip install unidecode\n",
|
||||
"#pip install dataclass\n",
|
||||
"#pip install nltk\n",
|
||||
"#import os\n",
|
||||
"#from unidecode import unidecode\n",
|
||||
"#import nltk\n",
|
||||
"#from dataclasses import dataclass"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Première partie : presentation du problème et du materiel\n",
|
||||
"\n",
|
||||
"Nous cherchons a attribuer à une liste d'avis laissés un score global de satisfaction, ainsi qu'un score de satisfaction concernant chaque point pour lequel il sera particulierement interessant de se pencher (par exemple le delais d'attente dans un parc d'attraction ou la propreté dans un hotel).\n",
|
||||
"\n",
|
||||
"Nous allons pour cela utiliser une base de mots français associés chacun a un score de positivité, ainsi qu'une liste d'avis concernant le musée du Louvre.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 63,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#Emplacmement du fichier contenant des mots francais associés a une score sous la forme\n",
|
||||
"#mot1->son score\n",
|
||||
"#mot2->son score\n",
|
||||
"#mot3->son score ...\n",
|
||||
"\n",
|
||||
"lexiconPath = r\"fr_lexicon.txt\" \n",
|
||||
"\n",
|
||||
"\n",
|
||||
"#Emplacmement du fichier contenant des des avis sur le musée du Louvre sous la forme\n",
|
||||
"#Avis1\n",
|
||||
"#//Avis2\n",
|
||||
"#//Avis3 ...\n",
|
||||
"\n",
|
||||
"reviewPath = r\"LouvreAvis.txt\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Nous créons une liste de listes ordonnée alphabétiquement pour ne pas avoir à chercher un mot d'un avis dans le lexique en entier à chaque fois. La dernière case correspond aux expressions n'étant pas des mots."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 82,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"scoreWords = open(lexiconPath, \"r\")\n",
|
||||
"scoreTable = [[] for i in range(27)]\n",
|
||||
"line = scoreWords.readline()\n",
|
||||
"\n",
|
||||
"#Fonction d'ajout d'une paire mot-score par ordre alphabétique avec les\n",
|
||||
"#expressions n'étant pas des mots à la dernière case.\n",
|
||||
"#L'indice de la bonne case est trouvée avec le code ASCII en minuscule\n",
|
||||
"#(a vaut 97 et z vaut 122)\n",
|
||||
"\n",
|
||||
"def add(scoreword):\n",
|
||||
" if (ord(scoreword[0][0]) < 97 or ord(scoreword[0][0]) > 122):\n",
|
||||
" scoreTable[26].append(scoreword)\n",
|
||||
" else:\n",
|
||||
" scoreTable[ord(scoreword[0][0])-97].append(scoreword)\n",
|
||||
" \n",
|
||||
"#Ajout des paires mot-score dans scoreTable\n",
|
||||
"while (line != ''):\n",
|
||||
" line = line.strip().split(\"->\")\n",
|
||||
" add([line[0].lower(), float(line[1])])\n",
|
||||
" line = scoreWords.readline()\n",
|
||||
"scoreWords.close()\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 83,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"['abandon', -2.4]\n",
|
||||
"['abandonnant', -1.6]\n",
|
||||
"['abandonne', -1.3]\n",
|
||||
"['badass', 1.4]\n",
|
||||
"['badin', 1.2]\n",
|
||||
"['badine', 1.2]\n",
|
||||
"['cachant', -1.2]\n",
|
||||
"['cache', -0.7]\n",
|
||||
"['cachent', -0.7]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for i in range (3):\n",
|
||||
" print(scoreTable[i][0])\n",
|
||||
" print(scoreTable[i][1])\n",
|
||||
" print(scoreTable[i][2])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Deuxieme partue : analyse d'avis"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 67,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"file = open(reviewPath, \"r\")\n",
|
||||
"reviews = (file.read()).split('//')\n",
|
||||
" \n",
|
||||
"#liste (partielle) de mots-clé pertinents pour un musée\n",
|
||||
"keys=['attente', \"d'attente\", 'queue', 'patienter', 'patience', 'patient',\n",
|
||||
" 'patients', 'patiente', 'patientes',\n",
|
||||
" 'impolitesse' ,'impolie', 'impolies', 'impoli', 'impolis',\n",
|
||||
" 'gentillesse', 'amabilité', 'aimable', 'aimables','gentil', 'gentils',\n",
|
||||
" 'gentille', 'gentilles', 'personnel',\n",
|
||||
" 'sales', 'sale', 'saleté', 'propre', 'propres', 'propreté',\n",
|
||||
" 'acceuil', 'prix', 'cher', 'chers', 'chère', 'chères',\n",
|
||||
" 'onéreux', 'onéreuse', 'onéreuses', 'abordable',\n",
|
||||
" 'raisonnable', 'raisonnables', 'accessible', 'accessibilité', 'orienter','employé',\n",
|
||||
" 'employés', 'employées', 'employée',\n",
|
||||
" 'orientation', 'orienté', \"s'orienter\",\n",
|
||||
" 'désorienter', 'désorienté', 'désorientée', 'désorientés', 'désorientées',\n",
|
||||
" 'panneau', 'panneaux', 'signalétique', 'labyrinthe',\n",
|
||||
" 'perdu', 'perdus', 'perdue', 'perdues']\n",
|
||||
"\n",
|
||||
"#Tableau de paires mots-clé, score associé\n",
|
||||
"keyWords = []\n",
|
||||
"\n",
|
||||
"#Score moyen d'un avis\n",
|
||||
"averageScore = 0\n",
|
||||
"\n",
|
||||
"#Fonction de recherche d'un mot d'un avis parmis le lexique\n",
|
||||
"def search(word):\n",
|
||||
" if (len(word) != 0):\n",
|
||||
" if (ord(word[0]) < 97 or ord(word[0]) > 122):\n",
|
||||
" mots = list(e[0] for e in scoreTable[26])\n",
|
||||
" if (word in mots):\n",
|
||||
" return([word, scoreTable[26][mots.index(word)][1]])\n",
|
||||
" else:\n",
|
||||
" return(-1)\n",
|
||||
" mots = list(e[0] for e in scoreTable[ord(word[0])-97])\n",
|
||||
" if (word in mots):\n",
|
||||
" return([word, scoreTable[ord(word[0])-97][mots.index(word)][1]])\n",
|
||||
" return(-1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 68,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"for Review in reviews:\n",
|
||||
" #print(Review)\n",
|
||||
" reviewScore = 0\n",
|
||||
" miniKey = []\n",
|
||||
" #recherche de mots positifs/négatifs\n",
|
||||
" review = list(e.strip(',.') for e in Review.split())\n",
|
||||
" for Word in review:\n",
|
||||
" word = Word.lower()\n",
|
||||
" temp = search(word)\n",
|
||||
" #recherche d'un éventuel mot-clé associé à ce caractère positif/négatif\n",
|
||||
" if (temp != -1):\n",
|
||||
" for key in keys:\n",
|
||||
" if (key in review):\n",
|
||||
" cles = list(e[0] for e in keyWords)\n",
|
||||
" if (key in cles):\n",
|
||||
" keyWords[cles.index(key)][1] += temp[1]\n",
|
||||
" else:\n",
|
||||
" keyWords.append([key, temp[1]])\n",
|
||||
" miniKey.append(key)\n",
|
||||
" reviewScore += temp[1]\n",
|
||||
" averageScore += reviewScore\n",
|
||||
" #Caractéristique de l'avis analysé\n",
|
||||
" miniKey = set(miniKey)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Troisiere partie : affichage des resultats"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 69,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Format: [[Mot-clé, score associé]]\n",
|
||||
"[['cher', -2.7], ['orientation', -2.7], ['abordable', 3.4000000000000004], ['personnel', -3.0999999999999996], [\"s'orienter\", -0.10000000000000053], ['queue', 1.0000000000000004], ['orienter', -0.30000000000000004], ['prix', 7.4], ['raisonnable', 7.4], [\"d'attente\", 0.9000000000000012]]\n",
|
||||
"Nombre d'avis: 23\n",
|
||||
"Score moyen d'un avis: 1.5652173913043481\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"averageScore /= len(reviews)\n",
|
||||
"print(\"Format: [[Mot-clé, score associé]]\")\n",
|
||||
"print(keyWords)\n",
|
||||
"print(\"Nombre d'avis: \", len(reviews))\n",
|
||||
"print(\"Score moyen d'un avis: \", averageScore)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
BIN
code/backend_reconnaissance/__pycache__/manager.cpython-38.pyc
Normal file
BIN
code/backend_reconnaissance/__pycache__/manager.cpython-38.pyc
Normal file
Binary file not shown.
BIN
code/backend_reconnaissance/__pycache__/network.cpython-38.pyc
Normal file
BIN
code/backend_reconnaissance/__pycache__/network.cpython-38.pyc
Normal file
Binary file not shown.
Binary file not shown.
@@ -119,7 +119,6 @@ def init_database():
|
||||
if not os.path.isfile(os.path.join(data_dir, word)):
|
||||
for file in os.listdir(os.path.join(data_dir,word)):
|
||||
if os.path.isfile(os.path.join(data_dir, word,file)):
|
||||
print(word,os.path.join(data_dir, word,file))
|
||||
words.append(word)
|
||||
files.append(os.path.join(data_dir, word,file))
|
||||
return words,files
|
||||
@@ -130,7 +129,23 @@ def get_word_metadata(word):
|
||||
return data[word]
|
||||
|
||||
#Todo : detecte si pas de note donnée
|
||||
def get_grade():
|
||||
def record():
|
||||
sr = 44100 # fréquence d'échantillonnage
|
||||
duration = 6 # durée d'enregistrement en secondes
|
||||
filename = "recording" # nom du fichier à enregistrer
|
||||
record_audio(filename, duration, sr)
|
||||
audio_query,sr = librosa.load(f'{filename}.wav', sr=sr)
|
||||
return audio_query,sr
|
||||
|
||||
def analyze(audio_query,sr):
|
||||
coupe_silence(audio_query)
|
||||
words, files = init_database()
|
||||
audio_train_list = [librosa.load(file, sr=sr)[0] for file in files]
|
||||
recognized_word_index = recognize_speech(audio_query, audio_train_list, sr)
|
||||
recognized_word = words[recognized_word_index]
|
||||
return get_word_metadata(recognized_word)
|
||||
|
||||
def test():
|
||||
sr = 44100 # fréquence d'échantillonnage
|
||||
duration = 6 # durée d'enregistrement en secondes
|
||||
filename = "recording" # nom du fichier à enregistrer
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import cv2
|
||||
import mediapipe as mp
|
||||
import numpy as np
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class HandDetector():
|
||||
def __init__(self):
|
||||
self.camera_id = int(os.getenv("CAMERA_ID"))
|
||||
self.mp_drawing = mp.solutions.drawing_utils
|
||||
self.mp_drawing_styles = mp.solutions.drawing_styles
|
||||
self.mp_hands = mp.solutions.hands
|
||||
self.cap = cv2.VideoCapture(0)
|
||||
self.cap = cv2.VideoCapture(self.camera_id)
|
||||
self.hands = self.mp_hands.Hands(
|
||||
model_complexity=0,
|
||||
min_detection_confidence=0.5,
|
||||
@@ -51,8 +56,9 @@ class HandDetector():
|
||||
# To improve performance, optionally mark the image as not writeable to
|
||||
# pass by reference.
|
||||
image.flags.writeable = False
|
||||
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
||||
results = self.hands.process(image)
|
||||
# print(results)
|
||||
|
||||
if results.multi_hand_landmarks:
|
||||
handsPositions = []
|
||||
for hand_landmarks in results.multi_hand_landmarks:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from hand_detector import HandDetector
|
||||
from audio_detector import get_grade
|
||||
from audio_detector import record, analyze, test
|
||||
from network import ApiClient, WebsocketServer
|
||||
import time
|
||||
|
||||
@@ -57,23 +57,26 @@ class Manager():
|
||||
state, coords, size, finalDecision = res
|
||||
self.server.sendMessage({"type": "effects", "effects": [{"type": state, "x":coords[0], "y": coords[1], "width": size, "height": size}]})
|
||||
self.isLastHandPacketEmpty = False
|
||||
self.timeLastChange = time.time()
|
||||
if(finalDecision != False):
|
||||
self.avis["note"] = 10 if finalDecision == "thumbs_up" else 0
|
||||
self.state = 2
|
||||
self.timeLastChange = time.time()
|
||||
self.server.sendMessage({"type": "state", "state": 2})
|
||||
elif self.isLastHandPacketEmpty == False:
|
||||
self.server.sendMessage({"type":"effects","effects":[]})
|
||||
self.isLastHandPacketEmpty = True
|
||||
|
||||
def audio(self):
|
||||
result = get_grade()
|
||||
audio_query,sr = record()
|
||||
self.server.sendMessage({"type":"recording_done"})
|
||||
result = analyze(audio_query,sr)
|
||||
# result = test()
|
||||
if(result != False):
|
||||
print("mot detecté : " + result["display"] + " avec une note de " + str(result["grade"]))
|
||||
self.server.sendMessage({"type":"new_grade","word":result["display"]})
|
||||
self.avis["notes_autres"]["test"] = result["grade"]
|
||||
time.sleep(3)
|
||||
self.state = 3
|
||||
self.timeLastChange = time.time()
|
||||
self.server.sendMessage({"type": "state", "state": 3})
|
||||
|
||||
def thankYou(self):
|
||||
|
||||
@@ -4,6 +4,9 @@ import json
|
||||
import os
|
||||
import threading
|
||||
import websockets
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
class WebsocketServer(threading.Thread):
|
||||
def __init__(self, onMessage, port=os.getenv("PORT"), host=os.getenv("HOST")):
|
||||
|
||||
Binary file not shown.
@@ -85,22 +85,22 @@ services:
|
||||
# #Backend de la borne : scripts pythons de reconnaissances video et audio
|
||||
# #Envoient les infos a l'interface de la borne par websocket pour mettre a jour l'interface rapidement
|
||||
# #Met a jour les avis en faisant des requêtes a l'API
|
||||
backend_reconnaissance:
|
||||
build: ./backend_reconnaissance
|
||||
container_name: backend_reconnaissance
|
||||
restart: always
|
||||
devices:
|
||||
- /dev/video3:/dev/video0
|
||||
- /dev/snd:/dev/snd
|
||||
environment:
|
||||
- PORT=5000
|
||||
- HOST=backend_reconnaissance
|
||||
- API_HOST=reviews_api
|
||||
- API_PORT=8080
|
||||
ports:
|
||||
#Ce container est le serveur websocker dont le client est l'interface de la borne qui tourne dans le navigateur
|
||||
- 5000:5000
|
||||
user: root
|
||||
# backend_reconnaissance:
|
||||
# build: ./backend_reconnaissance
|
||||
# container_name: backend_reconnaissance
|
||||
# restart: always
|
||||
# devices:
|
||||
# - /dev/video3:/dev/video0
|
||||
# - /dev/snd:/dev/snd
|
||||
# environment:
|
||||
# - PORT=5000
|
||||
# - HOST=backend_reconnaissance
|
||||
# - API_HOST=reviews_api
|
||||
# - API_PORT=8080
|
||||
# ports:
|
||||
# #Ce container est le serveur websocker dont le client est l'interface de la borne qui tourne dans le navigateur
|
||||
# - 5000:5000
|
||||
# user: root
|
||||
|
||||
video_loopback:
|
||||
build: ./video_loopback
|
||||
|
||||
@@ -7,16 +7,21 @@ class AudioPage {
|
||||
set enabled(isEnabled) {
|
||||
this.isEnabled = isEnabled;
|
||||
this.DOMElement.style.display = isEnabled ? "block" : "none";
|
||||
document.getElementById("grade").innerHTML = "";
|
||||
document.getElementById("audio_status").innerHTML = "Enregistrement...";
|
||||
}
|
||||
onRecordingDone() {
|
||||
if(this.isEnabled) {
|
||||
document.getElementById("audio_status").innerHTML = "Traitement...";
|
||||
}
|
||||
}
|
||||
|
||||
setGrade(grade) {
|
||||
if(this.isEnabled) {
|
||||
document.getElementById("grade").innerHTML = grade.toString();
|
||||
document.getElementById("audio_status").innerHTML = grade;
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
document.getElementById("grade").innerHTML = "";
|
||||
document.getElementById("audio_status").innerHTML = "Enregistrement...";
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
class WebsocketClient {
|
||||
constructor(onNewEffects, onNewState, onNewGrade, onReset) {
|
||||
constructor(onNewEffects, onNewState, onNewGrade, onReset, onRecordingDone) {
|
||||
this.socket = new WebSocket("ws://localhost:5000");
|
||||
this.socket.addEventListener("open", (event) => {
|
||||
this.socket.send("connected");
|
||||
@@ -13,10 +13,13 @@ class WebsocketClient {
|
||||
}else if(msg.type == "state") {
|
||||
onNewState(msg.state);
|
||||
}else if(msg.type == "new_grade") {
|
||||
onNewGrade(Number(msg.grade));
|
||||
onNewGrade(msg.word);
|
||||
}else if(msg.type == "reset") {
|
||||
onReset();
|
||||
}
|
||||
else if(msg.type == "recording_done") {
|
||||
onRecordingDone();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ class StateManager {
|
||||
(state) => this.setState(state),
|
||||
(grade) => this._audioPage.setGrade(grade),
|
||||
() => this.reset(),
|
||||
() => this._audioPage.onRecordingDone(),
|
||||
);
|
||||
|
||||
this._sleepingPage.enabled = true;
|
||||
|
||||
@@ -35,17 +35,15 @@
|
||||
<div class="title">
|
||||
<h1>Dites-nous en plus</h1>
|
||||
</div>
|
||||
<p>Donnez une note sur 10 au critère suivant</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Critère</td>
|
||||
<th>Note / 10</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Calme</td>
|
||||
<td><span id="grade"></span>/10</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>Comment avez vous trouvé l'exposition ... ?</p>
|
||||
<p>Dites un mot parmis la liste suivante</p>
|
||||
<ul>
|
||||
<li>J'ai beaucoup aimé</li>
|
||||
<li>génial</li>
|
||||
<li>Ennuyant</li>
|
||||
<li>Nul</li>
|
||||
</ul>
|
||||
<p>Mot détécté : <span id="audio_status"></span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="thank-you">
|
||||
|
||||
Reference in New Issue
Block a user