☰
焊接缺陷检测数据集:6类4137张YOLO+VOC格式详解与YOLOv8训练实践
2026/10/2 8:36:56 网站建设 项目流程

简介:面向目标检测与工业视觉场景的焊接缺陷数据集,适合算法工程师、研究人员和学生进行YOLO、Faster R-CNN等模型的训练、验证与迁移学习,可直接作为焊接质量检测、智能质检项目的实验数据基础。数据包含4137张增强后的清晰焊接图像,配套VOC格式XML与YOLO格式TXT标注,覆盖烧穿、污染、未完全融合、未完全渗透、错位、正常六类缺陷,共4214个矩形框,各类样本框数较为均衡,可直接用于监督训练和模型评估。压缩包约153.88MB,内含4137张图片及对应的VOC格式XML、YOLO格式TXT文件,以XML标注和TXT标签为核心;目录按JPEGImages、Annotations、labels三个文件夹组织,便于脚本读入、划分训练集与交叉验证。已有201人学习下载,适合需快速获得带标注焊接缺陷数据来搭建检测模型、测试数据增强效果或开展毕业设计的读者。

1. 焊接缺陷检测数据集为什么值得认真对待

焊接缺陷检测这个目标检测任务,最难的不是模型,而是数据。产线上常见的六类缺陷——气孔、夹渣、未熔合、未焊透、裂纹、咬边——不常出现,拍回一张带缺陷的焊缝图,还要一框一框人工标注。「目标检测-焊接缺陷检测数据集6类4137张YOLO+VOC格式(已增强).zip」正好补上了从标注到训练之间的缺口:4137 张已经增强过的图片,6 个类别,YOLO 和 VOC 两套标签,手工标注和格式转换都省了。做毕业设计、做产线预研、想在缺陷检测方向快速跑通完整流程的工程师都用得上。下面按我拿到这类数据集的习惯顺序讲:先拆目录和标签格式,再给能直接跑的 YOLOv8 流程,最后列增强数据最容易踩的四个坑。

2. 拆开数据集压缩包:目录结构、两套标签格式与六类缺陷定义

2.1 解压后先确认三样东西:目录层级、文件名对应、增强副本痕迹

我拿到这类 zip 后的第一个动作不是装框架,而是先看目录。规范的双格式数据集目录一般长这样:

welding_defect_6cls/ ├── images/ │ ├── train/ # 训练图片,jpg │ ├── val/ # 验证图片 │ └── test/ # 测试图片,非必需 ├── labels/ │ ├── train/ # YOLO 格式标签,与 images/train 同名 txt │ ├── val/ │ └── test/ ├── VOC/ │ ├── JPEGImages/ # VOC 用的原图 │ ├── Annotations/ # Pascal VOC XML 标注 │ └── ImageSets/Main/ # train.txt / val.txt 划分文件 ├── data.yaml └── README.md

images 和 labels 分 train/val/test 是 YOLO 系工具的标准布局,训练脚本默认按这个层级找数据。VOC 目录则是给人看、给传统标注工具用的:JPEGImages 存原图,Annotations 存 XML,ImageSets/Main 里是划分列表。这里有个现实情况:不少数据集是用 labelImg 这类标注工具先标成 VOC XML,再用脚本转出 YOLO 的 txt,所以 VOC 目录里能看到对象名称,labels 目录里只剩类别编号。如果解压后根本没有 VOC 目录,说明发布方只保留了 YOLO 格式,需要转换时按 3.3 的脚本补。

文件名对应关系是第二个要确认的点。YOLO 训练要求每张 jpg 有且仅有一个同名 txt:weld_00001.jpg 对 weld_00001.txt,多一个少一个都会让训练时的图像和标签错位。第三个要注意的是增强副本的痕迹。已增强数据常见的命名是 weld_00001_aug01.jpg、weld_00001_flip.jpg 这类带后缀的文件,它们和原图往往只差亮度、翻转或轻微裁切。这个后缀习惯在判断验证集是否泄漏时非常有用,后面 4.4 会专门讲。

2.2 YOLO 和 VOC 在同张图上差在哪:归一化坐标与绝对坐标

同一张图、同一个框,两套格式写出来是完全不同的样子。下面是同一个气孔标注的对比:

YOLO txt 一行: 0 0.4521 0.3382 0.0214 0.0187 VOC XML 片段: <object> <name>porosity</name> <bndbox> <xmin>289</xmin> <ymin>216</ymin> <xmax>303</xmax> <ymax>228</ymax> </bndbox> </object>

