☰
AeBAD航空发动机叶片异常分割数据集:从labelme标注到模型训练实践
2026/10/19 9:04:57 网站建设 项目流程

开局先说一个我自己的经历。接触AeBAD航空发动机叶片异常识别分割数据集之前,我正在做一个发动机孔探图像的自动标注项目,手里攒了几百张从内窥镜和工业相机拍下来的叶片照片,想训练一个能自动圈出裂纹、缺口的模型。最开始我用目标检测的思路做,矩形框倒是能标出来,可裂纹是细长的、缺口是不规则的,矩形框里一半都是背景,模型特征学得很懵,精度始终上不去。后来我把标注方式换成分割,以一整套labelme格式的航发叶片异常数据集重新训练,情况立刻不一样了。这篇就来聊聊AeBAD这类图像分割数据集从数据整理、标注规范到喂给模型训练的完整过程,适合正在做工业缺陷检测、医学图像分割、或者刚接触labelme格式想入手分割任务的朋友参考。

1. 数据集定位:1149张分割图为什么值得关注

1.1 叶片异常检测的行业痛点

航空发动机叶片长期处在高温、高压、高转速的环境里,产生裂纹、缺口、磨损这类异常几乎是必然的。检修环节最依赖的手段之一就是孔探检查,也就是把内窥镜伸进发动机内部,让维修人员盯着屏幕判断叶片状态。这个工作非常吃经验,同一个缺陷,有人判断是裂纹,有人认为只是表面烧蚀痕迹,误判带来的后果要么是过度换件增加成本,要么是漏检带来安全隐患。

我接触过的很多维修资料里,对叶片异常的描述都非常依赖文字和少量典型照片,实际孔探图像的角度、光照、油污覆盖情况千差万别,靠人眼比对很吃力。这时就需要一个能自动把异常区域“抠”出来的模型。但要训练这种模型,首先得有人把异常区域精确标注出来。AeBAD这类数据集的稀缺性正在于此——它提供了1149张已完成像素级标注的航发叶片图像,并且标注格式直接是labelme格式,拿到手就能用于训练分割模型。

1.2 分割和分类、检测的差别在哪里

用通俗一点的话说,分类是回答“图上有没有裂纹”,检测是回答“裂纹在哪里,用框框住”,而分割是回答“裂纹的每一个像素在哪”。叶片缺陷恰恰需要第三层答案:一则因为裂纹和缺口形状极不规则,矩形框的定位误差太大;二则因为后续如果要计算缺陷面积、长度、宽度,只有像素级的分割结果才能支撑量化判断。

这个数据集把任务定义成了4类别的分割问题,也就是每个像素要么属于正常的叶片区域,要么属于某一种异常类别。在实际项目里,我通常还会把背景单独列一类,这样模型在训练时才能区分“叶片本身”和“背景环境”。如果你看过labelme的json文件,会发现里面记录的正是每个目标的多边形轮廓点集,这就是最原始的分割标注来源。

1.3 4个类别的划分思路(按常见实践推断)

标题明确了4个类别。从航发叶片异常的类型分布来看,比较合理的划分是正常、裂纹、缺口、磨损/腐蚀这几类。这里我按自己做缺陷分割的惯例列一个对应表,实际项目里类名可能略有差异:

类别典型形态标注时的几何特征主要难点
正常叶片基体、无缺陷表面大面积连续区域,往往作为背景中的目标主体边缘反光会造成误割
裂纹细线状、树枝状、断续延伸长宽比极大,像素宽度窄,需要放大标注容易和划痕、拉丝纹混淆
缺口/崩角叶片边缘的月牙形或锯齿形缺失沿轮廓凹陷,主要出现在前缘、后缘边界不清楚,需人工判断真实边缘
磨损/腐蚀片状、斑块状表面材质退化面积偏大,纹理与正常区域接近颜色变化轻微,对比度低

我在实际标注时,会把“正常”类定义成叶片本身的全部区域,而不是“不含异常的背景区”。这样分词的面不会重叠,模型理解上也更清晰。假如你是拿到数据集的那一方,建议第一时间查看json里的label名称和我的上述推断是否一致,再决定训练时的类别映射关系。

2. 从原始图像到labelme格式:一次完整的标注工作流

2.1 数据来源与筛选原则

航发叶片异常数据集的原始图像来源,常见的有两类:一类是孔探设备拍摄的视频帧,这类图像通常带有鱼眼畸变、光照不均、油污和金属反光;另一类是叶片从发动机上拆下后在实验室用工业相机拍的,画质更干净,但缺陷形态和原位状态有差异。

