第6章 非刚性人脸跟踪Word下载.docx
《第6章 非刚性人脸跟踪Word下载.docx》由会员分享,可在线阅读,更多相关《第6章 非刚性人脸跟踪Word下载.docx(50页珍藏版)》请在冰豆网上搜索。
沿着这些方向的另外一向工作是Blanz和Vetter进行的3DMM。
不光模拟图像纹理,反对像ASM中的沿着轮廓的边缘,而且进一步通过使用从激光扫描人脸学习的高密度3D数据来表示模型。
从中期到20世纪末,人脸跟踪研究的焦点从如何人脸参数化转向了如何跟踪算法任务的提出和最优化。
来至机器学习社区的各种各种的技术以不同的成功程度被应用到。
自从世纪交替时,焦点再一次转移,这次转向了联合参数和目标设计策略,即保证全局的解决。
尽管不断的强烈的研究进入到人脸跟踪,然而相比较有很少的商业应用使用到它。
尽管对于许多一般方法有许多可以免费利用源代码包,对嗜好者和热情者举起(发扬)仍然是一个滞后。
尽管如此,在过去的两年,在公众领域的人脸跟踪的潜在使用兴趣的复兴,并且商业级的产品也开始出现。
有用的东西
在投入人脸跟踪这个复杂的事物之前,许多标记任务和约定通常所有的人脸跟踪方法必须首先被介绍。
这部分剩下的是处理这些问题。
一个感兴趣的读者可能希望首先跳过这个部分的阅读并且直接到几何限制部分。
面向对象设计
正如人脸检测和人脸识别,计划性地,人脸跟踪有两个要素组成:
数据和算法。
算法通常通过参考存储数据(即离线)作向导来对输入的数据(即在线)执行某中操作。
例如,一个将算法和他们依靠的数据连接在一起的面向对象设计是一个方便的设计选择。
在OpenCVv2.x,一个方便的XML/YAML文件存储类被引进,极大的简化了为算法使用组织离线数据的任务。
为了利用特征,本章描述的所有类将实现读、写序列化功能。
用一个假象的类foo作为一个例子,如下:
#include<
opencv2/opencv.hpp>
usingnamesapcecv;
classfoo
{
public:
Mata;
type_bb;
voidwrite(FileStorgae&
fs)const{assert(fs.isOpened());
fs<
<
”{“<
”a”<
a<
”b”<
b<
”}”;
voidread(constFileNode&
node)
assert(node.type()==FileNode:
:
MAP);
node[“a”]>
>
a,node[“b”]>
b;
}
};
这里,Mat是OpenCV的矩阵类,type_b是一个用户定义的类,对它同样定义了序列化的功能。
I/O函数read和writer现实序列化。
FileStorage类支持两个类型数据结构的序列化。
为了简化,本章所有的类将只利用映射,即每一个存储变量创造一个类型为FileNode:
Map类型的FileNode对象。
这里需要唯一的键分配到每一个元素。
尽管这个键的选择是随意的,因为前后一致,我们将使用变量的名字作为标签。
像先前阐述的代码小片段,read和write功能呈现一个特别简单的形式,通过使用流操作符(<
and>
)来插入数据到FileStorage对象和从FileStorage对象中提取数据。
大部分OpenCV类有read和write功能的实现,允许他们轻松的处理包含的存储的数据。
除定义序列化功能之外,我们还必须在FileStorage类中为序列化定义两个附加的功能来工作,如下:
voidwriter(FileStorage&
fs,conststring&
constfoo&
x)
x.wirter(fs);
voidread(constFileNode&
node,foo&
x,constfoo&
default)
if(node.empty())x=d;
elsex.read(node);
同时本部分描述的所有类中,这两个函数的功能是保持一样的,他们模版化和定义在ft.hpp头文件内,可以在附属于本章的源代码中找到。
最后,为了简单地保存和导入用户定义的类,利用序列化功能,模版化的函数同样在头文件中实现,如下:
template<
classT>
Tload_ft(constchar*fname)
Tx;
FileStoragef(fname,FileStorage:
READ);
f[“ftobject”]>
x,f.release();
returnx;
voidsave_ft(constchar*fname,constT&
FileStoragef(fname,FileStorage:
WRITE);
f<
”ftobject”<
x;
f.release();
注意和对象一起的标签总是一样的(即,ftobject)。
采用这些定义的函数,保存和导入对象数据是一个轻松的过程。
通过下面的例子展示:
#include“opencv_hotshots/ft/ft.hpp”
#incldue“foo.hpp”
intmain()
.....
fooA;
save_ft<
foo>
(“fool.xml”,A);
fooB=load_ft<
(“foo.xml”);
....
注意.xm扩展名表示采用XML_格式化数据文件,对于任何其他的扩展名默认采用YAML格式。
数据收集:
图像和视频的注释
现在的人脸跟踪技术几乎完全是数据驱动的,即,算法通常依靠人脸面部特征外观和从一系列例子得到的他们的相对位置的几何依赖性形成的模型,来检测图像中面部特征的位置。
更大的样本集,算法表现更好的鲁棒性,因为他们能够更好的意识到人脸可以表现的变化性的全局性。
因此,建立人脸跟踪算法的第一步是创建一个图像/视频注释工具,用户可以通过该工具指定每一个样本图像中希望得到的面部特征的位置。
训练数据类型
用来训练人脸跟踪算法的数据一般由四个成分组成:
1、图像:
这个成分是一个包含全部人脸的图像集(静的图像或者视频帧)。
为了得到最好的效果,这个集应当详细说明环境的类型(即,统一性,光照,到相机的距离,捕捉设备,以及其他),跟踪器过会在该环境中展开进行。
集合中的人脸呈现我们应用所期望的头部姿势和面部表情也是重要的。
2、注释:
这个成分调整每一个图像的手动标记,以使相应的每一个人脸特征被跟踪。
更多的人脸特征经常导致更好的鲁棒性的跟踪器,因为跟踪算法可以是使用这些度量来加固彼此。
普通的跟踪算法在计算上的花费典型地与人脸特征的数量成线性关系。
3、对称目录:
这个成分有每个面部特征点的索引,这个成分定义了它的双边地对称特征。
这可以用来镜像训练图像,有效的双倍训练集大小和使数据沿着y轴对称。
4、连通性目录:
这个成分包含一个注释对的索引集,用来定义面部特征的语义的解释。
这些连接多可视化跟踪结果是有用的。
下面的图像是四个成分可视化的一个展示,从左到右分别是,原始图像,面部特征注释,彩色编码双边对称点,镜像图像,和注释和人脸特征联通。
为了更方便地使用这些数据,用一个类来实现存储和访问的功能是非常有用的。
OpenCV的ml模块中的CvMLData类处理一般数据的功能,通常用于机器学习问题。
然而,它缺少访问人脸跟踪数据的功能。
同样地,在这章,我们将使用ft_data类,该类声明在ft_data.hpp头文件中,专门为记住人脸跟踪数据的特性而设计的。
所有的数据成员定义为类的public成员,如下:
classft_data{
public:
vector<
int>
symmetry;
Vec2i>
connections;
string>
imnames;
Point2f>
>
points;
…
Vec2i和Point2f类型是OpenCV的类,分别为两个整形的容器和2D浮点型坐标。
对称的容器拥有和人脸特征点(同样地是用户定义)一样多的成分。
每一个connections定义为连通的面部表情的基于0的索引对(以0开始)。
因为训练集可能很大,而不是直接的存储图像,这个类通过imnames成员变量来存储每一个图像的文件名(注意为了是文件名有效,我们需要将图像放在同样的相对位置)。
最终,对于每一个训练的图像,面部特征位置的集合被作为一个浮点型坐标的容器存储,用points成员变量表示。
ft_data类实现了很多便利的方法用来访问数据。
为了在数据集中访问每一个图像,get_image函数通过指定的索引idx导入图像,并且可选择地围绕y轴进行镜像图像。
如下:
Matft_data:
get_image(constintidx,constintflag)
//constintidx;
//图像的索引用来从文件导入
//constintflag;
//0=gray,1=gray+flip,2=rgb,3=rgb+flip;
//flip镜像翻转
Matimg,im;
if(flag<
2)img=imread(imnames[idx],0);
esleimg=imread(imnames[idx],1);
if(flag%2!
=0)flip(img,im,1);
elseim=img;
returnim;
将(0,1)标志传递给OpenCV的imread函数用来指按3通道彩色图像的形式的导入还是按单通道灰色图像导入。
标志位传递给OpenCV的flip函数用来指定绕在y轴进行镜像。
为了用特殊的索引访问对应与图像的点集,get_points函数一个存储浮点型坐标的容器,带有可选的镜像索引,如下:
ft_data:
get_points(cosntintidx,constboolflipped)
//constintidx相应点的图像索引
//constboolflipped;
是否将图像绕在Y轴进行旋转
if((idx<
0)||idx>
=(int)imnames.size())))
returnvector<
();
//如果访问的图像不存在,返回空的容器
p=ponts[idx];
if(flipped)
Matim=this->
get_image(idx,0);
intn=p.size();
q(n);
//初始化N个空的对象
for(inti=0;
i<
n;
i++)
q[i].x=im.cols-1-p[symmerty[i]].x;
//n为点的个数。
q[i].y=p[symmetry[i]].y;
}returnq;
returnp;
注意当指定了镜像标记时,这个函数调用get_image函数。
这需要判断图像的宽度,以用来纠正镜像人脸特征坐标。
一个更有效的设计是传递图像的宽度作为一般变量。
最后,在这个函数中symmerty成员变量的用法被阐述。
一个特殊索引的镜像特征位置仅仅是symmetry变量的索引指定的位置和它的x坐标的翻转和偏移。
如果指定的索引在数据集中不存在,get_image和get_points函数均返回空的结构体。
同样可能的是,并不是集合中的所有图像都被注释。
人脸跟踪算法可以设计来处理缺失的数据,然而,这些实现通常相当的复杂并且超出了本章的范围。
ft_data函数实现了从集合中移除哪些没有相应注释的样本的功能,如下:
voidft_data:
rm_incomplete_samples()//rm=rremove
intn=ponts[0].size(),N=ponts.size();
N;
i++)n=max(n,int(points[i].size()));
//n取图像集中点集最大的
int(points.size());
if(int(points[i].size())!
=n)
points.erase(points.begin()+i);
imnames.erase(imnames.begin()+i);
i--;
}else
intj=0;
for(;
j<
j++)
if((points[i][j].x<
=0||(points[i][j].y<
=0))break;
if(j<
n)
points.erase(points.begin()+i);
简单的例子是,大多数量的注释被假定为是权威的样本。
拥有点集少于许多数量的点的数据实例将采用容器的erase函数将其从集合中移除。
同时也注意到带有(x,y)坐标小于0的点被认为在其相应的图像上缺失(可能是由于遮挡,差的可见度,或者模糊性)。
ft_data类实现序列化函数read和write,并且因此可以简单地存储和导入。
例如,保存一个数据集的简单做法如下:
ft_dataD;
//示例数据结构
..//填充数据
ft_data>
(“mydata.xml”,D);
//保存数据
为了可视化数据集,ft_data函数实现了一些画图功能。
他们的用法在visualize_annotations.cpp文件中阐述。
这个程序导入用命令行指定的文件中的注释数据,移除不完全的样本,并且显示带有相应注释的、对称,连接叠加的训练图像。
OpenCv的highgui模块的一些显著的特征在这里示范。
尽管相当基本并且不适合复杂用户接口。
OpenCV的highgui模块的功能在计算机视觉应用中对于导入和可视化数据和算法输出非常有用。
与其他的计算机视觉库相比,这可能是Opencv的品质。
注释工具:
为了用本章的代码产生注释,一个基本的注释工具可以在annotate.cpp文件中找到。
这工具用视频流或者文件或者相机作为输入。
使用这个工具的程序的四个步骤如下列出:
1、捕捉图像:
在这第一步中,图像流显示在屏幕上,用户通过按S键选择图像用来注释。
用来注释的最好的特征集是哪些最大的跨度面部行为的范围,这些面部行为是人脸跟踪系统需要跟踪的。
2、注释第一个图像:
在第二步中,将第一阶段捕获,选择的图像提供给用户。
然后用户接着点击图像上需要跟踪的附属于面部特征的位置。
3、注释连通性:
在第三步,为了更好的可视化一个模型,需要定义点的连通性结构。
这里,提供给用户先前阶段同样的图像,现在的任务是点击一些点对集,一个接一个的,用来为人脸模型建立连通结构。
4、注释对称性:
在这一步,任然使用同样的图像,用户选择展现双边对称性的点对。
5、注释剩下的图像:
在最后一步,这里的过程类似与步骤2,除此之外用户可以浏览图像集和异步地注释他们。
有兴趣的读者可能想通过改善他们的有用性来改善这些工具,或者甚至整合一个增加的学习过程,凭借一个跟踪模型的更新,该更新通过每个增加的注释的图像和随后的用来初始化的这些点来减少注释的负担。
尽管一些公开地可以利用的数据集可以为本章开发的代码利用。
(例如接下来的描述)。
注释工具可以被用来建立指定人脸跟踪模型,通常该模型比一般的,单独的人,相对应的人执行更好的结果。
预注释数据(MUCT数据集)
开发人脸跟踪系统的一个障碍因素是乏味的和易于出错的手动地注释大量图片集的过程,这些图像带有大量的点。
为了为本章接下来的工作的目的减轻这个过程,公开地可以利用的MUCT数据集可以通过下面的连接下载:
http:
//www/milbo.org/muct.
数据集由3755个注有67个特征点人脸图像组成。
数据集的主题在年龄和种族,和许多不同光照环境和头的姿势情况下捕捉中变化。
。
为了用本章的代码使用MUCT数据集,执行下面的步骤:
1、下载数据集。
在这一步,数据集中所用的图像可以通过下载文件muct-a-jpg-v1.tar.gz到muct-e-jpg-v1.tar.ga,并且解压他们来获得。
这将产生包含所有图像的新的文件夹。
2、下载注释:
在这一步,下载文件包含注释muct-landmarks-v1.tar.gz保存。
解压到和先图片的同样的文件夹呀。
3、利用注释工具定义连接和对称:
在这一步,来至命令行,发布命令./annotate-m$mdir-d$odir,这里$mdir指示MUCT数据集保存的路径文件夹,$ofir指示annotaion.yaml文件夹,包含数据以ft_data对象的形式被写入存储。
MUCT数据的使用促进在本章对人脸跟踪代码功能描述有一个快速的入门。
几何约束
在人脸跟踪,几何引用了预定义点集的空间配置,这些点集对应于人脸上物理连续位置。
(例如眼角,鼻尖和眼毛边缘)。
这些点的特殊选择是应用依赖的,一些应用需要超过100个点的稠密集,并且其他仅需要一个稀疏选择。
然而人脸跟踪算法的的鲁棒性通常伴随着点数目的更加而改善,因为他们分开的测量值可以通过他们相关空间依赖性来增强彼此。
例如,知道一个眼角的位置是对于期待鼻子在哪里定位是一个好的指示。
然而通过增加点的数目来获得鲁棒性的改善是有限制的,典型地100个点之后性能停滞。
而且,增加用来描述人脸的点集带有计算复杂度的线性增加。
因此,带有计算负载严格限制的应用可能带有更少的点集进展的更好。
同样也有这样的情况,在在线环境下更快的跟踪经常导致更精确的跟踪。
这是因为,当帧被抛弃时,两帧之间的增加察觉到的运动,以及用来在每一帧图像上寻找人脸配置的最优算法必须搜索可能的特征点配置的更大的空间。
当两个帧之间的位移变大时,这个过程经常失败。
总的来说,尽管怎样最好的设计面部特征带你的选择有一个一般的指导方针,为了得到最优的性能,这一章应当专业到应用领域。
面部几何通常参数化为两个元素的组成:
一个全局(刚性)转换和一般局部(非刚性)变形。
全局转换解释为图像中人脸的整体布局,通常允许没有约束的改变(即,人脸可以在图像上的任何地方显示)。
这包括每张图像人脸的x,y坐标,面内头部旋转,和图像中人脸的大小。
另一方面,局部变形解释为面部形状交叉身份之间的不同和表情之间的不同。
与全局转换相对照,这些局部变形通常有更多的限制,约束,主要是由于人脸特征的更高地结构化参数。
全局转换是2D坐标的一般功能,适用于任何类型的对象,然而局部变形是特定对象并且必须从训练集中学习。
在这一部分,我们描述面部结构几何模型的构造,因此成为形状模型。
取决于应用,它可以捕捉单个个体的表情变化,和交叉不同的人脸形状或者两者的混合。
这个模型用shape_model类实现,可以在shape_model.hpp和shape_mode.cpp文件中找到。
下面的代码片段是shape_model类的头的一部分,用来说明它的基本的功能。
classshape_model{//2d线性模型
Matp;
//参数矢量k*1CV_32F
MatV;
//线性子空间(2n*k)CV_32F
Mate;
//参数变化(k*1)CV_32F
MatC;
//连通性(c*2)CV_32S
...
voidcalc_params(
constvector<
&
pts;
//用来计算参数的点
constMat&
weight=Mat()//weight/pointN*1CV_32F
constfloatc_factor=3.0)//clampingfactor夹因素
//用参数描述的形状
calc_shape();
voidtrain(
p,//N个样本形状
&
con=vector<
()//连通性
constfloatfrac=0.95;
//用来保留的变化的分数
constintkmax=10)//用来保留的模型的最大数量
..
用人脸的形状代表变化的模型用子空间矩阵V和方差e进行编码。
参数矢量P存储表示模型的形状的编码。
连通性矩阵C同时也存储在这个类,因为它仅术语人脸形状的可视化示例。
在这个类中基础兴趣的三个函数是calc_params,calc_shape,和train。
calc_params函数投影一个点集到似乎可信的人脸形状的空间。
calc_shape函数通过使用人脸模型(用v和e编码)解码参数矢量p来产生一个点集。
train函数用人脸形状数据集学习编码模型,每一个模型由同样数量的点组成。
参数frac和kmax是训练过程的参数,可以为眼前的数据特化该参数。
这个类的功能将在接下来的部分进行详细的描述,那里我们通过Procrustes分析开始,它是用来严格地登记一个点集的方法。
接下来通过用通常代表局部变形的线性模型。
train_shape_model.cpp和visulize_shape_model.cpp文件中的程序分别的训练和可视化模型。
他们的使用将在本部分的结束进行概括。
Procrustes分析
为了建立人