YOLO 标签里四个数是归一化后的中心点 x、中心点 y、宽度、高度,全部除以原图宽高,范围在 0 到 1 之间;VOC 的 bndbox 则是绝对像素坐标 xmin、ymin、xmax、ymax。我给新手一个判断办法:看到一行五个数字,第一个是 0 到 5 之间的整数,后面四个小数,那是 YOLO 格式;看到一堆带标签名的 XML 节点,那就是 VOC。

两种格式的取舍也值得注意。YOLO 的归一化坐标不依赖图像分辨率,训练时读起来快,但人眼没法直接看出框在哪;VOC 的绝对坐标直观看得懂,标注工具兼容性也好。如果你的目标是跑 YOLOv8 这类框架,只用 labels 目录就够了,VOC 目录的真正价值是在你想换用 mmdetection、Detectron 或做数据审查时,能对照 XML 回头查标签。这里有一个高频踩坑点:VOC 到 YOLO 的转换脚本如果直接拿字符串当类名,遇到中文名或者名称里带空格的对象就会报错,所以转换前必须先建立类名到编号的映射表,而且这个映射表必须和 data.yaml 里的 names 顺序一致,否则训练出来的类别编号全对不上。

2.3 六个缺陷类别的判定边界:气孔、夹渣、未熔合、未焊透、裂纹、咬边

标题写的 6 类,行业里最常见的划分就是这六种。它们的特征和容易混淆的类如下:

class id中文常见英文名典型特征容易混淆
0气孔porosity圆形或椭圆形暗斑,可单个也可密集分布夹渣
1夹渣slag_inclusion不规则块状,边缘毛糙气孔
2未熔合lack_of_fusion线状,沿坡口或焊道边缘分布未焊透
3未焊透incomplete_penetration长条状,出现在根部未熔合
4裂纹crack细长曲线,常有分支和尖端未熔合
5咬边undercut焊趾处凹陷,紧贴焊缝边缘未熔合

注意,这张表是我按常见缺陷定义列的参考口径,不同批次的数据集可能把 crack 放在 id 4 或 id 5,甚至改用别的类名。动手前一定要打开压缩包里的 data.yaml 看 names 顺序,训练脚本和部署代码里的类别顺序全部以它为准。我见过不止一次因为想当然写死类名,训练完才发现类别全乱的翻车现场。

容易混的不只是人和模型。气孔和夹渣在 X 光或灰度焊缝图上都是暗色块,形状差异是唯一的判别依据;未熔合和未焊透都是线状缺陷,区别在于未焊透一定在根部、未熔合更靠坡口。这个边界如果在标注阶段就没统一,模型后面怎么调参都学不会。我在拿到这类数据后,会先随机抽每个类 20 张图,把框和类别人工过一遍,确认标注口径一致再进训练流程。这一步花 20 分钟,但能避免整个训练周期白跑。

3. 把 YOLO+VOC 数据跑进 YOLOv8:校验、转换与训练命令

3.1 训练前必做的三类校验:数量、坐标、类别编号

数据解压后不能直接开训,先跑一个脚本把三件事确认掉:图片和标签数量是否对齐、坐标是否都在 0 到 1 之间、类别编号是不是连续的 0 到 5。这个脚本我在每个数据集上都会先跑一遍。

# train_val_ckpt.py —— 训练前的数据体检 from pathlib import Path import numpy as np for split in ["train", "val", "test"]: img_dir = Path(f"images/{split}") lab_dir = Path(f"labels/{split}") if not img_dir.exists() or not lab_dir.exists(): print(split, "目录不存在,跳过") continue imgs = {p.stem for p in img_dir.glob("*.jpg")} labs = {p.stem for p in lab_dir.glob("*.txt")} print(split, "图片数:", len(imgs), "标签数:", len(labs), "缺标签:", len(imgs - labs), "缺图:", len(labs - imgs)) bad, cls_ids = 0, set() for p in lab_dir.glob("*.txt"): arr = np.loadtxt(p, ndmin=2) if arr.size == 0: continue if arr.min() < 0 or arr[:, 1:].max() > 1: bad += 1 cls_ids.update(int(c) for c in arr[:, 0]) print(" 坐标越界的标签文件:", bad, "类别编号:", sorted(cls_ids))

