简介:面向瓷砖制造、建筑检测与计算机视觉开发者的瓷砖缺陷检测数据集,内含边缘崩裂、破洞、裂缝等常见缺陷的原始图片及其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 绝对像素 | 1 |
| YOLO txt | 一图一文件 | cx, cy, w, h 归一化 | 0 |
| VOC 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', encoding='utf-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/20,7992 张原图也就是训练 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 \ model=yolov8m.pt \ data=dataset/data.yaml \ epochs=100 \ imgsz=640 \ batch=16 \ device=0参数说明如下表格,方便直接参照调整:
| 参数 | 值 | 说明 |
|---|---|---|
| model | yolov8m.pt | 中杯模型,精度和速度折中;追求轻量换 yolov8s.pt |
| epochs | 100 | 7992 张图通常 50 epoch 左右就收敛,100 是为了留足余量 |
| imgsz | 640 | 默认推理尺寸;裂缝细长条建议提到 1280 |
| batch | 16 | 12GB 显存可跑 16,8GB 建议降到 8 |
| device | 0 | 指定 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,或者关掉 mosaic:
yolo detect train \ model=yolov8m.pt \ data=dataset/data.yaml \ epochs=100 \ imgsz=640 \ batch=8 \ mosaic=0.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( source='test_images/', conf=0.35, iou=0.5, imgsz=640, save_txt=True, save_conf=True, project='results/', name='conf035' )conf=0.35 表示低于该置信度的框全部丢弃,iou=0.5 是 NMS 的 IoU 阈值。瓷砖产线上误报意味着好砖被踢掉,成本高,所以我会把 conf 调高到 0.45~0.5,宁可漏检一些低置信度缺陷,也不能让合格砖进废料箱;如果后续有机械臂剔除环节,对漏检容忍度低,可以降到 0.25 然后靠人工复检兜底。这个阈值选择没有绝对答案,最好按 5.1 节的操作点方法定。
5.3 场景差异的兜底习惯
那次之后,我每次换产线部署都会强制走一遍相同流程:先用这个数据集预训练,再采集现场图 200 张做快速验证,最后把预测错的图人工补标进训练集做第二轮微调。模型不是一次训练完的成品,这个流程保证了召回率在真实环境下可用。希望这些踩坑记录能帮你把 7992 张图用出应有的效果。
本文还有配套的精品资源,点击获取