新闻详情

新闻详情

首页 / 资讯中心 / 详情

基于CNN的人脸识别疲劳检测:Python实现EAR/MAR驾驶员预警系统

发布时间:2026/10/1 3:05:14来源:尧图网络
基于CNN的人脸识别疲劳检测:Python实现EAR/MAR驾驶员预警系统
简介这份资源是面向计算机相关专业毕业设计、课程设计与项目实战学习者的完整项目包主题为基于卷积神经网络的人脸识别驾驶员疲劳检测与预警系统。项目经导师指导并认可可直接作为毕设或期末大作业使用代码经过严格调试确保能够运行。压缩包共19个文件约78.33MB以Python源码为主包含11个py脚本另有xml级联分类器文件、txt运行说明与依赖清单、hdf5模型权重、exe可执行程序及md说明文档覆盖从数据处理、模型训练到界面交互的完整链路。内容预览显示项目含人脸检测、眼睛定位、CNN模型构建与评估、Tkinter可视化界面等模块并附带训练好的mini_XCEPTION模型文件便于直接复现检测效果。目前已有244人学习下载适合希望快速掌握疲劳检测算法实现、积累深度学习项目经验的学习者参考与二次开发。1. 从一张打哈欠的抓拍说起这套疲劳检测系统到底在做什么凌晨两点跑长途的司机被车内摄像头抓拍到连续打哈欠系统在 1.5 秒内判定疲劳并触发蜂鸣预警——这就是基于卷积神经网络的人脸识别驾驶员疲劳检测与预警系统要解决的真实问题。它不依赖方向盘扭矩、不依赖车道偏移只靠一张脸把眼睛闭合、嘴巴张合这些视觉信号翻译成该休息了的判断。整套方案用 Python 落地核心是 CNN 卷积神经网络做人脸关键点回归再叠加 EAR、MAR 两个几何指标做疲劳判定最后接一个预警模块。适合做毕业设计、课程设计也适合想入门计算机视觉的 Python 学习者——你不需要 GPU 集群一台带普通摄像头的笔记本就能跑通全流程。下面我把这套系统从环境搭建到预警触发按能复现的顺序拆开讲。2. 疲劳检测的技术选型为什么是 CNN 而不是传统特征2.1 从 Haar 到 CNN人脸检测这一步怎么选做疲劳检测第一步永远是把人脸从画面里框出来。很多教程一上来就用 OpenCV 自带的 Haar 级联分类器代码三行就能跑但实际用起来问题不少侧脸、戴眼镜、光线偏暗时漏检率明显上升司机开车时头部本来就会转动Haar 的鲁棒性撑不住。常见做法是换成基于 CNN 的人脸检测器比如 OpenCV 的 DNN 模块加载 Caffe 模型或者直接用 dlib 的 HOG CNN 检测器。我一般会选 dlib 的 68 点模型原因是它同时输出人脸框和 68 个关键点一步到位省掉单独做关键点检测的环节。选型逻辑其实很简单疲劳判定的精度上限取决于关键点定位的稳定性。Haar 只给框关键点还得另找方案dlib 的 68 点模型在正脸和轻微侧脸下误差能控制在 3 像素以内对 EAR/MAR 这种比值型指标来说完全够用。代价是 dlib 的 CNN 检测器比 HOG 慢但在 640×480 分辨率、单张人脸场景下普通 CPU 也能跑到 15 FPS 以上实时性没问题。2.2 EAR 和 MAR把困翻译成两个数字关键点拿到之后怎么判断疲劳业界最通用的两个指标是 EAREye Aspect Ratio眼睛纵横比和 MARMouth Aspect Ratio嘴巴纵横比。EAR 的思路是眼睛睁开时上下眼睑的垂直距离和左右眼角的水平距离之比维持在一个稳定区间闭眼时垂直距离骤降比值跟着掉。公式用 68 点模型里的 6 个眼部点就能算import numpy as np def eye_aspect_ratio(eye_points): # eye_points: 6 个 (x, y) 坐标顺序为 [左眼角, 上左, 上右, 右眼角, 下右, 下左] # 垂直距离上眼睑两点与下眼睑两点的欧氏距离 vertical_1 np.linalg.norm(eye_points[1] - eye_points[5]) vertical_2 np.linalg.norm(eye_points[2] - eye_points[4]) # 水平距离左右眼角的欧氏距离 horizontal np.linalg.norm(eye_points[0] - eye_points[3]) ear (vertical_1 vertical_2) / (2.0 * horizontal) return ear逻辑说明分子取两组垂直距离的平均是为了抵消单点抖动分母用水平距离做归一化这样人脸远近变化时 EAR 值不会跟着漂。参数上正常人睁眼 EAR 在 0.250.35 之间闭眼会掉到 0.15 以下。阈值不能拍脑袋定我一般先跑一段自己录的视频统计睁眼帧的 EAR 均值和标准差取均值减 2 倍标准差作为闭眼阈值这样比固定 0.2 稳得多。MAR 的计算方式类似用嘴巴的 8 个点取上下唇垂直距离除以左右嘴角水平距离。打哈欠时 MAR 会从正常的 0.2 左右飙到 0.6 以上。注意 MAR 的阈值个体差异比 EAR 大有人天生嘴型宽建议同样用统计法标定。2.3 为什么不用端到端 CNN 直接分类疲劳有人会问既然都上 CNN 了为什么不直接训一个二分类网络输入人脸图输出疲劳/清醒这条路我试过翻车点在于数据。端到端分类需要大量标注好的疲劳/非疲劳人脸而公开数据集里真正标注疲劳状态的很少大部分只有打哈欠、闭眼这类动作标签且场景单一。自己标数据成本高模型还容易过拟合到某个人的脸型。相比之下关键点回归 几何指标的方案中间结果可解释、阈值可调、换个人只需重新标定阈值工程上更可控。毕业设计场景下这套组合拳的性价比明显更高。3. 用 Python 把检测流程跑通从摄像头到 EAR 曲线3.1 环境搭建与依赖安装先把环境弄干净。Python 版本建议 3.83.10太新的版本有些 CV 库轮子还没跟上。用 conda 或 venv 建虚拟环境都行我习惯 venvpython -m venv fatigue_env # Windows 激活 fatigue_env\Scripts\activate # Linux / macOS 激活 source fatigue_env/bin/activate pip install opencv-python dlib numpy scipy imutils参数说明opencv-python 负责读摄像头和图像处理dlib 提供 68 点关键点模型注意 dlib 在 Windows 上直接 pip 装可能编译失败稳妥做法是去下载对应 Python 版本的 .whl 文件再 pip installscipy 用来做滤波后面平滑 EAR 曲线要用imutils 是常用图像工具集可选但省事。装完跑一句python -c import cv2, dlib; print(cv2.__version__, dlib.__version__)验证能打印版本号就说明环境通了。3.2 关键点提取与 EAR/MAR 实时计算dlib 的 68 点模型需要单独下载shape_predictor_68_face_landmarks.dat这个文件约 100MB放到项目目录下。下面是把摄像头帧转成 EAR/MAR 数值的核心循环import cv2 import dlib import numpy as np from scipy.spatial import distance as dist detector dlib.get_frontal_face_detector() predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat) # 68 点索引左眼 36-41右眼 42-47嘴巴 48-67 LEFT_EYE list(range(36, 42)) RIGHT_EYE list(range(42, 48)) MOUTH list(range(48, 68)) def get_ear_mar(shape): # 把 dlib 的 shape 对象转成 numpy 数组 pts np.array([[shape.part(i).x, shape.part(i).y] for i in range(68)]) left_ear eye_aspect_ratio(pts[LEFT_EYE]) right_ear eye_aspect_ratio(pts[RIGHT_EYE]) ear (left_ear right_ear) / 2.0 # MAR上下唇垂直距离 / 嘴角水平距离 mouth_vertical dist.euclidean(pts[62], pts[66]) mouth_horizontal dist.euclidean(pts[60], pts[64]) mar mouth_vertical / mouth_horizontal return ear, mar cap cv2.VideoCapture(0) while True: ret, frame cap.read() if not ret: break gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces detector(gray, 0) for face in faces: shape predictor(gray, face) ear, mar get_ear_mar(shape) cv2.putText(frame, fEAR:{ear:.2f} MAR:{mar:.2f}, (30, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) cv2.imshow(Fatigue Detection, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()逻辑说明detector(gray, 0)里的 0 表示不上采样速度快但小脸可能漏检如果人脸离摄像头远可以改成 1。get_ear_mar把 dlib 的 shape 对象转成 numpy 数组再切片比逐个 part 调用快。参数上左右眼 EAR 取平均能抵消单侧遮挡MAR 用 62/66 和 60/64 这两组点是 68 点模型里上下唇和嘴角的标准索引别用错。跑起来后对着摄像头睁眼闭眼看 EAR 数值是否在 0.3 和 0.15 之间跳变跳变不明显就检查关键点有没有标歪。3.3 用滑动窗口和滤波压住抖动直接拿单帧 EAR 做判断会有一个血泪经验关键点每帧都有微小抖动EAR 曲线毛刺很多偶尔一帧掉到阈值以下就误报。解决办法是两层处理。第一层用滑动窗口平均取最近 N 帧的 EAR 均值第二层用一维卡尔曼滤波或简单的指数平滑。我一般用窗口长度 5、指数平滑系数 0.3 的组合既能压抖动又不至于把真实的闭眼信号也抹平。from collections import deque ear_window deque(maxlen5) alpha 0.3 smoothed_ear None def smooth_ear(raw_ear): global smoothed_ear ear_window.append(raw_ear) window_avg sum(ear_window) / len(ear_window) if smoothed_ear is None: smoothed_ear window_avg else: smoothed_ear alpha * window_avg (1 - alpha) * smoothed_ear return smoothed_ear参数说明maxlen5对应约 0.3 秒的窗口按 15 FPS 算太短压不住抖动太长会延迟预警alpha0.3表示新值权重越小越平滑但响应越慢。这两个值要根据实际帧率调帧率低就加大窗口。判断闭眼时不要用瞬时值用平滑后的值连续超过阈值若干帧才计数这个连续帧数是抗误报的关键。4. 疲劳判定与预警阈值、计数和触发逻辑4.1 闭眼与打哈欠的判定规则有了平滑后的 EAR/MAR判定规则要分两条线走。闭眼线EAR 连续低于阈值超过 M 帧判定为一次闭眼事件如果闭眼持续超过 2 秒约 30 帧直接触发疲劳预警因为正常眨眼不会闭这么久。打哈欠线MAR 连续高于阈值超过 N 帧判定为一次哈欠统计最近 60 秒内的哈欠次数超过 3 次触发预警。两条线是或的关系任一满足就报警。EAR_THRESHOLD 0.20 # 需按 3.2 的统计法重新标定 MAR_THRESHOLD 0.55 CONSEC_FRAMES 3 # 连续帧数抗单帧抖动 DROWSY_SECONDS 2.0 FPS_ESTIMATE 15 eye_counter 0 yawn_counter 0 drowsy_frames int(DROWSY_SECONDS * FPS_ESTIMATE) def judge(ear, mar): global eye_counter, yawn_counter if ear EAR_THRESHOLD: eye_counter 1 else: if eye_counter CONSEC_FRAMES: pass # 一次正常眨眼可记录 eye_counter 0 if mar MAR_THRESHOLD: yawn_counter 1 else: yawn_counter 0 if eye_counter drowsy_frames: return DROWSY_EYE if yawn_counter int(1.5 * FPS_ESTIMATE): return DROWSY_YAWN return NORMAL逻辑说明eye_counter在 EAR 低于阈值时累加回升即清零这样只有连续闭眼才会累积到drowsy_frames。yawn_counter同理一次哈欠持续 1.5 秒以上才计数。参数上EAR_THRESHOLD和MAR_THRESHOLD必须按 3.2 的统计法标定直接抄 0.20/0.55 只能算起点FPS_ESTIMATE最好实测用time.time()算实际帧率再代入否则drowsy_frames会偏。4.2 预警模块声音、弹窗还是外设预警触发后做什么取决于使用场景。桌面演示用winsound.BeepWindows或playsound播报警音最简单要做得像样一点可以用tkinter弹一个置顶窗口或者用pygame.mixer循环播放提示音直到驾驶员确认。如果标题里提到预警系统设计通常还需要一个状态记录把每次预警的时间戳写进日志文件方便事后分析。import time import csv def trigger_alarm(reason): # 声音预警Windows 用 winsound跨平台用 pygame try: import winsound winsound.Beep(1000, 500) except ImportError: print(ALARM:, reason) # 记录日志 with open(fatigue_log.csv, a, newline) as f: writer csv.writer(f) writer.writerow([time.strftime(%Y-%m-%d %H:%M:%S), reason])参数说明winsound.Beep(1000, 500)是 1000Hz 响 500ms频率太高刺耳、太低听不见1000Hz 左右比较合适。日志用 CSV 追加写字段是时间和触发原因后续可以用 pandas 读出来做统计。注意预警要有冷却时间比如触发后 10 秒内不重复报警否则连续帧会刷屏。4.3 把检测结果可视化出来毕业设计答辩时光有报警声不够得有可视化。我一般会在画面上叠加三样东西人脸框、眼睛和嘴巴的关键点连线、右上角的 EAR/MAR 实时曲线。曲线用cv2.line在画布上画维护一个长度 100 的 EAR 历史队列每帧把新值映射成 y 坐标。这样评委一眼就能看到 EAR 在闭眼时掉下去、MAR 在打哈欠时冲上去比干讲公式有说服力。可视化代码不复杂但要注意画布尺寸和坐标映射别让曲线画出屏幕。5. 避坑与排查那些让我熬夜的细节5.1 摄像头读不到帧或帧率骤降现象cap.read()返回 False或者画面卡成幻灯片。原因通常是摄像头被其他程序占用或者分辨率设太高导致 CPU 解码跟不上。解决先确认没有其他软件开着摄像头把cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)和高度设成 480别用默认的 1080p如果还是慢把 dlib 检测器的上采样参数从 1 改回 0。另外 Linux 下要确认当前用户在 video 组里否则权限不够。5.2 EAR 阈值换个人就失效现象自己电脑上跑得好好的换同学来测睁眼 EAR 只有 0.18一直误报闭眼。原因是个体眼型差异双眼皮、单眼皮、眼睛大小都会影响 EAR 绝对值。解决加一个 5 秒的标定环节让用户正常睁眼几秒程序自动统计 EAR 均值和标准差动态生成阈值。这个改动不大但能让系统从只能演示变成能给别人用。5.3 戴眼镜反光导致关键点漂移现象戴眼镜的测试者镜片反光时关键点会跳到镜框上EAR 突然异常。原因是 dlib 的关键点模型对高光区域敏感。解决预处理阶段加一步直方图均衡化cv2.equalizeHist压一下高光如果还不行在检测区域做一次高斯模糊再送进 predictor。更彻底的办法是换用对眼镜更鲁棒的模型但毕业设计场景下均衡化 模糊基本够用。5.4 打哈欠和说话分不清现象测试者正常说话MAR 频繁超过阈值误报哈欠。原因是说话时嘴巴也在张合单看 MAR 峰值区分不了。解决哈欠的持续时间比说话的单次张口长把判定条件从MAR 超阈值改成MAR 连续超阈值超过 1.5 秒说话时张口通常不到 1 秒这样能过滤掉大部分误报。另外可以叠加 EAR 条件真打哈欠时眼睛往往半闭说话时眼睛是睁着的。5.5 预警延迟太大或太灵敏现象要么闭眼好几秒才报警要么眨个眼就响。原因是滑动窗口长度和连续帧阈值没配合好。解决把窗口长度、平滑系数、连续帧数当成一组参数联合调。经验值是窗口 5 帧、平滑系数 0.3、闭眼连续 30 帧2 秒报警先按这个跑再根据实测微调。调参时录一段包含正常眨眼、闭眼、打哈欠的视频离线跑一遍看误报和漏报比对着摄像头反复试效率高得多。6. 让这套系统更耐用的两个进阶技巧第一个技巧是模型量化提速。dlib 的 68 点模型在 CPU 上跑单帧约 3050ms如果要做多路摄像头或者嵌入式部署这个速度不够。可以把 dlib 的模型转成 ONNX再用 onnxruntime 推理实测能快 23 倍。转换流程是先用 dlib 的shape_predictor导出再用onnxruntime加载输入输出对齐后替换掉原来的 predictor 调用。这一步对毕业设计来说是加分项能体现工程优化意识。第二个技巧是用轻量 CNN 做二次确认。EAR/MAR 是几何指标遇到极端光照或遮挡会失效。可以在几何判定触发预警后再截取眼部区域送进一个小型 CNN 做睁/闭二分类两个结果都指向疲劳才最终报警。这个小 CNN 不用自己训用公开的眼部状态数据集微调一个 MobileNet 就行推理耗时增加不多但误报率能明显下降。验证方法上我习惯用离线视频回放代替实时测试。录一段 5 分钟的视频包含清醒、眨眼、闭眼、打哈欠、说话各种状态用脚本逐帧跑检测把 EAR/MAR 曲线和判定结果画出来人工核对每个预警是否合理。这样调参有依据答辩时也能拿出量化结果比空口说效果不错强。最后说个我自己的习惯每次改完阈值或窗口参数一定把当次的参数组合和对应的误报/漏报数记在一个表格里跑够五六组再选最优。疲劳检测这套东西玄学的地方就在参数耦合凭感觉调很容易绕圈。把参数和结果记下来翻车了也有后悔药可吃。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

