Hugo.V commited on
Commit
c8275dd
·
1 Parent(s): 53fdea6

annotation en plus pour la presentation

Browse files
Files changed (1) hide show
  1. jericho/emotion.py +14 -18
jericho/emotion.py CHANGED
@@ -42,8 +42,8 @@ EAR_THRESHOLD = 0.20 # valeur limite d'ouverture de l'oeil
42
 
43
  class EMAFilter: # classe pour lisser les mouvements afin d eviter les micromouvements
44
 
45
- def __init__(self, alpha=0.5):
46
- self.alpha = alpha
47
  self.state = {} # dictionnaire memorisant valeur precedente
48
 
49
  def __call__(self, key, value):
@@ -55,12 +55,12 @@ class EMAFilter: # classe pour lisser les mouvements afin d eviter les micromouv
55
 
56
 
57
 
58
- def _pts(lms, indices, w, h): # convertit point en px
59
  return np.array([[lms[i].x * w, lms[i].y * h] for i in indices])
60
 
61
- def ear(lms, indices, w, h): # calcule ouverture des yeaux
62
  p = _pts(lms, indices, w, h) # coordonnée de l'oeil
63
- A = np.linalg.norm(p[1]-p[5]) # distance vert oeil gauche
64
  B = np.linalg.norm(p[2]-p[4]) # " " " droit
65
  C = np.linalg.norm(p[0]-p[3]) # largeur oeil
66
  return (A+B) / (2.0 * C + 1e-6) #formul calculant ear
@@ -128,7 +128,7 @@ def emotions_records(records): # fonction analyse la video pour trouver les émo
128
  return float(np.mean([r[key] for r in valid_recs]))
129
 
130
  def get_std(key, func=None): # calcul ecart type signal
131
- if func: return max(float(np.std([func(r) for r in valid_recs])), 1e-6)
132
  return max(float(np.std([r[key] for r in valid_recs])), 1e-6)
133
 
134
 
@@ -195,7 +195,7 @@ def emotions_records(records): # fonction analyse la video pour trouver les émo
195
  else:
196
  emotion = "NEUTRE"
197
 
198
- # cooldown : on ne change d'emotion que si au moins 1 seconde s'est ecoulee depuis le dernier changement
199
  ts = rec.get("timestamp_s", 0.0)
200
  if emotion != current_emotion and (ts - emotion_change_time) >= EMOTION_MIN_DURATION:
201
  current_emotion = emotion
@@ -232,8 +232,6 @@ def annotate_frame(frame, lms, sig, w, h): # fonction pour annoter la video
232
  f"Yaw:{sig['yaw']:+.1f} Pitch:{sig['pitch']:+.1f} Roll:{sig['roll']:+.1f}", # orientation tete
233
  f"EAR L:{sig['ear_left']:.3f} R:{sig['ear_right']:.3f}", # ouverture des yeux
234
  f"MAR:{sig['mar']:.3f} Smile:{sig['smile']:.3f}", # ouverture de la bouche et le sourire
235
- # f"BrowRaise L:{sig['brow_raise_left']:.3f} R:{sig['brow_raise_right']:.3f}", # hauteur des sourcils
236
- # f"BrowFurrow:{sig['brow_furrow']:.3f}", # froncement
237
  f"T:{sig['timestamp_s']:.2f}s", # Affiche le temps en secondes
238
  ]
239
  for i, line in enumerate(lines): # boucle pour ecrire les lignes au dessus
@@ -286,8 +284,7 @@ def _face_frame_runs(records):
286
 
287
  def merge_filtered_audio(original_video, silent_video, output_path, face_runs, fps):
288
 
289
- #Recolle l'audio en supprimant les mêmes intervalles que les frames sans visage.
290
- #Si l'audio n'est pas disponible, la vidéo muette reste utilisée.
291
 
292
  if not _ffmpeg_available():
293
  return False
@@ -295,10 +292,9 @@ def merge_filtered_audio(original_video, silent_video, output_path, face_runs, f
295
  if not face_runs:
296
  return False
297
 
298
- # Trop de micro-coupures peut créer une commande ffmpeg très longue.
299
- # Dans ce cas, on garde la vidéo synchronisée robot/visuel sans audio.
300
  if len(face_runs) > 300:
301
- print(" Trop de segments audio à concaténer : export vidéo muette synchronisée.")
302
  return False
303
 
304
  # aselect garde uniquement les morceaux où un visage existait, puis asetpts compacte la timeline audio.
@@ -348,9 +344,9 @@ def process_video(video_path, output_dir):
348
  base_options=base_opts, # Passe les options de base
349
  running_mode=mp_vision.RunningMode.VIDEO,
350
  num_faces=1, #un visage
351
- min_face_detection_confidence=0.5, # Tolérance de détection du visage à 50%
352
- min_face_presence_confidence=0.5, # Tolérance de maintien du visage à 50%
353
- min_tracking_confidence=0.5, # Tolérance de suivi de mouvement à 50%
354
  output_face_blendshapes=False,
355
  output_facial_transformation_matrixes=True, # matrice pour suivi de l'orientation
356
  )