这段脚本的要点:先用 p.stem 取不带后缀的文件名做集合差,能同时找出缺标签和缺图片的文件;np.loadtxt 用 ndmin=2 避免单目标文件被读成一维数组,这是最容易写崩的地方;类别编号单独收集,理想输出是 [0, 1, 2, 3, 4, 5],如果出现 6 或负数,说明要么标签转换时类名映射出错,要么混入了别的数据集样本。

如果你看到缺标签数量不是 0,先别自己补 txt。常见做法是把缺标签的图片挑出来重新标注,或者直接把这张图从训练集里删掉。自己手写一个空 txt 会让模型学到“这张图没有目标”,反而污染数据。坐标越界则几乎都是转换脚本的除法错位,回头检查是除以了宽高还是除以了 100。类别顺序里如果出现非连续编号,还要回到 data.yaml 确认发布方是不是用了 0、1、2、3、4、5 之外的编号体系。

3.2 用 YOLOv8 起一次训练:data.yaml、参数选择与断点恢复

先把数据集的 yaml 准备好。如果压缩包自带 data.yaml,我仍然会打开逐行核对一遍,因为它决定了训练和部署时类别怎么对应。

# WeldingDefect.yaml path: D:/datasets/welding_defect_6cls # 改成你解压后的实际路径 train: images/train val: images/val nc: 6 names: 0: porosity 1: slag_inclusion 2: lack_of_fusion 3: incomplete_penetration 4: crack 5: undercut

注意 path 用的是绝对路径。YOLOv8 的参数优先级是 命令行高于 data.yaml,所以如果你在命令行里又写了一个 data=,以命令行传入为准。names 的顺序一旦定下来,后续导出模型、写部署脚本都不能再改,这是整个流程里的锚点。

然后安装 ultralytics 并开始训练:

pip install ultralytics yolo detect train \ data=WeldingDefect.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ patience=20 \ cache=True

几个参数的取舍说一下。model=yolov8n.pt 我用的是 nano,因为缺陷检测场景输入图通常很大,但显存有限;如果你的显卡有 12G 以上显存,换成 yolov8s.pt 或 yolov8m.pt 效果会更好。imgsz=640 是默认值,但焊接缺陷里气孔、裂纹往往只有几十个像素,后面 4.2 会讲怎么针对小目标调大。batch=16 在 8G 显存上比较稳,爆显存就降到 8 并把 cache=True 去掉。patience=20 表示连续 20 个 epoch 验证指标不涨就提前停止,这个数据集增强过了,通常 60 到 80 个 epoch 就能收敛。

如果中途断了,用同一命令加 resume=True 从上次的权重接着训:

yolo detect train resume=True

它会自动读取 runs/detect/trainX/weights/last.pt。训练中如果看到 loss 直接变 NaN,多半是某张标签里出现零尺寸框或坐标越界,把 3.1 的脚本再跑一遍,重点查空 txt 和重复的坐标。yolo 训练里的 BN 崩溃也常常由这类脏标签引发,训练脚本不会帮你识别,必须由数据侧解决。

3.3 VOC 转 YOLO 的参考脚本,以及反向转换的适用场景

虽然这份数据集两个格式都给了,但你在实际项目中经常会拿到只有 VOC XML 的旧数据,转成 YOLO 格式才能喂给训练。我一般用一个脚本来兜底:

# voc2yolo.py —— 把 VOC XML 转 YOLO txt import xml.etree.ElementTree as ET from pathlib import Path xml_dir = Path("VOC/Annotations") out_dir = Path("labels/train") out_dir.mkdir(parents=True, exist_ok=True) class_map = { "porosity": 0, "slag_inclusion": 1, "lack_of_fusion": 2, "incomplete_penetration": 3, "crack": 4, "undercut": 5, } for xml in xml_dir.glob("*.xml"): root = ET.parse(xml).getroot() size = root.find("size") w = int(size.find("width").text) h = int(size.find("height").text) lines = [] for obj in root.findall("object"): name = obj.find("name").text.strip() if name not in class_map: print(f"警告: {xml.name} 里有未知类 {name}") continue 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) cx = (xmin + xmax) / 2 / w cy = (ymin + ymax) / 2 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{class_map[name]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") (out_dir / (xml.stem + ".txt")).write_text("\n".join(lines))

逻辑不复杂:解析 XML 里的 size 拿到宽高,遍历每个 object,把 bndbox 的绝对坐标换算成归一化中心点加宽高。两个容易出问题的地方:xmin、ymin 被一些标注工具写成整数字符串,float() 能处理,但如果有的文件把 xmax 写成小于 xmin,转换出来的宽度就是负数,这类文件要单独挑出来修复;class_map 的键必须是 XML 里实际的 name,如果原始数据用的是中文类名,先统一成英文再映射,否则每个中文类名都会触发警告并丢标签。

