开发者学堂课程【计算机视觉入门及案例实战:人脸识别的案例实现】学习笔记,与课程紧密连接,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/744/detail/13173
人脸识别的案例实现
内容介绍
一、人脸识别的前期准备
二、实现
一、人脸识别的前期准备
人脸识别的场景及需求就是识别许多人的脸部,并定位到目标人物。以下画面是智能程序识别到了许多人的脸部:
以下画面是智能程序锁定了要找的目标人物:
1.需求
(1)画面从哪里来?
打开摄像头,不停的获取拍摄到的画面。画面是从摄像头之中获取到的,需要不停地读摄像头拍摄到的画面。
(2)图一绿色框框框在什么地方,其中绿色文字是什么,从哪里来?
绿色的框在人的脸部,绿色的文字就是人对应的姓名。在数据库中会存储一个人的名字及其面部特征,面部特征和拍摄到的人的面部特征匹配上了,就可以把对应的人的名字打印在绿框上面。
(3)当识别到目标人物的时候,发生什么变化?
当识别到目标人物时,绿色框变成了红色。
2.准备
通过 PIP 安装第三方包。由于 Face recognition 依赖于前3个包,所以需要按照如下步骤依次安装第三方包:
pip install cmake
pip insta1l scikit-image
pip install dlib
pip install face_recognition
如果出现安装缓慢的情况,可以通过国内镜像来下载。
如:
pip install face_ recognition -i
https: //pypi . douban. com/simple
在安装的过程中出现提示符卡顿,需要耐心等待。
二、实现
将人脸识别案例分为4个步骤如下:
定位到人的脸部,并用绿色的框框把人的脸部框住
读取到数据库中的人名和面部特征
匹配拍摄到人的脸部特征和数据库中的面部特征,并用用户姓名标识
定位和锁定目标人物,改使用红色的框框把目标人物的脸部框住
1.定位
例如在乘坐高铁的时候:
首先会选择闸道进站,在闸道的终端会有一个机器,机器上有摄像头会拍摄到上半身,主要提取面部特征的信息。在机器上放上身份证时,会在公安的数据库中读取存储起来的面部特征信息,之后将查出来的面部特征与拍摄到的面部特征做匹配,如果完全匹配,就会放行,如果查询到的面部特征和拍摄到的面部特征不匹配,就不能够放行。
实现人脸识别第一步:打开摄像头,读取摄像头拍摄到的画面,定位到画面中的人的脸部,并用绿色的框把人的脸部框住。
此时使用 Spyder 编写程序。新建 Python 的模块文件,后置名改为 py,先导入要用到的库包:
import face_recognition
import cv2
需求是打开摄像头,读取摄像头所拍摄到的画面,定位到画面中人的脸部,并用绿色的框把人的脸部给框住;
分为以下三步:
第一步打开摄像头,获取摄像头对象;
第二步循环不停的获取画面,并做进一步处理;
第三步退出程序时,需要释放摄像机以及其他资源。
通过以下代码,打开电脑上的摄像头:
video_capture = cv2.VideoCapture(0)
电脑上可能会有多个摄像头,此时需要指定打开的几个摄像头,所以需要从零开始。
第二步 ,循环不停的获取摄像头拍摄的画面,并做进一步的处理:
While true:
通过while实现循环。
第三步,在退出程序时,释放摄像头及其他资源:
Video_capture.release()
第二步是核心步骤,在循环中编写代码。其中主要做五件事情:
2.1获取摄像头拍摄到的画面;
2.2从拍摄到的画面中,提取出人的脸部区域,一个摄像头可以拍摄到多个人,所以可能会有多个;
2.3循环遍历人的所在区域,并画框;
2.4通过 openCV,把结果展示出来;
2.5设定退出的机制。
通过以下代码获取摄像头拍摄到的画面:
Ret.frame = video capture.read()
该方法会返回两个参数,所以需要用两个参数获取。第一个参数是指该方法是否返回画面,第二个参数是指画面本身。不去考虑获取不到画面的情况。
通过以下代码将拍摄到的画面展示出来:
cv2.imshow( "Video", frame)
传入的第一个参数是窗口的名字,第二个参数是展示的内容。
以下代码是设置退出程序的机制,按Q即可退出循环:
If cv2.waitKey(1)& OxFF == ord( 'q') :
break
#退出while循环
运行代码可以看到启动了摄像头:
首先打开了摄像头,循环获取拍摄到的画面,返回布尔值指是否能够获取到画面,在该情况下是一定可以获取到画面的。 frame代表获取到的画面,通过OpenCV的库,将画面展示出来。展示出来需要设置名字,由此将其设置为Video。设置退出机制,如果按下Q就能够退出程序,释放摄像头资源。
要从拍摄到的画面中提取到人的脸部所在区域,一张图中人脸部的区域有多个,如图所示;
可以通过 Face_recognition 库中的方法来实现, 通过以下代码使用该方法:
face_locations = face_recognition.face_locations(frame)
该方法要求传入拍摄到的画面,对应就会返回人脸部所在的区域,返回的是一个列表,因为人脸所在的区域会有多个,所以需要用一个变量来承接返回值。展示窗口按Q退出之后,也需要将窗口关闭。在open CV中,关闭窗口通过以下代码实现:
cv2.destroyAl1windows()
将所有的OpenCV中的窗口关闭。
以下是Face locations的展现形式:
之所以只有一个列表,是因为只有一个人的面部,列表中只有一个对象。该对象是由4个数字组成。分别代表TOP、 right、 bottom 、Left。如图所示的红点:
将4个数字简化为,如图所示坐标:
沿着图片的顶部放置横坐标,沿着左侧放置纵坐标。设置完坐标之后可以得知任何一个点的位置都能够通过坐标表示。假设红点(90,30), X代表宽度, Y代表高度。右下角的点(100,45)。在红点处沿着X轴画平行线,沿着Y轴画平行线,右下角的点也同样如此。通过所画的2个点就能够定位到长方形的框。而两个点的坐标对应的也就是TOP right bottom Left。TOP代表红点距离顶部的位置,right代表红点距离左边界的位置,Buttom代表右下角的点距离上方的位置。通代表右下角的点距离左边界的位置,距离用像素表示。通过以上4个数字就能够表示长方形在坐标轴上的位置。
使用以下代码绘画绿色框:
for top,right, bottom,left in face_locations:
以上写法为拆包的形式。
用以下代码在人像所在区域放置框:
cv2.rectangle(frame,(left,top),(right,bottom),color)
第一个参数代表在哪个图像上面画框;
第二个参数代表Left和TOP;
第三个参数代表right和bottom,对应的就是2个点的坐标;
第四个参数是指颜色,颜色使用三通道。
cv2. rectangle(frame, (left, top), (right, bottom), (0, 255, 0)
在 OpenCV 中,颜色是倒过来写的。运行程序测试效果能够把人脸的区域标识出来。
至此,整个案例的第一部分已经实现。
2.读取
数据库就是文件夹,在文件夹中会有多张图片,每张图片对应一个特定的人物,面部特征存在于图片信息中,可以通过代码读取。对应的图片有文件名,也就是对应的人的名字,后缀部分需要去掉。由于是读取数字库中的信息,将整个步骤分为两大步:
第一步准备工作;
第二部分正式工作。
在准备工作中主要定义用到的变量如下:
首先定义数据库变量,方便之后使用。
face_databases_dir = 'face_databases'
定义数据列表用于存储用户。
user_names = []#存用户姓名
定义数据列表用于存储用户面部特征,与用户姓名一一对应。
user_faces_encodings = []#存用户面部特征向量(一一对应)
正式工作分为两部分:
第一部分需要得到文件夹下面所有的文件;
第二部分要循环读取文件名,做进一步处理。进一步的处理就是将用户的姓名及面部特征存到列表中。
首先导入包:
Import os
通过以下代码得到 face_databases_dir 文件夹下所有的文件名:
files = os.listdir( 'face_databases ' )
放入对应的文件路径,就能够得到所有的文件名。用files承接列表。执行结果如下:
由结果可知,符合实验要求,有2个结果。
通过以 for 循环,读取文件名进行进一步的处理:
for image_shot_name in files:
循环读取分为以下两步:
(1)截取文件名的前面部分作为用户名存入user_names的列表;
截取文件可以不用自己写方法实现,Python自己提供相应的方法在OS模块中,需要传入文件名,代码会返回两个结果,通过拆包的方式得到两个结果。
通过以下代码截取文件名的前面部分作为用户名存入user_names的列表中:
user_name,_ = os.path.splitext(image_shot_name)
通过以下代码将对象放入列表当中:
user names.append(user_name)
(2)将对象放入列表 user Faces encodings 当中
读取文件中的面部特征信息,存入到列表当中。首先需要拼接文件路径,因为当前的文件在外层目录中,不能直接读取图片,所以需要拼接文件路径,得到的值就是face_databases,是在一次遍历中产生的。
通过以下代码读取图片文件中的面部特征信息存入:
image_file_name=os.path.join(face_databases_dir,image_shot_name)
其次加载图片信息,因为面部信息的读取需要首先把图片加载出来。方法中放入文件路径,得到的对象使用变量来承接。
通过以下代码,加载图片信息:
image_file=face_recogition.load_image_file(image_file_name)
以下代码读取图片中的面部特征信息,需要传入图片信息。该方法会返回多个面部特征信息,因为一张图片可能有多个人像,在返回面部信息的过程中就可能会返回多个。此时不存在多个人像的情况,因为一张图片中只有一个人像,但是获取时还是需要将元素获取到。每个人的面部信息都是一个数组,并且用变量来承接数组。
通过以下代码实现:
face_encoding=face_recognition.face_encodings(image_file)[0]
通过以下代码将面部信息放到列表中:
user_faces_encodings.append(face_encoding)
结果如下:
用user_name列表存储用户姓名。用user_faces_encodings列表存储用户面部特征,其中第一个参数是第一个人的面部特征,第二个参数是第二个人的面部特征。能够根据对应关系,匹配到特征。特征的数组如下:
0也就是第一个图片的特征,本质就是一串数字,有128个数字。
3.匹配并做标识
用拍摄到人的脸部特征和数据库中的面部特征去匹配,并在用户头像的绿框上方用用户的姓名做标识,未知用户统一使用Unknown。也就是将第一步和第二步融合在一起。在第一步中,打开了摄像头,获取到摄像头拍摄的画面,定位到画面中人的脸部,并用绿色的框框住人脸。具体的操作如下:
首先,打开摄像头放到循环中,在循环中循环读取摄像头拍摄到的画面,并用Frame承接画面。从画面中提取人脸部所在的区域,由于摄像头拍摄到的画面可能有多张脸,所以用列表来承接结果。
其次,遍历列表,在人脸的区域画框。将图片展示且设置退出程序的机制。退出之后,释放摄像头资源及其他资源。然后用拍摄到的人的面部特征与数据库中的脸部特征匹配,并在用户头像上的绿框用用户的姓名做标识。如果没有匹配上的用户就使用Unknown。在2.2中,从拍摄的画面中提取人脸部所在的区域,可能会有多个,所以需要从所有人的头像所在区域,提取出每一个面部特征,可能会有多个,与上方一一对应,有一个人脸就有一个面部特征。
【第一个人脸所在区域,第二个人脸所在区域】
要得到的结果如下:
【第一个人脸对应的面部特征,第二个人脸对应的面部特征】
通过以下代码提取出脸部特征:
face_encodings=face_recognition.face_encodings
(frame,face_locations)
该方法传入两个参数,第一个参数是画面,第二个参数就是 Face _location,使用face_encodings 承接结果。此时,面部特征也是数组。
关键步骤是使用之前拍摄到的面部特征与数据库中的面部特征做匹配。首先定义用于存储用户姓名的变量:
Name=[]
目的在于往列表中填充面部特征对应的人的姓名:
【第一个人的姓名,第二个人的姓名】
如果在数据库中无法找到,即无法匹配特征,则是Unknown。首先需要遍历拍摄到的人的面部特征,和之前的面部特征做匹配,匹配到的人的姓名需要存到Names中。
代码如下:
for face_encoding in face_encodings:
face_recognition.compare_faces
(known_face_encodings, face_encoding_to_check)
第一个参数是许多面部特征的列表,第二个参数是未知的面部特征。该方法会使用列表中的面部特征与未知的面部特征做比较。如果匹配上,就会返回true。否则就会返回 false。
compare_ faces([ 面部特征1',面部特征2',' 面部特征3' ... ],未知的面部特征)
如果未知的面部特征是第一个人的面部特征,和面部特征2、面部特征3不匹配,返回的结果就是[true false false]。表示面部特征1匹配,和面部特征2不匹配,和面部特征3不匹配。
如果未知的面部特征是第二个人的面部特征,和面部特征1、面部特征3不匹配,返回的结果就是[false true false]。表示面部特征2匹配,和面部特征1不匹配,和面部特征3不匹配。
填充的第一个参数就是数据库中的面部特征,第二个参数就是拍摄到的面部特征,用一个变量来承接方法返回的结果。
代码如下:
matchs= face_recognition.compare_faces(user_faces_encodings, face_encoding)
User_names 返回的是姓名列表:
['第一个人的姓名’,'第二个人的姓名', '第三个人的姓名’]
与Face_ recognition是一一对应的。如果未知的面部特征与第一个面部特征相匹配,返回的结果就是第一个人的姓名。首先定义默认等于Unknown:
Name=”Unknown”
然后遍历匹配之后的结果。遍历的时候需要获取索引,因为需要使用索引从列表中取出姓名,可以通过Python中自带的方法来实现,需要传入参数matches。该方法遍历时会返回2个结果,所以需要2个参数来承接。例如第一次遍历的是false,返回的结果就是0,False;第二次遍历是false,返回的结果就是1,true;遍历的是false,返回的结果就是2,false。如果不匹配,直接使用Unknown。如果匹配,就需要将名字改为匹配到的人的名字。从对应的索引位置中获取名字。索引位置是index。匹配到之后,即可退出循环。然后将Name装载到Names列表中。
代码如下:
names = []
for face_encoding in face_encodings:
matchs= face_recognition.compare_faces(user_faces_encodings, face_encoding)
name = "UnKnown”
for index, is_ _match in enumerate(matchs) :
if is_ match:
name = user_ _names [index]
break
names . append(name)
此时已经拥有 Face_locations,也就是人脸所在区域。拥有人的姓名列表,与所在区域一一对应。第一个人脸所在区域对应第一个姓名,第二个人脸所在区域,对应第二个姓名。之后需要在绿框上标识人的姓名。 在Python中,有打包的语句,称为zip, zip是打包的命令,可以承接多个参数。代码如下:
zip(['第1个人的位置','第2个人的位置'],['第1个人的姓名', '第2个人的姓名])
迭代时得到的结果如下:第一次迭代,获得第一个人的位置和第一个人的姓名,第二次迭代,获得第二个人的位置和第二个人的姓名:
#for
#‘第1个人的位置',‘第1个人的姓名’
#‘第2个人的位置','第2个人的姓名'
for (top,right,bottom,left), name in zip(face_locations, names):
实际上就是将其拆开,前面是位置,后面是姓名,按照顺序一一对应上的。
标识步骤如下:
for (top, right, bottom,left), name in zip(face_locations,names):
cv2.rectangle(frame,(left, top),(right,bottom),(0,255,0),2)
font = cv2.FONT_HERSHEY_DUPLEX
cv2.putText(frame,name,(left,top-10),font,0.5,(0,255,0),1)
首先,定义一个字体:
font = cv2.FONT_HERSHEY_DUPLEX
接收多个参数,第一个参数就是在哪张图片上写字,第二个参数是写字的内容,第三个参数是写字的位置,也就是在坐标轴中正方形的两个对角线的点的坐标。不能直接写top,如果直接写TOP,就会和人脸重合,此时使用TOP减10。第四个参数是字体,第五个参数是字体大小,第六个参数是字的宽度。以上参数都能够根据自身喜好进行调整。代码如下:
cv2.putText(frame,name,(left,top-10),font,0.5,(0,255,0),1)
执行代码后,第2个人物放入摄像头拍摄到的区域时,绿色的框将两位目标人物脸部框柱。
此时将数据库中不存在的人像图片放入摄像头拍摄到的区域,将不会被识别。
4.定位和锁定目标人物
定位和锁定目标人物,改使用红色的框,将目标人物的脸部框住。首先需要确定目标人物是什么,可以首先定义一个列表,如果名字出现在这个列表中,就认为它是目标人物。接下来就是做进一步判断,在画框中做判断之后,将画框变为红色。代码如下:
首先定义颜色为绿色:
color = (0,255,0)
if name in boss_names:
表示如果人物是目标人物,就将颜色换为红色:
color = (0,0, 255)
总代码如下:
for (top,right,bottom,left), name in zip(face_locations,names):
color = (0,255,0)
if name in boss_names:
color = (0,0, 255)
cv2.rectangle(frame,(left,top),(right, bottom), color,2)
font = cv2.FONT_HERSHEY_DUPLEX
cv2.putText(frame,name,(left,top-10),font,0.5, color,1)
执行代码之后,符合预期结果,实验成功。