简介:面向高校计算机专业毕业设计人群,这份Python源码实现了基于驾驶员面部特征的疲劳检测,系统通过人脸识别分析眼部、嘴部等关键状态,自动判断驾驶员是否需要休息,也适用于交通监控、高速收费站等疲劳驾驶预警场景。整个压缩包共24个文件、大小约68.33MB,核心为5个py脚本,分别负责检测与判定逻辑;配套10个xml配置文件和2个iml工程文件,可帮助IDE快速识别项目结构,docx、md、txt提供说明文档,mp3用于疲劳语音提醒,dat文件保存特征数据,整体源码结构清晰,便于按模块阅读。项目已有1357人学习下载,说明其在毕业设计场景下具备较高的参考价值和使用基础。下载后可直接导入PyCharm等环境运行,便于快速复现实验,并可根据实际需求调整检测阈值、扩展接口或接入摄像头视频流,方便进一步研究疲劳检测算法细节。对于需要完成人脸识别类毕业设计、学习疲劳检测完整流程的同学,这份源码提供了简洁可用的基础框架和可直接演示的项目素材,同时有助于锻炼工程实现与二次开发能力。 最近后台收到好几个同学私信,都在问基于Python的驾驶员疲劳检测系统怎么做,说搜了一圈源码要么跑不起来,要么代码注释少得可怜,根本没法交毕业设计。我自己前前后后帮人调过好几版这类系统,从最早用OpenCV硬算眼睛长宽比,到后来接Dlib做68点关键点检测,踩过的坑确实不少。这篇就把这套系统的核心逻辑、完整实现流程、以及最容易出问题的细节一次性说清楚,想拿来做课设或者毕业设计的同学可以直接照着思路复现。
这套系统说白了就是让电脑通过摄像头实时判断一个人是不是在打瞌睡。核心链路是:摄像头抓帧 -> 人脸检测 -> 定位面部关键点(尤其眼睛周围)-> 计算眼睛开合程度 -> 根据连续帧的闭眼频率和时长判定疲劳状态 -> 触发报警。整条链路全部用Python实现,配合OpenCV和Dlib两个库就能跑通,这也是当前绝大多数毕设选题里最主流的方案。
1. 方案选型:为什么主流毕设都选Dlib而非深度学习
1.1 不同实现路径的优劣对比
疲劳检测的实现路线并不只有一条。我在给不同需求的同学做技术评估时,一般会列出三条路线:传统图像处理、经典机器学习、深度学习。三者各有各的适用场景。
传统图像处理路线,典型的做法是利用Haar特征做眼睛区域检测,然后靠像素灰度分布判断眼睛闭合状态。优点是代码量小、运行极快,缺点是鲁棒性非常差,戴眼镜、光线变化、头部转动都会让准确率直接崩掉,只适合做演示用。
经典机器学习路线,比如训练一个SVM或HOG分类器来区分睁眼闭眼,比传统方法要强一些,但你需要自己去准备标注数据集,这个工作量对于毕设来说并不小。
深度学习路线,目前效果最好,比如用YOLO检测人脸区域、用OpenFace或MediaPipe做面部关键点提取。但缺点是你要跑模型训练或者至少需要较重的推理框架,对于很多连环境都装不利索的同学来说,光配置CUDA和Torch就能折腾一星期。
1.2 Dlib方案的核心优势
折中下来,Dlib的HOG + 线性分类器做人脸检测,配合预训练好的68点面部关键点模型,是目前毕设场景下综合性价比最高的方案。
首先,Dlib官方提供了训练好的shape_predictor_68_face_landmarks.dat模型,你不需要自己采样和标注,下载直接用即可。其次,Dlib的CPU推理速度很快,普通笔记本上跑实时视频流完全可以做到每秒20帧以上。更重要的是,代码难度低,关键点提取只需要几行API调用,把精力留在逻辑层,这对于毕设项目来说极其重要。
这套选型思路对应到最终的实现架构就是:OpenCV负责图像采集和展示,Dlib负责定位68个关键点,numpy做坐标运算,然后基于EAR算法计算眼睛纵横比,加上阈值判定和报警触发,整个系统就成型了。
2. 环境搭建:从Python到Dlib的完整安装指南
2.1 基础环境配置要点
开始写代码之前,先把环境搞定。疲劳检测系统对Python版本要求不苛刻,3.7到3.10都没问题,我建议用3.8或3.9,兼容性最好。如果电脑还没装Python,直接去官网下载对应版本,安装时记得勾选Add Python to PATH,这一步不勾后面命令行输python会提示找不到命令。
装完之后,建议用虚拟环境管理依赖,不要让全局环境越来越乱。在项目目录下打开终端执行:
python -m venv venv venv\Scripts\activate # Windows source venv/bin/activate # Linux/Mac2.2 核心依赖库安装
激活虚拟环境后,依次安装四个核心库:
pip install opencv-python pip install dlib pip install numpy pip install imutilsOpenCV是图像处理和摄像头操作的主力,imutils是方便做图像缩放等操作的辅助库。numpy不用多说,坐标计算全靠它。
这里头最麻烦的是Dlib的安装。在Windows上pip直接装dlib经常会碰到编译错误,因为Dlib需要CMake和C++编译器。我的建议是优先尝试pip安装,如果报错再考虑两种替代方案:
第一种,去Visual Studio官网安装Desktop development with C++工作负载,装完重开终端再执行pip install dlib,这种方案最彻底。第二种,直接找别人编译好的whl版本文件下载到本地再pip install,可以省去编译时间,但要注意whl版本必须和你的Python版本、系统位数匹配。
2.3 模型文件下载与项目目录规划
Dlib的68点模型文件在网上可以直接搜到shape_predictor_68_face_landmarks.dat,文件大约100MB左右,需要单独下载。下载后放在项目的models目录下。
一个清晰的目录结构对于毕设答辩很重要,我习惯这样组织:
driver-fatigue-detection/ ├── main.py # 主程序入口 ├── config.py # 参数配置(阈值、报警时间等) ├── models/ │ └── shape_predictor_68_face_landmarks.dat └── utils/ ├── __init__.py ├── eye_aspect_ratio.py # EAR计算函数 └── alarm.py # 报警模块这种结构的好处是把功能拆分清楚,答辩时老师问每个文件是干什么的,你能答得条理分明。
3. EAR算法原理:眼睛开合度的精确数学表达
3.1 68个关键点与眼部特征点定位
Dlib的68点面部关键点模型,把脸部分成了若干个区域:下颌轮廓17个点、眉毛5个点、鼻子9个点、眼睛12个点、嘴巴20个点,另外还有面部轮廓的若干点。其中真正用于疲劳检测的是左右眼周围的12个点,左眼索引是36到41,右眼索引是42到47。
每一只眼睛周围6个点,这些点形成了一个近似菱形的轮廓。当人睁眼时,这个菱形在垂直方向上的开口较大;当人闭眼时,上下眼睑的点会靠近甚至重合。EAR算法就是把这个几何特征转换为一个数值指标。
3.2 EAR计算公式与标准阈值
EAR(Eye Aspect Ratio,眼睛纵横比)的计算公式如下:
def eye_aspect_ratio(eye): # 计算眼睛垂直方向上的两对关键点之间的欧氏距离 A = dist.euclidean(eye[1], eye[5]) B = dist.euclidean(eye[2], eye[4]) # 计算眼睛水平方向上的关键点之间的欧氏距离 C = dist.euclidean(eye[0], eye[3]) # EAR = (A + B) / (2 * C) ear = (A + B) / (2.0 * C) return ear其中eye是一个包含6个坐标点的列表,排列顺序是:眼头到眼尾方向依次排列。分子中的A对应上下眼睑第一对点的距离,B是第二对点的距离,C是眼头到眼尾的水平距离。
人在正常睁眼状态下,EAR大约在0.25到0.35之间。当眼睛开始闭合时,垂直距离迅速减小,EAR值随之快速下降。闭眼状态下EAR会趋近于0。学术界和工程实践中常用的闭眼判定阈值是0.2,也就是说EAR低于0.2时认为眼睛处于闭合状态。但这个值并不是绝对的,每个人的眼睛大小和睁开幅度不一样,实际部署时最好校准一次。
我在调系统时发现,阈值设置太敏感会导致戴眼镜的同学频繁误报,太迟钝又检测不到轻微瞌睡。合理的做法是把阈值设置在0.18到0.25之间,根据实际测试情况微调。另外可以做一个简单的自适应校准,在系统启动前采集用户正常睁眼状态的EAR均值,用这个均值的70%作为动态阈值。
4. 实时检测系统完整实现:代码逐段拆解
4.1 初始化模型与打开摄像头
主程序的第一步是加载Dlib的面部检测器和关键点预测器,同时打开摄像头。这里有个细节要注意,Dlib提供了两个检测器:get_frontal_face_detector()检测人脸框,shape_predictor加载模型预测关键点。
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("models/shape_predictor_68_face_landmarks.dat") # 定义左右眼的关键点索引 LEFT_EYE_START, LEFT_EYE_END = 36, 42 RIGHT_EYE_START, RIGHT_EYE_END = 42, 48 # 打开摄像头 cap = cv2.VideoCapture(0) if not cap.isOpened(): raise IOError("无法打开摄像头,请检查设备索引或驱动")摄像头打不开是高频问题,排查思路是先确认设备管理器里能看到摄像头,然后试试把VideoCapture参数从0改成1,因为有些笔记本的自带摄像头索引并不是0。
4.2 核心循环:实时抓帧与关键点提取
系统主干是一个while循环,持续从摄像头读取图像帧。为了提高速度,每帧先做灰度转换,再缩小尺寸到宽度500像素左右,这样可以显著降低面部检测的计算量。
while True: ret, frame = cap.read() if not ret: break # 缩小帧以加速检测 frame = imutils.resize(frame, width=500) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 检测人脸 faces = detector(gray, 0) for face in faces: # 获取关键点 landmarks = predictor(gray, face) # 转为numpy数组方便计算 landmarks_np = np.array([[p.x, p.y] for p in landmarks.parts()]) # 提取左右眼的关键点坐标 left_eye = landmarks_np[LEFT_EYE_START:LEFT_EYE_END] right_eye = landmarks_np[RIGHT_EYE_START:RIGHT_EYE_END] # 计算EAR值 left_ear = eye_aspect_ratio(left_eye) right_ear = eye_aspect_ratio(right_eye) ear = (left_ear + right_ear) / 2.0 # 绘制关键点 for i in range(36, 48): cv2.circle(frame, (landmarks_np[i][0], landmarks_np[i][1]), 2, (0, 255, 0), -1)以上每次循环会输出一个当前帧的平均EAR值,左右眼分别计算的用意在于消除单侧遮挡或角度带来的误差。
4.3 疲劳判定逻辑:连续帧闭眼统计
单个画面帧EAR低于阈值还不足以说明疲劳,因为正常人也经常眨眼,单次眨眼持续约100到400毫秒,对应到30帧每秒的摄像头就是3到12帧。所以判断疲劳必须基于连续时间窗口内的闭眼比例。
我采用的策略是记录EAR低于阈值的帧数counter,当counter连续超过一个设定值时判定为一次闭眼事件。然后维护一个闭眼帧率指标,比如在最近60秒内,如果闭眼帧数占总帧数的比例超过15%,或者出现连续3秒以上眼睛闭合的情况,就判断为疲劳状态。
EYE_AR_THRESH = 0.2 # EAR低于该值视为闭眼 EYE_AR_CONSEC_FRAMES = 48 # 连续闭眼48帧触发疲劳报警(约1.6秒) frame_counter = 0 alarm_on = False # 在检测循环内部 if ear < EYE_AR_THRESH: frame_counter += 1 if frame_counter >= EYE_AR_CONSEC_FRAMES: if not alarm_on: alarm_on = True cv2.putText(frame, "FATIGUE DETECTED!", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 2) # 触发声音报警 alarm_trigger() else: frame_counter = 0 alarm_on = FalseEYE_AR_CONSEC_FRAMES=48这个值的含义是:假设摄像头是30帧每秒,48帧就代表1.6秒的持续性闭眼。正常人正常眨眼不会持续这么久,所以这个值能有效防止误报。这个阈值设定的逻辑需要写进论文或答辩说明里,老师一定会问到。
5. 功能扩展与创新点设计:拉开毕设档次的加分项
5.1 哈欠检测与头部姿态估计
只做眼睛疲劳检测能拿个基础分,想拿高分需要适当增加创新点。最容易加的两个功能是哈欠检测和头部姿态估计。
哈欠检测的原理和眼睛检测类似,Dlib的68点模型里嘴巴周围有20个点,索引从48到67。计算嘴巴纵横比MAR(Mouth Aspect Ratio),当MAR超过阈值且持续一定帧数时判定为哈欠。多个连续哈欠可以进一步增强疲劳判断的置信度。
头部姿态估计稍微复杂一些,思路是利用人脸关键点二维坐标和标准三维人脸模型的对应关系,通过solvePnP算法估计头部的旋转角度。当头部频繁低头或大角度倾斜时,同样提示疲劳。OpenCV提供了cv2.solvePnP函数,传入3D人脸基准点和对应的2D关键点,即可求出旋转向量再转换成欧拉角。
5.2 数据记录与可视化界面
为了展现系统的完整性,建议把检测结果持久化到本地,每隔一分钟记录一次平均EAR值、闭眼次数、疲劳状态等数据,使用CSV格式存储。答辩时可以直接展示这段时间的数据变化趋势,说明系统确实在持续工作。
如果想要界面更好看,可以用PyQt5或者Tkinter做一个简单的桌面应用,把视频画面嵌入窗口,疲劳状态用红灯绿灯指示,报警记录显示在右侧列表。这一步虽然不是核心功能,但视觉呈现效果会直接影响答辩评分。
5.3 参数配置模块的设计思路
把阈值、帧率、报警声音路径等参数单独抽到config.py的好处是,答辩演示时可以当着老师的面修改阈值,现场演示系统参数调整对结果的影响,这个动作比任何口头解释都有说服力。
# config.py EYE_AR_THRESH = 0.2 EYE_AR_CONSEC_FRAMES = 48 MOUTH_AR_THRESH = 0.6 MOUTH_CONSEC_FRAMES = 30 ALARM_SOUND_PATH = "alarm.wav" MODE = "all" # eye/mouth/head/all6. 高频报错与实操心法:血泪踩坑记录
6.1 Dlib安装失败
这个问题出现频率极高。Windows下pip install dlib最常见的报错是找不到CMake或者无法找到Visual Studio编译器。我的调试建议是:先安装CMake并加入PATH,再安装Visual Studio Build Tools勾选C++桌面开发组件,装完重启终端再试。还是不行的话就直接下载对应版本的whl安装包,用pip install安装本地文件,这条路径成功率最高。
6.2 摄像头画面卡顿严重
卡顿的原因多半不是算法慢,而是每一帧的分辨率太高。默认的1280x720摄像头画面跑Dlib检测非常吃力。解决方案是先用cv2.CAP_PROP_FRAME_WIDTH和cv2.CAP_PROP_FRAME_HEIGHT把摄像头分辨率降到640x480,或者在读帧之后resize到宽度400到500像素。实测可以把帧率从8帧提升到25帧以上。
6.3 戴眼镜或强光下误报严重
Dlib关键点检测在强反光、逆光和戴墨镜场景下效果会变差。提升鲁棒性的手段有几个:一是把图像做直方图均衡化,通过cv2.equalizeHist增强对比度;二是把单帧眨眼滤除,利用眨眼持续时间作为辅助判据;三是条件允许的情况下用红外摄像头,IR光源下关键点检测受可见光干扰很小。
6.4 程序退出时摄像头资源释放
很多同学的代码里没有处理释放资源的逻辑,运行一段时间后摄像头被占用导致下次启动失败。主循环结束后需要执行cap.release()和cv2.destroyAllWindows(),用键盘按q退出循环之前也要确保这两行被调用。
7. 从毕设到实际工程的差距与改进方向
真诚地讲,这类基于传统图像特征的疲劳检测系统在受控环境下效果不错,但距离真正的商业级落地还有明显差距。实际行车环境下,光照剧烈变化、驾驶员频繁转头、视线偏离摄像头、墨镜遮挡等问题都会让Dlib方案的准确率下降。商用系统通常要结合车内红外摄像头、方向盘转向传感器、车道偏离预警等多模态数据,才能达到足够的可靠性。
如果你的毕业设计有余力,可以往深度学习方向探索一下,比如用PyTorch搭建一个小型卷积网络替代Dlib的关键点检测,或者直接用MediaPipe的Face Mesh模型,能做到468个面部关键点的实时检测,精度和鲁棒性都比Dlib高一个量级。但作为毕设,Dlib方案已经足够展示你对图像处理、特征工程和实时系统设计的理解。
最后再分享一个写论文和做答辩PPT时的小技巧:不要只放系统的最终效果截图,最好准备一张从原始帧到灰度图、人脸框、关键点标定、EAR曲线变化、疲劳状态输出的完整流程图。这张图能一次性串起你论文里最核心的五六个章节,老师顺着图问你的时候,你答起来也会更有条理。做这类项目,别指望一遍跑通,耐心调试才是常态。
本文还有配套的精品资源,点击获取