news 2026/9/15 5:17:37

PyQt+YOLOv5+dlib驾驶员行为监控系统:疲劳检测与EAR/MAR算法实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PyQt+YOLOv5+dlib驾驶员行为监控系统:疲劳检测与EAR/MAR算法实战

简介:一套基于PyQt、YOLOv5与Dlib的驾驶员行为监控系统课程设计资源包,适合高校计算机、人工智能相关专业学生完成课程设计或毕业设计使用。资源从图形界面搭建、实时视频流处理到疲劳与分心行为识别均有完整实现,既有摄像头画面捕获、人脸关键点定位、闭眼与低头状态判断,也有声音警报触发、阈值设定和日志记录等模块,代码可以直接运行,也便于在此基础上做二次开发。包体共105个文件,以Python源码(66个py)为主体,同时包含界面文件、模型权重、配置文件、音频提示与说明文档,模型涵盖YOLOv5权重、Dlib人脸关键点模型及分类映射表,压缩包整体约224MB,目录结构清晰,便于按功能模块查找。目前已有272人学习下载。借助Dlib提供的68点人脸关键点检测器,使用者可以系统掌握YOLOv5目标检测与关键点定位结合应用的完整流程,并结合OpenCV完成视频帧预处理,通过阈值判定、多行为联合分析构建一套可演示、可答辩的驾驶员状态监测原型,资源也预留了可扩展接口,适合继续加入新行为识别或与车载系统集成。

1. 基于PyQt YOLOv5 dlib的驾驶员行为监控系统,到底在监控什么

刚开始做驾驶员行为监控课设的人,很容易在逆光和偏头场景里被OpenCV自带的检测器搞得怀疑人生。这套基于PyQt YOLOv5 dlib的驾驶员行为监控系统之所以常被选为课程设计,是因为它把问题拆得清楚:YOLOv5在画面里找出人脸、手机、香烟等目标,dlib在人脸区域内提取68个关键点,用EAR/MAR等几何特征判断闭眼和打哈欠,PyQt5负责把这套逻辑包进一个能显示视频流、检测框和报警状态的界面。两个模型并行工作、结果互补,既能覆盖疲劳检测,又能扩展到分神、吸烟、打电话等行为识别,这也是它在求职项目中一直有热度的原因。作为课程设计,它不需要做到企业级的精度,但必须让人看到“每个组件为什么选它、数据在模块间怎么流动、参数调整怎么验证”。

2. YOLOv5与dlib协作的数据流:从检测框到疲劳判定的算法基线

2.1 YOLOv5检测类别与驾驶员行为目标的取舍

在这套系统里,YOLOv5不是拿来检测人脸的唯一途径,它更合适的定位是“宽泛场景的目标识别”。如果你只想判断驾驶员有没有闭眼,dlib的关键点已经足够;但一旦要检测“手里拿着手机”“正在吸烟”“视线偏离前方”,单靠关键点就不够了。所以常见做法是让YOLOv5同时检测人脸、手机、香烟、方向盘等目标。课程设计一般不会从头训练一个复杂模型,而是先用yolov5官网下载的预训练权重,比如yolov5s.pt,在摄像头画面上跑通用80类检测。COCO类别里包含person、cell phone、remote等,开箱就能识别手机;但“香烟”并不在COCO类别里,这就需要在后处理里做二次判断,或者干脆自己标注“smoking”这一类的数据。

这也引出了第一个设计决策:行为判定应该基于YOLOv5的类别置信度,还是基于位置关系?举个例子,手机App的检测框和司机手部的检测框重叠面积超过一定比例,才判定为“使用手机”;单纯检测到手机放在副驾驶座上不应报警。这个规则用IoU(Intersection over Union)或者归一化中心点距离来计算即可。课程设计阶段,建议先不做复杂的姿态估计,而是用2-3个检测框的几何关系来构造行为特征,这样既能体现YOLOv5的价值,又不会把工程难度推到不可控。

2.2 基于dlib的EAR与MAR疲劳特征计算

YOLOv5给出了“目标在哪”,dlib则负责回答“人看起来是否疲劳”。dlib提供的人脸检测器和人脸关键点模型(shape_predictor_68_face_landmarks.dat)输出68个关键点。左眼用点36到41表示,右眼用点42到47表示,嘴巴用点49到68表示。EAR(Eye Aspect Ratio)就是眼睛纵横比,定义是垂直方向两点距离的均值除以水平方向两点距离;MAR(Mouth Aspect Ratio)则用同样的思想衡量嘴的张合幅度。计算EAR的代码如下:

