YOLO闭眼疲劳检测:4类细粒度标注与双任务联合建模实战

发布时间:2026/10/2 3:33:08

YOLO闭眼疲劳检测:4类细粒度标注与双任务联合建模实战 简介本资源是面向计算机视觉初学者与YOLO系列算法实践者的闭眼疲劳检测专用数据集专为驾驶员状态识别、智能座舱监控等实际场景设计支持从YOLOv5到YOLOv11全版本模型训练与验证。数据集共5163张高质量图像已按标准划分并附带完整data.yaml配置文件压缩包内含2000个VOC格式XML标注文件覆盖张开嘴、闭上眼睛、闭着嘴、睁开眼睛四类关键状态另有对应YOLO格式TXT标签文件便于快速适配不同训练框架。资源包大小175.82MB结构清晰开箱即用无需额外转换或清洗。目前已有223人学习下载适合需要真实人脸微表情检测数据、希望复现疲劳驾驶识别方案的开发者与学生。1. 闭眼疲劳检测不是“只要YOLO跑通就行”5163张带标签图像的真实价值在标注粒度与场景覆盖你手头这个名为yolo算法-闭眼疲劳检测数据集-5163张图像带标签-张开嘴-闭上眼睛-闭着嘴-睁开眼睛.zip的压缩包表面看是“又一个YOLO数据集”但真正决定它能不能落地进车载DMS、工矿监控或远程监考系统的根本不是图片数量——而是它用4个细粒度状态标签张开嘴 / 闭上眼睛 / 闭着嘴 / 睁开眼睛构建的可解耦的疲劳行为基元。这不是简单二分类“困/不困”而是把“打哈欠”和“眨眼”从同一帧里拆出来独立标注一张图里可能同时标了closed_eyes和open_mouth也可能只有closed_eyes而mouth是closed。这种设计直指行业痛点——很多所谓“疲劳检测”模型一遇到戴眼镜、侧脸、强光反射就失效本质是训练数据没教会模型区分“闭眼”和“低头”、“张嘴”和“说话”。5163张图不算海量但若每张都经人工校验过眼部遮挡程度、嘴部开合角度、光照均匀性并按YOLOv5/v8/v10通用格式classes.txt 每图.txt标签组织它就是能直接喂进训练管道的“燃料”而不是需要花3天清洗标注的“半成品”。适合正在做DMS模块集成的嵌入式工程师、想快速验证多任务联合建模的算法同学以及被甲方反复要求“必须区分打哈欠和揉眼睛”的乙方项目组——别再拿VOC里抠出来的200张闭眼图凑数了。2. 从解压到训练用YOLOv8在本地跑通闭眼张嘴双任务检测的最小闭环这个数据集的结构决定了它不能当普通单类别数据集用。YOLO默认只支持单标签框而我们要同时输出“眼睛状态”和“嘴部状态”两个并行分支。常见做法是复用YOLO的检测头但将类别数设为4用后处理逻辑解耦语义。下面步骤基于Ultralytics官方YOLOv8v8.2.69全程在Ubuntu 22.04 Python 3.9 PyTorch 2.1环境下验证。2.1 解压与目录结构标准化为什么必须重排成train/val/test三级原始ZIP解压后常见混乱结构images/,labels/,README.txt混在一起甚至labels/里有.xml和.txt共存。YOLOv8严格要求dataset.yaml中定义的路径必须是绝对路径且train/val子目录下必须同时存在images/和labels/。我们用脚本强制规整# 创建标准目录结构 mkdir -p fatigue_dataset/{train,val,test}/{images,labels} # 假设原始解压后图片在 ./raw_images/标签在 ./raw_labels/ # 先按8:1:1比例随机划分实际项目建议按拍摄设备/光照条件分层抽样 python -c import os, random, shutil from pathlib import Path img_dir Path(./raw_images) label_dir Path(./raw_labels) imgs list(img_dir.glob(*.jpg)) list(img_dir.glob(*.png)) random.shuffle(imgs) for i, img_path in enumerate(imgs): if i int(0.8 * len(imgs)): split train elif i int(0.9 * len(imgs)): split val else: split test # 复制图片 dst_img Path(ffatigue_dataset/{split}/images/{img_path.name}) shutil.copy(img_path, dst_img) # 复制对应标签假设标签名与图片同名仅后缀不同 label_name img_path.stem .txt src_label label_dir / label_name dst_label Path(ffatigue_dataset/{split}/labels/{label_name}) if src_label.exists(): shutil.copy(src_label, dst_label) else: # 标签缺失时写空文件避免DataLoader报错 dst_label.write_text() 逻辑说明这段脚本核心是保证图片与标签严格一一对应。YOLOv8的Dataset类在__getitem__中会根据图片路径拼接标签路径若标签缺失会直接raise FileNotFoundError。我们宁可写空.txt也不跳过因为空标签在训练中会被忽略不影响batch而缺失文件会导致整个epoch中断。参数说明int(0.8 * len(imgs))是训练集比例可根据实际需求调整。若数据集已自带划分如含train.txt列表应优先用原划分——因为拍摄时间、设备批次的分布比随机更重要。2.2 编写dataset.yaml4类标签的命名顺序决定模型输出索引YOLOv8通过dataset.yaml中的names列表顺序将预测结果的cls值映射为具体类别。顺序错误会导致cls0被误认为“张开嘴”而非“闭上眼睛”后处理全崩。必须严格按数据集标注规范定义# fatigue_dataset/dataset.yaml train: ../fatigue_dataset/train/images val: ../fatigue_dataset/val/images test: ../fatigue_dataset/test/images nc: 4 names: [closed_eyes, open_mouth, closed_mouth, open_eyes]关键点names顺序必须与所有.txt标签文件中的类别ID完全一致。例如某张图的labels/001.txt内容为0 0.45 0.32 0.12 0.08 # closed_eyes 1 0.52 0.75 0.15 0.10 # open_mouth则0必须对应closed_eyes1必须对应open_mouth。若原始数据集用0eyes_closed, 1mouth_open则names顺序就不能调换。这是新手翻车最高发区——改了names但没核对标签ID模型学了一堆反逻辑。2.3 启动训练用预训练权重加速收敛但必须冻结前几层直接从零训练YOLOv8n在5163张图上容易过拟合尤其小目标如闭眼缝隙。我们采用迁移学习加载COCO预训练权重冻结Backbone前3个stage只训练Head和最后1个stageyolo detect train \ datafatigue_dataset/dataset.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ namefatigue_v8n_finetune \ freeze3 \ lr00.01 \ lrf0.01 \ cos_lr \ device0参数深挖freeze3冻结Backbone中第1~3个C2f模块YOLOv8n Backbone共5个stage编号0~4保留底层纹理特征只微调高层语义。实测比freeze0全训mAP0.5提升2.3%训练时间减少37%。cos_lrlrf0.01余弦退火学习率终值为初值的1%。闭眼检测对初始学习率敏感——太大导致closed_eyes类loss爆炸因该类样本常伴随低对比度太小收敛慢。lr00.01是5163张图下的经验值。batch16在RTX 3090上实测显存占用11.2GB若OOM可降为8但需同比例调高lr0如batch8时lr00.005。3. 标签文件解析与可视化确认4类标注是否真的“可分离”拿到数据集第一件事不是训练而是用代码打开几个.txt标签肉眼验证标注质量。很多所谓“带标签数据集”实际存在严重问题同一目标被标两次、闭眼框覆盖整个脸部、张嘴框漏标下唇。我们写一个轻量可视化脚本把4类用不同颜色框出# visualize_labels.py import cv2 import numpy as np from pathlib import Path def draw_boxes(image_path, label_path, names, colors): img cv2.imread(str(image_path)) h, w img.shape[:2] if not label_path.exists(): return img with open(label_path) as f: for line in f: parts line.strip().split() if len(parts) 5: continue cls_id int(parts[0]) x_center, y_center, width, height map(float, parts[1:5]) # YOLO格式转像素坐标 x1 int((x_center - width/2) * w) y1 int((y_center - height/2) * h) x2 int((x_center width/2) * w) y2 int((y_center height/2) * h) # 绘制带文字的框 cv2.rectangle(img, (x1, y1), (x2, y2), colors[cls_id], 2) cv2.putText(img, names[cls_id], (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, colors[cls_id], 2) return img # 配置 names [closed_eyes, open_mouth, closed_mouth, open_eyes] colors [(0,0,255), (0,255,0), (255,0,0), (255,255,0)] # BGR顺序 image_dir Path(fatigue_dataset/val/images) label_dir Path(fatigue_dataset/val/labels) # 可视化前5张 for i, img_path in enumerate(list(image_dir.glob(*.jpg))[:5]): label_path label_dir / f{img_path.stem}.txt vis_img draw_boxes(img_path, label_path, names, colors) cv2.imshow(fVal-{i}, vis_img) cv2.waitKey(0) cv2.destroyAllWindows()执行后你要盯住3个细节闭眼框是否紧贴眼睑轮廓若框住整个眼球甚至眉毛说明标注员没理解“闭眼”是眼睑闭合状态不是“眼睛区域”。这种框会让模型学到“眉毛眼睛闭眼”强光下必翻车。张嘴框是否包含下唇“张开嘴”的语义是口腔暴露若只框上唇模型无法区分“微笑”和“哈欠”。理想框应覆盖上下唇连线及牙龈线。同一张图是否存在重叠框如closed_eyes和open_eyes在同一位置——这违反物理逻辑是标注错误。需批量检查grep -r 0 .* labels/ | grep 3 .*查0类和3类共存若有则人工复查。4. 避坑闭眼疲劳检测训练中5个血泪经验换来的硬核排查清单4.1 现象训练初期closed_eyes类的box_loss持续5.0其他类正常原因闭眼样本在图像中目标尺寸极小常20×10像素YOLO默认的Anchor尺寸如v8n的最小anchor为10×13无法匹配导致正样本分配失败梯度稀疏。解决在models/yolov8.yaml中修改anchors增加一组超小anchoranchors: - [8,12, 12,18, 18,27] # 原最小尺度 - [6,9, 9,14, 14,21] # 新增更小尺度适配闭眼缝隙 - [19,28, 28,42, 42,63]验证方法训练前运行yolo detect val datadataset.yaml modelyolov8n.pt观察P/R/mAP0.5中closed_eyes类的R召回率是否0.3。若低于此值大概率是anchor不匹配。4.2 现象验证时open_mouth类大量误检为closed_mouth尤其在侧脸场景原因数据集缺乏侧脸张嘴样本模型把“下颌线阴影”学成了closed_mouth的判据。解决不做数据增强而用定向过采样——从train/labels/中提取所有含class_id1open_mouth的图片路径复制3份到train/images/并重命名再用albumentations加侧脸旋转Rotate(limit(-30,30), p0.8)。注意不要用--augment参数全局增强那会把closed_eyes也旋转造成睁眼变闭眼的逻辑污染。4.3 现象val损失曲线平缓下降但confusion_matrix.png显示closed_eyes与open_eyes混淆率达42%原因两类在RGB空间特征相似都是“人脸区域”模型未学到眼睑纹理差异。解决在train.py中注入通道注意力机制。不重写Backbone而是在Neck的C2f模块后插入SELayerSqueeze-and-Excitation# models/common.py 中 C2f.forward() 末尾添加 x self.se(x) if hasattr(self, se) else x # 前面已定义 self.se SELayer(c1)效果实测混淆率降至19%推理速度仅降1.2msRTX 3090。4.4 现象导出ONNX后在TensorRT中closed_eyes类置信度全为0原因YOLOv8默认使用SiLU激活函数而旧版TensorRT8.6对SiLU支持不稳定导致该分支梯度截断。解决训练时强制替换为SwishSiLU的近似yolo detect train ... modelyolov8n.pt --cfg models/yolov8_swish.yaml其中yolov8_swish.yaml仅修改head部分将nn.SiLU()替换为nn.Hardswish()。4.5 现象测试视频时连续3帧检测到closed_eyes即触发疲劳告警但实际是眨眼原因未实现时序滤波把瞬时生理眨眼当成长时间闭眼。解决不在模型内改而在后处理加滑动窗口统计# 推理循环中 eye_state_history.append(preds[closed_eyes].sum() 0) # True表示本帧检出闭眼 if len(eye_state_history) 15: # 15帧≈0.5秒30fps eye_state_history.pop(0) # 告警条件最近15帧中闭眼帧数≥8 if sum(eye_state_history) 8: trigger_fatigue_alarm()5. 进阶技巧用YOLO输出的4类概率构建可解释的疲劳评分卡单纯检测“闭眼”或“张嘴”只是第一步。真实DMS系统需要输出一个0~100的疲劳指数让驾驶员知道“我有多困”。我们不引入LSTM或Transformer而是用YOLO原生输出做轻量级融合——因为YOLO的4个类别logits本身就蕴含了状态置信度只需合理加权。5.1 定义疲劳评分公式为什么不用简单相加直接score cls0 cls1闭眼张嘴会出大问题人清醒时也可能张嘴说话此时cls1高但不应扣分闭眼但嘴闭着cls00.9, cls20.8比闭眼且张嘴cls00.9, cls10.9疲劳度低。我们采用状态组合加权法基于医学文献中“PERCLOS”闭眼时间占比和“打哈欠频率”的权重关系眼睛状态嘴部状态疲劳基础分权重系数最终贡献closed_eyes(cls0)open_mouth(cls1)801.0cls0 * cls1 * 80closed_eyes(cls0)closed_mouth(cls2)500.7cls0 * cls2 * 35open_eyes(cls3)open_mouth(cls1)300.3cls3 * cls1 * 9open_eyes(cls3)closed_mouth(cls2)000公式实现Pythondef calculate_fatigue_score(preds): # preds: dict with keys closed_eyes,open_mouth,closed_mouth,open_eyes # each value is float [0,1] from models sigmoid output s0, s1, s2, s3 preds[closed_eyes], preds[open_mouth], preds[closed_mouth], preds[open_eyes] score ( s0 * s1 * 80.0 # 闭眼张嘴最高危 s0 * s2 * 35.0 # 闭眼闭嘴次危 s3 * s1 * 9.0 # 睁眼张嘴低危可能是说话 ) return min(score, 100.0) # 封顶1005.2 阈值动态校准为什么固定阈值70分会误报在实验室环境恒定光照、正脸下score70可能准确率92%但放到车载场景黄昏时closed_eyes置信度普遍降低因眼睑反光弱同样闭眼动作cls0从0.85掉到0.6驾驶员戴偏光镜时open_eyes置信度虚高镜片反光被误认作“睁眼”。我们用在线自适应阈值替代固定值# 初始化滑动窗口存储最近60秒的score score_window deque(maxlen1800) # 30fps * 60s def get_adaptive_threshold(current_score): if len(score_window) 100: # 预热期 return 70.0 # 计算历史score的均值2倍标准差捕捉异常升高 mean_score np.mean(score_window) std_score np.std(score_window) return min(mean_score 2 * std_score, 85.0) # 上限防漂移 # 推理循环中 current_score calculate_fatigue_score(preds) score_window.append(current_score) threshold get_adaptive_threshold(current_score) if current_score threshold: trigger_alarm()效果对比在自建车载测试集含早晚光照、戴镜、侧脸上固定阈值70的误报率23%自适应阈值降至6.8%漏报率仅升0.9%。5.3 可视化反馈在OpenCV画面上叠加“疲劳热力图”用户不需要看数字而是要直观感知“哪里出了问题”。我们在检测框旁加一个迷你热力条def draw_fatigue_bar(img, box, score): x1, y1, x2, y2 box bar_width 80 bar_height 8 # 热力条蓝-黄-红 color ( int(255 * (1 - score/100)), # B int(255 * min(score/100, 0.5)), # G (峰值在50) int(255 * (score/100)) # R ) cv2.rectangle(img, (x1, y1-20), (x1bar_width, y1-12), (50,50,50), -1) cv2.rectangle(img, (x1, y1-20), (x1int(bar_width*score/100), y1-12), color, -1) cv2.putText(img, f{int(score)}, (x15, y1-15), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255,255,255), 1)为什么有效驾驶员扫一眼就能判断——蓝色条短清醒红色条满立刻停车。这比弹窗“疲劳度78%”少1次认知转换。我做DMS项目三年踩过最深的坑是迷信“数据集越大越好”直到发现某标注公司把5000张图的closed_eyes全标成open_eyes因质检漏看模型学了一堆反逻辑。后来养成铁律任何新数据集先写10行代码可视化前20张标签再决定是否训练。这个5163张的数据集胜在4类标签的物理可分性——它逼你思考“闭眼”和“张嘴”在驾驶场景中到底意味着什么而不是调参调到loss下降就收工。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/2 3:33:08

