File size: 7,541 Bytes
ee94b36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
eb9b1e7
 
ee94b36
eb9b1e7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee94b36
eb9b1e7
 
 
 
 
 
 
 
 
ee94b36
eb9b1e7
 
 
ee94b36
 
eb9b1e7
 
 
 
 
 
ee94b36
 
eb9b1e7
 
 
 
 
ee94b36
 
eb9b1e7
 
ee94b36
eb9b1e7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee94b36
eb9b1e7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee94b36
eb9b1e7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee94b36
eb9b1e7
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import numpy as np 
from scipy.spatial import distance as dist
from imutils import face_utils
(lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"]
(rStart, rEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eye"]

def euclidean_distance(point1, point2):
    return np.linalg.norm(point1 - point2)

def eyebrow(landmarks,sizes):
    eyebrow_dist=[]
    for landmark,size in zip(landmarks,sizes):
        if landmark is not None:
            right_eyebrow_inner = landmark[21]
            left_eyebrow_inner = landmark[22]
            eyebrow_distance = euclidean_distance(right_eyebrow_inner, left_eyebrow_inner)
            normalized_eyebrow_distance = eyebrow_distance / size[0]
            
        else:
            normalized_eyebrow_distance=None
        eyebrow_dist.append(normalized_eyebrow_distance)
    return eyebrow_dist

def eye_aspect_ratio(eye):
    A = dist.euclidean(eye[1], eye[5])  # Vertical distance 1
    B = dist.euclidean(eye[2], eye[4])  # Vertical distance 2
    C = dist.euclidean(eye[0], eye[3])  # Horizontal distance
    ear = (A + B) / (2.0 * C)  # EAR formula
    return ear

def euclidean_distance(p1, p2):
    return np.linalg.norm(p1 - p2)

# Function to detect smiles based on mouth aspect ratio
def detect_smiles(landmarks_list, face_sizes, fps=30, consecutive_frames=2):
    smile_ratios = []  # Store the smile ratios for plotting
    smiles = []
    smile_durations = []  # To store the duration of each smile
    total_smiles = 0
    smile_in_progress = False
    smile_start_frame = None
    avg_dynamic_threshold=[]
    for frame_idx, (landmarks, face_size) in enumerate(zip(landmarks_list, face_sizes)):
        if landmarks is not None:
            # Use NumPy array indices for the relevant mouth landmarks
            left_corner = np.array(landmarks[48])
            right_corner = np.array(landmarks[54])
            top_lip = np.array(landmarks[51])
            bottom_lip = np.array(landmarks[57])
            
            mouth_width = euclidean_distance(left_corner, right_corner)
            mouth_height = euclidean_distance(top_lip, bottom_lip)
            
            face_width, face_height = face_size  # face_size is (width, height)
            
            if face_width > 0 and face_height > 0:
                normalized_mouth_width = mouth_width / face_width
                normalized_mouth_height = mouth_height / face_height
            else:
                normalized_mouth_width = 0
                normalized_mouth_height = 0
            
            smile_ratios.append(normalized_mouth_width)
            dynamic_threshold = 0.2 + (0.05 * face_width / 100)
            avg_dynamic_threshold.append(dynamic_threshold)
            # print(dynamic_threshold)
            # Check if the smile meets the threshold
            if (normalized_mouth_width > dynamic_threshold) and (normalized_mouth_height > 0.06):
                smiles.append(True)
                if not smile_in_progress:
                    smile_in_progress = True
                    smile_start_frame = frame_idx  # Record the start of the smile
            else:
                smiles.append(False)
                if smile_in_progress and (frame_idx - smile_start_frame >= consecutive_frames):
                    smile_in_progress = False
                    smile_end_frame = frame_idx
                    smile_duration = (smile_end_frame - smile_start_frame) / fps  # Calculate smile duration
                    smile_durations.append(smile_duration)
                    total_smiles += 1  # Increment total smile count
        else:
            smiles.append(None)
    try:
        avg_thr=sum(avg_dynamic_threshold)/len(avg_dynamic_threshold)
    except:
        avg_thr=0
    return smiles, smile_ratios, total_smiles, smile_durations,avg_thr


# Function to detect blinks based on the eye aspect ratio (EAR)
import numpy as np

# Function to detect blinks based on the eye aspect ratio (EAR)
def detect_blinks(landmarks_list, face_sizes, ear_threshold=0.24, consecutive_frames=2):
    ear_ratios = []  # Store EAR for plotting
    blinks = []
    
    # Variables to monitor consecutive low EAR values
    blink_count = 0
    consec_low_ear = 0
    
    for landmarks, face in zip(landmarks_list, face_sizes):
        if landmarks is not None:
            left_eye = landmarks[36:42]  # Points 36-41 (inclusive) for the left eye
            right_eye = landmarks[42:48]
            
            def eye_aspect_ratio(eye):
                A = euclidean_distance(eye[1], eye[5])
                B = euclidean_distance(eye[2], eye[4])
                C = euclidean_distance(eye[0], eye[3])
                ear = (A + B) / (2.0 * C)
                return ear
            
            left_ear = eye_aspect_ratio(left_eye)
            right_ear = eye_aspect_ratio(right_eye)
            avg_ear = (left_ear + right_ear) / 2.0
            
            ear_ratios.append(avg_ear)
            
            if avg_ear < ear_threshold:
                consec_low_ear += 1
            else:
                # If low EAR is detected for enough consecutive frames, count as a blink
                if consec_low_ear >= consecutive_frames:
                    blink_count += 1
                consec_low_ear = 0  # Reset the consecutive low EAR counter
        else:
            blinks.append(None)
    
    return blink_count, ear_ratios

# Function to detect yawns based on the vertical distance between top and bottom lips
# Function to detect yawns based on the vertical distance between top and bottom lips
def detect_yawns(landmarks_list, face_sizes, fps=30, consecutive_frames=3):
    yawn_ratios = []  # Store the yawn ratios for plotting
    yawns = []
    yawn_durations = []  # To store the duration of each yawn
    total_yawns = 0
    yawn_in_progress = False
    yawn_start_frame = None
    
    for frame_idx, (landmarks, face_size) in enumerate(zip(landmarks_list, face_sizes)):
        if landmarks is not None:
            top_lip = np.array(landmarks[51])
            bottom_lip = np.array(landmarks[57])
            
            mouth_height = euclidean_distance(top_lip, bottom_lip)
            face_width, face_height = face_size  # face_size is (width, height)
            
            if face_height > 0:
                normalized_mouth_height = mouth_height / face_height
            else:
                normalized_mouth_height = 0
            
            yawn_ratios.append(normalized_mouth_height)
            
            # Check if the yawn meets the threshold
            if normalized_mouth_height > 0.24:
                yawns.append(True)
                if not yawn_in_progress:
                    yawn_in_progress = True
                    yawn_start_frame = frame_idx  # Record the start of the yawn
            else:
                yawns.append(False)
                if yawn_in_progress and (frame_idx - yawn_start_frame >= consecutive_frames):
                    yawn_in_progress = False
                    yawn_end_frame = frame_idx
                    yawn_duration = (yawn_end_frame - yawn_start_frame) / fps  # Calculate yawn duration
                    yawn_durations.append(yawn_duration)
                    total_yawns += 1  # Increment total yawn count
        else:
            yawns.append(None)
    
    return yawns, yawn_ratios, total_yawns, yawn_durations