几何匹配和分合算法的图像识别技术

 

第一章  引言
1.1  面像定位概述及其与面像识别的关系
    这个设计所涉及到的是面像的定位和识别。简单来说,所谓面像的定位,就是在照片(静态图像)或视频(动态图像)中标出面像所在的位置,把面像选取出来。而面像的识别就是把选取出来的面像与数据库中已有的面像进行比较,找出匹配的档案来。有的文献把面像的定位和识别统称为面像识别,定位和识别则是两个主要的步骤。完整的面像识别系统涉及到决定照片或视频中有无面像,并计数,定位,定出大小,然后根据数据库识别出个人,可能的话还要识别表情,以及根据脸的图像做出描述(瓜子脸,丹凤眼等等就是日常生活中“描述”的例子),或者反过来根据描述挑选匹配的面像图像。
    在日常生活中,我们主要根据脸来识别一个人。交谈中,我们往往看着对方的脸,尤其是眼睛。脸上的表情也是进行交谈的一个重要部分。所以,面像在人与人的交流过程中起着极其重要的作用。这就决定了在现代社会中,使用计算机自动进行面像的定位和识别工作有着重要的意义。其应用领域包括很多方面,比如身份鉴别,保安系统,人机交互界面等等。
    面像识别系统虽然有诱人的应用前景,但是在现实中却还没有开始大规模的使用。其主要原因之一就是用计算机自动进行面像的定位和识别十分困难,目前的识别效果(正确率,速度)不如其他的生物识别技术,如指纹识别,视网膜识别等等。人们在日常生活中就进行了大量的面像定位和识别工作,当然全部是由人的视觉系统和大脑“自动”进行的。目前还不清楚人的视觉系统和大脑的工作原理,因此这项人可以轻而易举完成的任务,对于目前还只会死板地执行程序指令的计算机来说却是极端困难。困难主要存在于两个方面:
1. 面像的图像数据具有高度的随机性。光照条件,脸的偏向,表情,发型,胡子,化妆,衣饰(眼镜,帽子)等等略有变化,就可以给识别系统带来巨大的困难。
2. 面像的图像数据量巨大。目前出于计算量的考虑,面像定位和识别算法研究大多使用尺寸很小的灰度图像。一张64´64像素的256级灰度图像就有4096个数据,每个数据有256种可能的取值。定位和识别算法一般都很复杂,在面像库较大的情况下,计算量十分大,很多情况下速度令人难以忍受。而灰度数据事实上是丧失了象色彩,运动等等的有用信息的。如果要使用全部的有用信息,计算量就更大了。
因此,研究面像的定位和识别不仅仅有实用上的考虑,而且对人们理解人脑的工作方式、研究人工智能和数字图像处理有重要的意义。

 

 

1.2 面像定位算法 从模型匹配的方法来看,目前的面像定位算法可以粗略地分为两大类:第一类是利用面像各器官之间的几何关系的方法;第二类是利用标...

1.2  面像定位算法
   
从模型匹配的方法来看,目前的面像定位算法可以粗略地分为两大类:第一类是利用面像各器官之间的几何关系的方法;第二类是利用标准面像图像或者其变换结果直接或者经特征提取后进行匹配的方法。第一类方法利用了明显的先验知识,因而方法简单明了,执行速度较快,对面像的方向和表情有一定的适应性(在一定的变化范围内面部特征的相对几何关系变化很小), 但是准确率往往不高(漏判和误判),而且对预处理要求高,依赖于所有面部特征都完整地被提取,所以对转角较大的侧脸, 光照极度不均匀, 部分脸被遮蔽(眼镜, 围巾等)适应性不好。第二类方法利用了更多的图像信息准确率高,不易受欺骗;缺点是计算量大,而且使用的面像模板受面像库中已有资料的影响,可能会有通用性不好的问题(比如不同人种的面像模板不能通用)。
   
利用面像各器官之间的几何关系的方法的典型例子可以参见Shi-Hong Jeng, Hong Yuan Mark Liao等人(1)的文章。而利用标准面像图像或者其变换结果直接或者经特征提取后进行匹配的方法的典型例子可以参见Toshiaki KondoHong Yan2)的工作。
我认为利用面像各器官之间的几何关系的方法可以作为完整的面像定位算法的一个基础, 就是说利用几何匹配先大致找到图像中的面像(可能含有很多错误),然后使用利用标准面像图像或者其变换结果直接或者经特征提取后进行匹配的方法来确证,剔除实际不是面像的区域。这样既可以利用第一类方法的速度优势,又可以利用第二类方法的准确度优势。

1.3  分割算法和区域生长
   
从一幅图中,按一定规则划分出感兴趣的部分或区域叫做分割。对于利用面像各器官之间的几何关系的面像定位方法来说,分割算法是十分重要而关键的。在这里,感兴趣的部分是面像的器官(眼睛,嘴巴,眉毛,鼻子等等)。分割算法如果能够有效地把面像器官和脸的其他部分分离开来,并且保持器官的完整性(比如嘴巴没有破碎成几段),就是成功地达到了目的。
   
