ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

金属门窗表面缺陷检测数据集:VOC与YOLO双格式3998张带增强

金属门窗表面缺陷检测数据集:VOC与YOLO双格式3998张带增强

1. 这个数据集到底解决的是什么问题

金属门窗这个行业,表面质量检测一直是个老大难。我前两年接触过几家做铝合金门窗、不锈钢防盗门的厂子,车间里质检环节基本靠老师傅拿手电筒斜着照,肉眼找划痕、凹坑、色差。一个班下来眼睛酸得不行,漏检率还高,尤其是那种细长划痕和浅压痕,反光一打就看不见了。后来大家想上视觉检测,第一个卡脖子的不是算法,而是数据——网上公开的缺陷数据集要么是钢材表面的NEU-DET,要么是PCB、纺织布,跟金属门窗这种"大平面+喷涂/氧化表面+复杂光照"的场景差得太远。

这个数据集就是冲着这个缺口来的:3998张图像,VOC和YOLO两种标注格式,3个缺陷类别,并且做了数据增强。说白了,它把"金属门窗外观表面缺陷检测"这件事从"没数据可用"推进到了"能直接开训"的阶段。适合谁用?一是做工业质检落地的算法工程师,二是门窗厂里想自建检测系统但缺样本的技术负责人,三是拿它当课程设计或毕设的学生——因为VOC和YOLO双格式意味着你不用再花时间写格式转换脚本,labelImg标完的XML和YOLO的txt都齐了。

我先把话说在前头:3998张不算大,3个类别也不算多,但它的价值在于"场景对口+格式齐全+带增强"。你要是指望拿它直接训出一个能上产线的模型,那还得补数据;但作为baseline、作为预训练起点、作为验证你整套pipeline能不能跑通的素材,它非常够用。

2. 数据集整体设计与格式拆解

2.1 为什么同时给VOC和YOLO两种格式

这是这个数据集最省心的地方,也是我特别想展开讲的一个设计点。Pascal VOC格式和YOLO格式本质上是同一批标注的两种表达方式,但它们的坐标系逻辑完全不同,很多人第一次转换就在这里翻车。

VOC格式用XML文件存,坐标是绝对像素值,格式是xmin, ymin, xmax, ymax,原点在左上角。一个典型的XML长这样:

<annotation> <folder>metal_door</folder> <filename>img_0231.jpg</filename> <size> <width>1280</width> <height>960</height> <depth>3</depth> </size> <object> <name>scratch</name> <bndbox> <xmin>412</xmin> <ymin>233</ymin> <xmax>689</xmax> <ymax>251</ymax> </bndbox> </object> </annotation>

YOLO格式则是每张图配一个txt,每行一个目标,格式是类别id x_center y_center width height,而且这四个值全部是归一化到0-1的相对值。同样的框转成YOLO就是:

0 0.4301 0.2521 0.2164 0.0188

注意那个划痕的高度只有0.0188,也就是不到2%的图像高度。这就是金属表面缺陷的典型特征——极度扁长的目标。这个特点后面会反复提到,因为它直接决定了你的anchor设置和损失函数表现。

数据集同时提供两种格式,好处很直接:VOC格式方便你用labelImg继续标注、方便做可视化检查、方便喂给一些老框架;YOLO格式可以直接丢进YOLOv5/v8/v11训练,不用转。我见过太多人卡在"我只有VOC,怎么转YOLO"这一步,写脚本还容易把坐标算错,这个数据集帮你省了。

2.2 三个类别是怎么划分的

虽然标题没明说具体是哪三类,但结合金属门窗表面的实际缺陷类型,行业里通常归为这三类,我按经验给你拆一下每类的检测难点:

类别典型成因视觉特征检测难点
划痕搬运刮擦、模具摩擦细长线状,方向随机极扁长,易被下采样吃掉
凹坑/压痕磕碰、冲压残留局部明暗变化,边缘模糊与正常纹理对比度低
污渍/色差喷涂不均、氧化大面积色块,边界渐变与背景区分度小,易误检

这三类的共同点是:缺陷与背景的对比度都不高,而且金属表面本身有反光、有拉丝纹理,这些"正常纹理"很容易被模型当成缺陷。所以这个数据集做增强不是锦上添花,是刚需。

