diff options
Diffstat (limited to 'megapixels/app/processors/face_pose.py')
| -rw-r--r-- | megapixels/app/processors/face_pose.py | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/megapixels/app/processors/face_pose.py b/megapixels/app/processors/face_pose.py new file mode 100644 index 00000000..f2548b32 --- /dev/null +++ b/megapixels/app/processors/face_pose.py @@ -0,0 +1,104 @@ +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, project_points=False): + # 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) + + result = {} + + # project points + if 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) + result['points_model'] = pts_model + result['points_image'] = pts_im + result['point_nose'] = tuple(landmarks[pose_points_idx[0]]) + + 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} + result['degrees'] = degrees + + return result + + + def draw_pose(self, 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 + + + def draw_degrees(self, 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)
\ No newline at end of file |
