ARTICLE DETAIL

资讯详情

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

COCO瓷砖缺陷数据集转YOLO训练全流程指南

COCO瓷砖缺陷数据集转YOLO训练全流程指南 简介这套瓷砖缺陷检测数据集面向工业质检、智能建造与机器学习研究场景可供目标检测算法工程师、质量控制人员及从事表面缺陷研究的开发者使用。数据集覆盖边缘崩裂、破洞、裂缝等典型瓷砖缺陷采用COCO JSON格式标注包含清晰的类别与边界框信息可直接用于YOLO等主流检测模型的训练与评估。压缩包共有2000个文件其中1997张为JPG原始图像3个为JSON标注文件整体大小约579MB图像均按真实拍摄场景命名便于后续按批次或来源筛选。目前已有646人学习/下载。在实际应用中该数据集既能辅助工厂产线自动识别缺陷瓷砖、降低废品率也可用于零售商来货分拣、装修前质量评估和建筑竣工检测还能作为算法团队进行模型调参、对比实验与模型迭代的基础数据具备较强的工程落地价值。1. 拿到7992张COCO瓷砖缺陷图边缘崩裂、破洞、裂缝能直接训练吗在陶瓷厂质检场景里瓷砖缺陷检测数据集是最容易被低估的一环。你拿到手的是一个已经用COCO JSON格式标注好的数据集一共7992张原始图能识别边缘崩裂、破洞、裂缝三类缺陷。这意味着你不需要从零开始标注也不用为“标注格式不统一”头痛数据可以直接喂给目标检测模型训练。关键是看这个COCO标注到底规不规范、三类缺陷有没有被区分清楚、类别分布是不是偏得离谱。这套数据适合正在做工业视觉落地的算法工程师、质检设备集成商以及需要快速产出检测原型的开发者。COCO JSON只是第一步真正决定模型能不能上线的是后续的数据体检、格式转换和边界处理。2. 读懂COCO JSON标注三类缺陷为什么这样标、先做数据体检2.1 边缘崩裂、破洞、裂缝的标注形态差异与COCO多边形语义COCO JSON是目标检测和实例分割领域最常用的标注格式之一原因不是它“洋气”而是它的结构足够收敛整个数据集只需一个JSON文件里面包含images、annotations、categories三个核心字段缩略图路径、目标框坐标、多边形分割点都挂在这三个字段下。和VOC XML一大堆文件、YOLO TXT一行一个目标相比COCO用一整个JSON文件管理全部标注在分发、读取、校验上都有明显优势。瓷砖缺陷有一个特点边缘崩裂、破洞、裂缝在形态上差别很大。边缘崩裂一般发生在瓷砖边缘是不规则的长条或锯齿状破损破洞是相对封闭的近似圆形或椭圆形区域裂缝则是细长的折线段可能贯穿整块砖。不同的形态对应不同的标注策略破洞适合用封闭多边形去圈裂缝如果用矩形框去框框会特别大而目标实际面积特别小这会让模型在训练时学到大量背景。COCO annotations里的segmentation字段正好能表达多边形所以拿到这个数据集时不要只看bbox还要把segmentation的坐标点数、是否闭合、是否跨图片边界一起查一遍。2.2 用Python解析annotations加载、类别映射、统计数据跑通最小体检脚本拿到COCO JSON后的第一个动作不是训练而是体检。我一般会写一个最短的解析脚本把图片数量和标注数量对齐检查一遍顺带看看这三类缺陷各自有多少目标。很多时候一个看着没问题的JSON读出来才发现有图片没标注、有标注对不上图、类别ID错乱这些问题在训练前发现能省好几天排错时间。import json from collections import Counter, defaultdict coco_path tile_defect/annotations/instances_train.json with open(coco_path, r, encodingutf-8) as f: coco json.load(f) # images与annotations的id对齐检查 img_ids {img[id] for img in coco[images]} ann_img_ids {ann[image_id] for ann in coco[annotations]} missing img_ids - ann_img_ids print(有图无标注:, len(missing)) # 类别映射 cat_map {cat[id]: cat[name] for cat in coco[categories]} print(类别ID-名称:, cat_map) # 每个类别的目标数量 cat_counter Counter(ann[category_id] for ann in coco[annotations]) print(各类别目标统计:, {cat_map[k]: v for k, v in cat_counter.items()}) # 每张图的目标数分布 per_img_count Counter() for ann in coco[annotations]: per_img_count[ann[image_id]] 1 img_nums [per_img_count[i] for i in img_ids] print(平均每图目标数:, sum(img_nums) / len(img_ids)) # 异常框检查右下角坐标不得小于左上角 bad 0 for ann in coco[annotations]: x, y, w, h ann[bbox] if w 0 or h 0 or x 0 or y 0: bad 1 print(异常框数量:, bad)这段脚本做了四件事。第一把images里的图片ID和annotations里的image_id做一个差集找出哪些图没被标注防止后面转换时出现“某张图没有对应标签”。第二把categories里的数字ID映射成中文名避免你拿着标注文件却不知道哪个ID代表裂缝。第三统计每类目标数量直接看出类别是否均衡如果边缘崩裂有1万框而裂缝只有800框训练权重就要调整。第四粗查bbox是否出现负坐标或宽高为0这类异常是数据转换期最容易爆炸的定时炸弹。这里有一个细节这段脚本只做“粗查”不会告诉你segmentation是否闭合、裂缝多边形是否过长那些需要可视化抽样确认。一次跑完脚本后如果平均每张图目标数在1到4之间异常框数量为0这个数据集就可以进入下一步了。如果异常框数量很多先不要急着转换去找原始标注软件确认坐标体系瓷砖图像一旦经过裁剪或旋转坐标经常整批偏移。2.3 从体检结果判断数据集能不能用面积分布与类别平衡表体检不能只看数量还要看目标面积分布。裂缝这类缺陷在7992张图里如果普遍都很小那么后续训练必须考虑小目标检测的配置比如在YOLO里调小anchor、增大输入分辨率、或者用P2层特征。破洞如果面积中位数很大那模型会相对好收敛但容易过拟合到“深色圆形区域”这种浅层特征上。边缘崩裂如果往往贴着图像边缘转换时还要考虑坐标裁剪。我一般会把体检结果落成一张表作为训练前的验收记录。 | 检查项 | 判断标准 | 不合格时的处理方式 | | --- | --- | --- | | 有图无标注数量 | 0 | 联系数据方补标或删除无标注图 | | 类别目标数占比 | 最少类不低于最多类的20% | 做类别重采样或增加loss权重 | | 异常框数量 | 0 | 修正annotations里bbox字段 | | bbox面积中位数 | 每类分别统计最好超过全图面积1% | 增大输入分辨率或调整anchor | | 宽高比大于10的框占比 | 裂缝类允许偏高其他类应低于5% | 回查segmentation是否被错误整包 | 这张表的价值在于让你明确“这个数据集能不能直接用”。如果边缘崩裂和破洞的标注数量均衡但裂缝只标了零星几百个那后面训练出来的模型对裂缝几乎等于瞎猜。相反如果7982张图里只有几十张有异常那这类问题属于可修复噪声直接进入转换流程就好。3. 把7992张COCO数据转成YOLO训练集切分、归一化与脚本落地3.1 为什么要从COCO转YOLO训练框架更普及、锚框与归一化要求现实中的落地项目更多人直接顺着“yolov8训练自己的数据集”这条路走。YOLO系列的训练接口统一、部署生态完善从训练到TensorRT导出都是一条链路所以即使COCO JSON是更完整的标注格式最终训练前还是要把COCO转换成YOLO的TXT标签格式。YOLO标签没有JSON那样的分层结构每一个标注目标是一行纯文本格式是“类别ID 中心点x 中心点y 宽度w 高度h”坐标经过归一化全部介于0到1之间。这个极简格式的好处是读取速度快、训练时不依赖额外标注引擎坏处是丢了segmentation多边形信息只剩矩形框。做瓷砖缺陷检测通常用矩形框就够了边缘崩裂、破洞、裂缝这些缺陷用框来定位能覆盖绝大多数质检需求所以这种转换不是降级而是实用主义。COCO的bbox是左上角坐标加宽高YOLO需要的是中心点坐标加宽高同时要除以图片尺寸。这里最容易翻车的不是公式而是图片尺寸读错。有些标注者给的宽高是标注时用的尺寸图像文件本身可能是旋转后的尺寸两者不一致时转换出来的坐标全是错的。所以脚本里必须从图像文件本身读取宽高不能用JSON里images字段的宽高直接当唯一依据至少要交叉验证一遍。3.2 转换脚本COCO转YOLO坐标归一化与train/val切分我自己在项目里用的转换脚本逻辑很直白读JSON按图片ID分组遍历每一张图的标注把坐标从像素值转成归一化值写到与图片同名的TXT文件里同时按预设比例划分训练集和验证集。切分要按图片切不能按目标切否则同一张图的标注会被拆到两个集合里造成数据泄漏。import json import cv2 from pathlib import Path coco_path tile_defect/annotations/instances_train.json image_dir Path(tile_defect/images) output_dir Path(tile_defect/yolo_dataset) val_ratio 0.15 seed 42 # 类别ID重新映射为0,1,2顺序可自定义 class_map {1: 0, 2: 1, 3: 2} # 原始COCO的1,2,3 - 边缘崩裂,破洞,裂缝 with open(coco_path, r, encodingutf-8) as f: coco json.load(f) img_id2info {img[id]: img for img in coco[images]} anns_by_img defaultdict(list) for ann in coco[annotations]: anns_by_img[ann[image_id]].append(ann) all_img_ids list(img_id2info.keys()) rng random.Random(seed) rng.shuffle(all_img_ids) val_count int(len(all_img_ids) * val_ratio) val_ids set(all_img_ids[:val_count]) # 图片文件后缀可能是.jpg/.png/.JPG按实际文件名匹配 for img_id in all_img_ids: img_info img_id2info[img_id] img_path image_dir / img_info[file_name] im cv2.imread(str(img_path)) if im is None: print(跳过读不到的图:, img_path) continue H, W im.shape[:2] split val if img_id in val_ids else train label_dir output_dir / split / labels image_out_dir output_dir / split / images label_dir.mkdir(parentsTrue, exist_okTrue) image_out_dir.mkdir(parentsTrue, exist_okTrue) txt_lines [] for ann in anns_by_img[img_id]: cat_id ann[category_id] if cat_id not in class_map: continue x, y, w, h ann[bbox] # 越界框先clip后转归一化 x1 max(0, x) y1 max(0, y) x2 min(W, x w) y2 min(H, y h) if x2 - x1 1 or y2 - y1 1: continue cx (x1 x2) / 2 / W cy (y1 y2) / 2 / H nw (x2 - x1) / W nh (y2 - y1) / H txt_lines.append(f{class_map[cat_id]} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}) if txt_lines: label_path label_dir / (img_path.stem .txt) label_path.write_text(\n.join(txt_lines), encodingutf-8) # 拷贝图片到对应split目录保持文件命名一致 cv2.imwrite(str(image_out_dir / img_path.name), im) print(转换完成)这个脚本里比较关键的有几个地方。class_map变量把COCO原始的类别ID映射成0、1、2因为在YOLO训练里类别ID必须连续否则模型输出的类别数会和你的分类头对不上。坐标clip的逻辑我专门放在转换前边缘崩裂这类目标天然贴近图像边界标注时经常出现x为负或xw超过图片宽度的现象如果不强制把越界坐标拉回图像范围归一化后的中心点和宽高就会超过1训练时Grid分配会直接把这种目标归到背景上。凡是裁剪后宽或高不足1个像素的目标直接丢弃这类目标在图像上肉眼几乎看不清留着只会增加训练噪声。还有一个分布细节验证集是按图片ISO分配但如果你发现某类缺陷只集中在某几张大图上这种随机切分会让验证集缺失这一类。更保险的做法是按“每张图是否包含裂缝”做分层保证val里一定能见到裂缝样本代价是代码多写十几行。如果你的任务是上线质检建议不要省这一步。3.3 切分策略按图不按框避免同图数据泄漏很多第一次接触COCO数据集的工程师会犯一个错用了sklearn的train_test_split却直接拿annotations数组去切。这样同一张图的一部分标注在训练集另一部分在验证集模型训练时变相看到了验证答案最终mAP虚高上线就现原形。正确的做法始终是以image_id为最小切分单位。对于7992张图这个规模我给的默认配比是85%训练、15%验证。如果打算做模型选型对比再从训练集里切一部分做测试集比例可以是70%训练、15%验证、15%测试。切分时用固定随机种子保证每次复现实验的结果一致。更严格的工程流程还会生成一个split.json把每张图的归属记录下来这样后续跑数据增强或换模型时不用重新随机切一遍。此外切分之后一定要生成一个class_counts.txt分别统计train和val里每类目标的总数。我遇到过数据切完后才发现裂缝目标数在验证集里只有个位数这种情况下的评估指标完全没有统计意义等于白训。3.4 可视化校验把YOLO格式画回原图确认对齐转换脚本跑完后第一件事不是急着开训练而是随机抽50张图把TXT里的坐标画回原图上看一眼。这一步能发现坐标缩放错误、图片旋转方向不一致、类别ID错位等各种转换问题。我一般用OpenCV写一个极短的检查脚本直接把标注框叠加在原图上输出到check目录。import cv2 from pathlib import Path label_dir Path(tile_defect/yolo_dataset/train/labels) image_dir Path(tile_defect/yolo_dataset/train/images) out_dir Path(tile_defect/check) out_dir.mkdir(exist_okTrue) class_colors {0: (0, 255, 0), 1: (0, 255, 255), 2: (0, 0, 255)} class_names {0: chip, 1: hole, 2: crack} for label_path in list(label_dir.glob(*.txt))[:50]: img_path image_dir / (label_path.stem .jpg) im cv2.imread(str(img_path)) if im is None: continue H, W im.shape[:2] for line in label_path.read_text(encodingutf-8).strip().splitlines(): parts line.strip().split() if len(parts) ! 5: continue cid, cx, cy, nw, nh map(float, parts) x1 int((cx - nw / 2) * W) y1 int((cy - nh / 2) * H) x2 int((cx nw / 2) * W) y2 int((cy nh / 2) * H) cv2.rectangle(im, (x1, y1), (x2, y2), class_colors[int(cid)], 2) cv2.putText(im, class_names[int(cid)], (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, class_colors[int(cid)], 2) cv2.imwrite(str(out_dir / label_path.stem .jpg), im) print(可视化图已输出到 check 目录)这个可视化代码本身不难难的是“看什么”。我会重点看三类问题。第一裂缝目标的框是不是紧紧包住裂缝线如果框内有大面积瓷砖背景说明标注框打宽了第二边缘崩裂的框是否压到图片外如果框的边界正好和图像边界重合没问题但如果框明显超出且被clip回原图核对是否漏掉了一部分裂纹第三同一位置是否出现两个颜色框重叠这是常见重复标注。宽度小于2像素的框在压缩后的可视化图里可能看不清所以抽样时要优先抽那些包含裂缝的图源码里遍历顺序是按标签名排的效率不高但用于抽检足够。4. 数据转换与标注五个高频坑现象、原因、解决办法4.1 裂缝目标整批被跳过转换后训练警告“empty label”现象转换时没报错但开始用YOLO训练后日志里频繁出现“WARNING: empty label”或某个epoch的loss直接是0一看统计裂缝类一个目标都没进训练集。原因裂缝是细长目标很多人工标注框的原始高度只有3到5个像素。转成YOLO格式后归一化高度是5除以图片高度比如5/1024约等于0.0049。如果之后训练预处理把图片缩放到640x640归一化坐标还是按原始图算但网络层下采样后裂缝的框高度可能连1个特征图网格都占不到。更直接的原因是转换脚本里“若裁剪后宽高小于1像素则跳过”这个过滤条件把裂缝全滤掉了。解决过滤条件不能一刀切。裂缝类要单独放宽阈值最小保留宽度或高度可以放到0.5像素。你也可以不按像素过滤而是按归一化后的宽高过滤例如保留nw大于0.001或nh大于0.001的目标小于这个值的才丢弃。与此同时训练时把输入分辨率从640提到960或1280细裂缝在放大后的图像上就不再是亚像素目标。4.2 边缘崩裂的框越界训练时出现nan坐标或loss异常现象可视化检查时发现边缘崩裂的标框有一部分画在图像外面。转换后某些标签行里的cx或cy大于1训练时输出坐标出现nan。原因标注工具里允许框超出画布边界有些标注员为了快速框住边缘缺陷会把框的起始点拖到画布外。COCO JSON里这类坐标被原样保存直接转成YOLO格式时不做裁剪就会产生非法坐标。解决在转换脚本里对每个bbox做一次clip这一步一定不能省。先取x1max(0,x)y1max(0,y)x2min(W,xw)y2min(H,yh)再参与后续归一化计算。要注意如果clip后的宽度占原宽度的比例过小比如原框是200像素宽clip后只剩5像素说明这个目标本身标注就有问题最好回原图核对而不是无脑保留。这种越界情况在边缘崩裂类别里出现概率最高所以转换脚本的日志里最好单独统计每一类被clip了多少个框超过总框数10%就要回头找标注方统一修。4.3 同一破洞被重复标注模型训练后误检率虚高现象可视化时看到同一个破洞位置被两个几乎重合的框覆盖颜色不同但类别相同。训练结束后测试集mAP很高但现场一跑同一缺陷在结果里被输出两个检测框NMS也压不掉因为两个框的置信度都超过阈值且中心距离较远时NMS不认为是重复框。原因数据集中破洞类别存在重复标注。这种情况在多人协同标注时最常见两个人先后标注了同一张图标注平台合并时没有去重。加上这个数据集的类别ID在原始JSON里可能没有严格规范同一个目标被标了两次但category_id不同。解决写一个去重脚本按image_id分组对组内的目标bbox两两计算IoUIoU大于0.7且类别相同的保留高置信度文件里靠前的那一个或者保留面积更大的那一个。去重之后重新跑一遍数据体检看目标总数下降了多少。如果重复率超过5%说明标注流程本身有疏漏建议对全部训练结果做一次置信度分布检查凡是ground truth没覆盖但模型频繁输出的位置去原图里确认是不是未修复的重复标注。4.4 images与annotations数量对不上训练集被莫名截断现象数据体检脚本打印“有图无标注: 98”转换后YOLO目录里train/images有6000张图train/labels只有5950个txt文件。原因COCO JSON里部分图片没有任何标注可能本身是负样本也可能是标注后被删掉了还没清除image记录。YOLO格式没有“空标签”概念一张图没有txt会被多数训练框架跳过。解决负样本如果是有意保留的转换脚本里要为它们生成空txt文件并在训练配置里把“drop empty”关掉。如果负样本是误产生的直接把这些图片从数据集里过滤掉比保留更省心。实际操作时最好同时导出两份清单一份是有标注图片清单一份是无标注图片清单人工扫一眼无标注图片确认它们到底是“干净的瓷砖”还是“标注遗漏”。我遇到过所谓无标注图片里肉眼能看见大面积裂缝的情况那属于标注事故这种图必须联系数据方补标否则模型会学着把裂缝当背景。诊断时可以借助jq快速筛出多余的id比如jq [.images[].id] - [.annotations[].image_id] | unique instances_train.json先确认差异数量再写代码处理。4.5 中文路径与编码问题读取图片返回None现象在Windows上跑转换脚本cv2.imread报错或者返回None但在文件管理器里图片明明存在。换成Linux服务器后部分图片读取依旧失败。原因COCO JSON里的file_name可能带中文或图片路径含中文目录名。Windows下OpenCV的imread函数对非ASCII路径支持很差返回None是常态。另一个来源是JSON文件编码不是UTF-8json.load直接抛UnicodeDecodeError。解决读取图片使用pathlib构造路径并考虑用cv2.imdecode代替cv2.imread先用read_bytes读取文件再用np.frombuffer和cv2.imdecode解码这样可以绕开文件名编码问题。JSON文件统一用open(path, r, encodingutf-8)打开不要用系统默认编码。更进一步数据集分发前最好把中文文件名批量改成英文或纯数字ID这一步在数据清洗阶段做一次之后所有脚本都少踩一类坑。此外有些图片后缀是.JPG或.jpeg但JSON里写的是.jpg导致路径拼接后找不到文件转换脚本里要兼容这类后缀不一致问题。5. 用COCO数据训练并验证检测模型可视化、评估与补标注5.1 先跑通“能检测”的最小闭环YOLOv8与mmdetection两种路线数据集经过体检、转换、可视化校验后就可以进入训练环节。常见做法是先用YOLOv8把最小闭环跑通yolo train datatile_defect.yaml modelyolov8s.pt epochs100 imgsz960。这里的tile_defect.yaml是指向第3章生成的train和val目录的配置文件类别名按顺序写成chip、hole、crack即可。先别急着上大模型yolov8s在这个量级的数据集上足够验证标注质量如果验证集的mAP在训练刚开始30个epoch内一直低于0.1先回头看数据不要加模型复杂度。mmdetection路线适合需要精确控制anchor、想用Cascade R-CNN或Mask R-CNN输出分割轮廓的读者。COCO JSON本来就是mmdet的默认标注格式改一下data_root和classes配置就可以训练。两种路线不冲突我通常用YOLOv8做快速反馈用mmdet做最终精度验证特别是在缺陷形态有强旋转或细长特征时Cascade R-CNN的回归稳定性比单阶段模型更可控。5.2 按缺陷形态看评估裂缝类不要迷信mAP训练结束后不要只看一个整体mAP要把三个类别拆开看。边缘崩裂通常面积较大AP0.5能达到0.9以上才算合格破洞是相对规整的目标重点看AP0.75因为高IoU下的精度才反映边界贴合程度裂缝最特殊宽高比极大通用mAP会把这种目标算进“小目标”或“中目标”统计里数值偏低是必然的这时候要单独看验证集里裂缝的召回率以及预测框中心点到裂缝线的距离误差而不是mAP绝对值。我习惯把裂缝的IoU阈值单独放宽到0.3到0.5进行验证。裂缝线的像素面积太小IoU超过0.5几乎不可能用0.5作为唯一门槛会得出“模型检测不了裂缝”的错误结论。实际质检中裂缝能被定位到附近几像素就算成功所以业务指标和学术指标要分开定义。5.3 误检回注与主动学习把错误样本送回CVAT复标训练完成后最容易被忽略的一步是把现场误检样本回注成新的标注数据。7992张原始图听着不少但在真实产线中光照、釉面颜色、砖型变化都会让分布漂移数据集迟早不够用。做法是把训练后的模型对一批无标签产线图做预测筛选出置信度在0.3到0.6之间的模糊样本导出成COCO JSON送到CVAT或LabelImg里人工复标。这类样本比从零标数据高效得多。如果以后想继续在这个方向上投入建议预留至少20%的图片不参与第一轮训练专门当作测试集和主动学习的种子池。我在多个工业检测项目里反复踩过的坑是数据一多就全扔进训练导致最终没有一张“干净”的测试集来评估真实性能。把一部分数据锁起来等模型迭代到某个阶段再打开你的评估结果才真正反映上线效果。这条习惯帮我少走了很多弯路希望帮到你。本文还有配套的精品资源点击获取
返回列表