分割算法是数字图像处理中非常经典的课题,有着众多的算法可供选择。这些算法各有所长,但是没有哪一种是普适的。最常见的几类方法是:按像素或处理过的像素的灰度值进行分割(其实就是取阈值做二值化);提取边缘,利用边缘进行分割;区域生长方法。
区域生长方法寻找互相连接在一起,并有相同特征的像素所形成的区域(我把这样的区域称作特征块或者物体),是实现图像分割的一种重要方法。区域是一个二维连通区,所以生成区域的办法大致有三类:局部法:这是根据像素的性质或像素的临近像素的性质决定像素分在什么区域;总体法:这是根据遍及整幅图中大量像素的性质决定把一群像素划分到一个区域中;分裂合并法(简称分合):这是利用状态空间技术分裂或合并区域,用图结构表示区域和边界,这时要利用局部和总体的合并与分裂准则。

 

 

 我在设计中采用的是区域生长方法中的分合算法。分合算法70年代就由Horowitz和Pavlidis(3)提出,发展至今已经是一种非常成熟的经典算法,被广泛用于图像分割的应用,比如卫星遥感照片的自动分割等。这种方法被证明效果较好,但是计算和存储量大。

 

1.4  主要工作
    工作的目标是寻找一种切实可行的面像定位算法,并且编制出演示的软件。我采用的是分合的分割算法和几何匹配的算法。算法和软件的实现采用的是Borland Delphi 3。Windows位图的基本操作和一些数字图像处理的基本操作来自钟立晨老师所编制的模块,其余的各模块是由我自己编写的,包括:演示软件主程序;图像的预处理;分合算法;几何面像匹配算法;基本的链表,堆栈和队列的数据结构。

第二章  一种有效的分割算法: 分合算法
2.1 面像定位中的图像分割
    在引言中已经说明了图像分割的基本概念和常见的算法类型。这里更加详细地讨论在面像定位中图像分割的方法。
    图像分割的目的是把面像的器官与脸的其他部分分离开来,并保存每个器官的完整性。在照片中,面像比较明显而易辨别的特征主要是面部器官:眼睛,嘴巴,鼻子和眉毛。鼻子往往与脸的反差不大,因此通常用鼻孔代替。这几个面部特征在灰度图像中一般比周围区域暗。尤其是眼睛和嘴巴在绝大部分情况下都清晰可见。因此图像分割可以利用的特征就是:灰度比周围区域暗的区域。在我的毕业设计中,为了减少图像分割这一步的运算量,预处理的时候就对图像做了二值化。因此事实上分割的对象已经是黑白图像,分割简化为找出所有黑色的连通区域,它们都成为面像器官的候选者。
对分割算法有两个要求:
1. 一定的抗噪声能力。经过二值化的黑白图像中,难免会有很多与主旨无关的细碎黑色像素连接了本应分开的特征,而某些单一的特征也可能会破碎成几块。要求分割算法具有一定的能力抵抗这些噪声,将应该分开的特征分开,应该成为整体的部分连起来。
2. 运行速度要快。面像定位和识别系统常常作为实时应用,在这些场合要求系统的运行不能耗时过长,否则就失去意义了。从几何匹配方法来看,最耗时的步骤就是图像分割这一步。所以这一步的速度决定了整个面像定位方法的运行速度。
由于我们需要的是能够把黑色连通的区域分别标记出来的分割算法,选择区域生长法就是最自然的了。
在区域生长法中,有两类算法最常见:分合算法和运行长度表方法。分合算法抗噪声能力好一些,所以它成为了我首选的算法。但是经过实践发现它的最大弱点是计算量大,而且计算量随问题规模的增长是非线性的,这就导致对大的或者复杂图像的处理时间很长。运行长度表方法优点是速度快,缺点则是没有多少抗噪声能力。

 

 

2.2 分合算法描述
下面具体描述分合算法和我在它的实现中的一些考虑。具体的模块组织见4.1“软件的组成”。
    要事先说明的一点是,现在个人计算机的内存一般都有几十M以上,对于面像定位的应用绰绰有余,所以运行在个人计算机上的算法完全不必为了节省内存空间而做专门的考虑。但是如果希望算法能够运行在数字图像处理芯片上,就需要做改写以适应节省内存的要求了。
    分合算法的目的是把图像分割为许多这样的区域:具有某种共同特征并且相互连通的像素分在同一个区域,而特征不同或者不相互连通的像素分在不同的区域。(我使用的连通性定义是四连通。关于四连通和八连通的概念参见 Kenneth R. Castleman(4))。
    判定区域R内的像素是不是具有共同的特征需要均匀性判据H(R),H(R)为True表示区域均匀(从而可以认为像素具有共同的特征),否则表示不均匀。一般应用的均匀性判据往往是区域内的灰度比较均匀,比如区域内最大和最小的灰度值之差不超过一定阈值(5)。在我的应用中,因为图像已事先做了二值化,所以均匀性的判定有所不同。我只关心黑色的区域,因为只有黑色区域可能代表面像特征。当一个区域中黑色像素占了绝大部分时,我就认为它是均匀的。具体的判据将在算法中给出。
    分合算法采用金字塔数据结构。首先讨论以像素计的边长是2的整数次幂的正方形图像。塔的底层(第0层)是原始图像(在我的应用中是已经过二值化的图像)。用相邻排作正方形的四个像素作为子结点,计算上一层相应像素的取值。这样每上溯一层(层编号加1),像素数目成为原来的1/4。当整幅图只剩下一个像素时,金字塔构造完毕(见图1)。数据结构某一层中的一个像素事实上代表了第0层中2¬¬j个像素组成的一个数据方块(j是该层编号)。为了表示和计算方便起见,用编码来代表这样的一个数据块。设数据结构中最高一层的编号为n,则这个编码共有n位。把一个方块内的四个子方块按照图2顺时针标记为1,2,3,4。为了表示一个数据块,我们首先把原始图像划分成四个正方形,第一位编码就描述该数据块落在了1,2,3,4中哪个正方形里。然后对包含该数据块的正方形再划分为四个小正方形,第二位编码就描述该数据块落在了哪个小正方形里。如此反复,直到某个小正方形恰好是该数据块,此后的编码就全部取0,表示不必再细分。例如图2所示的3层金字塔结构:
    对于一幅普通的图像,一般都不会恰好是正方形,边长也不会恰好是2的整数次幂。我便以128×128的方块作为基本单位,用m×n个这样的方块对图像做覆盖。遇到图像中某个部分填不满一个方块的情形,就补上白色(白色不作为特征,看成空白)。每个方块都产生自己的一个7层金字塔数据结构,相互独立地运行分裂算法和合并算法。在相邻归并算法中,不处于同一个128×128划分块但是相邻的数据块应该被归入同一个特征块,所以数据块编码需要在全图像内唯一。我使用的数据块编码是在每个金字塔7位编码的基础上再加两位,分别表示在m×n个128×128划分块中该数据块所处的划分块的x和y位置。因此完整的编码是9位。

 


    根据子结点像素计算父结点像素时常用的是灰度平均法。我的应用中有所不同:如果四个像素中白色占了3块以上则父结点像素赋值为白色;如果黑白2:2对角分布也赋值为白色;如果黑色占了三块以上则赋值为黑色;如果黑白2:2各占据一侧则赋值为黑色。这样的好处是可以消除一部分不紧密的连接(2:2对角分布),而保留所有可能的紧密的连接(2:2同侧分布)。
