日萌社

人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新)


人脸识别功能实现的原理介绍与算法介绍

人脸识别:人脸数据集

Adaboost 人脸检测:Haar特征及积分图、分类器的级联

关键点提取:face_recognition、疲劳检测、人脸校准、人脸数据库


face_recognition 是世界上最简单的人脸识别库了,你可以通过Python引用或者命令行的形式使用
它,来管理和识别人脸。
该软件包使用dlib中最先进的人脸识别深度学习算法,使得识别准确率在Labled Faces in the world
数据集下达到了99.38%准确率。
它同时提供了一个叫face_recognition的命令行工具,以便你可以用命令行对一个文件夹中的图片进
行识别操作。
安装:pip install face_recognition

创建两个存储图片的目录,known_people目录存储的图片,
输入命令:
face_recognition /known_people/ /unknown_pictures/
输出结果:
/unknown_pictures/unknown1.jpg,Obama
输出中,识别到的每张脸都单独占一行,输出格式为<图片名称>,<人名>
如果想指定想并行处理图像,则可以指定cpu核数,例如指定4个cpu则可认为是并行处理4倍的图像:
face_recognition --cpus 4 /known_people/ /unknown_pictures/

图像载入函数——load_image_file
load_image_file( file , mode='RGB' )
加载一个图像文件到一个numpy array类型的对象上。
参数:
file :待加载的图像文件名字
mode :转换图像的格式
只支持“RGB”(8位RGB, 3通道)和“L”(黑白)
返回值: 一个包含图像数据的numpy array类型的对象

首先我们加载一张人脸图片,调用face_landmarks函数 从该图片中,得到一个由脸部特征关键点位置组成的字
典记录列表:
(脸部特征包括:鼻梁nose_bridge、鼻尖nose_tip、 下巴chin、左眼left_eye、右眼right_eye、左眉 left_eyebrow、右眉right_eyebrow、上唇top_lip、下 唇bottom_lip)

Dlib库实现了2014年一篇非常经典的人 脸特征点检测的论文: Face Alignment at 3000 FPS via
Regression Local Binary Features,其 人脸特征点检测又快又准。 深圳大学于仕祺老师公布的免费的
libfacedetect,人脸特征点检测也非常 快,效果也不错,和Dlib一样为68特征 点检测,但鲁棒性不如Dlib。Seetaface、
Dlib和libfacedetect都提供了人脸特征 点检测的接口。

人脸特征提取函数——face_landmarks
face_landmarks( face_image , face_locations=None, model="large" )
给定一个图像,提取图像中每个人脸的脸部特征位置
参数:
face_image :输入的人脸图片
face_locations=None
可选参数,默认值为None,代表默认解码图片中的每一个人脸。 若输入face_locations()[i]可指定人脸进行解码
model="large" :输出的特征模型,默认为“large”,可选“small”。 当选择为"small"时,只提取左眼、右眼、鼻尖这三种脸部特征。

人脸特征提取函数——face_landmarks
face_landmarks( face_image , face_locations=None, model="large" )
给定一个图像,提取图像中每个人脸的脸部特征位置
参数:
face_image :输入的人脸图片
face_locations=None
可选参数,默认值为None,代表默认解码图片中的每一个人脸。
若输入face_locations()[i]可指定人脸进行解码 model="large"
输出的特征模型,默认为“large”,可选“small”。 当选择为"small"时,只提取左眼、右眼、鼻尖这三种脸部特征。

人脸编码函数——face_encodings

