-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathirisDepth.py
More file actions
153 lines (125 loc) · 6.31 KB
/
Copy pathirisDepth.py
File metadata and controls
153 lines (125 loc) · 6.31 KB
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
import argparse
import cv2
import mediapipe as mp
import numpy as np
from helper.iris_lm_depth import from_landmarks_to_depth
from videosource import FileSource, WebcamSource
# Device dictionary for focal length (in px)
device_dict = {
"phone": 1700,
"computer": 1000
}
mp_face_mesh = mp.solutions.face_mesh
points_idx = [33, 133, 362, 263]
points_idx = list(set(points_idx))
points_idx.sort()
left_eye_landmarks_id = np.array([33, 133])
right_eye_landmarks_id = np.array([362, 263])
dist_coeff = np.zeros((4, 1))
YELLOW = (0, 255, 255)
GREEN = (0, 255, 0)
BLUE = (255, 0, 0)
LIGHT_RED = (0, 0, 200)
RED = (0, 0, 255)
GREY = (220, 220, 220)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
SMALL_CIRCLE_SIZE = 1
MEDIUM_CIRCLE_SIZE = 2
LARGE_CIRCLE_SIZE = 3
def main(input, device):
if input is None:
frame_height, frame_width = (720, 1280)
source = WebcamSource(width=frame_width, height=frame_height)
else:
source = FileSource(input)
frame_width, frame_height = (int(i) for i in source.get_image_size())
image_size = (frame_width, frame_height)
# pseudo camera internals
focal_length = device_dict.get(device)
landmarks = None
smooth_left_depth = -1
smooth_right_depth = -1
smooth_factor = 0.1
with mp_face_mesh.FaceMesh(static_image_mode=False, min_detection_confidence=0.5, min_tracking_confidence=0.5) as face_mesh:
for idx, (frame, frame_rgb) in enumerate(source):
results = face_mesh.process(frame_rgb)
multi_face_landmarks = results.multi_face_landmarks
if multi_face_landmarks:
face_landmarks = results.multi_face_landmarks[0]
landmarks = np.array([(lm.x, lm.y, lm.z) for lm in face_landmarks.landmark])
landmarks = landmarks.T
(left_depth, left_iris_size, left_iris_landmarks, left_eye_contours) = from_landmarks_to_depth(frame_rgb, landmarks[:, left_eye_landmarks_id], image_size, is_right_eye=False, focal_length=focal_length)
(right_depth, right_iris_size, right_iris_landmarks, right_eye_contours) = from_landmarks_to_depth(frame_rgb, landmarks[:, right_eye_landmarks_id], image_size, is_right_eye=True, focal_length=focal_length)
if smooth_right_depth < 0:
smooth_right_depth = right_depth
else:
smooth_right_depth = (smooth_right_depth * (1 - smooth_factor) + right_depth * smooth_factor)
if smooth_left_depth < 0:
smooth_left_depth = left_depth
else:
smooth_left_depth = (smooth_left_depth * (1 - smooth_factor) + left_depth * smooth_factor)
print(
f"depth in cm: {smooth_left_depth / 10:.2f}, {smooth_right_depth / 10:.2f}"
)
print(f"size: {left_iris_size:.2f}, {right_iris_size:.2f}")
if landmarks is not None:
# draw subset of facemesh
for ii in points_idx:
pos = (np.array(image_size) * landmarks[:2, ii]).astype(np.int32)
frame = cv2.circle(frame, tuple(pos), MEDIUM_CIRCLE_SIZE, YELLOW, -1)
# draw eye contours
eye_landmarks = np.concatenate(
[
right_eye_contours,
left_eye_contours,
]
)
for landmark in eye_landmarks:
pos = (np.array(image_size) * landmark[:2]).astype(np.int32)
frame = cv2.circle(frame, tuple(pos), SMALL_CIRCLE_SIZE, LIGHT_RED, -1)
# draw iris landmarks
iris_landmarks = np.concatenate(
[
right_iris_landmarks,
left_iris_landmarks,
]
)
for landmark in iris_landmarks:
pos = (np.array(image_size) * landmark[:2]).astype(np.int32)
frame = cv2.circle(frame, tuple(pos), LARGE_CIRCLE_SIZE, WHITE, -1)
# write depth values into frame
text_color = WHITE
rectangle_bgr = BLACK
scale = 1.5
thickness = 2
# text and box for OD (right eye)
text_od = "OD: {:.2f}cm".format(smooth_right_depth / 10)
(text_width_od, text_height_od) = cv2.getTextSize(text_od, cv2.FONT_HERSHEY_SIMPLEX, scale, thickness)[0]
text_offset_x_od = 20
text_offset_y_od = text_height_od + 40 # a bit of padding
box_coords_od = ((text_offset_x_od, text_offset_y_od), (text_offset_x_od + text_width_od + 20, text_offset_y_od - text_height_od - 20)) # more padding
frame = cv2.rectangle(frame, box_coords_od[0], box_coords_od[1], rectangle_bgr, cv2.FILLED)
frame = cv2.putText(frame, text_od, (text_offset_x_od + 10, text_offset_y_od - 10), cv2.FONT_HERSHEY_SIMPLEX, scale, text_color, thickness) # and more padding
# text and box for OS (left eye)
text_os = "OS: {:.2f}cm".format(smooth_left_depth / 10)
(text_width_os, text_height_os) = cv2.getTextSize(text_os, cv2.FONT_HERSHEY_SIMPLEX, scale, thickness)[0]
text_offset_x_os = 20
text_offset_y_os = 2 * text_height_os + 60 # move down by previous text height and some padding
box_coords_os = ((text_offset_x_os, text_offset_y_os), (text_offset_x_os + text_width_os + 20, text_offset_y_os - text_height_os - 20)) # more padding
frame = cv2.rectangle(frame, box_coords_os[0], box_coords_os[1], rectangle_bgr, cv2.FILLED)
frame = cv2.putText(frame, text_os, (text_offset_x_os + 10, text_offset_y_os - 10), cv2.FONT_HERSHEY_SIMPLEX, scale, text_color, thickness) # and more padding
source.show(frame)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Choose video file otherwise webcam is used."
)
parser.add_argument(
"-i", metavar="path-to-file", type=str, help="Path to video file"
)
parser.add_argument(
"-d", metavar="device", type=str, required=True, help="Specify the device type"
)
args = parser.parse_args()
input = args.i; device = args.d
main(input, device)