ARTICLE DETAIL

资讯详情

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

危化品运输车辆数据集:VOC转YOLO格式与YOLO训练全流程

危化品运输车辆数据集:VOC转YOLO格式与YOLO训练全流程 简介这份危化品运输车辆数据集专为目标检测模型训练与验证设计面向计算机视觉学习者、算法工程师及安监物流领域的智能监控应用开发者主体为油罐车等危化品运输车辆使用labelImg工具完成矩形框标注类别统一为dangercar。压缩包共2000个文件以XML标注文件为主另含TXT使用授权说明资源包约116.56MB图像样本共2007张标注框总计2211个所有目标均为危化品运输车辆标注准确且规则清晰。绝大多数样本为油罐车还配套有视频讲解数据组织与使用要点可直接用于模型训练、验证与格式转换也可作为VOC标注格式的参考样例。需要说明的是数据集只提供准确合理的人工标注不附带训练权重或精度保证用户可基于自身任务自行开展训练与评估。目前已有691人学习下载适合需要危化品车辆识别数据的入门与进阶用户快速获取规范数据集。1. 危化品运输车辆数据集2007张图、1个类别为什么值得你重新审视做目标检测的人对“危化品运输车辆数据集VOCYOLO格式2007张1类别.zip”这个标题应该不陌生。它给的是一份已经标注好的、针对危化品运输车辆的图片集VOC和YOLO两种格式都替你准备好了解压就能开训。所谓1类别就是只检测“危化品运输车”这一个目标常见的罐车、液罐半挂车都在里面。2007张图不算多但对单一类别来说配合预训练权重和合理的数据增强完全够跑出一个能用的模型。这个数据集真正解决的是行业里的一个尴尬问题通用目标检测数据集里几乎没有危化品车辆这个类别自己标注一批又贵又慢。有了现成的VOC和YOLO双格式数据你省掉的是从零标注、格式转换、类别映射这些最耗时间的环节。适合谁用搞化工园区安防、道路危险品运输监管、港口或物流园区车辆识别的工程师以及刚入门YOLO想拿一份“干净数据”跑通全流程的学生。它的意义不在于2007张这个数字而在于“开箱即用”这四个字。2. VOC与YOLO格式的本质标注文件里到底存了什么2.1 两种格式的物理布局解压后你应该看到什么拿到zip压缩包第一步不是急着解压训练而是先看目录结构。VOC格式和YOLO格式的组织方式完全不同弄混了后面训练直接报错。常见的目录组织是这样的VOC部分有JPEGImages、Annotations、ImageSets/Main三个文件夹JPEGImages放图片Annotations放对应的xml标注文件ImageSets/Main里是train.txt、val.txt这些划分文件。YOLO部分则通常是images和labels两个文件夹images里是图片labels里是每个图片对应的txt标注文件。有的打包者会把两个格式的图片分开各放一份有的共用一份图片只把标注文件分开解压后第一件事就是确认图片是否重复存放。VOC格式的xml文件用标签树描述目标信息最核心的几个节点是filename、size、object。filename给的是图片文件名size里是图像的宽和高object里包含目标的类别名name和边界框bndbox。bndbox里是xmin、ymin、xmax、ymax四个绝对像素坐标。注意VOC的坐标是从左上角开始算的xmin和ymin是框左上角的像素位置xmax和ymax是右下角的像素位置。这个坐标是绝对坐标不需要归一化直接写的就是图片上的真实像素值读取的时候不需要做任何缩放换算。YOLO格式的txt文件就简洁得多每一行对应一个目标格式是“class_id x_center y_center width height”。前一个数字是类别编号从0开始计数后面四个数全部是相对于图片宽高的归一化值取值范围在0到1之间。x_center是目标中心点的横坐标除以图片宽度y_center是中心点的纵坐标除以图片高度width是目标框宽度除以图片宽度height是目标框高度除以图片高度。由于只有1个类别class_id只有0不会有第二个数字出现。2.2 为什么要同时提供VOC和YOLO你的训练框架决定你用哪份如果你用的是YOLOv5、YOLOv8这类框架你会直接用YOLO格式的txt标注因为框架内置的数据加载器只认txt。如果你要用mmdetection、Detectron2或自己写训练脚本那VOC格式的xml更通用因为很多框架的VOC数据加载器是现成的。提供双格式的意义在于免去你自己做格式转换这一步也避免你因为转换脚本写得不对导致坐标错乱。这里有个很多人容易忽略的点同一个数据集转成两种格式如果转换脚本坐标算错了两份标注的框就会对不上。所以拿到数据的第一件事不是直接用而是随机抽几张图把VOC的框和YOLO的框分别画出来对比一下确认两个格式标注的是同一个目标区域。这个验证花不了十分钟但能避免你训练到一半才发现边界框全部偏移。3. 用Python把VOC转成YOLO转换脚本与坐标归一化细节3.1 目录准备不要在原数据上直接改不管你是要验证双格式一致性还是想自己重新划分数据集我都建议复制一份到工作目录再动手。直接在原zip解压出来的文件夹上做转换或修改一旦出错想恢复就得重新解压浪费时间。我一般的工作目录结构如下把VOC作为原始输入YOLO作为输出目标workdir/ ├── voc_data/ │ ├── JPEGImages/ │ ├── Annotations/ │ └── ImageSets/ │ └── Main/ └── yolo_data/ ├── images/ ├── labels/ └── classes.txtclasses.txt里只有一行内容dangerous_goods_vehicle。这个文件不是可有可无的yolov5训练时靠它确定类别名推理时靠它把类别编号映射回可读名称。有的数据集打包者会把它放在yolo目录下有的会忘掉如果你发现没有自己建一个只含类别的文本文件就行。注意类别名不要用中文虽然YOLO能读但后面部署到C或TensorRT时中文类别名会出现编码问题。3.2 转换脚本解析XML、计算归一化坐标、写入txt核心转换逻辑是遍历Annotations目录下所有xml文件解析每个目标框的绝对坐标然后除以图片宽高得到归一化值。图片宽高从哪里拿优先从xml的size节点读不推荐用PIL去打开图片确认尺寸因为一张张读图片IO开销大2007张图跑下来明显慢。下面是完整的转换脚本用Python的标准库就能跑不需要额外安装依赖。import os import xml.etree.ElementTree as ET voc_annotations voc_data/Annotations yolo_labels yolo_data/labels yolo_images yolo_data/images os.makedirs(yolo_labels, exist_okTrue) os.makedirs(yolo_images, exist_okTrue) # 定义类别映射classes.txt里第0行就是这个类别 class_name_to_id {dangerous_goods_vehicle: 0} for xml_file in os.listdir(voc_annotations): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(voc_annotations, xml_file)) root tree.getroot() # 从XML的size节点读取原始宽高避免逐张读图片 width int(root.find(size/width).text) height int(root.find(size/height).text) # 转换后的txt文件与xml同名只是后缀不同 txt_path os.path.join(yolo_labels, xml_file.replace(.xml, .txt)) lines [] for obj in root.iter(object): # 跳过difficult标注这类目标在VOC里算作难以辨认的样本 difficult obj.find(difficult) if difficult is not None and difficult.text 1: continue name obj.find(name).text if name not in class_name_to_id: print(f跳过未知类别: {name} in {xml_file}) continue class_id class_name_to_id[name] bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 坐标归一化中心点坐标除以宽高框宽高也除以宽高 x_center ((xmin xmax) / 2) / width y_center ((ymin ymax) / 2) / height box_width (xmax - xmin) / width box_height (ymax - ymin) / height lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {box_width:.6f} {box_height:.6f}) # 空标注文件也要生成YOLO训练时会跳过空txt对应的图片 with open(txt_path, w) as f: f.write(\n.join(lines))这段代码的坐标计算要仔细看x_center用的是(xmin xmax) / 2也就是左边界加右边界取平均值而不是xmin加上宽度的一半。虽然数学上等价但初学者容易写成xmin width / 2然后忘记width这个变量还没有除以图片宽度导致中心点坐标超过1。y_center同理。所有归一化坐标必须确保在0到1之间如果出现负数或大于1的值说明源XML的标注框越界了这种框训练时会被YOLO直接丢弃看似没报错实际有效样本少了。类别映射只处理了dangerous_goods_vehicle这一种如果xml里有其他类别脚本会打印提示并跳过。这其实是有意的设计不是bug因为目的是确认标注里没有混入背景或其他车辆类别。如果打印出来的未知类别过多说明数据集标注不够干净这时候要回到VOC文件里人工检查。3.3 数据划分随机种子固定验证集才有可比性转换完格式后要划分train/val/test。很多数据集打包者会在ImageSets/Main里提供官方划分但我建议自己重新划分一次原因很简单官方划分可能把难例集中放在验证集也可能出现的图片重复自己划分能确保分布均匀。划分时一定要固定随机种子否则每次运行结果不同实验结果没法复现。import os import random import shutil random.seed(42) image_dir voc_data/JPEGImages image_files [f for f in os.listdir(image_dir) if f.endswith(.jpg)] random.shuffle(image_files) train_num int(len(image_files) * 0.7) val_num int(len(image_files) * 0.2) train_files image_files[:train_num] val_files image_files[train_num:train_num val_num] test_files image_files[train_num val_num:] def copy_files(file_list, src_imgs, dst_imgs, dst_labels, label_suffix.txt): for f in file_list: shutil.copy(os.path.join(src_imgs, f), os.path.join(dst_imgs, f)) label_name f.replace(.jpg, label_suffix) src_label os.path.join(voc_data/Annotations, label_name) dst_label os.path.join(dst_labels, label_name) if os.path.exists(src_label): shutil.copy(src_label, dst_label) copy_files(train_files, voc_data/JPEGImages, yolo_data/images, yolo_data/labels)这里有个前提假设是标注文件的命名和图片文件完全对应jpg换txt后缀。如果数据集里存在一些图片没有对应标注copy_files里if os.path.exists的判断会直接跳过不会报错但你要知道这些无标注图片不会参与训练。划分比例的选法要看你的数据量2007张图我用7:2:1训练集1400张左右验证集400张左右测试集200张左右。如果你发现训练集里车辆角度分布不均衡比如侧面的特别多、正面的特别少可以把验证集比例降到0.15把多出来的图补给训练集。4. 用YOLOv5或YOLOv8训练单类别模型最少参数与调优顺序4.1 为什么建议从yolov5s或yolov8n起步算力与精度的平衡危化品运输车辆这个任务的检测难度不算高目标尺度偏大角度变化集中在正侧和斜侧没有特别小的目标。这意味着你不需要一上来就用yolov8x这种大模型2007张图的规模喂给大模型很快会过拟合。我一般会先用yolov5s或yolov8n跑通基线看看验证集mAP能到多少再决定要不要换更大的模型。对单类别检测来说yolov8n的参数量约3.2M推理速度快训练一张图在单张V100上大概0.1秒到0.2秒2007张图几十个epoch就够跑完。用yolov8x参数量接近70M训练时间成倍增加但精度提升有限因为类别少、目标特征本身区分度大。选择yolov5还是yolov8看你的部署环境。如果你要转TensorRT或者用C部署yolov5的生态更成熟网上能找到大量的部署案例。如果你只是做实验、出结果快或者后续要接分割任务yolov8更合适。两者训练命令大同小异数据集配置文件格式也几乎一致核心都是指定图片路径、标签路径和类别数。4.2 数据集配置文件与训练命令一个都不能错yolov5用data.yamlyolov8也用data.yaml但格式略有差异。yolov8的yaml里path是指向数据集根目录的train和val是相对路径yolov5的yaml里train和val可以是绝对路径。这里最容易踩的坑是路径分隔符Windows下用反斜杠Linux下用正斜杠如果你在Windows上写好的yaml直接拷到Linux服务器上跑必须检查路径格式。# data.yaml path: ./yolo_data # 数据集根目录 train: images/train # 训练集图片相对路径 val: images/val # 验证集图片相对路径 nc: 1 # 类别数只有危化品运输车一类 names: [dangerous_goods_vehicle]训练命令在yolov8下是这样yolo detect train \ modelyolov8n.pt \ datadata.yaml \ epochs200 \ imgsz640 \ batch16 \ optimizerSGD \ lr00.01 \ close_mosaic50参数说明方面关键参数是关闭马赛克增强的轮数close_mosaic50这个值意思是训练到第50轮后关闭mosaic增强。马赛克增强能让模型在训练早期看到更多样的背景组合但最后几十轮如果不关闭模型会过度依赖拼接出来的假图片特征在真实场景上表现反而下降。尤其危化品车辆的数据集只有2007张mosaic在后期的影响更明显。lr0设成0.01是SGD优化器的常用起始学习率如果你用AdamW起步学习率一般要降到0.001直接套用0.01很容易发散。如果你用的是yolov5训练命令变成python train.py --data data.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 200 --close-mosaic 50参数含义相同只是传参方式从命令行选项变成了--加名称的形式。yolov8把配置集中到model、data这两个参数其他全部是可选覆盖项理解难度低一些。4.3 单类别训练的三个必调参数从默认值出发的调优路径第一是imgsz。640是默认值但如果你的监控画面里车辆占比大可以提高到960或1280代价是显存占用翻倍、训练时间变长。如果车辆在画面里占比小比如在高速公路卡口图片中车辆只占整张图的十分之一那imgsz反而要调大因为小目标在低分辨率下特征丢失严重。第二是batch。16是一个保守值单卡V100可以开到32甚至64显存不够时先降batch而不是降imgsz因为imgsz对精度的影响大于batch。第三是epochs。200轮对2007张图来说足够配合早停策略当验证集mAP连续30轮不上升就停止能省下近一半训练时间。训练过程中要监控的指标不是损失值而是验证集的mAP和recall。单类别的检测任务recall比precision重要因为漏检一辆危化品车比误检一辆普通卡车的后果严重得多。YOLO训练日志里每个epoch会输出mAP50、mAP50-95、precision、recall四个值你主要看mAP50和recallmAP50-95是跨IOU阈值的平均对单类别任务参考价值有限因为目标本身不存在多类别混淆问题。5. 避坑清单坐标错乱、loss翻车与验证集泄漏5.1 XML里的filename与实际图片名不一致训练时数据加载报错现象是训练刚开始就报错找不到图片或者数据加载器警告“image not found”。原因是数据集打包者可能在xml里写的filename和JPEGImages里的实际文件名不一致常见的差一个空格、后缀大小写不同、文件名带前缀。解决方法是写一个脚本遍历所有xml读取filename字段再检查对应图片是否存在不存在的记录到log。但更稳的做法是不要依赖xml里的filename直接用Annotations目录下的xml文件名去反查JPEGImages里的jpg同名文件因为打包者一般不会把xml命名和jpg命名独立处理反查成功率更高。5.2 归一化坐标出现负值或大于1训练时loss变成nan现象是训练到某一轮loss突然变成nan或验证集mAP始终为0。原因是xml里的bndbox坐标有错误比如xmin大于xmax、ymin大于ymax或者坐标超出了图片尺寸。这类错误在手工标注时很常见尤其当标注工具对图片做过裁剪但xml没有同步更新。解决方法是转换脚本里对归一化坐标做一次防御性检查任何小于0或大于1的坐标直接打印出xml文件名和原始坐标值然后跳过这个目标框。不要自动截断到0和1因为截断后的框位置已经不对拿去训练只会把错误信息教给模型。5.3 类别编号不是从0开始模型把所有目标都当成背景现象是训练loss正常下降但预测结果全为空或者把目标全预测成背景。原因是打包者给出的boxes.txt类别编号可能从1开始也可能类别名不是唯一一个导致模型学到的类别编号与实际不符。解决方法是打开任意一个txt标注文件确认第一列数字只有0。如果出现非0数字说明标注文件的类别ID和data.yaml里的nc、names配置对不上。最直接的做法是跑一个统计脚本遍历所有txt文件提取所有出现过的class_id看是不是只有0。如果只有0那就是data.yaml配置问题如果出现其他数字需要写脚本把全部class_id重映射到0。5.4 划分数据集前不固定随机种子实验结果无法复现现象是两次训练用完全相同的参数和代码验证集mAP却差了几个点。原因是你没有固定random.seed导致每次运行程序时随机划分的训练集和验证集不同。模型在A验证集上表现好在B验证集上表现差这不是模型不稳定而是验证集本身变了。解决方法是数据划分脚本开头写上random.seed(42)这个42不是魔法数字你可以换成任何整数只要固定就行。同时把划分好的文件清单导出成train.txt和val.txt存到项目目录这样后续不管谁跑实验用的都是同一份划分。5.5 解压后图片直接放在根目录而不是images子目录训练命令找不到数据现象是训练命令执行后提示Dataset not found或no labels found。原因是数据集的zip解压后图片可能直接在dataset根目录下而yolov8的data.yaml里train写成images/train路径对不上。解决方法是不要直接改yaml里的路径到处试先看解压目录的实际结构用ls命令确认图片到底在哪个子目录再按实际结构修改data.yaml。这里要提醒一点数据集提供者打包的目录结构和标准YOLO项目约定的结构可能不同不要假设images/train一定存在一定要亲眼确认。6. 用统计与可视化验证标注质量训练前花十分钟省三天训练开始前我强烈建议做一次标注质量可视化不要盲目开训。方法是在yolo_data下写一个脚本读取每张图的txt标注把框画回图片上存成新图随机抽50张肉眼检查一遍。重点看两件事框是否贴合车辆轮廓、是否有漏框的车辆。框偏大偏小都还好说模型能自己修正漏框才是致命问题漏框的车会被模型当成背景学进去。更进一步的验证方法是用脚本统计所有标注框的中心点分布和宽高比。危化品运输车辆的宽高比通常集中在2到5之间因为罐体是细长结构。如果你的标注框宽高比出现大量小于1的说明车辆是竖直方向停放或者标注方向有问题。中心点分布可以画成散点图如果发现某个区域完全没有中心点比如图片上半部分一辆车都没有那要回到原图确认是不是漏标了。统计脚本的核心是读取txt文件里的归一化坐标乘以图片宽高还原成像素坐标然后计算宽高比。宽高比异常的框要单独列表输出我遇到过标注员把罐体和车头分开标两个框的情况这种框会严重干扰模型学习必须合并或删除。把标注质量这关把好训练出来的模型才不会带着坏样本的偏见部署到真实监控场景时才不会在关键时刻漏报。这也是我做目标检测一直坚持的习惯数据不验证模型跑得再快也是白搭。希望帮到你。本文还有配套的精品资源点击获取
返回列表