非刚性人脸跟踪(4)

2019-09-01 18:37

因为源于刚性转换的变化性的方向已经从先前先前学习变形模型的数据中去掉,作为结果变形的子空间将正交与刚性转换的子空间。因此,连接两个子空间将导致一个组合局部-全局线性面部形状的表示,这同样也正交。这里联合可以利ROI抽取机制,排列两个矩阵到混合子空间矩阵的子矩阵来现实。ROI的抽取可以用OpenCV的Mat类来实现。如下: V.create(2*n,4+k,CV_32F);//组合子空间

Mat Vr=V(Rect(0,0,4,2*n));R.copyTo(Vr);//刚性子空间 Mat vd=V(Rect(4,0,k,2*n));D.copyTo(Vd);//非刚性子空间

结构模型的正交性意味着描述一个形状的参数可以简单地被计算,就像shape_model::calc_param函数做的: p=V.t()*s;

这里的s是一个矢量化的人脸形状,并且p存储着表示它的人脸子空间的坐标。

最后一点注意的是线性化模型人脸形状是怎样约束子空间的坐标,例如使用它产生的形状任然有效。在下面的图像中,位于子空间的人脸形状示例被展示,用来表现坐标的一个增加值, 用变量的一个方向值和4个标准差的增加。注意对于小的值,结果形状保持的像人脸,但是随着值变大开始恶化。

阻止这种变化的一个简单的方法是固定住子空间坐标的值,使当用子空间的数据决定时位于允许的区域。对于这个一个普遍的选择是用+-3数据标准差约束的框,这占数据变化的99.7比例。这些固定的值通过shape_model::train函数,在找到子空间后,计算得到的。如下: Mat Q=Vt()*X;//将原始数据投影到子空间 for(int i=0;i

float v=Q.fl(0,i); Mat q=Q.col(i);q/=v; }

e.create(4+k,1,CV_32F); multiply(Q,Q,Q); for(int i=0;i<4+k;i++) {

if(i<4) e.fl(i)=-1;//对于刚性稀疏不固定

else e.fl(i)=Q.row(i).dot(Mat::ones(1,N,CV_32F))/(N-1); }

注意方差是在子空间标准化后的坐标上计算的,标准化是关于第一维的坐标(即尺度)。这样可以阻止相对大尺度的样本数据主导估计。也注意到一个负值被分配到刚性子空间坐标的方差(即V的前四列)。用来固定的函数shape_model::clamp来检测是否一个特殊方向的方差是负值,如果不是负值则应用固定,如下:

void shape_model::clamp(

const float c)//固定作为标准方差的分数 {

double scale=p.fl(0);//提取尺度 for(int i=0;i

if(e.fl(i)<0)continue;//忽略刚性成分 float v=c*sqrt(e.fl(i))//c*标准化框 if(fabs(p.fl(i)/scale)>v)//保存坐标符号 {

if(p.fl(i)>0) p.fl(i)=v*scale;//正阈值 else p.fl(i)=-v*scale;//负阈值 } }

这样做的理由是训练数据通常是在人为的环境下获取的,在这种环境下,人脸是竖直的并且以一个特殊的尺度位于图像的中心。依赖于训练集中的配置的形状模型的刚性成分是非常受限制的。最终,随着每一个可变形坐标的方差在尺度化的帧中被计算,在固定时,同样的尺度化必须应用于坐标。

训练和可视化

用标记的数据训练一个形状模型的例程可以在train_shape_model.cpp中找到。带有包含标记数据路径的命令行参数argv[1],训练通过导入数据到内存开始,并且移除不完全的样本,如下:

ft_data data=load_ft(argv[1]); data.rm_imcomlete_samples();

对于每个例子的标记和可选的他们的镜像部分,在传输到训练函数之前被存储在一个容器中,如下:

vetctor >points

for(int i=0;i

points.push_back(data.get_points(i,false)); if(mirro) points.push_back(data.get_points(i,true)); }

然后形状模型通过调用一个函数shape_model::train()来训练,如下: shape_model smodel ;smodel.train(points,data.connections,frac,kmax);

这里frac(即用来保留的变化分数)和kmax(即用来保持的最大特征矢量的数目)通过命令行选项可以有选择的设置,默认设置为0.95和20,分别地,(这两个参数)在大部分情况下趋向于工作好。最后采用包含保存训练形状模型路径的命令行参数argv[2],保存可以通过调用一个函数来执行,如下: save_ft(argv[2],smodel);

这部的简单性是由于为shape_model类定义了read和write序列化函数。

为了可视化训练模型,visualize_shape_model.cpp程序生动化地轮流显示每个方向的非刚性变形。通过导入形状模型到存储器开始,如下: shape_model smodel=load_ft(argv[1]);

用来置于显示窗口的中心的模型的刚性参数的计算如下: int n=smodel.V.rows/2;

float scale=calc_scale(smodel.V.col(0),200);

