Spaces:
Running
Running
Hugo.V commited on
Commit ·
c8275dd
1
Parent(s): 53fdea6
annotation en plus pour la presentation
Browse files- 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
|
| 46 |
-
self.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
|
| 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 |
-
#
|
| 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 |
-
|
| 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
|
| 299 |
-
# Dans ce cas, on garde la vidéo synchronisée robot/visuel sans audio.
|
| 300 |
if len(face_runs) > 300:
|
| 301 |
-
print("
|
| 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, #
|
| 352 |
-
min_face_presence_confidence=0.5, #
|
| 353 |
-
min_tracking_confidence=0.5, #
|
| 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
|
| 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 |
|