ARTICLE DETAIL

资讯详情

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

YOLOv5橙子成熟度检测数据集:从标注到训练全流程实战

YOLOv5橙子成熟度检测数据集:从标注到训练全流程实战 简介一套面向柑橘成熟度识别、果实分级和采摘机器人等农业自动化场景的YOLOv5目标检测数据集类别聚焦成熟与未成熟两个状态标注边界框清晰完整可直接服务于成熟度判别、果实计数等视觉应用。资源以zip压缩包形式提供共177个文件主体为171张640×640像素的彩色JPEG图像并附带2个与标注相关的txt文件、无需改参的可视化脚本show.py以及pt模型文件整体大小约13.29MB体积紧凑便于下载迁移。数据已按images与labels子目录整理并给出训练集与验证集的划分结构开发者可将数据直接放入标准YOLOv5工程减少数据清洗、格式转换和标注筛选等重复劳动。show.py脚本会自动随机加载一张样图并叠加显示边界框结果保存到当前目录便于快速验收标签质量。目前已有38人浏览学习适合目标检测初学者或农业视觉项目开发者作为训练基线也便于在此基础上扩展成熟度分级或采摘机器人应用模块。 做目标检测的伙计们应该都有体会搞模型之前最头疼的就是数据。图像分类有现成的ImageNet可以直接用检测这块想找个合适开源数据集真得碰运气。这套YOLOv5-ready orange maturity dataset是我最近在农业视觉项目里整理出来的2类标注的橙子成熟度数据集图片区分ripe成熟和unripe未熟两种状态搭配了train/val划分和可视化脚本拿到手就能直接跑YOLOv5训练。这篇主要聊聊这套数据集从采集、标注到划分、可视化的完整链路适合刚接触目标检测、想拿真实农业场景练手的同学参考。先说一个基本判断数据集质量决定了模型上限后面所有训练技巧都是在补救数据问题所以花时间把数据准备这步做扎实比反复调参划算得多。下面我把这套数据集的设计思路、标注规范、划分逻辑、可视化脚本以及实际训练中踩过的坑一次讲透。1. 为什么折腾这套橙子成熟度数据集1.1 农业检测场景里成熟度判断是第一道关卡农业视觉这块目标检测最常见的任务就是分拣、测产、采摘定位。分拣线上成熟度直接决定果子的收购等级一个果子因为颜色不均匀可能被分到三个不同的等级采摘机器人要动手之前第一步也得先确认果子到底熟了没摘早了口感不行摘晚了运输损耗大。以前这些判断全靠果农肉眼量大之后效率跟不上去自动化检测也就成了刚需。但橙子的成熟度检测有个天然难点成熟期不是所有果皮都变橙有些果子受光面先红背光面还是青绿色单凭颜色阈值根本切不干净。这就意味着成熟度不能简单用图像处理判断得靠模型学习上下文特征把颜色、纹理、形状甚至光照条件综合起来判断。这也是我选目标检测来做这套数据集的根本原因用深度学习模型替代人眼判断鲁棒性更高。1.2 数据集标准化的价值与YOLOv5的适配很多同学跑YOLOv5直接拿COCO练手虽然COCO里也有橙子这个类别但成熟状态是混在一起的更不会把未熟单独标出来。真实落地的时候你会发现通用数据集和专用数据集完全是两码事模型虽然见过橙子但不代表它能区分这个橙子能不能摘。这套数据集我特意做成YOLOv5-ready的格式核心思路是把手动预处理的工作全部前置。数据目录按官方建议组织标注文件直接生成YOLO格式的txtdata.yaml也都配好用户拿到手改改路径就能开训。这一步看起来简单实际省掉了很多重复沟通成本。之前我见过不少新手把数据集拷过来之后光整理目录格式就折腾一整天这套结构能直接避免这个问题。2. 数据集结构与标注规范2.1 目录组织与data.yaml配置YOLOv5对数据目录有一套约定俗成的组织方式我按官方习惯来搭orange-maturity-dataset/ ├── images/ │ ├── train/ # 训练集图片 │ └── val/ # 验证集图片 ├── labels/ │ ├── train/ # 对应标注 │ └── val/ # 对应标注 ├── data.yaml └── visualize.pyimages和labels文件名一一对应同一张图片的标注文件就是同名txt放在labels对应的子目录里。data.yaml的内容很简洁train: orange-maturity-dataset/images/train val: orange-maturity-dataset/images/val nc: 2 names: 0: ripe 1: unripe这里有个容易忽略的细节train和val的路径YOLOv5默认是相对运行train.py时的工作目录来解析的。把路径写成绝对路径换一台机器跑就得改写相对路径又可能因为工作目录不对而读不到数据。我的做法是把dataset文件夹和yolov5项目文件夹放在同一级目录yaml里直接写相对路径训练时保持工作目录在yolov5项目里基本不会出错。这一点在多人协作或者换机器复现的时候特别重要能省掉一堆我怎么跑不起来的求助信息。2.2 两类标签怎么定义才靠谱数据集标注最核心的就是类别定义我在这套数据集里的标准是这样的ripe成熟果皮呈明显橙黄色或橙红色面积占比超过一半整体色泽均匀不带明显青色区域。unripe未熟果皮仍以青绿色为主或者只有受光面少量泛黄还没有达到采摘标准。听起来很简单但实际操作会碰到不少边界情况。比如一个果子上有虫斑、晒斑或者一半橙一半绿这种到底算哪类我的处理原则是看主要面积占比超过50%橙黄就算ripe否则算unripe。标注时如果两个人对同一张图的判断不一致这张图就重新讨论或者直接剔除宁可少一张图也不能给模型传一个模糊的标签。标注工具我用的是LabelImg界面简单能直接导出YOLO格式的txt。标注时有几个实操技巧值得一说框要贴近果实的实际轮廓别留太多背景也别把果子切掉一块被遮挡严重的果子可见面积小于三分之一就直接跳过容易和背景混淆的果子比如树枝阴影下的标注时多加确认。标注规范这件事很多人一开始不重视觉得差不多就行但模型学到的边界框质量和标注的整齐程度直接相关一个突出到背景里的框会在训练时给模型传递错误信息最后推理框也会偏移。2.3 标注质量检查的经验补充标注完成后我一般会做一轮系统性检查而不是盲信标注工具的导出结果。检查方法是写一个临时脚本统计每个类别的目标数量、图片数量以及每张图的目标数量分布。如果发现某张图有超过10个待检测目标我就会单独打开看一眼是不是标注过度了因为正常果园场景里一个视角下可采摘的橙子通常不会太多。还要检查标注框的宽高比。橙子本身是接近圆形的如果大量标注框宽高比超过2:1那大概率是标注时把旁边相邻的果子一起框进来了。这种错误在模型训练时会被当成正常样本导致后续检测框变得又宽又扁。我用一个简单的统计脚本把宽高比异常的目标过滤出来人工核对一遍再进训练集。3. train/val划分的核心逻辑与脚本实现3.1 划分比例和随机种子怎么定数据划分是数据集准备里看似简单、实际很有讲究的一步。最常用的比例是8:2也就是80%训练、20%验证。我在这个数据集上也用的这个比例原因是多方面的。训练集太少模型学不到足够特征容易欠拟合尤其橙子这种在树上有遮挡、有光照变化的场景样本多样性不够时模型很快就过拟合验证集太少则评估结果波动大一个epoch的mAP可能就差好几个百分点你都没法判断模型是真的变好了还是只是随机抖动。8:2是YOLOv5官方示例里很常见的选择参数比较稳妥。如果以后数据量继续扩大比如到了几千张甚至上万张可以适当把比例调整为9:1因为数据量大了之后验证集可以缩小一点把更多数据留给训练。另一个容易被忽略的问题是随机种子。划分时必须固定随机种子否则每次运行脚本得到的训练验证集都不一样训练结果就没法对比复现。我在脚本里固定了random.seed(42)保证大家拿到手跑出来的划分结果是一样的后续对比实验才有意义。3.2 划分脚本实战划分脚本用的是Python核心逻辑其实很短import os import random import shutil from sklearn.model_selection import train_test_split random.seed(42) img_dir raw_images train_ratio 0.8 all_images [f for f in os.listdir(img_dir) if f.endswith((.jpg, .jpeg, .png))] train_images, val_images train_test_split( all_images, test_size0.2, random_state42 ) print(ftotal: {len(all_images)}, train: {len(train_images)}, val: {len(val_images)})划分之后要做一件事把对应的标签文件也同步拷贝过去。很多人只复制图片忘了复制labels结果训练时找不到标注直接报错。我写了个函数统一处理def copy_files(image_list, img_dst, lbl_dst, src_lbl_dir): for img in image_list: shutil.copy(os.path.join(img_dir, img), os.path.join(img_dst, img)) lbl img.replace(.jpg, .txt).replace(.jpeg, .txt).replace(.png, .txt) lbl_src os.path.join(src_lbl_dir, lbl) if os.path.exists(lbl_src): shutil.copy(lbl_src, os.path.join(lbl_dst, lbl)) else: print(f[WARN] missing label: {lbl})3.3 划分时的几个隐藏坑这里有个我前期踩过的坑必须单独提出来划分的时候要按场景维度来划分而不是简单地把所有图片混在一起随机切。如果同一个果园场景连续拍了好多张图片这些图片之间的光照、角度、背景高度相似相当于一个场景被重复采样了很多次。随机划分时同一个场景的照片很可能同时出现在训练集和验证集里模型在训练时已经见过类似画面验证结果就会虚高看起来mAP很好一到新场景就原形毕露。更严谨的做法是采集时记录每张图片所属的场景编号或者按时间、地点分组后再划分。我在这个数据集里对拍摄批次做了标记确保同一批次的图片尽量分到同一个集合里。这是个影响很大的细节很多公开数据集其实也没处理好这个问题但自己做数据的时候值得多花一步。4. 可视化脚本不只是一个画框工具4.1 可视化能帮你发现哪些问题很多人觉得可视化脚本就是看看标注框画得准不准其实它的作用远不止这一点。在我自己构建数据集的流程里训练前一定会把整个数据集完整跑一遍可视化重点看四类问题第一标注框是否过大或过小有没有明显偏离目标轮廓第二图片里是否有该标注而漏标注的目标第三类别标签是否标错比如把unripe的橙子标成ripe第四标注坐标是否越界比如框的某些边超出了图片边界。这四类问题中坐标越界尤其隐蔽肉眼从标签文件里根本看不出来只有画到图片上才能发现。可视化输出还有一层价值它能让非技术人员快速理解数据集的状况。之前我做一个农业项目客户对模型效果没有直观概念但可视化结果一摆出来哪里标对了、哪里标错了、数据量够不够一眼就能看清楚沟通效率提升了非常多。所以可视化脚本不是给开发者自嗨用的它是数据集质量的对外窗口。4.2 核心代码解读可视化脚本的核心逻辑是读取图片和同名的label文件把YOLO格式的归一化坐标转换成像素坐标再用OpenCV画框和类别名。核心代码不长import cv2 import os IMG_DIR images/train LBL_DIR labels/train CLASS_NAMES [ripe, unripe] COLORS { ripe: (0, 255, 0), unripe: (0, 0, 255), } def visualize(img_path, lbl_path, save_pathNone): img cv2.imread(img_path) h, w img.shape[:2] if img is None: return None with open(lbl_path, r) as f: lines f.readlines() for line in lines: parts line.strip().split() if len(parts) ! 5: continue cls_id, xc, yc, bw, bh parts cls_id int(cls_id) xc, yc, bw, bh map(float, (xc, yc, bw, bh)) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) x1, y1 max(0, x1), max(0, y1) x2, y2 min(w, x2), min(h, y2) class_name CLASS_NAMES[cls_id] if cls_id len(CLASS_NAMES) else unknown color COLORS.get(class_name, (255, 0, 0)) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, class_name, (x1, max(0, y1 - 8)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) if save_path: cv2.imwrite(save_path, img) return img坐标转换是这类脚本最核心的一步。YOLO格式里的坐标都是相对于图片宽高的归一化值比如中心点x是0.5表示目标中心在图片水平方向的正中间只有乘上实际的图片宽度才能得到像素坐标。画框的时候要注意用int转换因为OpenCV的rectangle函数要求整数坐标。边缘被截断的框用max和min夹取到图片范围内避免画到图片外面去。4.3 检查一批数据时的高效操作单张可视化没问题但整个数据集跑一遍时我建议不要一张张点开看而是批量输出到目录后快速翻阅。我是这样做的save_dir vis_output os.makedirs(save_dir, exist_okTrue) for img_name in os.listdir(IMG_DIR): if not img_name.endswith(.jpg): continue img_path os.path.join(IMG_DIR, img_name) lbl_path os.path.join(LBL_DIR, img_name.replace(.jpg, .txt)) if not os.path.exists(lbl_path): continue visualize(img_path, lbl_path, save_pathos.path.join(save_dir, img_name))把输出目录拖进图片查看器用快捷键快速翻页比在代码里逐张print有效率得多。检查时重点关注那些框明显偏离目标、类别颜色不符合直觉的图片。如果发现某类错误大量出现就回到标注阶段统一修正而不是在原图上手动改几个框就完事。5. 实际训练中碰到的坑与排查思路5.1 标注文件与图片不匹配训练YOLOv5时最容易遇到的报错就是No labels found in ...或者在训练日志里不断出现missing labels的警告。这个问题大部分时候是标注文件与图片文件名不匹配或者标注文件放错了目录。排查顺序我总结成一套固定流程先检查labels目录下有没有对应图片的同名txt文件注意图片是IMG_001.jpg标签就得是IMG_001.txt扩展名必须对上也别忽略大小写再检查txt文件是否为空空的标注文件也会触发警告然后检查txt内容格式每一行应该是类别 中心x 中心y 宽 高共5个数值用空格分隔最后检查类别编号是否越界比如data.yaml里设置了2个类别那标注里类别编号只能是0或者1出现了2就会报错。我在划分脚本里特别加了缺失label的打印输出一次性把所有问题文件暴露出来。这个习惯帮我省了很多排查时间数据量上百张的时候靠肉眼一个个找根本找不过来。5.2 训练loss在epoch 50附近震荡我用这套数据训练YOLOv5时前40个epoch的box_loss和cls_loss都正常下降但到第50轮左右开始震荡过拟合的苗头和欠拟合的信号同时出现验证集mAP迟迟上不去。排查后发现两个主要原因。第一个原因是学习率。YOLOv5默认的lr0是0.01配合余弦退火调度在大多数数据集上表现不错但这套数据集规模不大默认学习率偏大后半段优化不稳定loss在最低点附近来回横跳。我把lr0降到0.005之后训练明显平稳了一些。第二个原因是锚框参数。YOLOv5在训练时会自动计算数据集的锚框但如果同一张图里橙子的大小差异很大自动锚框可能偏向占多数的大目标。我的做法是先单独运行YOLOv5的锚框计算工具查看输出结果发现锚框和实际目标尺寸分布差距大时就在模型配置里手动指定锚框参数。5.3 类别不平衡导致的漏检训练初期ripe类别的mAP能到0.9但unripe的mAP只有0.6左右这就是明显的类别不平衡问题。原因很好理解成熟橙子颜色鲜艳、辨识度高图片采集也轻松所以数量多未熟橙子和叶子、枝干的颜色接近肉眼都不容易发现图片数量自然少模型学习难度更大。处理思路分两步走。第一步从根源上补充数据专门增加unripe类别的图片而不是盲目增加总数据量如果训练集整体扩大但两个类别还是同比例增加不平衡问题一点都不会缓解。第二步用数据增强辅助对unripe类别做更多的颜色扰动、亮度调整让模型对这个类别的特征更鲁棒。另外也可以在训练时调整YOLOv5的类别损失权重把cls_pw往unripe倾斜一点让少样本的类别获得更高的梯度权重。不过这个参数要谨慎调节调过头会让模型过度关注unripe反而把ripe的检测精度拉下来。5.4 可视化脚本的边界情况处理可视化脚本本身也有几个容易踩的坑。最常见的是读取txt时遇到空行或者行内有多个连续空格导致split之后的parts长度不是5。我在脚本里加了len(parts) ! 5的判断遇到异常行直接跳过不中断整个批处理。另一个坑是图片尺寸过大。果园实拍图有时候是几千万像素的原图直接读进来画框保存内存和磁盘都吃不消。我一般用cv2.resize把长边缩到1280再可视化标注框也会按比例自动缩放不影响判断标注质量但内存压力小很多。5.5 后续扩展方向这套数据集有几个很自然的扩展方向。第一是把2类扩展成3类增加半熟状态对采摘机器人的决策更友好第二是训练更轻量的YOLOv5n或者YOLOv5s模型部署到树莓派5这类边缘设备上做实时检测我自己在类似项目里做过TensorRT加速推理速度能到实时但需要剪枝和量化配合第三是增加更多的果园环境数据不同光照、不同角度、不同品种的橙子让数据集的覆盖度更高。我在实操这套数据集的体会是数据集准备是最花时间也最值得花时间的环节一开始把数据做扎实后面训练和部署能省下大量返工时间。如果你正准备跑自己的YOLOv5项目我建议先把可视化流程跑通花一晚上仔细检查数据质量这比多训一百个epoch管用得多。最后再分享一个习惯每个版本的标注结果都打包保留标注规范改动后能和旧版做对比快速定位差异这套工作流在项目迭代到后期时真的能救命。本文还有配套的精品资源点击获取
返回列表