前言:因为我要做选修课的关于ai的项目,所以整个代码其实是摘录自hwtl0703598这个csdn博客主的python人脸识别源码,不过也在调试的过程过发现了那位博客主的一些代码的问题,并改善了,我会在整篇文章后端指出改正,基本上是可以配好环境后直接使用。
简要概述之LBPH是什么?
LBPH(Local Binary PatternsHistograms)局部二进制编码直方图,建立在LBPH基础之上的人脸识别法基本思想如下:首先以每个像素为中心,判断与周围像素灰度值大小关系,对其进行二进制编码,从而获得整幅图像的LBP编码图像;再将LBP图像分为个区域,获取每个区域的LBP编码直方图,继而得到整幅图像的LBP编码直方图,通过比较不同人脸图像LBP编码直方图达到人脸识别的目的,其优点是不会受到光照、缩放、旋转和平移的影响。
PS:OpenCV提供了三种人脸识别的方法,分别是LBPH方法、EigenFishfaces方法、Fisherfaces方法。
为什么要使用LBPH而不是其他?
LBPH:将检测到的人脸分为小单元, 并将其与模型中的对应单元进行比较, 对每个区域的匹配值产生一个直方图。 由于这种方法的灵活性, LBPH是唯一允许模型样本人脸和检测到的人脸在形状、 大小上可以不同的人脸识别算法。
人脸识别的步骤
- step1:进行人脸的录入
- step2:用保存的人脸进行训练
- step3:利用训练好的数据去识别特别的人
准备工作:
- step1.使用python,任意一个编译器,eclipse或者pycharm
- step2.准备好opencv,OpenCV是一个开源的计算机视觉库,在本项目中用于图像的识别和摄像头的调用
- step3.numpy(Numerical Python):python语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组提供大量的数学函数库。
- step4.PIL:Python Imaging Library,Python平台用于图像处理的标准库
人脸录入流程:
- step1:利用opencv的VideoCapture函数打开摄像头并获取视频流。
- step2:获取视频流的每一帧的图像,将图像转成灰度图,在图像上利用opencv人脸检测器去检测人脸,并在人脸上画个框,保存框里面的人脸的图像到自己选定的图像文件夹,保存200张左右,实际是训练越多,识别的成功率也越高。
- step3:最后保存完200张图像,程序自动结束,释放摄像头缓存并关闭窗口。
PS:每张人脸需要转成灰度图,图像灰度化的目的是为了简化矩阵,提高运算速度,这里是提高识别效率。
人脸录入完整代码
import cv2
cap = cv2.VideoCapture(0)
face_detector = cv2.CascadeClassifier('D:/opencv\sources/data/haarcascades/haarcascade_frontalface_default.xml')
face_id = input('User data input,Look at the camera and wait ...')
count = 0
while cap.isOpened():
ret, frame = cap.read()
if ret is True:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
else:
break
faces = face_detector.detectMultiScale(gray, 1.3, 5)
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y), (x + w, y + w), (255, 0, 0))
count += 1
cv2.imwrite("D:/opencv_test/" + str(face_id) + '.' + str(count) + '.jpg', gray[y:y + h, x:x + w])
cv2.imshow('image', frame)
k = cv2.waitKey(1)
if k == 27:
break
elif count >= 200:
break
cap.release()
cv2.destroyAllWindows()
代码解析:
- cap = cv2.VideoCapture(0):调用摄像头,参数0表示默认为笔记本的内置第一个摄像头
- cap.isOpened():判断视频对象是否成功读取,成功读取视频对象返回True,这里作为循环一直执行的条件。
- ret,frame = cap.read():按帧读取视频,返回值ret是布尔型,正确读取则返回True,读取失败或读取视频结尾则会返回False;frame为每一帧的图像。
- faces = face_detector.detectMultiScale(gray, 1.3, 5):#第一个参数是灰度图像,第二个参数是尺度变换,就是向上或者向下每次是原来的多少倍,这里是1.02倍,第三个参数是人脸检测次数,设置越高,误检率越低,但是对于迷糊图片,我们设置越高,越不易检测出来,要适当降低
- key = cv2.waitKey(1):等待键盘输入,参数1表示延时1ms切换到下一帧,参数为0表示显示当前帧,相当于暂停,让其等于27,27在电脑上表示Esc退出。
- 这里根据count的数值,录入200张结束,实际也可以录入更多,但也不是更多更好。
- 这里的保存的图片都固定放到了一个人脸图像集里面,里面的图片名为user.count.jpg
- 运行代码会在User data input,Look at the camera and wait …停下,这里是要输入,因为后期是需要用names数组来显示名字,所以这里的user名为0,1,2的下标,输入为0,1,2……
效果展示1
效果展示2
人脸数据集训练流程
- step1:给出保存好的人脸图像文件夹的路径,遍历该路径下所有的图片,通过os函数,导入图片的名字和图像进face_samples和ids两个list数组,图片通过numpy中的array函数转成数组。
- step2:调用LBPH人脸识别器,将图像数组和对应的图像名进行训练,并将结果保存到trainer/trainer.yml这个文件下。
人脸训练完整代码
import os
import cv2
import numpy as np
from PIL import Image
path = 'D:/opencv_test/'
recognizer = cv2.face.LBPHFaceRecognizer_create()
detector = cv2.CascadeClassifier('D:/opencv\sources/data/haarcascades/haarcascade_frontalface_default.xml')
def get_images_and_labels(path):
image_paths = [os.path.join(path, f) for f in os.listdir(path)]
face_samples = []
ids = []
for image_path in image_paths:
img = Image.open(image_path).convert('L')
img_np = np.array(img, 'uint8')
if os.path.split(image_path)[-1].split(".")[-1] != 'jpg':
continue
id = int(os.path.split(image_path)[-1].split(".")[0])
faces = detector.detectMultiScale(img_np)
for (x, y, w, h) in faces:
face_samples.append(img_np[y:y + h, x:x + w])
ids.append(id)
return face_samples, ids
faces, ids = get_images_and_labels(path)
recognizer.train(faces, np.array(ids))
recognizer.save('trainer/trainer.yml')
代码解析:
- recognizer = cv2.face.LBPHFaceRecognizer_create():生成LBPH识别器实例模型
- cv2.face_FaceRecognizer.train():对每个参考图像计算LBPH,得到一个向量,每个人脸都是整个向量集中的一个点
- detector = cv2.CascadeClassifier(‘sources/data/haarcascades/haarcascade_frontalface_default.xml’):调用Opencv自带训练好的人脸检测器(默认)
- OpenCV自带的人脸检测器在sources/data/目录下,根据每个人OpenCV安装的目录不同,这里也都是用的绝对地址
- 这里获取的id是user而不是count,这也就是我改善了那位博客主的代码问题,将对应路径下的所有人脸图片按对应的user名进行训练,保存训练好的数据集。
人脸识别流程:
- step1:调用摄像头,获取视频流的图像帧,框出人脸,用训练好的人脸图像集去和视频中的人脸进行匹配,使用预测函数predict()获取置信评分。
- step2:LBPH识别置信评分80以上就算是不合格,所以判断预测出来的置信评分,如果视频流中的人脸没有在训练集中,那么就表示成unknow。
- step3:框出的视频流图像帧,把人脸框出来,显示置信评分和预测出来的人脸的标签,用标签去指定输出是图像集中的哪个人。
- step4:释放摄像头资源并关闭窗口。
人脸识别完整代码
import cv2
recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.read('trainer/trainer.yml')
face_cascade = cv2.CascadeClassifier("D:/opencv\sources/data/haarcascades/haarcascade_frontalface_default.xml")
font = cv2.FONT_HERSHEY_SIMPLEX
idnum = 0
cam = cv2.VideoCapture(0)
cam.set(6, cv2.VideoWriter.fourcc('M', 'J', 'P', 'G'))
minW = 0.1 * cam.get(3)
minH = 0.1 * cam.get(4)
names = ['linluocheng','zhupengcheng']
while True:
ret, img = cam.read()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(
gray,
scaleFactor=1.2,
minNeighbors=5,
minSize=(int(minW), int(minH))
)
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
idnum, confidence = recognizer.predict(gray[y:y + h, x:x + w])
if confidence < 80:
idum = names[idnum]
confidence = "{0}%".format(round(100 - confidence))
else:
idum = "unknown"
confidence = "{0}%".format(round(100 - confidence))
cv2.putText(img, str(idum), (x + 5, y - 5), font, 1, (0, 0, 255), 1)
cv2.putText(img, str(confidence), (x + 5, y + h - 5), font, 1, (0, 0, 0), 1)
cv2.imshow('camera', img)
k = cv2.waitKey(10)
if k == 27:
break
cam.release()
cv2.destroyAllWindows()
代码解析:
- recongnizer.predict():一个预测函数,获取图像的标签和图像和训练集的相似度,也称置信评分。
- cv2.putText():参数(图片 添加的文字 位置 字体 字体大小 字体颜色 字体粗细),将文字显示到图像上,中文会显示乱码。
- format 格式化函数:基本语法是通过{}和:来代替之前前的%,有点类似C语言的格式符
- round() 方法返回浮点数x的四舍五入值
- 转成灰度图并在上面框住人脸,用人脸和训练好的去比较,当置信评分达到一定程度,即为匹配成功,显示人脸和置信评分。
- 这里还是有一处是那位博客主的错误,就是format函数和前面的“”之前是用.隔开,而不是逗号,
效果展示图 1
效果展示图2
鉴于使用源码出现的各种问题做个总结:
1、这里的代码是我通过别人的代码稍作修改的,如果你碰到了问题去找其他的人的意见去修改,还是会出现问题,如果出现函数没有找到,不存在的问题,那就是opencv的版本问题,在更新迭代后各种函数会出现相应修改。
2、有人用这个博客的代码出了各种问题,原因是一个训练的流程不是很清楚,关键在于人脸录入的时候的输入名称,这里针对能运行程序,给出以下的流程。
step1(注意这里输入的是0,1,2,最好按照顺序输入):
step2:
人脸训练直接运行即可。
step3(在人脸识别代码中,会有一个类似的names列表,这个就是我们自定义的名字,在录入人脸的时候的序号,即对应这里的下标,然后取得对应的人脸标签):
step3:
然后进行识别,最终识别完成。
文章出处登录后可见!