【保姆级教程】基于opencv+python的人脸识别上课签到系统
一、软件安装及环境配置
1. 安装ide:pycharm
-
进入pycharm官网:https://www.jetbrains.com/pycharm/ ,点击download.

-
选择系统版本windows,选择community版本(因为免费),点击downlad。

-
下载完成后双击开始安装,点击next.

-
选择合适路径(建议除了c以外的其他盘),点击next。

-
勾选所有选项,点击next.

-
点击install,选择jetbrains.

-
等待安装结束,rboot now或 i want manually reboot later随便选一个即可,点击finish.

-
双击打开pycharm,首次打开会出现如下弹窗,勾选方框,点击continue。勾选don’t send


至此pycharm软件安装完毕.
2. 搭建python的环境
-
打开python官方网站:https://www.python.org,点击downlads.

-
进入python下载界面,选择windows.

-
选择下面红框框住的版本:download windows x86-64 executable installer

这可能会下载的非常慢,推荐internat download manager下载器(简称idm),可以加速外网资源的下载,几个线程同时下载提高下载速度(官方说的最多五倍,个人觉得不止),官方地址https://www.internetdownloadmanager.com/download.html.

鼠标移到安装包上,按右键复制地址,打开idm后,新建任务,把下载资源地址复制进去即可.

-
下载完成后,双击python-3.6.5rc1-amd64文件进行安装,切记在选项add python 3.6 to path的框中打钩,然后点击install now进入下一步.

注:安装时一定要自定义安装解释器,因为后续库的安装地址同解释器的安装位置。项目小还好,项目大会把c盘“撑爆”. -
耐心等待,安装完成后会弹出一个界面,点击close.

-
检查python环境是否搭建成功,在windows窗口中按 win+r,打开命令窗口,输入cmd,点击“确定”按钮,在新弹出的命令窗口中输入“python” (或“py”)回车,显示如下界面说明安装成功。


3. 新建项目、安装插件、库
- 打开pycharm,点击新建项目,为新项目命名并选择路径,点击create.

- 安装中文插件,在搜索框中输入chinese,安装中文简体语言包.


安装成功后,点击restart ide重启软件,即可换为中文模式. - 安装项目所需要的库,本项目用到的第三方库:opencv-python、opencv-contrib-python、pillow、
numpy、tk、xlrd、xlwt、xlutils、datetime。
由于外网网速原因,大概率会面临安装失败的问题,此时可以采用国内镜像源,利用pip加速安装.

以opencv-python库为例,打开终端,输入下述命令:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple opencv-python

终端pip安装完后,再打开python解释器安装软件包,速度快如飞雷神.

其余库安装流程类似,此处不做演示.
二、源文件编写
整体架构:

注意:把代码中涉及到的路径换为自己电脑下,否则编译器找不见报错
1. 采集人脸.py
演示:学号为:1111111111;姓名为:iu

