这篇文章由我早期的疲劳检测项目文档整理而来。原文记录了人脸检测、关键点定位、眼睛长宽比和 PERCLOS 的基本流程;这里补充算法原理、参数含义、完整示例代码以及实际使用时容易踩到的问题。
本文实现的是一个便于理解和实验的视觉检测方案,不应替代车辆原厂安全系统、专业驾驶员监测设备或医学判断。
整体思路
程序从摄像头逐帧读取图像,并依次完成以下处理:
- 使用 dlib 的 HOG 正面人脸检测器定位人脸。
- 使用 68 点模型获取人脸关键点。
- 从关键点中取出左右眼轮廓,计算眼睛长宽比 EAR。
- 根据 EAR 判断当前帧中眼睛是否闭合,并用连续帧过滤瞬时抖动。
- 在固定时间窗口内计算闭眼帧占比 PERCLOS。
- PERCLOS 超过阈值时输出疲劳提示,同时绘制关键点和运行状态。
原始项目的流程图如下:

EAR:把眼睛开合程度变成一个数值
在 68 点人脸关键点模型中,每只眼睛由 6 个点表示。设这 6 个二维坐标依次为 p1 到 p6,眼睛长宽比定义为:
1 | EAR = (||p2 - p6|| + ||p3 - p5||) / (2 × ||p1 - p4||) |
分子是眼睛上下边缘的两组距离,分母是眼角之间宽度的两倍。眼睛睁开时 EAR 通常比较稳定;闭眼时上下眼睑靠近,EAR 会快速下降。左右眼通常同步眨动,因此程序取两只眼睛 EAR 的平均值,以减少单侧关键点抖动。
EAR 没有适合所有人的固定阈值。原始流程使用 0.25,这是一个可用的起点,但眼型、摄像头角度、镜片反光和关键点模型都会影响结果。更稳妥的做法是先采集数秒正常睁眼数据,再根据个人基线确定阈值。
1 | def eye_aspect_ratio(eye: np.ndarray) -> float: |
PERCLOS:不要用单帧直接判断疲劳
一次普通眨眼也会产生很低的 EAR,因此“某一帧闭眼”并不等于疲劳。PERCLOS 关注的是一段时间内眼睛处于闭合状态的比例:
1 | PERCLOS = 闭眼有效帧数 / 窗口内有效帧总数 |
严格的 P80 定义关注眼睑遮挡瞳孔超过 80% 的时间比例;本文没有直接测量瞳孔遮挡,而是用 EAR 阈值把每帧简化为“睁眼/闭眼”两种状态,因此这里计算的是适合原型验证的 PERCLOS 近似值。
原始文档中的 ratio = 0.8 可以理解为“窗口内至少有 80% 的时间保持睁眼”;换成闭眼占比,就是当 PERCLOS 超过 0.2 时发出提示。这里直接使用闭眼占比表达,含义更直观。
下面的监测器使用 deque 维护固定长度的滑动窗口。只有连续多帧低于 EAR 阈值才确认闭眼;未检测到人脸的帧不会加入队列,避免把离开画面误判成闭眼。
1 | from collections import deque |
环境准备
示例使用 Python 3.10 及以上版本:
1 | python -m pip install opencv-python numpy dlib |
还需要下载 dlib 官方示例所使用的 shape_predictor_68_face_landmarks.dat,解压后把模型路径传给程序。某些平台安装 dlib 时需要本地 C++ 编译环境和 CMake;如果 pip 构建失败,应优先参考 dlib 的安装说明或使用包含 dlib 的 Conda 环境。
完整摄像头示例
把下面代码保存为 fatigue_detection.py:
1 | from __future__ import annotations |
运行方式:
1 | python fatigue_detection.py \ |
Windows PowerShell 中可以写成单行命令,或者使用反引号代替上面的反斜杠续行。
参数应该怎样调整
EAR 阈值
让使用者自然看向摄像头并保持睁眼 5 到 10 秒,记录 EAR 的中位数作为睁眼基线。再采集几次正常眨眼,观察闭眼谷值。阈值应位于两组数值之间,而不是直接假设所有人都适合 0.25。
连续闭眼帧数
原始流程使用 3 帧。在 30 FPS 下约为 100 毫秒,在 15 FPS 下则约为 200 毫秒,因此同一个帧数在不同摄像头上代表不同时间。正式实现最好按时间计算,例如把确认时长设为 100 到 200 毫秒,再根据实际 FPS 换算帧数。
PERCLOS 窗口与阈值
示例使用 30 秒窗口和 0.20 阈值,目的是复现原始文档中“睁眼比例低于 80%”的逻辑,并不代表通用标准。窗口太短时对一次长眨眼过于敏感,太长则会延迟告警。阈值应通过目标场景的数据验证,同时记录误报率和漏报率。
常见误判及改进方向
- 未检测到人脸:不要直接当作闭眼,应暂停 PERCLOS 采样,并单独提示调整位置。
- 侧脸或低头:二维 EAR 会受到较大头部姿态影响,可以加入姿态估计或使用三维关键点。
- 眼镜反光与弱光:增加补光、使用红外摄像头,或换用在目标环境中训练的眼睛状态模型。
- 个体差异:启动时做个人 EAR 标定,通常比全局固定阈值更可靠。
- 单一指标不足:可进一步融合闭眼持续时间、眨眼频率、哈欠、头部姿态和方向盘行为。
- 性能问题:可以缩小检测图像、隔帧执行人脸检测,并在相邻帧使用跟踪器维护人脸区域。
结果与边界
EAR 的优势是计算量小、结果可解释,PERCLOS 又能把单帧信号转化为时间窗口统计,因此二者很适合教学、原型验证和资源受限设备。但它们依赖关键点质量,并且“眼睛长时间闭合”只覆盖疲劳表现的一部分,不能识别所有形式的注意力下降。
真正部署前,应在目标摄像头、安装角度、光照、眼镜类型和人群上建立测试集,分别统计正常、眨眼、低头、侧脸、遮挡和真实疲劳片段。只有经过这些验证,阈值才有实际意义。