@@ -496,7 +492,7 @@ def process_video(video_path, output_dir):
496
  "timeline": "compact_face_only",
497
  "frames": compact_records,
498
  },
499
- f, ensure_ascii=False, indent=2 # Avec une jolie indentation (2 espaces) pour que ça soit lisible
500
  )
501
  print(f"\n fait, {skipped_count} frames sans visage supprimées. Fichiers dans : {os.path.abspath(output_dir)}/")
502
 
 
42
 
43
  class EMAFilter: # classe pour lisser les mouvements afin d eviter les micromouvements
44
 
45
+ def __init__(self):
46
+ self.alpha = 0.5
47
  self.state = {} # dictionnaire memorisant valeur precedente
48
 
49
  def __call__(self, key, value):
 
55
 
56
 
57
 
58
+ def _pts(lms, indices, w, h): # convertit point en px car mediapipe utilise les pourcentage donc c plus facile pour
59
  return np.array([[lms[i].x * w, lms[i].y * h] for i in indices])
60
 
61
+ def ear(lms, indices, w, h): # calcule ouverture des yeaux| lms landmarks contient coordonnées du visages et indices ctient les points de la zone concernes
62
  p = _pts(lms, indices, w, h) # coordonnée de l'oeil
63
+ A = np.linalg.norm(p[1]-p[5]) # distance verti oeil gauche
64
  B = np.linalg.norm(p[2]-p[4]) # " " " droit
65
  C = np.linalg.norm(p[0]-p[3]) # largeur oeil
66
  return (A+B) / (2.0 * C + 1e-6) #formul calculant ear
 
128
  return float(np.mean([r[key] for r in valid_recs]))
129
 
130
  def get_std(key, func=None): # calcul ecart type signal
131
+ if func: return max(float(np.std([func(r) for r in valid_recs])), 1e-6) # pour pas prendre si la personne ne bouge pas
132
  return max(float(np.std([r[key] for r in valid_recs])), 1e-6)
133
 
134
 
 
195
  else:
196
  emotion = "NEUTRE"
197
 
198
+ #changement d'emotion que 1 x par sec max
199
  ts = rec.get("timestamp_s", 0.0)
200
  if emotion != current_emotion and (ts - emotion_change_time) >= EMOTION_MIN_DURATION:
201
  current_emotion = emotion
 
232
  f"Yaw:{sig['yaw']:+.1f} Pitch:{sig['pitch']:+.1f} Roll:{sig['roll']:+.1f}", # orientation tete
233
  f"EAR L:{sig['ear_left']:.3f} R:{sig['ear_right']:.3f}", # ouverture des yeux
234
  f"MAR:{sig['mar']:.3f} Smile:{sig['smile']:.3f}", # ouverture de la bouche et le sourire
 
 
235
  f"T:{sig['timestamp_s']:.2f}s", # Affiche le temps en secondes
236
  ]
237
  for i, line in enumerate(lines): # boucle pour ecrire les lignes au dessus
 
284
 
285
  def merge_filtered_audio(original_video, silent_video, output_path, face_runs, fps):
286
 
287
+
 
288
 
289
  if not _ffmpeg_available():
290
  return False
 
292
  if not face_runs:
293
  return False
294
 
295
+ # Trop de micro-coupures vidéo synchronisée robot/visuel sans audio.
 
296
  if len(face_runs) > 300:
297
+ print(" son pas synchronisé à la video car traitement trop long.")
298
  return False
299
 
300
  # aselect garde uniquement les morceaux où un visage existait, puis asetpts compacte la timeline audio.
 
344
  base_options=base_opts, # Passe les options de base
345
  running_mode=mp_vision.RunningMode.VIDEO,
346
  num_faces=1, #un visage
347
+ min_face_detection_confidence=0.5, # toléerance de détection du visage à 50%
348
+ min_face_presence_confidence=0.5, # tolerance de maintien du visage à 50%
349
+ min_tracking_confidence=0.5, # tolerance de suivi de mouvement à 50%
350
  output_face_blendshapes=False,
351
  output_facial_transformation_matrixes=True, # matrice pour suivi de l'orientation
352
  )
 
492
  "timeline": "compact_face_only",
493
  "frames": compact_records,
494
  },
495
+ f, ensure_ascii=False, indent=2
496
  )
497
  print(f"\n fait, {skipped_count} frames sans visage supprimées. Fichiers dans : {os.path.abspath(output_dir)}/")
498