ARTICLE DETAIL

资讯详情

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

5类飞机表面缺陷数据集:4264张图的YOLOv8目标检测实战

5类飞机表面缺陷数据集:4264张图的YOLOv8目标检测实战 简介一套面向飞机表面缺陷检测的目标检测数据集共包含4264张真实飞机外观图像覆盖裂缝、凹陷、缺角、掉漆、划痕5类典型缺陷总标注框数达8768个适合作为航空制造质检、工业视觉缺陷识别等场景的算法训练与验证数据。资源包内含4264张jpg原图并同步提供相同数量的VOC格式xml标注和YOLO格式txt标注均由labelImg完成矩形框标注类别与位置信息准确可直接接入YOLO、Faster R-CNN等主流检测框架使用。压缩包整体约163MB文件构成以jpg图片、xml标注和txt标注三类为主VOC与YOLO格式目录清晰随包说明txt还列出每类缺陷框数统计便于评估类别平衡性。目前已有456人学习下载适合需要真实工业缺陷样本的目标检测研究人员与开发者。1. 4264张5类飞机表面缺陷数据集目标检测里“小而刚需”的底盘数据在飞机蒙皮、机翼前缘、舱门和检修口盖这一类表面检测场景里真正的门槛从来不是模型结构而是数据。划痕只有十几个像素宽腐蚀区域的边界和阴影几乎融合漆层脱落和光照反光混在一起——这类缺陷如果靠人工标注一张图的成本比训练一轮还高。4264张、5类、VOCYOLO双格式打包这个飞机表面缺陷数据集解决的是目标检测从业者最常卡住的两个问题数据从哪里来以及标注怎么转成模型能直接吃进去的格式。它适合刚跑通YOLO但缺真实工业数据的初学者也适合想在小目标检测上做数据增强实验的进阶玩家。不适合谁指望这4264张图直接上产线部署的——它能把模型训熟部署前还得补现场数据和边界case。2. 拆开4264张图5类缺陷、VOC与YOLO双格式到底差在哪2.1 五类缺陷的常规划分以及为什么它们都难检测先说明一点拿到压缩包后的第一个动作永远是打开里面的classes.txt或者labels目录确认五类的真实英文名别靠压缩包文件名猜。航空表面缺陷数据集的常见五类划分大致是划痕、腐蚀、漆层脱落/开裂、凹坑dent/bulge、铆钉区异常。划痕长宽比极端普通的锚框设计很难兜住腐蚀区边缘模糊模型经常把蒙皮纹理背景误检成目标漆层脱落和铝蒙皮原色的灰度差极小属于典型低对比度目标。这五类里至少有三种天然就是目标检测里最难的那一档。这也是这套数据最有价值的地方它不给你一堆又大又清晰的标注框而是逼着你处理小目标、极端长宽比和模糊边界。对于刚跑通YOLOv8、却只拿公开数据集练手的从业者这种数据能让你提前遇到真实巡检项目中80%的问题。很多人在COCO上有0.85的mAP换到飞机表面缺陷上掉到0.5不是模型退化了是数据的难度曲线突然变陡。2.2 VOC的XML和YOLO的TXT同一标注的两种数学表达VOC格式把标注框存成像素绝对坐标。每个XML文件里object节点给出类别名和bndbox里面是xmin、ymin、xmax、ymax四个整数单位是原始图像的像素。人眼可读方便排查但训练时模型不直接吃XML必须先换算成相对坐标。YOLO格式则把同一个框归一化到0~1之间每行一个目标五个数字依次是class_id、center_x、center_y、width、height。所有坐标都除以图像的宽度和高度。两者本质是同一个标注的两种坐标系换格式只是做一次坐标变换并不改变标注信息。# voc2yolo.py # 把VOC的XML标注转成YOLO训练用的TXT标注 import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, class_names, out_dir): tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) file_stem os.path.splitext(os.path.basename(xml_path))[0] out_lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in class_names: continue # XML里出现不在名单里的类直接跳过并留日志 class_id class_names.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 下面四个数值正是YOLO需要的归一化坐标 cx (xmin xmax) / 2.0 / img_w cy (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h # 边界保护坐标算出来略小于0或大于1时裁剪到合法区间 cx min(max(cx, 0.0), 1.0) cy min(max(cy, 0.0), 1.0) w min(max(w, 0.0), 1.0) h min(max(h, 0.0), 1.0) out_lines.append(f{class_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) if out_lines: with open(os.path.join(out_dir, file_stem .txt), w) as f: f.write(\n.join(out_lines) \n) if __name__ __main__: # class_names的顺序必须和后续训练data.yaml里的names完全一致 class_names [scratch, corrosion, coating_peeling, dent, rivet_defect] xml_dir Annotations out_dir labels os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if xml_file.endswith(.xml): voc_to_yolo(os.path.join(xml_dir, xml_file), class_names, out_dir)逻辑说明代码遍历每个XML先读图像宽高再把标注框的中心点和宽高换算出来统一除以图像尺寸做归一化。三个地方需要特别注意。第一class_names的顺序一旦和之后data.yaml里的names不一致类别就会整体错位训练不报错、推理全是错的。第二坐标的min/max保护不能去掉标注软件偶尔会画出超出图像边缘的框不裁剪的话训练时会引入异常高响应。第三输出的txt必须和对应图片同名YOLO训练就是靠文件名前缀把图和标签配对的。反过来做YOLO转VOC时把归一化坐标乘以图像宽高还原成xmin、ymin、xmax、ymax就行公式反向使用其余逻辑完全相同。提示转换完成后抽5个XML和5个TXT做人工校验。直接用OpenCV把框画回图上看一眼比任何脚本日志都可靠。2.3 这套数据集够不够干净四个检查点跑一遍解压之后不要急着训练。先花十分钟做数据体检四个检查点能排除掉绝大部分“白训练一轮”的风险。第一文件数量对齐。压缩包里VOC目录有4264个xml那YOLO目录就必须有4264个txtimages目录有4264张图。数量不一致说明里头有重复图或缺标注。# 统计YOLO标签和图片各自的数量判断是否一一对应 ls labels/*.txt | wc -l ls images/*.jpg | wc -l第二类别分布是否极端。4264张图5个类不代表每个类的框数量均匀。很多工业缺陷数据集里划痕这类目标占一半以上铆钉异常可能只有5%。训练前统计每个class_id出现的次数能直接决定要不要做类别重采样。# 统计所有txt标注里每个class_id出现的次数 cat labels/*.txt | cut -d -f1 | sort | uniq -c第三图像分辨率是否统一。混合分辨率会让推理时的letterbox缩放行为不一致小目标在低分辨率图里几乎不可见。# 用Python检查所有图片尺寸打印出现过的分辨率集合 python -c import os, cv2 sizes set() for f in os.listdir(images): img cv2.imread(os.path.join(images, f)) if img is not None: sizes.add(img.shape[:2]) print(sorted(sizes)) 第四验证集划分。4264张图通常按8:2或9:1切训练和验证。但飞机表面缺陷有个特殊性同一架飞机的不同照片在光照和角度上高度相似如果随机切分训练集和验证集里会混进同一架飞机的图造成验证指标虚高。正确做法是按文件名里的架次或批次字段分组切分把同一次采样序列放进同一侧。这一步和模型结构无关却是整个流程里影响结果可信度最大的环节。3. 用这份VOCYOLO数据集跑通YOLOv8训练最小可复现流程3.1 目录整理与标签文件校验YOLO系列训练要求的目录结构非常固定很多第一次解压这种数据集包的人都会在这一步翻车。以YOLOv8为例标准目录长这样datasets/ └── aircraft/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── aircraft.yaml压缩包解开之后如果文件混在一个目录里第一件事是整理成上面的形状。推荐直接写一个split脚本而不是用系统命令去碰文件因为随机切分时要同时移动图片和同名txt还要考虑按架次分组的问题。一个最小可用的脚本# split_train_val.py # 按文件名前缀做分组切分避免同一架飞机的图同时出现在train和val import os import random from collections import defaultdict random.seed(42) img_dir images label_dir labels train_img, val_img datasets/aircraft/images/train, datasets/aircraft/images/val train_lbl, val_lbl datasets/aircraft/labels/train, datasets/aircraft/labels/val for d in [train_img, val_img, train_lbl, val_lbl]: os.makedirs(d, exist_okTrue) # 假设文件名形如 20240513_A320_001.jpg取前两段当作分组key groups defaultdict(list) for f in os.listdir(img_dir): if not f.endswith(.jpg): continue key _.join(f.split(_)[:2]) # 同一架飞机、同一批次归一组 groups[key].append(f) keys list(groups.keys()) random.shuffle(keys) cut int(len(keys) * 0.8) for key in keys[:cut]: for f in groups[key]: os.rename(os.path.join(img_dir, f), os.path.join(train_img, f)) lbl f.replace(.jpg, .txt) if os.path.exists(os.path.join(label_dir, lbl)): os.rename(os.path.join(label_dir, lbl), os.path.join(train_lbl, lbl)) # 剩余keys进val代码同理略逻辑说明先按文件名中的飞机标识分组再把组级别的key列表随机打乱切分。这样保证同一架飞机的照片不会被拆到两侧能有效防止验证指标虚高。随机种子固定为42是为了让多次实验得到完全相同的划分方便对比模型改动前后真正的差异。如果你的文件名没有明显的分组字段退而求其次用完整文件名随机切但要知道验证集的结果会有水分。3.2 aircraft.yaml的写法与names顺序陷阱数据集整理好之后在datasets/aircraft目录下写一个yaml。这个文件是YOLO训练唯一需要你手写的配置内容很简单但names字段的坑很深。# aircraft.yaml # YOLOv8训练用数据集描述文件 path: /home/you/datasets/aircraft # 改成实际绝对路径 train: images/train val: images/val # test: images/test # 如果压缩包里单独给了test集取消注释 nc: 5 # names的顺序必须和2.2节转换脚本里的class_names完全一致 names: 0: scratch 1: corrosion 2: coating_peeling 3: dent 4: rivet_defect参数说明path建议写绝对路径这样无论从哪个目录启动训练模型都找得到文件相对路径在命令行和IDE混用的时候容易产生歧义。train和val填的是相对于path的路径不要写绝对路径拼接否则换机器就要改一行。nc必须和names数量严格相等YOLO启动时会检查这两个值不一致直接报错。names的顺序是这套流程里最容易被忽略的坑如果转换脚本里class_names是scratch、corrosion的顺序yaml里却写成corrosion、scratch训练不会报错loss也正常但推理时所有类别错位框的位置是对的类别名全是乱的。3.3 训练参数怎么设才不浪费这4264张图四千多张图属于中等偏小的数据集规模训练参数不能照搬COCO默认配置。我从模型、轮数、增强三块说我的经验值。第一模型选型。先从yolov8n或者yolov8s起步。4264张图训yolov8l或yolov8x参数量大但数据量撑不住验证集mAP反而不如小模型。先用小模型跑通整个流程确认数据链路正确再换s或m做精度提升实验这是最省时间的常见做法。第二训练轮数和早停。epochs给150到200不要太少。小目标检测的学习信号弱前50个epoch模型往往还在学背景和边缘纹理mAP到后期才缓慢爬升。配合早停策略patience设30模型在验证集上连续30轮不提升就自动停能省掉大量空跑时间。具体命令# 最小可复现训练命令YOLOv8 yolo detect train \ modelyolov8n.pt \ dataaircraft.yaml \ imgsz640 \ epochs150 \ batch16 \ patience30 \ device0 \ project./runs \ nameaircraft_surface参数说明model用yolov8n.pt表示从COCO预训练权重继续微调而不是从头训练。imgsz640是最低成本的起点如果画框平均宽度小于32像素再试1280但要接受显存占用和训练时长翻倍。batch16在8GB显存上基本够用显存更大可以加但加了batch要同步降学习率否则BN统计会出问题。patience30是保守值给太小时模型还在上涨期就被拦腰截断。第三数据增强。YOLOv8默认自带mosaic和mixup。在小目标缺陷集上mosaic在训练后期反而有害——mosaic把四张图缩小拼一起缺陷被进一步缩小对本来就小的目标不友好。我的习惯是前100轮保持默认增强最后10轮关掉mosaic让模型在完整分辨率下稳定输出。YOLOv8可以这样覆盖超参# 最后10轮关闭mosaic避免拼接图降低小目标检测精度 yolo detect train ... mosaic0.0 close_mosaic10参数说明mosaic0.0直接关闭mosaic增强close_mosaic10表示训练最后10轮关闭mosaic。对飞机蒙皮这类背景高度一致的图mosaic贡献的正样本多样性很小反而让模型学会依赖拼接缝特征关掉之后验证集指标通常更稳。训练结束后先跑一次验证看一眼结果再决定要不要换大模型# 在验证集上评估训练好的权重 yolo detect val \ modelruns/aircraft_surface/weights/best.pt \ dataaircraft.yaml它会输出混淆矩阵和一张表格包含mAP50、mAP50-95、Precision、Recall。这里先给个预期飞机表面缺陷因为小目标占比高mAP50-95通常比通用物体检测低一大截0.5甚至0.4都正常关键是mAP50是否在0.9附近、Recall是否超过0.85。如果mAP50也很低先回去查标注不要急着换大模型。4. 飞机表面缺陷训练避坑记录这5个翻车点我替你踩过了这一章每条按“现象—原因—解决”来写全是在这套类型的数据集上反复出现的问题。4.1 验证指标好看实际推理漏检小划痕现象验证集mAP50有0.92似乎可以直接交付但拿新拍的照片测试细划痕漏了一大半置信度都集中在0.1到0.3之间。原因验证集的图片分布和训练集太接近模型实际上是在背场景另一个根因是划痕类目标太小经过YOLO多次下采样后在高层特征图上的响应只剩一两个像素置信度天然偏低。解决先检查验证集是不是同架次泄漏再用置信度阈值0.1做一次推理对比如果低置信度下能检出说明模型学到了只是阈值设得太激进。缺陷检测场景漏检比误检代价高conf阈值放到0.1到0.15配合后处理能挽回大部分漏检。再不行就把imgsz从640提到1280小目标特征会被放大。提示统计一下模型输出框的宽高像素分布能辅助判断该不该提升输入分辨率。4.2 两个相邻缺陷被模型合并成一个框现象输出结果里两块离得很近的漆层脱落被一个框同时框住且这个框的中心正好落在两个缺陷之间的缝隙处。原因YOLO默认的NMS IoU阈值是0.7。两个缺陷的真实框IoU如果超过这个值预测时会被当成同一个目标置信度高的框把另一个抑制掉。密集缺陷区域出现这个问题特别频繁。解决推理阶段把NMS的IoU阈值调到0.5甚至0.4会增加少量误检但密集缺陷的召回明显改善。训练阶段检查标注框的重叠率如果大量框的IoU超过0.5说明标注本身有问题需要合并或重标。还有一个方向是把模型从框检测换成实例分割用分割掩码天然绕开多框重叠被NMS吃掉的问题。4.3 训练中途loss变成NaN或mAP断崖式下跌现象第60个epoch左右loss从0.06突然跳到nan或者val mAP在一个epoch内从0.85掉到0.5之后再也回不来。原因大概率是学习率过高和batch太小互相踩踏。小数据集上常见的组合是batch8配上默认lrBN的batch统计量在8张图里噪声过大某个epoch统计量一漂移整个网络直接崩。预训练模型微调时前几层学习率没有被压低也会触发同样现象。解决把初始学习率降到0.001或者开启warmup让前10个epoch从更小的值线性升到目标值batch提升到16以上如果模型大就冻结骨干前10层训练。这套组合基本能消除BN崩溃。有个笨办法也有效崩了之后加载崩之前最后一个epoch的权重继续训loss能稳住。4.4 VOC转YOLO后类别错位训练不报错、推理全错现象训练日志里loss正常下降mAP正常上升但推理时把置信度最高的铆钉异常识别成划痕每个类别都系统性错位一个位置。原因转换脚本的class_names顺序和data.yaml里names的顺序不一致。VOC的XML里存的是类别名字符串换脚本时排序规则一变class_id就跟着变而TXT里只有数字没有名字错位不会在训练时暴露。解决训练开始前随机抽3张验证集图片做一次推理人工看框和类别名的对应。更好的防范是转换脚本里把class_names固定下来并把顺序导出成一份jsondata.yaml直接读这个json的key顺序。训练前再扫一遍labels目录确认最大class_id小于nc且每个id都出现过。4.5 验证集指标虚高一上现场就原形毕露现象内部train/val都0.9以上但换成完全独立的新数据只有0.6落差巨大。原因随机切分导致信息泄漏。同一架飞机的多张照片角度相似、光照相似模型在训练时已经见过验证场景。4264张图如果来自200个架次随机切分时同一架次的图极大概率同时进train和val验证mAP的参考价值就打了折扣。解决从一开始就按架次或图组切分。最严苛的做法是只留一个架次进val其余全部进train模拟没见过的新飞机评估这才接近真实部署场景。如果压缩包里无法识别架次信息至少按文件名前缀分组别按单张随机。5. 部署前的阈值扫描用confidence与IoU把虚高指标拉回现实best.pt训练出来之后先别急着写部署配置。找一个没进过训练也没进过验证的独立测试集做一遍置信度阈值扫描找出F1最优工作点。这里有一个技巧不要把验证集的mAP直接当作部署依据因为验证集的分布和训练集太接近大概率偏乐观。写一个极简脚本遍历一组置信度阈值观察输出框数量和置信度的关系# threshold_scan.py # 用独立测试集扫描置信度阈值看不同阈值下的输出框数量 from ultralytics import YOLO model YOLO(runs/aircraft_surface/weights/best.pt) conf_range [0.05, 0.1, 0.15, 0.2, 0.3, 0.4, 0.5] for conf in conf_range: # 推理时只改conf后处理IoU固定为0.5 results model.predict( sourceindependent_test/, confconf, iou0.5, saveFalse, verboseFalse, ) total sum(len(r.boxes) for r in results) print(fconf{conf:.2f}, 输出框数{total})逻辑说明循环里只调整置信度IoU固定。输出框数会随conf降低单调增加但框数增幅在哪一段开始暴涨就说明阈值扫到了大量低质量预测的位置。对飞机表面缺陷F1最高点往往落在conf0.1到0.25之间而不是默认的0.25。原因是小目标置信度整体偏低沿用默认阈值等于白白扔掉一部分召回。如果扫描发现框数在某个阈值附近突然翻倍那就该去查那一批不准确的标注而不是继续调阈值。我现在的习惯是任何缺陷检测模型先写一个阈值扫描脚本再画PR曲线确认独立测试集上的最优工作点最后才把conf和iou写进部署配置。这比盯着训练日志里的mAP可靠得多。4264张图只是个开始但把这块底盘打磨好后面补数据、换模型、上产线都会顺畅很多。希望帮到你。本文还有配套的精品资源点击获取
返回列表