分合算法的思路是(5):
1. 任取一层数据结构,检验其均匀性H,若对于该结构中某一区域R有H(R)=false,则分裂这个区域为4个子区;若4个彼此相邻属于同一个父结点的区域Rk1,…,Rk4满足H(Rk1∪Rk2∪Rk3∪Rk4)=true,则合并它们成为单一区域。当不再有区域可分裂或合并时,停止分割。
2. 如果二任意相邻区域Ri和Rj(可以不属于同一个父结点;可以有不同的大小)使得H(Ri∪Rj)=true,则合并它们。

2.2.1 分割算法
分割算法的描述如下:
1. 把金字塔数据结构中起始某个中间层的数据块编码全部压入工作堆栈RgStack。
2. 从RgStack弹出一个数据块编码Code。如果Code代表的数据块不均匀,就把Code分裂为四个数据块,再把颜色为黑色的那些块的编码压入堆栈RgStack。如果均匀,或者Code代表的已经是不可分的像素,那么把Code压入中间结果堆栈RgCode。
3. 反复执行2,直至堆栈RgStack空。
有两点值得解释一下。首先,我在程序中采用整形数来保存数据块编码,这样较字符串而言更加方便了计算(子结点或父结点编码的计算,数据块坐标的计算等等)。其次,我使用了递归方法产生中间层所有数据块的编码,相对于多重循环而言,不仅使程序书写简洁,而且起始的中间层号可以方便地修改,而多重循环结构要修改起始层则必须加或减循环的层数。

2.2.2 合并算法
合并算法的描述如下:
1. 把中间层里所有均匀的数据块的编码存入工作队列RgA。
2. 从RgA出队一个编码Code。在RgA中寻找和Code属于同一个父结点的其他数据块。如果找不齐所有四个,那么把Code和找到的都从RgA删除,而压入中间接果数组RgCode。如果找齐了,那么把这四个编码都从RgA删除,而把父结点的数据块编码入队到RgA中。
3. 反复执行2,直至队RgA空。
需要说明的一点:步骤1虽然是属于合并这部分的,但是在执行分裂时就可以方便地做掉,节省代码和时间。

2.2.3 相邻归并算法
相邻归并算法的描述如下:
1. 从RgCode中弹出一个编码,压入工作堆栈RgStack。
2. 从RgStack中弹出一个编码Code,将它入队到RgA。扫描RgCode,把所有与Code相邻的数据块的编码都压入堆栈RgStack。

 

 

3. 反复执行2,直至栈RgStack空。此时RgA里面存有的一系列数据块编码就是属于同一个特征块的,可以用来计算这个特征块的形状属性并保存起来。具体计算方法见2.4“形状属性的计算”。
4. 反复执行1-2-3,直至栈RgCode空。
这里有两点值得说明。首先,相邻归并算法是三个算法中最耗时的,占去了分合算法中绝大部分的运行时间(对一幅182×248单面像灰度图像做试验,分裂,合并,相邻归并共用3224毫秒,其中相邻归并占了3074毫秒)。而且其耗时随问题规模是非线性增长的。对于小而简单的图像,分合算法可以很快(3秒钟),可是对于较大或较复杂的图像,分合算法往往耗时很长(20~40秒)。这么大的差别就是来自于非线性增长的计算量。其次,判断两个编码所代表的数据块是否相邻的函数在相邻归并算法中被频频调用,它的效率会大大影响整个算法执行的时间。我曾经采用计算两个数据块中心的x向和y向距离,然后与半边长之和比较的方法来判断数据块是否相邻,方法直观,代码较少,但是由于涉及了浮点运算,并且对每一个编码都要做完整的一套计算,导致运行速度很慢。现在采用的是分析两个编码在各位上的组合方式,从大尺度开始一位一位进行检查,只要发现这两个数据块在某一尺度上不可能相邻,就马上做出不相邻的判断,除非它们能够一直保留到被证实符合一种相邻的编码组合。具体方法描述较繁,这里省略。采用这种方法之后,虽然代码长度增加了很多,但是执行效率却大大提高了。