import dlib import cv2 import numpy as np detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat") def eye_aspect_ratio(eye_points): # eye_points 是6个关键点坐标组成的numpy数组 A = np.linalg.norm(eye_points[1] - eye_points[5]) B = np.linalg.norm(eye_points[2] - eye_points[4]) C = np.linalg.norm(eye_points[0] - eye_points[3]) return (A + B) / (2.0 * C) def mouth_aspect_ratio(mouth_points): # 嘴巴外轮廓点,取内侧两点计算张合幅度 D = np.linalg.norm(mouth_points[13] - mouth_points[19]) E = np.linalg.norm(mouth_points[15] - mouth_points[17]) F = np.linalg.norm(mouth_points[14] - mouth_points[18]) return (D + E) / (2.0 * F)

这里的坐标顺序需要与dlib的68点索引严格对应。实际工程中,不能每帧都对整幅图像跑一次dlib人脸检测器,那会很慢;正确做法是先用YOLOv5给出的人脸框作为ROI,再在ROI里调用dlib检测器或者直接用关键点预测器。这样既能减少计算量,又能缓解倾斜和遮挡带来的漏检。EAR与MAR判定疲劳的核心逻辑是:当EAR连续多帧低于阈值时,认为正在瞌睡;当MAR连续多帧高于阈值时,认为在打哈欠。代码里通常还需要一个眨眼计数器,避免把正常眨眼误判为闭眼。

2.3 判定阈值怎么定,从课程设计到真实驾驶的差异

阈值设置是这类系统的灵魂。课程设计阶段可以给出一个静态阈值表,但这并不代表它可靠。常见做法是统计一段时间内正常睁眼、正常说话时的EAR/MAR基线,然后以基线均值的70%或85%作为动态阈值。下面给出一个参考配置:

行为特征计算指标常见阈值区间判定条件
闭眼/瞌睡EAR0.18 - 0.25EAR低于阈值连续20帧以上
打哈欠MAR0.55 - 0.70MAR高于阈值连续15帧以上
低头/姿态异常头部俯仰角-20°到-40°俯仰角超出基线±15°
使用手机IoU(手机框, 手框)大于0.35持续时间大于3秒
吸烟检测到Smoking类或烟与手框重叠置信度大于0.45连续检测超过1秒

表中的低帧数要求是为了滤除眨眼和瞬间张嘴。如果你拿到的摄像头是15fps,连续20帧意味着1.3秒左右,比较合理;但如果是Jetson Nano这类设备跑起来只有5-8fps,同样的帧数就对应2.5秒以上,响应就太慢了。所以“基于pyqt yolov5 dlib的驾驶员行为监控系统”在课程设计答辩中,老师最常问的问题就是“你这个阈值为什么这样设”,回答的重点应该是:先定语义时间(比如闭眼超过1.2秒报警),再根据实际帧率换算成帧数。真实驾驶场景还要考虑白天逆光、夜间红外补光、佩戴墨镜等因素,静态阈值很容易失灵。课程设计里做到“有基线、有时间窗口、有可调参数”就已经领先大部分同类作业了。

再补充一下数据流的组织:YOLOv5输出的检测框是缩放回原图尺度后的像素坐标,dlib的输入通常是RGB图像,OpenCV读出来的是BGR,因此在做ROI裁剪时要记得通过cv2.cvtColor转换色彩空间,否则关键点容易跑偏。很多人在集成测试时发现dlib检测的眼部位置总是偏下一点,十有八九是颜色通道顺序问题。

3. 环境配置与最小推理链路:YOLOv5官网下载、dlib安装和摄像头检测

3.1 yolov5环境配置:torch、CUDA与官方仓库拉取

作为一个课程设计,环境配置往往比代码本身更消耗时间。yolov5环境配置的第一步是根据本机显卡驱动版本选择CUDA和PyTorch。如果机器有NVIDIA显卡且驱动已经装好,在命令行里执行nvidia-smi,看右上角支持的CUDA版本,再去PyTorch官网选对应版本安装;如果只有CPU,也可以直接用CPU版PyTorch跑yolov5s,一帧300毫秒左右,足够演示。然后是拉取YOLOv5官方仓库。标准的做法是从yolov5官网下载源码压缩包,或者用git clone拉取。不要直接去下载别人二次修改过的包,否则后续训练自定义数据集的yaml文件格式会跟官方脚本不匹配。

conda create -n drive python=3.9 -y conda activate drive pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt

