drrobot9 commited on
Commit
06d31de
·
verified ·
1 Parent(s): ffdaad9

Update facial_diagnostics.py

Browse files
Files changed (1) hide show
  1. facial_diagnostics.py +92 -90
facial_diagnostics.py CHANGED
@@ -1,90 +1,92 @@
1
- # facial_diagnostics.py
2
-
3
-
4
- import cv2
5
- import numpy as np
6
- import mediapipe as mp
7
- import time
8
- import json
9
- import math
10
- import requests
11
- from statistics import median
12
-
13
-
14
- mp_face_mesh = mp.solutions.face_mesh
15
- FACE_MESH = mp_face_mesh.FaceMesh(
16
- static_image_mode=False,
17
- max_num_faces=1,
18
- refine_landmarks=True,
19
- min_detection_confidence=0.6,
20
- min_tracking_confidence=0.6
21
- )
22
-
23
- LEFT_EYE = [33,160,158,133,153,144]
24
- RIGHT_EYE = [362,385,387,263,373,380]
25
- MOUTH = [13,14,78,308]
26
- SMILE_L = 61
27
- SMILE_R = 291
28
-
29
-
30
- def eye_aspect_ratio(pts, idx):
31
- a = np.linalg.norm(np.array(pts[idx[1]]) - np.array(pts[idx[5]]))
32
- b = np.linalg.norm(np.array(pts[idx[2]]) - np.array(pts[idx[4]]))
33
- c = np.linalg.norm(np.array(pts[idx[0]]) - np.array(pts[idx[3]])) + 1e-8
34
- return float((a + b) / (2.0 * c))
35
-
36
- def mouth_ratio(pts):
37
- top, bottom, left, right = pts[MOUTH[0]], pts[MOUTH[1]], pts[MOUTH[2]], pts[MOUTH[3]]
38
- return float(np.linalg.norm(np.array(top)-np.array(bottom)) /
39
- (np.linalg.norm(np.array(left)-np.array(right)) + 1e-8))
40
-
41
- def head_tilt_angle(pts):
42
- left = np.mean([pts[33], pts[133]], axis=0)
43
- right = np.mean([pts[362], pts[263]], axis=0)
44
- diff = np.array(right) - np.array(left)
45
- return float(math.degrees(math.atan2(diff[1], diff[0])))
46
-
47
- def smile_symmetry(pts):
48
- left = np.array(pts[SMILE_L])
49
- right = np.array(pts[SMILE_R])
50
- center = (left + right) / 2
51
- L = np.linalg.norm(left - center)
52
- R = np.linalg.norm(right - center)
53
- return float(min(L, R) / max(L, R)) if L+R != 0 else 1.0
54
-
55
- def analyze_frame(frame):
56
- h, w, _ = frame.shape
57
- rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
58
- res = FACE_MESH.process(rgb)
59
-
60
- if not res.multi_face_landmarks:
61
- return {"found": False}
62
-
63
- lm = res.multi_face_landmarks[0]
64
- pts = [(int(p.x * w), int(p.y * h)) for p in lm.landmark]
65
-
66
- return {
67
- "found": True,
68
- "ear": (eye_aspect_ratio(pts, LEFT_EYE) + eye_aspect_ratio(pts, RIGHT_EYE))/2,
69
- "mouth_ratio": mouth_ratio(pts),
70
- "head_tilt": head_tilt_angle(pts),
71
- "smile_sym": smile_symmetry(pts)
72
- }
73
-
74
-
75
- def aggregate_metrics(metrics):
76
- if not metrics:
77
- return {}
78
-
79
- ears = [m["ear"] for m in metrics]
80
- mouths = [m["mouth_ratio"] for m in metrics]
81
- tilts = [m["head_tilt"] for m in metrics]
82
- smiles = [m["smile_sym"] for m in metrics]
83
-
84
- return {
85
- "frames": len(metrics),
86
- "ear_median": median(ears),
87
- "mouth_median": median(mouths),
88
- "tilt_median": median(tilts),
89
- "smile_median": median(smiles),
90
- }
 
 
 
