简介:本资源是面向智能座舱与疲劳驾驶监测领域的YOLO目标检测专用数据集,适用于计算机视觉初学者、车载AI算法工程师及高校相关课题研究者,用于训练和验证驾驶人眼部开闭状态与口腔张合动作的多类别目标检测模型。压缩包共含2000个XML格式标注文件,严格遵循PASCAL VOC规范,每个文件对应一张原始图像的边界框坐标、类别标签(如open_eye、close_eye、open_mouth、close_mouth)及关键点辅助信息,便于直接转换为YOLOv5/v8所需格式;整体包体大小为256.91MB,结构简洁,无冗余文件。目前已有69人学习下载,适合快速构建驾驶员状态识别基线模型。读者可直接加载该数据集开展标注格式转换、数据增强实验、mAP对比测试及轻量化部署验证,配套XML结构统一、样本覆盖光照变化与姿态多样性,显著降低数据预处理门槛。 开车犯困这件事,做过实车测试的人都知道有多难搞。早期我用过基于方向盘的偏离检测,也试过仅靠心率带做疲劳推断,但这些方案都有一个共同问题:它们测的是“间接信号”,而不是直接看驾驶员的面部状态。真正可靠的方案,绕不开对“眼睛是否睁开、嘴巴是否张合”的实时判断。这也是YOLO类目标检测模型在这个场景下最吃香的原因——直接把睁眼、闭眼、张嘴、闭嘴这些状态当作目标框出来,交给分类器或时序模型去做疲劳判定。
我最近在整理一套专门用来训练这类模型的资源:驾驶人睁闭眼张合嘴检测数据集,图片配xml格式标签。这篇文章就围绕这个数据集展开,从数据规格、标注细节、模型训练适配到实际部署中的坑,一条线讲清楚。
1. 驾驶人状态检测任务的场景与难点
1.1 为什么单靠传感器方案不够
市面上很多疲劳驾驶预警系统,走的都是“间接推断”路线。比如监测方向盘转角是否长时间不动、车道偏离次数是否陡增、跟车距离是否异常拉近。这些方法有用,但问题也很明显:它们必须等“异常行为已经发生”才能报警,属于事后检测。而真正的疲劳往往伴随面部特征的渐进变化——眼睛闭合时间变长、眨眼频率降低、打哈欠次数增多。这些信号在行为异常之前就已经出现,能争取到的提前量通常是几秒到几十秒,别小看这点时间,高速上几十秒就是几百米甚至上千米的刹车距离。
所以现在的行业主流方案,都转向了直接监测驾驶员面部状态。摄像头装在A柱、方向盘或仪表盘附近,对准人脸,用目标检测模型实时输出眼睛和嘴巴的ROI(感兴趣区域),再结合状态分类器判断当前是睁眼还是闭眼、张嘴还是闭嘴。这个方案看起来简单,但真正落地时对数据集的要求极高。
1.2 脸部状态检测对数据集的特殊要求
和通用目标检测不同,驾驶人状态检测的数据集有几个特殊性,筛选时一定要留心。
第一是视角的集中性。摄像头安装位置固定,人脸角度变化范围有限,所以数据集里的图片主体应该尽量是正脸或近正脸,而不是像COCO那样的任意场景。
第二是光照多样性。车内光照条件跨度极大,白天强光、逆光、夜间仪表盘微光、隧道内忽明忽暗,样本里这些情况都得覆盖。
第三是遮挡情况。戴眼镜、墨镜、口罩、刘海遮眼、手部遮挡,这些情况会导致检测框本身不完整,需要在模型设计时提前考虑。
第四是状态分布均衡性。闭眼和打哈欠这类状态在自然驾驶中占比极低,如果数据集里睁眼和闭嘴样本占九成以上,训练出来的模型对少数类几乎不敏感。好的数据集应该有意均衡两个类别的样本量。
这套驾驶人睁闭眼张合嘴检测数据集,就是围绕以上几个要求整理的。图片内容聚焦于人脸局部区域,标签覆盖眼睛和嘴巴的开关状态,格式上用VOC标准的xml标注,可以直接配合YOLO系模型使用,也方便转成其他格式。
2. 数据集内容规格与目录结构详解
2.1 图片内容与标注类别
这个数据集的核心检测对象是两类器官、四种状态:眼睛的睁开与闭合,嘴部的张开与闭合。具体的标注类别通常以open_eye、closed_eye、open_mouth、closed_mouth这四类来命名,也有数据集会把眼睛统一为eye,嘴统一为mouth,通过标签属性区分开关状态。拿到手之后,第一步就是确认类别名称,因为YOLO训练时类别ID直接依赖标签顺序,搞错了就得改一堆配置文件。
从图片内容看,数据采集通常包含多名不同性别、年龄、是否佩戴眼镜的受试者,背景以车内环境为主,部分图片会有光照变化。图片以单张jpg格式存储,分辨率需要能让眼睛区域至少占到几十个像素以上,否则检测框会小到难以训练。拿到数据后我建议先做一步筛查,把分辨率过低、目标过小、严重模糊的图片剔除掉,这类脏数据对模型精度的拖累比想象中大。
2.2 VOC xml标签结构
xml格式的标注遵循Pascal VOC的标准结构。每张图片对应一个同名xml文件,内部核心信息包含图片路径、尺寸、通道数,以及每个目标的类别名称和边界框坐标。坐标以四个整数记录:xmin、ymin、xmax、ymax,表示边界框左上角和右下角的位置。
这里有一个非常容易踩的坑:VOC坐标是绝对像素坐标,而YOLO训练需要的是归一化到0到1之间的中心点坐标和宽高。转换公式是固定的,但很多人会在宽高计算上出错。YOLO格式里,中心点x需要除以图片宽度,中心点y需要除以图片高度,框宽和框高也分别要除以图片宽高。换句话说是四个值全都除以各自的对应维度,而不是统一除以一个大数。
2.3 验证集与训练集的划分思路
数据集打包后,通常没有直接划分train和val目录,而是所有图片和xml放在一起,需要自己拆。拆分的比例建议按8比2来,如果图片总量不大,可以采用K折交叉验证来评估模型稳定性。
划分时要注意一个点:不要让同一个人不同状态下的图片出现在训练集和验证集里。比如同一个受试者的闭眼图在训练集、睁眼图在验证集,这会导致验证结果虚高,因为模型学到了人的特征而不是状态的特征。如果数据是按受试者组织的,按人划分而不是按图片划分才是正确做法。
3. 标注质量与使用前的关键检查
3.1 坐标越界与空框问题
拿到标注数据,我强烈建议先跑一段脚本做全量检查,而不是直接进训练。常见问题有三个:坐标越界、空框、类别不均衡。
坐标越界是指xmax或ymax超过了图片宽高,这种框在转换YOLO格式后会出现大于1的归一化值,训练时直接报错或产生nan损失。还有一些标注框是空框,即宽或高为0,这类样本应该在预处理阶段直接剔除。
检查和修复逻辑可以这样写:遍历所有xml,读取每个object的边界框坐标,判断是否在图片边界内,宽高是否大于0,类别名称是否在预定义类别列表里。一旦发现问题,可以选择剔除该标注或整张图片。如果是坐标轻微越界(例如越界几个像素),可以用钳位操作把坐标值裁剪回图片范围内。
3.2 类别名称统一与漏标检查
另一个高频问题是从不同来源收集的数据,标签命名风格不统一。有的叫closed_eye,有的叫eye_close,有的叫eye_closed。如果不做统一,训练时就等于把同一类目标拆成了多个类别,导致检测混乱。
统一类别名称的逻辑很简单:在预处理脚本里维护一个映射表,把所有可能的命名变体映射到标准名称,然后批量改写xml文件。改写后还要检查是否有漏标的情况——比如一张图里明显有睁眼和闭眼两个目标,但xml里只标了其中一个。这类问题比较难自动化处理,最笨也最可靠的办法是随机抽样几百张图片,可视化标注框,人工肉眼过一遍。
3.3 数据增强的边界
训练时数据增强能显著提升模型的泛化能力,尤其是光照变化和遮挡场景。但在做增强时要注意一个边界——对闭眼和睁眼这种细微差别的目标,翻转和旋转幅度过大会改变语义。
比如左右翻转是安全的,因为左右眼互换不影响“睁或闭”的语义。但大角度旋转就可能出问题,旋转90度后,眼睛的形态特征变得不典型,模型学到的特征会偏离真实场景。对于眼睛和嘴巴检测,我建议的增强策略以亮度调整、对比度调整、小范围平移缩放、左右翻转为主,尽量避免大角度旋转和极端透视变换。
另外还有一个常用技巧:Mosaic增强在YOLOv4之后基本都是标配,但用在眼睛和嘴巴这种小目标上,有时会适得其反。Mosaic把四张图拼在一起,目标会更小,小目标检测本来就难,拼图后更难。如果发现小目标检测效果差,可以尝试关闭Mosaic或降低其使用概率。
4. 用YOLO训练驾驶人状态检测模型:从xml到推理的完整流程
4.1 环境准备与目录搭建
在开始训练之前,先把环境搭好。推荐使用YOLOv8或YOLOv5,两者对自定义数据集的支持都比较完善,资料也多。以YOLOv8为例,项目目录下需要建立这样的结构:
dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamlimages目录放图片,labels目录放转换好的YOLO格式txt文件,每个txt文件名与对应图片名相同。data.yml内容如下:
train: dataset/images/train val: dataset/images/val names: 0: open_eye 1: closed_eye 2: open_mouth 3: closed_mouth这个文件指定了训练和验证图片的路径,以及类别名称与ID的对应关系。类别顺序必须和标签转换脚本保持一致,否则模型学到的类别含义会错位。
4.2 xml转YOLO txt的脚本实现
目录准备好之后,写一段脚本把VOC xml转成YOLO格式。核心逻辑是读取xml中的size信息(图片宽高)和每个object的边界框坐标,计算归一化中心点坐标和宽高,写入txt文件。
import os import xml.etree.ElementTree as ET def convert_voc_to_yolo(xml_file, out_dir, classes): tree = ET.parse(xml_file) root = tree.getroot() img_width = int(root.find('size/width').text) img_height = int(root.find('size/height').text) out_lines = [] for obj in root.iter('object'): cls_name = obj.find('name').text if cls_name not in classes: continue cls_id = classes.index(cls_name) bbox = obj.find('bndbox') xmin = float(bbox.find('xmin').text) ymin = float(bbox.find('ymin').text) xmax = float(bbox.find('xmax').text) ymax = float(bbox.find('ymax').text) # 钳位到图片边界内 xmin = max(0, min(xmin, img_width - 1)) xmax = max(0, min(xmax, img_width - 1)) ymin = max(0, min(ymin, img_height - 1)) ymax = max(0, min(ymax, img_height - 1)) if xmax <= xmin or ymax <= ymin: continue dw = 1.0 / img_width dh = 1.0 / img_height center_x = (xmin + xmax) / 2.0 * dw center_y = (ymin + ymax) / 2.0 * dh box_w = (xmax - xmin) * dw box_h = (ymax - ymin) * dh out_lines.append(f"{cls_id} {center_x:.6f} {center_y:.6f} {box_w:.6f} {box_h:.6f}") if out_lines: out_name = os.path.splitext(os.path.basename(xml_file))[0] + '.txt' with open(os.path.join(out_dir, out_name), 'w') as f: f.write('\n'.join(out_lines)) classes = ['open_eye', 'closed_eye', 'open_mouth', 'closed_mouth'] # 遍历所有xml文件循环处理即可这段脚本里有三个细节值得注意。一是钳位操作,防止边界越界,但如果你发现坐标系里xmax等于图片宽度这样看似“越界”的值其实是合法的像素索引,说明你对坐标的理解还不够透彻。二是类别过滤,如果xml里有模型不需要的类别,直接跳过。三是输出格式,每行五个值,类别ID加四个归一化坐标,YOLO系列模型统一用这个格式。
4.3 训练参数设置与调参经验
训练时首先是模型规模的选择。对于眼睛和嘴巴这类小目标检测,我建议从yolov8n或yolov8s起步,不要一上来就上yolov8x。原因有二:一是驾驶员状态检测通常在车载嵌入式设备上运行,算力有限,大模型即使精度高也部署不了;二是眼睛和嘴巴目标数量少、类别简单,大模型不仅浪费算力,还容易过拟合。
图像分辨率方面,YOLOv8默认的640x640是够用的,但前提是数据集中眼睛区域的像素尺寸不能太小。建议训练时将imgsz参数设为640,如果数据集中人脸占比较大、眼睛区域像素清晰,也可以试着调到960,有时能提升小目标召回率,但训练时间和显存消耗会明显增加。
epochs建议从100起步,配合早停机制(patience设为20到30)。由于数据集规模通常不算大,几十个epoch之后模型可能就收敛了。优化器用默认的SGD或AdamW都可以,YOLOv8默认的SGD在实际训练中表现稳定。关键的超参数是mosaic和mixup这两种增强的概率,如果发现训练集损失下降很快但验证集损失震荡,优先检查是不是增强强度过大。
训练命令参考:
yolo detect train data=data.yaml model=yolov8n.pt epochs=150 imgsz=640 batch=16 patience=30 project=runs name=driver_status训练结束后,在runs/driver_status/weights/目录下会生成best.pt和last.pt,取best.pt做后续推理和导出。
4.4 推理与状态判定逻辑
模型训练好后,推理流程是对视频帧做人脸检测,或者直接对整帧做目标检测,输出眼睛和嘴巴的检测框及类别。实际部署中通常是先做人脸检测,在脸部区域内再做眼睛和嘴巴检测,这样可以减少全图搜索的干扰,提升检测速度。
写一个简单而有效的状态判定逻辑:对连续若干帧的检测结果做平滑处理。举例来说,单帧检测出closed_eye置信度很高,但下一帧又是open_eye,直接按单帧判定会频繁抖动。常规做法是加一个状态计数器,假设连续5帧中4帧以上判定为闭眼,才认为驾驶员确实处于闭眼状态。打哈欠的判定类似,连续检测到open_mouth超过2秒,才算一次有效哈欠,误报会低很多。
5. 训练状态分类器的经验与踩坑记录
5.1 目标检测与状态分类的分工
严格来说,YOLO目标检测模型输出的是“眼睛在图像的哪个位置”和“这个位置的类别是睁眼还是闭眼”。它把检测和分类合二为一,所以部署起来非常方便。但实际项目中,我见过不少人走入一个误区:试图让YOLO直接输出“疲劳等级”或“是否疲劳”这种整体状态。
疲劳是一个时序概念,单帧图像无法定义。闭眼0.1秒还是闭眼2秒,含义完全不同。正确的架构是:YOLO做目标级检测,把眼睛和嘴巴的状态输出出来;然后再用一个状态机或时序模型(例如LSTM、GRU,甚至简单的滑动窗口统计)对一段时间内的状态序列做综合判定。这种分工既简单又可靠,不要把疲劳判定的压力全放在检测模型上。
5.2 夜间与光照不足场景的处理
夜间是驾驶员状态检测的重灾区。如果训练数据以白天为主,模型在夜间红外摄像头的画面下表现会很差。解决思路有几个:
一是针对性补数据。在真实夜间环境下采集图片,加入训练集。这是最直接也最有效的方法。
二是预处理适配。如果部署端用的是红外摄像头,训练时就要把图片做灰度化或红外风格化预处理,让模型学到的特征与部署环境一致。很多人忽视这个,导致白天精度很高、夜晚上车直接不能用。
三是曝光和对比度增强。对暗光图片做CLAHE(对比度受限自适应直方图均衡化)增强,能在不改变语义的前提下让特征更清晰。这部分可以在数据加载时做在线增强,也可以在预处理阶段做离线增强后加入训练集。
5.3 误检与漏检的排查思路
训练完成后,用一段实际驾驶视频测试,结果如果出现严重误检或漏检,按这个思路排查。
漏检眼睛或嘴巴,优先检查图片分辨率。如果摄像头距离驾驶员较远,人脸区域小,眼睛可能只占几个像素,此时要么调整摄像头安装位置,要么在检测链路中增加一个人脸放大步骤。误检则首先看背景干扰,例如方向盘上的logo、手机屏幕、窗外的反光,都可能被模型误认为眼睛或嘴巴。
我自己的实践经验是:在训练集中随机混入一些不含驾驶人状态的负样本图片,比如空座椅、中控台、挡风玻璃外景,告诉模型“这些地方没有目标”,能显著降低误检率。YOLO系列支持这种负样本训练,但需要对应空的txt标签文件。
5.4 数据集的扩展与持续迭代
模型部署之后的迭代比初始训练更关键。建议在实车测试中收集困难样本,比如戴墨镜的眼睛、阳光直射下的强反光、打哈欠时嘴巴被手遮住一半,定期补充到训练集里重新微调。数据集不是一次性的东西,它是一个需要持续维护的资产。
对现有数据集做增强扩展也是一种方式。比如用亮度扰动模拟不同时段的光线,用模拟遮挡生成戴口罩或墨镜的负样本,用mixup方法把不同状态的人脸做混合,增强模型的鲁棒性。这个过程没有太多高深理论,就是靠样本量和对真实场景的理解堆出来的。
6. 部署实践中的几个关键选择
6.1 模型导出与推理框架
训练好的PyTorch权重不能直接上车,需要导出成部署格式。以YOLOv8为例,可以用一行命令导出为ONNX或TensorRT引擎:
yolo export model=best.pt format=onnx opset=12导出ONNX后可以用ONNX Runtime跑CPU推理,也可以用TensorRT做GPU推理。实际部署时,车载设备往往没有强劲GPU,更常见的选择是瑞芯微、地平线或英伟达Jetson系列平台。不同平台支持的模型格式和算子库不一样,ONNX是中间转换最稳妥的格式。导出后要跑一遍推理,确认输出结果和PyTorch原版一致,这一步叫做精度对齐验证,是在嵌入式平台上经常会踩的坑。
6.2 帧率与检测策略的平衡
车载场景下,检测帧率直接决定了系统的可用性。如果每秒只能检测5帧,一个人眨眼的动作可能就错过了。为了保证帧率,需要做一些取舍。
实际部署中常见的策略是分级检测:先用轻量级人脸检测模型定位人脸区域,裁剪出人脸ROI,只在ROI内部跑眼睛和嘴巴检测。这样可以大幅减少计算量,因为整帧图像的大部分区域不需要做目标检测。我自己实测,在Jetson Nano这类边缘设备上,这种策略能把眼睛嘴巴检测的帧率从个位数提升到20帧以上。
另一个思路是帧间复用:如果上一帧检测到人脸位置,下一帧大幅度移动的可能性不大,可以在上一帧人脸位置附近扩大一定范围的区域做检测,而不是全图搜索。这本质上是用时序连续性换算力,效果非常明显。
6.3 告警阈值与误报控制
最后一步是告警策略。闭眼检测到一次并不代表一定要立刻报警,需要结合持续时间来设定阈值。闭眼超过0.8秒或1秒才触发预警,是行业内比较常见的设置。打哈欠的告警阈值可以设定为1分钟内检测到3次以上张嘴动作。
这些阈值没有一个固定标准,需要根据实车测试的效果不断调整。阈值设得太高,系统反应迟钝,起不到预警作用;设得太低,误报频繁,驾驶员很快会对报警产生免疫。我建议在处理逻辑里把检测置信度、连续帧计数和持续时间三者结合起来,做成可配置的参数,方便在现场标定。
在我实际做过的项目里,单靠YOLO检测模型的输出直接接一个滑动窗口判断,就能在误报可控的前提下达到较好的疲劳预警效果,并不需要上复杂的时序网络。很多团队一上来就上LSTM加注意力,反而把简单的任务复杂化了。如果你刚开始接触这个方向,先用这套“检测加窗口统计”的组合跑通全流程,再考虑优化,其实是最务实的路线。
本文还有配套的精品资源,点击获取