Raspberry Pi:使用OpenCV和dlib进行面部标志+嗜睡检测
一、环境搭建以及简单介绍
安装树莓派的GPIO接口环境
pip install RPi.GPIO
pip install gpiozero
除此之外你还应该安装了NumPy,dlib,和imutils
pip install numpy
pip install dlib
pip install imutils
opencv自行安装
检测是否环境已安装成功
运行Python解释器
python
导入各个库,若成功则像以下显示状态
>>> import RPi.GPIO
>>> import gpiozero
>>> import numpy
>>> import dlib
>>> import cv2
>>> import imutils
首先,我们将应用OpenCV的Haar级联来检测图像中的人脸,归结为找到帧中人脸的边界框(x,y)-坐标。
给定脸部边界框,我们可以应用dlib的面部界标预测器来获得68个显着点,这些显着点用于定位眼睛,眉毛,鼻子,嘴巴和下巴线:
dlib的68个面部界标是可索引的,这使我们能够使用简单的Python数组切片提取各种面部结构
给定与眼睛相关的面部标志,我们可以应用Soukupová和Čech’s在其2017年论文《实时面部眨眼检测中使用面部标志》中引入的眼睛纵横比(EAR)算法:
在左上角,我们的眼睛完全张开,并绘制了眼睛面部轮廓。然后在右上角,我们的眼睛闭上了。然后,底部绘制了随时间变化的眼睛纵横比。如我们所见,眼睛的纵横比是恒定的(表示眼睛睁开),然后迅速下降到接近于零,然后再次增大,表明眨眼了
在睡意检测器的情况下,我们将监视眼睛的宽高比,以查看该值是否下降但没有再次增加,从而暗示驾驶员/用户已经闭上了眼睛。
一旦实现,我们的算法将从提取眼睛区域时定位面部界标开始:
如果眼睛的宽高比在足够长的时间内低于预定义的阈值(表示驾驶员/用户感到疲倦),则最后发出警报:
二、代码编写
具有OpenCV和dlib的Raspberry Pi上的实时睡意检测器
在您喜欢的编辑器或IDE中打开一个新文件并命名 pi_drowsiness_detection.py。从那里开始,让我们开始编码:
第1-9行处理我们的导入-确保在虚拟环境中安装了每个导入。
从那里定义一个距离函数:
在11-14行中,我们定义了一个便利函数,用于使用NumPy计算欧几里得距离。欧几里得可以说是最著名的并且必须使用的距离度量。欧几里得距离通常被描述为“乌鸦飞过”两点之间的距离。
现在,让我们定义我们的眼睛长宽比(EAR)函数,该函数用于计算垂直眼睛界标之间的距离与水平眼睛界标之间的距离的比率:
睁开眼睛时,返回值将大致保持不变,眨眼时返回值将减小至零。如果闭上眼睛,则眼睛的纵横比将保持较小的恒定值。
从那里,我们需要解析我们的命令行参数
我们在第33-40行定义了两个必需的参数和一个可选的参数:
- 级联 :用于人脸检测的Haar级联XML文件的路径。
-形状预测器 :dlib面部界标预测器文件的路径。 - 警报 :一个布尔值,指示在检测到困倦时是否应使用TrafficHat蜂鸣器。
这俩 - 级联 和 -形状预测器文章末尾 的“下载”部分中提供了这些文件。
如果 - 警报 标记已设置,我们将设置TrafficHat:
如第43-46行所示,如果提供的参数大于0,我们将导入TrafficHat函数来处理蜂鸣器警报。
让我们还定义一组重要的配置变量
第52和53行上的两个常数定义了EAR阈值,必须闭眼的连续帧数才能分别视为昏昏欲睡。
然后,我们初始化帧计数器和警报的布尔值(第57和58行)。
从那里,我们将加载Haar级联和面部界标预测器文件:
64行在这里,我们使用了更快的检测算法(Haar级联),同时又提高了准确性。Haar级联比dlib的面部检测器(基于HOG +线性SVM)更快,使其成为Raspberry Pi的绝佳选择。
第65行没有更改,我们在其中加载dlibshape_predictor 同时提供文件的路径。
接下来,我们将为每只眼睛初始化脸部界标的索引:
在这里,我们提供数组切片索引,以便从面部界标集中提取眼睛区域。
现在,我们准备开始我们的视频流线程
如果您使用的是PiCamera模块,请确保注释掉第74行并取消注释 第75行,以将视频流切换到Raspberry Pi摄像机。否则,如果您使用的是USB相机,则可以保持不变。
我们有一秒钟的睡眠,因此相机传感器可以预热。
从那里开始,让我们遍历视频流中的帧
我们读取一帧,调整大小(以提高效率),然后将其转换为灰度(第83-85行)。
然后,使用第88-90行的检测器检测灰度图像中的人脸。
现在让我们遍历检测:
第93行开始一个冗长的for循环,在这里将其分解为几个代码块。首先,我们提取坐标和直肠 检测。然后,在96和97行中,我们构造了一个dlib长方形 对象使用从Haar级联边界框中提取的信息。
从那里,我们确定脸部区域的脸部界标(第102行),并将脸部界标(x,y)坐标转换为NumPy数组。
鉴于我们的NumPy数组, 形状 ,我们可以提取每只眼睛的坐标并计算EAR:
利用眼睛界标的索引,我们可以将 形状 阵列以获得每只眼睛的(x,y)坐标(第107和108行)。
然后,我们在109和110行上为每只眼睛计算EAR 。
Soukupová和Čech建议将两只眼睛的长宽比平均在一起以获得更好的估计(第113行)。
下一个块严格用于可视化目的:
我们可以通过使用以下方式可视化框架中的每个眼睛区域 cv2.drawContours 并提供 cv2.convexHull 计算每只眼睛(第117-120行)。这几行代码非常适合调试我们的脚本,但是如果您要制作没有屏幕的嵌入式产品,则不需要。
从那里,我们将检查我们的眼睛长宽比(耳朵 )和帧计数器(柜台 ),看看眼睛是否闭上,同时响起警报以提醒昏昏欲睡的驾驶员(如果需要):
在第124行,我们检查了耳朵 反对这 EYE_AR_THRESH —如果小于阈值(闭眼),我们将增加 柜台 (第125行),然后检查它是否已经合上足够连续的眼睛以发出警报声(第129行)。
如果没有打开警报,我们将其打开几秒钟以唤醒昏昏欲睡的驱动程序。这是在行136-138上完成的。
(可选)(如果您正在通过屏幕实现此代码),可以像在141和142行中所做的那样在框架上绘制警报。
这使我们想到了 耳朵 不少于 EYE_AR_THRESH -在这种情况下,我们重置了 柜台 设为0,并确保我们的闹钟已关闭(第146-148行)。
我们几乎完成了–在我们的最后一个代码块中,我们将EAR绘制在 框架 ,显示 框架 ,并进行一些清理
如果您要与屏幕集成或进行调试,则可能希望像我在第153和154行中所做的那样,在框架上显示计算出的眼睛纵横比。该帧显示在第157和158行的实际屏幕上。
当按下键盘上的“ q”键时,程序将停止(第157和158行)。
最后,我们通过关闭所有打开的窗口并停止视频流进行清理(第165和166行)。
三、运行及代码下载
要运行该程序,只需执行以下命令:
$ python pi_detect_drowsiness.py --cascade haarcascade_frontalface_default.xml \
--shape-predictor shape_predictor_68_face_landmarks.dat --alarm 1
链接: 完整代码下载(包含haarcascade_frontalface_default.xml、shape_predictor_68_face_landmarks.dat).