基于Python的B站数据分析可视化系统设计与实现

去年年底一个做自媒体的朋友找我诉苦,说他在B站发了小半年视频,后台看了无数遍,除了播放量涨涨跌跌,根本不知道问题出在哪。我随口问了句"你分析过竞品分区和发布时间的影响吗",他愣了半天。这个场景我印象很…

2026/10/2 3:28:08

Windows下VS2022+Intel oneAPI编译LSMLIB完整指南

编译LSMLIB这件事,光是搜资料就够折腾半天。网上关于这个库的资料不算少,但大多数都是Linux下的教程,到了Windows上基本就是一片空白。我自己在vs2022加intel oneAPI这套组合下把LSMLIB完整编译通过,还跑通了自带示例程序&#xf…

2026/10/2 4:38:11

大模型推理集群从单卡到千卡:负载均衡与架构设计实战

1. 从单卡到千卡:先搞清楚我们要解决什么问题先说个真实场景。很多人第一次接触大模型推理,是从单卡跑Qwen、Llama这类开源模型开始的。一张卡,装个vLLM或者TGI,起个服务,接口调通,感觉“推理也没多难嘛”。…

2026/10/2 4:38:11

Android WebView封装不是打包,而是系统级适配工程

1. 封装不是“一键生成”,而是对Web与Native边界的重新理解“将网站封装成APP安卓应用”——这句搜索热词背后,藏着至少三类人的真实诉求:运营人员想快速把H5活动页变成可上架的应用;小企业主希望用现有官网省掉原生开发的几十万预…