返回值:
返回值类型为:List[Dict[str,List[Tuple[Any,Any]]]],是由各个脸部特征关键点位置组成的字典记录
列表,一个Dict对象对应图片中的一个人脸,其key为某个脸部特征:
如输出中的nose_bridge、left_eye等,value是由该脸部特征各个关键点位置组成的List,关键点位置是
一个Tuple(如上输出中,nose_bridge对应的关键点位置组成的列表为[(881L, 128L), (880L, 141L),
(880L, 154L), (879L, 167L)]

人脸编码函数——face_encodings
face_encodings( face_image , known_face_locations=None , num_jitters=1 )
给定一个图像,返回图像中每个人脸的128脸部编码(特征向量)。
参数:
face_image 输入的人脸图像
known_face_locations 可选参数,如果你知道每个人脸所在的边界框
num_jitters=1 在计算编码时要重新采样的次数。越高越准确,但速度越慢(100就会慢100倍)
返回值: 一个128维的脸部编码列表

载入图片,构建一个ImageDraw对象: polygon() 方法用于绘制多边形:第一个参数是多边
形的几个顶点位置组成的list,第二个参数fill是填充该 多边形的颜色。
line() 方法是用来画多个点之间构成的线段,第一个参 数是点位置组成的list,第二个参数fill是线段的颜色,
第三个参数width是线段的宽度。

face_distance( face_encodings, face_to_compare )
给定一组面部编码,将它们与已知的面部编码进行比较,得到欧氏距离。对于每一个比较的脸,欧氏距离代表了这些
脸有多相似。
参数:
faces:要比较的人脸编码列表
face_to_compare:待进行对比的单张人脸编码数据
tolerance:两张脸之间有多少距离才算匹配。该值越小对比越严格,0.6是典型的最佳值
返回值: 一个numpy ndarray,数组中的欧式距离与faces数组的顺序一一对应

face_recognition
找到每个人眼睛、鼻子、嘴巴和下巴的位置和轮廓。
import face_recognition
image = face_recognition.load_image_file("your_file.jpg")
face_locations = face_recognition.face_locations(image)

batch_face_locations( images, number_of_times_to_upsample=1, batch_size=128 )
使用cnn人脸检测器返回一个包含人脸特征的二维数组,如果使用了GPU,这个函数能够更快速的返回结果;如果不使
用GPU的话,该函数就没必要使用。
参数:
images : 一个包含图像数据的list,每个成员都是一个 numpy array类型
number_of_times_to_upsample: 从images的样本中查找多少次人脸,该参数的值越高的话越能发现更小的人脸
batch_size: 每个GPU一次批处理多少个image
返回值:
一个元组列表,列表中的每个元组包含人脸的位置(top, right, bottom, left)

compare_faces( known_face_encodings, face_encoding_to_check, tolerance=0.6 )
比较脸部编码列表和候选编码,看看它们是否匹配。
参数:
known_face_encodings:已知的人脸编码列表
face_encoding_to_check:待进行对比的单张人脸编码数据
tolerance:两张脸之间有多少距离才算匹配。该值越小对比越严格,0.6是典型的最佳值。
返回值:
一个 True或者False值的列表,该表指示了known_face_encodings列表的每个成员的匹配结果

face_locations( img, number_of_times_to_upsample=1, model='hog' )
给定一个图像,返回图像中每个人脸的面部特征位置(眼睛、鼻子等)
参数:
img:一个image(numpy array类型)
number_of_times_to_upsample:从images的样本中查找多少次人脸,该参数值越高的话越能发现更小的人脸。
model:使用哪种人脸检测模型。“hog” 准确率不高,但是在CPUs上运行更快,“cnn” 更准确更深度(且
GPU/CUDA加速,如果有GPU支持的话),默认是“hog”
返回值:
一个元组列表,列表中的每个元组包含人脸的位置(top, right, bottom, left)

face_recognition API 总结:
load_image_file( file, mode='RGB' )
face_landmarks( face_image,face_locations=None,model="large" )
face_encodings( face_image, known_face_locations=None, num_jitters=1 )
batch_face_locations( images, number_of_times_to_upsample=1, batch_size=128 )
compare_faces( known_face_encodings, face_encoding_to_check, tolerance=0.6 )
face_distance( face_encodings, face_to_compare )

实践 1 :人脸特征点定位及上妆

ImageDraw 模块提供了图像对 象的简单2D 绘制。用户可以 使用这个模块创建新的图像, 注释或润饰已存在图像,为 web应用实时产生各种图形
Python 图像处理库 PIL ImageDraw 模块
https://pillow.readthedocs.io/en/3.1.x/reference/ImageDraw.html

载入图片,构建一个ImageDraw对象:
polygon() 方法用于绘制多边形:第一个参数是多边形的几 个顶点位置组成的list,第二个参数fill是填充该多边形的颜
色。
line() 方法是用来画多个点之间构成的线段,第一个参数是 点位置组成的list,第二个参数fill是线段的颜色,第三个参 数width是线段的宽度。
d.line( face_landmarks['right_eyebrow'] , fill=(68, 54, 39, 150), width=5 )

实践 2 :人脸相似度比较
dlib 库的“ shape_predictor_68_face_landmarks.dat ”提取的只是人脸的 68 个特征点(例如眼、嘴等
位置),“ dlib_face_recognition_resnet_model_v1.dat ”是人脸特征提取的分类网络,提取的 128
维信息即是人脸的特征信息,即人脸特征向量。
不同人脸特征向量之间,余弦距离或欧式距离越大,则两个特征值相似度越低,属于同一个人
的可能性越小,此时判断两人为不同的人。
人脸特征向量距离函数 ——face_distance()
face_distance( face_encodings , face_to_compare )
给定一组面部编码,将它们与已知的面部编码进行比较,得到欧氏距离。对于每一个比较的 脸,欧氏距离代表了这些脸有多相似。
参数:
face_encodings :输入的人脸图片
face_to_compare :待进行对比的单张人脸编码数据
tolerance : 比对阈值,即两张脸之间有多少距离才算匹配。该值越小对比越严格,0.6 是典型的最佳值
返回值: 一个numpy ndarray ,数组中的欧式距离与 faces 数组的顺序一一对应
dlib 库的“ shape_predictor_68_face_landmarks.dat ”提取的只是人脸的 68 个特征点(例如眼、嘴等
位置),“ dlib_face_recognition_resnet_model_v1.dat ”是人脸特征提取的分类网络,提取的 128
维信息即是人脸的特征信息,即人脸特征向量。
不同人脸特征向量之间,余弦距离或欧式距离越大,则两个特征值相似度越低,属于同一个人
的可能性越小,此时判断两人为不同的人。

余弦相似度/余弦相似性

1.余弦相似度
	通过计算两个向量的夹角余弦值来评估他们之间的相似度。
	夹角余弦值的取值范围在[-1,1],任何角度的余弦值都在-1到1之间。
	两个向量之间的夹角角度的余弦值(余弦相似度的值)确定两个向量是否大致指向相同的方向,与向量的的长度无关,仅仅与向量的指向方向相关。
	两个向量之间夹角为0°的话,余弦值(余弦相似度的值)为1,代表两个向量的指向方向相同。
	两个向量之间夹角为90°的话,余弦值(余弦相似度的值)为0,代表两个向量的指向方向垂直。
	两个向量之间夹角为180°的话,余弦值(余弦相似度的值)为-1,代表两个向量的指向方向相反。
	余弦相似度通常用于正空间,因此给出的值为0到1之间,通常用于文本挖掘中的文件比较。
	一个向量空间中两个向量夹角间的余弦值作为衡量两个个体之间差异的大小,余弦值接近1,夹角趋于0,表明两个向量越相似;
	余弦值接近于0,夹角趋于90度,表明两个向量越不相似。

2.余弦相似度,又称为余弦相似性,是通过计算两个向量的夹角余弦值来评估他们的相似度。
  余弦相似度将向量根据坐标值,绘制到向量空间中,如最常见的二维空间。
  余弦相似度用于评估两个向量的夹角的相似度。
  余弦值的范围在[-1,1]之间,值越趋近于1,代表两个向量的方向越接近;越趋近于-1,他们的方向越相反;接近于0,表示两个向量近乎于正交。
  最常见的应用就是计算文本相似度。将两个文本根据他们词,建立两个向量,计算这两个向量的余弦值,就可以知道两个文本在统计学方法中他们的相似度情况。
  实践证明,这是一个非常有效的方法。

3.余弦相似性通过测量两个向量的夹角的余弦值来度量它们之间的相似性。0度角的余弦值是1,而其他任何角度的余弦值都不大于1;并且其最小值是-1。
  从而两个向量之间的角度的余弦值确定两个向量是否大致指向相同的方向。两个向量有相同的指向时,余弦相似度的值为1;
  两个向量夹角为90°时,余弦相似度的值为0;两个向量指向完全相反的方向时,余弦相似度的值为-1。
  这结果是与向量的长度无关的,仅仅与向量的指向方向相关。余弦相似度通常用于正空间,因此给出的值为-1到1之间。
  注意这上下界对任何维度的向量空间中都适用,而且余弦相似性最常用于高维正空间。
  例如在信息检索中,每个词项被赋予不同的维度,而一个维度由一个向量表示,其各个维度上的值对应于该词项在文档中出现的频率。
  余弦相似度因此可以给出两篇文档在其主题方面的相似度。
  另外,它通常用于文本挖掘中的文件比较。此外,在数据挖掘领域中,会用到它来度量集群内部的凝聚力。
  
4.两个向量间的余弦值可以通过使用欧几里得点积公式求出:

	给定两个属性向量,A和B,其余弦相似性θ由点积和向量长度给出,如下所示:

	给出的相似性范围从-1到1:-1意味着两个向量指向的方向正好截然相反,1表示它们的指向是完全相同的,0通常表示它们之间是独立的,
	而在这之间的值则表示中间的相似性或相异性。
	对于文本匹配,属性向量A和B通常是文档中的词频向量。余弦相似性,可以被看作是在比较过程中把文件长度正规化的方法。
	在信息检索的情况下,由于一个词的频率(TF-IDF权)不能为负数,所以这两个文档的余弦相似性范围从0到1。并且,两个词的频率向量之间的角度不能大于90°。

5.角相似性
	“余弦相似性”一词有时也被用来表示另一个系数,尽管最常见的是像上述定义那样的。透过使用相同计算方式得到的相似性,
	向量之间的规范化角度可以作为一个范围在[0,1]上的有界相似性函数,从上述定义的相似性计算如下:

	这式子适用于向量系数可以为正或负的情况。或者,用以下式子计算

	这式子适用于向量系数总为正的情况。
	虽然“余弦相似性”一词有时会用来表示这个角距离,但实际上很少这样说,因为角度的余弦值只是作为一种计算角度的简便方法而被用到,
	本身并不是意思的一部分。角相似系数的优点是,当作为一个差异系数(从1减去它)时,产生的函数是一个严格距离度量,
	而对于第一种意义的“余弦相似性”则不然。然而,对于大多数的用途,这不是一个重要的性质。若对于某些情况下,
	只有一组向量之间的相似性或距离的相对顺序是重要的,那么不管是使用哪个函数,所得出的顺序都是一样的。

6.与“Tanimoto”系数的混淆
	有时,余弦相似性会跟一种特殊形式的、有着类似代数形式的相似系数相混淆:

	事实上,这个代数形式是首先被Tanimoto定义,作为在所比较集合由位元向量表示时计算其Jaccard系数的方法。
	虽然这公式也可以扩展到向量,它具有和余弦相似性颇为不同的性质,并且除了形式相似外便没有什么关系。

7.Ochiai系数
	这个系数在生物学中也叫Ochiai系数,或Ochiai-Barkman系数:

	这里A和B是集合,n(A)是A的元素个数。如果集合由位元向量所代表,那么可看到Ochiai系数跟余弦相似性是等同的。

8.计算余弦值推导
	1.余弦函数在三角形中的计算公式为

		a和b代表两个向量(向量是在空间中具有大小和方向的量,在数据计量中表示带箭头的线段)

	2.二维空间的余弦函数的公式:将a、b两个向量二维化,通过余弦定理与二维空间结合,即可推导出来二维空间下计算两个向量的余弦相似性公式。

		上图中向量a用坐标(x1,y1)表示,向量b用坐标(x2,y2)表示。向量a和向量b在直角坐标中的长度分别为

		向量a和向量b之间的距离我们用向量c表示,那么向量c在直角坐标系中的长度为

		将a,b,c带入三角函数的公式中便得到了直角坐标系中向量表示的三角形的余弦函数。
		二维向量图和余弦定理的结合:

	3.多维空间的余弦函数的公式

9.计算余弦相似度例子一
	1.余弦相似度量:计算个体间的相似度。
		相似度越小,距离越大。相似度越大,距离越小。
	2.假设有3个物品,item1,item2和item3,用向量表示分别为:item1[1,1,0,0,1],item2[0,0,1,2,1],item3[0,0,1,2,0]。
	3.用欧式距离公式计算item1、itme2之间的距离,以及item2和item3之间的距离,分别是:

	4.用余弦函数计算item1和item2夹角间的余弦值为:

	5.用余弦函数计算item2和item3夹角间的余弦值为:

	6.由此可得出item1和item2相似度小,两个之间的距离大(距离为7),item2和itme3相似度大,两者之间的距离小(距离为1)。
	7.余弦相似度算法:
		一个向量空间中两个向量夹角间的余弦值作为衡量两个个体之间差异的大小,余弦值接近1,夹角趋于0,表明两个向量越相似;
	  	余弦值接近于0,夹角趋于90度,表明两个向量越不相似。
 
10.计算余弦相似度例子二
	1.使用余弦相似度计算两段文本的相似度。
		思路:1、对原文进行jieba中文分词;
		      2、set中去重后存储所有词,并使用dict字段存储set中元素值(key)和索引值(value);
		      3、中文分词进行编码:每个中文分词转换为出现在set中的索引值;
		      4、词频向量化:即计算每个分词出现的次数,通过oneHot编码对分词进行编码来计算词频,从而得到两个句子分别的词频向量;
		      5、根据余弦相似度的公式分别计算出两个句子的的相似度,即计算两个句子分别的词频向量之间夹角的余弦值,值越大相似度越高。
	2.原文句子A:这只皮靴号码大了。那只号码合适。
	  原文句子B:这只皮靴号码不小,那只更合适。
	3.分词
		使用jieba分词对上面两个原文句子分词后,分别得到两个列表:
		listA=['这', '只', '皮靴', '号码', '大', '了', '那', '只', '号码', '合适']
		listB=['这', '只', '皮靴', '号码', '不小', '那', '只','更合', '合适']
	3.列出所有词,将listA和listB放在一个set中,set中去重后得到:
		set={'不小', '了', '合适', '那', '只', '皮靴', '更合', '号码', '这', '大'}
	  将上述set转换为dict,key为set中的词,value为set中词出现的索引位置。
		dict1={'不小': 0, '了': 1, '合适': 2, '那': 3, '只': 4, '皮靴': 5, '更合': 6, '号码': 7, '这': 8, '大': 9},
	  可以看出“不小”这个词在set中排第1,下标为0。
	4.将listA和listB中的中文分词进行编码,将每个中文分词转换为出现在set中的索引值,转换后为:
		listAcode=[8, 4, 5, 7, 9, 1, 3, 4, 7, 2]
		listBcode=[8, 4, 5, 7, 0, 3, 4, 6, 2]
	  我们来分析listAcode,结合dict1,得知元素值8对应的字是“这”,元素值4对应的字是“只”,元素值9对应的字是“大”,就是句子A和句子B转换为用set中的索引值来表示。
	5.对listAcode和listBcode进行oneHot编码,就是计算每个分词出现的次数。
	  词频向量化:即计算每个分词出现的次数,通过oneHot编码对分词进行编码来计算词频,从而得到两个句子分别的词频向量。
	  oneHot编号后得到的结果如下:
		listAcodeOneHot = [0, 1, 1, 1, 2, 1, 0, 2, 1, 1]
		listBcodeOneHot = [1, 0, 1, 1, 2, 1, 1, 1, 1, 0]
 	6.根据余弦相似度的公式分别计算出两个句子的的相似度,即计算两个句子分别的词频向量之间夹角的余弦值,值越大相似度越高。

	7.流程图

11.实现余弦相似度的代码
                import jieba
                import math
                
                s1 = '这只皮靴号码大了。那只号码合适'
                s1_cut = [i for i in jieba.cut(s1, cut_all=True) if i != '']
                s2 = '这只皮靴号码不小,那只更合适'
                s2_cut = [i for i in jieba.cut(s2, cut_all=True) if i != '']
                print(s1_cut) #['这', '只', '皮靴', '号码', '大', '了', '那', '只', '号码', '合适']
                print(s2_cut)#['这', '只', '皮靴', '号码', '不小', '那', '只', '更合', '合适']
                
                #两个set分别对中文切割后的数组元素进行去重,然后两个set联合union放到一起组成一个新的set
                word_set = set(s1_cut).union(set(s2_cut))
                print(word_set)#{'只', '合适', '不小', '更合', '号码', '皮靴', '这', '了', '大', '那'}
                
                word_dict = dict()
                i = 0
                #构建dict字典中的key为set中的每个中文单词,value为set中的该中文单词的索引值
                for word in word_set:
                    word_dict[word] = i
                    i += 1
                print(word_dict) #{'只': 0, '合适': 1, '不小': 2, '更合': 3, '号码': 4, '皮靴': 5, '这': 6, '了': 7, '大': 8, '那': 9}
                
                #把原文中每个中文单词作为key对应的从dict字典中取出对应的value,值为set中的该中文单词的索引值
                s1_cut_code = [word_dict[word] for word in s1_cut]
                print(s1_cut_code) #[6, 0, 5, 4, 8, 7, 9, 0, 4, 1]
                
                #把原文中每个中文单词作为key对应的从dict字典中取出对应的value,值为set中的该中文单词的索引值
                s2_cut_code = [word_dict[word] for word in s2_cut]
                print(s2_cut_code) #[6, 0, 5, 4, 2, 9, 0, 3, 1]
                
                #重新创建一个新的和dict字典键值对数量相同的初始化值为0的数组
                s1_cut_code = [0]*len(word_dict)
                #oneHot编码:计算原文中每个中文分词出现的次数,即能得到原文句子的词频向量
                #根据原文中相同中文单词出现的重复次数记录到新数组中,出现次数要记录到的索引位置为该中文单词在set中索引值,或者说dict字典中的该中文单词作为key对应的value
                for word in s1_cut:
                    s1_cut_code[word_dict[word]]+=1
                print(s1_cut_code)#[2, 1, 0, 0, 2, 1, 1, 1, 1, 1]
                
                #重新创建一个新的和dict字典键值对数量相同的初始化值为0的数组
                s2_cut_code = [0]*len(word_dict)
                #oneHot编码:计算原文中每个中文分词出现的次数,即能得到原文句子的词频向量
                #根据原文中相同中文单词出现的重复次数记录到新数组中,出现次数要记录到的索引位置为该中文单词在set中索引值,或者说dict字典中的该中文单词作为key对应的value
                for word in s2_cut:
                    s2_cut_code[word_dict[word]]+=1
                print(s2_cut_code)#[2, 1, 1, 1, 1, 1, 1, 0, 0, 1]
                
                
                # 计算余弦相似度
                sum = 0
                sq1 = 0
                sq2 = 0
                #通过计算余弦值的公式cos(θ)= (x1*x2 + y1*y2) / sqrt(pow(x1,2)+pow(y1,2)) * sqrt(pow(x2,2)+pow(y2,2))
                #pow(x,y)函数用于求 x 的 y 次方
                for i in range(len(s1_cut_code)):
                    #计算(x1*x2 + y1*y2):x1*x2或y1*y2均代表词频向量s1_cut_code中的元素值 乘以 词频向量s2_cut_code中的元素值
                    sum += s1_cut_code[i] * s2_cut_code[i]
                    #计算pow(x1,2)+pow(y1,2):代表求词频向量s1_cut_code中的每个元素值的2次方
                    sq1 += pow(s1_cut_code[i], 2)
                    #计算pow(x2,2)+pow(y2,2):代表求词频向量s2_cut_code中的每个元素值的2次方
                    sq2 += pow(s2_cut_code[i], 2)
                
                try:
                    #sqrt() 计算平方根,即开平方
                    #round(浮点数x,四舍五入要保留的小数位) 方法返回浮点数x的四舍五入值。
                    #计算出余弦值的结果cos(θ)= (x1*x2 + y1*y2) / sqrt(pow(x1,2)+pow(y1,2)) * sqrt(pow(x2,2)+pow(y2,2))
                    result = round(float(sum) / (math.sqrt(sq1) * math.sqrt(sq2)), 2)
                except ZeroDivisionError:
                    result = 0.0
                print(result) #0.81


疲劳检测

疲劳驾驶在造成交通事故的危险因素中高居第三位,在死亡交通事故原因中占首位。
研究开发疲劳预警系统可以有效地减少疲劳驾驶所造成的交通事故,其中基于视频的疲劳驾驶
监测系统因其 实时性、非接触性 得到国内外的广泛关注。
使用基于Haar特征的Adaboost算法训练分类器, 实现嘴的正常状态和张嘴状态的区分,再针
对区分结果实现二次处理,计算嘴的张开程度,判定是否处于疲劳状态。
处理方法:
选取 打哈欠及各种张嘴时的嘴部图像为正样本 ,选取 脸部其他部分图像为负样本 ,正样本和负
样本图像均从网上搜索得到, 正样本图像250张 ,大小统一缩放为 24×24,负样本图像550张
由于嘴位于人脸的下半部分,所以搜索区域可缩小至人脸下半部分,节省搜索时间。
检测完成得到打哈欠(或张嘴)的图片,则再用局部搜索确定二值化分割阈值,把嘴的轮廓分割出来。
为判断嘴的张开程度,我们同样在轮廓上取最具代表性的4个点: 上下边缘点和左右边缘点, 通过计
算轮廓的外接矩形的宽高比来判定是否为打哈欠状态,一般认为当宽高比大于1.5的时候处于疲劳状态。
数量:正样本250张,负样本550张。
正样本:
负样本:
驾驶员疲劳驾驶监测系统软件设计过程中主要包含以下几个方 面的内容:
(1) 双眼位置的精确定位:
在得到包含驾驶员人脸的图像数据之后,应设计算法定位图像 中的人眼。这个过程中需要解决的问题有:
①如何使驾驶员的双眼特征强于图像中其他内容;
②如何区别图像中可能存在的类似人眼的物体与真实人眼;
③如何 减小计算量,保证算法效率
一般情况下,是在定位人眼之前先检测并定位出人脸,然后在 人脸区域范围内进行人眼的检测和定位,这样可以有效减少算
法在图像内搜素的面积,且可排除图像内的类人眼物体。
(2) 双眼位置的跟踪
在完成初始的人眼定位后,如果仍按照常规途径实现每一帧图 像的人眼位置精确定位,那么整个过程的计算量和计算复杂度
都较高,这样无法满足系统实时性要求;而且虽然大多数情况 下采集设备能够得到包含人眼的图像,但是, 也不排除因为故
障、驾驶员头部姿态等因素导致无法按初始时的人眼定位算法 实现双眼的检测和定位
因此,为了保证算法的效率和准确性,应当在完成双眼位置的 定位之后随即采用跟踪算法来完成对人眼的后续定位和跟踪,
这样既能节省时间,还可以保证较复杂环境下人眼位置的准确 定位。
(3) 双眼状态信息的分析
一旦实现驾驶员双眼的准确跟踪,就能够获得每一时刻驾 驶员 双眼的位置、张开幅度等信息 ,通过这些信息的统计
及分析,结合多个具有代表性的疲劳状态评价指标建立疲 劳状态评价模型,就能实现任意时刻对驾驶员精神状态的
判断。
眼睛纵横比(eye aspect ratio (EAR)):
通过计算这个EAR的数值,我们可以判断眼 睛是张开还是闭合,从而检测眨眼动作,进 一步使用若干帧中检测到的EAR组成一个特 征向量,送到一个SVM中来进行分类。
分子中计算的是眼睛的特征点在垂直方向上 的距离,分母计算的是眼睛的特征点在水平 方向上的距离。由于水平点只有一组,而垂 直点有两组,所以分母乘上了2,以保证两组 特征点的权重相同。
定义参数
EYE_AR_THRESH = 0.3 # EAR阈值
# 当EAR小于阈值时,接连多少帧一定发生眨眼动作
EYE_AR_CONSEC_FRAMES = 3
# 对应特征点的序号
RIGHT_EYE_START = 37 - 1
RIGHT_EYE_END = 42 - 1
LEFT_EYE_START = 43 - 1
LEFT_EYE_END = 48 - 1
EYE_AR_THRESH是判断阈值,默认为0.3。如果EAR大于它,则认为眼睛是睁开的;如果EAR小于它,则认
为眼睛是闭上的。
EYE_AR_CONSEC_FRAMES表示的是,当EAR小于阈值时,连续的帧数。只有小于阈值的帧数超过了这个值
时,才认为当前眼睛是闭合的,即发生了疲劳闭眼的操作;否则则认为是误操作。
RIGHT_EYE_START、RIGHT_EYE_END、LEFT_EYE_START、LEFT_EYE_END:这几个都对应了人脸特征点
中对应眼睛的那几个特征点的序号。由于list中默认从0开始,为保持一致,所以减1。
下面的眨眼和张嘴,摇头和点头,都是通 过连续的图片,计算变化距离与不变距离 的比,进行上一帧图像与下一帧图像的比, 来判断是否为活体。 这里用的是dlib,速度很快,可以做到实 施检测,每秒20帧是可以做到的。
介绍张嘴检测,见下图。 模仿眨眼,取点的时候需要注意, 内圈的点变化更加明显。变化距离 建议取内圈,固定距离选用外圈。
摇头和点头的方法和上面的想法几乎一样。
首先说摇头,当摇头的时候,你的脸颊会变窄,而你的鼻子长度几乎是不变的。
点头的时候,鼻子的距离会变短,而脸颊的会几乎不会变窄。
在驾驶过程中,由于实际情况相对复杂,单一的摄像头 必然存在一些问题:
(1)对人脸转向比较敏感,当人脸发生偏转时由于拍 摄角度的原因,使得采集到的人脸或人眼发生变形,易 导致检测和定位失败。
(2)在倒车时,驾驶员可能会转头观察车后的情况, 此时易超出单一摄像头的采集范围。

人脸校准

LFW 人脸数据库 (LFW face database)是在各类人脸检测和识别算法的研究中经常用到的数据集。而且在各类与
人脸有关的各类赛事上也经常用到。图像的来源主要是网络,涉及人物不分种族,性别和年龄。是一种完全真实
自然的状态下的人脸图像。该数据集被用于非限制性环境下的算法实验。
数据集共有 13000 多张人脸照片,其中有的人只有一张,而有的人拥有两张以及两张以上图像。此类共有1680
人。每张图像已经被标好了唯一的标签。LFW 数据集中的图像里的光照,姿态和遮挡情况各不相同,因此是一
个具有挑战性的人脸数据集。
Yale 人脸数据库 是用作限制性环境下测试人脸识别 算法的经典数据集。 该数据集由 15 位志愿者组成,每位志愿者的具有表
情,姿态和光照因素的人脸图像共11 张。整个数据 集有 1653 张图片。每张图片大小为 100*100。整 个数据集非常小,图片信息也较为简单。Yale 人脸 数据的扩展数据集 The extended Yale Face DatabaseB 共有 16178 张图像。由 28 位志愿者组
成,每位志愿者共有 9 中姿态,另外有 64种不同光 照。该数据集包括人脸检测对齐好的数据集和未对齐 的数据集。
Yale 人脸数据库 是用作限制性环境下测试人脸识别 算法的经典数据集。 数据集中人脸数据已经标定,因此这并不是传统意义
上的人脸识别任务,而是一个简单的图像多分类问题。 另外,每个人包含了在不同表情、光照下的人脸图像, 这就要求我们提取的图像特征要具有光照不敏感性, 能够很好得体现人脸的轮廓信息。
FERET 数据库 同样是用来检测限制性环境 下人脸相关算法的数据集。 由 200多位志愿者提供的不同姿态和表情
的图像组成。该数据集最开始由 Counterdrug Technology Transfer Program (CTTP)发起了一个人脸识别技
术(Face Recognition Technology 简称 FERET)工程创建的,现在已经成为人脸 识别领域应用最多的人脸数据集。部分数
据如图所示。
在做人脸识别的时候,前期的数据处理过程中可能会遇到一个问题, 即将人脸从不同尺寸的图像中截取
出来,再进行“对齐”操作 。这样可以使每一个截取的人脸中的眼睛等位置处于同一位置,会对后面的
识别算法起到一定的优化作用。
比如,下面 3 张图片所示,人脸的位置,图像的大小各不一样。我们所需要做的就是,将人脸从各个图
片中截取出来,再旋转进行校正,使得眼睛在同一位置上,最后将图片的大小也统一调整为 224 x 224。
校正的效果如下图所示:
1. 计算直线距离及倾斜角度
计算两眼之间的距离很简单,只要找出两个向量的坐标就可以计算出来。计算两眼之间的距离 直线的倾斜角度,就是将两个向量相减,得到一组横纵坐标,然后利用 arctan 的公式求解角 度,公式如下所示:
整个过程遵循以下几个步骤:
1,提取出每张图片里眼睛的坐标,进行读取数据
2,找两眼间的直线距离并计算该直线与水平线之间的夹角,即倾斜角度
3,根据找到的倾斜角度旋转图片
4,在旋转后的图片中找到眼睛的位置
5,根据眼睛坐标找到包含面部的框的宽度和高度,调整图片的尺寸
在不受限环境中,人脸识别仍不能万无一失,尤其是在人脸几何变形的情况下。人脸在角度和对齐方面(由
人脸检测边界框定位引入)的变形将从根本上影响人脸的表征和识别的性能。
有鉴于此,旷视科技创新性地提出 GridFace,通过学习局部单应变换减少人脸变形,提升识别性能。一句
话,先校正,再识别,过硬的校正技术使得人脸识别性能得到显著提高。
不规则形状分割人脸:
先用dlib等打点工具把人脸最外层的landmark点打出来,然后 利用opencv的convexhull得到凸包然后就可以抠出人脸区域了,
这里使用两种方式来得到人脸区域:
1,将mask作为通道,来控制图片区域的透明度,最后得到图 片是4通道的;
2,掩模与原始图像进行与运算,返回图像是三通道。 最后完成人脸检测-->人脸关键点检测-->人脸分割-->人脸对齐
ASM 主动外观模型
人脸识别大体分为三部分:
从复杂背景中检测人脸的位置;对人脸信息进行提取;最后进行匹配和识别。
人脸识别的关键步骤就是抽取人脸特征 ,而基于的人脸特征提取算法通过对人脸特征点的标定能够准确定
位人脸主要信息,如眼睛、眉毛、鼻子、嘴巴等,因此AAM特征定位与匹配在人脸识别领域有其一席之 地.
AAM(Active Shape Model)的理论基础是形变模型 ,只考虑局部形状信息的弊端影响了算法的精度,从
而ASM将纹理信息通过统计模型的方式引入人脸特征点定位,因此使模型携带了更多的人脸信息,并且
面部特征点的定位变得更加准确。
ASM主动形状模型 方法,是一种物体形状描述技术,用于解决图像中的目标搜寻的形状统计模型,具有一
般性,可以为任何物体形状建立模型。被广泛应用于医学图像处理、数字图像理解和计算机视觉等领域。
主动形状模型是一个基于统计学的模型,以目标物体的轮廓作为训练样本构造的方法,通过模型的形变最
终与目标形状相符合,最初由曼彻斯特大学提出。
1,使用人机交互方式提取目标轮廓的边界 点集合,统计样本的模型
2,根据训练样本建立局部灰度模型
3,进行主成分分析,获得模型变化的模式
4,检测图像的形状模型以达到最佳匹配。
算法主要分为两个阶段:
模型训练阶段 图像搜索阶段 人脸图像之所以可能有不同的变化,是由于个性特征、 面部表情、光线条件以及三维姿态等原因造成的。为 了使能够定位人脸特征,我们必须首先建立能够描述人 脸形状及典型变化的模型。
从一组训练样本集建立一个模型
1,选取需要的特征点
我们需要已经标定好的相关数据作为训练样本,取N幅人脸图片作为训练样本集,手工标定每幅训练图片上的
n个特征点,并且标记的顺序要保持一致,各个特征部位的特征点的个数也必须相同,然后将每幅图像对应的n
个特征点的坐标按序放在一个形状向量里。
这样,所获得的所有的形状向量就组成了的观察数据,第i个训练样本可表示为一个大的列向量:
在二维空间里的标记点最好是选择物体边界的 拐角点,在边界的“T”型交叉点或容易定位的 生物的标记点(红色的点是边界的拐角点,蓝色 的点是“T”型交叉点,黑色的是一些分布较均 匀的描述形状信息的点)如眼睛的中央及嘴巴 的拐角点。
然而这些点通常不够用于人脸的精确描述。因 此,我们一般还需使用定义好的标记点间的中间 点。
具体的对特征点的选取,我们可以按照如下原则进行:
1,关键特征点,即可以用肉眼直接分辨出的特征点,如面 部上的眼睛、鼻子的边界点、嘴唇的形状以及中心等
2,在关键特征点之间尽量均匀分布的一些特征点
3,特征点分布的密度要适当。密度过大将增加标定的 工作量以及算法的效率问题,而密度过小将不能达到理想 的效果。
要想建立表现比较完备的平均人脸模型,标定的训练集应该包括不同变化类型的图像。 对于人的面部图像来讲,应该包括不同性别、不同表情、不同姿态姿态角度变化不要大于15度。
2,计算权重W
在选取的特征点中,每个点的变化情况是不一样的。有一些点相对其它的点来说变化剧烈,而有一些
点的变化就相对稳定。例如人脸样本中的眼眉和鼻子的位置相对来说比较稳定,但是嘴巴和眼睛的
点就相对不稳定,虹膜两侧比较稳定但是上下因为有遮挡点所以变化相对比较剧烈。
这里拿一个训练样本Xi的特征点来说:
1,计算这个样本的每个特征点和其它点的距离,例如,第k个点和第l个点的距离Rkl,比方说68个特征点就 应该有68*68个距离组合(包括每个点到它自身的距离组合)。
2,接下来计算每个距离的方差(标准偏差的平方)标准偏差函数如式一所示:
3,对样本中的每一个点如第个点求的和再取倒数,即为该点的权重,其中n是每个样本中所选取的 特征点的数目
3,配准
对于标记好的图像,由于其在图像中所处的绝对位置有 差异,图像尺寸、方向上也存在不同,因此直接对这些 标记好的点进行统计建模是不合理的,也不能够反应它 们形变的规律。 因此为了克服上述因素的影响 ,为了能够比较来自不同 形状相对应的点,从而建立起能够真正反映出形状变化 规律的统计形状模型,我们需要将训练样本集中的形状 进行配准或称为对齐 。所谓的配准,就是以某个形状为 基础,对它形状进行尺度变化、旋转和平移使得它们与 基形状尽可能接近,这些变换必须在不改变训练样本的 整个刚体形状的前提下进行。
3,配准
之后,对计算得到的配准后的平均形状向量作归一化处 理,并以归一化的平均形状向量作为样本,再将其它经上 一步对齐的形状向量与此平均形状向量对齐。重复这 一过程,知道相邻两次的平均形状向量差别小于某一个 特定值。这种样本对齐的理论和方法非常关键,它影响 了算法的精确程度,贯穿了整个算法。
样本配准的方法:
1,旋转、尺度变化和平移,使得每个样本的特征点到一个选定样本例如,选定的样本为戈的特征
点,重复下面的步骤直到收敛
2,计算总的样本特征点的平均形状
3,把平均形状和选定的形状对齐(相当于归一化)
4,旋转、尺度变化和平移每个样本的特征点到平均形状
5,收敛检验
4,主成分分析
把训练集中的形状向量进行配准处理之后,就可以利用主成分分析的方法来找出形状变化的统计信息及规律,同时实
现在变换域中的表示。
主成分分析PCA是一种简化数据集的技术,它是一个线性变换,在特征处理、模式识别、数据分析等领域已经得到了
广泛的应用。
这个变换把数据变换到一个新的坐标系统中,使得任何数据投影的第一大方差在第一个坐标称为第一主成分上,第二
大方差在第二个坐标第二主成分上,依次类推,减少数据集的维数,减少数据冗余,使得数据在一个低维的特征空间被处
理,同时保持数据集的对方差贡献最大的特征,也就保持了原始数据的绝大部分的信息,从而解决数据空间维数过高的
瓶颈问题。
对同一个体进行多项观察时,必定涉及多个随机变量,它们都是具有相关性的。这时就需要借助主成分分析来概括诸
多信息的主要方面。我们希望有一个或几个较好的综合指标来概括信息,而且希望综合指标能够互相独立地各自代
表某一方面的性质。
任何一个度量指标的好坏除了可靠、真实之外,还 必须能充分反映个体间的变异。如果有一项指标, 不同个体的取值都大同小异,那么该指标不能用来 区分不同的个体。由这一点来看,一项指标在个体 间的变异越大越好。因此我们把“变异大”作为 “好”标准来寻求综合指标。 对于一个训练集,每一幅人脸图像都有乡潍的特征, 这样的庞大的计算量势必会影响到利用算法进行 搜索的速度。为了提高算法的效率,我们就需要在 训练阶段进行主成分分析。
一个二维分布的向量可以看成形状,通过变换,我们可以找到其分布轴,并求得其均值。如果我们现在 只取一个特征向量作为主轴,就可以用这个主轴来近似原来的向量。 经过主成分分析,取前t个特征值对应的特征向量,其选取依据为使前个t个特征值满足:
5,取相应的特征向量
把选定λ相对应的特征向量p组成一个矩阵P。 这样任一人脸形状X的变化子空间中可表示为
其中b是一个t*I的向量,通过改变就可以生成新的形状,b中每个值对应了在相应向量方向上的 变化程度。
下图即不同的b对应不同的人脸形状。
而b向量中的每一个分量都具有相对的独立性,即其中一个分量的变化不受其它分量变化的影 响。这样一来,任何一个变量都有可能在一个相当大的范围内变化,这样就不能保证配准后得 到的新形状是我们想要的人脸形状,因此,需要对这些参数bi的变化范围进行约束。
接下来,我们要通过训练样本建立起所标记的个点的局部灰度模型。 点的局部灰度模型就是在一个点附近的一定范围内的灰度分布情况,我们在 对训练样本的图像进行手工标定后,就可以建立这样的模型。 之所以要建立局部灰度模型,是因为初始形状不一定能够和真实的形状相一 致,需要不断调整其形状,使得它与真实的形状相吻合。为了调整形状,就需要 对训练图像特征点附近的灰度信息进行分析。因此,我们还需要提取特征点 周围的局部灰度信息,并计算其经验灰度分布。
采用三种常用的搜索策略:
基于像素邻域最大梯度值的搜索策略计算图像梯度场,在当前形状模型 的每个标记点的一个夕的邻域内,取梯度值最大的点作为当前标记点的替 代点。 采用这种搜索策略,算法速度快,效率也比较高,大体上可以定位出轮廓的, 但是标记点的变化没有方向性,容易受到其它边缘的干扰,比如衣领、耳朵 等。
基于轮廓曲线法方向的搜索策略沿当前形状曲线的法线所在直线上,以 标记点为中心在顺着法线方向和逆着法线方向各取无个像素,选择的梯 度值最大的点为当前标记点的替代点。针对不同的器官,选择一些关键 的标记点,只调整这些关键标记点,而其它标记点则根据模型的形状配准 算法来求出。
在传统的算法中建立并使用了两种模型全局形状模型和特征点的局部灰度模型。用特征点集合去描 述人脸面部的各个显著特征区域形状,采用全局特征子空间去约束迭代过程中每个特征点的定位结 果。每个特征点周围用局部灰度模型进行描述,在目标图像搜索过程中来寻找每个特征点移动的目 标点。 在拥有合理和足够的训练样本所组成的训练集的条件下,可以较好地学习样本集类物体的形状及变 化模式,对目标物体进行定位搜索 和解释。但是容易陷入局部极值而且对于纹理匹配的误差较大。
1,虽然传统ASM在很多情况下能得到较好的 收敛效果,但对平均形状模型的初始化依赖性 较强,当初始形状在接近目标形状的大致位置 时,算法效果较好,但当初始轮廓距离真实人 脸轮廓距离较远时,易出现局部最优,无法搜 索到正确位置。
2,特征点周围的纹理特征仅用灰度梯度信息描述容易受到光照和噪声的影响,若测试集与训练集光照条件
相差较大,会导致建立的灰度模型不能正确的指导特征点运动,使算法不收敛,特征点定位不理想甚至失败
3,进行平均形状建模时,对于但姿态人脸建模很有效,如左图所示,但是,若存在各种姿态,比如图像中
人脸出现左侧,右侧,抬头,低头等情况,建立模型可能出现右图的情况;
4,在特征点搜索过程中,只在法线方向进行了搜索,如果最佳位置不在法线方向上,就会导致模型不能很好地逼
近目标形状。
5,由于主动形状模型的方法是基于几何形状信息的方法,本身对图片的质量有着比较高的要求,对噪声和局部
变形比较敏感。

AAM与ASM
A SM 和主动外观模型AAM( A ctive A ppearance M odel) 都能在人脸特征 定位中取得良好的结果 :
ASM 利 用 特 征 点 附 近 的 局 部 灰 度 信 息 ; AAM 则 利 用 脸 部patch 区域的纹理信息 。 由于人脸图像中的原始数据就包含形状向量信息,所以人脸的形状特征可以 直接通过标记面部的关键特征点来获取,但是图像的纹理特征并没有显性的 表示在人脸的图像中,这里我们需要通过德劳内(Delaunay)三角变换和仿 射(Affine)变换来获取。
通过德劳内(Delaunay)三角变换和仿射(Affine)变换后建立的人脸的纹 理模型可以有效的消除样本尺寸、方向角度以及绝对位置的差异,达到了归 一化的效果。
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