New-API部署全攻略:用Docker搭建统一的LLM API网关与管理面板 2026/10/1 4:00:40

New-API部署全攻略:用Docker搭建统一的LLM API网关与管理面板

干过AI应用底层调度的朋友应该能理解,模型接口散落各处、每家的鉴权方式和计费标准还不一样,光是维护一堆上游Key就够头疼。New-API这个项目,简单说就是一个开源的LLM API网关和管理面板,把OpenAI、Claude、Gemini以及国内外各种兼…

阅读更多 →
OpenClaw生产环境安全加固:权限、沙箱与漏洞防护实战 2026/10/1 4:00:40

OpenClaw生产环境安全加固:权限、沙箱与漏洞防护实战

先问一个现实问题:你把 OpenClaw 部署完成之后,做的第一件事是什么?很多人是打开浏览器看控制台能不能访问,然后急着接入 Microsoft Teams、把 Obsidian 笔记库挂上来、跑第一个自动化任务。我理解这种急于验证功能的心情&#xf…

阅读更多 →
Java+Spring Boot智慧乡村管理系统后端源码设计与实战 2026/10/1 4:00:40

Java+Spring Boot智慧乡村管理系统后端源码设计与实战

简介:资源为基于Java与HTML实现的智慧乡村管理系统后端源码,面向Java后端开发者、毕业设计及课程实践人群,用于理解乡村管理类系统的模块划分、接口设计与前后端交互方式。资源包共45个文件,核心为40个Java源文件,覆盖…

