ARTICLE DETAIL

资讯详情

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

锈蚀检测数据集实测:VOC与YOLO双格式标注的转换与训练避坑指南

锈蚀检测数据集实测:VOC与YOLO双格式标注的转换与训练避坑指南 简介面向工业检测、设备维护与安全监控等场景这份锈蚀检测数据集用于训练和评估目标检测模型帮助识别物体表面的生锈、铁锈区域适合计算机视觉学习者和相关工程研发人员使用。压缩包共2000个文件以xml标注文件为主另含1个说明txt整体约414MB对应提供VOC与YOLO两种常用标注格式可配合图片解析目标位置标注规格对应5563张图片、33888个矩形框类别为Corrosion平均每张约6个目标。资源使用labelImg工具按矩形框规则标注锈蚀区域覆盖多样可直接用于YOLO、SSD、Faster R-CNN等常见检测模型的训练与验证。目前已有420人学习下载便于快速开展生锈锈蚀检测实验、构建工业视觉检测原型或进行算法对比研究。1. 锈蚀检测数据集5563张图、33888个框一张图平均6个标注的VOCYOLO双格式资源做钢结构巡检、管道腐蚀评估或设备表面缺陷检测时最卡人的环节往往不是模型结构而是找不到像样的标注数据。这个数据集定位很直接目标检测场景下的锈蚀Corrosion单类别识别5563张jpg图片每张都有对应的Pascal VOC格式xml和YOLO格式txt标注文件总框数33888个平均每张图约6个标注框用的是labelImg工具画矩形框。覆盖场景包括金属表面斑状锈、焊缝锈蚀、漆面鼓包生锈、拼接板材结合处氧化等适合拿来训练YOLOv5/v8/v11、SSD这类单阶段检测器也适合微调已有模型。想快速验证锈蚀检测可行性或者需要一份现成训练集的从业者这份资源可以直接用于模型训练和指标评估不用再花几周时间做数据清洗和标注。2. 先拆目录再谈训练VOC与YOLO两种标注格式的字段含义和对应关系2.1 解压后的目录结构与文件命名规律拿到压缩包先别急着把文件灌进训练脚本。我一般会先花几分钟跑一遍文件列表确认jpg、xml、txt三者是不是一一对应。这个数据集里图片文件个数、xml文件个数、txt文件个数都是5563命名规律是xyxr_images_编号.jpg、xyxr_images_编号.xml、xyxr_images_编号.txt这种形式三者共用同一个编号前缀。# 在数据集根目录执行快速核对三套文件是否对齐 ls *.jpg | wc -l # 输出应为 5563 ls *.xml | wc -l # 输出应为 5563 ls *.txt | wc -l # 输出应为 5563 # 提取编号并比对差集找出缺标注或缺图片的文件 ls *.jpg | sed s/\.jpg// | sort jpg_ids.txt ls *.xml | sed s/\.xml// | sort xml_ids.txt comm -23 jpg_ids.txt xml_ids.txt # 输出为空说明无缺失 comm -13 jpg_ids.txt xml_ids.txt # 输出为空说明无多余标注这段shell做的事情是先统计三类文件数量再用sed把扩展名去掉、只保留编号sort排序后通过comm命令对比两个文件列表的差异。正常情况下两个comm命令都不输出内容说明图片和标注文件完全对齐。如果有文件缺失或编号漂移这里会直接暴露出来可以提前决定是补标注还是删样本。这个数据集的说明文件里提到的xyzr_images开头的xml文件名和上面目录结构里的命名规律是一致的实际使用时以解压后的文件名为准。2.2 逐字段拆解VOC格式xml从filename到bndboxVOC格式是整个数据集的基准格式YOLO格式txt是从它转换来的。打开任意一个xml文件核心结构如下annotation folderJPEGImages/folder filenamexyxr_images_8009.jpg/filename path/home/user/dataset/xyxr_images_8009.jpg/path source databaseUnknown/database /source size width1920/width height1080/height depth3/depth /size segmented0/segmented object nameCorrosion/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin342/xmin ymin210/ymin xmax586/xmax ymax477/ymax /bndbox /object /annotation这里面真正影响训练结果的是三个部分filename决定图片文件名size里的width和height决定坐标系的尺寸基准object里的name和bndbox决定类别标签与矩形框坐标。xmin、ymin是框左上角像素坐标xmax、ymax是右下角像素坐标坐标原点在图片左上角。一个xml文件里可以有多个object节点每个object对应一个锈蚀区域这和数据集平均每张图约6个框的统计是吻合的。path字段要注意它记录的是标注当时机器的绝对路径在别的机器上基本无效训练时用到的是filename和sizepath可以直接忽略。truncated和difficult都是0说明标注时没有把超出边界的框单独标记也没有刻意区分难例。2.3 YOLO格式txt归一化坐标与类别编号的换算逻辑YOLO格式的txt每一行对应一个目标框格式为类别编号 中心点x 中心点y 框宽 框高后四个值全部归一化到0到1之间。VOC的像素坐标转YOLO归一化坐标公式并不复杂核心是除以图片宽高。0 0.241667 0.318056 0.127083 0.247222 0 0.503125 0.553704 0.189583 0.344444 0 0.068229 0.768519 0.091667 0.213889拿第一行来讲类别编号0对应xml里的Corrosion中心点x等于(342586)/2再除以1920结果是0.241667框宽等于(586-342)/1920。为什么数据集里YOLO格式txt文件里只有坐标没有类别名就是因为类别名统一由data.yaml或类别映射文件维护txt里只存编号。这个数据集只有一个类别所以txt每行首字段都是0。最简单验证方式是统计所有txt里首字段的取值种类# 检查所有YOLO标注txt的类别编号是否只有0 cat *.txt | awk {print $1} | sort -u # 期望输出只有 0若出现其他数字说明类别编号漂移awk提取每行首字段sort -u去重后输出所有出现过的类别编号。单类别数据集正常情况只会输出0这一个数字。如果出现别的编号就意味着有部分标注文件的类别编号不对或者这个数据集混入了其他类别的标注需要单独排查。2.4 两种格式的换算对照表下面这张表把VOC像素坐标和YOLO归一化坐标的对应关系列清楚后续写转换脚本或者手工核对标注时直接对照参考。字段含义VOC格式像素坐标YOLO格式归一化坐标类别object/name: Corrosion首字段: 0框左上角xbndbox/xmin(xmin xmax) / 2 / width - 框宽/2框左上角ybndbox/ymin(ymin ymax) / 2 / height - 框高/2框右下角xbndbox/xmax(xmin xmax) / 2 / width 框宽/2框右下角ybndbox/ymax(ymin ymax) / 2 / height 框高/2框宽度xmax - xmin(xmax - xmin) / width框高度ymax - ymin(ymax - ymin) / height中心点x(xmin xmax) / 2直接存储值中心点y(ymin ymax) / 2直接存储值有个常见认知误区要说清楚这个数据集说明里写的“不包含分割路径的txt文件”指的是txt是目标检测用的矩形框坐标文件不是语义分割的mask标签文件。有些做分割任务的同行下载下来一看有txt以为是分割掩码结果打开发现是归一化框坐标白白浪费时间。这个数据集的txt用途非常单纯就是给YOLO系列训练用的检测标注。3. 把数据集跑进YOLO训练流程目录重组、数据划分与train配置3.1 目录重组YOLO工程要求的images与labels分离结构YOLO官方仓库的训练逻辑默认images目录放图片、labels目录放txt标注而且train和val要分目录放。这个数据集解压后所有文件平铺在一个目录里直接拿去训练会报路径错误。第一步就是把目录结构调整成YOLO工程的约定结构。mkdir -p dataset/images/train dataset/images/val dataset/images/test mkdir -p dataset/labels/train dataset/labels/val dataset/labels/test创建六个目录的核心思路是图片归images标注归labels每个集合下再分train、val、test。后面划分数据时jpg放进images对应子目录txt放进labels对应子目录且文件名保持一致。xml文件训练用不到可以单独放在dataset/xml目录留作备份或者直接用VOC格式训练时再保留原目录。3.2 数据划分脚本按8:1:1随机切分并做文件名一致性校验划分训练集、验证集、测试集不能手动拖拽5563个文件手工操作既慢又容易出错。我习惯用Python脚本按8:1:1比例随机切分同时在脚本里做一遍文件名一致性校验确保每一张划入train的jpg都带着对应的txt。import os import random import shutil random.seed(42) # 固定随机种子保证每次划分结果可复现 src_dir ./source images_dst ./dataset/images/val labels_dst ./dataset/labels/val jpg_files [f for f in os.listdir(src_dir) if f.endswith(.jpg)] valid_pairs [] for jpg in jpg_files: base jpg[:-4] # 去掉.jpg后缀得到文件名主体 xml_path os.path.join(src_dir, base .xml) txt_path os.path.join(src_dir, base .txt) if os.path.exists(xml_path) and os.path.exists(txt_path): valid_pairs.append(base) print(f有效样本数: {len(valid_pairs)} / 总图片数: {len(jpg_files)}) # 对有效样本做索引洗牌后按 8:1:1 切分 random.shuffle(valid_pairs) n len(valid_pairs) train_ids valid_pairs[: int(n * 0.8)] val_ids valid_pairs[int(n * 0.8) : int(n * 0.9)] test_ids valid_pairs[int(n * 0.9) :] def copy_pair(base, img_out, lbl_out): shutil.copy(os.path.join(src_dir, base .jpg), img_out) shutil.copy(os.path.join(src_dir, base .txt), lbl_out) for base in train_ids: copy_pair(base, ./dataset/images/train, ./dataset/labels/train) for base in val_ids: copy_pair(base, ./dataset/images/val, ./dataset/labels/val) for base in test_ids: copy_pair(base, ./dataset/images/test, ./dataset/labels/test) print(ftrain: {len(train_ids)}, val: {len(val_ids)}, test: {len(test_ids)})这段脚本的实际作用有三个第一通过xml和txt同时存在来过滤有效样本防止有图无标注的文件混入训练第二固定random.seed保证后续任何人跑这个脚本都得到完全相同的划分有利于对比实验结果第三copy_pair同时复制jpg和txt从文件层面保证图片和标签永远成对出现。模拟运行时如果把src_dir指向数据集目录有效样本数应该等于5563train约4450张、val约1113张、test约1113张。实际操作时路径参数按自己的目录结构调整即可。3.3 data.yaml配置与训练启动参数单类别模型的几个关键设定划分完成后需要一个data.yaml文件告诉YOLO数据在哪、类别是什么。单类别数据集的配置比多类别简单但有几个参数容易写错。path: /absolute/path/to/dataset train: images/train val: images/val test: images/test nc: 1 names: 0: Corrosionpath必须是绝对路径或者和训练命令的工作目录相对路径保持一致train和val填写的是相对于path的目录名。nc等于1对应单类别names下面0号索引必须和txt文件里的首字段编号对应。这个数据集所有txt首字段都是0所以names列表里只有一个元素。如果names里写成逗号分隔的多个类别名会和txt里只有0这个编号冲突训练时会直接报标签索引越界。训练命令我一般这样起yolov8 train \ modelyolov8n.pt \ datadataset/data.yaml \ epochs100 \ imgsz640 \ batch16 \ workers8 \ patience15参数含义和选型理由modelyolov8n.pt用nano版本做基础模型锈蚀检测是相对简单的单类别任务nano参数量小、训练快先跑通流程看指标imgsz640是YOLOv8默认输入尺寸如果原始图片是1920x1080这种大图训练时会自动resize到640x640不需要手动预处理epochs100对单类别数据集来说足够模型收敛配合patience15做早停连续15个epoch验证集指标不涨就自动停止节省时间batch16在显存吃紧的显卡上能跑如果显卡显存大于12G可以调到32。训练完成后val目录下的图片会用于计算mAPtest目录暂不参与训练可用于最终评估。4. 避坑指南从路径错乱到类别编号漂移的五个实操问题4.1 把YOLO标注txt误当成分割路径文件现象下载后看到每个jpg对应一个txt里面是一串归一化浮点数加上数据集说明里写了“不包含分割路径的txt文件”误以为这是分割任务的mask标签直接拿去跑分割模型结果完全无法训练。原因数据集说明里的“不包含分割路径”指的是这些txt不是Segment Anything或COCO那种多边形分割坐标文件它就是YOLO检测格式的普通矩形框标注。每行的五个数字对应类别编号、中心点x、中心点y、框宽、框高。解决用第2.3节的awk命令检查txt内容如果每行都是5列且首列为0确认是检测标注按第3章的目录结构走YOLO训练流程即可。后续再遇到类似数据集先看txt列数5列是检测框分割标注通常是变长多列或多段坐标。4.2 拼接图里的跨区域框导致误检现象训练时loss正常下降验证集mAP也不差但实际推理时模型经常把一块锈斑周围的背景纹理也圈进去或者在拼接图接缝处产生大量冗余框。原因项目标题里写了“图片含拼接”这类图片本身就是多个子图拼在一起形成一整张大图标注时在整图上画框坐标系是整图的全局坐标。模型在训练时会学到拼接缝两侧的特征相关性但这种相关性在真实单图场景中并不存在。解决训练前先做一轮拼接图筛查用人工或脚本检测图片是否存在明显的拼接分割线对拼接图按分割线裁切成子图子图内标注坐标要做对应偏移换算。如果不想裁切至少要把拼接图单独分到验证集或测试集避免模型训练时被跨区域特征干扰。数据集没有单独标记哪些jpg是拼接图这一步会花一些功夫但值得在训练前做掉。4.3 类别编号漂移导致训练崩溃现象训练启动后几分钟日志里出现标签索引相关的报错类似“Label class X out of range”或者loss直接变成nan。原因单类别数据集的txt首字段应该是0但如果数据来源不干净部分txt可能混入了其他编号的历史标注或者标注工具导出时设置了错误类别索引。这个数据集用labelImg标注正常情况下导出类别编号是0但二次拷贝或手动编辑文件时有可能引入脏数据。解决训练前跑一次脚本扫描所有txt的首字段取值分布如果出现大于等于1的编号用第2.3节的awk或者Python脚本定位异常文件。我的一般做法是只保留首字段为0的行或重新从xml对照转换一遍。这属于损耗最小的兜底修复不会破坏原有标注内容。4.4 文件路径大小写不一致导致标签丢失现象在Windows上训练一切正常换到Linux服务器后大量图片报找不到对应标签文件val指标异常低。原因数据集xml里记录的filename字段是xyxr_images_8009.jpg这种统一小写格式但实际文件名如果存在大小写混写Windows文件系统不区分大小写所以验证时通过Linux严格区分大小写就会直接判定文件不存在。labelImg标注时批量导入的文件名大小写不一致是常见问题。解决训练前在目标机器上跑一次第三个2.1节的comm对比脚本把jpg和txt都转成小写后重新比对。如果发现大小写不一致用rename命令统一文件名和txt文件名。从那以后我每次换平台训练之前都会先跑一遍这个校验避免在环境切换上反复翻车。4.5 高密度框下的标注噪声与漏检现象模型收敛后验证集mAP到0.85左右就不动了继续训练提升很小人工检查预测结果时发现部分小面积锈蚀区域被漏检或者框边缘比人工标注明显偏大。原因这个数据集平均每张图约6个框33888个框总数不算少但labelImg人工画框时锈蚀区域边界往往是渐变的没有锐利边缘标注者很难把框贴着真实边界画紧。此外高密度情况下集中标注较明显的锈斑小面积锈坑被漏标的情况也会出现模型会把这种漏标当成负样本从而抑制小目标输出。解决训练完成后统计置信度分布和单图框数分布如果大量真实漏检集中在低置信度区间可以采取两种策略。一是用小学习率对难样本做增量训练从总数据中挑出漏检图片重新标注后补充进训练集二是通过labelImg检查置信度在0.2到0.5之间的预测框把漏标注的框补进xml并重新生成txt。5. 进阶技巧用框密度分布做训练集分档针对性提升小锈蚀区域的召回率跑完一轮训练后比起直接调整模型结构我更推荐先用统计数据定位模型短板。这个数据集一个显著特点是单图框数差异大——有些图只有1到2个框有些图超过10个框平均6个框的数据掩盖了这种分布差异。我通常写个小脚本统计每张图的框数分布然后按框数分档分析漏检分布。import os from collections import Counter label_dir ./dataset/labels/val density Counter() for txt in os.listdir(label_dir): with open(os.path.join(label_dir, txt)) as f: count sum(1 for line in f if line.strip()) if count 2: density[low(1-2)] 1 elif count 6: density[mid(3-6)] 1 else: density[high(7)] 1 total sum(density.values()) for k, v in density.most_common(): print(f{k}: {v} 张, 占比 {v / total * 100:.1f}%)这段脚本把验证集的标注按框密度分成低、中、高三档目的是确认不同复杂度样本的比例。实际统计出来后常见的分布是低密度占两三成、中密度占五成、高密度占两三成。如果模型高密度档的漏检明显偏多说明小目标在拥挤场景下存在类间遮挡这时优先考虑加大输入分辨率而不是加深网络。把imgsz从640调到768通常对高密度小锈蚀区域的召回有直接帮助。拼接图在这个阶段的处理策略也有讲究。如果训练前没有裁切拼接图推理时对拼接图输出多个跨区域冗余框可以用NMS后处理缓解但效果有限。更彻底的做法是用切片推理把拼接图按固定大小切块每块独立送进模型再把块上的检测框映射回整图坐标。这样既绕开了拼接缝的特征干扰又能让模型在原始分辨率下识别小锈蚀。做切片推理时注意重叠区域要设置一定比例的热度抑制我一般设10%到20%的重叠避免同一个锈蚀区域在相邻切片边缘被重复检出。切块尺寸取模型输入分辨率的1.5到2倍比较合适比如imgsz640时切块用960x960或1280x1280既能保留上下文又不至于因为resize过度而丢失小目标细节。最后补一个验证技巧把训练后模型对全部训练集图片做一次预测人工抽查置信度低于0.3但真实框存在的样本这部分往往是标注噪声和难样本的混合体。修复这批样本后再训练一轮对最终mAP提升比单纯调参更有效。这个数据集本身框密度高标注噪声相对可控但边界模糊的锈蚀区域仍然需要人工介入。希望这份扎实的标注数据能帮你把锈蚀检测这条路走顺也希望上面这些拆解和踩坑记录能让你少走几步弯路。本文还有配套的精品资源点击获取
返回列表