反向转换,也就是 YOLO txt 转 VOC XML 或 COCO JSON,我在什么时候用呢?常见场景是想用 mmdetection 训练,或者要把结果交给检测报告系统做可视化审查。做法正好是上面的逆运算:读出归一化的 cx、cy、bw、bh,乘回宽高得到像素框,再把类别编号映射回类名。注意 YOLO 的 w、h 是全宽全高,不是半宽半高,乘回像素时不要漏掉 2 倍系数,这是我见过最多的反向转换 bug。如果这份数据集本身的命名里带 aug 后缀,转换时最好保留原名,方便以后跟踪哪些是增强副本。

4. 焊接缺陷训练的四个坑:增强失真、小目标漏检与标签边界

4.1 现象:增强后的图片和标签错位,loss 降到一半不降了

跑训练时 loss 一路正常下降,到某个点突然卡住,val 的 mAP 只有 0.0x。停下一看训练样本,发现增强过的图里目标框位置和缺陷实际位置差了十几到几十像素。原因是增强工具只处理了图像,没有同步处理标签:比如脚本对整张图做了裁剪和旋转,却直接把原坐标写进新的 txt,坐标对不上新图。尤其常见于只改了图像增强、没改标签的“半自动”流程。

解决分两步。第一步跑 3.1 的脚本定位数量问题,第二步随机抽 30 张图把框画出来人工看。我常用的可视化检查脚本是这样:

# show_box.py —— 把 YOLO 标签画回原图上抽查 import cv2 from pathlib import Path img_path = "images/train/weld_00001.jpg" lab_path = "labels/train/weld_00001.txt" img = cv2.imread(str(img_path)) h, w = img.shape[:2] colors = [(0, 0, 255), (0, 255, 0), (255, 0, 0), (0, 255, 255), (255, 0, 255), (255, 255, 0)] for line in Path(lab_path).read_text().strip().splitlines(): cls, cx, cy, bw, bh = map(float, line.split()) x1 = int((cx - bw / 2) * w) y1 = int((cy - bh / 2) * h) x2 = int((cx + bw / 2) * w) y2 = int((cy + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), colors[int(cls)], 2) cv2.imwrite("check.jpg", img)

把框画出来后,错位的图一眼就能看出来。修复方法是让增强和标签走同一个变换:要么用 albumentations 这类同时支持 bounding box 参数的工具,要么把坐标换算统一放进同一个类里,不要在脚本外面手动补标签。如果你看到 loss 在某个 epoch 突然变成 NaN,多半也是这批脏标签把 BN 层搞崩了,不要急着调学习率,先回头查数据。

4.2 现象:气孔、裂纹这类小目标召回率上不去

模型训完,precision 不低,recall 只有 0.3 左右。看混淆矩阵,气孔和裂纹大量的真实框落在了漏检里。原因很直接:这两类缺陷在原始图片里往往只占几十乘几十像素,imgsz=640 下会被压缩到十几个像素,特征几乎消失。而这个数据集做了增强,增强里的缩放、随机裁剪会把小缺陷进一步缩小,等于模型见到的全是“缩水版”目标。

解决思路有三个方向。第一,把输入尺寸提到 960 或 1280,batch 相应减半,让网络在更高分辨率下去看这些小目标,代价是训练和推理都变慢。第二,关闭或者调低增强里的缩放幅度,ultralytics 的训练参数里 scale 默认 0.5,我会把它降到 0.1 甚至 0,避免把小缺陷缩没。第三,对大图做切图训练,把 1280 的原图切成 640 的 patch,patch 之间留 overlap,只保留包含缺陷的 patch,这样小目标在输入里的占比就正常了。这个思路和遥感图像目标检测里对大影像切块的做法是一样的,焊接缺陷图完全可以照搬。

注意调整 imgsz 之后,验证集也要用相同尺寸,否则训练和验证不在同一分布上,指标没有可比性。部署时也要用同样的输入尺寸,才能对齐模型学到的东西。另外,如果你在放大输入后显存不够,就配合切图方案使用,而不是硬撑一个巨大的 imgsz。

4.3 现象:六类 mAP 两极分化,个别类几乎检不到