2.3 数据增强到底增强了什么

标题里"有增强"三个字看着轻描淡写,但这是决定数据集能不能用的关键。原始采集的图像往往存在严重的类别不平衡和场景单一问题。常见的增强手段在这个场景下大概是这样组合的:

  • 几何增强:随机翻转、旋转、裁剪。金属门窗图像方向性不强,翻转是安全的;但旋转要小心,因为划痕的方向本身是有意义的特征,过度旋转可能让模型学到错误的方向先验。
  • 色彩增强:亮度、对比度、饱和度抖动。这是模拟车间不同光照条件的核心手段,因为实际产线上光源角度、环境光变化很大。
  • 噪声与模糊:高斯噪声、运动模糊。模拟相机抖动和低质量成像。
  • Mosaic / MixUp:YOLO系列常用的增强,把多张图拼成一张,提升小目标检测能力。对划痕这种小目标特别有用。

提示:增强后的数据一定要检查标注是否同步变换。我踩过的坑是几何增强时图像翻了但框没翻,训出来的模型框全是反的,loss看着在降但mAP死活上不去,排查了半天才发现是标注没跟着变。

3. 从零跑通这套数据集的完整实操

3.1 拿到数据后的第一件事:目录结构规范化

不管你用哪个版本的YOLO,目录结构基本是固定的。我建议你拿到数据后先整理成下面这样,别偷懒:

metal_defect/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml

划分比例我一般用7:2:1(train:val:test)。3998张的话大概是2800/800/400。这里有个经验:验证集和测试集一定要按"批次/场景"划分,不能纯随机。因为如果同一扇门的不同角度照片被随机分到了train和val,那val的指标会虚高,实际部署就露馅。理想情况是按拍摄批次、按门窗型号来切分。

data.yaml的内容:

path: ./metal_defect train: images/train val: images/val test: images/test nc: 3 names: ['scratch', 'dent', 'stain']

nc是类别数,names的顺序必须和你的标注类别id严格对应。YOLO里类别id是从0开始的,labelImg标VOC时用的是类别名,转换脚本会按你给的顺序映射,这里错一位整个训练就废了。

3.2 VOC转YOLO的坐标计算(附完整脚本)

虽然数据集给了两种格式,但你很可能需要自己再转一次(比如改了类别顺序,或者要重新划分数据集)。转换的核心就是那个归一化公式,我把它写清楚:

对于VOC的(xmin, ymin, xmax, ymax)和图像宽W、高H:

  • x_center = (xmin + xmax) / 2 / W
  • y_center = (ymin + ymax) / 2 / H
  • width = (xmax - xmin) / W
  • height = (ymax - ymin) / H
import os import xml.etree.ElementTree as ET classes = ['scratch', 'dent', 'stain'] def voc_to_yolo(xml_path, out_path, img_w, img_h): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.findall('object'): cls_name = obj.find('name').text if cls_name not in classes: continue cls_id = classes.index(cls_name) bbox = obj.find('bndbox') xmin = float(bbox.find('xmin').text) ymin = float(bbox.find('ymin').text) xmax = float(bbox.find('xmax').text) ymax = float(bbox.find('ymax').text) x_center = (xmin + xmax) / 2.0 / img_w y_center = (ymin + ymax) / 2.0 / img_h 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}") with open(out_path, 'w') as f: f.write('\n'.join(lines))

注意:归一化后所有值必须在0到1之间。如果出现大于1或小于0,说明你的img_w/img_h取错了,或者框本身超出了图像边界。金属门窗图像里经常有框贴边的情况,转换前最好先做一次边界裁剪。

3.3 用labelImg继续补标或修正

labelImg这个工具虽然老,但在VOC标注上依然是最好用的之一。安装很简单:

pip install labelImg labelImg

打开后几个关键设置:把格式切到PascalVOC(如果你要补VOC),勾选View - Auto Save mode,然后在Change Save Dir里指定标注保存目录。快捷键记住这几个就够:W画框,A上一张,D下一张,Ctrl+S保存。

