让学生了解数字图像处理在人工智能方面的应用。通过一个人脸识别系统的开发,提高学生综合运用面向对象程序设计思想、数字图像处理等专业知识和技术的能力;通过了解人脸识别系统开发方法与步骤,为以后从事图像分析与图像理解等人工智能方面的相关工作奠定基础。
1. 技术介绍
cv2: 在本项目中主要用于:
a. 图像的读取
b. 图像大小的改变
c. 人脸的检测(非识别,检测的目的在于裁剪图片,从而减少周围环境的影响)
d. 存储图片(写明存储的相对/绝对路径)
f. 通过窗体展示图片(可为窗体赋以名称)
Tips: 在安装的时候是安装的opencv,但是在导入(import)库的时候是导入cv2。
numpy:
a. Python 语言的一个扩展程序库,常被简写为np,支持大量的维度数组与矩阵运算(在线性代数中会学习数组与矩阵及其简单运算),此外也针对数组运算提供大量的数学函数库。
b. 简而言之,只需要调用一个方法即可完成数组和矩阵的相关运算(在本项目中需要了解矩阵之间的相加减、矩阵之间的乘法、以及矩阵与常数的乘除法)一张图像我们可以采用二维数组的形式进行存储(数字化),二维数组可以用矩阵的形式表示。因此对图像的操作我们可以转为对矩阵的运算,而numpy库恰好为我们提供了丰富的api(接口)帮我们完成运算的工作。
Tkinter: 图形开发界面库(python常用的GUI库)
2. 开发工具:
Pycharm: python语言开发IDE(学生可通过学校邮箱申请免费使用)
3. 开发环境
Python: 目前来说任何版本都可以,不过建议使用python3进行编写。
--------(当你有了以上的基础知识之后就可以开始学习人脸识别了)--------
首先,我们先了解一下什么叫人脸识别:
通过上述文字,我们可以注意到三点:
(1)我们的数据必须是包含人脸的图像或者视频流(保证数据的有效性)
(2)第一步要检测和跟踪到图像或视频流中的人脸
(3)第二步才是对人脸进行面部识别
由于我们提供的所有数据集均包含了人脸,因此我们仅需完成第二和第三小点的工作。
使用python+OpenCV实现人脸识别系统,要求系统带有界面和菜单。人脸识别系统实现步骤如下:
1、搭建python编译环境,并预导入OpenCV库
(OpenCV是什么,有什么用已经在第二大部分有详细解释)
2、创建正面人脸图像采集和识别程序,该程序分为人脸图像采集和人脸图像识别2个部
(即在“什么是人脸识别”中我们提到的需要同学们完成的两个工作)
注意,该系统所用图像数据均为灰度图像(什么是灰度图像?为什么要使用灰度图像?针对该问题,同学们可以自行思考,然后百度查阅)。
3、人脸图像采集步骤,每个人至少采集5-7张正面图像
(图像数据均会提供,此处同学们可以思考一下5-7张图片,是否是硬性要求?增加和减少采集数量是否会对我们机器的学习产生影响):
方法一:
(1) 打开人脸图像并显示在界面上。
(2) 用鼠标选取人脸各个眼睛中心位置。
(3) 依据人眼坐标,用下图采集人脸区域,并对人脸区域进行几何归一化。
几何归一化的目的是使用图像缩放技术把原始人脸区域图像缩放到统一的像素大小。比如规定图像大小是64×64,则按照上图人脸区域缩放倍数为β=2d/128。这样通过固定人眼坐标,保证了其他部位如鼻、嘴、脸颊等位置都保持在相对标准的位置,体现人脸在图像平面内的尺度不变性。
上面说了一大堆,简而言之把所有图像标记好的人脸区域都改变成大小统一的图像,然后进行保存。该项目中,我建议大家统一保存为128×128的大小。
(4) 对归一化后的人脸区域图像进行灰度归一化。
灰度归一化的目的是去除一定条件下的光照影响,灰度归一化的方法很多,这里可以使用比较典型的直方图均衡化方法。
(5)对每个采集的人脸图像按人脸类别进行编号形成人脸库。
方法二:
使用我们之前提到的cv2,通过cv.CascadeClassifier()接口,帮助实现人脸的自动检测。这是opencv自带的模型,是官方已经训练好的模型。值得注意的是该方法的参数为字符串类型,指向了模型的存储路径,本项目中我建议将路径指向haarcascade_frontalface_alt.xml。
上述方法会为我们返回一个分类器,有了分类器就可以进行人脸检测了。通过分类器我们可以调用detectMultiScale使其给我们返回检测的结果。
具体的使用方法可以参考以下博客,博客中已经解释的非常的清楚了,这里不过多阐述
https://www.cnblogs.com/lyx2018/p/7073025.html
------------------(至此所有的人脸采集工作就结束了)------------------
Ps:接下来的工作是让计算机把我们采集的所有人的人脸都认识一遍,但是每个人的照片只看一张可能印象不是很深,效果不是很好,因此我们可以每个人的照片都给计算机看个5-7张。
如果直接给计算机看不同人的图片,它可能会分不清,因为计算机跟人不一样,人可以一眼看出谁是谁,计算机不行。因此我们需要将所有人的图片放在另一个空间里,这个空间是一个可以让计算机很好的分辨不同人的空间,也就是说我们要构建一个特征脸空间
特征脸空间是什么?为什么构造这个空间可以让计算机分清楚不同人?可以参考下面的博客:https://blog.csdn.net/zouxy09/article/details/45276053 只需要看概念,不需要看matlab的实现,因为我们是用python实现的。
简而言之,特征脸就是一张像所有人的脸,一张很综合的脸。在数值上只要稍作计算,就可以与某个人的脸的数值一模一样。举个例子,现在有数字4(基粒a)和6(基粒b),我们把单个数字成为“基粒”,如果要说这基粒a,b有什么区别,你可能说不出来。但是你稍微转换一下4变成2 * 2,6变成2 * 3,现在把2作为基粒c。这样子的话我们就可以很清楚的看到,在基粒c的基础上,4和6的区别,那就是4是2个基粒c,6是3个基粒c。有一种找出所有数据的公因数的感觉。然后这个公因数通过倍数的变化可以变成数据集中任意一个数据。那么我们可以利用不同数据与公因数计算的结果作为不同数据之间的差异,通过这个差异让计算机区分开不同的人。
所以下面让我们开始用PCA方法计算出这个特征脸空间,即我们比喻的公因数。由于涉及到线性代数,同学们直接套公式计算即可,不过能理解其中含义是最好的。
下文提到的矩阵均可理解为二维数组
4、创建训练人脸库的特征脸空间——主成分分析(PCA)方法
(1)创建所有训练样本组成的M×N矩阵trainFaceMat。其中,M为样本个数,N为一个训练样本图像所有像素按列相连的像素值
(M = 人数[我们提供的数据包含了30个人的人脸数据] * count [5-7] ,也就是说我们有30个人的数据,每个人选出5-7张图片作为训练的数据。现在确定一下数据,假设我们每个人选取7张照片,那么M就等于30 * 7,代表了我们需要用210张图片作为训练数据)(我们让计算机认识每一个人,而每一个人我们给计算机看7张照片,这样可以加深计算机对每个人的印象,那么除了大家选取出来的7张照片以外,每个人脸数据都还有多余的照片没有使用,那么这些没让计算机看过的照片,就可以在计算机认识了每一个人之后用来考验计算机是否真的已经能够正确识别图像中的人是谁)
(N = 像素点的个数,比如一张图片的大小是60 * 50, 那么N就等于300, 一个N代表这一张人脸的数据)
(2)计算训练样本的平均值矩阵meanFaceMat,该矩阵为1×N矩阵
(1 * N就代表目前算出来的平均值矩阵是 一张 人脸的数据,这张平均脸其实就是把上面的M * N的矩阵平均化,把这M张照片的每一个位置对应的数值加起来,然后再除以M。可以理解为把一个M * N的二维数组的每一列都加起来,变成一个1 * N的数组,然后这个数组的每一位都除以M,最后得到一个1 * N的平均值脸)(平均脸的作用就是为了找到一张跟所有人都很像的脸,这个地方就有点找公因数的感觉了)
(3)计算规格化后的训练样本矩阵normTrainFaceMat,矩阵大小为M×N。计算公式为:
normTrainFaceMat(i,:)= trainFaceMat(i,:)-meanFaceMat
(这一步的公式包含了python的一点语言,这里的意思是用我们一开始得到的M*N的矩阵减去我们的平均脸,即计算出我每个人的数据跟这个平均脸的差异,我们把计算结果成为差值矩阵)
(4)计算normTrainFaceMat的协方差矩阵的特征值和特征向量。python中的计算公式为:
covariance = np.cov(normTrainFaceMat) #根据差值矩阵计算得出协方差矩阵
eigenvalue, featurevector = np.linalg.eig(covariance) #求得特征值和特征向量
其中,矩阵featurevector 的列向量为特征向量,eigenvalue为特征值组成的对角阵。
没有什么算法,大家直接调用numpy提供的函数就可以得到计算结果。
(5)获取特征值按降序排序对应原矩阵的下标,python中的计算公式为:
sorted_Index = np.argsort(eigenvalue)
其中,sorted_Index为排序后的特征值在原序列中的索引,这个只是个下标,并不是具体排序后的特征值。
(6)由于特征向量与特征值是相互对应的,我们可以直接根据刚刚得到的特征值排序后对应原序列中的索引来得到排序后的特征向量,公式如下:
topk_evecs = featurevector[:, sorted_Index[:-k - 1:-1]]
这是一个由前k个特征向量组成的矩阵,一个特征向量可以理解为一个特征,我们取最具有代表性的k个特征来构成我们的特征空间。选取前k个特征向量的过程我们叫做降维,降维的目的是为了抛弃那些对我们计算影响不大的值,减少计算量,只选前k个而不选排名靠后的原因就是因为后面的数据对我们的影响不大,可以抛弃。K可以取150左右。
(7)获得训练样本的特征脸空间:
eigenface = normTrainFaceMat' * eig_vec
注意这个地方的normTrainFaceMat是转置了的,它的右上角有个撇,在numpy中要使用transpose()方法,对其转置,而“ * ”号代表点乘,在numpy中使用dot()函数计算
得到的矩阵eigenface大小为N×m,每一列是一个长度为N的特征脸,共m列。
----------------------------(到这一步,我们就得到了所谓的公因数)--------------------------
(8)训练样本在特征脸空间的投影:
eigen_train_sample = np.dot(normTrainFaceMat, eigenface);
上面解释过dot函数是什么,它就代表了两个矩阵之间的点乘 —(到目前为止,我们计算出了特征脸空间,也计算出了训练数据在特征空间中的投影)— Ps:接下来,我们只需要把要识别的图片,也计算出它的差值矩阵,并且把差值矩阵在特征脸空间上的投影计算出来,这个投影即代表了我们之前提到的公因数的倍数。我们可以把待识别图片的投影与训练数据中每个人的图片的投影通过“距离公式”计算出“距离”,通过观察待识别图片与训练数据中哪个人的投影距离最近,那么待识别图片上的人就是谁。比如我们有编号1-30的训练数据的投影,我们的待识别图片为A,我们计算出A在特征脸空间上的投影与编号1-30的每一个投影的的距离,假设现在计算出A与1号的距离最近,那么我们就可以认为A就是1号。当然这里还存在一个A可能不属于1-30号中的任何一个人,但是这种情况我们在这里不做讨论,大家可以自行思考一下。 5、人脸识别 将normTestFaceMat向特征脸空间投影: 得到eigen_test_sample为待识别人脸的投影样本矩阵,大小为1×m。 ending: 写的不好的地方还请大家多多包含,文中比喻不恰当的地方也麻烦大家指出来,以后会考虑上传数据和代码的
得到eigen_train_sample为投影样本矩阵,大小为M×m,每一行为一个训练样本图像在特征脸空间的投影。由于m<
(1)测试人脸归一化。
读入待识别人脸图像,按照人脸采集步骤选取人脸双眼,然后对测试人脸进行几何归一化和灰度归一化,形成与训练样本相同尺寸的人脸图像区域。
这个地方仍然可以使用方法一或者方法二
(2)向特征脸空间进行投影。
将测试人脸展开为1×N矩阵testFaceMat,N为一个图像所有像素按列相连的像素值。计算规格化后的识别样本矩阵normTestFaceMat:normTestFaceMat = normTestFaceMat –meanFaceMat
eigen_test_sample = normTestFaceMat * eigenface
(3)计算eigen_test_sample与eigen_train_sample中各样本的距离
计算eigen_test_sample与eigen_train_sample中各样本(每行)的欧式距离,距离最小的那个样本则可以认为与待识别样本为同一人。
在numpy中同样也提供了计算欧式距离的方法,大家可以百度一下,学习如何使用numpy计算出欧氏距离,当然大家可以不用numpy实现的方法,可以自己写一段计算欧氏距离的代码。