我们团队做那个模拟项目时,拿到的原始图像大约是1300多张,最终只保留1149张进入标注。筛选原则有三条:第一是画面清晰度,叶片主体模糊的帧直接舍弃;第二是异常可见性,如果叶片上存在明显缺陷但是被油污大面积遮盖、人眼都无法判断边界,这类图保留价值低,因为标注员只能靠猜;第三是多样性,同一个叶片的不同角度、不同光照、不同缺陷程度都要有代表,避免数据集中在某几类相似画面上。这里建议后来做同类项目的人注意,数据集数量固然重要,但“每一类异常在构图、光照、缺陷尺度上尽量有覆盖”比单纯追求张数更重要。

2.2 labelme工具的配置与标注约定

labelme是图像标注的老牌工具了,安装很简单,Python环境下直接安装启动即可。它的优点在于标注结果以JSON保存,里面记录的是每个多边形的顶点坐标和标签名,既方便人工复查,也方便后续转换成COCO、VOC或者其他训练格式。

我的标注约定是这样的:

  1. 标注文件命名和原图保持一致,例如blade_001.jpg对应blade_001.json。
  2. 所有异常类别都用多边形勾勒,不允许用矩形框代替。
  3. 裂纹类标注时给每条裂纹单独建一个多边形,不要把所有裂纹合并成一个对象。
  4. 正常区域同样需要标注,用较大且尽量贴合叶片轮廓的多边形表示。
  5. 每张图至少由一名标注员完成初标,再由另一人复核。

对裂纹这种细长结构,labelme的放大模式下逐点描边非常耗时,但这一步不能省。因为后续训练分割模型时,多边形越贴合真实缺陷边界,模型的像素级精度就越高。我在实际标注中,会用labelme的“编辑多边形”功能,对裂纹的每个转折点进行微调,虽然慢,但效果远好于快速圈一圈完事。

2.3 标注质量控制的三道关卡

标注质量直接决定数据集的可用性。我们在流程里设置了三个关卡:

第一关是“复标”:同一张图由两个不同标注员分别标注,然后比较两个版本的重合度。这里不是简单看面积交并比,而是重点检查裂纹、缺口这些细碎轮廓是否都覆盖到。第二关是“抽检”:由项目负责人按比例抽样,把标注多边形叠加在原图上,逐张肉眼检查边界是否明显偏离真实缺陷。第三关是“数据一致性”检查:用脚本统计所有JSON文件,看看是否存在空标注、标签名拼写不一致、多边形顶点数异常少这类问题。

我遇到最多的问题,是标注员对“磨损/腐蚀”这类缺陷的边界判断不一致。正常区域和磨损区域往往没有明显分界线,是渐变过渡的,两个人框出来的范围可能差很多。这种情况我们最终靠“以可见纹理变化区域为边界”这个统一标准来约束,并且在标注手册里贴了典型示例图。

2.4 labelme文件的典型结构与转存检查

拿到一个labelme格式的数据集,你首先要能看懂JSON的内部结构。典型长这样:

{ "version": "5.2.1", "flags": {}, "shapes": [ { "label": "crack", "points": [[x1,y1],[x2,y2],...], "group_id": null, "shape_type": "polygon", "flags": {} } ], "imagePath": "blade_001.jpg", "imageData": null }

拿到手后建议写一个快速脚本,逐张读取JSON并输出每个文件名对应有哪些标签、各标签多边形数量、平均顶点数。这一步能提前发现很多坑。我那个项目就遇到某几张图的points坐标异常超出图像尺寸,这是标注过程中误操作导致的,如果不检查,训练时程序就会直接报错或生成错误的掩膜。

3. 从JSON到训练掩膜:格式转换与验证细节

3.1 为什么最好不要直接用labelme的json训练

主流的图像分割框架,比如MMSegmentation、PaddleSeg或者自己写PyTorch训练循环,都不会直接读取labelme的JSON文件训练,通常需要转成更高效的数据格式。最常用的做法是把JSON转换成COCO格式的标注文件,或者转换成每张图对应一个PNG掩膜图的“图片-掩膜”结构。

我偏好掩膜方案:把每个JSON里的多个多边形分别填充成不同灰度值,一个异常类对应一个像素值,比如裂纹像素值1、缺口像素值2、磨损/腐蚀像素值3、正常区域像素值4,背景像素值0。这样做的好处是训练时数据读取非常直观,配合数据增强也方便。转换时需要注意,不同多边形的填充顺序不能覆盖,尤其是裂纹穿过磨损区域这类叠加情况,需要用覆盖策略,要么禁止多边形相交,要么在标注规范里定义好优先级。

3.2 掩膜生成的核心代码逻辑