2.3 成组算法
引入成组算法出于如下事实:一些本属于同一个脸部器官的特征块在分合之后仍然是分开的。分开的特征块的中心、大小、取向等等参数相对于一个代表完整的器官的特征块而言都是错误的,导致后面的几何匹配得不到正确结果。成组算法就是用来把挨得足够近的可能本属于同一个器官的特征块合并起来(尽管它们并不是连通的)。算法实现参考了Shi-Hong Jeng, Hong Yuan Mark Liao等(1)的工作。算法有两个特点:1. 合并半径取决于待合并的特征块的大小(一个重要参数-最大合并半径Nmax事先指定),特征块越大,则合并半径越小;2. 如果待合并的两个数据块中任一个大于Nmax¬,那么它们必须位于同一主轴上(主轴的定义在形状属性的计算中介绍)才能合并。下面的伪代码描述了成组算法,其中N(i)代表第i个块Bi包含的像素个数,Nb代表图像中特征块的总数。

Procedure Grouping
for N := 1 to Nmax do
  R := 8 – 6 * (N-1) / (Nmax-1)
  for i :=1 to Nb do
  if N(i) = N then
    “找到所有位于Bi的r邻域内的特征块{Bj}”
  if {Bj} ≠φ then

 

 


    “从{Bj}中找出离Bi最近的块Bk”
  if  (N(k) < Nmax) or (Bk与Bi位于同一根主轴上) then
    “把Bi和Bk合并”
  end for
end for
end Grouping

合并后的特征块的各个形状属性都要重新计算,具体方法见2.4“形状属性的计算”。

2.4 形状属性的计算
    二值化后的原始图像直接用黑和白的像素表示。在用分合算法分割之后,每一个连通的黑色特征块就由一组数据块编码来表示,编码所代表的数据块(可能是来自金字塔数据结构中不同的层次)在一起组成了该特征块。但是这样仍然不能方便地进行几何匹配。为了进行几何匹配,需要知道每一个特征块的位置,大小,倾角及大概的形状。这些参数统称为形状属性。从组成一个特征块的数据块编码来计算这个特征块的形状属性是继分合算法之后的重要步骤。由于它与分合算法结合更紧密(因为不同的分割算法导致不同的计算形状参数的方法),所以把它放在分合算法这部分来介绍。
    图像用f(x,y)来表示,x,y分别是横、纵坐标(已经离散化,所以下面都使用求和而不是积分),f(x,y)是该坐标处的灰度值。用字母B代表特征块。设特征块由一组正方形数据块{Bi}组成,Bi包含的像素个数为Ni,边长为Di(Ni = Di2),其中心(也就是重心)坐标为 。Ni和 可以从数据块编码通过计算得到,方法较简单,这里就不赘述了。形状属性的计算就是要把特征块的形状属性都用Ni(或Di)和 表达出来。
1.特征块的位置:
特征块的位置用它的重心来表示。重心坐标计算公式如下:
 , 
在我的应用中,像素都是黑白的,即f(x,y)只有两种取值:0(黑)或者255(白),而特征块所包含的像素一定是黑色的(这是特征块的定义决定的)。所以在公式中完全可以把f(x,y)省略(为了方便,下面的公式中都直接略去了f(x,y)),直接写作:
 , 
经过简单推导得到用Ni和 表达的重心坐标:
 ,

其中 是特征块包含的像素个数,即特征块的大小。
2.特征块的取向:
特征块的取向用特征块的主轴的倾角(规定取值范围 )来表示。特征块的主轴定义为使转动惯量I(θ)取值最小的转轴(6)。可以证明(证明略),主轴一定穿过重心,而且使转动惯量取极值的倾角θ满足条件(4):
 ,其中μ¬11,μ20,μ02是三个二阶中心矩
由此可以得到:
 ,k为整数
由于θ取值范围的规定,k的所有可能取值只有-1,0,1。
算法中对k的所有三个可能取值分别计算θ,舍弃不在 内的值。然后分别检查转动惯量二阶导I’’(θ)的符号,使得I’’(θ)>0的才是能够令I(θ)取极小值的主轴倾角θ。检查I’’(θ) 的符号所依据的公式是:

 

 

 
计算主轴倾角θ要用到中心矩。中心矩的一般定义和详细讨论可参见Robert M. Haralick 和 Linda G. Shapiro 的 Computer and Robot Vision(6)。
特征块B的(j+k)阶中心矩μjk为(注意这里同样略去了f(x,y)):
 
经简单计算可以得到:
 , , 
因此问题转化为求 。
这一部分的推导比较繁琐,结果如下:
 
 
 
有了这些公式,特征块的取向就可以由特征块所包含的数据块的编码计算出来了。
3.特征块的外接矩形
特征块的外接矩形定义为长边平行于主轴且与特征块外接的矩形。
 
图4 外接矩形的计算
为了求得外接矩形,对特征块做坐标变换:把坐标原点挪到重心处,再逆时针旋转坐标系θ,得到的新坐标系记为po’q。在新坐标系下找出所有数据块顶点(每个数据块有四个顶点)坐标的最值pmin,pmax,qmin,qmax。易知在新坐标系下外接矩形的四个顶点坐标为(pmin,qmin),(pmin,qmax),(pmax,qmin),(pmax,qmax),特征块重心到矩形四条边的距离分别是L1 = - pmin,L2 =  pmax,W1 = - qmin,W2 =  qmax。将坐标系反变换回去(先顺时针旋转θ再平移),就得到了原坐标系下特征块的外接矩形的四个顶点坐标。参见图4。
最后讨论一下两个特征块(“物体”)合并时形状属性的计算。设B1和B2这两个特征块合并成了B。
显然有N = N1 + N2; ; 。
由刚才得到的公式:
 
