
简介面向风力叶片运维场景的缺陷检测数据集以2249张叶片图像为标注对象覆盖排水孔受损、雷击、污垢、漏油、PU胶带、表面裂纹、侵蚀等典型缺陷适合训练目标检测与实例分割模型也便于运维人员、算法工程师建立巡检识别基准。压缩包内共2000个文件包括1997张jpg原图与3个COCO JSON格式标注文件整体约74.32MB解压后按图像与标注文件配对使用即可无需额外转换。资源发布以来已有818人学习下载在风电视觉识别领域具备一定参考价值。读者可获得的COCO标注包含完整的类别标签与边界框信息能直接用于YOLO、MMDetection、Detectron2等主流框架训练也可用于缺陷分类实验与算法效果对比帮助快速构建针对叶片表面异常的检测流程。标注字段完整支持划分训练集与验证集便于复现已有工作并评估模型性能。1. 风力叶片缺陷检测数据集2249 张 COCO 标注图够不够训一个能上场的模型做风电运维检测的同行应该都有体会叶片缺陷检测的难点不在算法而在数据。叶片本体动辄几十米缺陷类型又多——排水孔受损、雷击、污垢、漏油、PU 胶带、表面裂纹、侵蚀——七类缺陷形态差异极大雷击点可能只有拳头大污垢却是一片一片的小目标和大目标的尺度能差几十倍。更麻烦的是这类数据大多在风机上、在野外环境里拍采集成本高公开数据集又少得很想要合格的标注数据基本只能自己攒。这份数据集一共 2249 张图全部用 COCO JSON 格式标注覆盖七类叶片常见缺陷。COCO 格式的好处是生态成熟主流检测框架Detectron2、MMDetection、YOLOv8 的 COCO 转换工具都能直接消费不需要自己写解析器。不过我在实际拆数据的时候发现这个数据集的图片命名和标注分布里藏着不少细节——比如不同风机批次、不同叶片编号、不同拍摄时间这些信息直接影响你划分训练集和验证集的方式。如果你正准备拿它做叶片缺陷检测的落地项目或者想用它练手跑通 COCO 到 YOLO 的完整流程这篇笔记应该能帮你少走几趟弯路。2. 数据集结构拆解从文件命名和 JSON 字段里读出隐藏信息拿到一份数据集第一件事不是直接开训而是把它的结构摸清楚。这个数据集表面上是「2249 张图 一个 JSON」但当你把文件名逐条拆开看会发现很多对后续训练有影响的细节。2.1 文件名解码机型前缀、叶片编号与拍摄时间戳文件名的格式是这样的ASP47BladeC1703859112_jpg.rf.721ccfc57d1b2244b8a4a1dcc41814c5.jpg MAN320BladeB1705596408_jpg.rf.9ee10415942caf9865588610a12291ca.jpg拆开看就是三段信息机型/批次前缀ASP47、ASP22、ASP54、ASP95是一类MAN320、MAN437、MAN492、MAN353、MAN380、MAN297是一类。我判断这是两个不同风场、或至少两个不同机型批次的数据。这个信息很重要——如果两个批次的拍摄光照、相机角度、叶片表面状态差异大随机划分训练集和验证集会引入数据泄漏模型在验证集上的指标会虚高。叶片编号BladeA、BladeB、BladeC说明数据采集是分叶片进行的。同一个叶片的图片之间高度相似背景、纹理、光照条件接近所以划分数据集时最好按叶片分组而不是按单张图随机分。时间戳1703859112和1705596408是 Unix 时间戳分别对应 2023-12-29 和 2024-01-18 左右。说明这批数据跨了至少两个月中间可能有叶片状态变化这类时间维度差异在缺陷检测里也算一种域偏移。文件名里的_jpg.rf.段是标注平台导出的标记.rf.后面是一串哈希值这是 Roboflow 导出数据时的常规痕迹不影响使用。2.2 用 Python 把 COCO JSON 拆开看categories、images、annotations 三件套COCO 格式的核心就是三个字段categories定义类别images记录每张图的尺寸和路径annotations存放每个目标的标注框和多边形。我建议任何数据集到手先写个三五十行的脚本做基础统计别急着训练。import json from collections import Counter with open(annotations.json, r, encodingutf-8) as f: coco json.load(f) # 类别信息 for cat in coco[categories]: print(f类别ID: {cat[id]}, 名称: {cat[name]}, 上级: {cat.get(supercategory, 无)}) # 图片信息 images coco[images] print(f\n图片总数: {len(images)}) print(f前3张图片记录: ) for img in images[:3]: print(f - id{img[id]}, 宽{img[width]}, 高{img[height]}, 文件名{img[file_name]}) # 标注统计 annos coco[annotations] print(f\n标注总数: {len(annos)}) # 按类别统计 cat_counter Counter() for ann in annos: cat_counter[ann[category_id]] 1 print(f\n各类别标注数量:) for cat in coco[categories]: print(f {cat[name]}: {cat_counter.get(cat[id], 0)} 个目标)这段代码做的事情很简单但非常实用。第一段遍历categories看清楚类别 ID 和名称的映射关系——我拆过不少数据集category_id不连续、从 1 开始、甚至有跳号的情况很常见这在转 YOLO 格式时是个坑。第二段看images里的宽高字段能快速判断图像分辨率是否一致。第三段统计每个类别的实例数量直接暴露类别不均衡问题——比如某类缺陷只有几十个框某类却有上千个这个信息直接决定你后面要不要做重采样或换损失函数。2.3 七类缺陷的形态差异与检测难度之前说的七类缺陷各自形态和检测难度完全不同。下表是我结合叶片巡检实际经验做的判断后面讲训练策略时会用到缺陷类别典型外观目标尺度检测难点排水孔受损叶片根部排水孔附近的破损、裂缝中通常几十像素与正常排水孔混淆雷击烧灼痕迹、黑色焦斑常伴裂纹小到中颜色与背景阴影接近污垢大面积脏污、灰尘堆积大有时占据半张图边界模糊无清晰轮廓漏油油渍从轴承或接头处渗出中与污垢视觉相似PU 胶带叶片表面贴附的胶带破损或脱落中到大边缘不规则反光表面裂纹细线状裂缝方向各异小对比度低易被噪声淹没侵蚀前缘或表面材料剥落中到大与正常磨损边界不清雷击和裂纹都是小目标且对比度低污垢和侵蚀都是大面积目标边界模糊。这意味着单尺度训练很难同时满足两类目标的收敛要求后面做多尺度或 TTA 时才算对症下药。3. 把 COCO JSON 转成 YOLO 格式转换脚本与四个边界坑3.1 为什么要转 YOLO 格式COCO 格式虽然生态好但实际训练时我通常还是会转到 YOLO 的 txt 标签格式。原因很直接YOLO 系列的训练生态最省心yolov8命令一行就能跑起来数据增强、anchor 策略、多尺度训练都是内建好的不需要像 Detectron2 那样手动配一堆参数。而且 YOLO 的标签是纯文本每张图一个 txt排查单张图的标注问题比在一个巨大的 JSON 里翻方便得多。另外YOLO 格式是归一化坐标对图像分辨率不敏感训练时无论原图是 6000×4000 还是 1280×720都能直接喂进网络。这个数据集里我注意到images字段中的宽高并不完全一致转 YOLO 格式时每张图独立归一化反而更稳。3.2 转换脚本从 JSON 到 txt 的完整实现import json import os from pathlib import Path def coco_to_yolo(coco_json_path, output_dir, class_mapping): 将COCO JSON转换为YOLO txt标签。 - coco_json_path: COCO标注文件路径 - output_dir: 输出目录会生成 labels 子目录 - class_mapping: 旧类别ID到新连续ID的映射, 如 {1: 0, 2: 1, 3: 2} with open(coco_json_path, r, encodingutf-8) as f: coco json.load(f) # 建立 image_id 到图片信息的索引 img_id_to_info {} for img in coco[images]: img_id_to_info[img[id]] img # 建立 image_id 到标注列表的索引 img_id_to_anns {} for ann in coco[annotations]: img_id_to_anns.setdefault(ann[image_id], []).append(ann) labels_dir Path(output_dir) / labels labels_dir.mkdir(parentsTrue, exist_okTrue) for img_id, img_info in img_id_to_info.items(): img_w, img_h img_info[width], img_info[height] # YOLO格式每行一个目标: class_id x_center y_center width height均归一化 lines [] for ann in img_id_to_anns.get(img_id, []): if ann[category_id] not in class_mapping: continue # 忽略不在映射表中的类别 new_id class_mapping[ann[category_id]] x, y, w, h ann[bbox] # COCO bbox是 [x, y, width, height] # 归一化并转换为中心点坐标 x_center (x w / 2) / img_w y_center (y h / 2) / img_h yolo_w w / img_w yolo_h h / img_h # 坐标裁剪到 [0,1]防止浮点误差越界 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) yolo_w min(max(yolo_w, 0.0), 1.0) yolo_h min(max(yolo_h, 0.0), 1.0) lines.append(f{new_id} {x_center:.6f} {y_center:.6f} {yolo_w:.6f} {yolo_h:.6f}) # 根据图片文件名生成对应的txt标签 stem Path(img_info[file_name]).stem label_path labels_dir / f{stem}.txt label_path.write_text(\n.join(lines), encodingutf-8) print(f已处理 {img_info[file_name]} - {len(lines)} 个目标) coco_to_yolo( annotations.json, yolo_dataset, class_mapping{1: 0, 2: 1, 3: 2, 4: 3, 5: 4, 6: 5, 7: 6} )这里有几个逻辑值得单独说class_mapping参数COCO JSON 里的category_id不一定从 0 开始且连续。YOLO 要求类别 ID 是从 0 开始的连续整数所以转换时必须手动建立映射把原类别 ID 重排成 0-6。写死映射表比自动推导更安全——自动推导万一某个类别在某个子 JSON 中缺失映射就乱了。归一化越界裁剪x_center等值理论上一定在 [0,1] 内但浮点除法偶尔会算出 0.999999 或 1.000001 这种临界值YOLO 训练时偶尔会因此报错。这个min/max裁剪是防御性代码我的习惯是永远加上。空标注图片某些图片可能没有任何标注对象这种图生成的 txt 是空文件。YOLO 训练时空标签文件对应的是负样本背景图如果你不想要这种图在转换时就需要额外过滤。3.3 转换后必须检查的三件事转换脚本跑完不算完我一般会立刻做三个检查第一数一下生成的 txt 文件数量是否等于有标注的图片数量。第二抽查几张图的 txt 内容手动确认类别 ID 是否在 0-6 范围内。第三把 YOLO 的 txt 坐标反算回像素坐标画到图上人工核对一遍——这一步光靠肉眼猜很容易漏掉坐标错位的问题我后面单独写一节讲可视化校验。这里有个关键点COCO 的 bbox 是[x, y, width, height]x、y 是左上角坐标YOLO 需要的是[x_center, y_center, width, height]两者差一次坐标变换。新手最容易在这里翻车直接套用转换代码而没做中心点换算训练出来的模型预测框会整体偏移一半的位置而且不会报错。4. 标注质量校验把 COCO 框和掩码画回原图4.1 可视化校验脚本不画图的标注检查都是耍流氓标注数据最怕的不是格式错而是内容错——框偏移、类别标反、多边形点顺序错乱这些问题光看 JSON 根本发现不了。我拿到这份数据集后的第一件事就是写可视化脚本把所有的框和多边形画到原图上逐张翻一遍。import json import cv2 import os import numpy as np from pathlib import Path def visualize_annotations(coco_json_path, image_root, output_root, categories_of_interestNone): 把COCO标注画回原图用于人工质检。 - coco_json_path: COCO标注文件 - image_root: 原图所在目录 - output_root: 可视化结果输出目录 - categories_of_interest: 只画指定类别ID列表None则画全部 with open(coco_json_path, r, encodingutf-8) as f: coco json.load(f) # 类别ID - 名称和颜色 id_to_name {cat[id]: cat[name] for cat in coco[categories]} # 给每个类别分配一个BGR颜色方便区分 np.random.seed(42) id_to_color {cat[id]: tuple(np.random.randint(0, 255, 3).tolist()) for cat in coco[categories]} # image_id - 图片路径 id_to_path {img[id]: os.path.join(image_root, img[file_name]) for img in coco[images]} # image_id - 标注列表 img_to_anns {} for ann in coco[annotations]: img_to_anns.setdefault(ann[image_id], []).append(ann) out_dir Path(output_root) out_dir.mkdir(parentsTrue, exist_okTrue) for img_id, img_path in id_to_path.items(): img cv2.imread(img_path) if img is None: print(f警告: 图片读取失败: {img_path}) continue h, w img.shape[:2] for ann in img_to_anns.get(img_id, []): cat_id ann[category_id] if categories_of_interest and cat_id not in categories_of_interest: continue name id_to_name.get(cat_id, str(cat_id)) color id_to_color[cat_id] # 画bbox x, y, bw, bh [int(v) for v in ann[bbox]] cv2.rectangle(img, (x, y), (x bw, y bh), color, 3) # 画segmentation多边形COCO允许存在多段多边形 for seg in ann[segmentation]: pts np.array(seg, dtypenp.float32).reshape(-1, 2) cv2.polylines(img, [pts.astype(np.int32)], True, color, 2) # 在框的左上角写类别名 label f{name} cv2.putText(img, label, (x, max(0, y - 10)), cv2.FONT_HERSHEY_SIMPLEX, 1.2, color, 2) out_path out_dir / Path(img_path).name cv2.imwrite(str(out_path), img) visualize_annotations(annotations.json, images, vis_check)这段脚本的核心逻辑是对每张图读取所有标注把 bbox 画成矩形、把 segmentation 画成多边形轮廓、把类别名写到框上方。为什么矩形和多边形都画因为很多工具的标注只画框不画多边形或者多边形点顺序乱导致可视化时形状错乱两种图叠在一起一眼就能看出问题。4.2 目检时重点看什么可视化跑完会得到 2249 张带标注叠加的图片不可能逐张细看但按下面的优先级抽查能覆盖 80% 以上的问题优先看小目标类别雷击和表面裂纹是小目标如果标注框明显大于目标本体或者框内部有一大半是背景说明标注员框得粗这类标注要让模型学的话会引入大量背景噪声。看类别混淆边界漏油和污垢、侵蚀和正常磨损之间的边界如果同一张图里两个同类缺陷被标成了不同类别这是最大的标注灾难会直接拉低模型的区分能力。看被截断的目标叶片边缘的缺陷如果被切了一半标注框是覆盖可见部分还是延伸到图外不同标注员的处理方式不一样。YOLO 训练时坐标越界的框有时会被自动裁剪有时不会最好在转换时统一处理。如果你没时间全看我建议优先检查排水孔受损和雷击这两类各抽 30-50 张就够了。因为这两类是典型的小目标最容易出标注质量问题。5. 避坑与排查这批叶片数据最容易踩的五个坑5.1 图片读取失败根因是文件名里的特殊字符现象跑可视化脚本时部分图片报图片读取失败控制台warning刷屏。原因文件名里包含_jpg.rf.这种带点号的中间段某些老旧代码里用os.path.splitext取扩展名时会把.rf.721c...jpg当成扩展名或者在使用中文路径时编码出问题。我查了一下这批图片的文件名里还有_jpg和.jpg两段如果拼接路径时用了错误的文件名cv2.imread会静默返回None而不是报错非常隐蔽。解决代码中加严格的防御判断——cv2.imread返回None时打印完整路径并跳过同时统一用Path对象拼接路径避免字符串拼接导致的路径分隔符问题。另外如果图片和标注文件在不同目录务必检查file_name里是否包含子目录前缀。5.2 类别 ID 跳号导致 YOLO 训练类别数对不上现象训练 YOLOv8 时设nc7但模型报错或训练时 loss 异常检查发现数据集的 txt 标签里出现了类别 ID 7。原因COCO JSON 里categories的id不是按 0-6 排列的可能是[1, 3, 5, 7, ...]这种跳号的排列。直接拿原始category_id写入 txt类别编号就超过了nc-1的范围。解决转换脚本里必须做 ID 重映射把原始 ID 映射到0..nc-1连续区间。做完后跑一条命令检查标签分布# 统计所有txt标签中出现的类别ID和数量 cat yolo_dataset/labels/*.txt | awk {print $1} | sort | uniq -c如果输出的 ID 最大值比nc-1大说明映射没做彻底返回去检查class_mapping参数。5.3 同一叶片的数据同时出现在训练集和验证集指标虚高现象模型在验证集上 mAP 很高但实际部署到新叶片上效果断崖式下跌。原因同一个BladeA的图片之间非常相似如果划分数据集时按单张图完全随机分同一叶片的图片会同时进入训练集和验证集验证集失去独立性等于模型「记住了」叶片纹理和背景而不是学到了缺陷本身的特征。解决按叶片编号分组划分数据。比如把MAN320BladeB、ASP47BladeC这类 ID 作为分组单位同一个叶片的所有图片全部进入训练集或全部进入验证集绝不能拆开。划分代码可以基于文件名里的机型前缀和叶片编号做 group split而不是直接train_test_split随机切。5.4 图像分辨率不一致导致归一化坐标错误现象训练时某些 batch 的 loss 突然异常甚至出现 NaN但检查标签坐标都在 [0,1] 范围内。原因这个数据集的图片尺寸并不统一部分图是 6000×4000 高分辨率部分是 1280×720 左右。如果转 YOLO 时用了一张统一尺寸做分母比如误用了数据集的平均宽度坐标归一化就会出错另一个坑是 YOLOv8 训练时imgsz640会把大图缩得很厉害小目标直接消失。解决坐标归一化必须用每张图自己的width和height这也是前面转换脚本里用img_info[width]而不是全局变量的原因。训练时用imgsz1280或开rectTrue按长边 batch 分组能明显缓解分辨率不一致的影响。5.5 COCO 的 segmentation 多边形坐标超出图像边界现象可视化检查时某些框和多边形画到了图像外形状不完整。原因标注工具导出的多边形有些是闭合到图像边缘的坐标等于图像宽高值有些标注员把框画到了图外一点COCO 格式允许这类越界坐标存在。训练时如果直接拿越界坐标做裁剪增强random_perspective这类增强操作有时会报错或产生无效框。解决转换前统一把 bbox 和多边形的坐标裁剪到[0, width]和[0, height]范围内对宽或高小于阈值的框比如小于 1 像素直接丢弃。这个操作要在格式转换之前做转成 YOLO 后再裁剪会引入比例误差。6. 进阶用法数据切片分析 多尺度训练提升小目标检出率如果你已经把数据集跑通了基础训练下一步值得做的事情是针对性提升雷击和表面裂纹这类小目标的检出率。我的做法分两步。第一步是按叶片编号做数据切片分析。统计每个叶片上每类缺陷的数量和像素面积分布确认哪些叶片是「重灾区」。这一步用 Pandas 跑起来很直接解析文件名里的叶片 ID关联 COCO 标注里的 bbox 面积按叶片分组聚合。分析结果能帮你决定要不要做按叶片的过采样——比如某些叶片裂纹多但实例总数少单独拎出来做二次微调数据集。第二步是针对性调参。训练时开mosaic1.0、mixup0.2增强把imgsz从默认 640 提到 960 或 1280小目标在特征图上的响应会明显增强。我对这个数据集的经验值是6000×4000 的高分图缩到 640 训练雷击目标的像素宽度往往只剩 10-15 个像素几乎不可辨认提到 1280 之后能到 20-30 像素配合多尺度测试TTA能捞回相当一部分漏检。类别不均衡方面如果污垢和侵蚀两类样本明显多于雷击和排水孔受损给损失函数加上focal_loss或按类别权重重加权会比默认的 BCE 更稳。这套流程做完我的习惯是拿着模型去跑之前标注质量校验时挑出来的「疑难图」——那些框偏移、类别边界模糊的样本——如果模型能稳定检出才觉得这个数据集的潜力真正被榨干了。从那以后我每次拿到新的叶片缺陷数据集都会先跑一遍切片分析加可视化校验的流程再谈训练这个习惯帮我少走了不少弯路。希望这篇拆解对你有用。本文还有配套的精品资源点击获取