阅读更多 →
Spring Boot + Vue构建汽车维修预约系统:从数据库设计到部署全指南 2026/10/1 4:00:40

Spring Boot + Vue构建汽车维修预约系统:从数据库设计到部署全指南

前阵子有个准备做毕设的读者问我:想实现一套汽车维修预约服务系统,后端到底该选什么?我几乎没有犹豫,直接回答Spring Boot。这个答案不只是因为Spring Boot是当前Java后端开发的事实标准,更重要的是,它几乎…

阅读更多 →
用Docker部署New-API:实现大模型API统一网关与令牌管理 2026/10/1 4:00:40

用Docker部署New-API:实现大模型API统一网关与令牌管理

很多人手里都攒着一堆大模型API的key,ChatGPT的、Claude的、DeepSeek的、各家国产模型的,散落得到处都是。真正要开发一个自己的AI应用时,问题就来了:要么这个渠道突然限流,要么那个服务商接口升级要改代码&#xff0c…

阅读更多 →
执行DELETE后到底要不要COMMIT?一文理清自动提交与事务边界 2026/10/1 4:00:33

执行DELETE后到底要不要COMMIT?一文理清自动提交与事务边界

刚工作那两年,我也被同样的问题问住过:执行完 delete 之后到底要不要 commit?当时带我的主管瞄了一眼代码,淡淡地说了句“你先把事务概念理清楚再来改”。后来我在生产环境看到有人因为没有理解 delete 的提交行为,几万…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