基于 OpenCV & face-recognition 的实时人脸识别与身份验证
项目地址:github
一、系统需求设计
1. 系统概述
本人工智能人脸识别系统旨在实现对人脸图像或视频流中的人脸进行精准识别、验证与分析,可以应用于安防监控、门禁系统、人员考勤、身份认证等多个领域,以提高安全性与管理效率。
2. 功能概述
1). 人脸录入与图像编码
2). 人脸识别与身份验证
3). web端实现上述功能
二、系统功能模块介绍
核心环境配置:
模块 | 版本 |
---|---|
python | 3.12.4 |
opencv-python | 4.10.0.84 |
face-recognition | 1.3.0 |
Pillow | 10.0.0 |
numpy | 2.1.2 |
Flask (web需要) | 3.1.0 |
项目架构如下:
1 | Face_recognition_local |
1 | Face_recognition_web |
camera.py:优化后的opencv视频捕获
fps.py:用于展示当前视频画面fps
haarcascade_frontalface_default.xml:opencv提供的预训练集,用于捕获人脸
trained_model.pkl:录入的人脸图片编码后的文件
training.py:用于将jpg图片编码为.pkl
person:存放人脸图片
main.py:主程序
app.py:web后端
templates:web前端文件夹
index.html:web前端
三、系统实现
系统核心流程图
训练模型——training.py
1 | import os |
分段解析:
-
读取person文件夹中.jpg文件,其中文件名格式为
姓名+编号.jpg
,如张三1.jpg
1 | # 读取person文件夹中的图像和姓名 |
-
使用face-recognition编码图片
1 | # 使用face-recognition加载图像并进行编码,并处理文件名中的编号 |
在person文件夹中放入目标人脸的jpg图片后运行脚本,得到模型trained_model.pkl
经测试,每人9个图片识别准确率为 65%-90%
主流程
启动摄像头——camera.py
来源:基于cv2.VideoCapture 和 OpenCV 得到更快的 FPS之文件篇
主要优化函数:cv2.VideoCapture
.read
方法是一个阻塞操作,通过将这些阻塞 I/O 操作移至单独的线程并维护解码帧队列,我们实际上可以将 FPS 处理速率提高 52% 以上
1 | from threading import Thread, Lock |
CameraThread
类继承自 Thread
类,可以在单独的线程中捕获摄像头图像
-
初始化方法
__init__
:
1 | def __init__(self, kill_event, src=0, width=320, height=240): |
-
kill_event
:用于停止线程的事件对象。 -
src
:摄像头索引,默认值为 0。 -
width
和height
:视频帧的宽度和高度。 -
self.stream
:创建一个视频捕获对象。 -
self.stream.set
:设置视频帧的宽度和高度。 -
self.grabbed
和self.frame
:读取第一帧图像。 -
self.read_lock
:创建一个锁对象,用于线程同步。
-
更新方法
update
:
1 | def update(self): |
-
self.stream.read()
:读取一帧图像。 -
self.read_lock.acquire()
和self.read_lock.release()
:在更新self.grabbed
和self.frame
时加锁和解锁,以确保线程安全。
-
读取方法
read
:
1 | def read(self): |
-
self.read_lock.acquire()
和self.read_lock.release()
:在读取self.frame
时加锁和解锁,以确保线程安全。 -
self.frame.copy()
:返回当前帧的副本。
-
运行方法
run
:
1 | def run(self): |
-
while not self.kill_event.is_set()
:循环运行,直到kill_event
被设置。 -
start_time
和finish_time
:记录每次循环的开始和结束时间。 -
dt
:计算每次循环的时间差。 -
ms
:将时间差转换为毫秒。 -
time.sleep((time_cycle - ms) / 1000.0)
:如果循环时间小于time_cycle
,则延时以控制帧率。
fps计算——fps.py
1 | import time |
-
获取当前时间戳
current_time
。 -
计算当前帧率
self.fps
$$
fps = {1\over(current_time-self.prev_time)}
$$
-
更新
self.prev_time
为当前时间戳current_time
。 -
返回计算得到的帧率
self.fps
。
人脸检测——main.py
1 | import cv2 |
web实现——app.py & index.html
app.py
app.py是main.py使用Flask框架后的后端服务
1 | from flask import Flask, render_template, Response |
前端页面index.html
1 |
|
四、系统效果
预先使用本人9张照片进行训练
CPU | i9-14900HX |
---|---|
内存 | 32GB |
显卡 | RTX4060 |
平均帧率:2.6
平均准确率:70%
五、改进历程与不足
在刚开始制作时,并没有使用face-recognition训练模型进行人脸验证,采取的验证手段的为读取文件夹内所有图片,寻找与捕获人脸匹配度更高的图片。发现每次验证都要遍历文件夹内所有图片,严重影响系统性能(以至于卡死),于是对代码进行重构,使用face-recognition训练模型,将捕获的人脸与模型对比,大大缓解了性能问题。同时,由于cv2.VideoCapture的read方法阻塞,性能仍然不佳,搜集资料后采用一位博主的方法,将这些阻塞 I/O 操作移至单独的线程。
虽然系统已经能正常运行,但帧率仍然很低,还需要进一步优化。除此之外,目前系统使用的opencv官方提供的分类器 haarcascade_frontalface_default.xml
对于捕获正脸方面较优,但捕获其他方向和复杂表情方面效果很差。需要训练一个新的分类器来适应更复杂的环境。
六、后续优化
根据实际情况来看,并不需要每一帧都检测人脸,因此可以通过增加检测人脸间隔来提升流畅度。
大致思路:
1 | detection_interval = 5 //间隔帧率 |
此外,还可以将人脸检测放到独立的线程中,避免阻塞线程
结合以上两点,创建新的python文件 FaceDetector.py
,主程序通过在新的线程调用人脸检测来缓解卡顿
1 | # FaceDetect.py |
1 | #main.py |
这样修改后帧率大大提高,但是出现了框选人脸位置错误的问题,如下图:
人脸位置和框选位置刚好对称
原因是main.py中通过 frame = cv2.flip(frame, 1)
进行了镜像处理,FaceDetect.py 中调用 camera.py 后没有镜像处理,返回的人脸位置是没有经过处理的人脸位置,所以可以在 FaceDetect.py 中添加 frame = cv2.flip(frame, 1)
,或者删除 main.py 中的镜像处理。
1 | #添加镜像处理后的FaceDetect.py |
改进效果比对如下
-
改进前无人脸
-
改进前有人脸
-
改进后无人脸
-
改进后有人脸
可见通过添加新的线程和增加间隔的改进效果非常显著。
参考资料:
[1] OpenCV Tutorials: https://docs.opencv.org/4.x/d9/df8/tutorial_root.html
[2] face-recognition: https://github.com/ageitgey/face_recognition
[3] 基于cv2.VideoCapture 和 OpenCV 得到更快的 FPS之文件篇: https://blog.csdn.net/weixin_43229348/article/details/122688684
[4] OpenCV 中文文档: https://apachecn.github.io/opencv-doc-zh/#/
[5] Flask框架入门教程: https://blog.csdn.net/wly55690/article/details/131683846