刚才i是对数据块计数的,现在推广到对特征块计数。可见如果把每个特征块的∑xy,∑x2,∑y2这三个数据保存在“物体”属性里,那么合并以后的特征块的∑xy,∑x2,∑y2很容易就能计算出来,其主轴倾角也就容易计算了。
 
“物体”合并后外接矩形的近似计算
合并后“物体”的外接矩形理论上应该由所有组成这个“物体”的数据块来计算。由于在“物体”属性里不能再保留所有成员数据块的编号(否则存贮量太大),所以合并后“物体”的外接矩形无法精确求出。可以把各个子“物体”的外接矩形的外接矩形近似当作合并后“物体”的外接矩形,如图5。计算的方法与单个特征块外接矩形大同小异,这里就不重复了。
 

分合完成,物体属性都计算完毕之后,就可以在图像中标出所有的特征块。在我的程序中,每个特征块用一个外接矩形和一个位于重心的十字架来表示。可以看见,虽然图像中有很多细碎的黑点,但是最后得到的特征块数目并不大(图6中有20个特征块)。这正是因为分合算法具有一定的抗噪声能力。


3.1 
预处理
   
预处理常常有以下几个目的:把图像变成便于后续处理的形式;提高对比度,抑制背景而突出面部特征;去除噪声和其他有害的信息。在我的应用中,把图像变成分合算法能够处理的二值化图像是预处理最重要的功能。其次,希望预处理能够提高二值化前的对比度,把面部器官清晰地突出出来,这样加上合适的二值化方法,就能够保证二值化后的图像中,面部器官以黑色特征块的形式被尽量完整而独立地保留下来。

3.1.1  图像对比度的增强
   
增强图像对比度有很多种方法,常见的有“S”形变换,直方图均衡化方法等等(参见Kenneth R. Castleman, Digital Image Processing4)),都是十分经典而使用很广的,这里就不赘述其具体实现了。简单地说,“S”形变换办法将灰度处于某一范围内(比如灰度很大和很小,或者灰度取值处于中间)的像素的灰度分布差距拉大,从而使对比度提高,这是以牺牲其他灰度范围的对比度为代价的。直方图均衡化则把像素的灰度分布尽量展开在所有可能的灰度取值上,同样能够使对比度提高。
   
尝试把这些方法应用到面像定位的预处理中时,效果却不好。原因是这些方法增强对比度没有针对性,结果往往不仅不能突出脸部器官的特征,有时还会造成特征的损失。尤其是直方图均衡化,其结果往往是不能忍受的。
   
在这样的情况下,我根据实际的需要找到了一种能够突出脸部特征,有针对地增强对比度的方法。
众所周知,边缘提取是特征提取中的一种重要方法。利用边缘提取,可以把图像中灰度发生跳跃变化的地方全部显现出来。脸部器官(尤其是眼睛和嘴)表现为在整个脸的较均匀灰度的背景上的暗团块。所以利用边缘提取可以得到脸部器官的轮廓和内部发生灰度突变的所有边缘。
    
但是实践证明直接采用边缘提取来提取脸部器官的特征,其效果并不好。边缘提取可类比于微分运算,对于噪声十分敏感,容易造成本不相连的区域粘连起来。我现在采用的办法是:对输入的原始灰度图像做Sobel运算(4)提取边缘,得到的是灰度的边缘图像(边缘特征用较小的灰度值,即较偏黑色来表示)。将边缘图像按照一定的比例加到原始图像上,再做一个线性变换保证所有灰度值落在0255的有效灰度范围之内。这样,原始图像中边缘特征就被加强了,脸部器官的特征从而也被加强了,和脸部较为均匀的灰度形成了鲜明的对比。
从图7可以看到原始灰度图像,经Sobel运算提取了边缘之后的灰度边缘图像,以及相加处理完毕的图像(原始图与边缘图比重为5:1)作为比较,使用“S”形变换(参数α4)为0.5)和直方图均衡化的结果也显示在图中。

 

 

 


   
      
原始灰度图像              Sobel运算结果             相加处理完毕
   
     “S”
形变换结果          直方图均衡化结果         灰度分布标准化结果

    还有一种不错的预处理的方法是对灰度分布进行标准化。这是参考了梁路宏, 艾海舟, 何克忠(7)的方法。预先设定合适的灰度平均值和方差,对输入图像做一个线性变换,使得其灰度平均值和方差都调整得和预先设定值近似相等。这个线性变换的公式是:
 
    
   
其中 是输入灰度图像, 是变换后的灰度图像,xy是离散化了的像素坐标,W是图像宽度,H是图像高度, 是预先设定的标准的灰度平均值和方差, 则是输入灰度图像的平均值和方差。 可按下式计算:
 
 
标准化的效果见图7(标准的灰度平均值为127,方差为10000)。这样做的好处是可以消除部分由于光照条件不同等原因造成的图与图之间的差异,得到一个标准化的输入。而且对于原始输入图像对比度较差的情形,这样做就可以提高对比度。但是实用中发现对于某些情形,这种方法会导致脸部特征的损失。所以目前的算法中没有采用它。

3.1.2  二值化
   
二值化是预处理中最关键的步骤,因为它直接产生能够被分合算法使用的二值化图像。可以说,二值化结果的好坏决定了分合算法结果的好坏,从而决定了整个面像定位能否成功。
   
二值化的方法举不胜举,但都可以分为取全局阈值的二值化和取局部阈值的二值化两大类。
   
