拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

集装箱缺陷检测数据集详解:VOC/YOLO双格式与YOLOv8训练避坑

集装箱缺陷检测数据集详解:VOC/YOLO双格式与YOLOv8训练避坑 简介面向集装箱表面缺陷检测任务这份数据集包含1476张真实场景图片覆盖Deframe、Dent、Hole、Rusty、Scratch五类常见缺陷共计4227个矩形标注框。所有图片已使用labelImg工具完成Pascal VOC与YOLO两种格式的标注可直接用于训练YOLO系列、Faster R-CNN等目标检测模型免去人工标注和格式转换的重复工作。资源包为zip压缩格式共2000个文件包含1476个xml标注文件与524个txt文本文件压缩包大小约163.76MB。目前已有494人浏览学习适合计算机视觉入门者熟悉标注规范也适合工业质检场景下快速验证缺陷检测方案。下载即可获得带标签的完整数据集便于开展模型训练、精度对比与算法复现有效缩短项目前期数据准备周期。1. 集装箱缺陷检测数据集1476张5类缺陷能省掉采集标注的整段路港口堆场里的集装箱锈蚀、凹陷、变形这类缺陷过去基本靠人拿着巡检单挨个核对。一个人一天看几百个箱子漏检和疲劳是常态缺陷照片存不下来后续追溯也很麻烦。这个目标检测集装箱缺陷检测数据集就是为这类场景准备的1476张真实箱体图像标注了5类常见缺陷同时提供VOC和YOLO两种格式。解压之后你既可以用Annotations配合ImageSets下的清单跑Faster R-CNN那套流程也可以直接把labels目录交给YOLOv8甚至yolov26去训练不用自己写标注转换脚本。对正在做缺陷检测算法验证、被数据标注折腾过的工程师来说这份资源能直接省掉两天采集和格式转换的时间。1476张不算海量但配合预训练权重和数据增强跑通一个能用于原型评估的检测模型绰绰有余。2. 拆解压缩包VOC与YOLO两套标注的目录结构和坐标换算逻辑2.1 解压后的目录组织XML与TXT各归其位拿到zip先解压用tree看一眼目录结构就能确认这份资源是「双格式并存」而不是「单一格式转来转去」。这种VOCYOLO双格式打包的标注数据集解压后大概率是下面这种布局container_defect/ ├── JPEGImages/ # VOC格式原始图像 │ ├── img_0001.jpg │ └── ... ├── Annotations/ # VOC格式每张图对应的XML标注 │ ├── img_0001.xml │ └── ... ├── ImageSets/ │ └── Main/ # VOC格式划分好的训练/验证清单 │ ├── train.txt │ └── val.txt ├── images/ # YOLO格式与labels同名的原图 │ ├── img_0001.jpg │ └── ... ├── labels/ # YOLO格式每张图对应的TXT标注 │ ├── img_0001.txt │ └── ... ├── classes.txt # 类别清单一行一个类别名 └── data.yaml # YOLO训练入口配置各目录的用途整理成一张表训练时对照着用目录/文件归属格式作用JPEGImages/VOC存放原始箱体图像XML里通过filename引用Annotations/VOC每张图对应一个XML记录类别名和像素坐标ImageSets/Main/VOCtrain.txt/val.txt按行列出图片文件名是VOC训练的划分依据images/YOLOYOLO训练时直接读取的原图目录labels/YOLO与images同名的TXT内容是归一化后的坐标classes.txt通用类别名清单YOLO的data.yaml和VOC的class_names都靠它对齐data.yamlYOLO指向images/labels路径声明nc和names为什么厂商愿意同时给两套格式因为VOC格式走的是以像素为单位的绝对坐标适合Faster R-CNN、SSD这类两阶段或基于区域的目标检测框架YOLO格式则用归一化相对坐标直接喂给单阶段检测器。很多转换脚本在换算时容易把中心点坐标算错半个像素或者类别索引从1开始导致训练对不上这份资源直接给两套省掉了中间转换的出错环节。2.2 XML标注文件逐字段解析object节点与bndbox坐标打开Annotations目录下任意一个XML核心结构就是annotation根节点下挂filename、size和一个或多个object节点。下面是一个典型结构真实类别名以classes.txt为准annotation folderJPEGImages/folder filenameimg_0001.jpg/filename size width1920/width height1080/height depth3/depth /size object namedefect_class_1/name difficult0/difficult bndbox xmin320/xmin ymin128/ymin xmax580/xmax ymax420/ymax /bndbox /object /annotation这个XML透露的信息比表面看起来多。filename决定了它和JPEGImages里哪张图对应size里的宽高是整个坐标换算的基准任何大于宽高的xmax/ymax都说明标注出了问题。object节点代表一个缺陷框一张图有多个缺陷就挂多个object。bndbox里的四个值分别是左上角x、左上角y、右下角x、右下角y单位是像素且xmin xmax、ymin ymax是基本约束。difficult字段值得留意值为0表示这个缺陷框是清晰可辨认的值为1表示模糊或争议样本。用这份数据集训练时是否保留difficult等于1的样本会直接影响模型对难样本的召回我一般会在转换脚本里加一个开关先保留全部样本训练一版再对比去掉难样本后的效果。2.3 YOLO格式TXT的换算逻辑中心点坐标与归一化labels目录下的TXT每行五个数字class_id x_center y_center width height全部是相对于图像宽高的比例值范围在0到1之间。这段Python展示了从XML到TXT的完整换算过程排查坐标越界时也能靠它定位问题import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, class_names): 把VOC XML转成YOLO TXT行返回每行字符串列表 tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) # XML里的图像宽度 img_h int(root.find(size/height).text) # XML里的图像高度 lines [] for obj in root.iter(object): name obj.find(name).text.strip() if name not in class_names: continue # 跳过没登记的类别 cls_id class_names.index(name) # 类别索引YOLO必须从0开始 box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) x_center (xmin xmax) / 2 / img_w # 中心点x归一化 y_center (ymin ymax) / 2 / img_h # 中心点y归一化 w (xmax - xmin) / img_w # 框宽与图宽的比例 h (ymax - ymin) / img_h # 框高与图高的比例 lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) return lines这段脚本的关键点在于除法算的是比例不是像素差本身。x_center取的是左右边界的中点到图左边缘的比例w是框宽占整图宽度的比例所有值都在0到1之间才算合理。class_names.index(name)要求类别清单的顺序和data.yaml里的names严格一致一旦两边的顺序错位训练出来的模型预测的类别就是张冠李戴。拿到这份数据集时你真正要用的其实是labels目录下已经算好的TXT上面的脚本只是帮你搞明白坐标是怎么来的以及越界时该怀疑哪个环节。3. 训练前的工程准备数据体检、目录规整与data.yaml编写3.1 先做一次数据体检统计类别分布与空标签我见过太多人拿到数据集直接开训结果跑了两百个epoch才发现某个类别的样本只有十几张模型对那类缺陷基本瞎猜。1476张图分5类平均下来每类不到300张但真实分布一定不平均。所以第一步是统计类别分布和空标签文件import os from collections import Counter labels_dir labels cls_counter Counter() empty_files [] for file in os.listdir(labels_dir): if not file.endswith(.txt): continue path os.path.join(labels_dir, file) with open(path) as f: lines [ln.strip() for ln in f if ln.strip()] if not lines: empty_files.append(file) # 空标签文件会导致训练时样本被丢弃 continue for line in lines: parts line.split() if len(parts) 5: cls_counter[int(parts[0])] 1 else: print(f{file} 有格式异常的行: {line}) print(类别分布:, dict(cls_counter)) print(空标签文件数:, len(empty_files), empty_files[:10])这段脚本干了两件事统计每个类别的标注框数量找出空标签和格式异常的文件。cls_counter的键是类别索引如果发现索引5出现但nc只设了5说明labels里混入了越界索引训练会直接报错。parts的长度必须等于5多一个少一个都说明该行数据损坏。顺便用PIL确认一下图片格式和尺寸分布避免有灰度图和RGB图混在一起。集装箱图片多数是RGB三通道但偶尔会混入某些设备输出的单通道灰度图YOLO训练时对通道数不一致的输入会报错。3.2 目录规整把图片和标签按train/val归档YOLO训练推荐的结构是images/train配labels/train文件名一一对应。如果压缩包里已经排好了直接跳过这步如果没有就按ImageSets/Main里的清单把文件分拣开。常见的做法是写一个bash循环# 创建训练和验证的图片、标签目录 mkdir -p container_defect/images/train container_defect/images/val mkdir -p container_defect/labels/train container_defect/labels/val # 按ImageSets/Main/train.txt清单拷贝训练集 while read img; do cp $img container_defect/images/train/ base$(basename $img .jpg) cp labels/${base}.txt container_defect/labels/train/ done ImageSets/Main/train.txt # 验证集同理把ImageSets换成val.txt目标目录换成val这段命令的核心是$(basename $img .jpg)它把JPEGImages/img_0001.jpg转成img_0001这样labels/img_0001.txt才能精确匹配。注意图片后缀可能是.JPG或.pngbasename时后缀要写对否则找不到对应的标签文件。拷贝完成之后检查一下labels/train里的文件数量是否和images/train一致差一个都要回去查。3.3 编写data.yaml路径、nc与names一个都不能错YOLO训练全靠data.yaml指路路径错了报错信息还特别迷惑。以这份数据集为例data.yaml长这样# 数据集根目录建议写绝对路径 path: /home/yourname/container_defect train: images/train # 训练图像目录相对于path val: images/val # 验证图像目录相对于path nc: 5 # 类别数必须和labels里的最大索引1相等 names: 0: defect_class_1 1: defect_class_2 2: defect_class_3 3: defect_class_4 4: defect_class_5nc和names是这里最容易出错的两个字段。nc一定要等于labels里实际出现的最大类别索引加1如果labels里出现了索引5但nc只写了5训练时加载标签会直接崩。names的顺序必须和转换脚本里的class_names.index(name)顺序一致否则模型推理输出的类别名全是错的。我习惯把classes.txt放在data.yaml旁边每次训练前用一行Python对比两边内容确保没有遗漏。4. YOLO训练实操从命令参数到训练曲线判断4.1 训练命令与关键超参数epoch、imgsz、batch怎么设环境配好之后训练命令本身不复杂一行就能起来cd container_defect yolo detect train datadata.yaml modelyolov8n.pt epochs120 imgsz640 batch16 device0modelyolov8n.pt表示用COCO预训练权重做迁移学习初始化。这份数据集只有1476张图从零训练肯定不现实用预训练权重可以大幅缩短收敛时间。epochs我先给120数据集小一般到不了120就会收敛配合早停回调可以自动截断。imgsz640是YOLOv8的默认推理尺寸集装箱缺陷尺寸跨度大640能兼顾速度和精度。batch16在显存足够的前提下比较稳妥如果你只有8G显存降到8也能跑。各参数的调整原则整理成表参数建议值说明modelyolov8n.pt先用nano验证流程再换s/m提升精度epochs100-150小数据集关注验证集mAP是否先升后平imgsz640默认值缺陷小就改736但显存占用增加batch16或32受显存限制16G显存以下用16device0单卡GPU训练CPU训练慢到不可用用yolov26相关项目的源码跑这份数据也一样代码结构里data.yaml、labels目录这些入口都是通用的区别只在于配置文件字段可能有细微差异具体以你用的那份源码为准。4.2 观察训练曲线loss下降和mAP走势说明什么训练开始后我会盯着两个东西训练日志里的box_loss、cls_loss以及每个epoch结束打印的mAP50。正常情况下box_loss应该从1.5附近稳步下降到0.5以下mAP50从0开始爬坡在早期epoch提升最快后期逐渐走平。如果box_loss出现断崖式下跌后长期不降大概率是学习率设置导致模型陷入局部最优常见做法是把学习率从默认的0.01降到0.001重新跑。如果mAP50一直上不来且低于0.3先别急着调参回头检查labels里的坐标是否越界或者是否存在空标签文件拖了后腿。训练日志不是玄学每条曲线的走势都对应数据或参数的一个具体问题。另外建议在训练配置里开启saveTrue和plotsTrue这样每个epoch结束会把验证集的预测图存到runs/detect/train目录。直接翻预测图比看数字直观得多看到集装箱表面被画上乱七八糟的框基本可以判断是标注坐标的问题而不是模型的问题。4.3 推理验证与导出用训练好的权重跑没有见过的图片训练结束后runs/detect/train/weights/下会有best.pt和last.pt两个权重。best.pt是在验证集上mAP最高那轮的权重推理验证用它yolo detect predict modelruns/detect/train/weights/best.pt sourceJPEGImages/img_0100.jpg conf0.25 saveTrueconf0.25表示置信度阈值低于25%的框会被过滤掉。集装箱场景我一般先用0.25看整体效果再降到0.1检视被漏掉的低置信度框判断是模型置信度不足还是标注本身就漏了。saveTrue会把画了框的结果图存到runs/detect/predict目录下。推理结果里除了图片还会看到一个labels.txt里面记录了每张图检出的类别和置信度。我会把这些结果和Annotations里的真值做一次逐图对比挑出那些模型明显检错或漏检的图作为后续数据清洗和补充标注的依据。5. 避坑数据集落地过程中最常见的5个坑5.1 现象训练日志里nc10明明数据集只有5类第一次用这份数据集训练时日志里显示的类别数翻了一倍还伴随大量WARNING: class not found之类的提示。原因几乎都是类别名大小写不一致比如XML里同一类缺陷有时写Rust有时写rust转换成TXT时被当成了两个独立类别。解决方法是写一个脚本统一类名大小写以classes.txt为准做一次全局替换然后重新生成labels。从那以后我拿到任何数据集都会先统计labels里的类别索引集合确保和期望值完全一致再开训。5.2 现象YOLO格式坐标出现大于1或小于0的越界值训练时弹出Box coordinates out of bounds的警告用脚本扫了一遍labels目录发现个别TXT行里出现了x_center1.0342这样的越界值。原因是XML里标注框的xmax大于图片宽度转换时没有做截断处理导致归一化后坐标超出0到1的范围。解决方法是写脚本过滤并修正越界行把坐标clip到0到1之间同时回去检查对应的XML和图像是否错位。更稳妥的做法是直接在转换函数里加一个if xmax img_w: xmax img_w的边界保护。5.3 现象训练时提示Found 0 images路径指向不存在最常见的情况是data.yaml里的path用了相对路径而训练命令的执行目录和数据目录不在同一层。YOLO在找不到图片时只给一句笼统的Found 0 images非常迷惑。解决方法是把path改成数据集根目录的绝对路径并且确认train和val字段是相对于path的相对路径不是从根目录开始的绝对路径。我在配环境时发现Windows机器上还要额外注意路径分隔符和盘符写法Linux上直接/home/user/...最省心。5.4 现象换模型后推理类别与标签对不上同一个数据集换了个模型架构训练推理结果里类别名全是错位的。原因是不同框架的类别索引规则不一致YOLO从0开始某些框架从1开始而data.yaml或classes文件没有跟着调整。解决方法是冻结类别定义文件数据集的classes.txt作为唯一事实来源任何转换脚本都从它读取类名和顺序。我在团队里的习惯是每个数据集包自带一份classes.txt建模时所有人引用同一个文件谁都不许手写类别列表。5.5 现象某一类缺陷mAP50始终很低甚至为0训练完查看mAP50按类别的明细发现某一类缺陷几乎完全检不出。打开标签统计发现只有十几个样本且这类缺陷的框特别小模型在640分辨率下根本学不到有效特征。解决方法是针对性地做数据增强把包含该类缺陷的样本做随机裁剪放大再复制进训练集或者直接对该类样本加权训练。1476张图分5类天然存在不均衡应该在一开始的数据体检阶段就标记出来不要等训练完再追悔。6. 验证数据集质量的实用技巧用物理解释反向核对标注与模型输出拿到一份数据集除了信它的标注我更相信数据和物理规律可以互相印证。集装箱缺陷的类型不同在图像上的几何特征也不同。比如大面积的锈蚀在标注框里应该占到一个相对大的区域裂纹类缺陷的框则应该是高宽比极大的细条。如果统计出来的面积分布违背这个直觉就要警惕标注质量或类别语义混淆。我先用下面的脚本统计每个类别标注框的面积占比分布把异常的框筛出来import os import numpy as np labels_dir labels area_ratio {i: [] for i in range(5)} # 5类按实际类别数调整 for file in os.listdir(labels_dir): if not file.endswith(.txt): continue with open(os.path.join(labels_dir, file)) as f: for line in f: parts line.split() if len(parts) ! 5: continue cid int(parts[0]) w float(parts[3]) # 归一化宽度 h float(parts[4]) # 归一化高度 area_ratio[cid].append(w * h) # 框面积占整图面积比例 for cid, ratios in area_ratio.items(): arr np.array(ratios) print(f类别{cid}: 样本数{len(arr)}, 平均面积占比{arr.mean():.4f}, f最小{arr.min():.4f}, 最大{arr.max():.4f})框面积占比能暴露两类问题一类是面积占比过小比如平均值小于0.01说明标注的缺陷在整图中只占极小区域训练时容易被下采样吞掉另一类是某一类别面积占比分布特别散可能把不同物理形态的缺陷混成了同一类。统计之后我会挑出面积占比最小的几十个框把对应的原图裁剪出来逐张看确认是本就微小的缺陷还是标注漏了周边区域。模型训练完之后还有一步验证值得做用conf0.1跑一遍验证集把低置信度的预测框和真值框叠在同一张图上输出。低置信度框集中出现在哪类缺陷、漏检集中在哪张图这些信息比mAP数字更能指导数据集优化。如果低置信度框都堆在某些光照暗的角落说明数据集的成像条件单一需要补充这类样本如果漏检全集中在一个类别说明该类别特征没学好或者样本太少。从那以后我每次拿到数据集都强制走一遍面积统计和推理可视化再开始调参。这个习惯帮我挡住了好几套标注质量有问题的数据省下的时间远超多花的半小时。希望帮到你。本文还有配套的精品资源点击获取
返回列表