集装箱缺陷检测数据集:VOC+YOLO双格式与yolov8训练实战指南
发布时间:2026/10/2 2:41:28来源:尧图网络
简介集装箱表面缺陷检测数据集面向计算机视觉目标检测任务提供4127张集装箱外观图片覆盖 Dent凹陷、Hole孔洞、Rust锈蚀三类缺陷总标注框数达10117。数据采用 Pascal VOC 与 YOLO 双格式每个样本均搭配 xml 与 txt 标注文件标注工具为 labelImg矩形框边界准确可直接用于 YOLO 系列、Faster R-CNN 等模型训练也可用于缺陷检测算法的精度对比与工业质检场景验证省去繁重的数据清洗和人工标注环节。压缩包内共 2000 个文件以 xml 格式标注文件与 txt 文件为主整体约 163.61MB解压后结构清晰便于批量加载和划分训练集、验证集。该数据集已有 348 人学习使用类别框数分布明确Dent 4943、Hole 1218、Rust 3956可作为集装箱缺陷检测项目的基础数据支撑适合具备一定目标检测基础的研究者与工程开发者直接开展实验。1. 集装箱缺陷检测数据集4127张VOCYOLO双格式先看值不值得入做工业视觉的同行应该都有这种体会集装箱表面的划痕、凹陷、锈蚀这类缺陷检测真正卡进度的往往不是模型选型而是数据。相机拍出来的原图动辄几千像素宽缺陷区域可能只占画面千分之一手动标注几百张就够让人崩溃了。这个集装箱缺陷检测数据集核心价值就是把“标注成本”这一步直接省掉——4127张图片3个类别VOC和YOLO两套格式都给你备好拿到手解压就能接进yolov8训练流程。适合谁一类是刚入行做缺陷检测、想先用现成数据把流程跑通的新手另一类是手头有类似的集装箱或金属表面检测项目想拿这批样本做预训练或在上面微调的工程师。值得先说清楚的一句话是数据集本身不解决“模型一定准”但能帮你把数据准备、格式转换、训练调试这条链路的坑全部提前踩一遍。2. 拆开7z后的家底VOC与YOLO的目录结构、命名规则与类别映射2.1 为什么集装箱缺陷标注偏爱VOCYOLO双格式做检测的人应该都跟这两种格式打过交道。VOC格式用XML文件存标注每个目标是一个object节点里面记录类别名和bndbox的四个角点坐标坐标是绝对的像素值。YOLO格式则是一个图片对应一个同名txt文件每行是“类别ID 归一化中心点x 归一化中心点y 归一化宽 归一化高”。两种格式各有各的拥趸VOC在数据可视化、目标裁剪、做分类任务时更直观很多标注工具如LabelImg默认导出XMLYOLO则是训练侧的主流通用格式Ultralytics YOLOv8、YOLOv5的原生接口都直接消费txt标注。数据集同时提供两套标注最大的好处就是省掉一次转换——你可以直接用它喂给YOLO系模型也可以把XML丢给LabelImg之类工具二次编辑或者转成COCO格式接进MMDetection不必再从零开始标注一遍。2.2 解压后第一件事核对目录结构与文件配对下载回来是一个.7z压缩包Linux下解压我习惯用命令行# 列出压缩包内容先看结构再解压 7z l container_defect.7z # 解压到指定目录-o后面不要留空格 7z x container_defect.7z -o/home/user/datasets/ # 解压后统计图片和标注数量确认文件齐不齐 find /home/user/datasets/ -name *.jpg | wc -l find /home/user/datasets/ -name *.xml | wc -l find /home/user/datasets/ -name *.txt | wc -l这里有个细节7z x默认会把压缩包内的完整路径还原出来解压前先用7z l看一眼内部目录层级避免解出来一堆散文件。统计文件数量时图片数、XML数、txt数三者应该完全一致——每个样本一份图配两份标注。如果发现某一种格式少文件多半是标注工具漏导出或者压缩时漏打包这时候优先以YOLO的txt为准跑训练XML那份用来做可视化排查。目录结构通常是这样的container_defect/ ├── JPEGImages/ # 原始图片 ├── Annotations/ # VOC格式XML标注 ├── labels/ # YOLO格式txt标注 ├── class_names.txt # 类别名清单 ├── train.txt # 训练集图片路径清单 └── val.txt # 验证集图片路径清单这是我见过的比较标准的VOCYOLO双格式数据集结构。实际拿到手可能微调比如images/和labels/平级但JPEGImages、Annotations、labels这三个目录是VOC转YOLO这条链路上最常见的约定看到它们基本就能对上号。2.3 类别映射与编号约定先认准类别名再谈训练3个类别具体是哪三个拿到数据第一件事是读类别清单。常见的集装箱表面缺陷不外乎划痕、凹陷、锈蚀这几类但不同数据集命名可能差异很大——有的是scratch、dent、corrosion有的是中文名如“划痕”“凹坑”“锈斑”还有的可能用defect_1这种占位符。千万别想当然。# 查看类别清单文件 cat /home/user/datasets/container_defect/class_names.txt # 看一个YOLO标注样本第一列就是类别ID head -n 5 /home/user/datasets/container_defect/labels/0001.txt类别ID是从0开始编号的0对应清单里的第一个类1对应第二个依此类推。数据集的txt标注第一列写的就是这个数字ID不是类别名。如果后续你新增了一个类别或者想换类别顺序ID就得重新映射否则模型训练时类别语义就全错位了。VOC的XML里用的是类别名转换格式时最常出的错就在这里类别名字符串没对上class_name_to_id字典里找不到键轻则跳过这个目标重则整个脚本抛异常。所以我一贯的建议是先建好类别清单文件再写转换脚本让脚本去读清单而不是自己硬编码。另外提一句VOC与YOLO的坐标系统差异这是新手最容易绕晕的地方对比项VOC格式XMLYOLO格式txt坐标含义左上角x_min、y_min右下角x_max、y_max中心点坐标x_center、y_center坐标单位绝对像素值归一化比例0~1宽度高度绝对像素值归一化比例0~1类别标识字符串类别名整数类别ID文件扩展名.xml.txt搞不清归一化公式转换出来的标注框就会乱飞。归一化的核心就是中心点x等于(x_min x_max) / 2再除以图片宽度宽等于x_max - x_min再除以图片宽度。一句话VOC存的是“框住目标的矩形”YOLO存的是“矩形中心和大小在整张图中的占比”。3. 把VOC转成YOLO再转回来三份转换脚本与四个边界坑3.1 VOC转YOLOXML里的坐标归一化成txt需要的中心点拿到双格式数据集通常不需要自己转但保不齐你要在这个数据集上做二次标注或者把别的VOC数据并进来一起训练这时就必须掌握转换。我一般会用一个最小可用的Python脚本直接把XML目录批量转成YOLO的labels目录import os import xml.etree.ElementTree as ET # 配置路径按你自己的目录改 voc_dir Annotations # VOC格式XML所在目录 yolo_dir labels # 输出YOLO格式txt的目录 jpg_dir JPEGImages # 原图目录脚本里用于检查图片是否存在 class_name_to_id {scratch: 0, dent: 1, corrosion: 2} # 必须和class_names.txt一致 os.makedirs(yolo_dir, exist_okTrue) for xml_name in os.listdir(voc_dir): if not xml_name.endswith(.xml): continue # 同一个图的txt和xml同名 txt_name xml_name.replace(.xml, .txt) xml_path os.path.join(voc_dir, xml_name) txt_path os.path.join(yolo_dir, txt_name) tree ET.parse(xml_path) root tree.getroot() # 读图片真实宽高归一化必须以它们为分母 img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in class_name_to_id: # 遇到没在清单里的类别跳过而不是报错 continue cls_id class_name_to_id[cls_name] 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) # 越界修正坐标超出图片范围就拉回边界 xmin max(0.0, min(xmin, img_w)) xmax max(0.0, min(xmax, img_w)) ymin max(0.0, min(ymin, img_h)) ymax max(0.0, min(ymax, img_h)) # 过滤掉宽或高为0的无效框 if xmax xmin or ymax ymin: continue # 转中心点宽高并归一化到[0,1] x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h # YOLO格式类ID 中心点x/y 宽高各字段用空格分隔 lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) with open(txt_path, w, encodingutf-8) as f: f.write(\n.join(lines)) print(转换完成生成文件数, len(os.listdir(yolo_dir)))脚本逻辑不复杂但有三个地方值得较真。第一宽度和高度必须取自size节点里的真实像素值不能拿固定的640或416去当分母否则训练时YOLO读到的归一化坐标和图片实际的对应关系就对不上。第二越界修正放在归一化之前因为边界外的坐标会算出负数或大于1的归一化值训练时轻则警告重则丢框。第三过滤宽高为0的框这种框通常来自标注时手滑保留只会污染标签。参数上class_name_to_id这个字典是整个脚本的灵魂换数据集必须改这里。如果你不确定类别映射建议把它换成读取class_names.txt文件的写法让脚本自动构建映射关系这样类别顺序变动时不用改代码。3.2 反向转换YOLO txt还原成XML的恢复路径双格式数据集里还会遇到一个反向需求你训练后想把预测结果转回VOC去对比或者要拿LabelImg打开YOLO标注做人工校对就得把txt转回XML。反向转换的恢复路径关键在图片尺寸从哪来。txt里存的是归一化坐标要还原成像素坐标必须有原始图片宽度和高度import os import cv2 def yolo_to_voc(txt_path, xml_path, img_path): # 从原图读取宽高这一步不能省 img cv2.imread(img_path) img_h, img_w img.shape[:2] boxes [] with open(txt_path, r, encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) ! 5: # 标注格式异常时跳过别硬解析 continue cls_id, x_center, y_center, w, h map(float, parts) # 反归一化中心点加减一半宽高得到左上右下角 xmin int((x_center - w / 2.0) * img_w) ymin int((y_center - h / 2.0) * img_h) xmax int((x_center w / 2.0) * img_w) ymax int((y_center h / 2.0) * img_h) boxes.append((int(cls_id), xmin, ymin, xmax, ymax)) return boxes这段代码里的两个细节值得提一是用cv2.imread读原图取宽高意味着txt本身不携带图片尺寸信息转出来的框精度完全取决于这张图片对应关系对不对二是反归一化后做了int取整浮点数乘除法会有精度损失取整是常规做法但要保证取整后的坐标不越界。实际上更稳妥的写法是再做一次clip把坐标限制在[0, img_w]和[0, img_h]之间避免反推出来的边界超出图片范围。3.3 四个边界坑空XML、越界框、一张图多目标、图片尺寸不一致这两个脚本写起来简单真正跑起来出问题的地方全在边界情况。我做这类数据集转换的经验是先承认“标注数据没有完美的”然后写脚本时就按脏数据来处理。第一坑空XML。一张图没有任何目标时LabelImg导出的XML里可能没有object节点或者XML文件本身是坏的。转换脚本里root.iter(object)遍历不到任何节点生成的txt是空文件。注意空txt不能删因为训练时YOLO需要这个空文件来表示“这张图没有目标”删了反而会报错说找不到对应标注。所以脚本里遇到空XML也要生成空txt这是正确行为。第二坑越界标注框。人工标注时鼠标拖出图片边界是家常便饭XML里xmax可能大于图片宽度。如果直接归一化width会大于1训练时这个框大概率被过滤或造成loss异常。解决方式就是3.1里的clip逻辑把坐标先夹到边界内再进行后续计算。第三坑一张图多目标。集装箱表面缺陷经常一图多框比如一张图里有三条划痕对应三个object节点。YOLO的txt一行一个框多个目标就是多行。转换脚本里用lines.append逐条追加是没问题的但要留意一个极端情况类别很多时第一行的类别ID和普通行一样是数字不是字符串下游解析时按数字处理就行。第四坑图片尺寸不一致。这个坑在训练阶段才会暴露——如果数据集里图片有横有竖有1920x1080也有4000x3000直接归一化后坐标本身没问题但训练时如果不用letterbox而resize成固定正方形长宽比变化会导致标注框整体偏移。处理办法是训练时强制使用letterbox填充或者在预处理阶段统一把图片resize到同一个尺寸并同步缩放标注。yolov8默认自带letterbox但如果你自己写dataloader或转成其他框架就得自己操心这个了。4. 处理数据集用于yolov8训练data.yaml写法、预训练模型与训练参数4.1 data.yaml是第一个门槛类别数和路径错一个就白跑拿到数据集后接yolov8训练自己的数据集第一道门槛就是data.yaml。这个文件写错训练跑起来也是白费。我见过最典型的问题有两个一是类别数nc和实际类别清单数量不一致二是names列表里的类别名跟标注的类别ID对不上。YOLO训练时根本不读类别名它只认ID从0到nc-1但如果names写错后面画混淆矩阵、输出检测结果时你看到的标签全是错的。# container_defect.yaml # 数据集根目录建议写绝对路径避免工作目录切换导致路径失效 path: /home/user/datasets/container_defect train: images/train val: images/val nc: 3 names: 0: scratch 1: dent 2: corrosion几个字段逐个说。path是根目录train和val是相对于根目录的图片路径nc必须等于names列表长度也等于labels里出现的最大类别ID加1names的顺序必须和3.1脚本里class_name_to_id的映射保持一致——如果脚本里scratch是0这里0也得是scratch否则预测结果的类别语义就是乱的。我一般建议在训练前先跑一个数据校验用YOLO自带的命令快速检查标注有没有明显问题。yolo detect train datacontainer_defect.yaml modelyolov8n.pt epochs1跑1个epoch训练如果能在几分钟内正常开始说明数据读入没问题。这个“先跑1个epoch再跑全量”的习惯帮我避过很多次因为路径少写一个斜杠或者类别总数对不上导致的深夜报错。4.2 预训练模型下载与选择yolov8s在3类别上更适合做基底yolo预训练模型下载是很多人纠结的点其实Ultralytics的训练命令会自动下载对应架构的预训练权重不需要手动去翻网站。你只要在model参数里写yolov8n.pt、yolov8s.pt这类文件名框架会先从缓存找找不到就去官方仓库拉取。选哪个架构我的建议是3个类别的任务不算复杂但集装箱缺陷是小目标检测yolov8n可能容量不够yolov8m以上又有点浪费算力。在4127张这个规模下yolov8s是性价比最稳的起点——参数量适中收敛快即使你只有一块消费级显卡也能跑。除非你的缺陷目标极其微小、必须依赖更强的特征提取能力才值得上yolov8m或更大模型。先s后m用验证集结果说话别一上来就堆大模型。4.3 训练参数怎么设imgsz、batch、amp、epochs的实际取舍参数设置里最影响集装箱缺陷检测效果的是imgsz。很多教程默认imgsz640但工业图片拍出来的集装箱表面缺陷在640x640下可能只有十几个像素宽特征几乎被下采样抹掉了。我的做法是先把图片原始分辨率列出来看看缺陷框占全图的比例再决定imgsz。像这种原始图片长边达到3000像素以上的场景我一般直接拉到imgsz1536或imgsz1280训练如果显存不够再降到imgsz1024并用cacheTrue加速数据读取。yolo detect train \ datacontainer_defect.yaml \ modelyolov8s.pt \ epochs100 \ imgsz1536 \ batch8 \ cacheTrue \ ampFalse \ device0几个参数逐个说。batch8配合imgsz1536在16GB显存的卡上差不多显存不够就先降batch不要降imgsz。ampFalse是我在工业小目标场景的习惯——AMP混合精度训练能省显存和加速但小目标场景偶尔会出现loss变成NaN或者精度反降的情况关掉更省心。epochs100对4127张的规模来说足够缺陷检测数据量不大一般50轮后验证集mAP就基本平了100轮只是为了保险。4.4 训练完先看什么loss曲线比mAP更早暴露问题训练过程中和训练结束后最该盯的不是那张mAP表格而是runs/detect/train/下生成的results.png里面画出了box_loss、cls_loss、dfl_loss三条损失曲线和mAP曲线。我的判断顺序是先看三路loss是不是在稳步下降且没有剧烈震荡再看验证集mAP50有没有缓慢爬升最后才看混淆矩阵。如果box_loss降了但cls_loss纹丝不动多半是类别定义有问题比如两个类在外观上太相似或者类别ID标反了。如果loss曲线断崖式跳变去查是不是某个标注文件突然出现了一个极端异常的框比如宽高比10比1的长条噪声。一个务实的检查办法是训练到一半暂停把验证集预测结果可视化出来看一眼——直接看图往往比盯曲线更快发现问题这也是我为什么一直强调别省掉可视化这一步。5. 集装箱缺陷数据集使用避坑7z解压、bn崩溃、标注越界的六个翻车现场5.1 7z解压报错“密码正确但一直报错”现象压缩包有密码命令行输入密码后提示CRC错误或者Windows图形界面下报“数据错误文件已损坏”但密码明明是对的。原因这个报错在7z里很典型——密码正确但解压失败绝大多数情况是解压工具版本太老不支持高压缩比LZMA2算法或大字典设置。Windows上老版本7-Zip解压新版工具压出来的包就会这样另一个常见原因是中文文件名编码在跨平台时错乱导致CRC校验不通过。解决换新版本7-Zip或者直接用Linux终端的p7zip# 先确认包完整性 7z t container_defect.7z # 指定输出目录并解压 7z x container_defect.7z -o/home/user/datasets/ -pyour_password7z t是测试模式只校验不落盘报错信息能精确告诉你哪个文件出了问题。如果7z t通过而7z x失败优先怀疑磁盘空间不足或者目标路径没有写权限。密码忘记这种场景唯一靠谱的办法是回忆密码线索暴力破解7z不现实别浪费时间。压缩包本身如果是从正规渠道来的先确认下载完整——.7z文件下载一半后缀名也是.7z解压时一样报错。5.2 训练中bn崩溃或loss出现NaN现象训练跑了十几轮loss突然变成NaN或者日志里出现bnBatchNorm层的运行均值异常模型输出全是0或者无穷大。搜“yolo训练中bn崩溃”能看到大量同类求助帖。原因小批量训练时bn统计量不稳定尤其是batch很小、学习率又偏大的组合AMP开启时某些算子在低精度下溢出也会触发。集装箱缺陷数据集如果背景单一、前景目标小bn的统计值更容易漂移。解决首选把学习率从默认的0.01降到0.0001重跑其次关掉AMP即ampFalse最后检查数据里有没有标注框全为0的坏样本。我的血泪经验是这条链路按顺序排查90%的NaN都出在前两个原因上不需要去改模型结构。5.3 混淆矩阵总合不唯一是常态现象训练完毕验证集上YOLO输出的混淆矩阵把各格数值加起来不等于验证集的目标总数甚至每次运行结果都不一样。你可能也搜到过“yolo混淆矩阵总合不唯一”这个疑问。原因YOLO的混淆矩阵统计的不是“框”而是“预测结果”同一个GT框可能对应多个预测框一个预测框可能被匹配到另一个类别的GT加上置信度阈值过滤总和自然不等于GT数。尤其是缺陷重叠、一图多框时这个现象会非常明显不是bug也不是数据集问题。解决别用混淆矩阵的总和去校验数据集质量。要看真实类别分布去统计labels目录里每个类别ID出现的频次要看模型效果直接看验证集的mAP和每个类别的召回率。混淆矩阵的正确用法是看“哪个类和哪个类互相被认错”不是看总数对不对。5.4 中文路径与中文类名让数据集读取二次翻车现象数据集放在D:\我的数据\集装箱\这种带中文的路径下训练时YOLO报找不到图片但路径明明是对的。或者XML里类别名是中文转换脚本里class_name_to_id字典也写了中文训练时类别标签显示乱码。原因Windows下命令行编码和Python字符串默认编码不一致中文路径在传给Ultralytics时被转成乱码标注文件里的中文类别名如果存的是GBK编码脚本里按UTF-8读就会读出一堆乱字符。解决把数据集放到纯英文路径下目录命名用container_defect这种这一步能省掉后续所有编码头疼同时强制标注文件编码统一——转换脚本里写文件时指定encodingutf-8读文件时也用同样的编码。这个小习惯值得养成。5.5 标注框越界与漏标比你想的更常见现象训练正常跑但可视化验证集预测结果时发现有些GT框画出了图片边界或者某些明显带缺陷的图片一个框都没有。原因人工标注时框拖出边界属于常规操作漏标则是因为标注员疲劳或者缺陷太小没注意到。相比漏标越界框的危害更直接——训练时YOLO会计算框和图片的交集越界部分被裁掉导致框的位置整体偏移。解决训练前跑一遍数据检查脚本遍历所有XML和txt把xmin 0、xmax 图片宽这类异常统计出来。如果只有少量越界用3.1的clip逻辑修正如果大量越界就要考虑是否原图被缩放过后标注没同步更新。漏标是另一个层面的事只能靠抽样可视化人工复核看完100张图的预测结果基本上就能判断漏标比例高不高。5.6 小目标缺陷imgsz太小缺陷一旦小到几十像素就漏检现象模型训练完mAP看着不错但单独挑出缺陷较小的图片测试一个都检不出来。集装箱原图几千像素宽一条划痕可能只有30x80像素在640分辨率下缩放后可能只剩几个像素。原因YOLO的下采样倍数决定了小目标在特征图上的尺寸目标太小经过数次下采样后特征完全消失模型学不到可判别的信息。解决第一优先拉高imgsz能上1536就上1536这是最直接的手段其次可以考虑把原图切块成若干重叠patch分别检测再合并结果最后才是换带高分辨率分支的模型比如yolov8的P2输出层或引入SAHI这类切片辅助推理工具。注意不要盲目堆模型大小yolov8x对小目标的帮助常常不如把输入分辨率提上来。6. 让三个类别更耐打数据增强取舍与缺陷定位验证小技巧集装箱缺陷检测最怕的不是识别错而是漏检因为一个漏检的凹坑在实际码头场景里可能意味着整箱货物受损理赔。基于这个目标我最后分享两个自己常用的技巧一个关于增强策略一个关于验证方法。增强策略上YOLOv8默认开启的Mosaic增强在集装箱缺陷数据集上要谨慎。Mosaic把四张图拼成一张小目标会被进一步缩小原本就几十像素的缺陷在Mosaic图上可能直接消失。我的做法是把mosaic概率调低甚至关掉同时把HSV扰动加大——集装箱表面在不同光照、不同天气下拍摄色彩偏移很大Hue和Saturation扰动模拟的就是这种真实场景差异。# 在训练命令里直接覆盖增强参数 yolo detect train \ datacontainer_defect.yaml \ modelyolov8s.pt \ mosaic0.0 \ hsv_h0.02 \ hsv_s0.8 \ hsv_v0.5验证方法上我更信任“定位可视化”而不是只盯mAP数字。训练完跑一遍验证集把预测框、GT框、类别置信度一起画到图上人工扫图判断“漏检”和“误检”哪个更严重。这里养成一个习惯固定验证集每次改完参数都在同一个集上对比不要换来换去。# 快速可视化验证集预测结果 from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict(sourceimages/val, saveTrue, conf0.3, imgsz1536)输出会存在runs/detect/predict/下扫一遍这些图你会比看任何指标都更快地知道模型的真实水平三个类别的框颜色是否清晰可分、重叠缺陷是否被拆成多个框、小目标是不是漏了一整片。看图上框的位置和数量分布比对着混淆矩阵猜半天高效得多。我个人现在的习惯是拿到任何VOCYOLO双格式数据集先花十分钟做三件事——查类别清单、跑转换脚本、扫可视化样本然后才允许自己碰训练命令。这套流程在集装箱缺陷检测这个数据集上帮我省掉了至少两轮返工。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网