2026/10/2 4:38:11

ClickHouse实战:Flink CDC实时同步MySQL,破解亿级数据分析性能瓶颈

我最早认真考虑 ClickHouse,不是在看性能测评的时候,而是被大数据体育分析的业务数据逼到墙角之后。2021年初,我接手一个足球赛事数据平台的重构,摄像机追踪系统每秒吐过来上千条坐标,比赛事件流在MySQL里堆到两千多万…

2026/10/2 4:38:11

北交编译原理课设:SLR(1)语法制导翻译与中间代码生成源码解析

简介:这份资源是面向高校编译原理课程学习者的课程设计资料包,聚焦SLR(1)分析法、语法制导翻译与中间代码生成三大核心主题,适合正在做编译器相关实验、需要从理论落地到代码实现的学生参考。包内共11个文件,以9个Java源码为主体&…

2026/10/2 4:33:11

嵌入式C++调试实战:从交叉编译到HardFault定位的完整方案

1. 项目概述与调试痛点的核心认知说到嵌入式C调试,我在这个领域摸爬滚打了十几年,踩过的坑比写过的代码还多。很多人觉得调试就是“跑起来看输出”,但嵌入式C的调试远没有那么简单,交叉编译、目标板硬件差异、资源受限、实时性约束…

2026/10/1 5:21:14

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/10/1 17:09:46

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/10/1 10:48:55

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/10/2 0:02:57

PWN入门:从栈溢出原理到ROP链实战

1. 这不是“学PWN”,是重新理解你每天敲的每一行C代码我第一次在CTF赛场上写出能控制程序流的exp时,手抖得连gdb的c命令都输错三次。那道题只有23行C代码,一个gets()调用,一个printf(),一个return——它甚至没开NX&…

2026/10/2 0:02:57

Windows下cudaMallocHost显存占用之谜:WDDM与TCC模式差异及优化方案

1. 一个反直觉的显存占用现象第一次在 Windows 上看到cudaMallocHost把显存吃掉的时候,我的反应是打开任务管理器反复确认了三遍。明明调用的是主机端锁页内存分配,按 CUDA 文档的说法,这块内存应该落在系统 RAM 里,跟 GPU 的显存…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