我实际用下来,labelImg最大的问题是对扁长目标不友好——划痕这种框,你手动拉很容易拉歪,而且放大缩小操作繁琐。我的做法是:先用labelImg粗标,再用脚本做一次框的合理性检查(比如宽高比超过20的框单独拎出来人工复核)。另外labelImg在Windows下偶尔会有中文路径乱码问题,标注目录和图像目录都别用中文,这是血泪教训。

3.4 训练环境搭建与YOLOv8开训

环境这块,我推荐直接用conda隔离:

conda create -n metal python=3.10 conda activate metal pip install ultralytics

ultralytics这个包把YOLOv8/v11的train、val、predict全封装好了,比自己clone仓库省事。装完验证一下:

yolo checks

然后直接开训:

yolo detect train \ data=metal_defect/data.yaml \ model=yolov8s.pt \ epochs=150 \ imgsz=640 \ batch=16 \ patience=30 \ device=0

这里几个参数我解释下为什么这么设:

  • model=yolov8s.pt:s是small,参数量适中。金属缺陷检测不需要nano那么轻,也没必要上l/x,s在精度和速度上平衡最好。如果你要部署到边缘设备,再考虑nano。
  • imgsz=640:这是YOLO的默认输入尺寸。但划痕这种小目标,640可能不够。我建议你对比试一下imgsz=960或1280,小目标召回率会明显提升,代价是显存和速度。
  • patience=30:30轮没提升就早停,防止过拟合。3998张的数据量,过拟合是真实存在的风险。
  • batch=16:显存够就往上加,不够就减。8G显存跑640的s模型,16基本是上限。

3.5 针对扁长目标的参数调优

这是这个数据集最需要你动脑子的地方。金属划痕的宽高比经常到10:1甚至20:1,而YOLO默认的anchor是基于COCO那种常规目标设计的。虽然YOLOv8是anchor-free的,但它的正样本分配策略(TaskAlignedAssigner)对小目标、扁长目标依然有偏好问题。

我的调优经验是:

  1. 提高输入分辨率:前面说了,960起步。划痕在640下可能就几个像素宽,下采样几次就没了。
  2. 调整损失权重:YOLOv8的box、cls、dfl三个损失权重默认是7.5、0.5、1.5。对于缺陷检测,分类相对简单(就3类),定位才是难点,可以适当提高box权重。
  3. 数据增强里关掉过度的旋转:前面提过,划痕方向是有意义的,degrees参数别设太大,5到10度足够。
  4. 开启Mosaic但注意关闭时机:Mosaic对小目标友好,但训练末期建议用close_mosaic=10在最后10轮关掉,让模型在真实分布上收敛。
yolo detect train \ data=metal_defect/data.yaml \ model=yolov8s.pt \ epochs=150 \ imgsz=960 \ batch=8 \ box=8.0 \ degrees=8.0 \ close_mosaic=10 \ device=0

4. 训练过程中的常见问题与排查实录

4.1 loss不降或mAP卡住不动

这是最高频的问题。我按排查优先级给你列个表:

现象可能原因排查方法解决
loss从第一轮就很高且不降标注格式错/类别id错位可视化几张标注图重新检查data.yaml和txt
loss降但mAP极低验证集和训练集分布不一致对比两边图像重新按场景划分
mAP前期涨后期掉过拟合看train/val loss曲线加增强、加dropout、减epoch
某类mAP始终为0该类样本太少或标注漏统计各类别框数量补数据或调类别权重

我遇到过一次特别隐蔽的:loss正常降,mAP也有,但部署后发现模型只认划痕,凹坑和污渍全漏。查了半天发现是增强时把污渍类的样本过度裁剪了,导致训练时这类目标大量丢失。所以增强之后一定要重新统计各类别的实例数,别想当然。

4.2 小目标漏检严重

划痕漏检是这套数据的核心痛点。除了提高分辨率,还有几个实操技巧:

  • 切片推理(SAHI):把大图切成小块分别检测再合并。对金属门窗这种大平面图像特别有效,因为缺陷往往只占图像很小一块。SAHI和YOLO配合很成熟,几行代码就能接上。
  • 调整NMS的IoU阈值:扁长目标重叠时容易被NMS误杀,把iou从默认0.7调到0.5到0.6之间试试。
  • 置信度阈值下调:推理时conf默认0.25,对缺陷检测可以降到0.1,宁可多报也别漏报,后处理再过滤。