1
+ # facial_diagnostics.py
2
+
3
+
4
+ import cv2
5
+ import numpy as np
6
+ import math
7
+ from statistics import median
8
+
9
+ # Facial metric helpers
10
+ LEFT_EYE_IDX = [33, 160, 158, 133, 153, 144]
11
+ RIGHT_EYE_IDX = [362, 385, 387, 263, 373, 380]
12
+ MOUTH_IDX = [13, 14, 78, 308]
13
+ SMILE_LEFT = 61
14
+ SMILE_RIGHT = 291
15
+
16
+ def eye_aspect_ratio(pts, idx):
17
+ a = np.linalg.norm(np.array(pts[idx[1]]) - np.array(pts[idx[5]]))
18
+ b = np.linalg.norm(np.array(pts[idx[2]]) - np.array(pts[idx[4]]))
19
+ c = np.linalg.norm(np.array(pts[idx[0]]) - np.array(idx[3])) + 1e-8
20
+ return float((a + b) / (2.0 * c))
21
+
22
+ def mouth_ratio(pts):
23
+ top, bottom, left, right = pts[MOUTH_IDX[0]], pts[MOUTH_IDX[1]], pts[MOUTH_IDX[2]], pts[MOUTH_IDX[3]]
24
+ return float(np.linalg.norm(np.array(top) - np.array(bottom)) /
25
+ (np.linalg.norm(np.array(left) - np.array(right)) + 1e-8))
26
+
27
+ def head_tilt_angle(pts):
28
+ left = np.mean([pts[33], pts[133]], axis=0)
29
+ right = np.mean([pts[362], pts[263]], axis=0)
30
+ diff = np.array(right) - np.array(left)
31
+ return float(math.degrees(math.atan2(diff[1], diff[0])))
32
+
33
+ def smile_symmetry(pts):
34
+ left = np.array(pts[SMILE_LEFT])
35
+ right = np.array(pts[SMILE_RIGHT])
36
+ center = (left + right) / 2
37
+ L = np.linalg.norm(left - center)
38
+ R = np.linalg.norm(right - center)
39
+ if L + R == 0:
40
+ return 1.0
41
+ return float(min(L, R) / max(L, R))
42
+
43
+ # Face mesh
44
+ import mediapipe as mp
45
+ mp_face_mesh = mp.solutions.face_mesh
46
+ FACE_MESH = mp_face_mesh.FaceMesh(
47
+ static_image_mode=False,
48
+ max_num_faces=1,
49
+ refine_landmarks=True,
50
+ min_detection_confidence=0.6,
51
+ min_tracking_confidence=0.6
52
+ )
53
+
54
+ def analyze_frame(frame):
55
+ h, w, _ = frame.shape
56
+ rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
57
+ res = FACE_MESH.process(rgb)
58
+
59
+ if not res.multi_face_landmarks:
60
+ return {"found": False}
61
+
62
+ lm = res.multi_face_landmarks[0]
63
+ pts = [(int(p.x * w), int(p.y * h)) for p in lm.landmark]
64
+
65
+ ear = (eye_aspect_ratio(pts, LEFT_EYE_IDX) + eye_aspect_ratio(pts, RIGHT_EYE_IDX)) / 2
66
+ mratio = mouth_ratio(pts)
67
+ tilt = head_tilt_angle(pts)
68
+ symmetry = smile_symmetry(pts)
69
+
70
+ return {
71
+ "found": True,
72
+ "ear": float(ear),
73
+ "mouth_ratio": float(mratio),
74
+ "head_tilt": float(tilt),
75
+ "smile_sym": float(symmetry)
76
+ }
77
+
78
+ def aggregate_metrics(metrics):
79
+ if not metrics:
80
+ return {}
81
+ ears = [m["ear"] for m in metrics]
82
+ mouths = [m["mouth_ratio"] for m in metrics]
83
+ tilts = [m["head_tilt"] for m in metrics]
84
+ smiles = [m["smile_sym"] for m in metrics]
85
+ return {
86
+ "count": len(metrics),
87
+ "ear_median": median(ears),
88
+ "mouth_median": median(mouths),
89
+ "tilt_median": median(tilts),
90
+ "smile_median": median(smiles)
91
+ }
92
+