第5章 人脸检测和识别
5.1 Haar级联的概念
提取图像的细节对产生稳定分类结果和追踪结果很有用。
这些细节称为特征。
特征可能因为区域大小(window size)而不同,能为不同大小的窗口生成特征非常有用,这些特征称为级联。harr级联具有尺度不变性。
OpenCV提供尺度不变Harr级联的分类器和追踪器,但不具有旋转不变性。
5.2 获取Haar级联数据。
OpenCV3源代码的副本中有一个文件夹data/haarcascades。该文件夹包含了所有OpenCV的人脸检测的xml文件,用于检测静止图像、视频和摄像头得到的图像中的人脸。
将haarcascades文件复制到项目所在文件夹,命名为cascades。
5.3 使用OpenCV进行人脸检测
5.3.1 静态图像中的人脸检测
人脸检测首先是加载图像并检测人脸。
import cv2
filename = 'pic.jpg'
def detect(filename):
face_cascade = cv2.CascadeClassifier('./cascades/haarcascade_frontalface_default.xml')
img = cv2.imread(filename)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray,1.3,5)
for(x,y,w,h) in faces:
img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0,),2)
cv2.namedWindow('face detected!')
cv2.imshow('face detected',img)
cv2.imwrite('face_detected.jpg',img)
cv2.waitKey(0)
detect(filename)
5.3.2 视频中的人脸检测
对视频帧进行人脸检测,类似静态图像的人脸检测。
5.3.3 人脸识别
实现人脸识别的方法之一是用一系列分好类的图像(人脸数据库)来“训练”程序,并基于这些图像进行识别。
人脸识别模块的另一重要特征是:每个识别都具有转置信评分,因此可通过设置其阈值来进行筛选。
人脸数据库有两种方式获得:自己获得图像或从人脸数据库获得。
1.生成人脸识别数据
数据要求:1.灰度格式,后缀为.pgm
2.正方形
3.图像大小相同。
import cv2
def generate():
face_cascade = cv2.CascadeClassifier('./cascades/haarcascade_frontalface_default.xml')
camera = cv2.VideoCapture(0)
count = 0
while (True):
ret, frame = camera.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x, y, w, h) in faces:
img = cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2)
f = cv2.resize(gray[y:y + h, x:x + w], (200, 200))
cv2.imwrite('./data/at/sw/%s.pgm' % str(count), f)
print(count)
count += 1
cv2.imshow("camera", frame)
if cv2.waitKey(int(1000 / 12)) & 0xff == ord("Q"):
break
camera.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
generate()
2.人脸识别(概念)
OpenCV3 有3种人脸识别的方法,它们分别基于三种算法:Eigenfaces
Fishfaces和Local Binary Pattern Histogram(LBPH)。
首先,所有的方法都有类似的过程,都使用分好类的训练数据集来进行‘’训练“,
对图像或视频的人像进行分析,并从两方面来确定:是否识别到目标和置信度。
Eigenfaces通过PCA来处理,PCA的本质是识别某个训练集上的主成分,并计算出训练集相对于数据库的发散程度,并输出一个值。值越小,越匹配
Fisherfaces是从PCA发展起来的概念,采用更复杂的逻辑,效果更准确。
LBPH粗略地将人脸分成小单元,与模型中的对应单元进行比较,得到每个区域匹配值的直方图。LBPH是唯一允许模型样板人脸和检测的人类在性质大小可以不同的人类识别算法。作者认为是做准确的算法。
3.准备训练数据(这一步我没用到)
1已经获取了数据,现在要讲数据加载到人脸识别算法中。
所有人脸识别算法在他们的train函数都有两个参数:图像数组和标签数组,这些标签表示进行人脸识别时,某个人的ID。
为了实现这一点,要创建一个CSV文件,用来根据ID记录样本图像的路径。
lf/1.pgm;0
lf/2.pgm;0
lf/3.pgm;0
lf/4.pgm;0
lf/5.pgm;0
lf/6.pgm;0
lf/7.pgm;0
lf/8.pgm;0
lf/9.pgm;0
lf/10.pgm;0
lf/11.pgm;0
lf/12.pgm;0
lf/13.pgm;0
lf/14.pgm;0
lf/15.pgm;0
lf/16.pgm;0
lf/17.pgm;0
lf/18.pgm;0
lf/19.pgm;0
lf/20.pgm;0
4. 加载数据并识别人脸
首先逐行读取CSV文件,并将对应路径图像加载到图像数组中,将ID加载到标签数组中。
# coding=utf-8
import os
import sys
import cv2
import numpy as np
# 图片的路径
filepath = './data/at/sw'
# 定义2个数组,X存放每幅图片的数组列表,y存放每幅图片的序号,后面有句print函数
# 可以在IDLE里看哪张图片特征最匹配实时检测到的脸,并给出置信度
X = []
y = []
# 顾名思义,读取特征图片
def read_images(path):
# 初始化计数器
c = 0
# 扫描路径下的路径名,文件名,不明白的可以在下面print一下
for dirname, dirnames, filenames in os.walk(path):
print (dirname,dirnames,filenames)
#提取每个文件并保存到X,y数组里,这里老衲做了点改动,应为老衲的特征图片路径没有书上代码那么深
for filename in filenames:
try:
# 组合路径和文件名,得到特征图的绝对路径c:\MyW\date\1.pgm
filename = os.path.join(path, filename)
# 把特征图以灰度图读取
im = cv2.imread(filename, cv2.IMREAD_GRAYSCALE)
# 重新格式化图片为200x200像素,原书估计打错字了
if (im is not None):
im = cv2.resize(im, (200, 200))
# 把特征图片数组添加到X数组中,组成一个大的特征数组
X.append(np.asarray(im, dtype=np.uint8))
y.append(c)
# 输入输出错误检查
except IOError:
print("I/O error")
except:
print("Unexpected error:")
raise
c = c + 1
# print X
# print y
# 估计到这,这数组的维度大得吓人了
return [X, y]
# 顾名思义,人脸检测开始了
def face_rec():
# 定义一个名字的数组,随便瞎打几个英文字母,等会下面会用到
names = ['SHG', 'JSY', 'LYF']
# 加载特征图片
[x, y] = read_images(filepath)
# 把y数组保存为int32格式的数组,用asarry()不用开辟新的内存,其实老衲觉得array()函数也可以,现在谁的电脑内存没个几G啊
y = np.asarray(y, dtype=np.int32)
# 加载EigenFaceRecognizer算法,这里必须改为EigenFaceRecognizer_create,原书createEigenFaceRecognizer
# 因为老衲用的是opencv_contrib_python-3.4.0.12-cp27-cp27m-win32.whl
# 如何安装contrib请各位施主自行百度,后面会讲一个老衲安装时碰到的小故事
model = cv2.face.EigenFaceRecognizer_create()
# 训练数据集,貌似机器学习,好高深,不深究
model.train(np.asarray(X), np.asarray(y))
# 和第一步里generate()里的用法一样,懒得解释了
face_cascade = cv2.CascadeClassifier('./cascades/haarcascade_frontalface_default.xml')
camera = cv2.VideoCapture(0)
while (True):
read, img = camera.read()
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)
# 预测实时图片,这里老衲也没看明白,下次有时间去看看EigenFaceRecognizer的方法
params = model.predict(roi)
# 把匹配的特征和置信度打印在IDLE内
print("Label: %s, Confidence: %.2f" % (params[0], params[1]))
# 把匹配的名字显示在方框左上角,有时候会瞎显示,以后研究,还有就是现在无法显示中文字符,也以后吧 :P
if params[1]<5500:
cv2.putText(img, "LF", (x, y - 20), \
cv2.FONT_HERSHEY_SIMPLEX, 1, 255, 2)
except:
continue
cv2.imshow("camera", img)
if cv2.waitKey(int(1000 / 12)) & 0xff == ord("q"):
break
cv2.destroyAllWindows()
if __name__ == "__main__":
face_rec()
置信度:
predict()函数返回含有两个元素的数组,第一个是所识别个体的标签,第二个是置信度评分。置信度评分用来衡量所测人脸和模型的差距,0表示完全匹配。
Eigenfaces/Fisherfaces 产生0到20 000的值,而低于5000的值都是可靠的。
LBPH则需要低于50才比较可靠,高于80认为不可靠。