训练完成后发现未焊透、咬边的 AP 能到 0.8,夹渣和裂纹只有 0.1。这类现象在六类数据集里很常见。原因是类间样本不均衡——增强可以增加总张数,但无法凭空把裂纹这种稀有缺陷变多;另一个原因是标注一致性差,裂纹有人按整条焊道标,有人只标明显的一段,同一个目标在不同批次里标签长度都不一样,模型学到的边界就是乱的。

解决办法分数据层面和标签层面。数据层面,先统计每个类别的框数量,对少数类做 oversample,常见做法是把含裂纹的 txt 在训练集里复制几份,让模型每个 epoch 多看到几次。ultralytics 的命令行没有直接暴露类别权重参数,与其改损失函数,不如在数据层解决。标签层面,把裂纹、夹渣这类容易混的类单独抽 50 张图,找两个懂焊接的人分别标一遍,算标注交并比,如果两个人标出来的框差异很大,就要先定统一的标注规范,再重新洗一遍标签。标签边界不统一是这一类问题的根因,改模型结构没有用。

注意:如果某一个类的 AP 长期低于 0.3,先不要往模型上加注意力机制,回到这一类样本里逐张看,大概率是标注把两个视觉上接近的类混在了一起。

4.4 现象:验证集混入增强副本,指标虚高

训练集 mAP 高到 0.95,拿生产的实拍图一测掉到 0.6。先别怀疑模型过拟合,更大的可能是验证集泄漏。增强数据的命名如果带 aug 后缀,而切分 train/val 时只是按文件名随机分,同一个源图的不同增强版本就可能同时出现在两边。模型在训练时见过近乎一样的图,验证指标自然虚高。

解决方法是切分前先按源图去重。做法是把文件名里的 _aug、_flip、_bright 这类后缀去掉,按源文件名分组,再把整个组只放进 train 或 val,保证每个源图及其所有副本只出现在一边。更稳妥的做法是用哈希对原始图片做一次聚类,把感知哈希相近的图片归为同一组,然后再切分。如果你不确定这个数据集发布时有没有做过这类去重,最简单的验证方法是在验证集里随机挑 30 张图,去训练集里找肉眼相似的副本,找到了就按 6.1 的哈希脚本重新分组切分。

我现在的习惯是只看 val 指标,并且在项目验收时额外准备 200 张从未进入过数据集的现场图。数据集内部指标再高,都不如一批陌生样本实测有说服力。增强副本泄漏会让训练和验证一起好看,只有真正的现场数据才能暴露模型的真实水平。

5. 从数据集到能上线的模型:指标怎么读、模型怎么导出、后续怎么扩

5.1 用混淆矩阵和类别 AP 判断这个数据集合不合格

训练结束后,runs/detect/trainX/ 目录里会生成 confusion_matrix.png、results.csv、labels.jpg 这些文件。判断这个数据集到底能不能用,我先看两个东西:每类的 AP 和混淆矩阵。

先打开 results.csv,找到 val 的 mAP50 和 mAP50-95,再按类别看 AP。如果六个类里有类的 AP 低于 0.5,我不会急着继续调参,而是回到数据本身。再看 confusion_matrix.png:理想的对角线最亮,最怕的是气孔和夹渣在矩阵上有明显的互相错分。出现这种错分,说明这两个类在标注上就没分开,或者它们在这批图像里视觉上确实接近。如果是前者就洗标签,如果是后者就要在项目层面接受合并——把气孔和夹渣合并成一个“孔洞类缺陷”,整体精度往往反而更高。

判断数据集合格还有一个更朴素的依据:训练集和验证集的 AP 差距。差距在 0.1 以内算健康,超过 0.2 基本可以确定是验证泄漏或者增强过度,对应 4.4 的去重流程先做一遍。我习惯在训练前就把每个类别的样本框数记下来,对照训练后的每类 AP,哪个类样本最少但 AP 最高,哪个类样本多却 AP 低,这两个数字能直接告诉你要补什么数据。这里有一个容易被忽略的点:数据集是“已增强”的,增强副本从训练视角看是独立样本,但评估模型对新场景的泛化能力时,它们仍然来自同一批源图,不能当作真实新数据来评价。

5.2 导出 ONNX 并检查输出层:类别顺序最容易在部署时翻车

训练完并确认指标后,下一步是把权重导出成 ONNX,方便用 onnxruntime 或者推理框架部署。

yolo export model=runs/detect/trainX/weights/best.pt format=onnx imgsz=640 opset=12

