File size: 5,539 Bytes
959739d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import math
import mediapipe
import time
import cv2
from tqdm import tqdm
import numpy as np 
def EuclideanDistance(point1, point2):
        x1, y1 = point1
        x2, y2 = point2

        distance = math.sqrt((x2 - x1)**2 + (y2 - y1)**2)

        return distance

class BlinkDetector:
    def __init__(self):
        self.COUNTER = 0
        self.TOTAL_BLINKS = 0   # put 1 when divide 
        self.blink_start_time = 0
        self.blink_durations = []
        self.LEFT_EYE = [362, 382, 381, 380, 374, 373, 390, 249, 263, 466, 388, 387, 386, 385, 384, 398]
        self.RIGHT_EYE = [33, 7, 163, 144, 145, 153, 154, 155, 133, 173, 157, 158, 159, 160, 161, 246]

    
    def FaceMeshInitialiser(self, 

                            max_num_faces,

                            min_detection_confidence,

                            min_tracking_confidence):
        
        face_mesh = mediapipe.solutions.face_mesh.FaceMesh(max_num_faces=max_num_faces,
                                        min_detection_confidence=min_detection_confidence,
                                        min_tracking_confidence=min_tracking_confidence)
        
        return face_mesh
    
    def LandmarksDetector(self, 

                          frame,

                          face_mesh_results,

                          draw: bool=False

                          ):
        
        image_height, image_width = frame.shape[:2]
        mesh_coordinates = [(int(point.x * image_width), int(point.y * image_height)) for point in face_mesh_results.multi_face_landmarks[0].landmark]

        if draw:
            [cv2.circle(frame, i, 2, (0, 255, 0), -1) for i in mesh_coordinates]
        
        return mesh_coordinates
    
    def BlinkRatioCalculator(self, 

                             landmarks):
        
        right_eye_landmark1 = landmarks[self.RIGHT_EYE[0]]
        right_eye_landmark2 = landmarks[self.RIGHT_EYE[8]]
        right_eye_landmark3 = landmarks[self.RIGHT_EYE[12]]
        right_eye_landmark4 = landmarks[self.RIGHT_EYE[4]]

        left_eye_landmark1 = landmarks[self.LEFT_EYE[0]]
        left_eye_landmark2 = landmarks[self.LEFT_EYE[8]]
        left_eye_landmark3 = landmarks[self.LEFT_EYE[12]]
        left_eye_landmark4 = landmarks[self.LEFT_EYE[4]]

        right_eye_horizontal_distance = EuclideanDistance(right_eye_landmark1, right_eye_landmark2)
        right_eye_verticle_distance = EuclideanDistance(right_eye_landmark3, right_eye_landmark4)

        left_eye_horizontal_distance = EuclideanDistance(left_eye_landmark1, left_eye_landmark2)
        left_eye_verticle_distance = EuclideanDistance(left_eye_landmark3, left_eye_landmark4)
        try:
            right_eye_ratio = right_eye_horizontal_distance / right_eye_verticle_distance
        except:
            right_eye_ratio = 0 
        try:
            left_eye_ratio = left_eye_horizontal_distance / left_eye_verticle_distance
        except:
            left_eye_ratio=0
        # eyes_ratio = (right_eye_ratio + left_eye_ratio) / 2

        return [right_eye_ratio, left_eye_ratio]
    
    def BlinkCounter(self,

                     eyes_ratio):
        
        if eyes_ratio[0] > 4 or eyes_ratio[1] > 4:
            if self.COUNTER == 0:
                self.blink_start_time = time.time()
            self.COUNTER += 1
        else:
            if self.COUNTER > 4:
                self.TOTAL_BLINKS += 1
                blink_duration = time.time() - self.blink_start_time
                self.blink_durations.append(blink_duration)
                self.COUNTER = 0
        
        return [self.TOTAL_BLINKS, self.blink_durations]
    

def InitialiseVariables():
    return BlinkDetector()

class Facetrack(BlinkDetector):
    def __init__(self):
        super().__init__()

        # create object for mediapipe face_mesh
        self.mediapipe_face_mesh = self.FaceMeshInitialiser(max_num_faces=1,
                                                            min_detection_confidence=0.6,
                                                            min_tracking_confidence=0.7)
        self.frame = None
        self.avg_blink_duration=0
        self.list_blinks=[]

    def predict(self,img):
        self.rgb_frame = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
        self.results = self.mediapipe_face_mesh.process(self.rgb_frame)
        if self.results.multi_face_landmarks:
            self.mesh_coordinates = self.LandmarksDetector(img, self.results, draw=True)
            self.eyes_ratio = self.BlinkRatioCalculator(self.mesh_coordinates)
            self.list_blinks = self.BlinkCounter(self.eyes_ratio)
            if self.list_blinks[1]:
                try:
                    self.avg_blink_duration = sum(self.list_blinks[1]) / len(self.list_blinks[1])
                except:
                    self.avg_blink_duration = sum(self.list_blinks[1])

                self.blink_durations = self.list_blinks[1]
        if len(self.list_blinks)>0 :

            self.TOTAL_BLINKS = self.list_blinks[0]
        else:
            self.TOTAL_BLINKS = 0
            

def eye_track_predict(fc,frames,fps):
    preds=[]
    for frame in tqdm(frames):
        if frame is not None:
            frame=np.copy(frame)
            fc.predict(frame)
            data=fc.TOTAL_BLINKS
        else:
            data='frame error'
        preds.append(data)
    return preds,fc.blink_durations,fc.TOTAL_BLINKS