Spaces:
No application file
No application file
| import cv2 | |
| import mediapipe as mp | |
| import numpy as np | |
| import sys | |
| NOSE_TIP = 1 | |
| CHIN = 152 | |
| FOREHEAD = 10 | |
| LEFT_EYE_OUTER = 263 | |
| RIGHT_EYE_OUTER = 33 | |
| LEFT_MOUTH = 61 | |
| RIGHT_MOUTH = 291 | |
| TOP_LIP = 13 | |
| BOTTOM_LIP = 14 | |
| LEFT_EYE_TOP = 159 | |
| LEFT_EYE_BOTTOM = 145 | |
| RIGHT_EYE_TOP = 386 | |
| RIGHT_EYE_BOTTOM = 374 | |
| FACE_3D_MODEL = np.array([ | |
| [0.0, 0.0, 0.0], | |
| [0.0, -330.0, -65.0], | |
| [-225.0, 170.0, -135.0], | |
| [225.0, 170.0, -135.0], | |
| [-150.0, -150.0, -125.0], | |
| [150.0, -150.0, -125.0], | |
| ], dtype=np.float64) | |
| def dessiner_landmarks(frame, landmarks, frame_w, frame_h): | |
| for point in landmarks.landmark: | |
| x = int(point.x * frame_w) | |
| y = int(point.y * frame_h) | |
| cv2.circle(frame, (x, y), 1, (0, 200, 100), -1) | |
| def dessiner_axe_tete(frame, landmarks, frame_w, frame_h): | |
| image_points = np.array([ | |
| [landmarks[NOSE_TIP].x * frame_w, landmarks[NOSE_TIP].y * frame_h], | |
| [landmarks[CHIN].x * frame_w, landmarks[CHIN].y * frame_h], | |
| [landmarks[LEFT_EYE_OUTER].x * frame_w, landmarks[LEFT_EYE_OUTER].y * frame_h], | |
| [landmarks[RIGHT_EYE_OUTER].x * frame_w, landmarks[RIGHT_EYE_OUTER].y * frame_h], | |
| [landmarks[LEFT_MOUTH].x * frame_w, landmarks[LEFT_MOUTH].y * frame_h], | |
| [landmarks[RIGHT_MOUTH].x * frame_w, landmarks[RIGHT_MOUTH].y * frame_h], | |
| ], dtype=np.float64) | |
| focal_length = frame_w | |
| camera_matrix = np.array([ | |
| [focal_length, 0, frame_w / 2], | |
| [0, focal_length, frame_h / 2], | |
| [0, 0, 1] | |
| ], dtype=np.float64) | |
| success, rotation_vec, translation_vec = cv2.solvePnP( | |
| FACE_3D_MODEL, image_points, camera_matrix, np.zeros((4, 1)) | |
| ) | |
| if not success: | |
| return None, None, None | |
| axis_length = 80 | |
| axes_3d = np.float32([ | |
| [axis_length, 0, 0], | |
| [0, axis_length, 0], | |
| [0, 0, axis_length] | |
| ]) | |
| axes_2d, _ = cv2.projectPoints(axes_3d, rotation_vec, translation_vec, camera_matrix, np.zeros((4, 1))) | |
| nose = (int(image_points[0][0]), int(image_points[0][1])) | |
| cv2.line(frame, nose, tuple(axes_2d[0].ravel().astype(int)), (0, 0, 255), 2) | |
| cv2.line(frame, nose, tuple(axes_2d[1].ravel().astype(int)), (0, 255, 0), 2) | |
| cv2.line(frame, nose, tuple(axes_2d[2].ravel().astype(int)), (255, 0, 0), 2) | |
| rotation_mat, _ = cv2.Rodrigues(rotation_vec) | |
| angles, _, _, _, _, _ = cv2.RQDecomp3x3(rotation_mat) | |
| return angles[0], angles[1], angles[2] | |
| def dessiner_signaux(frame, lm, pitch, yaw, roll): | |
| face_height = abs(lm[CHIN].y - lm[FOREHEAD].y) | |
| face_width = abs(lm[LEFT_EYE_OUTER].x - lm[RIGHT_EYE_OUTER].x) | |
| mouth_opening = abs(lm[TOP_LIP].y - lm[BOTTOM_LIP].y) / face_height if face_height > 0 else 0 | |
| left_eye = abs(lm[LEFT_EYE_TOP].y - lm[LEFT_EYE_BOTTOM].y) / face_height if face_height > 0 else 0 | |
| right_eye = abs(lm[RIGHT_EYE_TOP].y - lm[RIGHT_EYE_BOTTOM].y) / face_height if face_height > 0 else 0 | |
| smile = abs(lm[LEFT_MOUTH].x - lm[RIGHT_MOUTH].x) / face_width if face_width > 0 else 0 | |
| lignes = [ | |
| f"Pitch : {pitch:.1f}", | |
| f"Yaw : {yaw:.1f}", | |
| f"Roll : {roll:.1f}", | |
| f"Bouche: {mouth_opening:.3f}", | |
| f"Oeil G: {left_eye:.3f}", | |
| f"Oeil D: {right_eye:.3f}", | |
| f"Sourire: {smile:.3f}", | |
| ] | |
| overlay = frame.copy() | |
| cv2.rectangle(overlay, (8, 8), (200, 18 + len(lignes) * 20), (0, 0, 0), -1) | |
| cv2.addWeighted(overlay, 0.5, frame, 0.5, 0, frame) | |
| for i, texte in enumerate(lignes): | |
| cv2.putText(frame, texte, (12, 24 + i * 20), | |
| cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 180), 1) | |
| def visualiser_live(chemin_video): | |
| cap = cv2.VideoCapture(chemin_video) | |
| if not cap.isOpened(): | |
| raise ValueError(f"Impossible d'ouvrir : {chemin_video}") | |
| frame_w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) | |
| frame_h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) | |
| fps = cap.get(cv2.CAP_PROP_FPS) | |
| wait_ms = max(1, int(1000 / fps)) | |
| with mp.solutions.face_mesh.FaceMesh( | |
| static_image_mode=False, | |
| max_num_faces=1, | |
| refine_landmarks=True, | |
| min_detection_confidence=0.5, | |
| min_tracking_confidence=0.5 | |
| ) as face_mesh: | |
| while True: | |
| ret, frame = cap.read() | |
| if not ret: | |
| break | |
| results = face_mesh.process(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) | |
| if results.multi_face_landmarks: | |
| face_landmarks = results.multi_face_landmarks[0] | |
| lm = face_landmarks.landmark | |
| dessiner_landmarks(frame, face_landmarks, frame_w, frame_h) | |
| pitch, yaw, roll = dessiner_axe_tete(frame, lm, frame_w, frame_h) | |
| if pitch is not None: | |
| dessiner_signaux(frame, lm, pitch, yaw, roll) | |
| cv2.imshow("Visualisation faciale - Q pour quitter", frame) | |
| if cv2.waitKey(wait_ms) & 0xFF == ord('q'): | |
| break | |
| cap.release() | |
| cv2.destroyAllWindows() | |
| if __name__ == "__main__": | |
| chemin = sys.argv[1] if len(sys.argv) > 1 else "test.mp4" | |
| visualiser_live(chemin) |