ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

YOLOv5+dlib驾驶员疲劳检测:两级管线原理与代码实现

YOLOv5+dlib驾驶员疲劳检测:两级管线原理与代码实现 简介面向计算机专业毕业设计及深度学习实战学习者这份基于YOLOv5与dlib的驾驶员疲劳检测项目提供了完整可运行的源码、预训练权重与使用说明。项目结合YOLOv5目标检测和dlib人脸68关键点模型可识别驾驶员闭眼、打哈欠等疲劳状态适合作为毕设、课程设计或期末大作业。资源共93个文件压缩包约136.5MB包含Python源码、YOLOv5模型配置yaml文件、PyTorch权重pt、dlib人脸关键点dat模型、SVM分类器及MP4测试视频等目录内还提供README使用说明和requirements依赖清单便于环境配置与快速启动。已有44人浏览学习。代码结构清晰从数据集加载、模型训练到检测推理均有对应脚本配合输入输出视频可直观看到检测效果能够帮助初学者快速复现疲劳检测流程也为二次开发与论文撰写提供扎实基础。1. 基于YOLOV5加dlib的驾驶员疲劳检测识别两级管线的分工逻辑驾驶员疲劳检测是车载视觉里少数靠一个普通摄像头就能完整跑通的场景基于YOLOV5加dlib的实现方式是毕设里出现频率很高的组合。两个模型任务边界互补YOLOV5负责在行车画面里可靠地找出人脸dlib负责在框内做68点关键点定位EAR、PERCLOS、MAR 这些疲劳指标由此计算。很多人一开始用 dlib 自带的 get_frontal_face_detector 做人脸检测侧脸、暗光、车窗反光下召回率掉得很快行车视频经常整段丢脸。换成 YOLOV5 后漏检明显缓解dlib 只做框内关键点回归耗时也降到可接受范围。这本质上是检测加关键点的两级管线两条技术路线各有各的擅长区。文章按管线逐层展开YOLOV5网络结构与 dlib 的原理选型、环境配置与依赖安装、完整检测代码、YOLOV5训练自己的数据集与阈值校准覆盖毕设从复现到改进的全过程。2. YOLOv5网络结构与dlib关键点模型疲劳检测的原理与选型做这个项目之前先把两件事想清楚YOLOV5 在管线里到底承担什么角色dlib 的 68 点模型为什么不能交给 YOLO 去完成。理解这两层后面的参数调整和论文里的方案对比才不会写成流水账。2.1 YOLOv5网络结构CSPDarknet骨干与PANet多尺度输出YOLOv5 的网络结构是目标检测里最标准的工程化设计之一不需要啃完整论文抓住骨干、颈部、检测头三个部分就够用。输入一张 640×640 的图后Backbone 采用 CSPDarknet 提取特征核心模块是 C3。C3 把输入沿通道拆成两路一路经过若干 Bottleneck 做深层变换另一路直接跨接最后拼接再上一层 1×1 卷积。这样做的目的和 ResNet 的残差思想一致网络加深的同时控制梯度消失计算量也能压住。对驾驶员人脸这种类别单一、语义不复杂的目标YOLOv5s 的骨干深度已经足够没必要上更大的 m 或 l 版本。Neck 部分用 PANet 做多尺度融合。YOLOv5 从骨干的浅层、中层、深层各取一组特征图先自顶向下做上采样增强再自底向上做路径聚合两趟路径交汇后80×80、40×40、20×20 三个尺度的输出分别偏向近处大脸、中等距离人脸和远处小脸。行车画面里驾驶员头部远近变化频繁缺少多尺度输出会导致人脸框大量漏检这也是 dlib 的 HOG 检测器在同类场景中表现差的原因之一。2.1.1 为什么选择 YOLOv5 而不是 YOLOv8 或更轻的方案YOLOv8 的 anchor-free 设计在通用目标检测上表现更好但疲劳检测是单类目标场景YOLOv5s 的 mAP 不是瓶颈推理速度才是。YOLOv5 工程配套成熟加载本地模型、导出 ONNX、调整超参数的资料都很全网上搜「yolov5网络结构」和「yolov5训练自己的数据集」能直接拿到可对照的步骤遇到问题容易排查。对毕设而言可维护性比纸面精度更重要。2.2 dlib的68点关键点模型EAR与PERCLOS的几何基础dlib 提供的人脸关键点模型 shape_predictor_68_face_landmarks.dat 基于级联回归树ERT训练输入一张人脸框输出 68 个坐标点。这个模型对接近正面的姿态精度很高纯 CPU 推理单张脸耗时很低这是它被广泛用在疲劳检测里的直接原因。68 个点里左眼周围是索引 36 到 41右眼周围是 42 到 47嘴巴周围是 48 到 67。判断闭眼用 EAREye Aspect Ratio眼睛纵横比核心计算只有几行import numpy as np def eye_aspect_ratio(points): # points按顺序传入6个眼角/眼睑坐标: 外眼角、上睑两点、内眼角、下睑两点 vertical (np.linalg.norm(points[1] - points[5]) np.linalg.norm(points[2] - points[4])) / 2.0 horizontal np.linalg.norm(points[0] - points[3]) return vertical / horizontalEAR 的几何含义很直观眼睛睁开时纵向距离和横向距离的比值通常在 0.3 左右闭眼时纵向距离趋近于零EAR 会掉到 0.1 以下。它对头部小幅平移不敏感但对点头俯仰和侧脸敏感这是调阈值时需要注意的边界。PERCLOS单位时间内眼睛闭合程度超过 80% 的时间占比是疲劳判定中引用最多的指标。代码实现一般简化为统计一个滑动窗口内 EAR 低于闭眼阈值的帧数占比超过 0.4 就判定为疲劳。这部分逻辑在第四章给出完整代码。2.3 方案对比为什么不直接用 dlib 自带的人脸检测器方案检测器关键点单帧耗时参考侧脸/暗光鲁棒性训练成本纯 dlibHOG 线性SVMdlib 68点CPU 约 20-40ms低无YOLOv5 dlibYOLOv5sdlib 68点GPU 约 5-15ms高需权重或微调OpenCV Haar dlibHaar级联dlib 68点CPU 约 10-20ms中低无Haar 级联在驾驶员低头看手机或转头时检测窗直接丢失且误检框多。dlib 的 HOG 检测器在分辨率偏低的行车画面里召回率不足典型表现是暗光下隔几帧丢一次脸PERCLOS 统计因此混入大量无效帧。把检测层换成 YOLOV5 后GPU 推理人脸框只需几毫秒整体管线在 25 到 30 帧的视频源上可以接近实时。提示如果毕设要求「能跑、有指标、有可视化界面」这个组合的性价比高于端到端疲劳分类网络。人脸框、关键点、EAR 曲线都是中间产物论文里做展示和分析时素材更丰富。3. dlib下载安装与YOLOv5环境配置从零跑通检测环境问题是「yolov5环境配置」里最高频的报错来源。这个项目涉及两套运行时PyTorch 跑 YOLOv5dlib 跑关键点版本配比一旦出错安装阶段就会卡住根本到不了写代码那一步。3.1 Python、CUDA 与 PyTorch 版本配比先把版本关系定下来再动手装。下表是经过大量实践验证的一组搭配。组件推荐版本说明Python3.8 ~ 3.103.11 以上编译 dlib 容易踩 C 兼容问题CUDA11.8 或 12.1与 PyTorch 预编译 wheel 对应即可PyTorch2.x官方 wheel 内置 CUDA 运行时无需单独装 CUDA Toolkitdlib19.24.x有预编译 wheel能省掉最麻烦的源码编译用 conda 建独立环境是最稳妥的隔离方式conda create -n fatigue python3.9 -y conda activate fatigue pip install torch2.1.0 torchvision0.16.0 --index-url https://download.pytorch.org/whl/cu118 pip install opencv-python numpy matplotlibpip 安装 PyTorch 时指定官方预编译索引CUDA 版本要和显卡驱动兼容。只有 CPU 的机器也可以跑把后续推理参数改成 cpu 即可速度慢一些流程不变。3.2 dlib下载与安装的两种路径dlib 安装是这套环境里最容易栽跟头的地方常见问题集中在源码编译失败和依赖缺失。优先走预编译 wheelpip install dlib19.24.2如果 pip 在当前 Python 版本下找不到匹配的 wheel会退回源码编译此时需要 CMake 和 C 编译器。Windows 上安装 Visual Studio 的「使用 C 的桌面开发」工作负载Linux 上安装 build-essential再执行pip install cmake pip install dlib19.24.2源码编译通常耗时五到十分钟如果报错信息里出现 C2039 或 C1083 这类编号基本都是编译器版本过老或者 Python 版本过高把 Python 降到 3.9 再试一次。dlib 装好后用下面的代码验证能成功加载模型文件就算通过import dlib predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat) print(dlib loaded ok)shape_predictor_68_face_landmarks.dat 要去 dlib 的模型文件列表里下载 68 点版本不要下成 5 点的轻量版两者输出坐标数量完全不同后续索引会全部错位。3.3 YOLOv5源码获取、权重加载与自动标注YOLOv5 的源码通过 git 拉取然后安装依赖列表里的包git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txtyolov5s.pt 权重在第一次运行检测时会自动下载到本地也可以手动放到 weights 目录里。用官方示例图验证安装结果python detect.py --source data/images/bus.jpg --weights yolov5s.pt --conf 0.4运行结束后runs/detect 目录下会生成带检测框的结果图看到框就说明整套环境通了。接下来是人脸数据集的制作这也是「如何通过界面操作yolov5完成数据集的自动标注」这个问题对应的标准做法。先用已训练好的模型跑一遍原始行车截图把检测结果落成 txt 标注再人工修正错误框工作量比纯手标小一个数量级。python detect.py --source raw/ --weights yolov5s.pt --save-txt --save-conf --project label_out--save-txt 会把每个目标的类别、归一化中心坐标和宽高写入同名 txt 文件--save-conf 额外保存置信度--project 指定输出目录。自动标注只适合迁移场景不能完全替代人工复核用它初始化「驾驶员人脸」这种类别单一的检测器再用 labelImg 之类工具修正错框漏框就能得到一份干净的训练集。注意用错误标注训练出来的模型会出现系统性漏检比如永远检不出戴墨镜的人脸。自动标注后的逐张复核这步不能省。4. 疲劳检测完整实现YOLOv5人脸框加dlib关键点加EAR/PERCLOS环境就绪后把各部分拼成完整程序。整条管线可以拆成五步每一步的输入输出都很明确调试时按步骤断开即可定位问题。4.1 检测管线的整体结构管线依次是读取帧、YOLOv5 推理得到人脸框、对每个框调用 dlib shape_predictor 得到 68 点、计算 EAR 和 MAR、滑动窗口统计 PERCLOS 并输出疲劳状态。设计上有个容易忽略的原则YOLOv5 输出的原始人脸框直接用来构造 dlib.rectangle不要在中间再随便裁剪或扩边。dlib 关键点模型对输入框的位置和尺寸很敏感在原始框上做额外处理反而会影响坐标回归结果。类别过滤也放在 YOLOv5 的后处理阶段做不要在拿到框之后再逐个判断。4.2 YOLOv5加载本地模型与dlib关键点衔接代码import cv2 import dlib import numpy as np import torch # 加载本地YOLOv5模型path指向训练好的best.pt model torch.hub.load(., custom, pathruns/train/exp/weights/best.pt, sourcelocal) model.conf 0.45 # 置信度阈值行车场景建议0.4-0.5 model.iou 0.45 # NMS的IoU阈值 model.classes [0] # 只保留人脸类别 predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat) LEFT_EYE [36, 37, 38, 39, 40, 41] RIGHT_EYE [42, 43, 44, 45, 46, 47] MOUTH [60, 61, 62, 63, 64, 65, 66, 67] def landmark_array(shape): # 把dlib返回的shape对象转成numpy坐标数组便于批量计算 return np.array([[shape.part(i).x, shape.part(i).y] for i in range(68)], dtypenp.float32) def ear(points, idx): p points[idx] vertical (np.linalg.norm(p[1] - p[5]) np.linalg.norm(p[2] - p[4])) / 2.0 horizontal np.linalg.norm(p[0] - p[3]) return vertical / horizontal def mar(points, idx): p points[idx] vertical_open np.linalg.norm(p[2] - p[6]) # 上下嘴唇中点距离 horizontal np.linalg.norm(p[0] - p[4]) # 左右嘴角距离 return vertical_open / horizontaltorch.hub.load 使用 sourcelocal 表示从本地源码目录加载path 指向 weights 里的 best.pt加载本地模型之后不再需要访问网络下载权重。EAR 函数里 0 到 5 的索引顺序对应外眼角、上睑、内眼角、下睑的坐标排列MAR 的分子是嘴唇垂直开度分母是嘴角横向宽度打哈欠时垂直开度被明显拉大MAR 会超过正常说话的水平。4.3 主循环PERCLOS滑动窗口与疲劳判定cap cv2.VideoCapture(0) WINDOW 60 # 滑动窗口长度30fps下对应2秒 EAR_THRESH 0.22 # 低于该值记为闭眼帧 PERC_THRESH 0.40 # 窗口内闭眼帧占比超过40%触发疲劳 MAR_THRESH 0.60 # 打哈欠MAR阈值 YAWN_FRAMES 45 # 连续45帧张口判定一次哈欠 eye_state [] # 定长闭眼状态队列 yawn_count 0 yawn_flag False while True: ret, frame cap.read() if not ret: break results model(frame, size640) boxes results.xyxy[0].cpu().numpy() # [x1, y1, x2, y2, conf, cls] for box in boxes: x1, y1, x2, y2 map(int, box[:4]) rect dlib.rectangle(x1, y1, x2, y2) shape predictor(cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY), rect) pts landmark_array(shape) eye_ratio (ear(pts, LEFT_EYE) ear(pts, RIGHT_EYE)) / 2.0 mouth_ratio mar(pts, MOUTH) eye_state.append(1 if eye_ratio EAR_THRESH else 0) if len(eye_state) WINDOW: eye_state.pop(0) # 丢最旧帧保持窗口定长 perc sum(eye_state) / len(eye_state) if mouth_ratio MAR_THRESH: yawn_count 1 else: yawn_count 0 yawn_flag yawn_count YAWN_FRAMES fatigue perc PERC_THRESH or yawn_flag cv2.putText(frame, fEAR:{eye_ratio:.2f} PERC:{perc:.2f}, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) if fatigue: cv2.putText(frame, FATIGUE, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 2) cv2.imshow(driver fatigue, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()eye_state 是一个定长队列每帧只入队一个 0 或 1超过 60 就丢弃最旧的这样 PERCLOS 反映的是最近两秒的相对统计量而不是从程序启动开始累计的全局值。疲劳判定用 OR 逻辑闭眼占比超标或持续打哈欠都会触发。EAR_THRESH 在 0.20 到 0.25 之间调PERC_THRESH 对应 P80 标准的 0.4。4.4 疲劳判定参数表与常见误用参数推荐范围影响因素调参建议EAR_THRESH0.20 ~ 0.25摄像头角度、人眼大小戴眼镜时下调 0.02PERC_THRESH0.35 ~ 0.45窗口长度窗口越大阈值越低MAR_THRESH0.55 ~ 0.65说话与哈欠的区分说话频繁则上调model.conf0.40 ~ 0.50漏检与误检平衡夜景环境降到 0.35常见误用集中在三处。第一把网上免费python源码大全里抄来的 EAR 阈值直接套到行车场景车内摄像头仰角会让 EAR 整体偏低出现睁眼被误判为闭眼的情况。第二把 shape_predictor 当检测器用直接对整帧调用没有输入框时它会给出无意义坐标或直接崩溃。第三PERCLOS 窗口里混入无人脸帧并把它们当闭眼帧计数正确做法是检测不到人脸时暂停窗口更新而不是强行计 1。5. YOLOv5训练自己的数据集与疲劳检测阈值校准技巧5.1 数据集组织与训练命令YOLOv5 训练自己的数据集只需要三样东西图片、与图片同名的 txt 标注文件、一个 data.yaml 描述文件。train: datasets/face/train/images val: datasets/face/val/images nc: 1 names: [face]txt 标注每行格式是 cls cx cy w hcx cy w h 是归一化后的中心点和宽高。类别只有 face 一种数据量在 2000 到 3000 张之间就能训出可用模型再往上加对精度的提升会变缓。训练命令如下python train.py --data datasets/face.yaml --weights yolov5s.pt --epochs 100 --batch-size 16 --img 640 --patience 20patience 20 表示连续 20 个 epoch 指标不提升就提前停止避免无效的长时间训练。5.2 超参数调整与模型验证yolov5 超参数集中在 hyp.scratch-low.yaml 中实际需要动的只有几个。lr0 初始学习率默认 0.01数据量小于 1000 张时降到 0.001 更稳mosaic 数据增强默认开启对驾驶员这种大目标场景不用关anchor 不需要手调训练前会自动按数据集重新聚类。训练结束后看 runs/train/exp 下的 results.png关注 mAP 曲线是否收敛。部署时务必用 best.pt 而不是 last.pt前者是验证集上指标最好的权重。整个过程和「yolov5模型训练教程」里描述的标准流程一致不需要额外魔改。5.3 上车前自动校准 EAR 阈值的技巧疲劳检测落地时最大的变量是驾驶员本人。眼皮厚度、眼镜、摄像头安装高度都会让 EAR 的基准值偏移固定阈值必然产生误报。一个实用的做法是启动后做三秒自动校准让驾驶员正常睁眼平视前方采集 90 帧 EAR用均值和标准差计算个体化阈值。CALIB_FRAMES 90 ear_samples [] # 校准阶段: 驾驶员睁眼正视前方采集EAR样本 while len(ear_samples) CALIB_FRAMES: ret, frame cap.read() if not ret: continue # 复用4.2/4.3中的人脸检测与关键点代码得到eye_ratio ear_samples.append(eye_ratio) mean_ear float(np.mean(ear_samples)) std_ear float(np.std(ear_samples)) # 均值减两倍标准差且不低于0.18的下限 EAR_THRESH max(0.18, mean_ear - 2 * std_ear) print(fcalibrated EAR_THRESH {EAR_THRESH:.3f})两个标准差的下取法对应约 95% 的正常睁眼分布闭眼帧的 EAR 会明显落入阈值之下。下限 0.18 的作用是防止极端样本把阈值压得过低导致完全失去检测能力。校准代码就是主循环前加一个纯统计阶段帧数采满后回填 EAR_THRESH 再进入正式疲劳检测这个细节能把误报率压下去答辩时也属于能让评审眼前一亮的工程处理。本文还有配套的精品资源点击获取
返回列表