float tranx=n*150.0/smodel.V.col(2).dot(Mat::onews(2*n,1,CV_32F)); float trainy=n*150.0/smodel.V.col(2).dot(Mat::onews(2*n,1,CV_32F));

这里calc_scale函数寻找尺度化系数,该系数可以产生带有200像素宽的人脸形状。平移成分通过找到系数(用来产生150像素的平移)来计算(即,这里的模型均值中心化并且用大小为300*300的窗口显示)。 注释

注意shape_model::V的第一列对应于尺度并且第三列和第四列分别对应于x和y的平移。

然后参数值的轨道被产生,该轨道从0开始,移动到正的极端,移动到负的极端,然后回到0,如下:

vector val;

for(int i=0;i<50;i++)val.push_back(float(i)/50); for(int i=0;i<50;i++)val.push_back(float(50-i)/50); for(int i=0;i<50;i++)val.push_back(-float(i)/50); for(int i=0;i<50;i++)val.push_back(-float(50-i)/50);

这里,每一个动画的相位由50个增值组成。然后这个轨道被用于在显示窗口上动画人脸模型和给于(渲染)结果。如下:

Mat img(300,300,CV_8UC3); namedWindow(“shape model”); while(1) {

for(int k=4;k

for(int j = 0; j < int(val.size()); j++){ Mat p = Mat::zeros(smodel.V.cols,1,CV_32F); p.at(0) = scale; p.at(2) = tranx; p.at(3) = trany;

p.at(k) = scale*val[j]*3.0* sqrt(smodel.e.at(k));

p.copyTo(smodel.p); img = Scalar::all(255); vector q = smodel.calc_shape(); draw_shape(img,q,smodel.C); imshow(\

if(waitKey(10) == 'q')return 0; } } } 注释

注意刚性系数(即,这些对应于shape_model::V的前四列)通常设置为先前计算得到的值,用来将人脸显示在屏幕的中心。

面部特征检测器

检测图像中的面部特征和一般目标检测很相似。OpenCV有一组精细的函数来建立一般目标检测,其中最知名就是基于Haar特征级联检测器,它采用的是著名的Viola-Jones人脸检测器实现的。然而,有些可以可区分的因素,这些因素使得面部检测唯一,他们如下:

1、精度对鲁棒性:在一般的目标检测,目的是为了寻找图像中目标的大致位置。面部检测器需要得到特征位置的高精度的估计。一种错误是:在目标检测中少量像素被认为不重要,但是在通过特征检测进行人脸表情估计时它可能意味着微笑和皱眉的之间的差异。

2、来至有限空间支持的歧义:一个很常见的假设是用一般目标检测器检测感兴趣的目标表现出充分的图像结构,这样,它可以可靠的区分不包含目标的图像区域。对于面部特征来说这不是常见的情况,它典型地限制了空间支持。这是因为不包含目标的图像区域通常表现出和面部特征非常相似的结构。例如,人脸边缘的一个特征,从中心位于该特征点的小边界框来看,很容易和任何其他的包含有明显边缘的图像块混淆。

3、计算的复杂度:一般目标检测的目的是寻找图像中所有目标的实体。另一方面,人脸跟踪需要得到所有面部特征的位置,这通常包含有大约20到100个特征。因此,有效地评估每一个特征检测器性能,最主要的在于能够建立一个实时的人脸跟踪器。

由于这些的差异,用于人脸跟踪的面部特征检测器通常专门的设计。当然,在人脸跟踪中,有很多一般目标检测器技术应用到面部特征人脸器。然而,在关于谁表现的更能适合解决面部特征跟踪问题上,没有一个一致的说法。

在这一部分,我们将使用被认为是最简单的模型——线性图像块来建立面部特征检测。尽管它简单,在设计它的学习过程时应关注一下,我们将看到事实上用在人脸跟踪算法上,它可以给出面部特征位置的合理估计。而且,他们的简单些使得估计速度非常快,使实时的人脸跟踪成为可能。由于他们表现的像一个图像块,因此,面部特征检测器称为块模型。这个模型通过patch_model类来实现,该类可以在patch_model.hpp和patch_model.cpp文件中找到。 下面的代码片段给出了path_model类的头部分,强调该类 的基本功能: class patch_model{ public:

Mat P; //normalized patch//归一化的块 ...

Mat //response map //映射 calc_response(

const Mat &im, //image patch of search region//搜索区域的图像块 const bool sum2one = false); //normalize to sum-to-one? ... void

train(const vector &images, //training image patches//训练图像块 const Size psize, //patch size//块大小

const float var = 1.0, //ideal response variance //实响应方差 const float lambda = 1e-6, //regularization weight//规则化权重 const float mu_init = 1e-3, //initial step size//初始化的步长大小 const int nsamples = 1000, //number of samples//样本的数量


非刚性人脸跟踪(4).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:东北师范大学《算法分析与设计》18秋在线作业1

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: