在机器学习、图像处理中,经常要计算两个特征向量之间形状的距离,比如我用face++检测到两张人脸的特征点数据,我们要用人脸的特征点计算出这两张人脸的之间的相似度,这个时候我们就需要对这两张人脸进行对齐操作,就是AAM算法中的Procrustes Analysis对齐算法。这个是基础算法,因此在这里做一下笔记,贴一下代码,以便日后调用,因为最近搞项目,老是用到这个算法。
一、固定两点对齐
假如我要计算线段PQ变换成P'Q'的变换矩阵,那么我首先要计算计算向量PQ和向量P'Q'的变换矩阵(旋转和缩放),然后再以P点为源点,把P点平移到P'点。因此过程分成两步:
1、计算向量间的变换矩阵
<span style="font-size:18px;">//计算两个线段之间的变换矩阵,求解线段(Line2P,Line2Q)往(Line1P,Line1Q)的变换矩阵 Eigen::Matrix2d CSearchFace::Transform_Line_Segment(cv::Vec2f Line1P,cv::Vec2f Line1Q,cv::Vec2f Line2P,cv::Vec2f Line2Q) { Eigen::Vector2d A; Eigen::Vector2d B; A(0)=Line1P[0]; A(1)=Line1P[1]; B(0)=Line1Q[0]; B(1)=Line1Q[1]; Eigen::Vector2d C; Eigen::Vector2d D; C(0)=Line2P[0]; C(1)=Line2P[1]; D(0)=Line2Q[0]; D(1)=Line2Q[1]; Eigen::Vector2d cd=D-C; Eigen::Vector2d ab=B-A; Eigen::Matrix2d cof; cof(0,0)=cd(0); cof(0,1)=cd(1); cof(1,0)=cd(1); cof(1,1)=-cd(0); Eigen::Vector2d tr=cof.inverse()*ab; cof(0,0)=tr(0); cof(0,1)=tr(1); cof(1,0)=-tr(1); cof(1,1)=tr(0); return cof; }</span>
2、平移变换。
下面以人脸特征点的对齐为例子,计算两个人脸特征点之间的相似度,因为最近做到的项目是参考文献《Data-Driven Face Cartoon Stylization》的算法,里面涉及到从脸型库里面寻找与用户输入的人脸最相似的脸型。在第4部分的第4段,Chin and mouth的算法,脸庞之间距离的计算方法是通过固定脸庞上的两个顶点,然后进行计算距离,因此我把代码贴一下:
//算法通过固定脸庞上的1、13号点,对这两个点进行变换对齐,然后计算1~13号点之间的距离总和 float CSearchFace::GetFaceDistance(vector<cv::Vec2f>face1_landmark,vector<cv::Vec2f>face2_landmark) { float sum_dis=0; vector<cv::Vec2f>result0; //计算向量间的变换矩阵 Eigen::Matrix2d tm=Transform_Line_Segment(face1_landmark[1],face1_landmark[13],face2_landmark[1],face2_landmark[13]); //平移 Eigen::Vector2d Line1P(face2_landmark[1][0],face2_landmark[1][1]); Eigen::Vector2d Movv(face1_landmark[1][0],face1_landmark[1][1]); for (int i=1;i<14;i++) { Eigen::Vector2d pt(face2_landmark[i][0],face2_landmark[i][1]); Eigen::Vector2d dst_point_eigen=pt-Line1P; Eigen::Vector2d src_point_eigen=tm*dst_point_eigen+Movv;//变换后的点 result0.push_back(cv::Vec2f(src_point_eigen(0),src_point_eigen(1)));//最后的结果 //距离计算 cv::Vec2f disv=cv::Vec2f(src_point_eigen(0),src_point_eigen(1))-face1_landmark[i]; sum_dis+=sqrt(disv[0]*disv[0]+disv[1]*disv[1]); } m_restul=result0; return sum_dis; }二、相似变换对齐
《Data-Driven Face Cartoon Stylization》的算法嘴部的对齐算法与脸庞的对齐算法不一样,嘴部是通过相似变换的算法实现的。或者称之为仿射变换
python 版本:
def transformation_from_points(points1, points2): """ Return an affine transformation [s * R | T] such that: sum ||s*R*p1,i + T - p2,i||^2 is minimized. """ 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.])])
//把src往ref进行变换对齐 Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> CSearchMouth::Similarity_transform_2d(vector<Vector2> src,vector<Vector2>ref, vector<Vector2>&dst) { int count=ref.size(); dst.resize(count); assert(count >= 3); Eigen::Matrix<float, Dynamic, Dynamic> A(2 * count, 4), X, B(2 * count, 1), S; for (int i = 0; i < count; i++) { A(i, 0) = src[i].x; A(i, 1) = src[i].y; A(i, 2) = 1; A(i, 3) = 0; A(i + count, 0) = src[i].y; A(i + count, 1) = -src[i].x; A(i + count, 2) = 0; A(i + count, 3) = 1; B(i, 0) = ref[i].x; B(i + count, 0) = ref[i].y; } X = A.jacobiSvd(ComputeThinU | ComputeThinV).solve(B); Eigen::Matrix<float, 2, 3> M; M << X(0, 0), X(1, 0), X(2, 0), -X(1, 0), X(0, 0), X(3, 0); for (int i = 0; i < count; i++) { dst[i].x = M(0, 0) * src[i].x + M(0, 1) * src[i].y + M(0, 2); dst[i].y = M(1, 0) * src[i].x + M(1, 1) * src[i].y + M(1, 2); } return M; }本文地址:http://blog.csdn.net/hjimce/article/details/47019223 作者:hjimce 联系qq:1393852684 更多资源请关注我的博客:http://blog.csdn.net/hjimce 原创文章,转载请保留本行信息。