近期着手开展一个关于实验楼的门禁项目,老板表明预算整体绝对不可以超出200块。
我还得用把人脸识别弄出来,源码还得自己写。
说实话,刚开始觉得是不是有点扯。
但搞了两周,现在门已经能刷脸开了,总成本87块6。
还没谈到源码的时候,我要先讲讲,我为何没去选那些具备高精度特性的深度学习方法。
不是我不想,是我那破树莓派2B根本跑不动。
那算法到底咋选?LBPH凭什么上?
首先,我尝试运用Haar级联来开展人脸检测,它所具备的速度着实非常快,然而,其误检率相对较高,竟然能够将墙上的插座识别成是人脸。
其后,我再度更替了DNN模块,须知Caffe – SSD的精度是颇高的,然而,我的树莓派却径直卡顿得宛如播放卡壳的PPT了。
最后定下来的组合是:Haar检测 + LBPH识别。
基于局部纹理特征的LBPH,对光照变化较为鲁棒,对表情变化也是比较鲁棒的,其参数数量少,训练速度快,只需一个小样本数据集就能运行得颇具样子。
但说句实话,光线差的时候识别率会掉,这是它的硬伤。
晚上测试,把走廊灯关一半,它就不认我了。
所以我后来加了一个逻辑:光线不足时先触发暖光补光。
– 你要是也卡在那个报错上,我这有救
写源码的时候我踩过一个巨坑。
我费尽心力写完了,一运行,直接弹出属性错误标明:模块‘cv2.face’没有属性‘reate’。
我当时差点把键盘摔了。
半小时都在百度,才晓得,cv2.face模块并不在 – 主包里,而是要安装 – – 。
而且主包和扩展包的版本必须严格匹配,版本对不上照样报错。
我的修复方案是:
先pip – –,然后指定版本重新安装,open cv 选4.10.0.84,也装一模一样的版本。
装完再确认一下cv2.face模块有没有加载成功:
首先,导入cv2,接着,打印cv2是否有属性face。
输出True的时候我差点哭出来。
硬件怎么配?几十块钱搞定
我的配置单:
二手的树莓派 2B,五十元,旧手机拆解下来的 USB 摄像头,折价二十块,七点六元的 5V 继电器模块,十元的电磁锁。
电磁锁接继电器,继电器接GPIO 18,正负极别接反。
摄像头呢,我是把它固定在了门的上沿之处,并且使其向下造成了15度的倾斜角度,如此这般,不管是有着不同身高的人走过来,那张脸都能够实现对上焦的情况。
另有个经验要讲一下:不要选用价格昂的摄像头,因为一旦分辨率过高那么处理速度就会变慢 ,我最终确定了的灰度图,帧率好不容易才维持在12至15fps。
训练数据那点事儿
训练集是我自己在实验室拍的,每个人拍了30张。
留心,角度的种类要丰富:有正脸的角度,有左侧偏离正方向三十度的角度,有右侧偏离正方向三十度的角度,有头部向下低垂的角度,有头部向上扬起的角度——后面这三种角度,每种角度都要留存若干张。
光线照明的条件也要变:日光灯、暗光、逆光,每种都拍几张。
照片统一缩放到,转灰度,做直方图均衡化。
训练代码不复杂,核心就几行:
创建一个对象,此对象是通过cv2的一个相关方法实现的,该方法用于创建LBPH人脸识别器。得到的这个对象被命名为。
进行训练,针对的是faces,使用的是np.array()这一数据。
那个识别器,进行保存操作,并把内容保存到名为’.yml’的文件之中。
有这样一个细节存在于此,为由0起始的整数,每个人所拥有的标签其对应关系需逐个对应,无一遗漏。
– 识别那块的代码结构
实时识别的核心逻辑是:
cap = cv2.(0)
while True:
ret, frame = cap.read()
从彩色的帧,通过cv2.这种颜色转换方式,将其转换为灰度的gray ,这一操作借助cv2.函数实现。
脸的数量 = 脸部级联分类器来检测多尺度(灰度图像,1.1的比例因子,5的最小邻居数),得到的数据放入“脸的数量”变量。
for (x, y, w, h) in faces:
= gray[y:y+h, x:x+w]
那识别器预测感兴趣区域的灰度值后,所得到的标签,以及置信度,被分别赋值给了标识,还有可信度。
if < 60:
设置通用输入输出引脚的输出值为低电平,此操作是用于打开门。
time.sleep(2)
通过GPIO.函数,将引脚设置为GPIO.HIGH状态,以此来实现关门的操作。
else:
print(“不认识你”)
那些参数的阈值,我进行了许多回的调整,其中60这个数值是相对最为适宜恰当的。
设太低了陌生人轻易就混进去,设太高了认识的人也经常进不来。
– 活体检测怎么做?照片攻击头秃
刚开始的时候我天真了,以为跑通识别就算完事了。
就有那么一天,同事把我手机里的自拍照片,拿到摄像头跟前飞快地晃那么一下,然后,嗞的一声,门开了。
我当时那个心情。
后来补了活体检测,用的眼动法。
采集画面的动作需连续进行,要采集好几帧画面,之后计算眼睛纵横比EAR相关的值呀,要是EAR一直都没有出现变化,那就表明是照片,只有当有明显的眨眼变化情况时,才能够予以放行呢。
代码实现大致这样:
def (eye):
A等于,距离的欧几里得度量,在,眼睛数组的第一个元素,与,眼睛数组的第五个元素,之间。
B等于,欧几里得距离函数,从索引为2的元素,与索引为4的另一元素,计算得出的结果。
C等于,通过欧几里得距离计算方式,计算所得的,第一个眼睛位置与第三个眼睛位置之间的距离。
ear = (A + B) / (2.0 * C)
ear
连续5帧EAR值都低于阈值,就不开门。
这个针对照片实施的攻击大体上能够阻拦得住,然而对于视频回放进行的攻击,我尚未完成编写,暂且搁置到以后再说了。
断网也能跑吗?能,全在本地
有人问过我这系统是不是还要联网。
不联网。
在树莓派的SD卡里存放着训练数据,同时特征库也存于其中, 进行识别的过程并不需要与服务器进行通信。
接线虽然断网,但安全起见我在源码里加了一层。
加密存储的授权人脸库,识别之前先解密特征数据。
你拿什么源码?代码放了
老实讲,该项目的源代码在所运用的数量层面大概是从一百五十行及至两百行的范围区间内,其是将几个开源教程之中的精粹予以汇总融合而成的。
我把代码都整理好上传了,地址在下面。
repo包含了:
人脸检测部分(Haar级联)
人脸识别部分(LBPH模型训练和实时预测)
门禁控制部分(GPIO和继电器驱动)
活体检测模块(眼动法)
日志记录系统
训练数据和模型文件也都放进去了。
需要跑起来的步骤也很简单:
1. 先将环境安装好,再把依赖安装完成, – – 版本需要固定为特定版本,不能随意变动,要锁定住这个版本。
2. 数据集放到文件夹下,每个人一个子文件夹
3. 跑.py生成.yml
4. 跑.py主程序,刷你的脸开门
收工。

