写在前面
本文为零基础实现人脸识别的教程,读者不需要人工智能学习背景,不需要机器学习相关基础,只要能读懂简单的Pyhton代码,便可以轻松地在自己的电脑上实现人脸识别(两个文件,加注释共96行)。
引入
图为华中科技大学南四门门禁
在2020上半年的疫情攻坚战中,人工智能以其特有的优势发挥了巨大的作用。而包含人脸识别在内的智能门禁系统更是掌握了我们的进出大权,让我们又爱又恨。今天,小编就手把手带大家来用96行python代码在自己电脑上实现这个看似“高大上”的人脸识别。
废话不多说,直接上干货。
今天我们主要用到的库和方法有:
OpenCV:轻量级的计算机视觉和机器学习软件库
cv2.CascadeClassifier:OpenCV提供的级联分类器,用于目标(人脸)检测
haarcascade_frontalface_default.xml:OpenCV已经训练好的数据文件,这里我们直接调用,省去了模型训练过程
cv2.face.EigenFaceRecognizer:OpenCV提供的一个人脸识别算法
01
人脸图像采集与检测
想要进行人脸识别,我们首先要获得原始的人脸图像,也即门禁系统的“信息录入”环节。这里图像的来源可以是电脑摄像头采集,也可以是提前拍摄的视频或图片。
接下来我们要把获取的图像简单处理后传入分类器,分类器会自动识别出图像中的人脸,之后将人脸部分图像保存到本地。
代码及注释如下:
代码语言:javascript复制import cv2 # opencv
import os
def generate(dirname):
face_cascade = cv2.CascadeClassifier(r'C:Users86155AppDataLocalProgramsPythonPython37Libsite-packagescv2datahaarcascade_frontalface_default.xml'
)#导入OpenCV已经训练好的数据文件
if (not os.path.isdir(dirname)):
os.makedirs(dirname)
camera = cv2.VideoCapture(0) #参数为0表示打开笔记本摄像头,参数为路径表示打开视频
count = 0 #计数变量
while (True):
ret, frame = camera.read() #camera.read()按帧读取视频,ret,frame是获cap.read()方法的两个返回值。其中ret是布尔值,如果读取帧是正确的则返回True,如果文件读取到结尾,它的返回值就为False。frame就是每一帧的图像,是个三维矩阵
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #色彩空间的转化 生成灰度图
faces = face_cascade.detectMultiScale(gray,1.3,5) #scaleFactor表示每次图像尺寸减小的比例 minNeighbors表示每一个目标至少要被检测到5次才算是真的目标
for (x, y, w, h) in faces:
img = cv2.rectangle(frame, (x, y), (x w, y h), (255, 0, 0),2) #在frame上以两点连成对角线确定蓝色矩形框
f = cv2.resize(gray[y:y h, x:x w],(200, 200)) #将人脸部分分辨率调整为200*200
cv2.imwrite(dirname '/%s.pgm' % str(count), f) #将图片写入硬盘
print(count)
count = 1
cv2.imshow("camera", frame) #在摄像头上显示获取的人脸部分图片
if cv2.waitKey(100) & 0xff == ord("q"): #按q退出
break
elif count > 20: #拍摄二十张后退出
break
camera.release()
cv2.destroyAllWindows() #关闭相机,释放资源
if __name__ == "__main__":
generate(r"C:Users86155Desktop123editor") #传入图片保存路径,路径中不要出现中文
这里使用的python版本为3.7 其中OpenCV库的安装命令为:pip install opencv-python
第4行为传入opencv训练好的人脸提取数据文件,安装好opencv库时这个文件会自动下载到本地,保存路径依据python包安装路径的不同而不同,可以在本地直接搜索文件名haarcascade_frontalface_default.xml来找到他们的储存地址,并用本地地址替换上述代码中小编的地址 。该分类器的官方文档和训练数据的项目地址如下
https://docs.opencv.org/2.4/modules/objdetect/doc/cascade_classification.html
https://github.com/opencv/opencv/tree/master/data/haarcascades
为了更好地模拟实际应用中的人脸识别,这里我们通过笔记本摄像头来现场获取人脸图像。打开摄像头并通过read方法获取图像后,我们将其转换为灰度图来减少运算量,提高运行速度。
第14行调用级联分类器的detectMultiScale方法,检测输入图像中的(大小不同的)人脸,并将检测到含脸矩形的宽度,h为高度,如下图
有人脸的图像作为矩形列表返回。x,y参数为人脸图像的左下角坐标,w为人
16行在图片中用矩形圈出人脸部分,如上图蓝色框,18行将灰度图写入本地。22行将成功检测到人脸的图片显示在摄像头界面,如上图。
23到26行为设置退出条件:按键盘上的q或者成功获取了20张图片。
最后,关闭摄像头,释放资源。
程序运行成功后,打开传入的图片保存路径,应该可以看到二十张.pgm后缀的刚刚获取到的人脸灰度图。
上图为editor,下图为友情出镜的editor's roomate
至此,第一步人脸图像采集与检测完成。
02
将采集到的信息写入文本文件(可跳过)
实际应用中我们我们会收集大量的人脸信息,这些信息可能储存在不同的位置,为了方便识别时调用原始数据,我们将不同的人脸原始数据储存在不同的子文件夹下,并将赋予他们不同的标签,最后将这些信息全部写入一个文本文件
代码语言:javascript复制
代码语言:javascript复制import sys
import os.path
if __name__ == "__main__":
BASE_PATH = r"C:Users86155Desktop123"
SEPARATOR = ";"
fh = open(r"C:Users86155Desktop123at.text", 'w')
label = 0
for dirname, dirnames, filenames in os.walk(BASE_PATH):
for subdirname in dirnames:
subject_path = os.path.join(dirname, subdirname)
for filename in os.listdir(subject_path):
abs_path = "%s/%s" % (subject_path, filename)
print("%s%s%d" % (abs_path, SEPARATOR, label))
fh.write(abs_path)
fh.write(SEPARATOR)
fh.write(str(label))
fh.write("n")
label = label 1
fh.close()
这里主要是python基础操作,此处不做过多讲解。若读者基础较为薄弱对文件读写不熟悉可越过第二步直接看第三步。
运行后文本文件如下图:
至此,前期工作准备完毕,马上就可以开始人脸识别啦。
03
人脸图像匹配与识别
这一步,我们首先要读取原始数据和标签,并将其传入opencv提供的人脸识别算法模型进行训练(让算法学习什么样的脸是标签一,什么样的脸是标签二······)。
训练完成后,就可以和第一步一样获取新的人脸信息,处理后传入训练过的模型,让模型将新图像与原始图像进行匹配,之后返回匹配的结果(最接近的标签号以及预测准确性),从而达到人脸识别的目的。
代码如下:
代码语言:javascript复制import os
import sys
import numpy as np
import cv2
def read_images(path, sz=None):
c = 0 #标签号
X, y = [], []
for dirname, dirnames, filenames in os.walk(path):
for subdirname in dirnames:
subject_path = os.path.join(dirname, subdirname)
for filename in os.listdir(subject_path):
try:
if not filename.endswith('.pgm'):
continue
filepath = os.path.join(subject_path, filename)
im = cv2.imread(
filepath, cv2.IMREAD_GRAYSCALE
) #读取图片后已多维数组的形式保存图片信息,前两维表示图片的像素坐标,最后一维表示图片的通道索引
if sz is not None:
im = cv2.resize(im, (200, 200))
X.append(np.asarray(im, dtype=np.uint8))
y.append(c)
except:
print("Unexpected error:", sys.exc_info()[0])
c = c 1
return [X, y]
def face_rec(img_path):
names = ['editor', 'roommate'] #标签对应的人名
[X, y] = read_images(img_path)
y = np.asarray(y, dtype=np.int32)
model = cv2.face.EigenFaceRecognizer_create() #特征脸人脸识别算法
model.train(np.asarray(X), np.asarray(y)) #传入特征(原始图像)和标签进行训练
camera = cv2.VideoCapture(0) #打开摄像头
face_cascade = cv2.CascadeClassifier(
r'C:Users86155AppDataLocalProgramsPythonPython37Libsite-packagescv2datahaarcascade_frontalface_default.xml'
) #读入级联分类器
while (True):
read, img = camera.read() #第一个返回值为boolen值
faces = face_cascade.detectMultiScale(img, 1.3, 5)
for (x, y, w, h) in faces:
img = cv2.rectangle(img, (x, y), (x w, y h), (255, 0, 0), 2)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
roi = gray[x:x w, y:y h] #重复第一步图像获取
try:
roi = cv2.resize(
roi, (200, 200), interpolation=cv2.INTER_LINEAR) #处理刚刚回去的要识别的人脸
params = model.predict(roi) #进行识别 返回最接近的标签和置信度(数值越小,准确性越高)
print("Label: %s, Confidence: %.2f" % (params[0], params[1]))
cv2.putText(img, names[params[0] - 1], (x, y - 20),
cv2.FONT_HERSHEY_SIMPLEX, 1, 255, 2) #将识别出的人名信息显示在图像上
except:
continue
cv2.imshow("camera", img)
if cv2.waitKey(1000 // 12) & 0xff == ord('q'):
break #按q结束
cv2.destroyAllWindows()
if __name__ == "__main__":
face_rec(r'C:Users86155Desktop123') #传入原始图像的父文件夹
在这一步为了避开文件读写操作我们没有使用上一步写好了的文本文件,选择直接从目录中读入原始图像,并将图像矩阵和标签分别放入X,y列表。
将读取的原始图像矩阵和对应标签传入人脸识别算法模型,对模型进行训练。关于该模型的具体实现,opencv的官方文档里有详细说明,链接如下
https://docs.opencv.org/2.4/modules/contrib/doc/facerec/facerec_api.html
模型训练完成后,我们就可以开始进行人脸识别了。和第一步相同,我们通过摄像头来获取要被识别的人脸图像,将其处理后传入训练好的模型进行识别(代码51行),模型会返回给我们识别的结果(最接近的标签号和置信度,置信度低于5000的识别即为相当可靠)。然后我们将识别结果写在图像上并在摄像头界面展示,效果如下
至此,我们通过通过 96行(加上第二步119行,但实际上这里的简单实现我们并没有用到第二步)python代码在自己电脑上成功实现了人脸识别。人工智能、机器学习实际上并没有我们想象的那么遥远那么高大上,只要我们愿意学习,他就在我们的面前,触手可及。
下面,我们将讨论一些人脸识别技术实现的难点和存在的问题。
04
人脸识别技术的难点
1.图像光照问题
人脸识别时面临各种环境光源的考验,可能出现侧光、顶光、背光和高光等现象,这给人脸识别造成了相当大的挑战。比如小编寝室里,光线较暗时人脸识别的速度和准确率都有下降。解决此问题目前有两种方案:三维图形人脸识别和热成像人脸识别,但这两种技术效果不明显仍然未能克服光照对识别率的影响。
2.人脸姿态和饰物问题
除非特殊要求,人脸识别都是非配合型的,人脸上的表情和识别时的姿态都可能千变万化,另外还常会出现佩戴帽子、黑框眼镜、口罩等饰物现象,如何排除这些因素的干扰,也是人脸识别必须考虑的问题。
3.人脸存在相似性
不同个体之间特别是同一民族的区别不大,所有人脸的结构都相似,甚至人脸的结构外形都很相似。这样的特点对于利用人脸进行定位是有利的,但是对于利用人脸区分个体是不利的。再加上化妆的掩盖及双胞胎的天然相似性更增加了识别的难度。
4.人脸存在易变性
人脸的外形和特征不是长久不变的,随着年龄变化或其他因素会发生很大变化,而在不同观察角度,人脸的视觉图像也相差很大。
5.安全性
照片、视频甚至人皮面具等带有面部特征的东西都可以拿来应付人脸识别,这对人脸识别的安全性造成了极大挑战,下面我们将继续讨论这个问题。
05
人脸识别真的安全吗?
人脸识别系统的安全性第一次被公众热议是2013年的一个案件,著名演员赵薇的老公黄有龙被他的司机冒名顶替把自己的房子给卖了。根据新京报当时的报道,北京市方正公证处在审核到场办理公证手续人是否为黄有龙时,使用人脸验证系统将到场人员与黄有龙身份证照片进行了比对,验证分值超过0.6,遂审核通过,认定现场办理公证的人即为黄有龙本人,并办理了公证手续。这样的人脸识别系统,谁还敢用?
而本例中,我们采集的信息都为静态二维灰度图像,识别也只采用了Eigenfaces这一种算法,这就导致上述过程实现的人脸识别安全性相当差。事实上,小编拿手机拍摄的正面照放在摄像头前进行识别,不仅可以轻松识别成功,置信度最高时竟达到了6000左右。
所以,在实际应用中,人脸识别不仅要保证面部特征的一致性,还要保证进行识别的是生物活体而不是平面的图像或视频。这也就是我们在识别过程中经常需要做出眨眼、摇头、张嘴等动作的原因。此外,通过多种传感器为脸部绘制精确细致的深度图(类似3D建模)来作为原始信息,也解决了用图片视频等二维信息蒙混过关的问题。
iPhone X 的点阵投影器能在不同的光照条件下将 3 万 多个肉眼不可见的光点投影在 人的脸部,绘制出独一无二的面谱,由红外相机得到面部 3D 深度信息模型。再结合前置摄像头拍摄的可见光人脸,将人脸的纹理与人脸 3D 模型通过算法结合, 得到真实的 3D 人脸图。
三维特征的采集的确解决了相当一部分人脸识别的安全问题,但,道高一尺魔高一丈,2019年12月,一篇来自《财富》的报道称,人工智能公司Kneron发起了一次测试,Kneron团队利用高质量的人皮面具欺骗了支付宝和微信的刷脸支付系统,成功地完成了购买行为。Kneron团队还称,他们可以使用同样的方式通过中国的火车站安检系统。
事后,支付宝和微信这两家公司也迅速作出紧急回应,两家公司都表示,如果在刷脸支付的过程中产生盗刷的问题,用户是可以申请全额赔付的。很显然,支付宝和微信都等同于承认了人皮面具攻击风险的客观存在。
所以我们必须承认,刷脸技术目前还远未达到真正成熟的状态,也确实有很多安全漏洞可能会被黑客们钻空子。
不过,从技术发展的角度上来看,人皮面具与人脸识别技术,将来肯定是向着人脸识别技术越来越难以攻破的趋势去发展的。人类的面部特征信息,除了现在已经被数据建模的脸型、肤色、嘴唇、眼距以及五官之间的几何关系以外,还有更多细节特征可以用于身份识别。
人皮面具技术与刷脸认证技术的对攻,从本质上来说,其实是信息量的比拼。刷脸技术拼命地想要通过各种传感器从人身上搜集到更多的数据。而人皮面具则在努力盗取这些数据,并且把这些数据还原成实体。一个是搜集,一个是还原,这里面的技术难度显然是不对等的。当单纯的人脸识别变成了同时识别掌纹、面部特征以及虹膜特征时,想要造假的可能性自然就大幅度降低,甚至变得完全不可能了。
人脸识别技术发展和应用中遇到的各种挫折,并不会成为其发展的阻碍,反而很可能成为人脸识别技术自我完善的动力。人脸识别技术的应用领域极为广泛,除了最常见的金融领域以外,在商业管理、公共安全、交通及军事等领域也有很多突出的贡献。这也是当今科技投资的最热门的领域之一。随着技术的逐步发展和更多生物特征被加入到交叉验证当中来,人脸识别的安全性也会得到大幅度的提高。
- END -