一、实验目的
在一般环境中识别出人脸。
二、概要
本实验所探讨的是一般环境图像中单个正面端正人脸的检测问题。这种条件下的人脸检测的方法主要有模板匹配方法、可变形模板方法等。概括的说,基于模板匹配的方法是在图形灰度上直接比较目标模板和候选图像区域之间的相似性,而基于特征匹配的方法是比较从图像中抽取的一定特征的相似性。
本实验主要用到两种模板:双眼模板和不同长宽比的模板。在检测时首先使用双眼模板进行粗筛选,然后使用不同长宽比的人脸模板确定出区域的位置和范围,接着通过确定出来的位置和匹配模板的相似度程度来确定是否是人脸。
模板生成,我们在人脸检测中使用了 6个模板:一个用于初筛选的双眼模板,五个用于检测不同长宽比的人脸模板。采用多个人脸模板是为了适应不同人脸部的不同长宽比。模板是通过对多个样本平均构造出来的。首先在选取的样本图像上用手工画出人脸的区域作为人脸样本。由于各个样本的尺度大小和灰度分布不同,因此首先对他们进行尺度和灰度标准化,然后将所用样本取灰度平均值并压缩到需要的尺度作为原始模板。拷贝原始模板的眼睛部分,进行灰度分布标准化后作为双眼模板;对原始模板分别按照 1:0.9、1:1、1:1.、1:2、1:1.3的宽长比变形,进行灰度分布标准化后作为人脸模板。
在模板生成中最主要的工作是图像的尺度变换和灰度分布标准化。
尺度变换主要是基于线性插值的重采样方法。
三、参数说明
将图像看成一个二维矩阵 D[W][H],其中 W和 H分别表示图像的宽和高。
1.图像的灰度值
2.灰度分布的方差:
3.灰度均衡化对于输入的每个样本图像,为了将其灰度平均值和方差变换到时先设定的灰度平均值 和方差,从而样本中每个像素点的灰度值进行如下的变换:
四、检测方法
假设人脸模板的灰度矩阵为 T[M][N],灰度均值为 ,均方差为,输入图像区域的灰度矩阵为 R[M][N],灰度均值为 ,均方差为 ,那么他们之间的相关系数 r(T,R)和对应像素灰度值得平均偏差 d(T,R)f分别为:
r(T,R)越大表示模板与输入图像区域的匹配程度越高;而 d(T,R)正相反。将他们综合起来作为匹配程度的度量:
其中.为权重系数。我们取经验值. =35.0。
五、算法实现
算法的基本思路是:搜索输入图像中所有可能为人脸的区域,认为满足一定条件且与模板匹配程度最高的区域是人脸。为了检测不同长宽比的人脸,采用前述 5种不同长宽比的模板进行匹配。
假设输入图像中最多只有一个完整的人脸(正面、端正),可能的人脸宽度在 24像素到图像宽度的 1/2之间,人脸位置未知,设输入图像为 Is(宽为 H、高为 W);双眼模板为, 5个人脸模板为,其宽度为 24.,高度分别为 。
算法如下:
(1)初始化最大匹配度 ,设当前图像 ;
(2)设当前扫描点(x,y)为图像起始点(0,0);
(3)检测扫描区域 是否为双眼区,(即计算区域 的灰度均方值是否大于 20)若不是则转( 6)
(4)检测相应区域是否为人脸(即计算 与模板 的相关系数 是否大于
0.3),是则求出当前区域与人脸的匹配度 D,否则转(6)
(5)若 ,记录当前区域的位置和大小,并令
(6)若 x+24<W, x=x+1,转(3)
(7)若 y+31<H, x=0, y=y+1转(3)
注:对于( 4)中,初始化 i=0,D为无穷小的数,拷贝区域 进行灰度分布标准化,求的匹配程度 。
六、程序使用说明
例程给出了人脸识别的大部分代码,要求补充填写部分在
coreA的 common.c中,两个子程序.
double Degs(double Dev, double Rel)
/*******
入口参数Dev表示输入图像区域与模板间图像向量的欧式距离
Rel表示输入图像区域与模板间图像向量的夹角
返回值为变量Deg,经验值a = 35.0
***********/
{
double a = 35.0;
double Deg = Rel + a / (1 + Dev);
return Deg;
}
/********************添加程序部分******************/
/*********************图片取均值************************/
/***该部分实现平均脸,其中入口参数Data1、Data2、Data3、Data4、Data5分别为灰度分布标准化后图像指针(数据已经获得),
k 为标准化后图像个数,w和h分别为标准化图像的长和宽,
Data为平均脸指针,
要求获得的平均脸以Data输出。
***/
void Imgmeans(unsigned char * Data,unsigned char * Data1,unsigned char * Data2,
unsigned char * Data3,unsigned char * Data4,unsigned char * Data5, int k, int w, int h)
{
unsigned int i, j;
for(i = 0; i < h; ++i)
{
for(j = 0; j < w; ++j)
{
Data[i * w + j] = (unsigned char)((Data1[i * w + j] + Data2[i * w + j] + Data3[i * w + j] + Data4[i * w + j] + Data5[i * w + j]) / k);
}
}
}