取全局阈值的二值化方法中,有一种叫做组内方差最小化方法8),由Otsu1979年提出。它的思路是,最好的阈值应该使得被阈值分开的两组的方差的加权和达到最小,其中某组的加权系数就是该组的概率(其实就是该组像素数目占总像素数目的比例)。
设所有像素被阈值t分为两组,灰度≤t的称做组1,灰度>t的称做组2σ12(t)σ22(t)1组和2组各自的方差;q1(t)q2(t)1组和2组各自的概率;μ1(t)μ2(t)1组和2组各自的灰度平均值。定义组内方差σW2(t)为这两个组的方差的加权和:
 
所有像素的平均灰度和方差记做μσ2。有下面的关系式成立:
 
其中σB2(t)叫做组间方差,
 
显然σ2并不随t变化,所以使得组内方差最小的t就是使得组间方差最大的t。寻找这个t的方法是穷举法,即搜索t的每一个可能值,计算出相应的σB2(t),然后找出最大的σB2(t)对应的t

 

 

 

当阈值为t时的各参量已知时,阈值为t+1时的各参量可以用下列递推公式算出,从而避免对每个t都做独立的大量的运算。
 ,递推起点是q1(1)=P(1)
 ,递推起点是μ1(0)=0
 
有了q1(t+1),μ1(t+1)和μ2(t+1)之后,σB2(t+1)就可以求出。
    这个全局取阈值方法常常可以得到不错的效果。但是对于面像定位的应用,全局取阈值的方法就不合适了。这是因为面像各器官相对于面部皮肤的对比度各不相同,比如眼睛就往往比嘴的对比度要高得多。如果对全图像使用同一个阈值,那么无论怎么调整阈值,结果不是眼睛不正确就是嘴不正确,或者两个都不怎么好,捉襟见肘。
    我以组内方差最小化方法为基础,构造了一种局域取阈值的方法。其思路是 :首先对整幅图像用组内方差最小化方法求出阈值,并记录下组间方差。然后把整幅图像划分为m×n个正方形,每个正方形边长都为十几个像素量级。对每个正方形的子图像按组内方差最小化方法求出一个阈值和一个组间方差。最后某一个正方形子图像二值化所使用的阈值是由全局阈值和局域阈值,以全局组间方差和局域组间方差作为权重,再加上事先指定的加大全局阈值权重的因子,综合计算出来。显然全局阈值应该占到绝大部分的比重,因为正方形子图内的像素分部可能与全局分布极不相同,甚至出现全是单一灰度的极端情形。如果仅仅用子图的像素分布来决定子图使用的阈值,那么各个子图取的阈值就会各自相差很大,导致结果图像零乱破碎,无法使用。
全局取阈值二值化方法和局域取阈值二值化方法的结果比较见图8:
   
   全局取阈值二值化方法     局域取阈值二值化方法1    局域取阈值二值化方法2
                                  逐块取阈值                逐点取阈值
图8 二值化方法比较
(可以看出局域取阈值二值化方法对嘴的保存更完整)

    该局域取阈值二值化方法有边界效应的缺陷。所谓边界效应,是指由于相邻两个正方形子图所取阈值不同,造成边界两边本来灰度变化缓慢的像素有可能一边被二值化成黑色,一边被二值化成白色,形成突变。为了解决这个问题,我曾经构造了另一种局域取阈值二值化方法:对每一个像素,都使用它周围一个边长为十几个像素的正方形区域内的灰度分布来计算一个局域阈值,然后和全局阈值合成这个像素使用的阈值。这样的效果虽然不错,避免了边界效应,但是计算量太大,耗时太长,所以目前的算法中没有采用。这两种局域取阈值二值化方法的结果比较见图8(图中没有明显的边界效应)。

 

 


3.2 
几何匹配算法
   
图像分割完成后,结果保存在物体数组中,每一个物体就是图像中一个连通的黑色区域。物体的属性也已经计算出来,包括其位置,大小,取向,外接矩形。几何匹配算法所做的就是在物体中寻找可能是面像器官的组合,所利用的判据则是面像器官之间所固有的几何关系。使用这些判据按照一定的权重组合,可以对每一组可能被当作面部特征组合的物体计算一个评价函数。如果这个函数值超过了一定的阈值,就认为它是一张面像。
   
几何关系的模型参考的是Shi-Hong JengHong Yuan Mark Liao等人(1)的工作,但是在评价函数的构造上有一些改动。详细的规则在下面解释每个评价函数时介绍。举个例子来说明几何模型的意义。如图9,以两眼中心连线为基线,两眼中心距离为D,则鼻孔,眉毛和嘴巴到基线的垂直距离分别约为0.6D0.3D1.0D。越符合这个标准的物体组合,其评价函数值越大。
 
   
实际的照片中,眉毛常和头发粘连在一起无法分割出来,而鼻子往往因为与脸部皮肤的对比太不强烈而根本不出现在二值化图像中。经过实践,发现使用仅仅包括眼睛和嘴巴的几何模型就足以解决问题,所以我的算法只考虑了眼睛和嘴巴。
具体的算法是这样的:
1
.首先任意挑选一对物体(设为eye1eye2),计算它们作为一对眼睛的评价函数: 
强制性条件:eye1必须在eye2的左边(防止同一对物体被考虑两遍);eye1eye2的连线倾角必须在 之间(面像的角度不能太倾斜)。
评价函数表达式:
 
