总的说来
形状空间是把任何一个人脸的特征点集分成刚性空间和非刚性空间,一共k+4列,该k+4列向量两两正交,然后使用投影的方法纠正对于识别的新图片的形状
刚性空间—- - 可以由运动和缩放构成的部分
下面经过计算可以得到4个正交的列向量来描述
非刚性空间—— 描述形变的部分,根据样本的个数使用PCA算法得到主要的k个列向量
下面先贴下总的训练代码
void
shape_model::
train(const vector<vector<Point2f> > &points,
const vector<Vec2i> &con,
const float frac,
const int kmax)
{
//vectorize points
Mat X = this->pts2mat(points);
int N = X.cols,n = X.rows/2;
//align shapes
Mat Y = this->procrustes(X);
//compute rigid transformation, rigid subspace
Mat R = this->calc_rigid_basis(Y);
//compute non-rigid transformation
Mat P = R.t()*Y;
Mat dY = Y - R*P; //project-out rigidity
SVD svd(dY*dY.t());
int m = min(min(kmax,N-1),n-1);
float vsum = 0; for(int i = 0; i < m; i++)vsum += svd.w.fl(i);
float v = 0; int k = 0;
for(k = 0; k < m; k++){v += svd.w.fl(k); if(v/vsum >= frac){k++; break;}} /*取前k个奇异值*/
if(k > m)k = m;
Mat D = svd.u(Rect(0,0,k,2*n));
//combine bases
V.create(2*n,4+k,CV_32F);
Mat Vr = V(Rect(0,0,4,2*n)); R.copyTo(Vr);//rigid subspace
Mat Vd = V(Rect(4,0,k,2*n)); D.copyTo(Vd);//nonrigid subspace
//compute variance (normalized wrt scale)
Mat Q = V.t()*X;
for(int i = 0; i < N; i++){
float v = Q.fl(0,i); Mat q = Q.col(i); q /= v;
}
e.create(4+k,1,CV_32F);
pow(Q,2,Q); // multiply(Q,Q,Q);
for(int i = 0; i < 4+k; i++){
if(i < 4)e.fl(i) = -1; //no clamping for rigid coefficients
else e.fl(i) = Q.row(i).dot(Mat::ones(1,N,CV_32F))/(N-1); //点积
}
//store connectivity
if(con.size() > 0){ //default connectivity
int m = con.size();
C.create(m,2,CV_32F);
for(int i = 0; i < m; i++){
C.at<int>(i,0) = con[i][0]; C.at<int>(i,1) = con[i][1];
}
}else{ //user-specified connectivity
C.create(n,2,CV_32S);
for(int i = 0; i < n-1; i++){
C.at<int>(i,0) = i; C.at<int>(i,1) = i+1;
}
C.at<int>(n-1,0) = n-1; C.at<int>(n-1,1) = 0;
}
}
下面开始解释
Mat X = this->pts2mat(points);
这个函数做的事情是把所有的点都对其到零点坐标上
Mat Y = this->procrustes(X);
这个是进行procrustes分析,可以参考http://www.mamicode.com/info-detail-999642.html
这个只是procrustes分析的一种推广吧
个人也了解了下普通的procrustes分析,这里的procrustes分析只是思想上一致,方式取巧了
大致如下
Mat R = this->calc_rigid_basis(Y);
这边是把数据进行矩阵化
R矩阵是将刚性空间施密特正交化的矩阵
关于施密特正交化的方式参照我的另一篇http://blog.csdn.net/limit_ing/article/details/62069691
显然前4列的列向量因为使用施密特正交法自然两两正交
Mat P = R.t()*Y;
Mat dY = Y - R*P; //project-out rigidity
SVD svd(dY*dY.t());
int m = min(min(kmax,N-1),n-1);
float vsum = 0; for(int i = 0; i < m; i++)vsum += svd.w.fl(i);
float v = 0; int k = 0;
for(k = 0; k < m; k++){v += svd.w.fl(k); if(v/vsum >= frac){k++; break;}} /*取前k个奇异值*/
if(k > m)k = m;
Mat D = svd.u(Rect(0,0,k,2*n));
矩阵P是把样本点投影到刚性空间的成分
dY是样本点减去刚性空间的成分,就是非刚性部分
下面关于PCA参见我的http://blog.csdn.net/limit_ing/article/details/61970031
svd参见http://blog.csdn.net/limit_ing/article/details/62068466
上面两个包含个人推理过程,比较繁琐