资讯详情

资讯详情

茄子实例分割数据集解析:从目标检测到像素级mask的农业视觉实践

如果你也在做温室作物的视觉识别大概率遇到过这种场景茄子挂果密密麻麻用目标检测框架框出来的边界框东叠一个西叠一个NMS过完以后剩不下几个果实计数根本没法看。后来我把方案从目标检测换成实例分割给每个茄子单独输出一个像素级的mask漏检问题才明显缓解。今天要聊的就是面向这个场景的一份公开数据资源1类农学茄子目标实例分割数据集类别名是eggplant总共2766张图像训练集2261张、验证集505张图像分辨率集中在1280x1706附近同时带lab标注文件。我会从这份数据集的定位、数据划分、分辨率与标注管理、训练链路、实测踩坑到后续迭代完整拆一遍。1. 这份茄子实例分割数据集定位在哪1.1 检测框解决不了的数果难题为什么要交给mask去切目标检测和实例分割最大的差别在输出粒度。检测只给一个axis-aligned的矩形框框里除了茄子还有叶片、背景和遮挡物。当两个果实挨得很近时边界框会大量重叠后处理NMS要决定留谁舍谁很容易把其中一个真值目标直接滤掉。而实例分割在检测的基础上还要对每个目标的像素做分类输出一个二值掩码。框可以重叠mask可以互相压着但每个实例的边缘是清晰的计数只需要数连通域。做农业视觉的人对数果这件事应该深有体会。温室里茄子成串生长枝条间的果实彼此遮挡非常常见三四个茄子挤在一个矩形框里你甚至没法从框内区域判断到底有几个目标。分割掩码不一样即使是部分遮挡模型也能把露出来的轮廓切开只要mask面积达到一定阈值就可以算作一个有效实例。所以说如果项目的核心需求是株果比统计、机械臂采摘点定位、果实尺寸估计实例分割比检测高一个层次。1.2 只有一个eggplant类别说明任务边界被设计得很窄标题里明确写了类别名是eggplant没有第二个类别这是一份单类别的实例分割数据集。单类别带来的直接好处是模型不用花容量去区分茄子和其他蔬果之间的类间差异所有的学习精力都集中在轮廓拟合和前景分离上。对新手来说模型的训练难度、调参空间都小很多对工程项目来说单类别的分割模型更稳不容易出现跨类混淆导致的误检。但单类别也说明它的使用边界很清楚这份数据不做成熟度分级、不做病虫害识别、不做品种分类。想从这份数据出发做成熟和未成熟茄子的区分得自己补颜色层级或者新增类别。我见过有人拿着单类别数据硬套多分类任务最后验证时发现模型把绿色幼果和叶片混成一团问题不在模型而是任务和标签对不上。单类别的分割先做扎实再往上游加分类头才是这条数据线比较合理的延展路径。1.3 什么样的人适合拿这2766张图上手我理解这个标题的读者大概分三类。第一类是高校研究者和学生做农业视觉、果实分割方向需要一份任务边界清晰的数据集作为对比基线。第二类是智慧农业或温室自动化的工程团队要把果实分割模型落到采摘、计数、测产环节需要先拿现成数据验证流程。第三类是刚入门实例分割的开发者手头缺一个干净的单一类别数据集练手这份数据规模适中、标注结构清晰拿来跑通PyTorch或YOLO的完整训练推理链路非常合适。第一类和第二类读者拿到数据后切忌先盲目训练。先想清楚自己交付的是检测框还是分割掩码是离线统计还是实时推理模型部署GPU还是边缘设备。这些前置决策会直接影响后续所有操作。目录结构、图像分辨率、标注文件在标题里已经给了剩下的工程问题需要按任务重新设计而不是直接套默认模板。2. 2766张图的划分数字里藏着哪些工程判断2.1 2261与505这个82开对实例分割意味着什么标题里的数字很有意思2261加505正好等于2766和总数对得上说明这份数据在发布时已经做好了训练和验证的划分用户拿上手不需要再自己切一遍。2261除以2766约等于0.817标准的8比2比例。对单类别的实例分割来说2261张训练图是个合格的起步规模模型可以在这些图上学会茄子轮廓的基本形态505张验证图也足够给出稳定的mAP估计。相比动辄上万张的图像分类数据实例分割标注昂贵两千多张已经是不小的投入。不过要清醒看到这个规模对实例分割来说并不宽裕。推理阶段最难处理的遮挡目标、小目标、高光反光都需要大量极端案例喂给模型。假设每张图平均有三到五个实例那么实际训练样本量在6800到13800个mask左右如果要做高召回率的采摘系统这个量级肯定要扩充。我自己的经验是单类别实例分割低于五千个mask时模型很容易过拟合到采集现场的光照和背景上换个温室性能就掉。2261张只是能开始的门槛不是够用的保证。2.2 没有测试集的常见应对自己留一档标题里只写了训练集和验证集没有测试集这是公开数据集的常态。验证集用于训练中的早停、超参选择和模型保存严格意义上测试集要等实验全部结束以后再碰一次用来评估最终泛化能力。如果一直拿验证集反复调参最后得到的是一个对验证集过拟合的数字不是真实泛化水平。实操方向我建议把现有验证集再纵向切一次。比如505张验证图里留出100到150张作为test set剩下的继续当日常验证。另一个做法是K折交叉验证把2261张训练图分成五折轮训每轮用四折训练、一折验证最后取mAP均值。这两种方案都能降低调参时的数据偷看风险。切分时注意按图像ID切而不是按实例切同一张图里的不同mask不能出现在两个集合中否则会构成数据泄漏。2.3 同类数据下分布不均比数量不足更致命很多人拿到数据只关心总数忽略分布。实例分割的分布问题至少有三个维度第一是生长阶段幼茄细长、成茄粗短轮廓差异很大第二是光照条件温室顶光、逆光、人工补光下果实表面纹理和阴影完全不同第三是遮挡程度单果、双果重叠、多果成串mask的完整度天差地别。如果数据随手一划训练集全是单果清晰图验证集塞满重叠成串的图训练曲线会非常难看——训练loss一路下降验证mAP反复震荡上不去。正确做法是先做实例分布统计画每张图的实例数和mask面积直方图再做分层采样保证训练集和验证集都有相似比例的清晰图、遮挡图、小目标图。这一步虽然烦琐但能省掉后面至少十次无效调参时间。调参救不回来的分布问题只有数据工程能解决。3. 分辨率1280x1706和lab标注决定了后续所有操作3.1 为什么这个分辨率对mask边缘精度很重要1280x1706约等于218万像素接近3比4的竖向构图这个画幅在农学数据采集中很常见通常来自手持设备或固定机位竖拍。高分辨率对实例分割的意义在边缘细节茄子的萼片锯齿、果面高光、果柄连接处这些在低分辨率下会被糊成一片的区域1280宽度下还能保留轮廓形状。分割掩码的质量本质上取决于边缘的像素分类准确度输入分辨率越低边缘越容易被压缩成几个像素最终mask的IoU就上不去。但高分辨率也不能直接扔进网络。大部分实例分割框架默认输入是方形比如640x640或1280x1280。如果无脑resize成6401280x1706的长边被压到640短边只有约480信息损失很大。更合理的是用letterbox等比缩放保持长宽比剩余区域填灰边或者直接设置imgsz1280训练。1280输入显存需求大概是640的四倍8GB显卡几乎跑不动大模型的训练所以这个分辨率既是精度资产也是显存负担要根据硬件条件取舍。3.2 三种主流的实例分割标注格式拿到手先认格式标题末尾的lab我按行业习惯理解为label标注文件。实例分割数据集的标注格式常见的有三种。第一种是COCO JSON整套标注集中在一个annotations.json里每个实例有segmentation字段存着多边形顶点坐标或RLE编码。第二种是YOLO分割格式每张图对应一个txt行格式是class_id x1 y1 x2 y2 ...所有坐标除以图像宽高做归一化。第三种是VOC风格每张图用XML描述目标框和分割文件路径分割掩码通常单独存成PNG。拿到数据的第一件事不是加载训练脚本而是打开标注文件确认格式归属。我见过太多人在网上下了一份数据没仔细看格式直接把JSON喂给YOLO报错以后开始怀疑环境坏了。先认格式再选转换工具这两步顺序别颠倒能节约至少半天。另外如果标注文件里同时存在一个实例的mask和bbox要确认引擎在转换时是否会自动从mask重新计算bbox有些框架允许两者不一致这在后处理阶段会带来奇怪行为。3.3 训练之前必做的一次可视化审核拿到标注之后无论如何都要先做一次可视化。用Python读取标注文件把mask或者polygon画在原图上随机抽五十张人工扫一遍。重点看三类问题掩码是否紧跟果实边缘有没有把背景叶片圈进去同一个果实是否被重复标注成两个实例是否存在面积极大或极小的离群mask比如只有几十个像素的噪声标注。这一步重要是因为实例分割训练一旦跑起来标注错误会被模型原样学走甚至推理时放大。边界标得马虎的mask会让seg_loss长期降不下去漏标的果实会直接教模型看到它也不要输出。可视化审核用OpenCV加几十行脚本就能完成。如果发现明显系统性问题比如所有mask都比真实轮廓偏大一圈回退给数据端修正比花两周调模型参数划算得多。先数据后模型这是我在多个数据集项目里验证过的原则。3.4 从标注规模看这份数据集的成本结构2766张图听起来不多但乘以每张图的实例数就是另一回事。假设平均每张图3到5个茄子实例总标注量在8300到13800个mask之间。熟练的标注人员用多边形工具给每个果实画贴合轮廓的mask一分钟到几分钟不等再加上审核、修改、格式转换整份数据的标注成本不低。这从侧面说明这份数据的价值不在量大而在于每个mask都经过了质量控制可以直接作为任务基线。反过来这也提醒我们复用时别浪费。很多人数据增强时随手把图像上下翻转、随机裁剪却从不检查裁剪后mask是否出界、翻转是否改变果实形态。与其花大价钱新增标注不如把现有mask用足。成本结构决定了这类数据集适合迭代式使用先跑基线再用模型挑困难样本最后只针对困难样本做定向标注每一份标注预算都花在刀刃上。4. 用这份数据把模型真正跑起来从格式转换到训练曲线4.1 框架选型YOLOv8-seg为什么是我默认的第一选择实例分割框架选择不少。Detectron2稳定、学术生态好MMDetection功能全但配置层级深PaddleDetection对国产硬件友好。如果让我给一个默认第一选择我推YOLOv8-seg。理由有三个第一从标注到训练到部署的链路最短命令行和Python API封装得很顺手对刚接触分割的人最友好第二模型家族从n到x不同规模单类别数据量不大选s或m规模足够第三导出ONNX和TensorRT路径成熟从实验到落地不用重写代码。不是其他框架不好。复现论文里Mask R-CNN的基线指标Detectron2的config体系更贴近学术习惯做复杂的多阶段项目MMDetection模块化带来灵活度。就这份单类别茄子数据来说复杂度不高YOLOv8-seg用最小的认知成本帮我把流程跑通等流程通了再按需换框架成本低得多。4.2 把标注转成训练格式避开坐标归一化的坑假设拿到的是COCO JSON第一件事是转成YOLO分割的txt格式。核心逻辑不复杂读取每个实例的segmentation多边形坐标所有坐标除以图像宽高得到归一化坐标然后拼成class_id加坐标串写进txt。最容易出错的点有两个一是原始polygon坐标可能是浮点二是某些标注工具有多环多边形外环加内环转换时只取了第一个环导致部分mask变成空心。写转换脚本时最好加校验每个txt坐标数量是否为偶数坐标值是否落在0到1之间。下面给一个COCO JSON转YOLO分割txt的参考脚本我处理类似数据集时基本是在这个框架上改的import json import os def coco_to_yolo_seg(json_path, out_dir): with open(json_path, r, encodingutf-8) as f: coco json.load(f) img_map {img[id]: img for img in coco[images]} cat_map {cat[id]: cat for cat in coco[categories]} for ann in coco[annotations]: img img_map[ann[image_id]] cat cat_map[ann[category_id]] w, h img[width], img[height] seg ann[segmentation][0] points [] for i in range(0, len(seg), 2): x min(max(seg[i] / w, 0.0), 1.0) y min(max(seg[i 1] / h, 0.0), 1.0) points.append(f{x:.6f} {y:.6f}) txt_path os.path.join(out_dir, img[file_name].replace(.jpg, .txt)) with open(txt_path, a, encodingutf-8) as g: g.write(f{cat[id] - 1} .join(points) \n)脚本里有个细节COCO的category_id通常从1开始YOLO的class_id从0开始所以转换时要减1。这份数据只有一个eggplant类别简单处理即可。跑完脚本后再做反向校验随机选几张原图把txt坐标换算回像素坐标画在原图上和原始标注对比确认坐标没有平移或缩放。4.3 训练参数这么定第一轮别乱调数据准备好以后训练命令可以很简洁。以YOLOv8-seg为例yolo segment train dataeggplant.yaml modelyolov8s-seg.pt epochs100 imgsz1280 batch8eggplant.yaml里写清楚train和val的图片路径以及nc1、names[eggplant]。第一轮训练我建议全部用默认超参最多改三个参数epochs设为100imgsz按显存情况设640或1280batch按显存上限来。有人一上来把学习率从0.01改成0.001觉得改一下就能避免发散其实大多数情况只是浪费第一轮时间。先让默认参数跑一轮拿到baseline再做后续实验才符合科学迭代的顺序。imgsz的选择依赖硬件。8GB显存跑1280的yolov8s-seg基本不可能选640更实际14GB以上显存1280值得投资从640提到1280对边缘精度和小目标召回率的提升非常直观。batch尽量往大了开显存允许就开16梯度估计更稳定收敛速度通常更快。预训练权重yolov8s-seg.pt建议保留单类别任务从COCO权重起步会明显缩短训练时间。4.4 训练过程看曲线比等mAP更早暴露问题训练开始以后不要只盯每个epoch结束打印的mAP50和mAP50-95中间的loss曲线才是诊断工具。YOLOv8-seg训练时控制台会输出box_loss、seg_loss、cls_loss、dfl_loss四个指标。对分割任务来说seg_loss是关键它衡量mask预测和标注mask的差异。如果seg_loss在某个epoch后不再下降甚至反弹优先怀疑两种可能一是训练集里出现大量标注不一致的mask二是当前学习率下模型容量到了瓶颈。还有一个常见情况训练曲线一路下滑但验证mAP长期不动。这种训练好、验证拉胯的情况多半是过拟合信号。解决思路不是继续加epoch而是增强数据增强强度、提高权重衰减或者换容量更小的模型。如果验证mAP在末尾还在缓慢上升说明epoch可以再加但每次加三五十个不要翻倍。训练结束后best.pt和last.pt都保留后者在继续训练或做知识蒸馏时可能比best.pt更好用因为它没被验证集上的偶然波动打断。5. 实测阶段最容易翻车的三件事5.1 绿色萼片把模型带偏的误检茄子果实顶部有一圈绿色萼片和叶片同色系这是最容易引入误检的地方。模型如果只靠颜色纹理区分前景很容易把带叶片边缘的区域也当成茄子轮廓推理结果里出现长条形的绿色噪声mask。温室环境下尤其明显背景叶片密集绿色像素占比极高模型稍微放松一点就会把叶片归为果实。我处理这类问题的经验分三层。第一层在标注上统一规则mask是否包含萼片整个数据集里必须保持一致不能有的标萼片有的不标模型学边缘会被不一致搞乱。第二层在增强上加HSV色彩抖动和对比度扰动让模型不再依赖固定的绿色区间用mosaic把背景多样性拉高。第三层在推理上把conf阈值从默认0.25提到0.4左右。宁可漏少量低置信度果实也别让背景噪声混进计数。调阈值成本极低收益往往立竿见影。5.2 果实重叠后mask只露半边脸的漏检茄子成串生长意味着前果挡后果是常态。对实例分割来说被遮挡的果实通常只能标注出可见部分模型学到的也是露出多少就分割多少。但如果遮挡比例太高比如露出区域不到整个果实的20%模型经常会直接判定为背景。问题的本质是训练集里极端遮挡案例太少模型没见过只露一截也要输出mask的情况。想提升这部分召回建议先统计训练集里不同遮挡程度下的实例占比极端遮挡案例不足就人为构造用copy-paste增强把切出来的茄子mask叠到其他果实附近模拟不同遮挡比例。另一个思路是配合视频帧连续信息用前后帧互相补。单帧模型的上限摆在那里压不出新潜力。如果做采摘系统宁可多一个半遮挡mask也比漏掉果实好机械臂后续还有末端定位环节可以兜底。5.3 缩小图像后小目标直接从mask里消失我用640x640训过一轮发现最明显的问题是远处枝条上的小果预测结果里几乎不出现完整mask要么被忽略要么只输出一个几个像素的小点。这个现象在高分辨率数据集里非常典型原图1280x1706一串小型幼果可能只占整张图的2%缩放到640后对应区域只剩十几像素宽轮廓细节被彻底压平分割头无从下手。针对小目标优先级最高的方案是把imgsz从640提到1280代价是显存和训练时间翻倍收益是小目标召回率直观提升。硬件实在扛不住就做patch训练原图按网格切成若干子图每个子图独立训练和推理推理时再把结果拼回原图坐标。这个方案绕过显存瓶颈代价是多一次或多次切片操作。对小目标占比高的农业数据集强烈建议先看mask面积分布再定训练分辨率这个习惯能帮你少走几轮弯路。5.4 外加一个单图验证流程排查起来更顺手训练结束别急着看汇总指标先把推理脚本搭好才能针对具体图像定位问题。最小可用的单图验证流程加载best.pt指定一张验证图像设置conf和iou阈值保存带mask的可视化结果同时打印该图检测到的实例数量、平均置信度和mask像素数。from ultralytics import YOLO model YOLO(runs/segment/train/weights/best.pt) results model.predict( sourceval_sample.jpg, conf0.4, iou0.5, saveTrue, save_txtTrue, showFalse, ) for r in results: print(instances:, len(r.boxes)) print(conf:, r.boxes.conf.tolist()) print(mask areas:, r.masks.xy.sum(axis1) if r.masks is not None else None)有了这组输出配合可视化图像就能快速判断当前问题是误检、漏检还是阈值太严。如果conf都在0.8以上但count远低于真值说明模型漏检和阈值无关该查小目标和遮挡如果conf在0.3到0.5的预测很多但都是噪声该提阈值或修标注。这个调试闭环比反复盯mAP曲线高效得多。6. 从能跑的模型到田间能用还差哪几步6.1 用主动学习做数据迭代比闷头加数据更划算如果2766张图只是起点后续扩充数据最划算的方式是主动学习而不是随机采集标注。流程很简单拿当前模型推理一批未标注的新图像按置信度排序优先挑两类样本——置信度特别低的模型觉得不像茄子很可能是没见过的形态置信度在0.4到0.7之间徘徊的模型拿不准往往是难例富矿。把这些样本交给标注人员修正效率和随机标注比会高很多。我实践下来三轮主动学习迭代后数据规模从2766涨到四五千张模型mAP的提升通常比相同数量随机采样高两三倍。原因是农业现场的长尾分布太明显强光、逆光、密集串果、雨滴附着这些极端情况在随机采样里占比很低主动学习会主动把它们筛进训练集。数据迭代的终点不是图够了而是难例覆盖率够了。6.2 针对温室光照和果实密度的增强清单农业场景的数据增强不能照搬通用数据集那套。我的建议清单如下色彩层面HSV色相、饱和度、亮度扰动必须开温室光照一天之内变化很大模型学死某个亮度范围换个天气就崩。空间层面mosaic和copy-paste是两大利器mosaic能把四张图拼成一张提高密度多样性copy-paste可以把分割出来的茄子实例贴到其他背景上低成本模拟不同密度和遮挡组合。尺度层面随机缩放要小心原图已经是高分辨率过度缩放会把小目标弄丢建议控制在0.6到1.2倍之间。有一个增强禁忌需要提醒后续任务如果要做机械臂抓取点估计或果实朝向判断水平翻转和旋转要谨慎。翻转会改变果实相对茎秆的方向模型学会翻转后依然能分割但对方向敏感的下游模块是误导。通用分割任务无所谓精密农业任务要先确认下游需不需要方向信息再决定翻转增强的开关。6.3 部署到边缘设备时的取舍思路从实验模型到田间部署第一个要砍的是模型规模。单类别分割任务不需要yolov8x-seg这种大模型yolov8n-seg或yolov8s-seg经过蒸馏后通常够用。实测里n模型mAP比s模型低两三个点推理速度能快一半以上对实时采摘或计数系统来说非常划算。第二个是推理后处理把置信度阈值、NMS阈值、最大检测数量在导出时固定住运行时不做动态调整能省不少CPU算力。部署链路我一般走训练好的best.pt导出ONNX再做TensorRT的FP16量化输入分辨率固定为640或1280的letterbox规格。在边缘设备上FP16量化后的n模型对640x640输入的推理延迟通常能压到实时水平分割帧率比FP32提高一到两倍。量化带来的精度损失对单类别果实分割基本可接受mask IoU掉一两个点但推理速度翻倍实际项目里是划算的。如果做完全部优化还是不够快再考虑裁剪输入分辨率或改用patch推理。每砍一档都要回头验证下游任务指标别只看单帧延迟。这篇文章写到最后我还是想再把那个老观点强调一遍实例分割数据集的价值一半在标注一半在怎么用。我在实际跑这份茄子数据时踩过最亏的一次坑是把所有图像统一拉伸成正方形再训练后来一查小果实的mask面积整体缩水了将近三分之一。改成按原图比例做letterbox之后同样的模型mask IoU直接涨了约四个点这个细节论文里通常不会写。最后再分享一个小技巧训练完别急着删last.pt保留一份开启测试时增强的推理结果做对照。有时候标准推理看着不行的图TTA能拉回来不少尤其对遮挡严重的果实串留着这份对照能帮你判断到底是模型不行还是推理配置没给到位。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →