ARTICLE DETAIL

资讯详情

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

易拉罐底部缺陷检测数据集VOC+YOLO格式1122张5类别训练全流程

易拉罐底部缺陷检测数据集VOC+YOLO格式1122张5类别训练全流程 简介易拉罐底部缺陷检测数据集面向工业外观质检与计算机视觉目标检测适合算法工程师、深度学习者及智能制造项目开发者用于缺陷识别模型训练与验证。数据包含五个标注类别共三千三百零八个矩形标注框可作为易拉罐底部表面缺陷定位、分类及模型评估的基准素材。7z压缩包共两千个文件主体为一千一百二十二个XML标注文件与八百七十八个TXT标注文件分别对应VOC与YOLO格式另含使用前必读等说明文本整体大小约四十五点七七MB下载与解压较为便捷。数据由图形标注工具绘制矩形框标注规整说明文件提醒部分图片为增强生成建议训练前仔细筛选以保证模型评估的可靠性。目前已有165人学习或浏览可直接用于缺陷检测项目起步、数据格式转换练习也可作为数据增强效果对照的参考样本。1. 易拉罐底部缺陷检测数据集VOCYOLO格式1122张5类别先看清它解决什么问题易拉罐底部缺陷检测数据集VOCYOLO格式1122张5类别.7z从文件名能读出三件事场景是罐底外观质检交付是1122张带标注图片标注格式同时给了VOC与YOLO两套。在通用目标检测里1122张只能算热身量级但在产线缺陷检测里这个体量已经足够训练出一个能跑的baseline。真正让新手卡住的往往不是模型而是拿到压缩包之后那一连串琐碎问题双格式目录怎么对应、VOC怎么转成YOLO、data.yaml怎么写、训练时loss为什么一开跑就变nan。这篇按一线做法把流程完整走一遍顺手把7z解压、标注错位、小目标丢失这几个高频坑也讲清楚。适合刚入手缺陷检测、手里有工业数据集想跑通YOLOv8的工程师。2. 拆包先看双格式VOC与YOLO目录结构、5类缺陷来源与数据体检拿到7z包第一件事不是解压后立刻开训而是先把目录结构和标注内容看明白。工业数据集交付时的“格式”往往比模型本身更考验人尤其双格式并存时图片、xml、txt三者一旦对不上后续训练指标全是废的。这一章把VOC和YOLO两套标注的组织方式讲清楚再给一个统计脚本做数据体检。2.1 5类缺陷从哪来按罐底成型、印刷、输送线摩擦来划分易拉罐底部的缺陷检测放在产线里通常不是“找有没有罐子”而是找罐底外观上不影响密封、但影响客户观感的瑕疵。按工序来源划分常见的5类思路会落在三个环节上成型环节容易出现凹陷、压痕、边缘毛刺印刷环节容易出现漏印、套色偏差、油墨堆积输送和码垛环节最常见的是划伤、擦伤、油污和脏点。因此同类项目里“划伤/擦伤、凹陷/压痕、脏污/油污、涂层破损、边缘缺陷”是出现频率最高的组合方式。这里要提醒一句标题只告诉你有5个类别具体类名以解压后 data.yaml 或 labels 目录里对应的 names 为准不要凭文件名猜。我处理这类包的习惯是先找到 classes.txt、data.yaml 或 xml 里的name字段把所有不重复的类名列出来再决定类别映射字典。最常见的一个翻车现场是按文件名前缀想当然写了5个类结果转换脚本里类名对不上训练时模型学了个寂寞。罐底缺陷还有一个天然难点是光照。罐底是弧形金属面打光角度一变同一种划伤的成像差异就很大反光区域还会把轻微脏污“洗白”。所以拿到数据集后先看图像里的拍摄环境是否统一再看同一类缺陷在不同图上的外观差异。如果差异大训练时的增强策略和超参都要相应调整这一点在第2.3节的统计脚本里会进一步体现。2.2 VOC与YOLO双格式并存目录、xml与txt的对应关系VOC格式和YOLO格式并不是两套图片而是同一批图片配了两套标注文件。常见的目录布局如下格式典型目录标注文件内容VOCJPEGImages/ Annotations/每张图对应一个xmlobject节点写类别名和bndbox像素坐标VOC可选 ImageSets/Main/train.txt、val.txt里写不带扩展名的图片名用于划分YOLOimages/ labels/每张图对应一个txt每行是“类别ID cx cy w h”全部归一化到0~1xml里最关键的是object下的name和bndboxbndbox提供xmin、ymin、xmax、ymax四个整数像素坐标坐标是绝对像素值跟图片本身的宽高绑定。YOLO的txt则把同一目标写成一行浮点数第一个数字是类别ID从0开始后面四个是归一化后的中心点cx、cy和宽高w、h。归一化意味着w和h是相对图片宽高的比例不是像素值。两个格式之间靠文件名stem一一对应比如 image_001.jpg 对应 Annotations/image_001.xml也对应 labels/image_001.txt。解压后第一件事是数文件数量jpg、xml、txt三种文件数量应该一致如果只有图片加一套标注也要把另一套的生成方式想清楚。如果不一致多半存在空标注图、损坏图或重复命名这部分要在开训前清掉。VOC侧如果带了ImageSets/Main下的train.txt和val.txt说明作者已经把划分做好了但我仍建议打开看一眼划分比例。很多数据集默认按7:2:1分但它是完全随机分还是按批次分直接决定训练结果能不能反映真实产线情况这也是第4章专门讲划分的原因。2.3 拿到包先做数据体检统计脚本与三类必须看的分布空谈格式不如直接跑统计。下面这个脚本只依赖OpenCV和Python标准库遍历Annotations目录里的xml把每个框的类别和面积占比算出来输出三类关键分布类别数量、每张图的目标数、缺陷框相对原图的面积占比。import os import cv2 from xml.etree import ElementTree as ET voc_dir VOC/Annotations # 改成实际xml目录 img_dir VOC/JPEGImages # 改成实际图片目录 def analyze_voc(voc_dir, img_dir): stats {} # 类别 - 框数量 size_ratios [] # 每个框面积占原图比例 empty [] # 没有目标的图 broken [] # 读不出来的图 for xml_name in sorted(os.listdir(voc_dir)): if not xml_name.endswith(.xml): continue stem xml_name[:-4] xml_path os.path.join(voc_dir, xml_name) img_path os.path.join(img_dir, stem .jpg) img cv2.imread(img_path) if img is None: broken.append(stem) continue H, W img.shape[:2] tree ET.parse(xml_path) root tree.getroot() objs root.findall(object) if len(objs) 0: empty.append(stem) for obj in objs: name obj.find(name).text stats[name] stats.get(name, 0) 1 box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) ratio (x2 - x1) * (y2 - y1) / (W * H) size_ratios.append(ratio) print(类别统计:, stats) print(空标注图:, empty) print(损坏图:, broken) print(框面积占比 最小/中位/最大:, round(min(size_ratios), 5), round(sorted(size_ratios)[len(size_ratios)//2], 5), round(max(size_ratios), 5)) analyze_voc(voc_dir, img_dir)跑完后重点关注三个量。第一是类别分布如果某一类只占5%以下训练时recall大概率拉不起来后面要考虑类别加权或先扩样本。第二是框面积占比如果大多数框占原图不到1%说明缺陷是小目标默认的imgsz640很可能会把缺陷缩没训练前就要把分辨率提到1024甚至更高。第三是空标注和损坏图cv2读不出或XML里没有目标的图YOLO训练时会直接跳过或当背景处理会让有效样本数比文件名显示的少一截。我还会顺手统计每张图的目标数量。如果大量图只有1个框验证集mAP波动会很大如果某些图有十几个小框说明那张图是“困难样本”对模型学习更值钱。这个脚本的输出会成为第4章划分训练集和设置超参的直接依据。3. 把VOC转成YOLO转换脚本、归一化公式与四个边界坑VOC转YOLO是手里有这套数据集之后绕不开的一步。虽然第2章提过包内可能直接带了YOLO格式但工业项目里经常出现“VOC侧完整、YOLO侧缺失”或“txt是从别人那儿拷来的类别顺序跟我不一致”的情况所以转换脚本必须自己攥在手里。3.1 为什么YOLO默认不吃VOC训练入口与归一化坐标YOLOv5和YOLOv8的数据加载器默认读取的是每张图一个txt的标签文件而不是xml。原因是训练时每张图要随机做letterbox、缩放和增强标签必须跟着图像尺寸一起变换VOC的绝对像素坐标在这种动态流程里很不方便归一化坐标则可以直接配合等比缩放计算。所以“处理数据集用于yolov8训练”通常的第一步就是先把VOC的xml解析成YOLO的txt。归一化公式很简单但细节容易错cx ((xmin xmax) / 2) / W cy ((ymin ymax) / 2) / H w (xmax - xmin) / W h (ymax - ymin) / H注意分母W和H必须是图片真实的宽高不是xml里写的尺寸。工业数据集里经常出现xml的width字段和实际图片尺寸不一致的情况最常见原因是中间有人压缩过图片但没重新生成标注用实测尺寸做分母结果才可靠。还有一个容易忽略的误用有人把txt直接存进YOLO侧后又用逗号分隔坐标或写成x1,y1,x2,y2的像素格式YOLO加载器不认这种写法训练时会出现“标签格式异常”的告警。归一是必须的再小的项目也别偷懒。3.2 XML转TXT的Python脚本可直接改路径复用下面这段脚本按“图片Annotations”结构解析输出YOLO格式。类名顺序用 class_names 列表固定列表下标就是YOLO的类别ID这个顺序必须和第4章data.yaml里的names完全一致否则训练出的推理结果会张冠李戴。import os import cv2 from xml.etree import ElementTree as ET # 按你想训练的类别顺序写顺序就是YOLO ID class_names [scratch, dent, stain, coating_break, edge_ndefect] def convert_one(xml_path, img_dir, out_dir): stem os.path.basename(xml_path)[:-4] img_path os.path.join(img_dir, stem .jpg) img cv2.imread(img_path) if img is None: print(图片缺失跳过:, img_path) return False H, W img.shape[:2] tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_names: print(未知类别跳过该框:, name) continue class_id class_names.index(name) box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) # 第一道坑xmin/xmax可能写反 x1, x2 min(x1, x2), max(x1, x2) y1, y2 min(y1, y2), max(y1, y2) # 第二道坑框可能越过图像边界必须clip x1 max(0.0, min(x1, W)) y1 max(0.0, min(y1, H)) x2 max(0.0, min(x2, W)) y2 max(0.0, min(y2, H)) if x2 - x1 1 or y2 - y1 1: print(clip后框为空丢弃:, stem, name) continue cx ((x1 x2) / 2) / W cy ((y1 y2) / 2) / H w (x2 - x1) / W h (y2 - y1) / H lines.append(f{class_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(os.path.join(out_dir, stem .txt), w) as f: f.write(\n.join(lines)) xml_dir VOC/Annotations img_dir VOC/JPEGImages out_dir YOLO/labels os.makedirs(out_dir, exist_okTrue) for f in sorted(os.listdir(xml_dir)): if f.endswith(.xml): convert_one(os.path.join(xml_dir, f), img_dir, out_dir)这段代码几个参数要按实际情况改class_names 是类别映射的唯一真源xml_dir、img_dir、out_dir 分别指向对象。输出txt里每行保留6位小数足够YOLO的浮点精度也避免文件体积无意义膨胀。转换日志里的“未知类别”和“图片缺失”要认真看不要视而不见未知类别说明你的类名猜得不对缺图说明文件名体系有问题这两类问题不及时处理训练时症状都会被当成“模型不行”。第三道坑是clip本身如果源框越界超过边框的30%说明标注质量堪忧clip只是止损不是修复第四道坑是类别ID从0开始第1行如果写了1模型就会少学一类验证时所有框的类别都会偏移。3.3 转完必须做的事画框回看与坐标合法性检查转完不代表完事。我会把生成的txt画回图上随机抽30到50张看一眼这一步能发现绝大多数标注错位问题。画框脚本很简单读txt乘回图像宽高得到像素坐标用OpenCV画矩形。具体做法是遍历 labels 下的txt每行按 class_id cx cy w h 解析令 x1 int((cx - w/2) * W)。如果发现框整体偏移半个身位、框比真实缺陷大一圈基本可以断定转换脚本里坐标写反或者除错了尺寸。除了人眼抽查还要做三项机器检查一是txt里不能出现负值、大于1的值、NaN一旦出现训练时损失函数直接爆炸二是每个stem都要有对应的jpg反之亦然三是YOLOv8在训练开头会校验标签格式如果txt是UTF-8带BOM可能报标签格式相关错误用ASCII写文件最稳。这三项检查跑完再把转换脚本打印出来的空标签图数量记下来第4章划分数据时把这些图单独处理。如果包内YOLO侧已经给全了txt我的建议是也用这套脚本反向生成一份VOC用于回看校准两份标注互相背书。很多数据集在交付时VOC和YOLO两套并非同一次标注生成其中一套可能改过但没同步这个动作能帮你提前发现不一致。4. 用YOLOv8训练易拉罐底部缺陷数据划分、data.yaml与超参设置格式转换没问题之后真正进入训练环节。1122张图的数据量不大训练策略要偏保守宁可多花几次验证也不要一把梭跑300个epoch后发现分布划分本身就是错的。4.1 数据划分不能全随机按批次分且保持类分布一致1122张全部随机划分的后果往往是验证集mAP虚高。原因很简单同一产线连续拍摄的图片背景、光照几乎一样随机划分会让“同场景复制品”同时出现在train和val里模型实际上是在背图。如果数据集没提供批次或时间信息至少要按文件名的编号顺序分块比如前800张做train中间200张做val后面122张做test。文件名编号连续基本可以近似看成采集时间连续。再做一层stratified处理让每类缺陷在train、val、test里的比例和全集一致。比如某类只有50张随机划分极可能把其中15张分到val、让train只剩35张导致这类学不好。常见比例是7:2:1也就是约785张train、225张val、112张test。如果样本量紧张也可以先做3折交叉验证把1122张分成3份每次拿1份做val报告3次val mAP的均值和方差这个数字比单次随机划分可靠得多。4.2 data.yaml关键字段names顺序、绝对路径与类别数data.yaml是YOLOv8训练时唯一必须保证正确的配置文件。下面是一个可直接改的模板path: /home/yourname/cans_bottom # 数据集根目录用绝对路径 train: images/train # 相对path的训练图目录 val: images/val # 相对path的验证图目录 test: images/test # 可留空 nc: 5 names: 0: scratch 1: dent 2: stain 3: coating_break 4: edge_ndefect三个容易踩的位置第一条是path别写相对路径YOLOv8在不同工作目录下启动时相对路径解析结果不一样第二条是names列表顺序必须与第3章转换脚本里的class_names完全一致YOLO训练只认这个索引不认类名文本第三条是nc可以自动推断但显式写出来能防止names写少一类而不自知。另外7z解压后的目录里如果带中文或空格YAML解析路径时经常出问题。我的做法是解压后先把整个数据集目录重命名为纯英文目录比如 cans_bottom_v1再改data.yaml。这一步花不了两分钟但能省下大量莫名其妙的图片读取报错。4.3 训练超参与损失函数观察小数据集的保守开局训练命令用YOLOv8的标准命令行即可yolo detect train \ modelyolov8n.pt \ datacans_bottom.yaml \ epochs200 \ imgsz640 \ batch16 \ lr00.001 \ mosaic0.0 \ patience40这里每个参数都按小数据集场景做了收敛调整。model选yolov8n.pt是nano版1122张图用s或m版很容易过拟合n版起步、不够再加容量imgsz先用640如果第2.3节统计的框面积占比中位数低于1%提到1024batch选16主要为了让BN的统计量在小数据集上不至于太抖lr0降到0.001因为数据集小默认的0.01往往跑不了几个epoch就震荡mosaic关掉罐底小目标的框本来就细mosaic在拼接时容易把目标切成两半导致学到的全是残缺特征patience40是早停没改善就停不硬耗。训练时看三部分lossbox_loss是预测框与真实框位置的差异cls_loss是分类错误dfl_loss是边框分布损失的变体。三者都该随epoch下降如果val的box_loss先降后升就是过拟合信号如果几轮后数值纹丝不动优先怀疑学习率太低或数据划分有问题。显存够大比如V100这类显卡可以开batch32和mosaic0.5但小数据集上收益有限我一般先保守等第一版结果出来再放开。4.4 训练中容易误读的三个指标mAP、混淆矩阵与PR曲线训练结束先看val的mAP50和mAP50-95不要只盯着mAP50。小缺陷检测里mAP50-95天然偏低因为IOU阈值从0.5上升到0.95时几个像素宽的划伤框很容易被判为不重合数值低不一定是模型不行也可能是标签框本身的边界打得不紧。混淆矩阵里经常有人问“为什么每行加起来不是100%”。YOLOv8输出的混淆矩阵默认是计数矩阵不是归一化矩阵总和当然不唯一看它主要看两类信息哪些类互相混淆严重背景被误检的比例有多高。如果某个类别一半样本被分到相邻类先去查标注看是不是类边界本身就不清比如“划伤”和“涂层破损”在图像上长得像。PR曲线重点看尾部曲线后段断崖式下跌说明高置信度预测里混着大量误检曲线平滑但整体偏低说明特征本身不够可分。把这些写在笔记里之后每次调参只改一个变量否则你根本不知道是imgsz起作用、还是mosaic关掉起作用、还是数据划分变了起作用。5. 这套数据集落地必踩的坑7z解压、标注错位、小目标丢失与BN崩溃前面讲的是标准流程这一章讲的是我实际处理工业数据集时反复踩过的坑按出现频率高到低排。每一条都是“现象、原因、解决”的结构可以直接对照。5.1 7z解压报错密码正确但一直报错的几个真实原因现象用7z命令解压密码输入了、确认没输错结果报“CRC错误”或“数据错误”甚至在Windows下都解出一半就中断。原因通常是四种第一磁盘空间不足或输出目录所在分区满了CRC失败是表象第二压缩时开了文件名加密包里所有文件名都不可见密码会同时参与文件头校验稍有出入就整体失败第三分卷包只放了其中几个part或分卷被改过名第四压缩包本身做了tar外套7z解出的是tar再解一层才算完。解决顺序先7z l 包名.7z看能不能列出内容再确认磁盘剩余空间如果列不出内容但你有密码用7z l -p密码试试再把所有分卷放到同一目录文件名保持原始命名最后检查解出来是不是还带.tar后缀。Linux下解压前先确保装了p7zipapt install p7zip-fullWindows下用最新版7-Zip老版本对高压缩等级兼容性差。5.2 标注错位xml与txt对不上时先从文件名和坐标写反排查现象训练出来的mAP看起来不错一到真实验证就发现检测框位置整体偏移或者类别张冠李戴。原因多半不是模型而是两部分标注根本没对齐。常见的是文件名大小写问题Windows里生成的 image_001.JPG 和 image_001.jpg 是同一个文件Linux里是两个文件cv2.imread会读到一个空的YOLO训练时该图直接变背景更隐蔽的是坐标写反有人把txt写成了 x1,y1,x2,y2 的像素坐标或者把 cx,cy,w,h 排成了 x1,y1,w,hYOLO加载器不报错但训练出来全是歪框。解决统一把所有文件名转成小写再建索引用第3.3节的画框脚本回看50张再写一个检查脚本读txt的最后一列看h是否为负或大于1一旦出现直接删掉重新转换。这类问题越早发现在前面越省训练时间。5.3 小目标缺陷在缩放后消失imgsz与切图策略现象模型在训练集上loss正常但val的recall极低尤其划伤、边缘毛刺这类细长缺陷几乎全军覆没。原因很直接1122张图如果原图是1600x1600缺陷框只占20x10像素缩放到640后缺陷变成不到8x4像素经过letterbox再缩一圈特征彻底丢了。解决有几个层次第一把imgsz从640提到1024或1280这是最省事的方案代价是显存和训练时间翻倍第二如果1024仍然不够做切图训练把每张原图切成若干tile比如1600x1600切成4张800x800tile之间留50像素overlap推理时再把所有框坐标还原回原图坐标系第三检查标签里有没有宽或高小于2像素的框这类框在增强缩放后约等于消失要么通过提高采集分辨率重新标注要么把它从训练集剔除否则模型会被废标签干扰。5.4 YOLO训练中BN崩溃与loss爆炸小数据集上的常规诱因现象训练到十几轮loss突然跳到几个数量级以上随后出现nanval指标清零。原因在小数据集上高度集中在三处batch太小而学习率太大导致BN统计量剧烈抖动数据里混入了全黑或全白图cv2读出来shape没问题但像素方差为0标签坐标里存在nan或超大值梯度一传就炸。解决先把batch提到16以上lr0降到0.001甚至0.0005然后把所有图片做一次像素值方差检查去掉方差极低的坏图最后用一个最小脚本遍历所有txt检查每行是否都能解析出五个合法浮点数范围是否都在0到1之间。这三步做完再开训练。记住一个常识在小数据集上BN崩溃十有八九是“喂进去的东西不干净”不是模型结构问题。5.5 5类别样本不均衡光加权不够还要观察召回分布现象mAP50整体还行但某两类recall特别低翻看标注统计才发现其中一类可能只有几十张占全集不到5%。解决不是简单地给loss加权。加权会让模型对少样本类的所有个体都“过度自信”一旦遇到现场新光照下的同类缺陷误检率反而更高。我一般分两步第一步先按第2.3节的统计脚本看每类在train、val里的绝对数量小类如果少于40张优先靠数据增强扩充形态比如旋转、镜像、光照扰动而不是复制粘贴同一张图第二步对大类做每epoch采样上限比如每轮最多训练150张该类的图避免模型被大类彻底主导。训练完不要只看mAP均值要看按类别的recall曲线少样本类的recall即使只有50%也比“看起来均值高、实际只学了一类”有价值。6. 回到产线用OpenCV做一次推理验证并沉淀新样本6.1 用OpenCV加载训练好的模型最小推理脚本模型训练完先拿没进过训练集的现场图跑一次推理。用OpenCV做图像读写用Ultralytics库完成推理脚本很短from ultralytics import YOLO import cv2 model YOLO(runs/detect/train/weights/best.pt) img cv2.imread(real_line_001.jpg) results model(img, conf0.25, imgsz640)[0] for box in results.boxes: x1, y1, x2, y2 map(int, box.xyxy[0].tolist()) label f{model.names[int(box.cls)]} {float(box.conf):.2f} cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(real_line_result.jpg, img)conf参数是关键产线误检率高就调到0.4以上漏检严重就降到0.1以下先用20张现场图测出合适的阈值区间再固定下来。6.2 让数据集越用越厚预标注回填、困难样本与现场增强跑通只是起点。我自己的习惯是每次现场测试后把所有漏检和误检图单独存一个hard_negatives目录每周用模型对这些新图做预标注人工只修正框的位置和类别修正后回填到数据集重新训练。这套“预标注人工修改”的闭环比一开始把1122张全量洗一遍更能提升现场表现。迁移到轴承、硅片、燃气管道等表面缺陷场景思路也一样双格式数据集负责打底现场样本负责纠偏。希望帮到你。本文还有配套的精品资源点击获取
返回列表