导出后我习惯先用 onnxruntime 检查输出张量的形状,确认类别数对不对。

import onnxruntime as ort import numpy as np sess = ort.InferenceSession("best.onnx") data = np.zeros((1, 3, 640, 640), dtype=np.float32) out = sess.run(None, {sess.get_inputs()[0].name: data})[0] print(out.shape) # 期望 (1, 10, 8400),10 = 4 个坐标 + 6 个类别

输出形状里第二维是 4 加类别数:焊接缺陷是 6 类,所以是 10;如果你拿到的是 (1, 84, 8400),说明部署代码按 COCO 的 80 类来解析了。为什么这么强调?因为 ONNX 文件里没有类名信息,只有编号,推理代码必须把输出编号映射回 data.yaml 里的 names。如果训练和部署用了两个不同顺序的 yaml,模型输出和显示名称就会错位:框是对的,标签却是另一个缺陷的名字。我见过一个真实例子,现场用气孔当裂纹显示,排查了两天才发现是部署端把类别顺序写死成了 COCO 风格。

5.3 后续迭代:哪些增强要关掉,哪些缺陷样本要重点补

用 4137 张增强数据训练的模型只能算第一版,上线前我建议再走一轮迭代。增强参数上,这个任务我会关掉 mosaic 和 mixup。缺陷目标通常较小,mosaic 会把多个小目标拼在一张图里,互相挤压,模型学到的是碎片的统计规律而不是缺陷本身的特征。hsv 增强保留但幅度控制在 0.2 左右,翻转保持 0.5。已增强数据本身已经做过一轮,训练时的在线增强要适度,否则等于把同一批缺陷反复变形,模型容易过拟合到这些变体上。

新数据的采集方向,优先补裂纹和夹渣。这两类在产线上出现频率低,恰恰是模型最容易漏检的类别。具体做法是把现场拍到的疑似缺陷图先自动标注一轮,人工修正后并入训练集。另外要留意数据来源的一致性:如果训练集是可见光焊缝图,而部署场景是探伤 X 光图,图像风格差异会抵消增强带来的收益,这类跨域迁移问题不是靠继续增强能解决的,需要单独采集对应成像方式的样本。我的原则是每补一批数据就重跑一次 3.1 的校验脚本,并对比同一验证集上的每类 AP,确保新数据确实带来提升而不是引入新噪声。

6. 用增强副本反推数据集标注质量:一个哈希聚类的自定义检查

最后分享一个我每次拿到“已增强”数据集都会做的检查:用感知哈希把互为副本的图片聚成一组,再对比它们之间的标签,反推这条数据标注得到底干不干净。增强副本的命名再规范,也可能出现翻转后的图片配着没翻转的框的情况,普通可视化抽查容易漏,哈希聚类能把整个训练集扫一遍。

思路很简单:给每张图算一个 64 位的 dhash,内容高度相似的图片得到相同或相邻的哈希值,然后按哈希值分组,同组里超过一张图的就列为增强副本组。代码不需要额外装大包,opencv 就够。

# group_dup.py —— 按感知哈希给增强副本分组 import cv2 import numpy as np from pathlib import Path def dhash(img_path, size=8): img = cv2.imread(str(img_path), cv2.IMREAD_GRAYSCALE) img = cv2.resize(img, (size + 1, size)) bits = (img[:, 1:] > img[:, :-1]).flatten() return sum(int(b) << i for i, b in enumerate(bits)) files = list(Path("images/train").glob("*.jpg")) groups = {} for f in files: groups.setdefault(dhash(f), []).append(f) for h, same in groups.items(): if len(same) > 1: print(f"{len(same)} 张相似: {[p.name for p in same[:5]]}")

拿到分组后,做两件事。第一,取一组里的两张图,分别读出它们的 txt,比对类别编号和框数量。如果图片明显做了水平翻转,而标签里的 cx 没有跟着翻,那就是增强管线没有同步标签,整组数据都要标记为不可信,回到 4.1 的修复流程。第二,检查这个组里有没有图片同时出现在 train 和 val 目录里,如果有,就是 4.4 说的验证集泄漏,直接重新切分,别指望靠随机种子规避。

我现在的习惯是:不管数据集是谁整理的、声称多干净,先花半小时跑这遍哈希和标签比对,再开始训练。因为这类问题的排查成本比训练本身高得多,训练一个模型只要几小时,但带着错标签训完再回头查数据,浪费的是整个迭代周期。这个习惯救过我不少次,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询