首页 / 资讯中心 / 文章详情

焊接缺陷检测数据集详解:VOC转YOLO格式与增强避坑指南

焊接缺陷检测数据集详解:VOC转YOLO格式与增强避坑指南 ★ FEATURED ARTICLE
简介面向焊接质检与工业视觉目标检测场景这套数据集覆盖烧穿、污染、未完全融合、未完全渗透、错位等常见焊接缺陷并包含正常状态样本共4137张已增强图片矩形框标注可供YOLO/VOC系列模型训练和验证。压缩包共2000个文件以XML标注文件与TXT标签说明为主整体约153.88MB解压后按JPEGImages、Annotations、labels三类目录组织分别存放图片、VOC格式XML标注与YOLO格式TXT标签。数据集标签格式规范六类合计4214个标注框且各框数分布相对均衡便于分类别评估模型表现图片清晰、已做增强可支持迁移学习、缺陷检测算法验证或教学实验。目前已有203人学习浏览。需要说明的是作者特别声明不对训练产生的模型或权重文件精度作保证使用时建议结合自有数据进一步验证。1. 焊接缺陷检测数据集为什么4137张增强图能替代上万张原始图质检员拿着一支手电、一块放大镜在焊件上来回扫那条焊缝看有没有气孔、裂纹、咬边。这套目检流程在工厂里跑了几十年效果不差但说到底也靠点经验玄学人一疲劳漏检率就上来了。把这事交给目标检测模型第一前提就是数据6类焊接缺陷、4137张已增强图、YOLOVOC双格式这类焊接缺陷检测数据集的目标就是让做工业视觉的团队不必从零开始采集和标注直接拿一份接近产线分布的标注数据把基线打通。它适合三类人焊缝质检方案工程师、做缺陷检测的算法同学以及想快速验证YOLO在工业场景能不能落地的从业者。拿到手先别急着跑训练先把类别定义和坐标格式核对清楚这篇文章就是把从拆包到验证的整条路径讲透。2. 6类缺陷与两种标注格式的本质像素坐标和归一化坐标差在哪2.1 六类焊接缺陷的形态与检测难点焊接缺陷数据集的品类划分行业里基本沿用探伤和目检的习惯最常见的是这六类气孔、夹渣、未熔合、未焊透、咬边、裂纹。每一类在图像上的形态差异很大检测难点也完全不同。缺陷类别典型形态检测难点气孔圆形或椭圆暗点常成群出现尺寸小单孔可能只有几个像素夹渣不规则块状灰度不均边界不清晰容易和背景反光混在一起未熔合条状或片状沿焊缝方向分布边缘模糊对比度低未焊透根部线状暗带细长几乎贴着母材对比度最差咬边焊缝边缘的沟槽细长条状宽度窄且紧贴焊缝边缘易被忽略裂纹细线状方向随机可能有分叉形态细标注难度大增强易断裂我会在拿到数据集后优先做一件事把标注里的类别名全部列出来和这六类对一遍。不同数据集命名习惯不一样有的用中文标签有的用porosity、slag这种英文缩写还有的会用crack代替所有细线缺陷。类别名对不上后面所有转换脚本都会跟着错。2.2 VOC格式的XML结构像素坐标与标注语义VOC格式指的是Pascal VOC的标注结构解压后一般会看到JPEGImages、Annotations、ImageSets/Main三个目录。JPEGImages放原图Annotations放同名XMLImageSets/Main里则是train.txt、val.txt这些划分文件。XML里的核心字段是object节点下的name和bndbox。name定义缺陷类别bndbox给出xmin、ymin、xmax、ymax四个像素坐标。注意这四个值是绝对像素坐标基于原图分辨率不随缩放变化。还有size节点记录图像的宽和高这个字段在格式转换时是关键分母很多脚本跑挂了就是因为它丢了或者填了0。XML字段含义使用时的注意点size/width原图宽度像素数转换时分母缺失会导致除零或坐标爆炸size/height原图高度像素数同上object/name缺陷类别名必须和转换脚本的类别映射一致bndbox/xmin等左上右下像素坐标转换后需验证是否落在0-1区间2.3 YOLO格式的txt文件归一化坐标与类别索引YOLO格式的标注是纯文本txt每行对应一个目标框格式是class_id cx cy w h。class_id是从0开始的类别索引cx、cy是目标中心点的归一化横纵坐标w、h是归一化框宽高。归一化的含义是除以图像宽高所以所有值都在0到1之间。为什么YOLO要归一化而不是直接用像素坐标核心原因是模型训练时图像会被resize到固定尺寸比如640x640归一化坐标在resize前后不需要做任何换算直接参与训练。而VOC的像素坐标在图像resize后必须同步缩放否则框就错位。从VOC到YOLO的映射关系其实就一句话像素坐标除以图像宽高得到归一化坐标。但这里有个容易被忽略的点类别名到索引的映射表classes.txt一旦确定训练过程中就不能改训练集和验证集必须共用同一个顺序。我之前见过有人训练集按字母序排、验证集按数据集自带顺序排结果mAP直接对不上。3. 把VOC转成YOLO格式转换脚本与三个边界坑3.1 用Python把XML转成YOLO训练的txt拿到VOC格式的标注后第一步就是转成YOLO的txt。下面这个脚本是我常用的最小可用版本逻辑清晰适合作为起点再按自己数据调整。import os import xml.etree.ElementTree as ET # 类别顺序一旦确定训练过程中不要再改动 # 训练集和验证集必须共用同一个映射表 CLASS_MAP { porosity: 0, # 气孔 slag: 1, # 夹渣 lack_of_fusion: 2, # 未熔合 incomplete_penetration: 3, # 未焊透 undercut: 4, # 咬边 crack: 5 # 裂纹 } voc_dir Annotations yolo_dir labels os.makedirs(yolo_dir, exist_okTrue) def convert_voc_to_yolo(xml_path, image_w, image_h): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.findtext(name) if name not in CLASS_MAP: # 遇到没定义的类别名先打印出来确认不要直接跳过 print(funknown class in {xml_path}: {name}) continue box obj.find(bndbox) xmin float(box.findtext(xmin)) ymin float(box.findtext(ymin)) xmax float(box.findtext(xmax)) ymax float(box.findtext(ymax)) # 像素坐标转归一化坐标计算中心点和宽高后除以图像尺寸 cx (xmin xmax) / 2.0 / image_w cy (ymin ymax) / 2.0 / image_h w (xmax - xmin) / image_w h (ymax - ymin) / image_h # 裁剪到[0,1]防止标注越界导致训练时框坐标溢出 cx max(0.0, min(1.0, cx)) cy max(0.0, min(1.0, cy)) w max(0.0, min(1.0, w)) h max(0.0, min(1.0, h)) lines.append(f{CLASS_MAP[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) return lines for xml_file in os.listdir(voc_dir): if not xml_file.endswith(.xml): continue xml_path os.path.join(voc_dir, xml_file) tree ET.parse(xml_path) size tree.getroot().find(size) if size is None or size.findtext(width) in (None, 0): # 常见做法从XML读宽高缺失时用PIL读真实图像兜底 from PIL import Image img_path os.path.join(JPEGImages, xml_file.replace(.xml, .jpg)) image_w, image_h Image.open(img_path).size else: image_w float(size.findtext(width)) image_h float(size.findtext(height)) lines convert_voc_to_yolo(xml_path, image_w, image_h) if lines: out_path os.path.join(yolo_dir, xml_file.replace(.xml, .txt)) with open(out_path, w) as f: f.write(\n.join(lines) \n)这段代码的流程很直接遍历Annotations目录下所有XML解析出每个object的name和bndbox按CLASS_MAP映射成索引再除以图像宽高得到归一化坐标。图像宽高优先从XML的size节点读取因为标注规范里size就是原图尺寸但如果size节点缺失或为0就改用PIL读JPEG图像的实际尺寸兜底。CLASS_MAP的顺序是整个脚本的灵魂。它决定了每个类别在训练时对应的class_id。如果数据集自带的标签说明里的顺序和这个映射不一样以数据集实际标注为准不要先入为主按行业习惯写死。转换完成后建议打印一遍每个类别的框数量确认没有类别消失。3.2 边界坑分母为零、越界坐标、类别错位第一个坑是分母为零。表现是转换脚本直接抛ZeroDivisionError或者生成的txt里cx、cy全是inf。原因是XML里的size节点缺失、width字段为0或者标注工具导出时根本没写分辨率。解决方法是脚本里必须保留PIL兜底分支用真实图像宽高替代。习惯上我会在转换前先跑一个脚本统计所有XML的width最小值低于阈值的直接标记出来。第二个坑是越界坐标。表现是归一化后w或h大于1或者cx、cy超出[0,1]。多数发生在增强后的数据上——旋转或裁剪时框坐标没有同步裁剪导致右边界超出图像宽度。脚本里加clip到[0,1]只能防训练报错但解决不了框实际位置错误的问题。处理办法是clip后重新算一次框面积如果面积趋近于0说明这个框已经被增强操作破坏直接过滤掉比留着更安全。第三个坑是类别错位。表现是训练能跑通但验证集上某个类别的AP始终为0混淆矩阵里预测结果整体偏移一位。最常见原因是训练集和验证集分别用了不同顺序的classes.txt或者原始数据集的标签名里有隐藏空格。我一般会写一行命令把生成txt里的class_id做一次unique统计再把类别名顺序和CLASS_MAP对比花两分钟就能定位问题。可视化抽样画框也是有效的复核手段这个习惯最好保留到整个数据管线里。4. 已增强数据怎么用增强策略、参数与三类校验4.1 针对性增强小目标与焊缝细长形态的约束焊接缺陷和通用目标检测的显著差异在于缺陷小、形态细长、局部对比度低。气孔可能只占几十个像素裂纹宽不过两三个像素。这意味着增强策略不能照搬COCO那套通用管线无脑随机裁剪和大幅旋转很容易把本来就小的缺陷裁掉或拉伸到认不出来。我一般会先分析三件事缺陷占图像面积的比例分布、缺陷的长宽比、以及缺陷和背景的灰度差。占比低的样本做随机裁剪时要限制scale下限长宽比大的缺陷裂纹、未焊透不适合做90度旋转灰度差低的样本不能大幅降饱和。这些都是增强前需要先看清楚的事实而不是凭感觉调参数。标题里的4137张已增强图本质上就是从原始样本通过组合增强扩出来的。常见做法是多路线叠加水平翻转、小幅旋转、亮度扰动、对比度调整、裁剪、拼接各来一种最终每个原始样本生成4到8张派生图总样本量就达到几千的量级。4137这个数字并不规整说明不同类别的增强倍数可能不一样这种非均匀增强在长尾分布下是合理的。4.2 增强参数表什么组合能凑出几千张下面这张参数表是我在焊接缺陷数据上反复调过的经验值适用于离线增强生成训练集也适用于在训练时叠加在线增强。增强项经验参数适用原因注意点水平翻转p0.5焊缝左右对称标注无需变换有方向物理含义的缺陷慎用小幅旋转±10度焊接姿态和拍摄角度差异必须同步旋转标注框HSV色相偏移H ±10金属反光和氧化色差异色相大幅偏移会让缺陷颜色失真饱和度偏移S ±30%打光不均导致颜色浓淡变化对比度低的缺陷别再降饱和亮度偏移V ±40%弧光和遮挡导致明暗变化亮度太高小缺陷会融入背景高斯模糊半径1-2对焦不准和轻微运动模糊模糊太强小目标直接消失椒盐噪声p0.01焊接飞溅和尘点干扰噪声分布要贴近产线实际随机裁剪scale 0.5-1.0等效增加拍摄视角裁剪后丢失标注框的样本要过滤Mosaic拼接4张拼接增加上下文多样性拼接时标注框要按偏移量重算数量估算的规则很简单最终训练集规模约等于原始样本数乘以每条增强路线的命中概率之和。如果原始样本800张水平翻转出800张亮度扰动出800张旋转出800张裁剪出800张Mosaic再出800张叠加去重后就是4000张上下和4137这个量级是吻合的。如果发现增强后的总量和预期差很多优先检查增强路线的执行概率是不是被过滤逻辑误伤了尤其是裁剪类增强会把大量小框滤掉。4.3 增强后三类校验文件、类别分布、可视化增强做完第一类校验是文件一一对应。每张增强图必须存在对应的txt或XML标注文件不能多也不能少。用下面的脚本可以快速核对import os images_dir images labels_dir labels img_names { os.path.splitext(f)[0] for f in os.listdir(images_dir) if f.endswith(.jpg) or f.endswith(.png) } label_names { os.path.splitext(f)[0] for f in os.listdir(labels_dir) if f.endswith(.txt) } missing_labels img_names - label_names missing_images label_names - img_names print(f缺标注的图: {len(missing_labels)}) print(f缺图像的标注: {len(missing_images)}) for name in list(missing_labels)[:10]: print(f {name})这段脚本的核心是构建两个文件名集合做差集。增强库如果没配对输出图像和标注这里立刻能看出来。缺标注的图直接删掉或重新生成缺图像的标注说明管线有严重问题需要回去检查增强流程。我一般要求差集数量为0才会进入下一步。第二类校验是类别分布统计。按txt里每行的class_id统计框数量观察六类缺陷的框数是否均衡。增强如果全用全局随机少数类通常是裂纹和未焊透的绝对数量可能仍然很少这时就要对少数类做额外加权增强否则后面训练时这一类AP大概率是0。第三类校验是可视化抽样。随机抽九张增强图把标注框画回去人工确认框有没有跑偏、缺陷有没有被增强操作破坏。import cv2 import os for img_name in [sample_001.jpg, sample_009.jpg, sample_013.jpg]: img cv2.imread(fimages/{img_name}) h, w img.shape[:2] label_path flabels/{img_name.replace(.jpg, .txt)} if not os.path.exists(label_path): print(f缺少标注: {label_path}) continue with open(label_path) as f: lines f.readlines() for line in lines: parts line.strip().split() cls_id int(parts[0]) x_c, y_c, bw, bh map(float, parts[1:]) # txt里是归一化坐标画框前要乘回图像宽高 x1 int((x_c - bw / 2) * w) y1 int((y_c - bh / 2) * h) x2 int((x_c bw / 2) * w) y2 int((y_c bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(cls_id), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(fcheck_{img_name}, img)画框输出后重点看两类情况框是否还紧贴缺陷边缘以及增强后缺陷是否仍然保持原来的可辨识形态。比如裂纹增强后如果断成几截模型大概率学到的是碎线段而不是完整裂纹这类样本应该调低旋转和裁剪强度后重新生成。这三类校验跑完数据才算真正可用。在YOLOv8训练自己的数据集时第一条命令不是调参而是跑通数据管线。确认txt格式无误后用最小配置先跑一轮yolo train modelyolov8n.pt \ datawelding_6cls.yaml \ imgsz640 epochs80 batch16这轮训练只看两件事loss有没有在正常下降以及有没有报坐标或标签相关的错误。如果第一个epoch loss就是nan优先回去查标注格式和类别索引而不是调学习率。数据集本身已经做过离线增强训练时在线增强建议保持默认强度但别再加额外的Mosaic否则叠加损伤会让小缺陷目标彻底消失这是我在实际项目里反复遇到的教训。5. 避坑手册焊接缺陷数据集最容易翻车的5个坑5.1 增强导致标注错位框跑到背景上现象是训练时loss下降正常但推理阶段误检特别多预测框大量出现在背景区域。原因是增强库在处理旋转或裁剪时只变换了图像没有同步变换标注框。特别是用OpenCV自己写旋转增强的时候图像转了90度但标注还指向原来的像素位置框就错位了。解决方法是统一使用带BboxParams的增强库让图像和标注走同一条变换链路并在增强后固定随机种子重跑可视化校验。我习惯把这类校验沉淀成一个固定脚本每次生成新数据集都跑一遍。5.2 小缺陷目标被缩放消失mAP卡在低位现象是训练了几百个epochmAP一直在低位徘徊验证集上小缺陷几乎全漏。原因多半是增强管线里的随机缩放把原本只有几十像素的小框缩到几个像素或者随机裁剪把框裁掉后被过滤逻辑直接删掉。解决方法是限制缩放尺度范围比如scale控制在0.5到1.5之间同时对长宽比极端的缺陷单独做拼接增强——把缺陷区域抠出来放大后贴到新背景上而不是整体依赖随机裁剪。过滤被破坏的标注框时优先做降权处理而不是直接删除否则少数类样本会越滤越少。5.3 训练验证划分泄露验证mAP虚高现象是验证集mAP比实际测试结果高出一大截差距超过10个点。原因是用增强后的文件名直接随机划分训练集和验证集同一原始样本的多个增强派生图被分到了两侧。模型等于在验证时看到了训练图的近亲版本评估结果自然乐观。解决方法是按原始样本ID划分而不是按文件名划分先给每个原始样本编号增强时把派生图继承这个编号划分时整个编号组要么全进训练集要么全进验证集。这是焊接缺陷数据最容易踩的大坑之一我见过不止一个项目在这里翻车。5.4 类别不平衡被增强完全忽略个别缺陷AP为零现象是六类缺陷里某一类的AP始终是0其他类都正常。原因是用全局统一的增强概率处理长尾分布少数类得到的新样本仍然很少。比如气孔数量多、增强概率高裂纹本身数量少、又被过滤掉一部分最终训练时裂纹的样本量远低于模型需求。解决方法是先按txt统计每类框数量对数量最少的1到2类单独设置更高的增强倍数或者在数据集yaml里配合类别权重。实操上我还会在训练完成后单独看每一类的PR曲线AP为0的类别基本都指向数据问题而不是模型问题。5.5 评估随机性同一张图两次结果不一致现象是同一张图两次推理结果差别很大验证集的mAP每次跑都有波动。原因是推理阶段开了TTA测试时增强或者训练代码里的随机增强流程没有固定随机种子离线增强数据集在训练时又被二次增强输入分布每次都不同。解决方法是推理时只保留letterbox和归一化预处理关掉一切随机增强评估时显式设置augmentFalse并固定随机种子保证结果可复现。对于已增强数据集训练时的在线增强强度建议减半避免二次变形破坏离线增强已经做好的分布。6. 验证与进阶mAP之外焊接缺陷模型还要看什么6.1 混淆矩阵与漏检方向mAP能反映整体水平但对焊接缺陷漏检方向比总分数更有价值。YOLOv8训练完会在验证集上直接输出混淆矩阵我习惯先看每一行裂纹被预测成什么类、未熔合是不是被当成气孔。如果条状缺陷之间互相混淆说明增强时要加大形态差异的样本量如果某一类整体漏检说明这类样本数量或标注质量还有问题。混淆矩阵比单个mAP数字诚实得多它能告诉你要回去改数据还是改损失函数。6.2 多尺度训练与TTA取舍焊接缺陷尺度跨度大气孔和裂纹可能相差几十倍。训练时加多尺度能提升小目标表现yolo train modelyolov8n.pt \ datawelding_6cls.yaml \ imgsz640 scale0.5,1.5 \ epochs100 batch16scale参数让模型在每个batch随机缩放图像相当于在训练时额外引入尺度多样性。但尺度范围不要超过0.5到1.5否则小缺陷会缩放过度。验证阶段TTA能多涨一点mAP但不适合作为线上推理的默认配置我一般只在验收时开一次确认模型上限日常评估保持关闭。导出ONNX后也要核对预处理训练时的归一化方式如果和部署端不一致掉点会非常明显。6.3 边界模糊缺陷的评估IoU阈值与标签规范焊接缺陷的边界本来就模糊尤其是未熔合和裂纹标注员之间画框差异可以超过10个像素。评估时我会同时看mAP50和mAP50-95两个指标边界模糊严重的类别mAP50-95会偏低但mAP50正常这不一定代表模型差。反而说明需要统一标注规范——框紧贴缺陷可见边界还是包含过渡区域直接影响训练和评估口径。我现在的习惯是拿到类似数据集先花半天做格式校验和可视化再进训练省得后面翻车再回头改数据。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站