-
Notifications
You must be signed in to change notification settings - Fork 335
/
eye_tracker.py
executable file
·208 lines (174 loc) · 5.69 KB
/
eye_tracker.py
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# -*- coding: utf-8 -*-
"""
Created on Thu Jul 30 19:21:18 2020
@author: hp
"""
import cv2
import numpy as np
from face_detector import get_face_detector, find_faces
from face_landmarks import get_landmark_model, detect_marks
def eye_on_mask(mask, side, shape):
"""
Create ROI on mask of the size of eyes and also find the extreme points of each eye
Parameters
----------
mask : np.uint8
Blank mask to draw eyes on
side : list of int
the facial landmark numbers of eyes
shape : Array of uint32
Facial landmarks
Returns
-------
mask : np.uint8
Mask with region of interest drawn
[l, t, r, b] : list
left, top, right, and bottommost points of ROI
"""
points = [shape[i] for i in side]
points = np.array(points, dtype=np.int32)
mask = cv2.fillConvexPoly(mask, points, 255)
l = points[0][0]
t = (points[1][1]+points[2][1])//2
r = points[3][0]
b = (points[4][1]+points[5][1])//2
return mask, [l, t, r, b]
def find_eyeball_position(end_points, cx, cy):
"""Find and return the eyeball positions, i.e. left or right or top or normal"""
x_ratio = (end_points[0] - cx)/(cx - end_points[2])
y_ratio = (cy - end_points[1])/(end_points[3] - cy)
if x_ratio > 3:
return 1
elif x_ratio < 0.33:
return 2
elif y_ratio < 0.33:
return 3
else:
return 0
def contouring(thresh, mid, img, end_points, right=False):
"""
Find the largest contour on an image divided by a midpoint and subsequently the eye position
Parameters
----------
thresh : Array of uint8
Thresholded image of one side containing the eyeball
mid : int
The mid point between the eyes
img : Array of uint8
Original Image
end_points : list
List containing the exteme points of eye
right : boolean, optional
Whether calculating for right eye or left eye. The default is False.
Returns
-------
pos: int
the position where eyeball is:
0 for normal
1 for left
2 for right
3 for up
"""
cnts, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
try:
cnt = max(cnts, key = cv2.contourArea)
M = cv2.moments(cnt)
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
if right:
cx += mid
cv2.circle(img, (cx, cy), 4, (0, 0, 255), 2)
pos = find_eyeball_position(end_points, cx, cy)
return pos
except:
pass
def process_thresh(thresh):
"""
Preprocessing the thresholded image
Parameters
----------
thresh : Array of uint8
Thresholded image to preprocess
Returns
-------
thresh : Array of uint8
Processed thresholded image
"""
thresh = cv2.erode(thresh, None, iterations=2)
thresh = cv2.dilate(thresh, None, iterations=4)
thresh = cv2.medianBlur(thresh, 3)
thresh = cv2.bitwise_not(thresh)
return thresh
def print_eye_pos(img, left, right):
"""
Print the side where eye is looking and display on image
Parameters
----------
img : Array of uint8
Image to display on
left : int
Position obtained of left eye.
right : int
Position obtained of right eye.
Returns
-------
None.
"""
if left == right and left != 0:
text = ''
if left == 1:
print('Looking left')
text = 'Looking left'
elif left == 2:
print('Looking right')
text = 'Looking right'
elif left == 3:
print('Looking up')
text = 'Looking up'
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img, text, (30, 30), font,
1, (0, 255, 255), 2, cv2.LINE_AA)
face_model = get_face_detector()
landmark_model = get_landmark_model()
left = [36, 37, 38, 39, 40, 41]
right = [42, 43, 44, 45, 46, 47]
cv2.namedWindow("image")
kernel = np.ones((9, 9), np.uint8)
def nothing(x):
pass
cv2.createTrackbar("threshold", "image", 75, 255, nothing)
def track_eye(video_path=None):
video_path = ""
cap = cv2.VideoCapture(video_path)
ret, img = cap.read()
thresh = img.copy()
while(True):
ret, img = cap.read()
rects = find_faces(img, face_model)
if not ret:
break
for rect in rects:
shape = detect_marks(img, landmark_model, rect)
mask = np.zeros(img.shape[:2], dtype=np.uint8)
mask, end_points_left = eye_on_mask(mask, left, shape)
mask, end_points_right = eye_on_mask(mask, right, shape)
mask = cv2.dilate(mask, kernel, 5)
eyes = cv2.bitwise_and(img, img, mask=mask)
mask = (eyes == [0, 0, 0]).all(axis=2)
eyes[mask] = [255, 255, 255]
mid = int((shape[42][0] + shape[39][0]) // 2)
eyes_gray = cv2.cvtColor(eyes, cv2.COLOR_BGR2GRAY)
threshold = cv2.getTrackbarPos('threshold', 'image')
_, thresh = cv2.threshold(eyes_gray, threshold, 255, cv2.THRESH_BINARY)
thresh = process_thresh(thresh)
eyeball_pos_left = contouring(thresh[:, 0:mid], mid, img, end_points_left)
eyeball_pos_right = contouring(thresh[:, mid:], mid, img, end_points_right, True)
print_eye_pos(img, eyeball_pos_left, eyeball_pos_right)
# for (x, y) in shape[36:48]:
# cv2.circle(img, (x, y), 2, (255, 0, 0), -1)
cv2.imshow('eyes', img)
cv2.imshow("image", thresh)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()