这里用Python 3.9是因为它是YOLOv5官方仓库做回归测试较多的版本,不容易出现依赖冲突。requirements.txt里包含opencv-python、numpy、matplotlib等基础库,安装完成后先跑一个快速验证:python detect.py --source data/images/bus.jpg --weights yolov5s.pt --conf 0.5。如果能看到输出图片上的检测框,说明yolov5环境配置成功。这里出现的--weights yolov5s.pt会自动下载预训练权重,如果下载速度不理想,就需要提前把yolov5s.pt放到根目录,并设置--weights指向本地路径。

3.2 dlib库安装的常见坑与验证

dlib库安装比YOLOv5更依赖编译环境。在Windows上直接pip install dlib经常会因为缺少CMake和VS Build Tools而报错。建议先执行pip install cmake,再安装Visual Studio的C++开发组件,最后重新执行pip install dlib。如果你用的是Anaconda,也可以尝试conda install -c conda-forge dlib,它会自动带上cmake。dlib下载完成后最大的问题是找不到shape_predictor_68_face_landmarks.dat,这个权重文件需要单独下载,官方和第三方都有提供。下载后不要放在带中文的路径下,dlib的read函数在部分Windows版本下对非ASCII路径处理得不好。

故障现象原因处理方式
dlib安装报错缺少CMakepip install cmake 后重试
import dlib失败依赖库冲突使用conda独立环境隔离
.dat文件读取失败路径包含中文移到纯英文路径重新加载

验证dlib安装是否成功,可以用下面这段代码:

import dlib detector = dlib.get_frontal_face_detector() print("dlib version:", dlib.__version__) print("detector:", detector)

如果能正常打印版本号,说明dlib库安装完成。但注意这里加载的关键点模型后续会用到,你需要确认shape_predictor能正常读取.dat文件,否则在运行阶段才会爆出读取失败的RuntimeError,那时候排查成本更高。建议在验证脚本中连模型一起加载:

predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")

注意:模型权重文件路径不要包含中文,dlib在部分Windows版本下可能无法读取。

3.3 用YOLOv5跑通第一帧摄像头检测

摄像头检测与图片检测的不同点在于,你需要处理实时视频流。YOLOv5的detect.py本身支持--source 0调用本机摄像头,但课程设计通常不会直接这样用,因为detect.py做了太多与界面无关的封装。更常见的做法是把YOLOv5作为模块调用,加载模型后对摄像头帧推理。下面的代码演示了最小推理链路:

import cv2 import torch model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True) cap = cv2.VideoCapture(0) while True: ret, frame = cap.read() if not ret: break results = model(frame) rendered = results.render()[0] cv2.imshow("yolov5", rendered) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()

这里先用torch.hub.load加载预训练模型,yolov5s是最常用的轻量版本,在普通笔记本上可以达到20-30fps。model(frame)返回的是一个Results对象,.render()[0]会在原图上绘制检测框。逻辑说明:这段代码没有做任何预处理或后处理,YOLOv5内部会完成letterbox缩放和NMS,所以输入原图尺寸即可。参数说明:cap.read()返回的帧是BGR格式,YOLOv5的推理接口内部会自动转换颜色空间,所以你不需要手动改。但如果后续要同时喂给dlib,就一定要把这一帧先保留一份,改成RGB再传给dlib。

3.4 dlib人脸关键点与YOLOv5检测框的坐标对齐

把两个模型串起来的第一步是让检测框坐标对齐。YOLOv5的Results对象里包含xyxy格式的坐标,单位是原图像素,可以直接用来裁剪人脸区域。由于dlib的关键点预测器接受的是Dlib的rectangle对象或简单的左上右下坐标,我们需要在调用前把YOLOv5的坐标转成整数并做边界裁剪,防止越界。示例:

import dlib boxes = results.pandas().xyxy[0] for _, row in boxes.iterrows(): if row['class'] == 0 and row['confidence'] > 0.5: x1, y1, x2, y2 = int(row['xmin']), int(row['ymin']), int(row['xmax']), int(row['ymax']) face_rect = dlib.rectangle(x1, y1, x2, y2) shape = predictor(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB), face_rect) # shape.num_parts 应该等于68,后续可以提取眼睛和嘴巴坐标

这里的row['class'] == 0指的是COCO数据集里的person类别,如果你自己训练了人脸类别,就替换成对应的类别ID。坐标对齐的坑主要来自两点:一是YOLOv5的高置信度人脸框可能略微大于人脸边缘,没关系,dlib的关键点预测器有一定容错;二是当画面中有多个人时,只会对检测到的每一个人都预测关键点,你需要用row['confidence']和与驾驶位的相对位置过滤掉后排乘客。课程设计里,通常只取画面中心面积最大的那个face框作为目标驾驶员。

