ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

疲劳驾驶数据集VOC格式转YOLO训练全流程实战指南

疲劳驾驶数据集VOC格式转YOLO训练全流程实战指南 简介这是一份面向计算机视觉目标检测任务的疲劳驾驶数据集采用Pascal VOC格式围绕驾驶疲劳状态识别场景定义了closed_eye、closed_mouth、open_eye、open_mouth四类标注方便直接训练车辆驾驶舱内的闭眼与张嘴检测模型。压缩包约368.57MB共8725个文件内含4362张jpg原图、4362个xml标注文件及1份使用说明txt目录结构清晰便于按类别与图片索引检索。所有标注均由labelImg工具按照矩形框规则完成四类目标框数统计为睁眼4903个、闭嘴3343个、闭眼2485个、张嘴936个可据此直观了解类别分布并划分训练验证集。目前已有2802人学习下载对于需要构建疲劳驾驶数据集或验证目标检测算法的开发者来说这套数据可放心用于模型训练与效果评测省去自行采集图像与手工标注的大量时间同时数据集只承诺准确合理的标注不夸大模型精度比较客观可信。1. 疲劳驾驶数据集能做什么4类别4362张的定位与选型做疲劳驾驶检测的工程落地最卡人的往往不是模型结构而是标注数据。公开的疲劳驾驶数据集VOC格式4类别4362张正好卡在一个非常实用的位置量级不大不小类别聚焦在驾驶舱内的典型姿态格式又是目标检测里最通用的Pascal VOC。拿它做算法验证、跑通训练流程、做毕业设计或者给公司预研做可行性测试都比从零标注省一到两周时间。适合的人群很明确正在学YOLO训练流程的初学者以及需要快速验证检测方案的一线工程师。下面从格式拆解、数据转换、训练验证到踩坑记录完整过一遍。2. VOC格式到底是什么XML标注结构、目录组织与类别映射2.1 从下载目录看VOC组织方式拿到数据集后第一件事不是直接扔进训练脚本而是先把目录结构摸清楚。Pascal VOC格式的约定组织方式很固定这个数据集沿用了同一套规范fatigue_driving_dataset/ ├── JPEGImages/ # 4362张原始图片jpg格式 ├── Annotations/ # 与图片同名的xml标注文件 ├── ImageSets/ │ └── Main/ # train.txt / val.txt / trainval.txt └── labels/ # 转换后的yolo格式标注自己生成JPEGImages目录存放原始图片Annotations目录存放每张图片对应的XML标注文件两者通过文件名一一对应。ImageSets/Main目录下的txt文件记录了哪些图片用于训练、哪些用于验证这是VOC格式与COCO格式最明显的区别——VOC用txt文件维护数据集划分而COCO用json文件一次搞定。我一般会先写一个统计脚本核对图片数量和标注数量的对应关系。4362张图的规模不算大但即便这样也会出现少数图片缺标注或者标注文件损坏的情况提前暴露问题比训练到一半再排查省事得多。2.2 XML标注字段逐个拆解打开任意一个XML标注文件核心结构大概是这样的annotation folderJPEGImages/folder filenameimg_20231105_142358.jpg/filename size width1920/width height1080/height depth3/depth /size object nameyawn/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin632/xmin ymin401/ymin xmax891/xmax ymax587/ymax /bndbox /object /annotationfilename字段是图片文件名size字段记录图片的宽高和通道数bndbox里是标注框的左上角和右下角坐标。这些坐标是绝对值单位是像素这也是VOC转YOLO时最需要关注的地方——YOLO要求的是归一化的中心点坐标和宽高。标注框的坐标精度直接影响训练效果。标注边界贴合目标边缘模型的定位损失才能收敛得干净。如果数据集里的标注本身就画得松训练出来的模型预测框也会偏大这属于数据质量问题后期用任何后处理手段都很难完全弥补。2.3 类别信息与标签顺序这个数据集包含4个类别针对疲劳驾驶场景常见的类别划分是正常驾驶、打哈欠、闭眼和使用手机具体类别名要以Annotations里object的name字段为准。这里有一条重要的工程规则类别顺序一旦确定在整个训练和推理链路中就不能再变。# 在数据集根目录下执行提取全部类别名并去重排序 grep -h name Annotations/*.xml | sort | uniq -c这条命令会输出每个类别出现的次数同时确认类别名的拼写是否一致。比如闭眼这个类别如果在部分标注文件里写成了close_eye在另一些文件里写成了closed_eye就会被统计成两个类别而实际标注的语义是一样的。这种问题在人工标注的数据集里非常常见也是数据清洗的第一步。确认无误后把所有类别写成一个固定的类别映射表按字母顺序或者按逻辑顺序排列均可但必须记住训练时用的类别顺序要和推理时类别文件里的顺序完全一致否则模型输出结果的类别对应关系就是错的。3. 用Python把VOC转成YOLO格式脚本与边界坑3.1 转换代码与关键参数YOLO系列模型不认XML只认txt格式的标注文件每一行对应一个目标框。所以动手训练之前必须先把VOC格式转成YOLO格式。转换的核心逻辑并不复杂读取XML里的bndbox坐标计算中心点和宽高再分别除以图片的宽和高做归一化。import os import xml.etree.ElementTree as ET # 类别映射表顺序一旦确定不要随意调整 CLASS_MAPPING { normal: 0, # 正常驾驶 yawn: 1, # 打哈欠 close_eye: 2, # 闭眼 phone: 3 # 使用手机 } def convert_voc_to_yolo(xml_path, output_dir, image_width, image_height): 将单个VOC xml标注转换为YOLO格式的txt标注 tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASS_MAPPING: print(f警告: 未知类别 {name} 出现在 {xml_path}) continue # 读取像素坐标 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) # 防御过滤无效坐标 if xmax xmin or ymax ymin: print(f警告: 无效坐标 {xml_path}) continue # 转成中心点坐标和宽高并归一化 center_x (xmin xmax) / 2.0 / image_width center_y (ymin ymax) / 2.0 / image_height box_width (xmax - xmin) / image_width box_height (ymax - ymin) / image_height # 防御归一化后数值必须落在0~1之间 if not (0 center_x 1 and 0 center_y 1): print(f警告: 归一化越界 {xml_path}) continue lines.append(f{CLASS_MAPPING[name]} {center_x:.6f} {center_y:.6f} {box_width:.6f} {box_height:.6f}) # 写输出文件名称与xml保持一致 txt_path os.path.join(output_dir, os.path.basename(xml_path).replace(.xml, .txt)) with open(txt_path, w, encodingutf-8) as f: f.write(\n.join(lines))这段代码做好了三层防御是我在实际转换过程中逐步补上的。第一层是过滤未知类别避免拼写不一致的类别混进训练集第二层是过滤坐标反转的无效框这种问题通常出现在标注工具导出异常时第三层是过滤归一化越界的坐标正常情况下不应发生一旦出现就说明XML里的坐标值或图片尺寸读取有误。3.2 按固定随机种子划分train/val数据集自带的ImageSets/Main目录里可能已经有划分好的train.txt和val.txt但稳妥的做法是自己重新划分一次因为原始划分的类别分布未必符合你的验证需求。划分时必须固定随机种子保证每次运行结果一致。import random from pathlib import Path random.seed(42) # 固定随机种子保证划分结果可复现 image_files list(Path(JPEGImages).glob(*.jpg)) random.shuffle(image_files) val_ratio 0.15 val_count int(len(image_files) * val_ratio) val_files image_files[:val_count] train_files image_files[val_count:] # 写入train.txt和val.txt with open(train.txt, w) as f: for img in train_files: f.write(str(img.resolve()) \n) with open(val.txt, w) as f: for img in val_files: f.write(str(img.resolve()) \n) print(f训练集 {len(train_files)} 张验证集 {len(val_files)} 张)15%的验证集比例在这个数据规模下比较合理。4362张图验证集约654张对评估模型性能来说已经足够。划分完成后强烈建议统计一下每类目标在训练集和验证集里的数量分布如果打哈欠这个类别在训练集里有1500个目标、在验证集里只有80个分布还算均衡如果出现训练集有1600个、验证集只有30个的情况就说明划分随机性不够好需要重新划分或者做类别分层采样。3.3 转换前后怎么自检转换完成后不要急着训练先用可视化和统计手段自检一遍。我习惯做法是生成一个检测结果标注预览图把YOLO格式的坐标还原到图片上直观看看标注框的位置是否正确。import cv2 def visualize_yolo_annotation(image_path, txt_path): 将yolo格式标注绘制到图片上用于检查转换结果 img cv2.imread(image_path) height, width img.shape[:2] with open(txt_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls_id int(parts[0]) center_x float(parts[1]) * width center_y float(parts[2]) * height box_w float(parts[3]) * width box_h float(parts[4]) * height x1 int(center_x - box_w / 2) y1 int(center_y - box_h / 2) x2 int(center_x box_w / 2) y2 int(center_y box_h / 2) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(cls_id), (x1, y1 - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) return img # 抽查5张图片 import random test_files random.sample(train_files, 5) for img_path in test_files: txt_path str(img_path).replace(JPEGImages, labels).replace(.jpg, .txt) result visualize_yolo_annotation(str(img_path), txt_path) cv2.imwrite(fcheck_{img_path.stem}.jpg, result)自检这一步看似费时间实际上是整个流程里性价比最高的一环。一次坐标转换错误如果没被及时发现后续训练出来的模型会带着系统性偏差预测框要么整体偏左上要么宽高比例失真到时候排错反而要花几倍时间。4. 用YOLOv8训练自己的数据集配置、命令与验证4.1 数据配置文件转换完成后接下来就是准备YOLOv8的数据配置。以YOLOv8为例训练前需要准备一个yaml格式的数据配置文件里面写明训练集路径、验证集路径、类别数和类别名YOLOv5及后续版本都沿用类似的配置规范。# fatigue.yaml path: /data/fatigue_driving_dataset # 数据集根目录按实际路径修改 train: train.txt # 训练集图片路径列表 val: val.txt # 验证集图片路径列表 # 类别定义顺序必须和转换脚本里的CLASS_MAPPING一致 nc: 4 names: 0: normal 1: yawn 2: close_eye 3: phone类别名并不影响训练过程本身影响的是训练完成后的输出可读性以及推理时标签的显示名。但类别的顺序映射与数据格式的转换是强相关的如果转换脚本里定义的映射是yawn为0、close_eye为1这里就也要对应写成这样。两处顺序不一致是训练结果类别错乱的最常见原因。这里的路径写法要注意train字段指向的是train.txt文件本身而不是JPEGImages目录。YOLOv8读取这个txt文件后会逐行读取图片绝对路径再去相同目录结构下找对应的标注txt文件。所以转换后的labels目录建议放在JPEGImages的同级位置比如数据集根目录下建一个labels文件夹里面的txt文件名与图片名完全一致。4.2 训练启动命令与参数调优数据配置完成后训练命令本身不复杂。关键是把几个影响训练质量的参数根据这个数据集的实际情况调好。yolo detect train \ datafatigue.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ patience20 \ device0 \ project./runs \ namefatigue_yolov8s逐项说明参数含义。model指定预训练权重yolov8s.pt是small版本参数约1100万在4362张图的数据规模下从yolov8s开始是比较合理的选择直接上yolov8l或yolov8x模型容量大而数据量不足反而容易过拟合。epochs设为100配合patience20的早停策略如果连续20个epoch验证集指标没有提升训练会自动终止。batch16取决于显存大小8G显存跑yolov8s加640分辨率batch16通常可以跑如果显存不足降到8或者把imgsz降到512。imgsz的取舍值得单独说一遍。数据集的原始图片如果是1920x1080的高分辨率而驾驶舱内的人脸区域往往只占画面的10%到25%大小。直接缩放到640x640后人脸区域可能只有64到160像素见方属于小目标范畴。这种情况下可以试试imgsz960甚至1088检测精度通常会有明显提升代价是训练速度和显存占用都会增加。我一般会先用640跑一轮看baseline再用960跑一轮做对比用数据决定用哪个尺寸。4.3 结果验证mAP、混淆矩阵与坏样本训练完成后验证工作从三个层面展开。第一层看总体指标也就是验证集上的mAP50和mAP50-95第二层看每个类别的单独指标判断哪些类容易混淆第三层把验证集的预测结果可视化系统的检查误检和漏检的图片。from ultralytics import YOLO # 加载训练好的模型 model YOLO(./runs/fatigue_yolov8s/weights/best.pt) # 在验证集上做评估自动输出mAP等指标 results model.val(datafatigue.yaml) # 单独评估闭眼类别 for cls_name, cls_result in results.class_result.items(): if cls_name close_eye: print(fclose_eye mAP50: {cls_result[0]:.4f}) print(fclose_eye mAP50-95: {cls_result[1]:.4f})看mAP指标时我关注的是类间差异而不是均值本身。4个类别里正常驾驶和打哈欠通常比较容易区分因为嘴部区域的特征差异明显闭眼和使用手机这两个类别如果标注框没有包含足够的区域背景模型容易混淆。如果某个类别的mAP比其他类低15个点以上优先怀疑标注框本身而不是模型结构的问题。5. 疲劳驾驶数据集避坑记录4条真实踩坑5.1 类别编号错位VOC顺序与YOLO类号不一致现象训练过程正常loss曲线收敛良好但推理时输出的类别标签和实际画面内容总是对不上——检测到打哈欠却显示成了正常驾驶闭眼显示成了手机。原因VOC标注里的类别名是没有顺序概念的YOLO格式必须把类别名编码成从0开始的整数。如果转换脚本里的类别映射表和数据配置文件里的names顺序不一致训练过程不会报任何错误因为模型只是按照数值在学最终输出就会错位。解决把所有环节的类别顺序收敛到一个文件中。我会在项目根目录放置一个classes.yaml作为唯一事实来源转换脚本和数据配置文件都从这个文件读取避免手工抄写导致的不一致。5.2 无标注图片导致的训练崩溃现象训练启动后很快报错日志里出现KeyError或者FileNotFoundError指向某张图片找不到对应的标注文件。原因4362张图片里混入了少量没有对应标注的图片即JPEGImages里有jpg文件但Annotations里没有对应xml文件转换后labels目录里也就没有对应txt文件。YOLO训练时每读一张图片就去查找同名txt标注找不到就会中断。解决转换前先做一次文件名差集检查。用Python对两个目录的文件名做set差运算把缺标注的图片列出来单独处理如果标注确实缺失就把这些图片从训练列表里剔除而不是保留在目录中。这一点在数据量越大的时候越容易出现因为人工整理过程中很难保证每张图都完成标注。5.3 分辨率不均衡导致小目标漏检现象训练和验证的mAP都不低但实际测试时驾驶员稍微离摄像头远一点闭眼检测就大面积失效。原因这个数据集的图片分辨率并不统一有的图片是1280x720有的是1920x1080还有少量800x600。统一缩放到640x640后原本在大分辨率图片中人脸占比较小的目标缩放后可能只剩下40到60像素检测器对这样的小目标非常敏感训练时收敛不充分自然无法泛化到实车场景。解决一方面把训练和推理的imgsz提高到960另一方面对原始图片里目标像素面积做过一次分析。对每个标注框计算占图片总面积的比例画出分布直方图如果大量目标框占比小于1%优先考虑在训练时加入多尺度训练或者把数据按分辨率分组处理。这类问题的修复优先级高于调模型结构。5.4 划分不洗牌验证集类别分布失衡现象训练集mAP接近0.98验证集mAP只有0.82两者差距过大而且每次训练结果波动明显。原因直接使用了数据集自带的train.txt和val.txt划分文件而这些文件可能是按时间顺序生成的图片列表。驾驶行为数据天然有时间连续性前5000张图片可能是同一批司机的白天驾驶数据后面的图片是另一批司机的夜间数据。按时间顺序划分验证集里可能集中了大量夜间样本类别分布和训练集差异明显。解决强制重新划分并且使用类别分层采样确保每个类别在训练集和验证集中的比例基本一致。具体做法是先把所有图片按类别标签分组然后再在每个组内部按比例切分而不是对全部图片做全局随机抽样。6. 进阶类别不均衡的采样策略与数据增强习惯疲劳驾驶数据集里类别不均衡是不可避免的问题。正常驾驶的样本数量往往远多于闭眼和打哈欠直接训练会让模型对少数类的检测能力偏弱。处理这个问题有两个常用的手段采样策略和数据增强。采样策略上最简单的做法是在数据加载阶段对少数类的图片做重复采样。YOLO训练时每个epoch都会遍历全部训练图片可以在自定义数据加载器中对包含少数类目标的图片提高采样概率。比如统计每张图片包含的类别标签含闭眼标注的图片采样权重设为2.0其余为1.0这样每轮训练中少数类图片被看到的频率大概翻倍。数据增强方面YOLOv8默认开启的马赛克增强对丰富背景多样性很有帮助。如果最终应用场景是驾驶舱内的固定摄像头视角还可以针对性加入轻微的角度旋转和亮度扰动模拟不同时间段的光线变化。但要控制增强幅度旋转角度不超过10度为好过大的旋转会让检测框失真。我做疲劳驾驶检测项目时养成的一个习惯是训练前先跑一个50个epoch的快速实验验证标注质量只看训练集的loss能不能降到合理区间再用完整配置跑最终版本。这个习惯帮我提前发现过好几次标注框偏移的问题省下了大量重训时间。希望帮到你。本文还有配套的精品资源点击获取
返回列表