1、定义与用途
1.1 定义
头部姿态估计(Head Pose Estimation):通过一幅面部图像来获得头部的姿态角。在3D空间中,表示物体的旋转可以由三个欧拉角(Euler Angle)来表示,即分别计算pitch(围绕X轴旋转,上负下正),yaw(围绕Y轴旋转,左负右正) 和roll(围绕Z轴旋转,左正右负) ,学名俯仰角、偏航角和滚转角,通俗讲就是抬头、摇头和转头。
1.2 用途
(1)注意力检测:通过头部姿态可以判断人的注意力情况。比如可以检测长途司机是不是在目视前方、监控学生上课时是否集中精力
(2)行为分析:心虚的人容易左顾右盼,通过视频监控分析再辅助其他算法可以判断一个人是否具有不轨行为
(3)人机互动:人的头部动作有时可以传递信息。摇头在大多数人看来是否认,点头表示同意
(4)视线追踪:也可以称为眼球跟踪,准确的头部姿态估计能够提高视线追踪的精度
2、求解步骤
使用透视变换可以完成2D到3D的转换,可以简单的想象为将照片上的人脸图像按照一定的角度进行多点拉扯形成3D图像,然后根据角度来判断姿态。具体方法为,使用2D平面上人脸的特征点和3D空间内对应的坐标点,按照求解pnp问题的思路,找到一个映射关系,从而估计头部的姿态。
2.1 2D人脸关键点检测
使用深度学习方法,定位出人脸面部的关键区域位置,包括眉毛、眼睛、鼻子、嘴巴、脸部轮廓等,从中挑选部分关键点
"""
17左眉左上角/21左眉右角/22右眉左上角/26右眉右上角/36左眼左上角/39左眼右上角/42右眼左上角/
45右眼右上角/31鼻子左上角/35鼻子右上角/48左上角/54嘴右上角/57嘴中央下角/8下巴角
"""
# 像素坐标集合
image_pts = np.float32([shape[33], shape[38], shape[42], shape[46], shape[60],
shape[64], shape[68], shape[72], shape[55], shape[59],
shape[76], shape[82], shape[85], shape[16]])
2.2 3D人脸模型匹配
使用3D通用头部刚体模型,即在世界坐标系中,建立一个头部的模型,该模型在世界坐标系中的各个点的坐标是固定的。通过在2.1中检测的特征点,找到在该3D模型中对应的世界坐标系,例如2D中的鼻尖坐标为(u,v),则可以使用该模型中的鼻尖位置的3D坐标为(Uc,Vc,Wc),从而形成一个点对。当有足够的点对时,就可以求解出对应的旋转矩阵和平移矩阵,从而进行姿态估计
# 世界坐标系(UVW)
object_pts = np.float32([[6.825897, 6.760612, 4.402142], # 33左眉左上角
[1.330353, 7.122144, 6.903745], # 29左眉右角
[-1.330353, 7.122144, 6.903745], # 34右眉左角
[-6.825897, 6.760612, 4.402142], # 38右眉右上角
[5.311432, 5.485328, 3.987654], # 13左眼左上角
[1.789930, 5.393625, 4.413414], # 17左眼右上角
[-1.789930, 5.393625, 4.413414], # 25右眼左上角
[-5.311432, 5.485328, 3.987654], # 21右眼右上角
[2.005628, 1.409845, 6.165652], # 55鼻子左上角
[-2.005628, 1.409845, 6.165652], # 49鼻子右上角
[2.774015, -2.080775, 5.048531], # 43嘴左上角
[-2.774015, -2.080775, 5.048531], # 39嘴右上角
[0.000000, -3.116408, 6.097667], # 45嘴中央下角
[0.000000, -7.415691, 4.070434]]) # 6下巴角
2.3 求解3D点和对应2D点的转换关系
调用solvPnP
函数,结合2D关键点、3D关键点、内参矩阵、畸变参数,求得旋转向量与平移向量,根据罗德里格斯公式,将旋转向量转换为旋转矩阵
# 相机内参
K = [6.5308391993466671e+002, 0.0, 3.1950000000000000e+002,
0.0, 6.5308391993466671e+002, 2.3950000000000000e+002,
0.0, 0.0, 1.0] # 等价于矩阵[fx, 0, cx; 0, fy, cy; 0, 0, 1]
# 相机畸变参数[k1, k2, p1, p2, k3]
D = [7.0834633684407095e-002, 6.9140193737175351e-002, 0.0, 0.0, -1.3073460323689292e+000]
cam_matrix = np.array(K).reshape(3, 3).astype(np.float32)
dist_coeffs = np.array(D).reshape(5, 1).astype(np.float32)
_, rotation_vec, translation_vec = cv2.solvePnP(object_pts, image_pts, cam_matrix, dist_coeffs)
rotation_mat, _ = cv2.Rodrigues(rotation_vec)
2.4 根据旋转矩阵求解欧拉角
无论是旋转矩阵、旋转向量,虽然它们能描述旋转,但对我们人类是非常不直观的。当我们看到一个旋转矩阵或旋转向量时,很难想象出来这个旋转究竟是什么样的。当它们变换时,我们也不知道物体是向哪个方向在转动。而欧拉角则提供了一种非常直观的方式来描述旋转——它使用了三个分离的转角,把一个旋转分解成三次绕不同轴的旋转
将旋转矩阵与平移向量拼接得到外参矩阵,进而调用decomposeProjectionMatrix
求得欧拉角,即得到pitch、yaw、roll
pose_mat = cv2.hconcat((rotation_mat, translation_vec))
_, _, _, _, _, _, euler_angle = cv2.decomposeProjectionMatrix(pose_mat)
pitch = eulerAngles[0]
yaw = eulerAngles[1]
roll = eulerAngles[2]
3、总结
一个3D人脸模型不能表示所有人的脸,对所有人采用一个模型得到的位姿精度会有稍许偏差,可对不同人拟合出对应的3D脸模型,这样关键点的空间位置就更准确,从而提升头部位姿估计的精度。但代价是计算量变大了,处理起来也就更慢。现有方案中使用通用3D人脸模型提取的头部位姿作为驾驶员疲劳检测判断的一项重要依据