4. PyQt5界面集成:视频流显示、报警事件与多线程处理

4.1 PyQt5里用QThread跑YOLOv5推理,避免界面卡死

PyQt5的GUI主线程不能执行耗时操作,而YOLOv5的推理和dlib关键点计算加起来一帧需要100-200毫秒,如果直接在主线程跑,界面会一直无响应,用户会以为程序卡死了。常见做法是写一个继承自QThread的工作类,负责读取摄像头、调用模型、发出图像信号和事件信号。下面是一个最小工作线程的骨架:

class DetectThread(QThread): frame_signal = pyqtSignal(object, object) # 原始帧、绘制后帧 event_signal = pyqtSignal(str, bool) # 事件名、是否触发 def __init__(self): super().__init__() self.running = True self.model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True) self.predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat") def run(self): cap = cv2.VideoCapture(0) while self.running: ret, frame = cap.read() if not ret: continue # 这里串联YOLOv5 + dlib检测逻辑 annotated = frame self.frame_signal.emit(frame, annotated) cap.release()

信号定义在类体中,pyqtSignal(object, object)的两个object参数可以传任意类型,PyQt5不会帮你做类型检查。run方法里的while循环会持续读取摄像头,遇到无法读帧时就跳过。这个线程不负责显示图像,只把结果通过信号发回主线程。这样做的好处是,界面始终能响应用户点击关闭按钮;同时,event_signal可以随时触发报警,而不必等到一帧完整处理完。

4.2 信号槽把检测结果和报警事件推送到界面

主窗口只需要连接信号槽,不需要关心检测细节。假设主界面里有一个QLabel用于显示视频,一个QTextEdit用于打印日志。在MainWindow.__init__里做如下连接:

self.detect_thread = DetectThread() self.detect_thread.frame_signal.connect(self.update_frame) self.detect_thread.event_signal.connect(self.handle_event) self.detect_thread.start() def update_frame(self, raw, annotated): rgb_image = cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB) qt_image = QImage(rgb_image.data, rgb_image.shape[1], rgb_image.shape[0], rgb_image.strides[0], QImage.Format_RGB888) self.video_label.setPixmap(QPixmap.fromImage(qt_image)) def handle_event(self, event_name, active): if active: self.log.append(f"[{time.strftime('%H:%M:%S')}] {event_name}") self.status_label.setText(event_name)

update_frame里的重点是rgb_image.strides[0],这是图像每行的字节数,QImage的构造函数需要它来正确解析内存对齐。如果你直接省略这个参数,在分辨率变化时画面可能会扭曲。handle_event先判断事件是否激活,激活时往日志控件里追加一条带时间戳的记录,同时更新状态栏。参数说明:frame_signal里的两个object,如果只用于显示,只传一个annotated也可以;保留raw的目的是后续做视频回放或帧缓存。

4.3 报警策略与日志记录

报警策略不能只依赖单帧结果,至少要做一个滑动窗口统计。一个实用的做法是在工作线程里维护一个collections.deque,每次检测完记录当前行为状态,当某个行为在最近N帧中出现的比例超过阈值时,再通过event_signal发出报警。这样能避免摄像头偶发的一帧误检导致界面频繁闪烁。报警触发的动作可以是显示红色边框、播放声音、或者写入CSV日志。日志字段建议包含时间、事件名、EAR值、YOLOv5置信度,这样答辩时可以展示这些中间指标来证明系统不是简单的“套一个模型”。

环节线程归属耗时(估算)优化方式
摄像头读取DetectThread5-10ms降低分辨率到640x480
YOLOv5推理DetectThread60-150ms使用TensorRT或半精度FP16
dlib关键点DetectThread5-20ms用YOLOv5人脸框作为ROI
图像缩放与转换DetectThread3-8ms避免重复分配numpy数组

表格里的耗时是按普通笔记本虚拟机估计的,如果部署到Jetson Nano,YOLOv5推理时间会翻2-3倍。优化方向上,先把YOLOv5的检测帧率限制在每两帧处理一次,然后dlib每帧都做,因为关键点相对便宜。这里要注意,工作线程里所有的OpenCV图像都要避免被主线程同时访问,PyQt的信号槽是队列连接,会自动做一次拷贝,所以即使主线程在绘制,工作线程也可以继续写入新的annotated帧,不会有数据竞争。

