摘要
驾驶疲劳检测是计算机视觉项目中非常适合做“项目实战交付”的方向。它不需要像目标检测项目那样准备大量标注框,也不需要训练一个庞大的深度学习模型,而是可以利用成熟的人脸关键点模型提取眼睛、嘴部和头部姿态信息,再通过时间序列规则判断驾驶员是否存在闭眼、打哈欠、视线偏离和专注度下降等风险。本项目围绕 MediaPipe Face Landmarker 驾驶疲劳检测系统 展开,提供一个可以直接运行的完整源码包,包含离线 replay 演示、OpenCV 摄像头检测、MediaPipe Tasks Face Landmarker 后端、Face Mesh 兼容后端、PySide6 桌面界面、运行截图、结果曲线、CSV 指标和 Markdown 报告。
项目默认使用离线 replay 数据运行,因此即使当前电脑没有摄像头、没有下载 face_landmarker.task 权重,也能通过 python main.py –mode demo 直接看到结果。对于真实摄像头场景,下载 MediaPipe 官方 Face Landmarker task 模型后,可以切换到 –backend task;如果不想下载 task 文件,也可以用 –backend mesh 快速测试摄像头。整套工程适合用于 CSDN 技术博客、课程设计、人工智能实践项目、OpenCV 项目展示和后续二次开发。
关键词:MediaPipe、Face Landmarker、驾驶疲劳检测、EAR、MAR、PERCLOS、PySide6、OpenCV、人脸关键点、专注度分析、Python 项目实战
1. 项目背景:为什么用人脸关键点做驾驶疲劳检测
很多视觉检测项目喜欢从 YOLO 目标检测入手,例如安全帽检测、车辆检测、烟火检测等。这类项目当然直观,但它们通常需要数据集、标注格式、训练参数、权重文件和大量图片样例。驾驶疲劳检测的技术路线不同,它更适合从“人脸关键点 + 时间序列规则”切入:先定位眼睛、嘴部、鼻尖、下巴等关键点,再计算眼睛开合程度、嘴部张开程度、头部姿态偏移和连续闭眼时间。这样不仅运行门槛更低,也更容易解释算法逻辑。
本项目的核心目标不是做一个只能演示概念的脚本,而是把驾驶疲劳检测整理成一个完整可交付的工程。项目包含配置文件、源码模块、示例数据、权重说明、运行脚本、桌面界面和结果报告。运行后系统会生成多种结果图片,博客可以直接引用,项目报告也可以直接展示。
项目结构围绕“输入源、关键点检测、疲劳指标计算、时间窗口状态判断、告警输出和报告保存”展开。输入层可以来自摄像头、图片或离线 replay;检测层负责输出人脸关键点;指标层计算 EAR、MAR 和头部姿态;状态层维护闭眼窗口、PERCLOS、眨眼次数和打哈欠次数;最后由告警模块输出疲劳等级,并把结果传递给 PySide6 界面和报告模块。
真实驾驶场景中的疲劳风险通常来自长时间驾驶、注意力下降和眨眼、打哈欠等行为变化。下面的道路疲劳驾驶提示场景用于补充说明系统的应用背景,后文的 EAR、MAR 和 PERCLOS 指标都围绕这类风险进行量化:

2. 技术路线概览
MediaPipe Face Landmarker 的价值在于它已经封装好人脸关键点检测能力,可以用于图片、视频帧和实时视频流。它的输出不只是二维点坐标,还可以包含 3D 面部关键点、面部表情 blendshape 系数和面部变换矩阵。对于疲劳检测项目来说,我们最关心的是稳定的人脸网格关键点,尤其是眼睛轮廓点、嘴部轮廓点、鼻尖、下巴和眼角点。
真实摄像头接入 MediaPipe Face Mesh 或 Face Landmarker 后,模型会在面部轮廓、眼部、鼻梁和嘴部输出密集关键点。下面这张运行参考图展示了 MediaPipe 在真人画面上的人脸关键点检测效果,和本项目后续计算 EAR、MAR、头部姿态的输入形式一致。