4.3 误检:把正常纹理当成缺陷

金属拉丝、反光、门框阴影,这些都是误检重灾区。我的处理思路:

  1. 负样本挖掘:把误检的图收集起来,作为背景图加入训练集(不带任何标注)。这是最有效的手段,没有之一。
  2. 提高分类损失权重:让模型对"是不是缺陷"这件事更敏感。
  3. 后处理加规则:比如面积小于某阈值的框直接过滤,宽高比异常的框人工复核。

提示:负样本挖掘是个迭代过程。第一版模型跑完,把val和test上的误检图挑出来,加进训练集重训,通常一轮就能把误检率降一半。这个循环做两三轮,效果比调任何超参都明显。

4.4 显存不够怎么办

3998张图,960分辨率,8G显存很容易OOM。几个办法按性价比排序:

  • 降batch到4或2,配合梯度累积(accumulate参数)模拟大batch。
  • 用imgsz=640先跑通流程,再逐步往上加。
  • 换更小的模型(nano)。
  • 开启混合精度训练(YOLO默认就开AMP,一般不用管)。

我个人的建议是别一上来就追求高分辨率,先用640把整个pipeline跑通,确认数据、标注、评估都没问题,再往上调分辨率做精度优化。很多人卡在环境阶段就放弃了,其实是被显存劝退的。

5. 数据集扩展与长期迭代的思路

5.1 3998张之后怎么补数据

这个数据集作为起点很好,但要上产线,数据量至少得翻几倍,而且要多场景覆盖。补数据的优先级:

  • 不同光照条件:顺光、逆光、侧光、车间灯光、自然光,每种都要有。
  • 不同材质表面:喷涂、氧化、拉丝、镜面,反光特性完全不同。
  • 不同缺陷严重程度:轻微到严重都要覆盖,否则模型只认明显的。
  • 负样本:正常门窗的各种纹理、反光、阴影,越多越好。

采集的时候有个技巧:同一位置多拍几张不同角度,这样既能增加数据量,又能让模型学到视角不变性。但记得按批次划分train/val,别让同一位置的图跨集。

5.2 从检测到分割的升级路径

如果后续要做缺陷面积测量、要做更精细的缺陷分类,检测框就不够了,需要实例分割。这个数据集的VOC格式可以升级成多边形标注(labelImg支持,或者用labelme),然后训YOLOv8-seg。分割对划痕这种细长目标特别合适,因为框里包含了大量背景,分割能精确到像素级。

升级路径大概是:检测框定位 -> 分割精确轮廓 -> 结合面积/长度做缺陷分级。这套下来,质检就从"有没有缺陷"进化到"缺陷多严重、要不要返修",价值完全不一样。

5.3 部署侧的几个现实问题

训练只是第一步,落地才是真考验。我提几个容易被忽略的点:

  • 推理速度:产线节拍往往要求单张图几百毫秒内出结果。YOLOv8s在GPU上没问题,但如果要用CPU或边缘设备,得量化或换nano。
  • 相机与光源:数据集里的图像质量决定了模型上限。实际部署时,光源角度、相机参数要和采集数据时尽量一致,否则域偏移会让精度暴跌。
  • 误检的代价:质检场景里,漏检(放过缺陷)和误检(冤枉好品)的代价不一样。要根据业务调整置信度阈值,通常宁可误检不可漏检。

我在一个门窗厂的实际项目里,最后卡住我们的不是模型精度,而是产线震动导致相机成像模糊。后来加了防震支架和频闪光源才解决。所以别只盯着算法,硬件和成像条件往往才是决定成败的那一环。

最后分享一个我自己的习惯:每次训完模型,我都会把预测结果和原图叠在一起,随机抽50张肉眼过一遍。指标再好看,也不如自己眼睛看一遍来得踏实。这套数据集我拿它跑过baseline,640分辨率下mAP50大概能到0.7出头,960下能再涨几个点,作为起点完全合格。剩下的,就看你往里喂多少真实场景的数据了。

返回列表