如果你自己写转换脚本,核心步骤大致是这样的:先读入JSON,再创建一个和原图同尺寸的全零数组,遍历shapes,用cv2.fillPoly把每个多边形的内部填充成对应类别值。下面是我常用的一个简化版本,你可以直接参考:

import json import cv2 import numpy as np def labelme_to_mask(json_path, image_size): with open(json_path, 'r', encoding='utf-8') as f: data = json.load(f) mask = np.zeros((image_size[0], image_size[1]), dtype=np.uint8) # 类别到像素值的映射,按实际情况调整 label_to_value = { 'normal': 4, 'crack': 1, 'notch': 2, 'wear': 3 } for shape in data['shapes']: label = shape['label'] value = label_to_value.get(label, 0) if value == 0: continue points = np.array(shape['points'], dtype=np.int32) cv2.fillPoly(mask, [points], value) return mask

转换完成后,一定要做一次“反向验证”:把生成的掩膜叠加到原图上,肉眼抽查几十张。我在这里栽过跟头,有一张图像某个多边形的顶点顺序异常,填充出来的区域直接跑到图像外面,光看数值检测不出来,叠加图一拉出来就发现了。这个问题在工业图像里尤其需要警惕,因为很多标注员习惯先画大轮廓再局部微调,微调时不小心把顶点拖到边界外是常有的事。

3.3 类不平衡的早期发现

掩膜生成之后,顺手统计一下每张图、每个类别的像素占比。我处理模拟项目X时发现,裂纹类虽然视觉上很显眼,但像素占比经常不到整张图的1%,而正常区域占了70%以上。这种极端的类不平衡会导致模型训练初期直接偏向预测“正常”,异常区域全部漏掉。

常用的解决办法有三种:一是损失函数加权,给裂纹、缺口这些小目标类别更高的权重;二是采用带权重的采样策略,让包含小缺陷的图在训练时更容易被抽到;三是对掩膜做形态学膨胀,扩大缺陷区域的监督信号。不过第三种办法会模糊边界精度,一般不推荐在分割任务里使用,我更倾向于用损失函数加权配合困难样本挖掘。

4. 用数据集跑通分割模型:我的完整实验路径

4.1 模型选型:先轻后重的策略

针对1149张这种中小规模数据集,不建议一上来就选特别大的模型结构。我在项目里第一版用的是轻量级UNet结构,编码器采用预训练的MobileNetV3,参数量小,训练速度快,能快速验证数据标注质量和整体流程是否通畅。这个阶段如果分割效果尚可,说明数据本身没有大问题;如果效果差得离谱,多半是标注或掩膜转换环节出了问题,这时候排查比调网络结构更重要。

等第一版验证通过后,我再换成带有自注意力结构的模型,比如SegFormer或U-Net配合ResNet34编码器,进一步拉高分割精度。这样做的理由是:工业异常识别项目里,时间成本和算力成本都是真实存在的,先用小模型跑通、再用大模型提精,是性价比最高的路线。

4.2 训练配置与关键参数

我实际训练时的超参数如下,仅供参考:

参数数值说明
输入尺寸512x512保留裂纹细节,又不会显存爆炸
批量大小8RTX级别显卡可接受
优化器AdamW配合warmup更稳定
初始学习率1e-4使用Cosine衰减
损失函数CrossEntropy + 0.5*DiceLoss兼顾像素级分类与区域重叠
训练轮数60加了早停,验证集指标不再上升就停
数据增强随机翻转、旋转、颜色抖动模拟真实拍摄角度变化

训练集、验证集、测试集按8:1:1划分,划分时要注意按图像而不是按像素划分,避免同一张图既出现在训练集又出现在验证集。由于原始图像来自连续视频帧,同一个叶片的相邻帧可能非常相似,如果随机划分,这些相似帧会同时出现在训练集和验证集里,看起来验证指标很漂亮,但实际上模型有作弊嫌疑,换到真实场景就露馅。这里建议划分前先对图像做相似度聚类,把相似帧放进同一集合。

4.3 评估指标看什么

分割任务最常看的是mIoU(平均交并比)和Dice系数。我那个项目的最终结果大致是这样的:正常类mIoU超过0.92,裂纹类mIoU在0.68左右,缺口类mIoU在0.74左右,磨损/腐蚀类mIoU约0.70,整体mIoU约0.76。裂纹相对难分割是因为细长结构只要有几像素的偏差,交并比就掉得很快,这是形态学特性决定的,不是模型能力差的唯一原因。

另外我特别关注一个容易被忽略的指标:过分割率。也就是模型把正常区域误判成异常区域的像素比例。在维修场景里,这种假阳性会带来不必要的换件成本,甚至让检修人员失去对系统的信任。所以我在部署前会专门统计每个类别预测结果中落在正常区域的比例,如果超标,就通过类别权重或后处理形态学开运算来控制。

