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
|
import os
from os.path import join
from pathlib import Path
import math
import cv2 as cv
import numpy as np
import imutils
from app.utils import im_utils, logger_utils
from app.models.bbox import BBox
from app.settings import app_cfg as cfg
from app.settings import types
class FacePoseDLIB:
dnn_size = (400, 400)
def __init__(self):
pass
def pose(self, landmarks, dim):
'''Calculates pose
'''
degrees = compute_pose_degrees(landmarks, dim)
return degrees
# -----------------------------------------------------------
# utilities
# -----------------------------------------------------------
def compute_pose_degrees(landmarks, dim):
# computes pose using 6 / 68 points from dlib face landmarks
# based on learnopencv.com and
# https://github.com/jerryhouuu/Face-Yaw-Roll-Pitch-from-Pose-Estimation-using-OpenCV/
# NB: not as accurate as MTCNN, see @jerryhouuu for ideas
pose_points_idx = (30, 8, 36, 45, 48, 54)
axis = np.float32([[500,0,0], [0,500,0], [0,0,500]])
# 3D model points.
model_points = np.array([
(0.0, 0.0, 0.0), # Nose tip
(0.0, -330.0, -65.0), # Chin
(-225.0, 170.0, -135.0), # Left eye left corner
(225.0, 170.0, -135.0), # Right eye right corne
(-150.0, -150.0, -125.0), # Left Mouth corner
(150.0, -150.0, -125.0) # Right mouth corner
])
# Assuming no lens distortion
dist_coeffs = np.zeros((4,1))
# find 6 pose points
pose_points = []
for j, idx in enumerate(pose_points_idx):
pt = landmarks[idx]
pose_points.append((pt[0], pt[1]))
pose_points = np.array(pose_points, dtype='double') # convert to double
# create camera matrix
focal_length = dim[0]
center = (dim[0]/2, dim[1]/2)
cam_mat = np.array(
[[focal_length, 0, center[0]],
[0, focal_length, center[1]],
[0, 1, 1]], dtype = "double")
# solve PnP for rotation and translation
(success, rot_vec, tran_vec) = cv.solvePnP(model_points, pose_points,
cam_mat, dist_coeffs,
flags=cv.SOLVEPNP_ITERATIVE)
# project points
#pts_im, jac = cv.projectPoints(axis, rot_vec, tran_vec, cam_mat, dist_coeffs)
#pts_model, jac2 = cv.projectPoints(model_points, rot_vec, tran_vec, cam_mat, dist_coeffs)
rvec_matrix = cv.Rodrigues(rot_vec)[0]
# convert to degrees
proj_matrix = np.hstack((rvec_matrix, tran_vec))
eulerAngles = cv.decomposeProjectionMatrix(proj_matrix)[6]
pitch, yaw, roll = [math.radians(x) for x in eulerAngles]
pitch = math.degrees(math.asin(math.sin(pitch)))
roll = -math.degrees(math.asin(math.sin(roll)))
yaw = math.degrees(math.asin(math.sin(yaw)))
degrees = {'pitch': pitch, 'roll': roll, 'yaw': yaw}
# add nose point
#pt_nose = tuple(landmarks[pose_points_idx[0]])
return degrees
#return pts_im, pts_model, degrees, pt_nose
def draw_pose(im, pts_im, pts_model, pt_nose):
cv.line(im, pt_nose, tuple(pts_im[1].ravel()), (0,255,0), 3) #GREEN
cv.line(im, pt_nose, tuple(pts_im[0].ravel()), (255,0,), 3) #BLUE
cv.line(im, pt_nose, tuple(pts_im[2].ravel()), (0,0,255), 3) #RED
return im
def draw_degrees(im, degrees, color=(0,255,0)):
for i, item in enumerate(degrees.items()):
k, v = item
t = '{}: {:.2f}'.format(k, v)
origin = (10, 30 + (25 * i))
cv.putText(im, t, origin, cv.FONT_HERSHEY_SIMPLEX, 0.5, color, thickness=2, lineType=2)
|