5. 训练自己的数据集提升行为识别:YOLOv5训练流程与验证技巧

5.1 从COCO预训练到自定义驾驶员行为数据集的迁移

如果需要识别“吸烟”或“低头看手机”这种COCO类别里没有的行为,最可靠的方式是用YOLOv5训练自己的数据集。首先是标注:用labelImg或labelme把视频帧里的目标框成YOLO格式的txt文件,类别从0开始。数据集比例可以按训练集:验证集:测试集=7:2:1划分。训练命令示例:

python train.py --data drive_dataset.yaml --weights yolov5s.pt --epochs 50 --batch-size 8 --img 640

其中drive_dataset.yaml需要配置trainvalncnames。这里--img 640表示把输入图像缩放到640x640像素;--batch-size 8在8G显存的显卡上比较可靠。在yolov5超参数上,课程设计不需要追求完全收敛,50轮一般就能看到明显效果。需要监控的训练指标是mAP@0.5,建议训练过程中打开--cos-lr,让学习率按余弦曲线下降,能减少震荡。

5.2 验证技巧:用视频回放定位误报根因

训练完成后不要只看测试集指标,真正的坑往往在连续视频流里。一个非常有效的验证技巧是:录制一段5分钟的模拟驾驶视频,把YOLOv5的检测结果和dlib的EAR值同时画到视频上,导出后再逐帧回放。当你看到误报时,先别急着调阈值,而是记录误报发生前后的EAR曲线和置信度曲线。如果某次“闭眼报警”其实只是侧脸,EAR本身也在波动,那问题出在dlib关键点漂移;如果EAR值没有明显掉下来,但报警触发了,那问题就在你的判断逻辑里没把连续帧数算对。这个验证方法的力量在于,它把模型性能和决策逻辑分开调试,比直接调一个小数点后两位的阈值高效得多。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 5:17:17

豆包+SiteNative:把AI助手本地化,解锁电脑清理与优化新玩法

1. 先从豆包最火的场景聊起:电脑优化背后的真实需求1.1 豆包为什么突然成了“电脑优化神器”最近一段时间,豆包的热度一直没降过,尤其是“豆包优化电脑的指令”“豆包清理电脑软件指令”“豆包清理c盘指令”这些搜索词,几乎成了豆…

作者头像 李华
网站建设 2026/9/15 5:16:27

MATLAB虹膜识别全流程实现:从定位到编码的可调试系统

简介:本资源是一套完整的MATLAB虹膜识别算法实现方案,面向图像处理与生物特征识别方向的初学者及课程设计者,聚焦虹膜边缘检测、归一化建模与二进制模板匹配等核心环节,解决身份验证系统中虹膜区域定位不准、尺度不一、特征鲁棒性…

作者头像 李华
网站建设 2026/9/15 5:16:15

内存成本重算模型:四维评估法避坑指南

1. 这不是涨价,是“内存税”计算方式错了最近在几个装机群和硬件论坛里,总能看到类似这样的吐槽:“i5-12400F配16G DDR4 3200,主板CPU内存加起来比去年贵了200块,说好的降价呢?”“RTX 4060显卡没涨&#x…

作者头像 李华
网站建设 2026/9/15 5:16:08

USB HID上位机开发:从设备枚举到报告读写的完整解析

简介:一套面向USB HID开发的工程源码资料,整合了C#上位机、C驱动以及STM32 USB-FS-Device库相关代码,覆盖HID设备通信、枚举和驱动调试等关键环节,适合需要编写上位机或底层驱动的嵌入式开发者参考。资源包虽然仅54KB,…

作者头像 李华
网站建设 2026/9/15 5:14:36

Claude Code与OpenClaw中文教程:AI编程助手与部署框架实战

1. 开源 Claude Code & OpenClaw 中文教程项目概述作为一名长期在AI工具应用一线踩坑的老兵,我深知技术文档碎片化带来的痛苦。当看到Claude Code和OpenClaw这两个极具潜力的开源项目时,第一反应不是兴奋,而是担忧——又要在无数零散的英…

作者头像 李华
网站建设 2026/9/15 5:13:37

新手入门看这100个农村电商平台设计规范

新手入门看这100个农村电商平台设计规范 改个需求建站公司拖一周,这种憋屈事谁没经历过?很多老板刚搞农村电商,找外包做个官网或商城,结果发现对方连基本的页面间距都调不好,稍微改个按钮颜色就要排期半天。对于 新手入门…

作者头像 李华