首先,下载haarcascade_frontalface_default.xml,并把该xml文件移动到该项目下。下载地址https://gitcode.com/opencv/opencv/tree/master/data/haarcascades?utm_source=csdn_github_accelerator&islogin=1
import cv2
import os
# 定义变量
classifier = cv2.cascadeclassifier(r'd:\hello_world\pythonproject\haarcascade_frontalface_default.xml')
font = cv2.font_hershey_simplex
stu_id = input('请输入你的学号: \n')
stu_name = input('请输入你的姓名: \n')
count = 0
# 建立人脸数据文件夹
if not os.path.exists('data'):
os.mkdir('data')
# 打开摄像头
capture = cv2.videocapture(0)
while capture.isopened():
kk = cv2.waitkey(1)
_,farme = capture.read()
gray = cv2.cvtcolor(farme, cv2.color_bgr2gray)
faces = classifier.detectmultiscale(gray, 1.2, 5)
if len(faces) != 0:
for x, y, w, h in faces:
cv2.rectangle(farme, (x, y), (x + w, y + h), (200, 0, 250), 2)
# center = (x + w // 2, y + h // 2)
# r = w // 2
# cv2.circle(farme, center, r, (0, 250, 0), 2)
cv2.puttext(farme, 'press "s" to save' , (x + w, y + h), font, 1, (200, 0, 250), 2)
if kk == ord('s'):
cv2.imwrite('data/'+str(stu_name)+'.'+str(stu_id)+'.'+str(count)+'.jpg', gray[y:y+h,x:x+w])
count += 1
print('采集了'+str(count)+'张图片。')
cv2.puttext(farme, 'press "q" to quit', (30, 60), font, 1, (200, 0, 250), 2)
cv2.imshow('picture from capture',farme)
if kk == ord('q'):
print('共采集了学号为'+str(stu_id)+'姓名为'+str(stu_name)+'的同学的'+str(count)+'张图片')
break
# 释放摄像头
capture.release()
cv2.destroyallwindows()
操作流程:右键运行’采集人脸’,输入自己的id号(笔者是10位学号),输入姓名首字母(例:张三,输入zs),enter。按s保存采集到的图像,一般采集20张,按q退出(注意:按s和q时必须切换至英文输入法模式)
2. 训练模型.py
import cv2
import numpy as np
from pil import image
import os
create = cv2.face.lbphfacerecognizer_create()
def data_translate(path):
face_data = []
id_data = []
file_list = [os.path.join(path, f) for f in os.listdir(path)]
# print(file_list)
# print(len(file_list))
for file in file_list:
pil_image = image.open(file).convert('l')
np_image = np.array(pil_image, "uint8")
# print(file)
# print(file.split('.'))
# print(file.split('.')[1])
id = int(file.split('.')[1])
# print(file.split('.')[0])
face_data.append(np_image)
id_data.append(id)
return face_data, id_data
print('开始训练模型')
# data_translate(r'data\data')
faces,ids = data_translate(r'd:\hello_world\pythonproject\data')
create.train(faces,np.array((ids)))
create.save('trainer.yml')
print('模型保存成功')
操作流程:右键运行’训练模型’,运行成功后,会生成一个trainer.yml文件.
3. 生成表格.py
# 引入库
import xlrd
import xlwt
from xlutils.copy import copy
# 创建工作簿
nwb = xlwt.workbook()
cjb = nwb.add_sheet('成绩表')
cjb.write_merge(0, 0, 0, 3, '成绩表')
a = ['序号', '学号', '姓名', '成绩', '签名', '签到时间']
for i in range(6):
cjb.write(1, i, a[i])
name = ["iu", "张三", "李四", "王五"]
id = ['1111111111', '2020001111', '2020002222', '2020003333']
b = 0
for a in range(2, 6):
# 写入学号
cjb.write(a, 1, id[b])
# 写入姓名
cjb.write(a, 2, name[b])
cjb.write(a, 0, b+1)
b = b+1
# 保存文件
nwb.save('人脸识别excel.xls')
操作流程:右键运行’生成表格’,可根据自己需求增加name和id个数.
4. 识别签到.py


