-
Notifications
You must be signed in to change notification settings - Fork 3
/
utils.py
77 lines (68 loc) · 3.34 KB
/
utils.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
'''
Description:
Author: notplus
Date: 2021-11-18 10:29:38
LastEditors: notplus
LastEditTime: 2021-11-22 13:53:53
FilePath: /utils.py
Copyright (c) 2021 notplus
'''
import cv2
import numpy as np
def calculate_pitch_yaw_roll(landmarks_2D,
cam_w=256,
cam_h=256,
radians=False):
""" Return the the pitch yaw and roll angles associated with the input image.
@param radians When True it returns the angle in radians, otherwise in degrees.
"""
assert landmarks_2D is not None, 'landmarks_2D is None'
# Estimated camera matrix values.
c_x = cam_w / 2
c_y = cam_h / 2
f_x = c_x / np.tan(60 / 2 * np.pi / 180)
f_y = f_x
camera_matrix = np.float32([[f_x, 0.0, c_x], [0.0, f_y, c_y],
[0.0, 0.0, 1.0]])
camera_distortion = np.float32([0.0, 0.0, 0.0, 0.0, 0.0])
# dlib (68 landmark) trached points
# TRACKED_POINTS = [17, 21, 22, 26, 36, 39, 42, 45, 31, 35, 48, 54, 57, 8]
# wflw(98 landmark) trached points
# TRACKED_POINTS = [33, 38, 50, 46, 60, 64, 68, 72, 55, 59, 76, 82, 85, 16]
# X-Y-Z with X pointing forward and Y on the left and Z up.
# The X-Y-Z coordinates used are like the standard coordinates of ROS (robotic operative system)
# OpenCV uses the reference usually used in computer vision:
# X points to the right, Y down, Z to the front
landmarks_3D = np.float32([
[6.825897, 6.760612, 4.402142], # LEFT_EYEBROW_LEFT,
[1.330353, 7.122144, 6.903745], # LEFT_EYEBROW_RIGHT,
[-1.330353, 7.122144, 6.903745], # RIGHT_EYEBROW_LEFT,
[-6.825897, 6.760612, 4.402142], # RIGHT_EYEBROW_RIGHT,
[5.311432, 5.485328, 3.987654], # LEFT_EYE_LEFT,
[1.789930, 5.393625, 4.413414], # LEFT_EYE_RIGHT,
[-1.789930, 5.393625, 4.413414], # RIGHT_EYE_LEFT,
[-5.311432, 5.485328, 3.987654], # RIGHT_EYE_RIGHT,
[-2.005628, 1.409845, 6.165652], # NOSE_LEFT,
[-2.005628, 1.409845, 6.165652], # NOSE_RIGHT,
[2.774015, -2.080775, 5.048531], # MOUTH_LEFT,
[-2.774015, -2.080775, 5.048531], # MOUTH_RIGHT,
[0.000000, -3.116408, 6.097667], # LOWER_LIP,
[0.000000, -7.415691, 4.070434], # CHIN
])
landmarks_2D = np.asarray(landmarks_2D, dtype=np.float32).reshape(-1, 2)
# Applying the PnP solver to find the 3D pose of the head from the 2D position of the landmarks.
# retval - bool
# rvec - Output rotation vector that, together with tvec, brings points from the world coordinate system to the camera coordinate system.
# tvec - Output translation vector. It is the position of the world origin (SELLION) in camera co-ords
_, rvec, tvec = cv2.solvePnP(landmarks_3D, landmarks_2D, camera_matrix,
camera_distortion)
# Get as input the rotational vector, Return a rotational matrix
# const double PI = 3.141592653;
# double thetaz = atan2(r21, r11) / PI * 180;
# double thetay = atan2(-1 * r31, sqrt(r32*r32 + r33*r33)) / PI * 180;
# double thetax = atan2(r32, r33) / PI * 180;
rmat, _ = cv2.Rodrigues(rvec)
pose_mat = cv2.hconcat((rmat, tvec))
_, _, _, _, _, _, euler_angles = cv2.decomposeProjectionMatrix(pose_mat)
return map(lambda k: k[0],
euler_angles) # euler_angles contain (pitch, yaw, roll)