ARTICLE DETAIL

资讯详情

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

目标检测实战:COCO/YOLO/VOC格式转换与瓷砖缺陷检测训练

目标检测实战:COCO/YOLO/VOC格式转换与瓷砖缺陷检测训练 简介面向瓷砖制造、建筑检测与计算机视觉开发者的瓷砖缺陷检测数据集内含边缘崩裂、破洞、裂缝等常见缺陷的原始图片及其COCO JSON格式标注可直接用于YOLO等目标检测模型的训练与评估也方便转换为Pascal VOC等格式。压缩包共2000个文件其中1997张JPG原图与3个JSON标注文件整体大小约579MB图片均来自真实瓷砖表面缺陷类型覆盖较全适合工业质检场景下的算法验证。已有646人学习浏览。数据集既可用于搭建自动化瓷砖质量检测流程也可作为库存到货分拣、安装前质量评估或建筑检测服务的训练基础对于研究数据增强、小目标检测或迁移学习的开发者也是一份标注规范的实战素材。另外标注文件采用标准COCO字典结构包含图像信息、类别映射与目标边界框可直接接入mmdetection、ultralytics等主流框架省去格式转换时间。1. 瓷砖缺陷检测数据集7992 张原图能做什么瓷砖质检在产线上历来是重体力活边缘崩裂、破洞、裂缝这三类缺陷在釉面背景下对比度极低人工盯着传送带看久了必然漏检。这个瓷砖缺陷检测数据集一共 7992 张原始图采用 COCO JSON 格式标注同时附带 YOLO txt 和 Pascal VOC XML 两种转换版本三类缺陷全部有框级标注而不是只给图级标签。对做质量控制的工程师这 7992 张图足够微调一个能在产线上筛砖的检测模型对刚入门目标检测的开发者一套数据同时拿到三种格式省掉自己写转换脚本的不确定环节可以专注把训练流程跑通。2. 标注格式解剖COCO JSON、YOLO、VOC XML 三种格式的对齐逻辑拿到数据集先别急着训练把三种格式吃透后面转换脚本才不会写出半吊子代码。这个数据集的标注文件名都带.rf.前缀是 Roboflow 导出的典型特征三种格式对应同一个标注事实只是坐标系统、存储粒度不一样。2.1 三种格式的存储结构与坐标体系COCO JSON 是集中式存储一个 JSON 文件里塞了 images、annotations、categories 三张表坐标是绝对像素的[x, y, width, height]左上角为原点。YOLO 是分散式存储每张图对应一个 txt 文件每行一个目标格式是class_id cx cy w h全部归一化到 [0,1]。Pascal VOC XML 同样是一图一文件但坐标是绝对像素的xmin ymin xmax ymax形式类别 id 从 1 开始编号。三种格式的坐标换算本质就是绝对像素与归一化值的互相换算以及 bbox 中心表示与对角点表示的互相换算。下面这个对应关系表能帮你快速定位转换时改哪个值格式存储粒度坐标形式类别 id 起始COCO JSON单文件三表x, y, w, h 绝对像素1YOLO txt一图一文件cx, cy, w, h 归一化0VOC XML一图一文件xmin, ymin, xmax, ymax 绝对像素1这个表里最坑的是类别 id 起始差异COCO 和 VOC 从 1 开始YOLO 从 0 开始。转换脚本里如果忘记减一训练出来的模型类别会整体错位逻辑上不会报错但结果全错属于典型的黑匣子式翻车。我一般会写一段校验脚本转换后抽查几张图把 YOLO 坐标反算回像素坐标画框人眼确认框和缺陷对得上再进训练。你自己复核标注时也可以把 COCO JSON 丢进 CVAT 或 labelimg 里过一遍虽然慢但能发现脚本发现不了的对齐问题。提示Roboflow 导出时如果勾选了 resize三种格式的坐标都会跟着变所以拿到包先自己确认一遍别轻信标注文件里的假设。2.2 从 COCO JSON 读取标注并统计类别COCO JSON 的读取逻辑和一般 JSON 不太一样因为它的三张表需要通过 id 互相 join。看一下具体代码import json from collections import defaultdict, Counter with open(instances_train.json, r, encodingutf-8) as f: coco json.load(f) img_id_to_name {img[id]: img[file_name] for img in coco[images]} cat_id_to_name {cat[id]: cat[name] for cat in coco[categories]} anns_by_img defaultdict(list) for ann in coco[annotations]: anns_by_img[ann[image_id]].append(ann) cat_counter Counter() for ann in coco[annotations]: cat_counter[cat_id_to_name[ann[category_id]]] 1 print(f图像数: {len(coco[images])}) print(f标注框数: {len(coco[annotations])}) for name, cnt in cat_counter.most_common(): print(f{name}: {cnt})这段脚本的核心是先建立img_id到文件名的映射和category_id到类名的映射再做一次内连接。COCO 的 annotations 里只有image_id和category_id这两个外键没有直接的文件名和类名字符串所以这两步映射是必须的。运行后如果发现某一类标注框数量明显少比如裂缝只有几百个框后面训练就要针对它做补偿这个我在第 4 章展开。2.3 图像分辨率与标注基准一致性检查7992 张图像如果来自不同产线采集批次分辨率极可能不一致。标注时 Roboflow 导出一般会统一 resize 到某个基准尺寸比如 640x640 或 800x800但我每次都会自己验证一遍不轻信导出设置。检查方式如下from PIL import Image import os from collections import Counter img_dir images/ sizes Counter() for fname in os.listdir(img_dir): fpath os.path.join(img_dir, fname) if os.path.isfile(fpath) and fname.lower().endswith((.jpg, .png)): w, h Image.open(fpath).size sizes[(w, h)] 1 for (w, h), cnt in sizes.most_common(8): print(f{w}x{h}: {cnt}张)打印出来如果发现有两三种分辨率混着那 COCO 标注里的绝对像素坐标就必须按各自图像的实际宽高做归一化不能想当然套同一个缩放因子。我在实际项目里遇到过标注基准是 800x800、实际图是 416x416 的情况直接训练 YOLO 后所有框都往左上角缩——因为 YOLO 的 dataloader 内部强制 resize 到 imgsz如果标注是按原图绝对像素存的resize 后坐标全错位。这个坑在避坑章节里再细说。3. 从标注到训练COCO 转 YOLO 的转换脚本与 YOLOv8 实操数据集的三种格式里YOLO 格式对训练最友好因为 ultralytics 生态直接吃 txt。如果你的下载包里只有 COCO JSON 版转换其实很简单按下面的步骤走。3.1 目录组织train/val 划分与坐标写盘先把数据组织成 YOLO 项目结构这步直接影响后续训练命令能否跑通tiles_dataset/ ├── images/ │ ├── train/ # 约 6393 张 │ └── val/ # 约 1599 张 ├── labels/ │ ├── train/ │ └── val/ └── data.yaml划分比例我习惯用 80/207992 张原图也就是训练 6393、验证 1599。划分时要注意按类别分布做分层不然某一类缺陷集中在验证集里训练集等于没学过它。下面这版划分脚本在按图分的基础上额外检查每个子集里三类缺陷的框总数占比import os import random from collections import defaultdict random.seed(42) # 承接 2.2 节已经加载好的 coco 变量 cat_ids_by_img defaultdict(list) for ann in coco[annotations]: cat_ids_by_img[ann[image_id]].append(ann[category_id]) img_files list(img_id_to_name.values()) random.shuffle(img_files) split_idx int(len(img_files) * 0.8) train_files img_files[:split_idx] val_files img_files[split_idx:]随机种子固定成 42 是为了结果可复现。分层校验的增强写法是把每张图的类别组合转成字符串然后按这个字符串做 stratify确保 train 和 val 里含裂缝的图比例接近。实际操作中如果某一类缺陷的标注框特别少我会把那部分图人工确认后全放进训练集验证集类不平衡的问题靠 mAP 分科评估去看。3.2 COCO 转 YOLO坐标换算与越界裁剪核心转换函数比较直接但有两个细节不能漏。先看代码def coco_to_yolo(bbox, img_w, img_h): x, y, w, h bbox cx (x w / 2) / img_w cy (y h / 2) / img_h w_norm max(0, min(1.0, w / img_w)) h_norm max(0, min(1.0, h / img_h)) cx max(0, min(1.0, cx)) cy max(0, min(1.0, cy)) return cx, cy, w_norm, h_normbbox 参数是 COCO 里的[x, y, w, h]其中 x、y 是框左上角绝对像素坐标。中心点的计算是 x w/2再除以图宽得到归一化中心。最后四行 clip 操作是防呆设计瓷砖缺陷如果贴在图边缘标注框可能越过图像边界归一化后出现大于 1 的坐标直接写进 YOLO txt 会在训练时报 coordinates out of range。这里的 img_w、img_h 不能拿一个全局假想值替代必须是当前图的实际分辨率这就是 2.3 节检查分辨率的用处。写盘时注意 YOLO class id 从 0 开始COCO 的 category_id 从 1 开始转换时统一减一with open(out_txt_path, w) as f: for ann in anns_for_img: cat_id ann[category_id] - 1 cx, cy, w_norm, h_norm coco_to_yolo(ann[bbox], img_w, img_h) f.write(f{cat_id} {cx:.6f} {cy:.6f} {w_norm:.6f} {h_norm:.6f}\n)每行五个值空格分隔类别 id 放第一位。这里有个细节如果一张图有 5 个缺陷框这个文件就有 5 行行与行之间不可以有空行YOLO 的 dataloader 是逐行读取的。提示转换脚本写完一定要抽查至少 5 张图把归一化坐标反算回像素坐标画框人眼确认框和缺陷对得上再进训练。3.3 data.yaml 与训练启动参数YOLOv8 的配置都用 data.yaml 管理训练脚本从 data 字段读路径。写法如下path: /absolute/path/to/tiles_dataset train: images/train val: images/val nc: 3 names: 0: edge_chip 1: hole 2: cracknc 必须和 names 里数量一致names 的顺序必须和转换 txt 里的 class id 一一对应。这个顺序错一个模型输出的类别名就全是错的——它不会报错只会给你一个无关的错误结果。训练命令我一般这样跑yolo detect train \ modelyolov8m.pt \ datadataset/data.yaml \ epochs100 \ imgsz640 \ batch16 \ device0参数说明如下表格方便直接参照调整参数值说明modelyolov8m.pt中杯模型精度和速度折中追求轻量换 yolov8s.ptepochs1007992 张图通常 50 epoch 左右就收敛100 是为了留足余量imgsz640默认推理尺寸裂缝细长条建议提到 1280batch1612GB 显存可跑 168GB 建议降到 8device0指定 GPU 序号无 GPU 改成 cpu 但很慢选 yolov8m 是因为瓷砖缺陷属于中小目标s 模型在细长裂缝上容易出现漏检m 的特征图分辨率相同但容量更大对低对比度的裂纹纹理更友好。如果你在资源受限的 Jetson 上部署建议直接用 s 起步然后对比 m 的 mAP 差多少再定。3.4 训练中的关键观察项与早停判断训练开始后不要只盯着终端输出重点看 val loss 曲线和每类的 PR 曲线。YOLOv8 每 10 个 epoch 会存一个 checkpoint观察 fitness 指标如果 30 epoch 后 val loss 还在逐轮下降、pr 曲线面积稳定增大说明还没收敛如果 val loss 开始回升而 train loss 还在降就是过拟合信号应该早停然后回滚到最低点的权重。另一个常见操作是把训练集里所有裂缝类样本单独拎出来做一次可视化确认转换后标注框和裂纹边缘贴合。这一步因为目标太细一旦转换坐标算错训练过程不会报错验证指标却始终提不上去。我习惯每 20 epoch 用验证集一半的图像跑一次 predict直接看推理画框的效果比盯着 loss 曲线更能发现问题。4. 避坑指南标注错位、类别不平衡与训练崩溃的 5 个现场这个数据集看起来干净但真正跑起来翻车点不少。下面五条是我实际处理此类数据集的血泪经验按现象、原因、解决三步写遇到类似问题可以直接对照。4.1 现象json.load 后 KeyError: bbox读 COCO JSON 时遍历 annotations突然有个 dict 里没有 bbox 键直接 KeyError。原因这个数据集如果经过 Roboflow 导出个别标注可能带有 iscrowd 或 segmentation 字段但没有 bbox或者导出过程裁剪过某张异常图。解决遍历前先加一个字段存在性判断if bbox in ann and ann[bbox]: process(ann[bbox])同时打印没有 bbox 的 annotation 原始内容确认是哪种情况。如果只有极少数比如个位数直接跳过不处理如果占比较高比如超过 10%就要怀疑导出配置有问题回源重新导出。4.2 现象YOLO 训练中途报 normalized coordinates out of range训练在第一个 epoch 就崩提示某个 label txt 里有坐标超出 [0,1]。原因上一章提到的边界框越界没有 clip或者图分辨率与标注基准不一致缩放时坐标整体偏移。解决写一个批量校验脚本读取所有 labels 下的 txt检查每行的六个值注意 YOLO 格式每行第一个是 class id后面四个才是坐标看是否都在 [0,1] 区间import os label_dir labels/ for fname in os.listdir(label_dir): fpath os.path.join(label_dir, fname) if not fname.endswith(.txt): continue with open(fpath, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(f格式错误: {fpath}: {line}) continue coords [float(p) for p in parts[1:]] if any(c 0 or c 1 for c in coords): print(f越界: {fpath}: {line})校验脚本本身不修改文件只是定位问题。定位到是哪些图出了问题后直接删除该图的 txt或者用 clip 后的值重写。注意删除时要连 images 里对应的图一起处理不然 dataloader 会报 image without label 或 label without image 的错。4.3 现象训练完 crack 类 mAP 明显低precision 甚至为 0整轮训练结束后其他两类 AP 在 0.8 左右裂缝只有 0.3。原因crack 类标注框数量太少或裂缝目标太细长在 640x640 下已经退化成几个像素宽的短线模型学不到稳定特征。解决先看类别统计确认数量——如果裂缝框少于几百个考虑对裂缝样本做过采样复制到训练集注意不能把同一张图既放 train 又放 val如果裂缝框数量足够但目标太细把 imgsz 提到 1280 重新训练。过采样代码不复杂import shutil crack_images [img_f for img_id, img_f in img_id_to_name.items() if any(cat_id_to_name[ann[category_id]] crack for ann in anns_by_img[img_id])] # 复制到训练集文件名加 _crack_aug 前缀 for img_f in crack_images: for i in range(2): # 复制 2 份 new_name img_f.replace(.jpg, f_crack_{i}.jpg) shutil.copy(os.path.join(images/, img_f), os.path.join(images/train/, new_name))复制的同时要把对应的 YOLO txt 也复制并改名图像和标签文件名必须保持一致YOLO 靠文件名前缀匹配样本对。这个方案简单粗暴但有效代价是训练集变大训练时间大约增加 20%。4.4 现象迁移学习效果好但换台机器后 mAP 骤降同一套权重在本机验证集 mAP 0.85部署到产线工控机后只有 0.6。原因工控机的摄像头分辨率、光照条件和训练集差异很大瓷砖缺陷是典型的低对比度目标光照一变边缘崩裂的特征分布就偏移。解决部署后采集新产线图加入训练集做二次微调如果部署紧急先调推理时的 conf 阈值和图像预处理参数——但这些都是治标根本解法是把新场景数据回流。这个数据集作为基准真正的项目落地一定是预训练 现场数据再微调的流程。4.5 现象一张图上有密集缺陷框训练直接 OOM有些大尺寸瓷砖图可能单图标注了几十个框加上 mosaic 增强一次凑 4 张图batch 16 时实际参与计算的框数非常大显存直接爆掉。原因GPU 显存不够或者 mosaic 增强把多张小图拼成大图后单张图的框数暴涨。解决batch 降到 8或者关掉 mosaicyolo detect train \ modelyolov8m.pt \ datadataset/data.yaml \ epochs100 \ imgsz640 \ batch8 \ mosaic0.0mosaic 是 YOLOv8 里默认开启的数据增强它把 4 张图拼在一起训练。瓷砖图缺陷密集时拼图后单张 feature map 上目标过多既吃显存又可能造成梯度不稳定。关掉后精度通常会掉一点但训练稳定性大幅提升。我一般建议先开 mosaic 跑通OOM 时再关。5. 把模型推向产线mAP 分科评估与批量推理落地技巧训练完不代表能上线最后一步是把模型在验证集上 score 高变成模型在产线上筛砖准。这里两个技巧值得细讲。5.1 分科的 PR 曲线与 mAP 怎么看YOLOv8 训练完会在 runs/detect/train/ 下生成 confusion_matrix.png 和 PR_curve.png。不要只看总 mAP瓷砖质检的决策点在类别级hole 和 edge_chip 相对好检crack 如果 recall 低意味着大量裂缝砖会漏过去。看 PR 曲线时优先找召回率到 0.8 时精确率还能维持 0.7的操作点再反推这个点对应的 conf 阈值写进推理配置。5.2 批量推理与 conf 阈值选择我一般把验证集的预测写成脚本用不同 conf 跑一遍对比 precision/recall 再定阈值from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict( sourcetest_images/, conf0.35, iou0.5, imgsz640, save_txtTrue, save_confTrue, projectresults/, nameconf035 )conf0.35 表示低于该置信度的框全部丢弃iou0.5 是 NMS 的 IoU 阈值。瓷砖产线上误报意味着好砖被踢掉成本高所以我会把 conf 调高到 0.45~0.5宁可漏检一些低置信度缺陷也不能让合格砖进废料箱如果后续有机械臂剔除环节对漏检容忍度低可以降到 0.25 然后靠人工复检兜底。这个阈值选择没有绝对答案最好按 5.1 节的操作点方法定。5.3 场景差异的兜底习惯那次之后我每次换产线部署都会强制走一遍相同流程先用这个数据集预训练再采集现场图 200 张做快速验证最后把预测错的图人工补标进训练集做第二轮微调。模型不是一次训练完的成品这个流程保证了召回率在真实环境下可用。希望这些踩坑记录能帮你把 7992 张图用出应有的效果。本文还有配套的精品资源点击获取
返回列表