项目处理流程是:每一帧图像进入系统后,先经过人脸关键点检测器。如果使用真实摄像头,检测器可以是 MediaPipe Tasks Face Landmarker 或 MediaPipe Face Mesh;如果只是离线演示,则使用项目内置的 replay 数据。拿到关键点后,系统计算 EAR、MAR、PERCLOS 和头部姿态,并结合滑动窗口判断是否出现疲劳风险。最终结果会叠加到画面上,同时写入 CSV 和 JSON 报告。
这个方案有几个实际好处。第一,不需要用户自己训练模型,降低了项目复现成本。第二,算法指标可以解释,适合写课程设计和论文实验部分。第三,系统可以扩展,比如加入语音提醒、串口报警、车载摄像头接入、疲劳等级统计、SQLite 记录和 Web 管理后台。第四,离线 demo 可以确保项目在没有摄像头的环境下也能生成完整运行效果,解决很多项目交付时“老师电脑没摄像头”“服务器没有 GUI”“模型文件太大无法打包”的问题。
3. 项目实现效果
项目运行命令非常简单:
python main.py –mode demo
本次源码包已经完成运行验证,系统自动生成了检测叠加图、趋势曲线、告警时间轴和汇总看板。为了展示真实图片输入效果,也可以使用 Face Mesh 后端运行图片模式:
python main.py –mode image –backend mesh –input demo_data/real_yawning_woman_wikimedia.jpg
下面截图来自真实真人照片输入。图中可以看到系统在眼部、嘴部和鼻梁位置绘制了关键点,并在右侧面板显示 EAR、MAR、PERCLOS、专注度评分、头部姿态和检测后端。由于测试图中人物闭眼且嘴部明显张开,系统给出了 DANGER 级别的疲劳风险提示。

从结果曲线可以更清楚地观察系统状态变化。EAR 用于表示眼睛开合程度,MAR 用于表示嘴部张开程度,PERCLOS 表示滑动窗口内闭眼帧占比,Focus Score 表示综合专注度。演示序列中包含正常状态、短眨眼、长闭眼、打哈欠和视线偏离,因此曲线会出现明显变化。

告警时间轴可以用于项目报告中的实验分析。比如正常驾驶时状态为 NORMAL,长闭眼阶段会进入 DANGER,打哈欠阶段会进入 YAWN_WARNING,视线偏离阶段会进入 ATTENTION_LOW。这比只展示单张截图更有说服力。

系统还会生成一个 session summary dashboard,用来总结本次运行的总帧数、平均专注度、最低专注度、最大 PERCLOS、眨眼次数、打哈欠次数和危险帧数量。

PySide6 桌面界面截图如下。界面左侧显示真实图片检测结果,右侧显示状态日志和操作按钮;实际运行 python main.py –mode gui 后,可以在界面中切换离线 replay 和摄像头模式。考虑到很多服务器环境没有显示器,默认命令仍然使用 demo 模式,保证项目可以稳定跑通。

4. 核心算法:EAR、MAR、PERCLOS 与专注度评分
疲劳检测不能只看单帧图像,因为驾驶员可能只是正常眨眼,也可能只是短暂低头。项目的关键是把单帧关键点指标和时间窗口结合起来。这里重点介绍三个指标:EAR、MAR 和 PERCLOS。
EAR 的全称是 Eye Aspect Ratio,即眼睛纵横比。它通过眼睛上下眼睑点之间的距离和左右眼角点之间的距离计算眼睛开合程度。项目中左眼使用 MediaPipe 关键点 [33, 160, 158, 133, 153, 144],右眼使用 [362, 385, 387, 263, 373, 380]。公式如下:
EAR = (||p2 – p6|| + ||p3 – p5||) / (2 * ||p1 – p4||)
当眼睛睁开时,上下眼睑距离较大,EAR 值相对较高;当眼睛闭合时,上下眼睑距离变小,EAR 值会明显降低。项目默认把 ear_closed 设置为 0.21,如果某一帧 EAR 低于该阈值,则认为该帧眼睛闭合。
MAR 的全称是 Mouth Aspect Ratio,即嘴部纵横比。项目使用嘴左点、上唇点、嘴右点、下唇点计算嘴部开合程度:
MAR = ||upper – lower|| / ||left – right||
当嘴部张开明显时,MAR 值会上升。项目默认把 mar_yawn 设置为 0.58,同时要求 MAR 连续超过阈值达到一定帧数,才统计为一次打哈欠。这样可以避免说话、表情变化或单帧误差造成误报。
PERCLOS 用于描述一段时间内眼睛闭合的比例。单次眨眼并不代表疲劳,但如果一段时间内闭眼帧比例持续升高,就说明驾驶员状态可能变差。项目中通过滑动窗口保存最近若干帧的闭眼状态,再计算闭眼帧占比。默认窗口长度为 20 秒,perclos_warning 为 0.22,perclos_danger 为 0.36。实际部署时可以根据摄像头帧率、光照和驾驶员个体差异微调。
下图总结了项目中的核心指标计算逻辑。

