基于Python+OpenCV的疲劳驾驶检测系统:dlib关键点与EAR算法详解
简介基于Python与OpenCV的疲劳驾驶检测完整项目面向计算机相关专业毕业设计、课程设计及需要实战演练的开发者。项目采用人脸关键点检测技术可实时分析眼睛闭合程度、打哈欠频率等疲劳特征并触发报警提示覆盖从环境配置到核心算法的完整实现链路。资源共包含4个文件以Python主程序为核心辅以OpenCV预训练关键点模型、中文标注字体及依赖清单压缩包整体74.52MB结构精简清晰便于直接调试运行。源码已经本地编译通过并严格调试经导师指导认可评审得分高达98分内容包含全部数据与运行配置难度适中适合作为高分毕业设计参考、期末大作业或项目实战练手。目前已有154人学习使用资源质量可靠下载后即可开展二次开发与效果验证。1. 疲劳驾驶检测毕业设计先弄清楚这份源码能解决什么问题答辩现场演示翻车是很多毕业设计最大的噩梦。而疲劳驾驶检测这类项目最容易翻车的地方恰恰是“看起来简单跑起来全是坑”——模型文件缺一个、中文字体没配、dlib 装不上任何一个环节都能让你当场黑屏。这份基于 pythonopencv 的疲劳驾驶检测源码包把 dlib 的 68 点人脸关键点模型、宋体字体文件、主程序和依赖清单全部配齐了解压之后按顺序装依赖就能跑通。它做的事情很明确通过摄像头实时检测人脸用眼睛纵横比 EAR 判断闭眼用嘴巴纵横比 MAR 判断打哈欠再结合 PERCLOS 统计在窗口期内的疲劳程度并触发报警。适合正在写毕业设计、课程设计或者想拿 OpenCV 做完整项目练手的人。2. 拆解源码与检测原理68点关键点、EAR和PERCLOS的配合方式2.1 资源包四个核心文件各干什么这个项目拿到手之后先别急着运行花两分钟把文件组织看清楚。我拆过不少这类毕设包很多同学翻车不是因为代码写得不对而是压根不知道每个文件是干嘛的、谁依赖谁。文件大小量级作用driverFatigue.py几 KB主程序负责摄像头采集、人脸检测、关键点提取、疲劳判定和告警shape_predictor_68_face_landmarks.dat约 100MBdlib 预训练模型输入人脸区域输出 68 个面部关键点坐标simsun.ttc十几 MB宋体字体OpenCV 自带 putText 不支持中文需要它来绘制汉字提示requirements.txt1KB 以内依赖清单声明 opencv-python、dlib、numpy、imutils 等库依赖关系是这样的driverFatigue.py 在运行时先调 dlib 的人脸检测器找到人脸框然后把框交给 shape_predictor_68 模型提取 68 个点拿到眼睛和嘴巴的点位之后计算 EAR 和 MAR显示中文提示时需要 simsun.ttc 配合 PIL 来绘制。所以四样东西缺一不可尤其是那个 100MB 的 dat 文件很多人下载不完整加载时报错还以为是代码问题。2.2 疲劳判定核心EAR眼睛纵横比的计算先看最核心的闭眼判断。dlib 的 68 点模型把眼睛区域标成 6 个关键点左眼是索引 36 到 41右眼是 42 到 47。这 6 个点围成一个近似椭圆眼睛睁开时椭圆比较“圆”闭眼时垂直方向的距离趋近于零。EAREye Aspect Ratio眼睛纵横比就是把这种形状变化量化成一个数值。# ear_example.py —— 眼睛纵横比计算示例 import numpy as np def eye_aspect_ratio(eye_pts: np.ndarray) - float: # eye_pts 是单眼 6 个关键点坐标顺序按 dlib 68 点定义排列 # 计算两组垂直距离的平均值再除以水平距离 vertical_1 np.linalg.norm(eye_pts[1] - eye_pts[5]) vertical_2 np.linalg.norm(eye_pts[2] - eye_pts[4]) horizontal np.linalg.norm(eye_pts[0] - eye_pts[3]) if horizontal 0: return 1.0 return (vertical_1 vertical_2) / (2.0 * horizontal)逻辑说明vertical_1 和 vertical_2 是眼睛上下眼睑的两组垂直距离horizontal 是眼睛左右眼角的水平距离。EAR 的本质是一个比例正常睁眼时这个比值通常在 0.3 左右闭眼时会掉到 0.1 以下。之所以用比值而不是直接用像素距离是因为比值是尺度无关的——人离摄像头近的时候眼睛像素面积大离得远的时候小但比值基本稳定。这一点也是让新手最容易迷惑的地方很多人一开始直接用眼睛的像素高度做判断结果换个坐姿就失灵了。参数说明主程序里会分别算左眼和右眼的 EAR然后取平均值。如果平均值小于 EAR_THRESHOLD通常取 0.25就认为当前帧是闭眼状态。注意单帧闭眼不能说明疲劳正常人每分钟要眨眼十几次每次闭眼就一两帧所以还需要配合连续帧数判断。2.3 打哈欠与PERCLOS用状态统计代替单帧判断单帧判断闭眼之后下一步是把“瞬时状态”累积成“疲劳证据”。这个项目里用了两个统计手段。第一个是连续帧计数。用一个计数器记录连续闭眼的帧数当连续闭眼帧数超过 CONSEC_FRAMES默认 20 帧左右时判定为一次“微睡眠”事件。为什么还要加这个条件因为人在正常眨眼时 EAR 也会短暂下降但通常只有 1 到 3 帧而真正的疲劳闭眼会持续更久。在 30fps 的摄像头下20 帧大约是 0.67 秒这个时长已经能明显区分眨眼和瞌睡了。第二个是打哈欠检测。嘴巴区域的 6 个关键点也可以用同样的纵横比逻辑只是把眼睛的垂直水平比值换成嘴巴的# mar_example.py —— 打哈欠的 MAR 计算 def mouth_aspect_ratio(mouth_pts: np.ndarray) - float: # 嘴巴关键点外唇或内唇一组 6 点均可需保持索引一致 # 纵向距离取两组横向距离取左右嘴角 vertical np.linalg.norm(mouth_pts[2] - mouth_pts[6]) horizontal np.linalg.norm(mouth_pts[0] - mouth_pts[4]) return vertical / horizontal参数说明MAR 在正常说话时也会有波动所以不能单帧触发一般要求 MAR 超过 0.6 且持续一定帧数才记一次哈欠。实际项目中还会加一个“哈欠计数 时间窗口”的组合判断比如在 5 分钟内哈欠超过 3 次就升级疲劳等级。PERCLOSPercentage of Eye Closure是这套系统的最后一道统计手段。它的定义是在指定时间窗口内眼睛闭合时间所占的比例。程序里会维护一个滑动的帧序列比如最近 300 帧统计其中闭眼帧的占比。当 PERCLOS 超过阈值常见取 0.2 或 0.4视标准而定时说明驾驶员的闭眼行为已经不是偶发而是持续性的疲劳状态此时触发声音和画面双重报警。整个流程是单帧关键点提取 → 计算 EAR/MAR → 连续帧状态确认 → 窗口期内 PERCLOS 统计 → 报警输出。3. 跑通环境与调参从requirements.txt到driverFatigue.py的完整落地上线3.1 环境准备Python 3.8与虚拟环境这个项目比较老牌依赖的是 dlib 和 OpenCV 的经典组合。我的习惯是用虚拟环境隔离避免把系统 Python 环境搞乱。版本上Python 3.8 是最稳的选择dlib 对 3.10 以上的新版本支持偶尔有编译问题没必要在这个环节浪费答辩时间。# 创建虚拟环境并激活Windows 和 Linux 命令略有差异 conda create -n fatigue python3.8 -y conda activate fatigue如果不用 conda用 python 自带的 venv 也可以python -m venv fatigue_env # Windows 激活 fatigue_env\Scripts\activate # Linux/macOS 激活 source fatigue_env/bin/activate逻辑说明虚拟环境的作用是让项目依赖与系统其他项目隔离。疲劳检测项目用到的 dlib 版本如果和系统里其他项目的 opencv-contrib-python 版本冲突现场折腾起来非常痛苦。conda 的好处是它能自动处理 dlib 的二进制依赖而 pip 安装 dlib 时在 Windows 上经常要现场编译容易翻车。3.2 依赖安装opencv-python和dlib的常见安装路线激活环境之后先看 requirements.txt 的内容。这个文件一般声明了 opencv-python、dlib、numpy、imutils 这几个核心库。直接用 pip 批量安装pip install -r requirements.txt如果一切顺利这一步就结束了。但据我实测的经验Windows 平台上 90% 的概率会卡在 dlib 这一步——它需要 cmake 和 C 编译器现场编译源码输出一大堆英文日志最后报一堆链接错误。常见做法是先装好编译工具链再单独装 dlib# Windows 下先装 Visual Studio Build Tools勾选 C 工作负载 # 再装 cmake pip install cmake # 单独编译安装 dlib pip install dlib更省事的路线是用 conda 的预编译包不需要本地编译器conda install -c conda-forge dlib注意如果第 2 条命令报错 ModuleNotFoundError: No module named dlib同时又看到一堆 CMake 相关的报错说明是编译环节的问题直接去按第 4 章的避坑引导处理。装完之后验证一下python -c import cv2, dlib, numpy; print(cv2.__version__, dlib.__version__)能看到两个版本号输出说明环境这块已经过关了可以进入主程序运行阶段。3.3 运行主程序与阈值参数调整环境就绪后进入项目目录确认四个核心文件都在同一个目录下。其中 shape_predictor_68_face_landmarks.dat 必须在当前目录或者路径被正确指定否则运行时直接抛 RuntimeError。启动命令很简单python driverFatigue.py运行之后摄像头灯亮起画面窗口会显示实时视频并且用矩形框标出人脸在脸上绘制 68 个关键点。在眼睛区域会显示当前 EAR 值嘴巴区域显示 MAR 值。当检测到闭眼超过连续帧数或者 PERCLOS 超标时画面顶部会显示“疲劳驾驶”的中文警告同时触发蜂鸣声。主程序头部一般有一个参数区这就是整个系统最需要动手调的地方。典型参数长这样# driverFatigue.py 内的参数区示例按实际文件位置为准 EAR_THRESHOLD 0.25 # 眼睛纵横比低于此值视为闭眼 CONSEC_FRAMES 20 # 连续闭眼达到此帧数记为一次疲劳事件 MAR_THRESHOLD 0.60 # 嘴巴纵横比阈值判断打哈欠 PERCLOS_WINDOW 300 # PERCLOS 统计窗口单位帧 SOUND_ALARM True # 是否启用声音告警参数说明EAR_THRESHOLD 是所有参数里最敏感的。戴眼镜、眼睛小、离摄像头远近都会影响这个数值我用过 0.22 到 0.28 的区间。CONSEC_FRAMES 决定“连续闭眼多久才算犯困”别设太低否则普通眨眼都会被误报也别设太高否则人已经睡着了好几秒才报警。PERCLOS_WINDOW 是统计窗口长度300 帧在 30fps 下对应 10 秒这个窗口要跟 CONSEC_FRAMES 配合是整体疲劳趋势的判定基础。跑通之后答辩演示基本没问题了。但真正要拿高分光会运行还不够还得知道阈值怎么调、报警逻辑怎么解释这些在后面的章节里展开。4. 避坑指南复现这个项目最容易翻车的五个环节4.1 dlib装不上ModuleNotFoundError vs 编译报错现象执行pip install dlib时报错有时直接提示 ModuleNotFoundError: No module named dlib有时是一大段 CMake 编译日志然后失败退出。原因dlib 在 Windows 上没有预编译的 wheel 时pip 会从源码编译而编译需要 cmake 和一个完整的 C 编译器。如果系统只有 Python 自带的编译器或者 VS 版本不对编译过程就会在生成 C 项目文件阶段中断。解决先安装 Visual Studio Build Tools注意勾选“使用 C 的桌面开发”工作负载然后pip install cmake再重新pip install dlib。不想折腾编译器的话直接用conda install -c conda-forge dlib用预编译的二进制品省掉整个编译过程。我的习惯是能 conda 就 conda毕设阶段不要在编译上赌运气。4.2 shape_predictor加载失败那个100MB的dat文件现象运行时提示RuntimeError: Unable to open shape_predictor_68_face_landmarks.dat或者能打开但报文件格式损坏。原因两种情况最常见——文件路径不对程序在相对路径下找不到模型文件或者下载不完整这个 dat 文件有约 100MB下载过程断掉之后留下一个大小缩水的文件加载时自然报错。解决检查文件大小正常应该在 99MB 左右只有几十 MB 说明下载不完整重新下载覆盖。代码里把相对路径改成绝对路径或者确保运行命令的当前目录就是项目目录。一个小技巧是启动时打印一下os.path.abspath(shape_predictor_68_face_landmarks.dat)确认程序实际找的路径。4.3 中文乱码OpenCV的putText不认中文现象画面里的中文告警文字变成了方块或者问号英文和数字正常显示。原因OpenCV 的 cv2.putText 底层只支持英文字符集画中文时编码对不上输出就是一堆乱码。这个项目带 simsun.ttc 就是为了解决这个问题但得有代码配合才生效。解决项目里常用的方案是用 PIL 绘制中文再转换回 OpenCV 的 BGR 格式。核心代码逻辑是这样from PIL import Image, ImageDraw, ImageFont def draw_text_cn(img, text, pos, size24): # OpenCV 的 putText 画不了中文改用 PIL 绘制后转回 BGR pil_img Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) draw ImageDraw.Draw(pil_img) font ImageFont.truetype(simsun.ttc, size) draw.text(pos, text, fontfont, fill(0, 0, 255)) return cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)参数说明fill 是文字颜色我这里用 (0, 0, 255) 对应红色。字体路径是相对路径如果运行目录变了同样需要用绝对路径。注意每次绘制都会做两次色彩空间转换帧率会受一点影响但英文告警和中文告警对照着写让原有的 putText 和 PIL 绘制并存就行。4.4 摄像头打不开VideoCapture(0)总返回False现象程序启动后没有画面控制台输出显示读取失败或者画面黑屏但程序不退出。原因最常见的是摄像头索引不对。cv2.VideoCapture(0) 里的 0 表示默认摄像头笔记本内置摄像头一般用 0但外接 USB 摄像头可能占用索引 1 或 2。还有可能是摄像头被其他软件微信、腾讯会议占用OpenCV 拿不到流。解决先写一个三行的测试脚本用循环遍历索引 0 到 2看哪个能打开import cv2 for idx in range(3): cap cv2.VideoCapture(idx) if cap.isOpened(): print(fcamera index {idx} works) cap.release()搞定索引问题后再把 driverFatigue.py 里的 VideoCapture 参数改成对应数字。如果还是打不开检查系统的相机隐私设置Windows 里给“桌面应用”授权访问摄像头。4.5 频繁误报先调阈值还是先调光线现象人明明睁着眼程序却不停地报疲劳或者正常说几句话哈欠一直触发。原因误报基本是两个来源——阈值和输入源质量。EAR_THRESHOLD 取值偏高时眼睛稍微眯一点就被判定为闭眼光线太暗或者逆光时dlib 检测人脸后关键点落在阴影区域EAR 会被压到阈值以下。反差大的环境还会让脸部关键点抖动单帧数值忽高忽低。解决调整优先级有讲究——先把摄像头摆在光线充足、人脸正对的位置确保关键点稳定不跳动再谈调阈值。我一般会打印一长段清醒状态下的 EAR 数据取其中的最小值向下让 20% 作为阈值。比如清醒时 EAR 最低到过 0.28阈值就设在 0.22 到 0.25 之间留出合理余量而不是拍脑袋定死 0.25。光线问题永远是第一位的光线不对任何阈值都救不回来。5. 进阶验证用视频文件给疲劳检测做一次参数标定论文和答辩材料里光有“能运行”是不够的老师大概率会问“你的效果怎么验证参数为什么取这个值”。最实用的验证手段是把摄像头输入换成视频文件通过控制变量法做参数标定。先把输入源切换成视频。在 driverFatigue.py 里找到摄像头初始化那一行把参数从摄像头索引改成视频文件路径# 原cap cv2.VideoCapture(0) cap cv2.VideoCapture(road_test.mp4)建议用一段 40 到 60 秒的驾驶模拟视频要求包含正常的睁眼驾驶、频繁眨眼、持续闭眼和打哈欠这几个阶段。把视频播放一遍记录输出日志里每一个 EAR 和 MAR 的值。然后用一个小脚本统计清醒段和疲劳段的数值分布import numpy as np # 假设已经采集了清醒段和疲劳段各 500 帧的 EAR 值 alert_ears np.array([...]) # 清醒段数据 drowsy_ears np.array([...]) # 疲劳段数据 print(清醒段 EAR 均值: %.3f, 最小值: %.3f % (alert_ears.mean(), alert_ears.min())) print(疲劳段 EAR 均值: %.3f, 最大值: %.3f % (drowsy_ears.mean(), drowsy_ears.max()))这一步跑完阈值就不是猜的了。取清醒段最小值和疲劳段最大值之间的中点作为 EAR_THRESHOLD再结合疲劳段的闭眼时长来定 CONSEC_FRAMES。比如疲劳段里单次闭眼最短持续了 15 帧那 CONSEC_FRAMES 就设在 15 到 20 之间既能捕捉到真实闭眼又不会把 1 到 3 帧的眨眼算进去。PERCLOS 的验证同理在统计窗口内人为制造一次持续闭眼确认报警延迟和触发时机符合预期。把验证过程整理成数据表格答辩时直接展示“参数是基于实测标定的”比说“我试了几个数发现这个效果最好”要让人信服得多。这套标定方法也适用于后续扩展比如把 EAR/MAR 特征收集起来接入一个简单的分类模型就能从规则判断升级成基于状态序列的疲劳分级。我从那以后每跑一个 OpenCV 相关的毕设项目都会强制自己先走一遍视频标定再上摄像头这个习惯帮我避开了不少现场演示的翻车环节。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →