Python使用OpenCV实现人脸识别
环境准备:opencv 以及 opencv拓展模块,numpy
pip install opencv-python
pip install opencv-contrib-python安装扩展模块
整体流程
面部检测函数
face_detect_demo
函数接收一张图片作为输入,将其转换为灰度图像,并使用Haar级联分类器在图像中检测面部。
def face_detect_demo(image):
# 将图像转换为灰度图像
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 使用OpenCV的级联分类器加载Haar级联文件
face_detector = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
# 使用detectMultiScale方法检测图像中的面部
faces = face_detector.detectMultiScale(gray, 1.2, 6)
# 如果未检测到面部,则返回None
if (len(faces) == 0):
return None, None
# 假设只有一张脸,获取面部的坐标和宽度高度
(x, y, w, h) = faces[0]
# 返回图像的面部部分和面部的坐标
return gray[y:y + w, x:x + h], faces[0]
如果检测到面部,它会返回灰度面部和其边界框。如果未检测到面部,则返回None。
文件处理函数
ReFileName
函数读取给定目录中的图片,对每张图片进行面部检测,将检测到的面部调整为固定大小,并返回这些面部的列表。
def ReFileName(dirPath):
# 初始化一个空列表来存储面部
faces=[]
# 遍历给定目录中的文件
for file in os.listdir(dirPath):
# 检查是否是文件
if os.path.isfile(os.path.join(dirPath, file)) == True:
# 获取文件的基本名称
c= os.path.basename(file)
# 构造文件的完整路径名
name = dirPath + '\\' + c
# 读取图像文件
img = cv2.imread(name)
# 在图像中检测面部
face, rect = face_detect_demo(img)
# 如果检测到面部,将其添加到面部列表中
if face is not None:
# 将检测到的面部调整为固定大小
face=cv2.resize(face,(300,300),interpolation = cv2.INTER_AREA)
faces.append(face)
# 返回面部列表
return faces
训练识别器
最后,我们从两个不同的目录读取两个人(杨幂和刘亦菲)的图片。我们为这些图片分配标签(0代表杨幂,1代表刘亦菲),将它们合并成训练数据和标签,然后打乱它们。然后我们使用这些数据训练一个LBPH(Local Binary Patterns Histograms)面部识别器,并将训练好的模型保存到’train.yml’。
# 杨幂的照片路径
dirPathyangmi = r"D:\\PythonProject\\Face_Recognition\\yangmi"
# 调用ReFileName函数处理杨幂的照片,并获取面部列表
yangmi=ReFileName(dirPathyangmi)
# 为杨幂的照片创建标签,全部标记为0
labelyangmi=np.array([0 for i in range(len(yangmi))])
# 刘亦菲的照片路径
dirPathliuyifei = r"D:\\PythonProject\\Face_Recognition\\liuyifei"
# 调用ReFileName函数处理刘亦菲的照片,并获取面部列表
liuyifei=ReFileName(dirPathliuyifei)
# 为刘亦菲的照片创建标签,全部标记为1
labelliuyifei=np.array([1 for i in range(len(liuyifei))])
# 将两个人的面部列表合并成一个数组
x=np.concatenate((yangmi,liuyifei),axis=0)
# 将两个人的标签列表合并成一个数组
y=np.concatenate((labelyangmi,labelliuyifei),axis=0)
# 创建一个索引列表,长度与标签数组相同
index = [i for i in range(len(y))]
# 设置随机数种子,确保每次打乱索引的结果都一样
np.random.seed(1)
# 打乱索引列表
np.random.shuffle(index)
# 根据打乱的索引重新排列训练数据和标签
train_data = x[index]
train_label = y[index]
# 创建一个LBPH面部识别器对象
recognizer = cv2.face.LBPHFaceRecognizer_create()
# 使用训练数据和标签训练识别器
recognizer.train(train_data, train_label)
# 将训练好的识别器保存到文件'train.yml'
recognizer.write('train.yml')
导入训练结果
我们导入训练结果,并读取前文训练的结果。
# 创建一个LBPH面部识别器对象
recognizer = cv2.face.LBPHFaceRecognizer_create()
# 从文件'train.yml'中读取训练好的识别器
recognizer.read('train.yml')
绘制矩形和文本
我们定义了两个函数来在图像上绘制矩形和文本。draw_rectangle
函数根据给定的人脸(x,y)坐标和宽度高度在图像上绘制矩形,而draw_text
函数则在给定的人脸(x,y)坐标处写出人名。
def draw_rectangle(img, rect):
(x, y, w, h) = rect
cv2.rectangle(img, (x, y), (x + w, y + h), (255, 255, 0), 2)
def draw_text(img, text, x, y):
cv2.putText(img, text, (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, (128, 128, 0), 2)
预测函数
最后,我们定义了一个预测函数。这个函数接收一张图片作为输入,检测图片中的人脸,并预测人脸的标签。然后,它在检测到的脸部周围画一个矩形,并标出预测的人名。
# 定义人物标签
facelabel = ["yangmi", "liuyifei"]
def predict(image):
# 创建图像的副本,以保留原始图像
img = image.copy()
# 在图像中检测人脸
face, rect = face_detect_demo(img)
# 将检测到的人脸调整为固定大小
face=cv2.resize(face,(300,300),interpolation = cv2.INTER_AREA)
# 预测人脸的标签和可信度
label = recognizer.predict(face)
# 如果可信度小于等于50
if label[1]<=50:
# 获取对应标签的人名
label_text = facelabel[label[0]]
# 在检测到的人脸周围画一个矩形
draw_rectangle(img, rect)
# 标出预测的人名
draw_text(img, label_text, rect[0], rect[1])
# 返回预测后的图像
return img
else:
# 在检测到的人脸周围画一个矩形
draw_rectangle(img, rect)
# 标出"not find"
draw_text(img, "not find", rect[0], rect[1])
# 返回预测后的图像
return img
如果识别器对预测结果的可信度大于50,则我们假设没有找到匹配的人脸,并在图片上标注"not find"。
最后,我们读取一张测试图片,对其进行预测,并显示预测结果。
test_img = cv2.imread("test.jpg")
pred_img = predict(test_img)
cv2.imshow('result', pred_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
完整代码:
train.py
import os
from pickletools import uint8
import numpy as np
import cv2
#脸部检测函数
def face_detect_demo(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
face_detector = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
faces = face_detector.detectMultiScale(gray, 1.2, 6) #图像缩放大小 影响检测质量
# 如果未检测到面部,则返回原始图像
if (len(faces) == 0):
return None, None
# 目前假设只有一张脸,xy为左上角坐标,wh为矩形的宽高
(x, y, w, h) = faces[0]
# 返回图像的脸部部分
return gray[y:y + w, x:x + h], faces[0]
def ReFileName(dirPath):
# 对目录下的文件进行遍历
faces=[]
for file in os.listdir(dirPath):
# 判断是否是文件
if os.path.isfile(os.path.join(dirPath, file)) == True:
c= os.path.basename(file)
name = dirPath + '\\' + c
img = cv2.imread(name)
# 检测脸部
face, rect = face_detect_demo(img)
# 我们忽略未检测到的脸部
if face is not None:
face=cv2.resize(face,(300,300),interpolation = cv2.INTER_AREA)#修改识别面部为固定大小,与后面检测面部大小一致
# 将脸添加到脸部列表并添加相应的标签
faces.append(face)
return faces
#杨幂照读取
dirPathyangmi = r"D:\\PythonProject\\Face_Recognition\\yangmi"#文件路径
yangmi=ReFileName(dirPathyangmi)#调用函数
labelyangmi=np.array([0 for i in range(len(yangmi))])#标签处理
#刘亦菲照读取
dirPathliuyifei = r"D:\\PythonProject\\Face_Recognition\\liuyifei"#文件路径
liuyifei=ReFileName(dirPathliuyifei)#调用函数
labelliuyifei=np.array([1 for i in range(len(liuyifei))])#标签处理
#拼接并打乱数据特征和标签
x=np.concatenate((yangmi,liuyifei),axis=0)
y=np.concatenate((labelyangmi,labelliuyifei),axis=0)
index = [i for i in range(len(y))] # test_data为测试数据
np.random.seed(1)
np.random.shuffle(index) # 打乱索引
train_data = x[index]
train_label = y[index]
#分类器
recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.train(train_data, train_label)
# 保存训练数据
recognizer.write('train.yml')
match.py
import cv2
#人脸检测函数
def face_detect_demo(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
face_detector = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
faces = face_detector.detectMultiScale(gray, 1.2, 6)
# 如果未检测到面部,则返回原始图像
if (len(faces) == 0):
return None, None
# 目前假设只有一张脸,xy为左上角坐标,wh为矩形的宽高
(x, y, w, h) = faces[0]
# 返回图像的脸部部分
return gray[y:y + w, x:x + h], faces[0]
#导入训练结果
recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.read('train.yml')#读取前文训练的结果
# 根据给定的人脸(x,y)坐标和宽度高度在图像上绘制矩形
def draw_rectangle(img, rect):
(x, y, w, h) = rect#矩形框
cv2.rectangle(img, (x, y), (x + w, y + h), (255, 255, 0), 2)
# 根据给定的人脸(x,y)坐标写出人名
def draw_text(img, text, x, y):
cv2.putText(img, text, (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, (128, 128, 0), 2)
# 此函数识别图像中的人物并在脸部周围绘制一个矩形及其人名
facelabel = ["yangmi", "liuyifei"]#人物名
def predict(image):
# 生成图像的副本,保留原始图像
img = image.copy()
# 检测人脸区域
face, rect = face_detect_demo(img)#face_detect_demo前面的人脸检测函数
#print(rect)=[x,y,w,h]
# 预测人脸名字
face=cv2.resize(face,(300,300),interpolation = cv2.INTER_AREA)
label = recognizer.predict(face)
print(label)#label[0]为名字,label[1]可信度数值越低,可信度越高(
if label[1]<=50:
# 获取由人脸识别器返回的相应标签的人名
label_text = facelabel[label[0]]
# 在检测到的脸部周围画一个矩形
draw_rectangle(img, rect)
# 标出预测的人名
draw_text(img, label_text, rect[0], rect[1])
# 返回预测的图像
return img
else:
# 在检测到的脸部周围画一个矩形
draw_rectangle(img, rect)
# 标出预测的人名
draw_text(img, "not find", rect[0], rect[1])
# 返回预测的图像
return img
test_img = cv2.imread("test.jpg")
#执行预测
pred_img = predict(test_img)
cv2.imshow('result', pred_img)
cv2.waitKey(0)
cv2.destroyAllWindows()