简介:面向计算机视觉目标检测研究与实践的风力叶片缺陷检测数据集,包含 2249 张缺陷图像,覆盖排水孔受损、雷击、污垢、漏油、PU 胶带、表面裂纹、侵蚀等多类典型风电叶片缺陷场景;图像采集自不同风机型号与叶片部位,缺陷形态多样,贴合真实运维环境。数据采用 COCO JSON 格式标注,可直接适配 MMDetection、Detectron2、YOLO 等主流目标检测框架,适合风电智能巡检、工业缺陷检测方向的研究人员、算法工程师和相关专业学生使用。资源包共 2000 个文件,其中 1997 张 JPG 图像与 3 个 JSON 标注文件,总大小约 74.32 MB,结构简洁,下载后即可快速开展训练实验。目前已有 818 人浏览学习。借助这套数据,既可完成缺陷识别模型的数据准备与训练验证,也能围绕叶片缺陷分类、定位等任务展开算法对比研究,为风电运维自动化提供可靠的数据支撑。
1. 风力叶片缺陷检测数据集:2249张COCO标注图到底能做什么
做风机运维的朋友应该都有体会:叶片巡检拍回来的照片动辄几千上万张,靠人眼一张张翻,漏检是迟早的事。这个名为“风力叶片缺陷检测数据集”的资源,用2249张真实叶片图像,统一转成COCO JSON格式标注,覆盖排水孔受损、雷击、污垢、漏油、PU胶带、表面裂纹、侵蚀七类缺陷。它的价值不在“图多”,而在“缺陷类型踩得很准”——这几类恰好是风场巡检报告里出现频率最高、也最容易被漏检的问题。
这个数据集适合两类人:一是刚接触缺陷检测、想拿一套带标准COCO标注的数据跑通训练流程的开发者;二是已经在做风电视觉巡检,但苦于没有高质量标注数据来验证模型选型和训练参数的算法工程师。2249张图不算大,但配合COCO格式,能直接喂给MMDetection、Detectron2、YOLO这些主流框架。下面从缺陷特征、COCO结构、训练配置到踩坑记录,按一条完整落地路径拆开讲。
2. 七类缺陷逐个拆解:视觉特征、检测难点与类别平衡真相
2.1 排水孔受损、漏油和PU胶带:小而局部,先厘清“看哪里”
排水孔受损通常发生在叶片根部或腹板附近,表现是孔洞边缘开裂、变形,面积很小,在一张2048分辨率的巡检图里往往只占几十个像素。漏油则是叶片内部轴承或液压系统渗漏后,在叶片表面形成的深色油渍,形态不规则,边缘有流淌痕迹,颜色和阴影容易混淆。PU胶带是叶片前缘或接缝处的保护胶带,缺陷形态是翘起、剥落、边缘破损,属于典型的细长条状目标。
这三类缺陷的共同点是“局部且面积小”。检测模型对它们的召回率高低,很大程度取决于标注质量——如果标注框把油渍和旁边的阴影一起框进去,模型学到的就是“深色区域”,而不是“油渍”。我在实际项目中遇到最典型的情况是:漏油和污垢在颜色特征上几乎一样,唯一的分界线是位置——漏油多在接缝、螺栓孔附近,污垢分布更随机。这个先验知识在后期处理预测结果时很有用。
2.2 雷击、表面裂纹和侵蚀:线状与面状缺陷为什么是检测的天敌
雷击缺陷是叶片被雷电击中后留下的树枝状烧蚀痕迹,通常从接闪器或叶尖向外辐射,颜色发黑,形态是典型的“长线加分支”。表面裂纹是细长的线性缺陷,宽度可能只有1到3个像素,长度却有几百像素。侵蚀则表现为叶片表面材料流失,呈片状粗糙区域,边界不清晰。
这两类(线状和面状)是缺陷检测里最难啃的骨头。线状缺陷的问题在于:检测框是矩形,一条斜跨叶片的裂纹,用bbox表示会框进大量背景,导致IoU计算失真;如果标注人员把裂纹拆成多段小框,模型又学不到“一整条裂纹”的语义。面状缺陷的问题相反——边界模糊导致标注一致性差,同一个侵蚀区域,两个标注员可能画出完全不同的轮廓。这个数据集如果对裂纹和侵蚀使用了polygon(多边形)而非纯bbox标注,对训练Mask R-CNN这类实例分割模型会有明显优势。
2.3 污垢:最容易标注错的一类,拉开类别间距离
污垢这个类别很有意思——它本身不难识别,但却是标注错误率最高的一类。原因是污垢的判定标准高度主观:一块深色附着物,有人标“污垢”,有人认为是“漏油”,还有人直接忽略。如果数据集的标注规范没有明确“颜色阈值、位置、面积下限”这些判据,模型学到的类别边界就是模糊的,训练时loss降不下去,推理时漏油和污垢互相误报。
处理这类问题时,我的做法是先把易混类别列成对比表,标注前统一判据。比如“油渍颜色更深、边缘更光滑且有流淌方向,污垢偏颗粒感、边缘毛糙”。这个原则同样适用于模型预测后的后处理——当漏油和污垢置信度都低于阈值时,优先输出位置更靠近接缝的类别。
2.4 2249张图的体量评估:类别分布与可训练性判断
2249张图的体量处于“能训练、但容错率低”的区间。如果七类缺陷分布相对均匀,每类大约300张,对单阶段检测器(如YOLO)来说刚够用;但如果存在严重的类别不均衡——比如污垢占800张、漏油只有40张——那漏油类的召回率基本不会好看。拿到数据的第一件事不是跑模型,而是统计类别分布和实例面积分布。
另一个隐含问题是图像尺寸。叶片巡检图通常是4000x3000甚至更高分辨率,而训练时为了塞进显卡,主流做法是缩放到1280或1536。这个缩放过程对裂纹、排水孔受损这类小目标极其不友好——原图里20像素宽的裂纹,缩放到1280后可能只剩6像素,直接降级为“极小目标”。所以训练前要确认标注面积的中位数,如果大量实例面积小于32x32像素,就需要考虑用原图尺寸训练或使用切图策略。
3. COCO JSON的数据结构:用pycocotools把标注吃透
3.1 COCO标注的五段式结构:images、categories、annotations怎么对齐
COCO JSON是目标检测领域最通用的标注格式之一,它的顶层是一个字典,包含info、licenses、images、annotations、categories五个字段。其中真正决定检测任务的是后三个。
images:每张图的id、file_name、width、height。注意id是全局唯一的,训练脚本靠它关联标注。annotations:每个实例一条记录,包含id、image_id、category_id、bbox、area、segmentation、iscrowd。bbox是[x, y, width, height]格式,左上角起点;segmentation对于多边形标注是一组坐标点的扁平列表。categories:每个类别一条记录,包含id、name。这里的id从1开始(COCO官方约定),而许多框架(如YOLO)需要类别ID从0开始连续分布,转换时要重映射。
这三者的对齐关系是:annotations.image_id指向images.id,annotations.category_id指向categories.id。如果标注工具导出的JSON里category_id有跳号(比如1、2、5、7),直接训练必然报错或分类错乱。拿到数据集第一步,先把这三个字段打印出来逐项核对。
3.2 用pycocotools读取数据集:统计类别、面积分布和单张标注数
我一般拿到COCO标注后,第一件事不是训练,而是写一个统计脚本,把类别实例数、面积分布、每张图的标注密度拉出来。这能快速暴露数据集的真实问题。下面这个脚本可以直接套用:
from pycocotools.coco import COCO import numpy as np coco = COCO('annotations/instances_train.json') # 1. 统计类别实例数 cat_ids = coco.getCatIds() cats = coco.loadCats(cat_ids) print('类别列表:', [(c['id'], c['name']) for c in cats]) for cat in cats: ann_ids = coco.getAnnIds(catIds=cat['id']) anns = coco.loadAnns(ann_ids) areas = np.array([a['area'] for a in anns]) print(f"类别 {cat['name']}: {len(anns)} 个实例, " f"面积中位数 {np.median(areas):.1f}, " f"最小 {areas.min():.1f}, 最大 {areas.max():.1f}") # 2. 统计每张图的标注数 img_ids = coco.getImgIds() ann_counts = [] for img_id in img_ids: ids = coco.getAnnIds(imgIds=img_id) ann_counts.append(len(ids)) print(f"图像总数: {len(img_ids)}") print(f"每图标注数: 中位数 {np.median(ann_counts)}, " f"最大 {np.max(ann_counts)}, " f"空图(无标注) {sum(1 for c in ann_counts if c == 0)} 张")这段代码的用途有两个:一是确认每类实例数量,判断是否需要类别重采样;二是看面积中位数,判断小目标占比。areas.min()如果出现大量小于100的实例,说明这些小目标在缩放后可能直接消失。空图数量也需要关注——如果训练集里有很多无标注图,模型会把无缺陷区域也学进特征分布,干扰收敛。
3.3 bbox与segmentation并存:细长缺陷该信哪个
COCO格式允许同一实例同时有bbox和segmentation。对于裂纹、雷击这类细长缺陷,两者差异会非常大。一条长300像素、宽3像素的裂纹,它的多边形面积大约是900像素,但bbox面积是9000像素——差了一个数量级。如果训练时用bbox作为回归目标,模型会被迫把大量背景区域学进特征;如果模型本身带mask分支,用polygon计算损失会更精确。
这个数据集的标题明确了是COCO JSON格式,但没说是否包含polygon标注。跑训练之前,建议用脚本检查annotations里的segmentation字段是空列表还是真实多边形坐标。如果是空的,那么只能用bbox做检测,细长缺陷的AP(平均精度)天花板会比较低;如果包含polygon,优先考虑带mask分支的模型,比如Mask R-CNN或Cascade Mask R-CNN。
另外还要检查area字段和bbox导出的面积是否一致。有些标注工具在导出COCO JSON时,area写的是bbox面积而不是polygon面积,这会影响pycocotools评估时的AP计算。遇到这种情况,要么重算area,要么在训练时忽略这个字段,让框架自己从segmentation计算。
3.4 数据划分的隐藏规则:按叶片分还是按图像分
COCO格式本身不规定train/val怎么分,但划分方式直接影响评估结果的可信度。风力叶片巡检有一个特点:同一片叶子的不同照片高度相似,背景、光照、叶片纹理几乎一致。如果简单的随机划分,同一叶片的图像可能同时落在训练集和验证集,模型在验证集上的表现会虚高——这不是模型泛化能力强,而是“背题”了。
正确做法是按叶片ID分组划分。如果原始数据文件名里带叶片编号(比如blade_03_shot_012.jpg),就按编号分组,确保同一叶片的所有图片只在训练集或只在验证集。如果文件名没有编号,只能从图像内容推断——叶片颜色、纹理、背景特征接近的图群大概率来自同一叶片。标题里的数据集没有说明文件命名规则,这个需要拿到数据后自己验证。划分建议是:训练集占80%、验证集20%,如果整体只有2249张图,验证集450张左右是合理区间。
4. 从COCO JSON到模型训练:用YOLOv8还是Mask R-CNN
4.1 缺陷类型决定检测头:box头与mask头怎么选
选模型前先回答一个问题:你的交付物是“框出缺陷位置”还是“标出缺陷轮廓”?如果是风机巡检的初步筛查,只要知道叶片哪一段有问题,YOLO系列就够用,部署也方便;如果后续需要根据裂纹长度、雷击面积做维修优先级判断,那必须用带mask分支的模型,否则细长缺陷的面积估算会严重失真。
这个数据集包含的七类缺陷里,雷击、表面裂纹、侵蚀三类如果用bbox做评估,AP会普遍偏低,不是模型不行,是bbox表达细长目标本身就吃亏。我的建议是:如果数据集里带polygon标注,优先选Cascade Mask R-CNN或Mask R-CNN,backbone用ResNet-50以上;如果只有bbox标注,YOLOv8或YOLO11是性价比更高的选择。MMDetection和Ultralytics YOLO都支持直接读取COCO JSON,前者要改配置文件,后者有现成的训练脚本。
4.2 把COCO JSON转成YOLO格式:脚本与三个关键注意点
YOLO官方训练接口接受的是每张图一个txt文件的格式,而不是COCO JSON。用Ultralytics框架时,需要先把COCO JSON转成YOLO格式。转换脚本的核心逻辑如下:
import json import os from pathlib import Path def coco_to_yolo(json_path, img_dir, out_dir, cat_id_map): with open(json_path, 'r') as f: data = json.load(f) img_id_to_info = {img['id']: img for img in data['images']} anns_by_img = {} for ann in data['annotations']: img_id = ann['image_id'] anns_by_img.setdefault(img_id, []).append(ann) for img_id, anns in anns_by_img.items(): img_info = img_id_to_info[img_id] w, h = img_info['width'], img_info['height'] txt_name = Path(img_info['file_name']).stem + '.txt' lines = [] for ann in anns: new_cat_id = cat_id_map[ann['category_id']] bbox = ann['bbox'] # [x, y, w, h],左上角+宽高 cx = (bbox[0] + bbox[2] / 2) / w cy = (bbox[1] + bbox[3] / 2) / h bw = bbox[2] / w bh = bbox[3] / h cx = min(max(cx, 0.0), 1.0) cy = min(max(cy, 0.0), 1.0) bw = min(max(bw, 0.0), 1.0) bh = min(max(bh, 0.0), 1.0) lines.append(f"{new_cat_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") with open(Path(out_dir) / txt_name, 'w') as f: f.write('\n'.join(lines))这段代码有三个关键注意点。第一,cat_id_map必须存在——COCO的类别ID可能从1开始或跳号,而YOLO要求从0开始连续分布,直接把ann['category_id']写进txt会导致训练时类别错乱。第二,坐标全部归一化到[0,1]区间,超出边界的要夹取,因为有些标注框会略微超出图像边界,不处理的话会在损失计算时报错或产生NaN。第三,输出的txt文件名必须和图像文件名严格一致(仅扩展名不同),且所有txt放在同一个目录下,Ultralytics会按这个目录扫描对应图片。
如果数据集里带polygon标注,还可以生成YOLO的segmentation格式(即每个目标一行,类别ID后跟归一化的多边形坐标点)。但要注意YOLO segmentation格式要求多边形闭合且不交叉,COCO的polygon转换后偶尔会出现坐标越界导致训练挂掉,建议先用上面的bbox格式跑通基线。
4.3 训练参数设置:图像尺寸、mosaic增强、学习率调度的取舍
用Ultralytics YOLO训练这个数据集,我的推荐配置如下:
yolo detect train \ data=blade.yaml \ model=yolo11m.pt \ imgsz=1280 \ batch=8 \ epochs=150 \ mosaic=0.0 \ cos_lr=True \ lr0=0.005 \ fliplr=0.5逐项说明参数背后的逻辑。imgsz=1280是这组参数里最关键的一项——叶片缺陷里的裂纹、排水孔受损属于小目标,如果用默认的640,2249张图里的细长目标会缩小到难以辨识,mAP会断崖式下跌。显存不够时优先切图(把原图切成四个1024的patch分别训练),而不是降低imgsz。mosaic=0.0是另一个容易被忽略的坑——Mosaic增强会把四张图拼在一起,裂纹和雷击这类贯穿性目标在拼接边界处会被硬生生截断,模型学到的是“半截裂纹”,推理时反而漏检。如果在训练中后期想加一点Mosaic提高鲁棒性,最多设到0.3,而且要配合close_mosaic=10让最后10个epoch回归正常样本分布。
cos_lr=True配lr0=0.005是2249张小数据集的稳妥组合。数据量小,初始学习率太高会导致前几个epoch loss直接炸掉;用cosine退火可以让模型在后半程稳定收敛。batch=8在单卡16GB显存下跑1280分辨率是上限,batch再大就得梯度累积。还有一个没有写进命令但值得尝试的项:scale=0.5。叶片缺陷的尺度差异极大,尺度扰动从0.5开始,能模拟巡检距离不同带来的目标大小变化。
4.4 评价指标别只看mAP:针对细长缺陷加看mask AP和小目标AP
训练完看指标时,常规的mAP@0.5和mAP@0.5:0.95只是第一层。对风力叶片缺陷,我还会额外看三个维度:
- 按类别分组的AP。七类分别报告,不要只看均值——均值很可能被污垢这类大目标拉高,掩盖裂纹和雷击的低AP。
- 按面积分组的AP。pycocotools会输出
small、medium、large三个面积档的AP。如果small档AP明显低于medium,优先优化输入分辨率和锚框设置。 - mask AP(如果模型是Mask R-CNN)。mask AP比box AP更能反映细长目标的分割质量,因为mask对轮廓敏感,禁止bbox那种“框住背景也算对”的宽松评估。
如果训练框架是Ultralytics,训练结束后用yolo detect val model=runs/train/weights/best.pt data=blade.yaml就能输出按类别的AP表。看到裂纹AP低于0.3是正常现象,别急着调模型,先回到数据层面检查标注的一致性和面积分布,很多时候是标注把裂纹拆碎了。
5. 避坑记录:七类缺陷标注与训练的5个血泪雷区
5.1 雷击痕迹被拆成多段标注,模型把一条缺陷学成了三块
现象:训练后推理,模型在一条完整雷击通道上输出了两三个零散的框,置信度还不低。验证集上雷击的recall看着还行,但框的位置东一块西一块,没法用。
原因:标注工具里画多边形太费劲,标注员用bbox沿着雷击通道的可见分段标了三段。模型学到的模式是“黑色团块”,而不是“树枝状烧蚀带”。
解决:标注规范里明确规定——雷击、裂纹这类连通缺陷必须用一个实例标完整,不允许分段。polygon画不准的,至少用倾斜框或一个cover整条缺陷的大bbox。如果数据集已经标完,只能在训练后处理里设定同类框合并规则,IoU大于0.3的同类预测框合并为一个。
5.2 漏油和污垢互相误报:类别边界不清晰,模型学了个寂寞
现象:验证集上漏油的精确率只有30%,一半的漏油预测框实际是污垢。反过来,污垢的召回里也混着漏油。
原因:标注阶段没有统一判据。同一块深色附着物,A标注员标了漏油,B标注员标了污垢。模型在特征空间里找不到这两个类别的稳定分界线。
解决:这种问题靠调模型参数没用,只能回到数据。我当时的做法是拉取所有预测置信度在0.4到0.7之间的误报样本,逐张review,把标注明显错误的样本重新标注,并明确判据:油渍边缘更光滑、有流淌下垂趋势、位置靠近接缝或螺栓孔;污垢边缘毛糙、分布更随机、颜色偏灰。同时给这两类在损失函数里加class-wise weight,让模型更关注难分的漏油类。
5.3 2249张图里漏油只有35张:少数类直接欠拟合
现象:训练50个epoch后,漏油类在验证集上的AP是0.1,几乎等于没学。其他类别AP都在0.5以上。
原因:类别严重不均衡。漏油样本少,且在训练过程中被大类的梯度淹没,模型倾向于把所有深色区域都预测成污垢。
解决:两个方向并行。一是数据层面——如果有原始图像素材,通过亮度调整、对比度增强合成漏油样本,但要小心不要破坏标注边界;如果没有,用MixUp在训练时把漏油实例贴到其他图上。二是训练层面——在loss里给漏油类设置更高的权重,比如cls_loss_weight按类别实例数的倒数设置。400张以上的类别权重为1.0,35张的类别权重设为3.0到5.0。还有一个经验是:对样本少的类别先冻结backbone只训练head 20个epoch,再解冻全模型训练,能避免头部网络把小类别的梯度带偏。
5.4 训练/验证随机划分,验证集指标虚高0.15
现象:模型验证mAP@0.5有0.72,上线后实际表现只有0.55左右。排查发现同一叶片的图像在train和val里都有。
原因:随机划分数据时,同一叶片不同角度、不同距离的照片被分到了两个集合。模型在训练时已经“见过”这个叶片的纹理、背景和光照条件,验证时自然表现好。
解决:按叶片分组划分。文件名里带叶片编号的直接按编号分组;没有编号的,用图像哈希聚类或人工看缩略图分桶。划分后跑一个快速trick验证——把val集里每个叶片的图像单独跑一遍,如果某个叶片的AP明显高于其他叶片,大概率存在泄漏。这个坑在2249张小数据集上后果更严重,因为一张训练图的影响占比更大。
5.5 标注类别名用了中文,训练直接报编码错误
现象:用Ultralytics训练时,报错Unable to read labels或者类别名乱码,模型能训练但输出标签是乱码。
原因:COCO JSON的categories.name写的是“表面裂纹”“漏油”等中文字符串,YOLO在读取标签文件时依赖UTF-8编码,Windows环境下默认GBK编码导致解析失败。
解决:统一把类别名改成英文或拼音,如surface_crack、oil_leak。数据集的yaml配置文件里names字段也要对应修改。这不是这个数据集独有的问题,但我在自己项目里见过太多次,处理大批量数据前先把类别名规范好,能省不少事。
6. 验证数据集质量的三种手段与两类吃亏教训
6.1 可视化叠加:第一个晚上必须做的检查
拿到COCO JSON后,第一晚别急着训练,先做可视化。用pycocotools把每张图的bbox和segmentation画出来叠加在原图上,重点看三类问题:一是bbox有没有明显偏移,某些标注工具导出的坐标原点可能是图像中心而不是左上角,画出来会发现所有框整体偏移;二是细长缺陷的polygon是否贴合轮廓,如果有大量锯齿或跨越背景区域,说明标注时缩放比例没设置对;三是漏油和污垢两类混标的情况,肉眼扫一遍基本能发现。
写个简单的可视化脚本,用OpenCV或者matplotlib把图和标注一起输出,前50张人工过一遍比跑100个epoch更有价值。这一步是血泪经验——我给客户做风电叶片项目时,因为跳过这一步,等训练完才发现有三分之一标注框的宽高写反了,全部返工。
6.2 用混淆矩阵定位错检:雷击和裂纹怎么分开
训练结束后用Ultralytics的confusion_matrix.png输出看一眼,通常会发现雷击和表面裂纹之间存在系统性混淆。原因不难理解:雷击的末端分支和细裂纹在局部特征上高度相似。解决思路有两个:一是给雷击类单独加一个“从叶尖或接闪器出发”的位置特征,这属于后处理逻辑,在推理代码里判断预测框与叶尖的距离;二是在数据层面补充雷击的完整形态样本,让模型学到“长距离黑色通道”而不是“局部黑色线段”。
混淆矩阵还能暴露另一个问题:如果模型把正常的叶片边缘误检为裂纹,说明训练数据里缺少硬负样本——叶片本身就有天然的纹理线条,和裂纹在灰度特征上接近。这时可以专门挑选一些无缺陷的叶片图像加入训练集,标注为空图,让模型学会“纹理不等于裂纹”。
6.3 按叶片分组的K折验证:小数据集的止损手段
2249张图做单次train/val划分,结果波动可能很大。我更推荐做3折或5折的按叶片分组交叉验证:每折换一个叶片组做验证,最终报告各折AP的均值和标准差。这样得到的性能评估更可信,也顺便检验了数据标注的一致性。如果某一折的AP明显低于其他折,大概率是那组叶片里存在标注噪声或样本难度偏高。
时间不够的话,至少做一次“反向验证”——把val损失最高的50张图拉出来逐张看,统计是标注错误、目标过小还是类别混淆。大多数情况下,这50张图能暴露数据集中80%的系统性问题。这个步骤投入产出比极高,胜过调三个epoch的学习率。
这个项目方向值不值得投入,我的判断是:2249张图的体量做技术验证和基线模型足够,但如果要做严谨的风场级部署,至少需要再补充两个维度的数据——更多风机型号和不同光照季节条件下的叶片图像。我自己的教训是:第一次拿到类似数据集时太急着调模型,花了三周把mAP从0.58调到0.63,后来发现换掉一个错误标注的类别ID,指标直接涨了5个点。数据质量永远是上限,模型只是在逼近这个上限。希望这个数据集的COCO结构能帮你少走一段我走过的弯路。
本文还有配套的精品资源,点击获取