注意:这里说的人脸对齐和传统的Face Alignment差别较大,传统的Face Alignment是指从人脸图片上检测人脸关键点,然后用仿射变换将人脸归正。而本文说的人脸对齐只是指仿射变换的部分。
参考代码https://github.com/foamliu/InsightFace-PyTorch/blob/master/align_faces.py
人脸对齐可分为如下步骤:
由检测到的人脸关键点和目标关键点位置得到仿射变换的变换矩阵
获取目标关键点位置
当人脸大小为(96/w,112/h)
时,目标关键点的位置为(该出处不明)
REFERENCE_FACIAL_POINTS = [
[30.29459953, 51.69630051],
[65.53179932, 51.50139999],
[48.02519989, 71.73660278],
[33.54930115, 92.3655014],
[62.72990036, 92.20410156]
]
DEFAULT_CROP_SIZE = (96, 112)
然后需要根据需要的人脸shape和内外padding的大小改变关键点的位置。 具体说明可以查看代码及其中说明align_faces.py
得到变换矩阵
得到变换矩阵共有三种方法:
最小二乘法
def get_affine_transform_matrix(src_pts, dst_pts):
tfm = np.float32([[1, 0, 0], [0, 1, 0]])
n_pts = src_pts.shape[0]
ones = np.ones((n_pts, 1), src_pts.dtype)
src_pts_ = np.hstack([src_pts, ones])
dst_pts_ = np.hstack([dst_pts, ones])
A, res, rank, s = np.linalg.lstsq(src_pts_, dst_pts_)
if rank == 3:
tfm = np.float32([
[A[0, 0], A[1, 0], A[2, 0]],
[A[0, 1], A[1, 1], A[2, 1]]
])
elif rank == 2:
tfm = np.float32([
[A[0, 0], A[1, 0], 0],
[A[0, 1], A[1, 1], 0]
])
return tfm
函数的输入src_pts
和dst_pts
均为ndarray:(5,2)
输出变换矩阵为ndarray:(3,2)
np.linalg
是numpy中的线性代数库, np.linalg.lstsq(a, b, rcond='warn')
lstsq 是 LeaST SQuare (最小二乘)的意思,即求使得|| ax - b ||
最小的x的值。函数返回一个包含四个值的元组:
通过三个点即可获得仿射变换矩阵
tfm = cv2.getAffineTransform(src_pts[0:3], ref_pts[0:3])
相似性变换矩阵
tform = trans.SimilarityTransform()
tform.estimate(src_pts, ref_pts)
tfm = tform.params[0:2, :]
相似变换的矩阵如下:
s∗cosθs∗sinθ0s∗(−sinθ)cosθ0txty1
相比仿射变换,相似变换的自由度较小,左上角2×2矩阵为旋转部分,tx和ty为平移因子,它有4个自由度,即旋转,x方向平移,y方向平移和缩放因子s。
补充知识:仿射变换
本部分内容参考《计算机图形学导论》第五章:几何变换
三维空间的变换矩阵是一个3*3
的矩阵W
, $P2 = W.dot(P1)$ , P1的坐标可以写作 $(x,y,1)$,其中第三维表示bias。
平移
将 $(x,y)$ 平移到 $(x+t_x,y+t_y)$ , 变换矩阵为:
[xy1]∗10tx01ty001=[x+txy+ty1] 缩放
将 $(x,y)$ 缩放到 $(xs_x,ys_y)$ , 变换矩阵为:
[xy1]∗sx000sy0001=[x∗sxy∗sy1] 旋转
将 $(x,y)$ 顺时针旋转 $\theta$ 角度
[xy1]∗cosθ−sinθ0sinθcosθ0001=[x∗cosθ−y∗sinθx∗sinθ+y∗cosθ1] 错切
[xy1]∗100a10001=[xa∗x+y1] 复合变换
仿射变换公有6个自由度,两个旋转因子,两个缩放因子,x和y的平移因子,共6个。
以 $(t_x,t_y)$ 为中心旋转 $\theta$ 度,先平移 $(-x,-y)$,然后以原点为中心旋转,最后再平移 $(x,y)$
[xy1]∗10−tx01−ty001∗cosθ−sinθ0sinθcosθ0001∗10tx01ty001 opencv中的仿射变换
crop_img = cv2.warpAffine(src_img, tfm, (crop_size[0], crop_size[1]))
其中tfm
是shape=(2,3)
的变换矩阵