其中l1l2分别是eye1eye2的归一化长度(长度除以eye1eye2的中心距离D),O1eye1的主轴倾角,O2eye2的主轴倾角,Oeye1eye2连线的倾角。
易知表达式中各项越接近0,则评价函数值越大,越接近1
评价函数中各项代表的含义:
(l1-l2)2
:两眼的长度应该相等。
(l1+l2-1)2
:两眼长度之和应该大致等于两眼的中心距。
(O1-O)2
(O2-O)2:两眼的倾角应该都等于两眼中心连线的倾角。
如果Eeye超过一定的阈值(0.85),那么认为这一对物体很可能是一对眼睛,进入下一步2。否则舍弃这对物体,另取一对重新做1
2
.在其余的物体中任取一个,计算它作为嘴巴的评价函数:
强制性条件:该物体须在眼睛的下面;该物体长宽比不能小于2(否则作为嘴巴来说太宽了)。
评价函数表达式:
 
其中dm物体中心到两眼中心连线的垂直距离,O是两眼中心连线的倾角(在1中算出),mO物体的倾角,xm物体相对于两眼中心连线中点的横向坐标(计算方法:先把坐标系原点移动到两眼中心连线中点,然后旋转坐标系使得两眼中心连线与x轴重合,此时物体的重心的x坐标就是xm),ml物体的长度,el是两眼长度的平均值。

 


评价函数中各项代表的含义:
(dm-1)2
:嘴到两眼中心连线的垂直距离应该大致等于两眼的中心距。
(O-mO)2
:嘴的倾角应该等于两眼中心连线的倾角。
xm2
:嘴的左右位置应该和两眼中心连线的中点一致。
(ml-1.5el)2
:嘴的长度应该大致等于眼睛长度的1.5倍。
如果Emouth超过了一定的阈值(0.7),那么认为物体很可能是一张嘴。如果这是第一个找到的嘴的候选者,那么把它存储起来。如果它不是第一个,那么就和存储的嘴的候选者进行比较,保留评价函数值较大的那一个。把所有的物体(除了两个眼睛)都检查过一遍之后,进入下一步3
由于照片中嘴和脸部皮肤之间的对比差别往往不大,所以经过二值化和分割以后,嘴常常会破碎成两块或更多块。所以我引入了一种方法来拼合这些碎块。当每次遇到一个可能是嘴的物体时(记为m),不是简单的和已存储的候选者(记为mouth)作比较二选一,而是尝试把它们合并成一个新的物体(记为mmerge),然后在mmouthmmerge三个物体中选出评价函数最高的那一个作为新的mouth存储起来。这样如果有几个碎块拼在一起更符合嘴的要求的话,算法就会把它们拼起来。考虑到嘴的碎片常常不符合长宽比不小于2的要求,上面提到的强制要求的第二条被取消,代之以要求拼合后嘴的长宽比不小于2。对拼合的嘴还有另一个强制要求,就是两个需要被拼合的碎块相距不能超过两个标准嘴的长度(眼睛平均长度的3倍)。
            
        
图像分割的结果                  几何匹配的结果
10 嘴的拼合效果
10显示了图像分割的结果与几何匹配的结果。可以从图中看到,嘴的拼合的确解决了嘴巴破碎的问题。
3
.计算综合的评价函数,表达式为:
 
如果这个评价函数值超过了一定的阈值(0.8),那么认为这些找到的物体合起来的确是一张面像。
有些情况下,程序可能错把眉毛当作眼睛,与嘴合起来形成一张面像,这样同一张面像就可能被检测到两次。经过实验,我发现单纯提高评价函数的阈值无法很好地解决这个问题。阈值设得大,虽然可以排除掉眉毛与嘴巴的组合,却会导致漏判其他的面像。所以我在3中又增加了一层判断:如果发现新找到的面像与已经找到的面像占用了共同的物体,那么就比较这两个面像的评价函数值,仅仅保留评价函数值较大的那个作为检测结果。实践证明这种方法能够较好地解决上述问题。

 

 

几何匹配结束后,在图像中显示代表眼睛和嘴巴的“物体”,表示找到的面像的位置


第四章 软件的组成和操作
我的面像定位演示软件采用Delphi 3.0书写,应用了面向对象的编程方法,有简单明了的界面。下面详细介绍其组成和操作。
4.1  软件的组成
面像定位演示软件由以下一些模块组成,可以分为三个层次:
应用层:
Main.pas 软件主模块,负责接受用户输入,调用其他模块完成面像定位的任务,并将结果显示给用户
算法层:
z_FLoCat.pas 面像定位主处理模块,包括面像定位主类(图像的装载和初始化,预处理,常用数字图像处理方法),基本类型定义,基本常数定义和一些函数
z_FLoRG.pas 区域生长算法模块,包括区域生长类(分合算法),金字塔数据结构
z_FLoMatch.pas 面像匹配模块,包括几何匹配类
基础层:
z_Point.pas 基本数字图像处理模块,包括常用的数字图像处理函数(没有做成类)
z_DataStructure.pas 基本数据结构模块,包括链表,堆栈和队列类
z_DIB.pas Windows位图处理模块,为了能够把位图装载成为可以获取的内存数据(Delphi不提供这样的低级功能,使用Windows API写成) 
应用层和基础层代码份量虽然不小,但是不涉及主要算法,所以这里就不详细展开了。基本的数字图像处理方法参考了Kenneth R. Castleman, Digital Image Processing(4)和Anil K. Jain, Fundamentals of Digital Image Processing(8);基本数据结构部分参考了徐士良和朱明方,软件应用技术基础(10)。
下面一一介绍算法层的三个模块:
1.z_FLoCat.pas
在这个模块中,定义了面像定位主类TFLoc,它的成员主要包括涉及位图操作的变量(比如图像数据临时内存FLoT),位图读取(如GetDIBFromFile)、初始化(InitFLoDIB)、显示(如PaintImg)、转化(如BmpFromFLoDat)的方法,数字图像处理常用的方法(如提取边缘的Sobel,直方图均衡化FlattenBMPbyDat等等),面像定位算法中的预处理部分PreProcessing(调用了本模块的Sobel,Add和z_Point中的LocalThresholding等过程来实现预处理)。模块还定义了重要的全局变量,如存放图像数据的FLoDat,临时存放图像数据用于显示结果的FLoT2,图像高度FLoH,图像宽度FLoW,为了配合计算机的32位对齐方式而设的折合图像宽度bbWidth,图像像素总数nFaceFLoDim,存放“物体”的链表FloObjects。模块也定义了一些非常重要的数据类型,比如存放图像的内存数组类型FLoD,存放一维整型直方图的数组类型Hist和浮点型直方图的数组类型sgHist,金字塔数据结构要用到的各层数组类型PyR0~PyR7,金字塔数据结构类型Pyramid,“物体”类型FloObject,面像类型Face。模块还定义了一些常量,比如EPS表明两个浮点数相差不超过多大时可以认为相等,ORIGINAL_SOBEL_RATIO表示的是在预处理中,原灰度图像和sobel提取边缘后的灰度图像相加时各自所占比重的比值。最后,模块还包含了一些常常要用到的不属于数字图像处理范畴的过程和函数,比如在FLoT2中画线的Line,画物体的DrawObject,计算坐标旋转的Rotate,求最大和最小值的Max和Min等。

 


