继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

人脸对齐:Procrustes analysis 普氏分析

大话西游666
关注TA
已关注
手记 209
粉丝 140
获赞 620

概述

在人脸相关应用中,获得的人脸图像常常形状各异,这时就需要对人脸形状进行归一化处理。人脸对齐就是将两个不同的形状进行归一化的过程,将一个形状尽可能地贴近另一个形状。

值得注意的是,在英语文献中,Face Alignment和Facial Landmark Detection常常混用,在我的系列博客里面,Facial Landmark Detection指的是人脸特征点检测,而Face Alignment指的是人脸对齐。人脸特征点检测是人脸对齐的必要步骤,现在有很多端到端(end to end)的方法不需要进行对齐,所以具体要不要对齐这一步需要结合实际分析。

人脸特征点检测的结果如下:

landmark

人脸对齐的效果如下,可以看到右边的脸已经和左边的脸形状大体一致:

face align

言归正传,Procrustes analysis是一种用来分析形状分布的统计方法。Procrustes源于古希腊神话中的一个强盗, 他常切断受害者的肢体使其身形与床相匹配,类似地Procrustes分析方法是对两个形状进行归一化处理 。从数学上来讲,普氏分析就是利用最小二乘法寻找形状A到形状B的仿射变换 。

模型

仿射变换

在高中的几何课程中,一定学过平移,放缩和旋转变换。

仿射变换

将这三种变换写成矩阵形式:



这个式子中,s就是缩放比例, 就是旋转角度,最后的t代表平移的位移,其中R是一个正交矩阵。 


Procrustes analysis

我们现在要解决如何旋转、平移和缩放第一个向量,使它们尽可能对齐第二个向量的点。一个想法是使用仿射变换将第一个图像变换覆盖第二个图像。如何判断这种对齐的效果呢?使用最小二乘法,使得变化后所有点与目标点距离和最小。

两个形状矩阵分别为p和q,矩阵的每一行代表一个特征点的x,y坐标,假设有68个特征点坐标,则。写成数学形式:



其中就是p矩阵的第i行。写成矩阵形式:



代表Frobenius范数,就是每一项的平方和。


求解

这个最小值问题是有解析解的。

先放上代码:

#Procrustes analysisdef transformation_from_points(points1, points2):
    points1 = points1.astype(numpy.float64)
    points2 = points2.astype(numpy.float64)

    c1 = numpy.mean(points1, axis=0)
    c2 = numpy.mean(points2, axis=0)
    points1 -= c1
    points2 -= c2

    s1 = numpy.std(points1)
    s2 = numpy.std(points2)
    points1 /= s1
    points2 /= s2

    U, S, Vt = numpy.linalg.svd(points1.T * points2)
    R = (U * Vt).T    return numpy.vstack([numpy.hstack(((s2 / s1) * R,
                                       c2.T - (s2 / s1) * R * c1.T)),
                         numpy.matrix([0., 0., 1.])])

根据

https://en.wikipedia.org/wiki/Orthogonal_Procrustes_problem

可以知道



是有解的。


需要将式子

进行一些变化,写成Wikipedia式子的形状。这里的变化就需要对原始点集p和q进行一些处理,使得最小化式子发生变化。


https://en.wikipedia.org/wiki/Procrustes_analysis#Ordinary_Procrustes_analysis

这里给出了对原始点集的变化步骤。结合代码来看:

    c1 = numpy.mean(points1, axis=0)
    c2 = numpy.mean(points2, axis=0)
    points1 -= c1
    points2 -= c2

这一步处理消除了平移T的影响。

    s1 = numpy.std(points1)
    s2 = numpy.std(points2)
    points1 /= s1
    points2 /= s2

这一步处理消除了缩放系数s的影响。

这两步处理以后,R就可以变成求解下面的式子:



这里的A,B不再是原始的数据点集,而是变成了处理以后点集。

根据维基百科,这个式子是可以求解的:



这样就解出了R:

    U, S, Vt = numpy.linalg.svd(points1.T * points2)
    R = (U * Vt).T

源代码最后一步返回的是仿射变换矩阵

原文出处

打开App,阅读手记
1人推荐
发表评论
随时随地看视频慕课网APP