专注度评分则是一个综合指标。系统从 100 分开始,分别根据闭眼持续时间、PERCLOS 升高、打哈欠、头部姿态偏离和人脸丢失进行扣分。评分经过指数平滑处理,避免单帧检测抖动导致界面频繁跳变。最终预警等级分为 NORMAL、FATIGUE_WARNING、YAWN_WARNING、ATTENTION_LOW、DANGER 和 NO_FACE。
5. 工程目录与文件说明
完整项目目录如下:
mediapipe_face_fatigue_system/
├── blog.md
├── README.md
├── requirements.txt
├── requirements_demo.txt
├── main.py
├── run_demo.py
├── app.py
├── src/
│ ├── __init__.py
│ ├── config_loader.py
│ ├── fatigue_analyzer.py
│ ├── landmark_detector.py
│ ├── report.py
│ ├── synthetic_data.py
│ ├── utils.py
│ └── visualization.py
├── configs/
│ └── default.yaml
├── demo_data/
│ ├── sample_driver.png
│ ├── real_yawning_woman_wikimedia.jpg
│ └── replay_landmarks.json
├── weights/
│ └── README_WEIGHTS.md
├── images/
│ ├── figures/
│ │ ├── project_architecture.png
│ │ ├── processing_pipeline.png
│ │ └── metric_formula.png
│ └── results/
│ ├── real_face_mesh_result.jpg
│ ├── trend_chart.png
│ ├── alert_timeline.png
│ ├── summary_dashboard.png
│ └── gui_preview.png
├── outputs/
│ ├── frames/
│ ├── reports/
│ │ ├── metrics.csv
│ │ └── summary.json
│ └── session_report.md
├── docs/
│ ├── algorithm_notes.md
│ ├── packaging_guide.md
│ └── troubleshooting.md
├── run.bat
└── run.sh
main.py 是统一入口,支持 demo、image、webcam 和 gui 四种模式。run_demo.py 负责离线演示,会自动生成 replay 数据、叠加图、结果曲线和报告。app.py 是 PySide6 桌面端代码,适合需要界面展示的场景。src/fatigue_analyzer.py 是核心算法模块,里面实现了 EAR、MAR、PERCLOS、头部姿态估计和预警规则。src/landmark_detector.py 封装了三种检测后端:MediaPipe Tasks Face Landmarker、MediaPipe Face Mesh 和离线 replay。src/synthetic_data.py 用于生成无摄像头环境下的演示数据,保证项目可以直接运行。
配置文件保存在 configs/default.yaml。如果要调整阈值,优先改这个文件,不建议直接在代码里写死参数。比如可以调整闭眼阈值、打哈欠阈值、长闭眼时间、PERCLOS 窗口长度和头部姿态角度阈值。
thresholds:
ear_closed: 0.21
mar_yawn: 0.58
long_eye_closed_seconds: 0.70
yawn_min_seconds: 0.45
yaw_degrees: 28
pitch_degrees: 22
perclos_warning: 0.22
perclos_danger: 0.36
6. 代码实现解析
项目把检测、分析、可视化和报告保存拆成不同模块,这样后续扩展会更方便。比如以后想把检测后端换成 ONNX,只需要新建一个 detector,把输出转换为 DetectionResult 格式即可,疲劳分析模块不用改。
核心数据结构在 src/fatigue_analyzer.py 中。DetectionResult 表示单帧关键点结果,FatigueMetrics 表示单帧疲劳指标。检测器只负责给出关键点,分析器负责计算所有状态。
@dataclass
class DetectionResult:
landmarks: Optional[List[Point3D]]
image_width: int
image_height: int
backend: str = "unknown"
confidence: float = 1.0
extra: Dict[str, Any] | None = None
FatigueAnalyzer 初始化时会读取配置文件,把秒级阈值转换成帧数。例如 long_eye_closed_seconds 会乘以 FPS,得到长闭眼触发帧数。这样配置更符合人类理解,不需要用户自己换算帧数。
self.long_eye_closed_frames = max(
1,
int(float(thresholds.get("long_eye_closed_seconds", 0.7)) * self.fps)
)
EAR 计算函数从六个眼睛关键点中取两个垂直距离和一个水平距离。项目分别计算左眼和右眼 EAR,再取平均值,这样单侧眼睛偶尔被遮挡时不会完全失效。
def eye_aspect_ratio(self, landmarks, eye_indices, width, height):
p = [self._point(landmarks, idx, width, height) for idx in eye_indices]
vertical_1 = self._distance(p[1], p[5])
vertical_2 = self._distance(p[2], p[4])
horizontal = self._distance(p[0], p[3])
return (vertical_1 + vertical_2) / (2.0 * horizontal)
眨眼统计和长闭眼判断都依赖连续闭眼帧数。短闭眼如果处于合理范围,就统计为 blink;如果超过长闭眼阈值,就进入危险判断。这种写法比简单地“EAR 小于阈值就报警”更合理。
if eyes_closed:
self.closed_run += 1
self.open_run = 0
else:
if self.blink_min_frames <= self.closed_run <= self.blink_max_frames:
self.blink_count += 1
self.closed_run = 0
self.open_run += 1
打哈欠判断使用 MAR 和连续张嘴帧数。现实中说话、微笑、表情变化也会影响嘴部关键点,所以不能只靠单帧 MAR 判断。项目中使用 mouth_open_run 进行持续性判断,达到阈值后统计一次 yawn,并通过 _yawn_latched 防止同一次张嘴过程被重复计数。
头部姿态估计在真实输入下使用 cv2.solvePnP。系统选取鼻尖、下巴、左右眼角和左右嘴角作为 2D 点,再构造一个近似 3D 人脸模型点。虽然这不是工业级的高精度头姿估计,但对于课程项目和实时提醒系统已经够用。离线 replay 模式会直接携带 yaw、pitch、roll,便于稳定复现实验结果。
7. 如何运行项目
如果只想快速查看效果,安装轻量依赖即可:
pip install -r requirements_demo.txt
python main.py –mode demo
运行完成后,终端会输出类似下面的信息:
Demo finished.
Trend chart: images/results/trend_chart.png
Summary dashboard: images/results/summary_dashboard.png
CSV report: outputs/reports/metrics.csv
Final level: FATIGUE_WARNING, avg focus: 84.02, danger frames: 2
如果要使用摄像头并且不想下载 .task 文件,可以安装完整依赖后运行 Face Mesh 后端:
pip install -r requirements.txt
python main.py –mode webcam –backend mesh
如果要使用 MediaPipe Tasks Face Landmarker 后端,需要先下载权重文件:
cd weights
curl -L -o face_landmarker.task "https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task"
cd ..
python main.py –mode webcam –backend task
如果想启动桌面端:
python main.py –mode gui
桌面端适合本地电脑展示。服务器、Docker、无显示器环境中不要使用 GUI,建议使用 demo 模式或 OpenCV 摄像头模式。
8. 运行验证结果分析
本源码包已经运行过 python main.py –mode demo,输出文件保存在 images/results/ 和 outputs/ 中。摘要结果如下:
Total frames: 90
Average focus score: 84.02
Minimum focus score: 49.72
Maximum PERCLOS: 0.3704
Blink count: 2
Yawn count: 1
Danger frames: 2
Final level: FATIGUE_WARNING
从结果可以看出,系统识别到了 2 次短眨眼、1 次打哈欠,并在长闭眼阶段产生了危险帧。最终状态为 FATIGUE_WARNING,说明演示序列结束时驾驶员从危险状态有所恢复,但疲劳趋势仍然高于正常状态。
metrics.csv 保存了每一帧的指标,包括 frame index、timestamp、EAR、MAR、yaw、pitch、roll、PERCLOS、focus score、alert level 和 alert text。这个文件很适合拿去做后续分析,例如绘制更细的实验图、统计不同阈值下的误报率、对比不同摄像头角度的稳定性。
summary.json 保存本次运行的汇总信息,适合在 Web 后台或报告系统中直接读取。outputs/session_report.md 则是自动生成的 Markdown 运行报告,可以直接放进项目报告附件中。
9. 真实部署时的参数调优建议
离线演示能够验证流程,但真实摄像头环境会受到光照、摄像头角度、驾驶员距离、眼镜反光、脸部遮挡和帧率波动影响。因此部署时建议先采集 30 秒到 60 秒的正常驾驶数据,观察 EAR、MAR 和头姿曲线,再调整阈值。
如果系统太容易误报闭眼,可以适当降低 ear_closed,例如从 0.21 降到 0.19。如果系统对闭眼不敏感,可以适当提高到 0.23。这个阈值和人脸距离、摄像头分辨率、眼睛大小都有关系,没有一个对所有人都最优的固定值。
如果打哈欠误报较多,可以提高 mar_yawn 或增加 yawn_min_seconds。如果驾驶员说话较多,建议不要只用 MAR 触发强预警,可以把打哈欠作为辅助扣分项,而不是直接进入危险状态。
头部姿态阈值也需要结合摄像头安装位置。比如摄像头如果安装在方向盘偏左位置,驾驶员正常看前方时 yaw 可能就不是 0。此时可以先记录正常姿态的平均 yaw,再把偏移阈值建立在相对角度上。当前项目采用简单阈值法,适合课程设计和项目展示;如果要用于更严肃的场景,可以加入姿态校准步骤。
10. 可以继续扩展的方向
这个项目的基础结构已经拆好,后续扩展比较直接。第一,可以加入语音提醒。当状态进入 DANGER 时,播放本地提示音或语音播报。第二,可以加入 SQLite 数据库,把每次运行的疲劳记录保存下来,形成历史趋势。第三,可以增加 Flask 或 FastAPI 服务,把摄像头检测结果推送到 Web 页面。第四,可以加入多摄像头支持,让系统同时监测驾驶员正脸和侧脸。第五,可以增加阈值自动校准,让系统在前 10 秒学习驾驶员正常 EAR 和 MAR 分布,再根据个体差异动态调整阈值。
如果要做论文或算法改进,可以把当前规则系统作为 baseline,再加入轻量级时序模型。例如把最近 5 秒的 EAR、MAR、yaw、pitch、roll 序列输入 LSTM、TCN 或 Transformer Encoder,输出疲劳等级。这种改法适合写“基于人脸关键点时序特征的驾驶疲劳识别方法”。不过对于课程设计和 CSDN 项目实战来说,当前规则版本已经足够清晰、可解释、可运行。
11. 常见问题
1. 不下载 MediaPipe 权重能运行吗?
可以。直接运行 python main.py –mode demo,系统会使用内置 replay 数据生成结果。这也是本项目默认推荐的首次运行方式。
2. 为什么要提供 replay 数据?
因为很多项目交付环境没有摄像头,或者服务器无法打开 GUI。如果项目只能依赖摄像头,就很难稳定生成博客截图和运行证明。replay 数据可以保证项目随时可复现。
3. Face Landmarker 和 Face Mesh 有什么区别?
Face Landmarker 是 MediaPipe Tasks API 中的任务封装,需要 .task 模型文件,输出结构更完整;Face Mesh 是旧版 solutions API,更方便快速测试,不需要额外 task 文件。项目同时封装两种后端,方便用户按环境选择。
4. PySide6 必须安装吗?
不是。离线 demo 和 OpenCV 摄像头模式不依赖 PySide6。只有运行 python main.py –mode gui 时才需要安装 PySide6。
5. 为什么运行结果中最终等级不是 DANGER?
演示序列包含疲劳和恢复过程。中间长闭眼阶段会出现 DANGER,后面进入打哈欠和视线偏离,最终状态可能回落到 FATIGUE_WARNING。这更符合连续监测的逻辑,而不是只看最严重的一帧。
12. 总结
本文完成了一个基于 MediaPipe Face Landmarker 思路的驾驶疲劳检测与专注度分析系统。项目没有停留在概念层,而是提供了完整目录、可运行主程序、离线演示数据、摄像头检测入口、PySide6 桌面端、配置文件、结果截图、趋势曲线和报告文件。核心算法围绕 EAR、MAR、PERCLOS 和头部姿态展开,既容易解释,也方便扩展。
对于 CSDN 项目实战文章来说,这类选题比单纯目标检测更有差异化。它不依赖大规模训练,不需要用户自己标注数据集,也能做出清晰的运行效果。对于课程设计和项目展示来说,项目结构完整,结果图丰富,算法指标可解释,后续还能扩展成 Web 系统、数据库记录、语音报警和时序模型识别。



