# MIT License # # Copyright (c) 2018 KwanHua Lee # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import cv2 import numpy as np class HeadPoseSolver: def __init__(self, image_width=640, image_height=480): self.rotation_vec = None self.translation_vec = None self.head_pose_box_points = np.float32([[10, 10.5, 17.0], [7, 7.5, 7.0], [7, -7.5, 7.0], [10, -10.5, 17.0], [-10, 10.5, 17.0], [-7.0, 7.5, 7.0], [-7.0, -7.5, 7.0], [-10.0, -10.5, 17.0]]) # From https://github.com/lincolnhard/head-pose-estimation/blob/master/video_test_shape.py self.face_model_points = np.float32([[6.825897, 6.760612, 4.402142], [1.330353, 7.122144, 6.903745], [-1.330353, 7.122144, 6.903745], [-6.825897, 6.760612, 4.402142], [5.311432, 5.485328, 3.987654], [1.789930, 5.393625, 4.413414], [-1.789930, 5.393625, 4.413414], [-5.311432, 5.485328, 3.987654], [2.005628, 1.409845, 6.165652], [-2.005628, 1.409845, 6.165652]]) K = [ image_width, 0.0, image_width / 2, 0, image_width, image_height / 2, 0, 0, 1 ] self.camera_matrix = np.array(K).reshape(3, 3).astype(np.float32) D = [0.0, 0.0, 0.0, 0.0, 0.0] self.distortion_coeffs = np.array(D).reshape(5, 1).astype(np.float32) def solve_head_pose(self, face_landmarks): indices = [17, 21, 22, 26, 36, 39, 42, 45, 31, 35] image_pts = np.zeros((len(indices), 2)) for i in range(len(indices)): part = face_landmarks.part(indices[i]) image_pts[i, 0] = part.x image_pts[i, 1] = part.y _, rotation_vec, translation_vec = cv2.solvePnP(self.face_model_points, image_pts, self.camera_matrix, self.distortion_coeffs) projected_head_pose_box_points, _ = cv2.projectPoints(self.head_pose_box_points, rotation_vec, translation_vec, self.camera_matrix, self.distortion_coeffs) projected_head_pose_box_points = tuple(map(tuple, projected_head_pose_box_points.reshape(8, 2))) # Calculate euler angle rotation_mat, _ = cv2.Rodrigues(rotation_vec) pose_mat = cv2.hconcat((rotation_mat, translation_vec)) _, _, _, _, _, _, euler_angles = cv2.decomposeProjectionMatrix(pose_mat) return projected_head_pose_box_points, euler_angles