需求与设计
使用面部识别技术,识别进出重要通道的人员,并对人员进出动作进行记录。在人员进出时,在摄像机前采集画面,使用采集到的画面与历史记录对比,如果人员已经存在出入记录,追加一条记录即可;如果不存在则新建记录。
##技术与实现
核心技术
使用Python来完成整个程序的编写,面部识别采用python开源库face_recognition, 基于dlib(C++实现)。
使用opencv来捕获视频流,支持本地摄像头和网络摄像头。
使用QThread创建线程更新每一帧图片到PyQt界面。
数据存储
使用Sqlite3作为单机版数据库,同时支持MySQL数据库。
使用sqlite3进行数据存储。
前端界面
使用PyQt4作为前端界面,使用绝对布局。
显示监控画面
opencv捕获摄像机视频流
Opencv捕获视频流,主要使用opencv的videoCapture方法, 传入摄像头物理地址(0-99)或者网络视频流地址。
- 安装(Ubuntu):
sudo apt install
- 使用(Python2.7 代码示例):
1 | import cv2 |
PyQt4子线程控制UI线程更新
使用场景一:子线程获取视频流的frame,主线程更新frame
#####使用QT提供的线程类:QThread
使用opencv获取视频流之后,应在UI线程中更新获得的每一帧。
可以自定义一个控件,集成自QLabel,然后自定义信号,这个信号将会在自线程里被触发,在主线程里执行。
具体实现方法:
1 | class VideoPlayer(QtGui.QLabel): |
这样主线程(UI线程已经做好了所有的准备工作,接下来就是在子线程内获取视频的每一帧,然后发送信号过来)
创建一个线程类,继承QtCore.QThread
1 | #其实就是上面opencv的方法放在一个线程内 |
这样就可以在前端播放画面了。
使用场景二:视频的截屏,在子线程里截屏并保存到本地
使用Python原声的threading.thread创建线程,只想简单工作
截屏的时候应当在界面显示:准备,3, 2, 1 的提示,这样的提示每一次间隔1秒钟,以便于给用户3秒的调整时间。 这样的工作必须在子线程内执行,否则在主线程内执行会阻断UI线程更新视频流的业务,造成视频卡顿。
实现方法: 首先创建一个用于显示提示消息的Label,然后在子线程里更新Label的text即可。
1 | from PtQt4 import QtGui, QtCore |
PyQt4信号的另一种定义方法
在子线程内打开新的窗口
定义信号,初始化的时候绑定,在子线程内调用
1 | class MainWindow(QtGui.QMainWindow): |
人脸检测和比对
使用face_recognition检测图片内有没有人脸
在进行人脸识别的时候,首先要从图片内找到人脸,有时候图片内根本没有人脸,有时候由很多张脸,都需要进行判断
安装face_recognition模块
安装依赖
cmake 负责编译dlibsudo apt install cmake
libboost dlib的依赖sudo apt install libboost1.61-dev
dlib 机器学习类库sudo apt install libdlib-dev
安装模块
pil python的图像处理模块pip install pillow
numpy 机器学习核心模块,无需多言pip install numpy face_recognition
使用face_recognition检测照片内的人脸
主要是一些API的使用,后台的方法都在dlib中实现,python只是调用而已
上一张效果图:
这张图片直接检测出来,两个人脸!!! 但是,其实我们只需要一张人脸。
1 | #face_locations 就是对一张照片中人脸的定义,本质上是一个数组 |
使用face_recognition进行人脸的比对
在人员数据库中存储了很多的人脸,当用户刷脸之后,要对人脸进行比对,确定用户身份。
人脸比对的API:
face_recognition.api.compare_faces(known_face_encodings, face_encoding_to_check, tolerance=0.6)
Compare a list of face encodings against a candidate encoding to see if they match.
Parameters:
known_face_encodings
– A list of known face encodings
face_encoding_to_check
– A single face encoding to compare against the list
tolerance
– How much distance between faces to consider it a match. Lower is more strict. 0.6 is typical best performance.
Returns:
A list of True/False values indicating which known_face_encodings match the face encoding to check
- 参数1: 需要数据库内的所有人脸数据组成的数组(
know_face_encodings
) - 参数2: 需要未知人脸的数据(矩阵): (
face_encoding_to_check
) 严格程度:
tolerance
严格程度从0.1 到1 越来越宽松,数值设置越大约有可能识别错误,数据太小由难以识别到。具体实现的代码:
1 | def recognite(unknow_img): |
总结
接到任务的第一反映
在刚开始接到这个任务的时候,其实是很震惊的,因为作为机器学期或者人工智能的内行人来讲,我很清楚人脸识别意味着什么。(….意味着算法,预测模型,训练模型,计算机图形学),总之这就是一个经典的课题,要从头实现绝非一个人一年两年就能出成果的。但是好在前人有很多经验!
最初的计划
额!最初的想法是自己实现人脸比对的算法, 因为之前使用knn算法随手写数字进行过识别,是一个典型的监督学分类案例,具体的方法就是将图片转换为矩阵,再对矩阵进行归一化,使用欧式距离公式来计算出来n维空间中这些点的距离,然后根据距离它最近的几个点进行概率判断,从而进行分类。但是人脸识别这样做根本不可行,人脸识别中既有回归分析又有分类,还是很棘手的。
第三方SDK
经过查阅资料发现,使用第三方SDK还是比较靠谱的,目前主流的由opencv和dlib,这里选择了dlib,因为dlib的面部识别的密度比较大,所以精准度会高一点,但是项目中还是使用了Opencv来进行视频流的获取。
最终结果
本来打算在windows环境下使用c#实现,但是考虑到开发效率, 决定还是用python开发比较靠谱。但在此之前并没有python GUI程序的开发经验,所以从头学习了python的QT开发, 开发环境选择在linux下,但是QT跨平台,今后也可在windows平台下部署,不过一想到windows下复杂的软件和类库安装过程,我就呵呵了,决定运行环境依旧在Linux下,避免电脑中毒导致系统崩溃(这个以前有血的教训,所以强烈建议使用Linux来运行重要系统)。
经过三天两夜的开发, 从零开始实现了人脸识别的程序, 测试之后还比较稳定,关于精准度的问题,这个还是需要根据实际环境来调试,并且也需要在拍照的时候做好用户引导。因为,天知道以后有多少人会被录在数据库内,而且精准度又不能太低,所以必须做好使用人员的培训,确保不会认错人,或者认不出来人。 如果单位的人比较少,大可以慢慢采集原始数据,正脸,侧脸,各种角度表情都可以存在数据库进行比对,但是这个程序要检测的对象是随机的,不是固定的那么几个人,但从目前测试的结果来看,正常使用,距离摄像头2到4米的距离拍照,基本上不会再误报,不遮挡眼睛,不吐舌头,微笑,大笑都可以正确识别。