资讯详情 柑橘成熟度分割数据集:从标注规范到模型训练实战复盘
📅 2026/10/22 2:53:47
做农业视觉有一段时间的朋友应该都有这种体会果实的成熟度判断远比想象中复杂。尤其是柑橘颜色跨度大果面又被树叶挡得严严实实目标检测框一框下去框里既有果皮也有叶子成熟度判断完全不靠谱。所以我和团队在做某个采摘机器人的识别模块时干脆放弃了纯检测方案改走像素级分割路线。整个项目最终沉淀下一套可以直接复用的数据资产3089张柑橘图像的成熟度分割数据集LabelMe格式标注3个成熟度类别。这篇文章就围绕这套数据集从方案选型、类别设计、标注规范、格式处理到训练验证完整复盘一次给做智慧农业视觉的朋友一些可参考的实操经验。这套数据集比较适合三类人一是做采摘机器人识别算法的研发人员需要带像素级标注的数据来做预训练或算法验证二是做果园巡检、产量预估产品的工程师想训练一个成熟度分布统计模型三是刚接触分割任务的学生或开发者需要一份干净的实例分割数据来做基准测试。3089张的体量不算大但在标注成本上已经是一笔不小的投入用好了足够支撑一个稳定的分割基线。1. 项目整体思路与方案取舍1.1 为什么成熟度问题必须用分割而不是检测框先聊方案选型。市面上很多农业视觉产品还停留在目标检测阶段用YOLO这类模型给果实画一个矩形框。矩形框最大的问题在于实际果园里的果实不是孤零零挂着的。叶子会盖住一部分果面枝条会横穿果体两个相邻果实经常挤在一起。这时候框出来的是果实加遮挡物的混合区域成熟度特征被严重污染。比如一个青果被黄叶挡住检测框内的平均颜色可能已经偏黄成熟度判断自然就出错了。分割完全绕开了这个问题。它对覆盖区域逐像素做判断输出的是一张和果实轮廓高度吻合的掩码图果面真实的颜色、纹理、光泽都来自果实可见部分不会被背景干扰。在成熟度识别场景里这几乎是刚需因为柑橘成熟度的核心信号就是果皮颜色分布和光泽状态。你要看的是果皮本身而不是果实周围那一块。所以项目定下的第一条原则就是直接做分割不做框。这背后还有一个实际业务考量。采摘机器人不仅要识别果实熟不熟还要估计可采摘面积有多大。有了像素级掩码就能算出果面可见面积比例从而推断有没有足够的抓取空间。如果只是目标检测框这部分几何信息完全丢失后端机械臂的路径规划就很难做精细。采摘是分秒必争的活几何信息的价值在这个环节体现得特别明显。1.2 数据规模和标注格式的选择逻辑3089张图像的规模看起来不大但结合分割标注的成本来算这个体量是经过认真权衡的。实测下来一张果树密集、果实重叠严重的图像标注一个多边形实例大概需要25到40秒多实例单张累加后要一分钟以上。算上边缘细化和后期审核单张图的平均耗时通常超过90秒。如果按1.5分钟一张、两人并行工作来估算3089张意味着接近80小时的纯标注劳动量这还不包括返工和审核。所以没有盲目追求上万张而是把精力花在保证每一张的标注精度上。格式选择落在LabelMe上原因也很直接。LabelMe的标注本质是一串多边形点坐标可以非常精细地贴合果皮轮廓这是很多结构化标注格式替代不了的。相比之下VOC格式在实例边界的表达上比较粗糙COCO格式更偏重机器可读性人工编辑时不够直观。LabelMe天然适合先从果皮边缘框起、再携带类别信息的工作流。而且LabelMe往COCO或者其它格式转换只需要一次性写一个脚本成本几乎可以忽略。对绝大多数农业视觉团队来说这已经是一个足够顺手的选择。这里插一个细节LabelMe的JSON文件里有一个imageData字段既可以存图像的base64编码也可以设为null。为了便于代码读取和文件管理我们选择把图像文件单独存放JSON里只保留imagePath和宽高信息避免每张图在标注文件里重复躺一份大体积编码。这样做还有一个好处图像存在网络存储或者本地方便路径同步JSON文件很小拉取和备份都很快。1.3 三个成熟度类别怎么定出来的类别体系是这类数据集的核心。最开始评审过两个极端方案一个是二值化只分未熟和成熟另一个是精细化分级按商业收购标准分成五六档。两个方案最后都否掉了。档位太少模型对过渡期的果实完全没有感知能力可黄绿相间的过渡果实恰恰是果园里最常见的状态档位太多标注员之间的一致性很难保证标注质量的方差会超过模型本身的误差。最终定成未成熟、半成熟、成熟三个档位判断依据集中在果皮颜色占比和光泽度上。未成熟类以深绿色果皮为主果面光泽度低偶尔带浅绿色斑块半成熟类是黄绿夹杂或者整体转为黄色、浅橙色果面开始出现反光成熟类则是橙红到橙黄色占据绝大部分果面光泽明显。这个划分不是拍脑袋参考了果业常用的果实色泽分级逻辑同时简化到让不同标注员能快速达成共识的程度。实测下来标注组内一致率能稳定在95%左右这个结果远好于当初多档位方案的小范围测试。2. 类别定义与标注规范2.1 三个成熟度档位的速查规则为了让标注规则足够机械和可执行我们专门做了一张速查表挂在标注界面旁边。遇到拿不准的样本先查表再落笔而不是凭个人经验去猜。这里把核心内容整理出来类别典型外观判断要点常见易混情况immature未成熟以深绿、绿色为主果面无明显反光绿色面积超过80%表皮较粗糙被阳光直射到局部发黄的老青果semi-mature半成熟黄绿相间、整体黄色或浅橙色绿色面积在20%到80%之间果面光泽有过渡青果晒出黄斑、成熟果局部返青mature成熟橙红、橙黄色为主光泽明显绿色面积低于20%果皮颜色均匀浅橙色的晚熟果容易和半熟类混淆这张表的核心价值是把主观视觉判断转换成可量化的面积比例。标注员不需要像农艺专家一样凭感觉判断只要在画多边形之前先肉眼估算一下果皮绿色面积占比就够了。规则越机械标注一致性越好后续模型训练就越省心。真实处理中经常遇到黄绿边界到底算哪一类的争议最后我们的裁决原则是绿色面积卡在50%左右的归半熟明显超过80%的归未熟明显低于20%的归成熟。2.2 图像采集环境与多样性设计数据集的落地能力图像多样性比数量更关键。采集阶段刻意覆盖了多种果园环境晴天直射光、阴天散射光、早晨逆光、傍晚低照度树冠外围果和树冠内层果采摘前期的黄果和留树保鲜期的深橙果。之所以反复强调光照场景是因为成熟度的核心特征是颜色而颜色信息在不同色温下会剧烈变化。如果训练集里全是晴天正午的顺光照片模型一到阴天或者逆光环境就会明显失准。同时我们按果实个体而不是图像来划分数据。同一个小枝条上连续拍的几张照片里很可能包含同一颗果实。如果这些相似图像被动地同时落进训练集和测试集模型相当于提前看到了答案测试指标会虚高。我们建立了一个粗粒度的果实去重流程先按果园区域和时间批次做人工筛选再用特征匹配跑一遍疑似重复检测尽量保证同一个果实在不同数据子集里最多出现一次。采集时还专门记录了一些元信息比如拍摄日期、天气、果园编号、树龄。这些信息在训练阶段可能用不上但在分析模型错误时非常有用。比如某段时间测试结果突然变差翻元信息一看可能是新增了一批不同品种的柑橘图像果皮纹理差异导致模型泛化能力下降。没有这些记录排查会非常被动。2.3 LabelMe标注操作的几个关键细节标注流程本身不复杂但有几个细节很容易被忽略。第一画多边形时每个果实至少点8到12个控制点。拐弯处密集一点直边处稀疏一点让曲线尽可能接近真实果皮轮廓。如果控制点太少边缘会变成一条粗糙的折线后续生成的掩码会丢失真实的弧形边界信息。第二遮挡要诚实标注。果实被树枝横向穿过时只沿着可见果面的边界画不要试图脑补完整果实轮廓。分割掩码追求的是可见区域准确而不是果实物理意义上的完整形状。这直接影响机械臂的抓取判断宁可边界保守也不能把枝叶区域混进果实掩码。第三是标签命名的一致性。全程必须使用统一的英文小写命名比如immature、semi_mature、mature坚决不允许出现未熟1、green2这种变体。标注期间偶尔会出现同一类别被不同标注员写成不同字符串的情况我们每周汇总一次label统计发现异常别名立刻批量替换。第四每人每天标注量控制在80张以内并在团队里安排一位审核人做随机抽查。抽查重点不是看多边形画得好不好看而是看类别是否错分、遮挡边界有没有被明显扩大。单个图像不合格会被退回同一批次退回率超过15%就整批重标。3. LabelMe标注文件格式解析与自动化处理3.1 标注文件的关键字段结构LabelMe的标注文件是JSON每个图像对应一个JSON文件。一个典型的标注结构是这样的{ version: 5.0.1, flags: {}, shapes: [ { label: mature, points: [ [283.4, 194.2], [301.8, 182.6], [353.5, 177.1], [382.9, 198.7], [351.2, 238.3], [300.6, 239.5] ], group_id: null, shape_type: polygon, flags: {} }, { label: immature, points: [ [512.7, 421.5], [529.4, 405.8], [560.3, 422.1], [551.0, 455.3], [523.8, 458.0] ], group_id: null, shape_type: polygon, flags: {} } ], imagePath: IMG_1023.jpg, imageData: null, imageHeight: 1080, imageWidth: 1920 }几个关键字段一定要搞清楚。shapes是一个列表里面每个对象代表图中的一个实例多边形。label是类别名points是多边形坐标点序列顺序就是鼠标点下去的顺序构成一条闭合边界。shape_type固定为polygon。imagePath指向对应的图像文件imageData我们统一设为null避免JSON文件体积失控。imageHeight和imageWidth是从图像元数据直接读出来的后续做掩码映射时要用。理解这个结构很重要因为后面做格式转换、统计覆盖面积、生成训练掩码全都是围绕这份JSON展开的。团队新人在标注时偶尔会把一组点画两遍造成重复轮廓点训练时fillPoly会产生一个锯齿状掩码肉眼又不容易发现。所以在后面的自动化脚本里我加了一道顶点数量校验和去重逻辑宁可多花一点处理时间也不让脏数据进训练管道。3.2 JSON到掩码的转换脚本把LabelMe的JSON转成训练用的掩码是标准模型训练流程的第一步。这里分享一个可以直接修改使用的Python脚本import json import numpy as np import cv2 def labelme_to_mask(json_path, height, width, label2idx): with open(json_path, r, encodingutf-8) as f: data json.load(f) mask np.zeros((height, width), dtypenp.uint8) for shape in data[shapes]: if shape[shape_type] ! polygon: continue label shape[label] if label not in label2idx: continue pts np.array(shape[points], dtypenp.int32) pts np.unique(pts, axis0) if len(pts) 3: continue cv2.fillPoly(mask, [pts], label2idx[label]) return mask label2idx { immature: 1, semi_mature: 2, mature: 3, } mask labelme_to_mask(IMG_1023.json, 1080, 1920, label2idx) np.save(IMG_1023_mask.npy, mask)fillPoly走的是多边形填充逻辑会把多边形内部所有像素都改成对应的类别索引值。注意传入的points必须是二维数组形状是(N, 2)。这里有个容易踩的坑LabelMe导出的坐标是浮点数直接传给fillPoly在某些版本环境下会报类型错误所以代码里加了dtypenp.int32的转换。另外cv2.fillPoly对特别细长或者自交的多边形偶尔会出现填充不完整遇到这种情况先回原始标注确认是不是控制点顺序有误。如果你用的分割框架是Mask R-CNN那通常需要转成COCO格式。转换思路是生成一个包含images、annotations、categories三块内容的大JSON。每个annotation要包含image_id、category_id、bbox和segmentation坐标其中segmentation多边形坐标要压平成一维列表。写一个一百多行的遍历函数把shapes里的坐标和类别重新组织成COCO规范结构这个转换就完成了。转换脚本建议单独保存后续如果增加图像或修改类别重新跑一遍并不会花多少时间。3.3 标注质量自动检查脚本除了格式转换强烈建议做一轮自动质量检查在训练开始前把低级问题全部扫掉。我们当时写的检查脚本主要覆盖多边形顶点数是否小于3、坐标是否超出图像宽高范围、同一张图内是否存在完全重合的多边形、label是否都在预定义类别列表里。检查结果会输出成一个报表标注员拿着报表逐张去修比肉眼翻图高效太多。这里要特别提醒一个LabelMe的老问题图像在标注前后如果被旋转或者翻转JSON里的坐标不会跟着变。你以前用看图软件顺手旋转了方向但后面训练用的还是原图掩码就会错位。最稳妥的办法是标注完成之后不轻易旋转原图必须旋转时重新导出标注。我们在这个问题上踩过大坑排查了很久才发现是源图在批量压缩环节被旋转了方向最后重新对齐图像和坐标才解决。4. 基于数据集的分割模型训练实录4.1 数据划分与增强策略数据集到手第一件事不是直接扔给模型而是做合理的划分。我们采用果实级去重后的图像级划分训练集占75%验证集占10%测试集占15%。划分时记录每张图像对应的采集时间和果园区域避免同一天同一棵树附近的图像全部集中在某一个子集里。否则测试结果会虚高部署到新果园时立刻现原形。数据增强策略需要仔细设计因为颜色是成熟度的核心特征增强不能乱来。常规的随机裁剪、水平翻转、小角度旋转都用上了这些操作不会改变果皮颜色信息。颜色增强使用了HSV空间的轻微扰动重点是把饱和度S和亮度V的控制范围调小。原因很简单成熟度判断非常依赖色彩的细微差异S和V扰动过大会把青果颜色硬生生晒成黄果等于教模型学错误的知识。与此同时不建议使用随机擦除或者Cutout这类遮挡式增强。分割任务中果实重叠本身已经是不小的难度来源再随机遮挡模型会对被遮挡果实区域产生错误的学习信号最后推理出的掩码容易出现空洞。如果你一定要试建议做一次消融实验在测试集上对比IoU眼见为实。我们团队实测的结果是遮挡类增强带来的收益微乎其微反而让成熟类别的召回率掉了1到2个点。4.2 模型选型与训练参数配置模型选型上我优先考虑了U-Net系列和DeepLabV3两条路线。U-Net结构简单、参数少在中小规模数据集上表现稳定训练时间友好适合快速验证DeepLabV3的ASPP模块在感知多尺度信息上更强适合果实大小差异明显的真实果园场景。最终采用的是DeepLabV3搭配轻量骨干网络在精度和推理速度之间取了一个平衡。如果做采摘机器人还要考虑嵌入式平台部署输入图像分辨率控制在512到768之间比较合适太高会拖慢推理帧率。训练参数的经验值整理如下参数设置值说明输入尺寸512x512兼顾细节和显存占用批大小8单卡24G显存环境优化器AdamW初始学习率4e-4学习率策略余弦退火共训练80个epoch损失函数DiceLoss 0.5倍CE缓解类别不均衡骨干网络ResNet50与输入分辨率匹配数据增强翻转加旋转加HSV微调避免颜色失真评估频率每2个epoch关注验证集mIoU类别不均衡是这个数据集绕不开的问题。真实果园里未成熟果和成熟果比例往往不均衡半成熟果数量相对偏少。单纯用交叉熵损失模型很容易为了整体准确率牺牲少数类。Dice损失加交叉熵的组合在两类之间起到了平衡作用Dice损失天然对前景和背景比例敏感能同时拉一把少数类。训练后的合理基线大概是这样成熟类IoU在0.86左右半成熟类IoU在0.78左右未成熟类IoU在0.88左右整体mIoU能稳定在0.84以上。如果你的复现效果达不到这个水平优先检查标注质量和数据划分方式模型结构反而不是首要怀疑对象。4.3 采摘场景下的推理与粘连处理训练完模型只是第一步真实采摘应用里还有一个容易被忽略的问题相邻果实的粘连分割。分割模型输出的是一个个独立的连通域如果两个果实贴合太紧甚至互相遮挡输出的掩码就可能熔成一个连通域机械臂根本不知道抓哪一个。我们的处理方式是在推理阶段加一道后处理对掩码做连通域分析面积过大的连通域再结合果实的径向对称性做二次切分。这个后处理在测试集上能把粘连案例的正确率提升大概5个百分点对实际抓取帮助非常大。还有一种经验是置信度阈值设计。成熟度的边界不是一条硬线尤其是半成熟和成熟之间的过渡非常连续。建议模型输出时不要只取最大值类别而是保留每个类别的置信度分数。采摘决策环节如果mature类置信度低于0.7就让机械臂跳过这颗果实宁可放弃一个可摘果也不要冒险摘半熟果。这个策略在真实果园里验证过误摘率可以压得很低实际投入产出比很不错。5. 实战中的常见问题与排查技巧5.1 标注不一致怎么处理标注团队协作时间长了同一个果实被不同标注员画出差异很大的多边形是肯定会遇到的问题。有的标得圆润有的标得方方正正还有的把枝叶遮挡区域直接扩进了果实掩码。要解决这个问题光靠口头强调不够需要建立一套多轮评审机制。第一轮做软件层的自动校验检查多边形顶点数、面积大小、坐标越界这些硬指标第二轮人工抽查初期抽检比例30%等到一致率稳定后降到10%。如果发现某个类别错分率特别高也不要急着全数据集返工。先把错分样本按原因归类比如逆光导致颜色偏暗、果皮返青导致看起来像未熟等等再针对性地给标注团队补一次培训把典型错误样例放进评审手册。我们统计过三轮修正之后类别错分率从最初的8%降到了2%左右。这个数字直接反映在模型指标上半成熟类的召回率提升了将近4个百分点。5.2 颜色边界模糊导致误判黄绿夹杂的过渡果实是数据集里最难处理的样本也是模型最容易漂移的地方。训练前期发现验证集上半成熟类召回率偏低逐个翻错误样本才发现模型把很多边缘发黄的青果判成了半熟。原因是阳光直射下青果表面局部会出现黄白色晒斑从像素上看确实和半熟果的黄色非常接近。如果模型对这些局部色斑过于敏感后果就是采摘机器人总是优先去摘青色果子。处理办法是为这类难例专门补了一批青果有日灼斑的图像并在标注规范里补充了一条只要果面整体绿色面积超过80%即使有晒斑也归为未成熟。模型在训练中学会了对局部色斑的容错半熟类的误判率明显下降。这个案例暴露的深层问题是类别规则的边界定义要足够细。边界定义越清晰模型的决策边界才越干净。5.3 图像重复与时间序列污染果园巡检常常会在一段时间内对同一棵树反复拍照时间跨度短的图像之间果实位置几乎不变时间跨度长的图像之间果实可能已经成熟了。如果把同一颗果实的两个不同时段图像分别放进训练集和测试集测试结果就带上了时间穿越信息指标看起来很漂亮部署一测就全线崩溃。排查方法就是前面提到的果实级去重。实际操作中我们给每颗被采样的果实做了粗略的区域定位和枝条编号拍摄时记录树号和枝号后期划分数据子集时严格按这个信息来。如果项目前期没做记录可以用图像检索或者特征匹配把相似度超过阈值的图像归为一组人工确认后只保留一组放进同一个集合。去重阶段花掉的时间会让后续所有训练评估结果的可信度显著提升这笔投入非常值得。6. 项目经验与后续扩展思路6.1 这批数据最合适的几种用法经过多轮训练验证我对3000多张级别分割数据集的定位有了一条清晰的经验它最适合用来做技术选型和算法验证。比如你想对比不同分割模型在细长边界保持上的能力或者想验证哪种损失函数对颜色渐变类目标更友好用它都能得出相对可信的结论。它也可以作为预训练数据先让模型学会识别柑橘果实的边界结构再用少量业务现场数据微调迁移效果明显好于从通用图像分类权重冷启动。对已经有一定私有数据的团队我建议在此基础上叠加自己的采集场景。不同果园的品种、光照、树形差异很大任何公开数据集都没办法覆盖所有实际环境。把这份数据当作一个稳定的评测基准每个版本迭代时先在这个基准上跑通再放到自有数据上验证能少走很多弯路。测试集上的阈值调整和模型对比都必须基于同一个固定基准才有横纵向比较的意义。6.2 从成熟度分割到更完整的农业应用最后分享一个我们已经开始尝试的扩展。成熟度分割的掩码能做的事情比单纯的采摘识别要多得多。统计每个成熟度档位的可见果面积占比结合树冠体积模型就能估算整园的成熟可摘产量按时序对比同一棵树的掩码变化速率可以预测未来3到5天的集中成熟窗口再叠加天气数据还能为采摘调度提供更可靠的依据。这些应用本质上都在复用同一套分割输出的几何和类别信息数据集的边际价值会被持续放大。如果继续往前走下一个值得尝试的方向是联合检测成熟度和果实大小。分割掩码已经携带了直径和面积的几何信息用掩码的长轴和短轴估算果径可以直接指导分级分拣。这是在农业场景里投入产出比最高的技术演进之一不仅服务采摘端也能服务采后处理端。希望这套数据集能成为你项目里的一个稳定起点让你少走一些我们当年走过的弯路把精力真正花在解决实际问题上面。