橙子成熟度检测数据集:YOLOv5-ready的构建与训练实践 简介面向农业自动化和柑橘智能化检测场景可直接获得一套YOLOv5兼容的橘子成熟度目标检测数据集按成熟/未成熟两类标注图像质量统一、边界框清晰完整可省去数据整理与格式转换环节。数据集包含2313张训练图与224张验证图均为640×640 RGB格式每张图配有对应YOLO格式txt标签目录按images/与labels/标准结构组织导入训练环境即可开工。整包共177个文件以171张JPG图像为主体另含标签txt、可视化脚本show.py及pt权重文件压缩包仅13.29MB轻量易下载。附带的show.py无需修改参数一键随机加载图片并叠加显示标注框自动保存结果方便快速核验标注质量。已有38人浏览学习对柑橘分级、成熟度识别或采摘机器人开发而言是一套非常省心的数据基础。1. 为什么我决定做一个“能直接训练”的橙子成熟度数据集做目标检测项目的人应该都有这种体会真正卡住进度的往往不是模型结构、不是显卡算力而是手里没有一份干净、规范、标注靠谱的数据集。尤其是农业场景想找一个现成的橙子成熟度检测数据集搜索结果翻来翻去要么是学术论文里只给了链接的哈佛数据集要么是几百张图片没做任何划分的随手打包拿来训练基本属于自找麻烦。这个YOLOv5-ready的橙子成熟度数据集核心就做了一件事把两类标注ripe成熟、unripe未成熟的橙子图片整理成开箱即用的格式附带train/val划分和可视化脚本喂给YOLOv5直接就能跑训练。这次分享不只是在说“我传了个数据集上去”而是把整个数据构建过程的思考、踩坑和验证方式完整拆开来讲。适合谁来参考两类人第一类是正在做农业目标检测、水果分拣相关课题的研究者或开发者这个数据集可以直接作为baseline数据用第二类是刚开始学YOLOv5、想走一遍“自定义数据集训练”全流程的初学者建议不要只下载完就跑跟着后面的可视化脚本和检查清单过一遍比闷头看文档有效得多。2. 数据采集与标注两类标签背后的“成熟度”判断逻辑2.1 采集场景怎么选直接决定模型的泛化上限这个数据集里的图片来源基本覆盖了橙子种植和流通环节的真实场景果园树上的自然光照状态、采摘后的堆叠状态、分拣线上的俯拍角度、不同成熟度混放的情况。这不是随手凑数而是有意为之。原因很简单目标检测模型学的是“外观特征上下文信息”的组合。如果只在单一背景下采集模型很容易把背景特征当成橙子特征的一部分换到真实分拣环境马上掉点。比如树上的橙子有树叶遮挡、有光照不均的问题这训练出来的模型到田里还能用但如果训练集全是白色背景下的商品图部署到果园就完全废掉。采集时还有一个常被忽略的点分辨率和拍摄距离。数据集中大部分图片是手机和普通数码相机拍摄的单张图片里橙子的像素尺寸在几十到几百像素之间浮动。这符合YOLOv5的实际使用场景毕竟部署端可能是树莓派上的摄像头也可能是分拣线上的工业相机目标大小本来就差异很大。2.2 “成熟”和“未成熟”的边界其实是标注规范在起作用两类标注——ripe和unripe——很多人以为看一眼颜色就能标实际上真的标注起来会碰到不少灰色地带。我的标注规范参考了果农的实际判断逻辑而不是单纯依赖RGB值ripe果皮呈橙色到深橙色色泽均匀有光泽感果实饱满。即使果蒂附近有一点点青色只要整体已经转橙就归为ripe。unripe果皮以绿色为主或者大面积青绿色、黄绿色果实偏硬朗、色泽暗淡。黄中带绿但明显没转橙的归为unripe。不标注的情况被遮挡超过50%的果实不标严重模糊、对焦失败的图不标果实只有一小条露在画面边缘的不标。这种“宁可少标不可错标”的原则大幅度减少了训练时的噪声。这里特别说一下为什么颜色边界不能一刀切。同一个品种的橙子在转色期会出现黄绿混杂的状态不同光照条件下相机拍出来的色温差异也很大。如果严格规定“绿色像素占比低于某个阈值就算成熟”反而会因为光照影响误标一堆样本。所以标注规范里加入了果形、光泽、果蒂状态这些辅助判断实测下来对模型收敛的帮助比像素级颜色判断大得多。2.3 标注格式统一到YOLO txt格式少走很多弯路考虑到数据集的定位是“YOLOv5-ready”标注文件没有用COCO的JSON格式而是直接输出成YOLO格式每张图对应一个同名txt文件每行是class_id x_center y_center width height坐标值全部归一化到0-1之间。当时这样选的原因很直接YOLOv5的dataset配置文件直接支持这种格式Labelimg或AnyLabeling标注完导出YOLO格式就能直接进训练流程不需要写额外的转换脚本。而且后续如果想转成COCO格式喂给其他模型用ultralytics仓库里的转换工具也花不了几分钟。有个细节容易被新人忽略YOLO格式里的框坐标是归一化值但标注工具里显示的是像素值。如果你用的是Labelimg导出时务必确认导出设置里选的是YOLO格式而不是PascalVOC。之前见过有人用VOC的XML喂给YOLOv5结果在数据加载阶段报了一堆错排查半天才发现是格式不匹配。3. train/val划分不是随机抽样那么简单3.1 划分层的逻辑按图像来源分组而不是按文件随机分从数据集里随机抽20%当验证集这个做法在学术demo里没问题但在实际项目中会让验证集的评估结果虚高。原因在于同一时间、同一场景拍摄的连续帧之间高度相似随机划分容易把同一场景的图像分别塞进训练集和验证集模型等于“开卷考试”。这个数据集的划分方式是按图像来源的场景分组后再整体分配。具体做法是先从采集记录里把图片按拍摄批次果园点位、时间段、光照条件归档每个批次作为一个unit整体分配到train或者val保证同一个场景的照片不会同时出现在两个集合中。最终比例大约在train 80%、val 20%图片总量上百张的量级虽然不算海量但类别均衡性和场景覆盖度才是这个数据集的重点。这样做的代价是验证集和训练集之间的分布差异会明显一些模型在val上的mAP会比随机划分低几个百分点。但这才是真实水平——部署到新果园、新光照环境时模型的表现就应该以这种“严格模式”评估为准。3.2 配置文件与目录结构直接clone下来就能用YOLOv5的目录结构要求比较明确数据集根目录下需要images/train、images/val、labels/train、labels/val四个标准子目录。这个数据集也按同样的约定组织orange_maturity_dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ ├── README.md └── visualize_dataset.py配套的orange.yaml内容如下train: /path/to/orange_maturity_dataset/images/train val: /path/to/orange_maturity_dataset/images/val nc: 2 names: [unripe, ripe]注意类别顺序names列表里unripe在前、ripe在后class_id从0开始。如果你后续用其他模型做迁移学习Class 0对应的是unripe这个顺序在数据处理阶段就要想清楚别等训练完看混淆矩阵才反应过来标签反了。3.3 每类样本数量差异带来的正负样本平衡问题两类标注的数量并没有完全做到1:1。橙子的成熟期是一个持续推进的过程采集到的unripe样本量会略少一些这是客观现实。处理方式没有选择简单的过采样复制而是靠调整采集策略来补去果园时刻意多拍了一些果皮偏绿、转色早期的果实让unripe的数量尽量贴近ripe。如果后续你自己扩展采集数据建议保持这个原则靠增加真实样本的多样性来平衡类别而不是靠复制图片或加噪声生成合成样本。目标检测模型对重复样本的记忆非常敏感用复制样本强行平衡轻则过拟合重则让模型对某些特定角度过度敏感。4. 可视化脚本验证标注质量的最后一道防线4.1 写这个脚本的动机光看txt文件根本发现不了问题标注质量出问题最坑的地方在于YOLOv5不会直接报错——框坐标超出图像边界、类别串号、标注框和实际目标严重错位这些都不会阻止训练启动只会让模型默默学到错误内容最后体现在mAP和PR曲线上排查时已经浪费了大量训练时间。可视化脚本就是干这个的把标注框直接画回到原图上生成一张带框的图片让人眼快速判断标注是否正确。核心代码逻辑不复杂import cv2 import glob def draw_yolo_boxes(image_path, label_path, class_names): img cv2.imread(image_path) h, w img.shape[:2] with open(label_path, r) as f: for line in f.readlines(): parts line.strip().split() cls_id int(parts[0]) x_center, y_center, box_w, box_h map(float, parts[1:]) x1 int((x_center - box_w / 2) * w) y1 int((y_center - box_h / 2) * h) x2 int((x_center box_w / 2) * w) y2 int((y_center box_h / 2) * h) color (0, 255, 0) if cls_id 1 else (0, 0, 255) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, class_names[cls_id], (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) return img class_names [unripe, ripe] output_dir visualized/脚本本身是拿来就能用的但重点在于用它执行一套检查流程而不是走马观花看图。4.2 抽检策略每一批数据都按比例可视化而不是随机抽几张随机抽几张图看一眼效果约等于没检查。这个数据集在交付前跑了一轮全量可视化生成的方式是把所有标注框的数量、尺寸分布、中心点分布统计出来先看数据层面是否有异常再逐张看可视化图。常用的快速筛查指标有三个标注框宽度和高度的比值橙子接近圆形宽高比应该在0.7到1.3之间如果出现大量细长框说明标注出错、标注框中心点的热区分布如果所有框都在画面中心区域说明边缘目标没被标到、单张图上的目标数量橙子堆叠场景目标数会明显偏高但正常单果场景不会出现十几个框。可视化脚本跑完一遍后我通常还会做一次人工抽帧——按train/val目录每20张挑一张把带框图认真看一遍重点检查三类问题误标把树叶、阴影当成了橙子、漏标图片里的橙子没有框、错标ripe和unripe标签贴反了。慢是慢一点但这一步省掉后面训练出问题再排查花的时间是十倍还不止。5. 数据集配合YOLOv5训练时的几个实测要点5.1 环境搭建和超参数选择不用盲目照搬围绕这个数据集训练YOLOv5环境方面常见的坑集中在显卡驱动和依赖版本上。PyTorch版本跟CUDA不匹配、cuDNN版本不对都会在训练启动阶段报一些让人摸不着头脑的错误比如CUDA error: no kernel image is available。这类问题通常不是代码的错误而是环境问题建议先查驱动和PyTorch版本的匹配关系再回头看代码。超参数方面数据集规模不算大的前提下不建议去动hyp.scratch.yaml里的学习率、动量这些全局参数直接把默认配置跑起来效果通常已经不错。值得关注的几个参数是--batch-size、--img-size和--epochs。图片尺寸用YOLOv5默认的640即可batch size根据显存调整——6G显存用812G显存用16这个数据集的图片分辨率不会把显存压力推得太高。epochs在100到200之间就能看到明显的收敛趋势不要一上来就300 epochs起步先跑100个epoch看val曲线再决定是继续训练还是加数据。5.2 训练过程中看哪几条曲线别只盯mAP训练启动后很多人习惯只盯着mAP看但mAP是最终指标训练过程中更重要的是loss曲线的形态。train/box_loss下降平稳、没有剧烈震荡说明标注框质量没问题val/box_loss和val/cls_loss在训练后期不再下降甚至回升说明过拟合苗头出现这时应该考虑早停或者数据增强val/cls_loss如果一直下不去优先怀疑类别标注的噪声回去用可视化脚本重新检查标签。初始跑出来的mAP0.5不应该低于0.85如果明显低于这个水平先不要急着调模型回到数据层面找问题图片是否模糊、标注是否错位、类别是否不均衡这些比换backbone的影响大多了。5.3 对这个数据集来说哪些数据增强值得开、哪些建议关YOLOv5默认开启的增强包括随机平移、随机缩放、HSV色域变换、Mosaic等。对橙子检测场景hsv_h、hsv_s这些颜色相关增强是友好的因为橙子的颜色和背景差异明显微小的色域扰动相当于做了一次光照校正泛化能力会变好。需要谨慎的是degrees旋转增强。橙子是近似圆形的旋转增强的影响虽然不会致命但训练集里如果存在大量旋转样本模型学到的是“看圆形”对其他形状的水果参考意义不大。建议把degrees设置为0或者一个很小的值比如5度以内用来抵消拍摄角度的轻微偏差就够了。另外fliplr水平翻转建议开启这个增强对任何目标检测任务都是安全的能有效提升样本多样性。mosaic增强在这个数据集上也有正向收益因为它模拟了果实堆叠场景和真实分拣环境一致。6. 从数据集到落地部署这条路还能往下走很远这个橙子成熟度数据集目前定位是基础版两类分类、标准YOLO格式、中小规模、单场景覆盖。它可以直接用于目标检测模型的训练入门、迁移学习的baseline、以及农业视觉算法的快速验证但真正用到工业分拣场景还有几个明确的方向可以扩展。第一个方向是增加类别粒度。现在只有ripe和unripe实际分拣需求往往要区分“成熟”、“接近成熟”、“未成熟”多个等级这直接在现有标注体系上细粒度化即可数据集的目录结构和脚本不需要改动。第二个方向是增加品种维度不同品种的橙子在颜色、纹理、光泽上的差异会直接影响模型的迁移效果目前标注的是单一品种跨品种泛化属于后续验证的重点。第三个方向是模型部署结合树莓派5或NXPi.MX8MP这类边缘设备把训练好的模型导出为TensorRT或NCNN格式做端侧推理这又是另一个值得展开的话题。从我实际使用的角度说一句数据集的构建难度从来不在采集本身而在于如何让数据在“采集—标注—清洗—训练—验证—反馈”这条链路中流动起来。这款数据集能直接把前面几环打通剩下的反馈环节就得靠在使用过程中自行积累了。本文还有配套的精品资源点击获取