2.z_FLoRG.pas
在这个模块中,定义了区域生长类TFLoRG.,其成员主要包括金字塔数据结构PyR,工作堆栈和队列RgStack,RgCode,RgA等变量,还有金字塔数据结构初始化InitPyramid,寻找“物体”FindObjects(通过调用分合算法Split,Merge和MergeNeighbor实现),成组Grouping,显示“物体”DisplayObjects等方法,及一些支持分合算法的过程和函数。模块中还包括常量START_LEVEL(分合算法的起始层号)和NMAX(Grouping算法中除了共线的特殊情形最大可合并块的大小)。
3.z_FLoMatch.pas
在这个模块中,定义了几何匹配类TFloGeometricMatch,其成员主要有存放找到的面像结果的堆栈Candidates,面像个数FaceCount,保存拼合后的嘴的链表MergedMouths,执行几何匹配算法的FindFaces等。这个模块定义的常量有EEYE_THRESH,EMOUTH_THRESH和EVAL_THRESH,是几何匹配算法中用到的各个评价函数阈值。
4.2  软件的操作

使用时首先点击窗口右面偏下的“Load”按钮,用“打开文件”对话框选择自己要处理的图像文件(必须是Windows 256级灰度位图,大小不能超过512×384像素)。图像装载进来以后,点击“Preprocess”进行预处理,结果是一幅二值图像。点击“Find Objects”,程序使用分合算法寻找“物体”,并把结果显示在窗口中。这一个过程往往需要比较长的时间。点击“Find Faces”,程序使用几何匹配算法搜寻面像,并且把结果显示出来。“Shift Image”按钮用来切换原始灰度图像,预处理后二值图像,金字塔数据结构示意图,寻找“物体”的结果显示,寻找面像的结果显示。除了“Shift Image”按钮随时都可以点击之外,其他四个按钮应依次点击,一步步执行。如果顺序颠倒了,程序会给出提示,并不执行命令。
窗口右面中间的“PyX”,“PyY”编辑框是在显示金字塔数据结构示意图时用来指定显示哪个128×128方块的。
窗口右面偏上的四个编辑框“Input1”,“Input2”, “Output1”,“Output2”和按钮“Debug RG”都是用来调试分合算法的。只要在程序源代码中相应的部分做一点修改,就可以单独测试分合算法中的各个底层过程和函数

第五章  工作总结
6.1  关于几何匹配算法和分合算法的总结
    几何匹配算法(利用面像各器官之间的几何关系的方法)是面像定位中常见的一种算法,仍然被频繁地以各种形式采用,比如Shi-Hong Jeng和Hong Yuan Mark Liao等(1),Jun Miao和Baocai Yin等(9),卢春雨和张长水等(11)。通过这次论文工作,我对几何匹配的优缺点有了直观的了解。它利用了人们对于面像各器官间几何关系的先验知识,所以算法清晰简单,每一步骤,每一评价函数项都有直观的解释。它的执行速度在我的程序中主要受到了分割算法的瓶颈限制,如果换用一种更快的分割算法,那么速度将会是很快的。但是它的准确率不高,常会出现漏判。这是因为它依赖于所有面部特征都完整地被提取,所以对预处理要求高,而且对转角较大的侧脸、光照极度不均匀、部分脸被遮蔽(眼镜, 围巾等)适应性不好,因此我认为它不适合单独应用。但是如果有可能与利用标准面像图像或者其变换结果直接或者经特征提取后进行匹配的方法结合起来使用,则它的快速的优点可以得到利用,而它的准确率不高的缺点可以避免。

 

   在使用几何匹配算法之前必须对图像进行分割,而且分割的结果严重影响匹配的效果,所以分割算法是使用几何匹配方法的面像定位系统中的重点。我目前采用的分合算法是一种经典而有效的算法,在很多图像处理/计算机视觉/模式识别的书中都有介绍。其优点是具有一定的抗噪声能力,符合人眼视觉多尺度的特征。缺点是要求存储量大,运行速度慢。要求存储量大在拥有数十M内存的个人计算机上不是问题。但是运行速度慢却是一个瓶颈,限制了它的应用。

你可能感兴趣的:(数据结构与算法,人工智能,数据库)