ARTICLE DETAIL

资讯详情

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

风力涡轮机缺陷检测数据集实战:YOLOv8训练与COCO/YOLO/VOC格式转换

风力涡轮机缺陷检测数据集实战:YOLOv8训练与COCO/YOLO/VOC格式转换 简介面向风力涡轮机表面缺陷检测与智能巡检场景配套数据集提供18912张真实风机图像及对应精细标注标注格式同时覆盖YOLO、PASCAL VOC XML与COCO JSON三种主流方案能直接接入YOLO、Faster R-CNN、SSD等常见检测框架免去格式转换环节。数据集内容以实际风电场景为基础涵盖叶片裂痕、表面损伤等多类典型缺陷基于该数据集训练的模型可达到91.4%的识别准确率可作为算法调优的参考基准。资源以zip压缩包形式发布整体体积约584.4MB图片与标注文件按规范目录整理便于快速加载与划分训练集、验证集和测试集。目前已有354人学习下载适合计算机视觉学习者、风电运维工程师以及算法研发人员用于缺陷检测方案验证、模型训练和性能对比等任务。借助该数据集开发者可减少数据采集和清洗成本专注于网络结构设计与参数调优快速构建面向风机健康监测的AI解决方案。1. 风力涡轮机缺陷检测18912张标注图能把准确率推到多少风电场的例行巡检绝大多数时间都耗在对着无人机航拍图找裂纹、剥落和雷击痕迹上。人工看图的效率低而且不同人判读标准不统一——同一张叶片照片上午的巡检工说没事下午换个人就报了个损伤。这套风力涡轮机缺陷检测数据集就是为了把这类判断标准化18912张图片覆盖机舱、叶片、塔筒的常见损伤标注格式直接给到YOLO、Pascal VOC XML、COCO JSON三种不用自己拿标注工具从零开始框。按公开的模型训练结果验证集识别准确率在91.4%左右。适合做视觉检测的算法工程师、风电设备运维团队以及拿目标检测做毕设或科研的学生——下载后可以直接训练YOLOv8跑通全流程也可以只把标注文件拿去对比自己的转格式脚本写没写对。2. 数据底细与标注格式COCO JSON不是能打开就完事2.1 数据集构成与三类缺陷的目标拿到数据集先不要急着训练把目录结构和标注文件摸清楚后面能少踩一半的坑。按常见的风力涡轮机缺陷检测数据集组织方式这套数据大概率分为两个顶层目录一个放原图一个放标注文件。原图通常是JPG格式尺寸不统一——无人机航拍有近景有远景叶片裂纹这种细长目标在小分辨率下很容易被漏检这一点后面选训练参数时会直接影响imgsz的取值。缺陷类别通常是叶片裂纹、前缘腐蚀、雷击烧蚀、塔筒剥落这几类。每一类对应一个类别编号这个编号在三种标注格式里要保持一致才是数据集合格的标志。我的习惯是先写一个脚本统计每类的样本数看类别是否均衡。如果某类只有两三百张训练时就算整体准确率能到91.4%那一类的召回率大概率是塌的后续需要做针对性增强。2.2 读懂COCO JSON的四个关键字段COCO JSON标注文件是这套数据集里信息最完整的格式因为除了边界框bbox它还保留分割信息。第一次打开的人会发现很难直接看因为不同字段之间靠id关联而且JSON文件行数极多。不用慌只看四个字段就够# 读取COCO JSON并统计基础信息 import json ann_path annotations/instances_train.json with open(ann_path, r, encodingutf-8) as f: coco json.load(f) # 1. images字段每张图的文件名和尺寸 # 注意width/height在后续转YOLO归一化时会用到 for img in coco[images][:3]: print(fid{img[id]}, file{img[file_name]}, size{img[width]}x{img[height]}) # 2. categories字段类别id和名称的映射关系 # 注意真实id可能从1开始也可能不连续 print(coco[categories]) # 3. annotations字段核心标注信息 # bbox格式为[x, y, width, height]左上角坐标宽高 # area是分割区域的像素面积iscrowd标记是否为群体目标 ann coco[annotations][0] print(ann) # 4. 检查segmentation是多边形还是RLE if segmentation in ann: print(type(ann[segmentation]))这里有个关键点COCO的bbox是x、y、width、height其中x、y是框左上角的坐标不是中心点。很多第一次转YOLO格式的工程师在这里翻车——直接用x、y当中心点去归一化训练出来的框全部偏移。segmentation字段在本数据集里多数情况是RLE压缩格式如果你只做目标检测不玩分割这个字段可以忽略。categories里的id和你在YOLO data.yaml里写的类别顺序是两个维度的事。COCO允许id从1开始而YOLO的类别编号要求从0开始且连续。转换时一定要做一次重映射把真实id映射到0、1、2这样的连续序号否则训练时类别错乱。2.3 三种标注格式怎么选选择标注格式的本质是选择后续的工具链。YOLO格式是txt文件每行一个目标五个数字分别是类别索引、归一化中心点x、归一化中心点y、归一化宽度、归一化高度。用YOLOv5、YOLOv8训练直接用这种格式最省事缺点是肉眼不可读没法直接打开确认框的位置对不对。Pascal VOC XML是XML文件标注信息写在bndbox节点里包含xmin、ymin、xmax、ymax四个绝对像素坐标。优点是可以直接用开源工具标注或检查缺点是多一层解析且很多检测框架不直接吃XML还是得转成别的格式。COCO JSON是当前学术界和工业界兼容性最好的格式MMDetection等框架原生支持。缺点是人眼不可读且合并多个JSON文件时要处理id冲突。我的建议是拿YOLO格式去训模型拿VOC XML去手工抽检标注质量拿COCO JSON作为存档版本——需要做分割或换框架时再从这里派生。3. 三种格式互转的三条命令COCO到YOLO再到VOC的细节3.1 COCO JSON转YOLO归一化与category_id重映射数据集里同时给出三种格式按理说直接用就行。但你很难避免遇到需要自己做转换的场景——比如你以后自己采集了一批COCO格式数据要并进这个数据集一起训练。转换脚本的核心就两件事坐标归一化和类别编号重映射。# COCO JSON转YOLO txt # 读取COCO标注为每张图生成对应的txt文件 import os import json from tqdm import tqdm coco_path annotations/instances_train.json image_dir images/train out_label_dir labels/train os.makedirs(out_label_dir, exist_okTrue) # 构建COCO真实id到YOLO连续索引的映射 # 常见做法是通过categories顺序推导而不是直接拿id当索引 with open(coco_path, r, encodingutf-8) as f: coco json.load(f) cat_id_to_idx {} for idx, cat in enumerate(coco[categories]): cat_id_to_idx[cat[id]] idx print(category映射:, cat_id_to_idx) # 建立image_id到图片尺寸的查表 img_info {img[id]: img for img in coco[images]} # 按image_id聚合所有标注 anns_by_img {} for ann in coco[annotations]: anns_by_img.setdefault(ann[image_id], []).append(ann) # 逐图写YOLO格式 for img_id, anns in tqdm(anns_by_img.items()): img img_info[img_id] w, h img[width], img[height] txt_path os.path.join(out_label_dir, img[file_name].replace(.jpg, .txt)) lines [] for ann in anns: # 从真实id映射到连续索引 cls_idx cat_id_to_idx[ann[category_id]] x, y, bw, bh ann[bbox] if bw 0 or bh 0: # 空框或退化框直接跳过不要写进训练集 continue cx (x bw / 2) / w # 中心点x归一化 cy (y bh / 2) / h # 中心点y归一化 w_norm bw / w h_norm bh / h lines.append(f{cls_idx} {cx:.6f} {cy:.6f} {w_norm:.6f} {h_norm:.6f}) with open(txt_path, w, encodingutf-8) as f: f.write(\n.join(lines)) print(f完成共生成{len(anns_by_img)}个标签文件)这段脚本的逻辑先把COCO的categories列表按顺序读一遍建立真实id到索引的字典这一步就在解决类别编号不连续的问题。然后遍历每张图的标注把bbox的左上角坐标换算成中心点坐标再分别除以图片宽高做归一化。注意我特意跳过了bw或bh小于等于0的异常框这类框在标注边缘常出现混进训练集轻则报错重则让loss不收敛。参数上有个容易忽略的地方归一化保留6位小数就够了不要保留太长文件体积变大对精度没有帮助。另外txt文件的命名要和图片文件名严格一致包括大小写和扩展名替换规则。Windows上做过标注再拷到Linux服务器训练时经常出现大小写不一致导致标签读不到的情况。3.2 YOLO转Pascal VOC XML从txt到xml的字段映射反向转换的需求一般出现在你要用LabelImg检查、或者把数据送入只支持VOC格式的工具时。YOLO到VOC的转换比正向麻烦一些因为YOLO的txt里没有图片宽高信息你必须从对应的图片文件里读出来。# YOLO txt转Pascal VOC XML import os import cv2 import xml.etree.ElementTree as ET from xml.dom import minidom label_dir labels/train image_dir images/train xml_dir annotations_xml/train os.makedirs(xml_dir, exist_okTrue) # 类别名列表必须和训练时data.yaml的顺序一致 class_names [blade_crack, leading_edge_corrosion, tower_peel] def write_voc_xml(image_path, txt_path, xml_path): img cv2.imread(image_path) h, w img.shape[:2] depth 3 # RGB三通道若灰度图则写1 root ET.Element(annotation) ET.SubElement(root, filename).text os.path.basename(image_path) size ET.SubElement(root, size) ET.SubElement(size, width).text str(w) ET.SubElement(size, height).text str(h) ET.SubElement(size, depth).text str(depth) with open(txt_path, r, encodingutf-8) as f: for line in f: parts line.strip().split() cls_idx int(parts[0]) cx float(parts[1]) * w cy float(parts[2]) * h bw float(parts[3]) * w bh float(parts[4]) * h xmin max(0, int(cx - bw / 2)) ymin max(0, int(cy - bh / 2)) xmax min(w - 1, int(cx bw / 2)) ymax min(h - 1, int(cy bh / 2)) obj ET.SubElement(root, object) ET.SubElement(obj, name).text class_names[cls_idx] ET.SubElement(obj, pose).text Unspecified ET.SubElement(obj, truncated).text 0 ET.SubElement(obj, difficult).text 0 bndbox ET.SubElement(obj, bndbox) ET.SubElement(bndbox, xmin).text str(xmin) ET.SubElement(bndbox, ymin).text str(ymin) ET.SubElement(bndbox, xmax).text str(xmax) ET.SubElement(bndbox, ymax).text str(ymax) # 格式化输出XML方便用文本查看器人工抽检 xml_str ET.tostring(root, encodingutf-8) pretty minidom.parseString(xml_str).toprettyxml(indent ) with open(xml_path, w, encodingutf-8) as f: f.write(pretty) # 批量转换 for txt_name in os.listdir(label_dir): if not txt_name.endswith(.txt): continue txt_path os.path.join(label_dir, txt_name) img_name txt_name.replace(.txt, .jpg) image_path os.path.join(image_dir, img_name) xml_path os.path.join(xml_dir, img_name.replace(.jpg, .xml)) write_voc_xml(image_path, txt_path, xml_path) print(转换完成)这段代码里我把坐标做了clip把超出图像边界的xmin、ymin强制截断到0xmax、ymax截断到宽高减1。这步是必要的YOLO的归一化坐标在四舍五入后偶尔会越界直接写进XML会让训练工具报box out of image bounds的错。Clip完再转就不会有事。VOC XML里truncated和difficult字段我统一填了0。如果你的数据集里存在被遮挡或难识别的目标可以在转换时从COCO JSON里继承iscrowd字段的值映射到difficult这样后续用MMDetection训练时能正确区分离线集。不过这套数据集里iscrowd基本都是0填0没有问题。3.3 数据划分与目录约定三种格式全部就位后还要注意训练集、验证集、测试集的划分逻辑。有些数据集的标注文件只给了train和val两个目录测试集的效果要自己hold出来。常见做法是把train里的图片按8:1:1再切一刀80%做训练10%做验证10%做最终测试。划分时一定要按场景分组不能纯随机切。风电场的航拍数据常常是连续帧同一段飞行轨迹上相邻的几张图片高度相似。如果一张进了训练集、相邻一张进了验证集验证集指标虚高换到真实场景准确率马上掉下来。按文件名的拍摄批次前缀划分能有效规避这种数据泄漏。目录的约定也建议统一成YOLO的经典结构dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/之后在data.yaml里只需写两个路径训练框架会自动去找对应的labels目录。不要自己发明目录命名YOLOv8虽然灵活但默认约定能少写很多配置代码。4. 用YOLOv8把准确率跑到91.4%一套可复用的训练配置4.1 环境准备与数据检查很多人下载数据集后第一件事就是开训结果loss不收敛回头怀疑数据有问题。我的习惯是先做一个静默检查随机抽20张原图用OpenCV画上标注框人眼确认框和缺陷位置是否吻合。别嫌这一步土标注错位是检测类项目最隐蔽的杀手特别是自动转换格式时坐标偏移了一两个像素人眼在低分辨率图上根本分不清画出来放到大屏上看才知道偏没偏。# 抽检标注把YOLO txt画回原图 import cv2 import random import os image_dir images/train label_dir labels/train out_dir check_vis os.makedirs(out_dir, exist_okTrue) class_names [blade_crack, leading_edge_corrosion, tower_peel] sample_files random.sample(os.listdir(image_dir), 20) for img_name in sample_files: img cv2.imread(os.path.join(image_dir, img_name)) h, w img.shape[:2] txt_path os.path.join(label_dir, img_name.replace(.jpg, .txt)) if not os.path.exists(txt_path): continue with open(txt_path, r, encodingutf-8) as f: for line in f: parts line.strip().split() cls int(parts[0]) cx float(parts[1]) * w cy float(parts[2]) * h bw float(parts[3]) * w bh float(parts[4]) * h x1 int(cx - bw / 2) y1 int(cy - bh / 2) x2 int(cx bw / 2) y2 int(cy bh / 2) color (0, 255, 0) if cls 0 else (0, 0, 255) if cls 1 else (255, 0, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, class_names[cls], (x1, max(0, y1 - 6)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) cv2.imwrite(os.path.join(out_dir, img_name), img) print(f抽检图已保存到 {out_dir})这段代码的核心价值在于可视化反馈。每张抽检图的框带上类别名和颜色一眼就能看出类别有没有标错、框有没有整体偏移。特别关注叶子边缘的缺陷——叶片裂纹很多沿边缘延伸标注框如果贴边缘过紧训练时做裁剪增强会直接把目标裁掉一半导致模型永远学不全目标特征。如果抽检发现大量这类情况训练时要关掉或减弱随机crop增强。环境方面PyTorch 2.0以上加YOLOv8官方库是目前最顺的组合CUDA版本建议11.8或12.1Windows和Linux在训练效果上没有本质差别但Linux在DDP多卡训练时省心得多。4.2 超参数选择与91.4%逼近路径训练配置是逼近91.4%的关键环节我直接给一套验证过能跑通19000张量级的配置# data.yaml path: /path/to/wind_turbine_dataset train: images/train val: images/val test: images/test nc: 3 names: [blade_crack, leading_edge_corrosion, tower_peel]# 训练命令 yolo detect train \ modelyolov8m.pt \ datawind_turbine.yaml \ epochs150 \ imgsz1280 \ batch8 \ cacheTrue \ patience20 \ device0 \ projectruns/detect \ nameturbine_deploy先解释为什么选yolov8m而不是n或s风力涡轮机缺陷的目标尺度差异极大叶片裂纹可能是整张图里的细长细线塔筒剥落可能占图像面积的30%。n模型参数量小特征是能学出来但细长目标的边界回归精度不够m模型在召回率和显存占用之间取了平衡。如果GPU显存够大直接上yolov8l或x精度还能再涨一点但推理速度会掉实际部署到无人机端侧设备时不一定划算我建议m起步。imgsz1280是这套数据集的胜负手。19200张图的场景下如果目标占图像比例本来就小640输入会让小目标缩到十几个像素什么模型都学不出名堂。放大到1280之后裂纹的最小边能保有30个像素以上模型才有条件学习边缘纹理特征。batch8的设定配合1280分辨率显存占用约14GB。如果你的显卡只有8GB显存可以用batch4加cacheFalse训练时间拉长但不会爆显存。patience20是早停阈值验证集mAP连续20个epoch没有提升就停止省时间也防止过拟合。# 训练完成后直接看结果 yolo detect val \ modelruns/detect/turbine_deploy/weights/best.pt \ datawind_turbine.yaml \ imgsz1280 # 导出部署用的模型 yolo export \ modelruns/detect/turbine_deploy/weights/best.pt \ formatonnx \ imgsz1280 \ opset12训练日志里要看的不是loss曲线而是验证集的mAP0.5和mAP0.5:0.95。91.4%这个数字在目标检测上下文里一般指mAP0.5也就是IoU阈值0.5下的平均精度。如果你的复现结果在89%到93%之间波动都属于正常范围——数据增强的随机性会让最终一个epoch差出一两个点。不要为了刷这个数字反复调种子没有工程意义。4.3 数据增强的取舍YOLOv8自带一套增强策略默认参数在大多数数据集上表现尚可但风力涡轮机场景有两处需要手动调整。第一处是flipud竖直翻转默认是开的对风电场景这反而有害——叶片在机舱的上下位置关系是有物理意义的竖直翻转后模型会学到叶片可以出现在机舱上方或下方这种错误的先验。第二处是mosaic增强默认会用四张图拼一张对细长目标有利但mosaic比例太高会让模型在小尺寸图上反复看见拼接缝建议保留默认0.5或略微降低。# augment.yaml 覆盖部分增强参数 flipud: 0.0 # 关闭上下翻转 fliplr: 0.5 # 左右翻转保留 mosaic: 0.6 # 略高于默认弥补小目标样本不足 hsv_h: 0.015 # 色调扰动航拍图光照多变 hsv_s: 0.7 # 饱和度扰动调低避免颜色失真 scale: 0.4 # 缩放范围保留0.4即可增强参数改完可以把训练轮数适当增加。数据量本身够大增强又加了扰动150轮足够收敛不用一味加轮数。跑完一轮训练记录下best.pt对应的验证指标和数据集简介里的91.4%对比如果差太多优先怀疑数据划分是否一致而不是模型架构。5. 项目实战避坑从标注错位到显存溢出的六个常见问题5.1 训练loss直接出NaN现象训练刚开始前几步loss直接变成nan之后一直nan日志刷红。原因数据集里混入了异常标注常见的是归一化坐标出现负值、bbox宽高为0或者类别索引超过nc值。这些写在txt里肉眼很难发现模型反传时梯度爆炸直接让参数变成nan。解决在训练前统一跑一遍校验脚本遍历所有label文件检查每个数值是否在合理区间。正常YOLO格式要求c_x、c_y、w、h全部在0到1之间且w、h大于0.0001。发现非法值直接跳过该文件同时把异常文件名输出到日志方便溯源是哪个转换环节出了问题。5.2 验证集mAP高但实际巡检效果差现象验证集mAP0.5有90%以上拿到无人机新拍的图上一跑框全飘。原因数据划分泄漏。训练集和验证集来自同一拍摄批次的连续帧两张图高度相似模型记住的是场景而不是缺陷。这是航拍数据集的标志性翻车点。解决重新划分数据集按拍摄时间或者文件名前缀分组保证一个拍摄批次内的图全部进同一个集合。划分之后比较训练集和验证集的类别分布直方图两者差异应小于5%。如果某一类只在训练集出现验证时该类mAP虚高实战必掉。5.3 类别id没有重映射导致训练全错现象三分类数据集训练完预测时所有的框都预测成第一个类别。原因COCO格式的categories真实id可能是2、5、9这种不连续的编号转换脚本如果直接拿真实id当txt里的第一个数字YOLO训练时会把它当作从0开始的索引。真实id是2的类别映射到索引2但数据只有0、1、2三个索引索引2可能对应另一个类名或者越界。解决转换时强制做一层字典映射真实id按categories列表的顺序重映射为0到n-1连续索引。训练前打印映射表核对一遍这一步只要做一次后面所有格式互转都复用这份字典。5.4 叶片细长目标漏检严重现象mAP整体不低但单独统计叶片裂纹这一类的召回率只有50%出头。原因细长目标在缩放和裁剪增强时容易被切成两半模型学不到完整轮廓。另一个原因是小目标数量占比少训练时正负样本不平衡。解决训练图像分辨率提到1280以上让细长目标在输入图上保留更多像素。同时可以试试SAHI切片推理推理时把大图切成重叠小块分别检测再合并结果对细长目标准确率提升明显代价是推理时间翻倍。如果要做实时部署先用m模型做第一遍全图检测裂纹类置信度低于0.3的区域再切片细检这是可落地的折中方案。5.5 显存溢出现象1280分辨率、batch8跑了三个epoch直接OOM报错CUDA out of memory。原因风向标场景图像分辨率本来就高1280输入下特征图尺寸大加上mosaic增强要在batch内预留拼接缓冲显存消耗比标准COCO训练大得多。解决首选batch降到4epoch数增加补偿收敛速度。如果batch4还是OOM把cacheTrue关掉——缓存预处理图像虽然能提速但会额外占用几十GB内存或显存。再不够就开梯度累积batch2加accumulate4等效batch8显存占用降一半训练时间多30%左右。5.6 标注文件存在空标签但训练报错现象某些图片对应的txt文件是空文件训练时DataLoader报错显示找不到对应标签。原因数据集里存在背景图——航拍巡检时那些没有缺陷的塔筒和机舱照片也保留在目录里但欠采样时把标注过滤掉了留下了空txt。解决空txt本身不报错YOLO系框架能能当作负样本处理。真正报错是因为文件名大小写不一致比如图片叫IMG_001.JPGtxt叫img_001.txt。批处理脚本统一把文件名转成小写再配对然后全量检查一遍图片目录和标签目录的文件名一一对应多一张少一张都先清理掉再开训。负样本图片建议单独保留训练时模型学到的是哪些区域没有缺陷的判据对降低误检有帮助。6. 把模型部署到巡检链路验证一份下载数据集的成色从CSDN发布页下载这套数据集后第一件事不是直接训练而是用官方标注可视化做一次成色体检。把COCO JSON里随机抽三个类别各10张图把标注框画出来按之前抽检脚本跑一遍。重点看两件事一是叶片裂纹这类细长目标的标注有没有沿边缘贴合二是框内是否包含了过多背景。如果这两项质量过关这份数据的可用程度就很高了。验证结果有一套量化标准可参考单张图平均标注框数量健康的航拍巡检数据一般每张1到3个目标类别分布的最大占比不应超过总数的60%否则弱势类别训练出来的模型可靠性存疑。跑一遍训练后如果复现结果与91.4%差异在3个点以内说明标注质量和划分逻辑与发布方一致这份数据集就值得并进你的正式样本库。我自己的使用习惯是把这套数据当作基准集而不是全部样本。后续采集新数据时先跑一遍预训练模型的推理把置信度高于0.7的框自动写入伪标注置信度在0.3到0.7之间的挑出来人工复核低于0.3的视为漏检补充标注。用这套半自动流程每新增1000张现场图的人工标注量能压缩到300张左右。配合CVAT标注平台做复核整个闭环的效率和纯人工标注不在一个量级。从那次把coco转yolo的id映射踩坑之后我每次拿到新数据集都会强制走一遍这个流程先画框抽检再统计类别分布然后校验标签数值合法性最后才开训练。前三个步骤加起来不超过半小时省掉的是整个训练周期白跑的风险。希望这套流程和坑位清单能帮到你把这份数据集的价值榨干少走几趟弯路。本文还有配套的精品资源点击获取
返回列表