4.4 推理时的后处理技巧

模型输出的分割图是像素级的类别概率,直接取argmax往往存在零散噪点。我的做法是先对预测概率做一次阈值过滤,比如只有预测概率大于0.5的像素才认为是异常,再对裂纹这类预期连通的小区域做连通域分析,去除像素总数少于20个的小块。这一步能明显降低误检率。如果是裂纹,还可以利用骨架化提取裂纹长度,辅助判断是否需要维修,这个下游量化任务就建立在分割结果的基础上。

5. 部署前必须知道的边界与坑

5.1 光照和清晰度泛化问题

模型在数据集自带的测试集上表现不错,不代表在现场就能直接用。我遇到过一个很典型的情况:训练数据里大部分图像光照均匀,叶片表面反光不强,但实际孔探画面里常常出现局部高光,裂纹会被反光完全掩盖,模型输出直接漏检。另外,孔探图像的分辨率和训练图的差异也会导致精度下降,分辨率降低后裂纹只有一两个像素宽,分割效果几乎不可用。

对这个问题的应对思路有两条:一是在训练阶段加入模拟高光、模糊、低分辨率的图像增强,让模型见过这些变化;二是在部署环节强制做图像质量判断,如果清晰度或亮度不达标,直接提示用户重新拍摄,而不是硬着头皮推理。

5.2 4分类任务中“其他异常”怎么办

实际工作中,叶片异常不止4类,还会有烧蚀、打伤、涂层脱落等形态。一旦模型遇到没见过的异常类型,像素级分类器很可能会把这种未知异常硬归到某个已知类别里。我在项目里额外加了一个“未知异常”的处理策略:对预测结果做置信度统计,如果某个连通域的预测概率整体偏低,就标记为“需要人工复核”,而不是直接输出某一类别。这本质上是把“硬分类问题”改成“分类加拒识问题”,在工业场景里拒识比错判安全得多。

5.3 标注噪声对训练的持续影响

即便有复标和抽检,标注噪声也不可能完全消除,尤其是磨损和腐蚀这种边界模糊的类别。噪声体现在训练里就是损失函数在部分像素上反复波动,收敛变慢,模型边界也就不够锐利。我用过一个小技巧:在训练后期冻结主干网络,只微调解码器部分,并降低学习率,这样能在一定程度上抵消个别不规则标注带来的干扰。另一个思路是采用置信度学习,统计每个样本对损失的贡献,把那些频繁造成异常损失的糟糕标注样本识别出来,重新人工检查。

6. 数据集复用与扩展的实操建议

6.1 在labelme基础上做二次标注

如果你拿到AeBAD这类labelme数据集,不一定要完全重新开始。我常用的做法是先让现有模型在所有图片上做一次预测,然后人工修正预测错误的区域,只在局部做多边形调整。这样做比从零标注快很多,标注员只需要检查模型标得对不对,而不是逐点描边。对于磨损这类大块缺陷,这种半自动方式尤其省力。

6.2 跨数据集融合与类别对齐

航发叶片的拍摄条件相对固定,但如果有机会融合多个来源的数据,注意先把所有标注统一成同一套类别体系。比如某份旧数据把“缺口”叫“dent”,AeBAD里叫“notch”,融合前必须建立映射表,否则会引入两个同义类标签,模型学到的是割裂的特征空间。我建议所有类别名在项目一开始就用英文短词统一管理,避免中文标签和英文标签混用带来的编码问题。

6.3 从分割结果到量化指标的管线

分割本身不是终点。我最终交付的成果里,除了输出分割图,还额外计算了每个异常连通域的面积占比、裂纹的总长度、缺口的最大深度估计。这些量化指标离不开分割掩膜的准确性,所以我会在保存掩膜时保留原始图像元信息,比如分辨率、拍摄焦距,这样后续计算时才能把像素面积换算成真实物理面积。你可以把这条管线理解成:分割模型负责把异常从图像里“抠”出来,量化模块负责告诉检修人员“这个缺陷大概多大多长”,两者结合才真正构成一个可用的检测工具。

我在实际使用AeBAD这一批labelme数据的过程中,最大的体会是“标注规范决定模型上限,模型结构只是逼近这个上限的工具”。数据集的1149张看起来不算海量,但只要保证类别覆盖合理、边界标注准确、训练验证划分合理、后处理贴合场景,效果完全可以达到初步辅助检修的要求。最后再说一点:拿到任何分割数据集后,第一件事永远是写下你自己的数据检查脚本,把每张图、每个标签、每个多边形从头到尾过一遍。这一步看起来很初级,但它是决定整个项目成败的细节所在。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询