# 导入库
import cv2
import time
import xlrd
import xlwt
from xlutils.copy import copy
from datetime import datetime
# 创建签名子函数
def sign_in(idx, name):
style0 =xlwt.easyxf('font:height 300,bold on,color_index black', num_format_str= 'mm:dd hh:mm')
style1 = xlwt.easyxf('font:height 300,bold on,color_index blue', num_format_str ='mm:dd hh:mm')
wb = xlrd.open_workbook('人脸识别excel.xls')
nwb = copy(wb)
nbs=nwb.get_sheet(0)
# 签名
nbs.write(idx, 3, name, style1)
# 签时间
nbs.write(idx, 4, datetime.now(), style0)
nbs.col(4).width=256*20
nwb.save('人脸识别excel.xls')
# 加载模型
classfier = cv2.cascadeclassifier('d:\hello_world\pythonproject\haarcascade_frontalface_default.xml')
create = cv2.face_lbphfacerecognizer.create()
create.read('trainer.yml')
# 定义变量
font = cv2.font_italic
starttime = time.time()
id = ('unknow')
name = ('unknow')
count = 0
# 从表格中获取学号、姓名,与识别结果比对
workbook = xlrd.open_workbook('人脸识别excel.xls')
worksheet = workbook.sheet_by_index(0)
stu_id = worksheet.col_values(1)
stu_name = worksheet.col_values(2)
print(stu_id)
print(stu_name)
# 打开摄像头
capture = cv2.videocapture(0)
while capture.isopened():
kk = cv2.waitkey(1)
_, farme = capture.read()
gray = cv2.cvtcolor(farme, cv2.color_bgr2gray)
faces = classfier.detectmultiscale(gray,1.2,5)
if len(faces) != 0:
for x, y, w, h in faces:
cv2.rectangle(farme, (x,y), (x+w, y+h), (180, 120, 220), 2)
gray1 = gray[y:y+h, x:x+w]
label, conf = create.predict(gray1)
print(label, conf)
if conf < 50:
index = [list for list, i in enumerate(stu_id)if i==str(label)]
print(index)
id = (str(label))
name = stu_name[index[0]]
print(id, name)
count = count + 1
else:
id = ('unkown')
cv2.puttext(farme, str(id), (x+w//2-50, y+h+30), font, 1.2, (200, 0, 250), 2)
cv2.puttext(farme, 'press "q" to quit', (30, 60), font, 1.2, (200, 0, 250), 2)
cv2.imshow('picture from capture.', farme)
if kk == ord('q'):
break
if count > 30:
sign_in(index[0], name)
print('学号为:'+str(label)+',姓名为:'+str(name))
break
if time.time()-starttime>30:
print('超时未识别')
break
# 关闭所有窗口,释放摄像头
capture.release()
cv2.destroyallwindows()
操作流程:右键运行’识别人脸’,运行成功后,打开人脸识别签到表.xls查看签到信息.
5. 创建图形界面.py
把上述功能做一个gui界面,集成显示.

# 导入库
import tkinter as tk
import os
from pil import image, imagetk
#创建采集人脸子函数
def cjrl():
os.system('python 采集人脸.py')
#创建训练模型子函数
def xl():
os.system('python 训练模型.py')
#创建识别签到子函数
def sbqd():
os.system('python 识别签到.py')
#创建签到表
def qdb():
os.startfile('人脸识别excel.xls')
#关闭窗口
def gb():
win.destroy()
# 创建窗口
win = tk.tk()
win.title('人脸识别签到系统')
win.geometry('310x500+800+50')
win.configure(bg='#ff8247')
# tk.label(win, text="自动化人脸识别", font=('黑体', 20, 'bold'), bg='#00bfff', fg='white').place(x=10,y=10)
# 设置图片以便使用
img = image.open('d:\hello_world\pythonproject\cat.jpg')
photo = imagetk.photoimage(img)
# 大标题
lab1 = tk.label(win, text="自动化人脸识别", font=('黑体', 20, 'bold'), bg='#00bfff', fg='white').grid(padx=20, pady=10, sticky=tk.w+tk.e)
# 显示图片
# lab2 = tk.label(win, image=photo).grid(padx=20, pady=10, sticky=tk.w+tk.e)
# 按钮
but1 = tk.button(win, text='采 集 人 脸 图 片', activebackground='yellow',command=cjrl, font=('黑体', 10, 'bold'), bg='#00bfff', fg='white').grid(padx=20, pady=10, sticky=tk.w+tk.e)
but2 = tk.button(win, text='训 练 模 型',activebackground='yellow', command=xl, font=('黑体', 10, 'bold'), bg='#00bfff', fg='white').grid(padx=20, pady=10, sticky=tk.w+tk.e)
but3 = tk.button(win, text='识 别 签 到',activebackground='yellow', command=sbqd, font=('黑体', 10, 'bold'), bg='#00bfff', fg='white').grid(padx=20, pady=10, sticky=tk.w+tk.e)
but4 = tk.button(win, text='签 到 表', activebackground='yellow',command=qdb, font=('黑体', 10, 'bold'), bg='#00bfff', fg='white').grid(padx=20, pady=10, sticky=tk.w+tk.e)
but5 = tk.button(win, text='关 闭 窗 口',activebackground='yellow', command=gb, font=('黑体', 10, 'bold'), bg='#00bfff', fg='white').grid(padx=20, pady=10, sticky=tk.w+tk.e)
tk.label(win, text='学号:1111111111 姓名:iu', bg='white', fg='black',font=('楷体',12)).grid(padx=20, pady=10, sticky=tk.w+tk.e)
# tk.label(win, text='字图一体', image=photo ,compound='bottom', bg='white', fg='black',font=('楷体',12)).grid(padx=20, pady=10, sticky=tk.w+tk.e)
win.mainloop()
三、相关函数分析
1.采集人脸
classifier = cv2.cascadeclassifier(r'd:\py-project\pythonproject1\haarcascade_frontalface_default.xml')
faces = classifier.detectmultiscale(gray, 1.2, 5)
2.训练模型
create = cv2.face.lbphfacerecognizer_create()
def data_translate(path):
# 初始化用于存储面部图像数据的列表
face_data = []
# 初始化用于存储与面部图像对应的id的列表(学号)
id_data = []
# 使用os.path.join(path, f)将路径和文件名组合成完整的文件路径
# 列表推导式用于创建包含所有文件完整路径的列表
file_list = [os.path.join(path, f) for f in os.listdir(path)]
# 遍历file_list中的每个文件路径
for file in file_list:
# 打开图像文件,并将其转换为灰度图像'l'模式
pil_image = image.open(file).convert('l')
# 转换为numpy数组,数据类型为"uint8"
np_image = np.array(pil_image, "uint8")
# 提取文件名中的id部分,文件名中第一个'.'之后,第二个'.'之前的部分
id = int(file.split('.')[1])
# 将转换后的图像数组添加到face_data列表
face_data.append(np_image)
# 将提取的id添加到id_data列表
id_data.append(id)
# 返回包含面部图像数据和对应id的两个列表
return face_data, id_data
3.识别签到
style0 =xlwt.easyxf('font:height 300,bold on,color_index black', num_format_str= 'mm:dd hh:mm')
for x, y, w, h in faces:
# 在图像farme上绘制矩形框,框住检测到的人脸区域
# 参数(x, y)是矩形左上角的坐标,(x+w, y+h)是矩形右下角的坐标(注:坐标系在左上角)
# (180, 120, 220)是矩形的颜色(bgr格式),2是线条的粗细
cv2.rectangle(farme, (x, y), (x+w, y+h), (180, 120, 220), 2)
# 从灰度图像gray中截取人脸区域,坐标(x, y)是人脸左上角,宽高为w和h
gray1 = gray[y:y+h, x:x+w]
# 使用人脸识别器create预测截取的人脸区域gray1的身份和置信度,前面create已经read过训练好的yml文件
label, conf = create.predict(gray1)
# 打印预测的标签(身份id)和置信度
print(label, conf)
# 如果置信度小于50,认为识别结果比较可信
if conf < 50:
# 尝试找到与预测标签(create检测出的label)匹配的学生id(excel表格中的stu_id),二者本质上都是学号.
index = [list for list, i in enumerate(stu_id) if i == str(label)]
print(index)
# 假设index列表中有匹配的id,把其转化为字符串格式,取出对应的学生姓名,生成表格.py生成excel默认是字符串格式(如不是,在单元格前+‘按回车)
id = (str(label))
name = stu_name[index[0]]
print(id, name)
# 对识别成功的人脸数量进行计数
count = count + 1
else:
# 如果置信度大于或等于50,认为识别结果不可信,标记为未知
id = ('unknown')
4.创建图形界面
lab1 = tk.label(win, text="自动化人脸识别", font=('黑体', 20, 'bold'), bg='#00bfff', fg='white').grid(padx=20, pady=10, sticky=tk.w+tk.e)
but1 = tk.button(win, text='采 集 人 脸 图 片', activebackground='yellow',command=cjrl, font=('黑体', 10, 'bold'), bg='#00bfff', fg='white').grid(padx=20, pady=10, sticky=tk.w+tk.e)
发表评论