基本介绍
项目分为3个程序,人脸照片录入、训练数据、人脸识别。获取摄像头画面,检测并保存人脸部分,用户输入人名,数据经过训练后,能够通过摄像头识别人脸,将人名显示到摄像头画面上。
模块介绍
- opencv-python:人脸的检测、识别,控制摄像头,图像处理
-
opencv-contrib-python:opencv的扩展模块,人脸识别的模型
-
os:操作文件和目录
-
numpy:只用了转数组
人脸录入程序
- 程序目标:为下一步训练数据提供需要的数据
- 程序分析:训练数据的函数使用 cv.face.LBPHFaceRecognizer_create().train()
- 第一个参数需要一组人脸信息,我们在本程序中检测并保存人脸部分图像
- 第二个参数是上一个参数中一组人脸对应的编号,由于最后我们显示的是人名,所以我们需要编号、人名、人脸图像三者一起存下来,给人脸图像的名字规则为:编号.人名.jpg
cv.CascadeClassifier(CLASSIFIER_MODEL)中的CLASSIFIER_MODEL为opencv提供的人脸级联分类器,路径为:venv/Lib/site-packages/cv2/data/haarcascade_frontalface_alt2.xml
import cv2 as cv
import os
def img_extract_faces(img):
"""
从图片中提取人脸部分,彩色图像转成灰度图像,减少计算量
:return: 返回人脸位置,灰度图像
"""
gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY)
return cv.CascadeClassifier(CLASSIFIER_MODEL).detectMultiScale(gray), gray
def box_faces_show(faces, img):
"""
框出人脸并展示图像
"""
for x, y, w, h in faces:
cv.rectangle(
img=img, pt1=(x, y), pt2=(x + w, y + h),
color=(0, 255, 0), thickness=GRAPH_THICKNESS
)
cv.imshow(INPUT_FACE_WINDOW_NAME, img)
def get_image_name(name):
"""
图片命名:编号.姓名.jpg。编号递增,重复录入的覆盖原照片
"""
name_map = {f.split('.')[1]: int(f.split('.')[0]) for f in os.listdir(IMG_SAVE_PATH)}
return IMG_SAVE_PATH + '/' + str(name_map.get(name, max(name_map.values() or [0]) + 1)) + "." + name + ".jpg"
def save_face(faces, img, name):
"""
截取人脸部分的照片保存
:param faces: 人脸位置数据
:param img: 原图像
:param name: 用户输入的名字
:return:
"""
if len(faces) == 0:
print("没有检测到人脸,请调整")
return
if len(faces) > 1:
print("检测到多个人脸,请调整")
return
x, y, w, h = faces[0]
cv.imwrite(get_image_name(name), img[y:y + h, x:x + w])
print("录入成功,按q键退出")
# 录入人名
name = input("请输入要录入的人名: ")
print("姓名输入完毕,请按q退出,请按c保存")
# 打开摄像头
capture = cv.VideoCapture(0)
if not capture.isOpened():
print("摄像头打开失败")
# 循环每帧画面
while True:
ret, frame = capture.read()
if ret == 0:
print("摄像头捕捉失败")
break
# 提取并框出人脸,展示画面
faces, _ = img_extract_faces(frame)
box_faces_show(faces, frame)
# 按 q 键退出,按 c 键保存
k = cv.waitKey(1)
if k == ord('q'):
break
if k == ord('c'):
save_face(faces, frame, name)
# 释放资源
capture.release()
cv.destroyAllWindows()
训练数据程序
- 程序目标:训练并保存数据,为识别人脸提供训练数据
- 程序分析:读取上一步录入的全部人脸图像和图像的文件名,提取文件名中的编号部分
# 获取图片路径
image_paths = [os.path.join(IMG_SAVE_PATH, f) for f in os.listdir(IMG_SAVE_PATH)]
# 读取图片和编号
faces = [cv.imread(image_path, 0) for image_path in image_paths]
img_ids = [int(f.split('.')[0]) for f in os.listdir(IMG_SAVE_PATH)]
# 训练数据
recognizer = cv.face.LBPHFaceRecognizer_create()
recognizer.train(faces, np.array(img_ids))
# 保存训练数据
recognizer.write(TRAIN_DATA_SAVE_PATH)
人脸识别程序
- 程序目标:摄像头中的人脸能够标识出对应的人名
import os
import cv2 as cv
# 人脸录入程序中定义过的函数不再列出
def mark_faces_show(faces_names, img):
for x, y, w, h, color, name in faces_names:
cv.putText(
img=img, org=(x, y), text=name,
fontFace=cv.FONT_HERSHEY_SCRIPT_SIMPLEX, fontScale=0.75,
color=color, thickness=1
)
cv.circle(
img=img, center=(x + w // 2, y + h // 2), radius=w // 2,
color=color, thickness=1
)
cv.imshow(RECOGNITION_FACE_WINDOW_NAME, img)
def recognize_face(faces, recognizer, gray, number_dict):
faces_names = []
for x, y, w, h in faces:
img_id, confidence = recognizer.predict(gray[y:y + h, x:x + w])
color, name = get_color_name(confidence, number_dict[img_id])
faces_names.append((x, y, w, h, color, name))
return faces_names
# 加载模型和训练数据
recognizer = cv.face.LBPHFaceRecognizer_create()
recognizer.read(TRAIN_DATA_PATH)
# 标签:名字
number_dict = {int(f.split('.')[0]): f.split('.')[1] for f in os.listdir(IMG_SAVE_PATH)}
# 打开摄像头
capture = cv.VideoCapture(0)
if not capture.isOpened():
print("摄像头链接失败")
# 循环提取每帧画面
while True:
ret, frame = capture.read()
if not ret:
print("读取帧率失败")
break
# 提取并框出人脸
faces, gray = img_extract_faces(frame)
# 识别并展示人脸
faces_names = recognize_face(faces, recognizer, gray, number_dict)
mark_faces_show(faces_names, frame)
if cv.waitKey(1) == ord('q'):
break
# 释放资源
capture.release()
cv.destroyAllWindows()