西红柿成熟度检测数据集:3241张VOC+YOLO双格式与YOLOv8训练实战
发布时间:2026/10/1 19:30:44来源:尧图网络
简介这份西红柿成熟度检测数据集面向计算机视觉学习者与农业智能化项目开发者用于训练和评估番茄成熟度分类与目标检测模型。数据同时提供Pascal VOC与YOLO两套标注格式包含jpg原图及一一对应的xml、txt标注文件可直接接入主流检测框架省去格式转换环节。压缩包内共2000个文件以1999个xml标注文件和1个说明用txt为主整体约34.62MB标注类别覆盖tomato_half_ripe、tomato_overripe、tomato_ripe、tomato_rotten、tomato_unripe五类完整刻画从生到腐的成熟度梯度。目前已有709人学习下载适合课程设计、毕业课题或农业分拣算法验证等场景。读者可据此快速搭建训练与验证流程开展类别分布统计、模型对比实验与精度调优并借助双格式标注灵活切换检测或分类任务为后续部署与改进提供可靠的数据基础。1. 西红柿成熟度检测数据集3241 张 VOCYOLO 双格式到底能干什么如果你正在做农业视觉分拣、温室采摘机器人或者果蔬品质分级大概率绕不开一个现实问题公开的西红柿成熟度数据太少了。COCO、VOC 这些通用数据集里西红柿样本零散、类别粗糙直接拿来训练成熟度分类模型根本分不清「青熟」和「半红」的边界。这个标题指向的就是一个专门解决该问题的资源3241 张图像、5 个成熟度类别、同时提供 VOC 与 YOLO 两种标注格式。它适合三类人——想快速跑通 YOLOv8 训练流程的新手、需要做成熟度分级落地的算法工程师、以及要给采摘机器人做视觉模块的嵌入式开发者。VOC 格式负责兼容传统检测框架和标注复核YOLO 格式负责直接喂给 ultralytics 系列开箱训练双格式并存省掉了自己写转换脚本的麻烦。下面我按「拿到压缩包之后怎么用」的顺序把格式结构、训练配置、参数调优和踩坑点讲清楚。2. 解压后先看什么VOC 与 YOLO 双格式的目录结构和标签差异拿到一个.7z数据集最忌讳的就是直接解压完往训练脚本里一扔。西红柿成熟度检测这个任务类别之间的视觉差异比通用检测小得多——青熟和转色期的颜色过渡是渐变的标注质量直接决定模型上限。所以第一步是把两种格式的目录结构、标签文件、类别映射关系全部核对一遍。2.1 VOC 格式的 Annotations、JPEGImages、ImageSets 三件套标准 VOC 格式的目录长这样解压后先确认这三个文件夹是否齐全VOC2007/ ├── Annotations/ # 每张图对应一个 XML存 bbox 和类别 ├── JPEGImages/ # 原始图像命名通常是 6 位数字.jpg ├── ImageSets/ │ └── Main/ # train.txt / val.txt / trainval.txt / test.txt └── SegmentationClass/ # 本数据集不涉及分割可忽略用下面这段脚本快速统计图像数量、类别分布和标注完整性避免拿到手就开始训练却发现某个类别只有几十张import os import xml.etree.ElementTree as ET from collections import Counter ann_dir VOC2007/Annotations img_dir VOC2007/JPEGImages # 统计图像与标注是否一一对应 imgs {os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith(.jpg)} anns {os.path.splitext(f)[0] for f in os.listdir(ann_dir) if f.endswith(.xml)} print(图像数:, len(imgs), 标注数:, len(anns)) print(缺失标注的图:, len(imgs - anns), 多余标注:, len(anns - imgs)) # 统计每个类别的 bbox 数量 cls_counter Counter() for xml_file in os.listdir(ann_dir): tree ET.parse(os.path.join(ann_dir, xml_file)) for obj in tree.findall(object): cls_counter[obj.find(name).text] 1 print(类别分布:, cls_counter)这段代码做了三件事核对图像与标注文件名是否一一对应、统计缺失和多余标注、统计每个类别的 bbox 数量。参数上只需要改ann_dir和img_dir两个路径。如果发现某个类别 bbox 数量低于 300训练时就要考虑过采样或者加类别权重否则模型会严重偏向多数类。VOC 的 XML 里size字段记录图像宽高bndbox记录xmin/ymin/xmax/ymax注意有些标注工具会写出xmax小于xmin的脏数据训练前必须过滤。2.2 YOLO 格式的 images/labels 配对与归一化坐标YOLO 格式把每张图对应一个.txt每行是class_id cx cy w h全部归一化到 0~1。目录通常是yolo_dataset/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/关键检查点是 images 和 labels 的文件名必须严格对应除扩展名外完全一致且类别 id 从 0 开始连续。用下面脚本验证归一化坐标是否越界import os label_dir yolo_dataset/labels/train bad [] for f in os.listdir(label_dir): with open(os.path.join(label_dir, f)) as fp: for i, line in enumerate(fp): parts line.strip().split() if len(parts) ! 5: bad.append((f, i, 字段数不对)) continue cls, cx, cy, w, h parts vals list(map(float, [cx, cy, w, h])) # 归一化坐标必须落在 0~1且宽高不能为 0 if any(v 0 or v 1 for v in vals) or vals[2] 0 or vals[3] 0: bad.append((f, i, line.strip())) print(异常标注行数:, len(bad)) for b in bad[:10]: print(b)归一化坐标越界是 YOLO 训练中最隐蔽的坑之一——它不会报错但会让 loss 出现 NaN 或者框跑到图像外面。这段脚本把字段数、坐标范围、宽高为零三种情况都查了。如果异常行占比超过 1%建议直接回退到 VOC 格式重新转换而不是手动修补。2.3 5 个成熟度类别的映射关系与顺序陷阱5 个类别通常对应绿熟、转色、半红、全红、过熟这样的分级。VOC 的classes.txt或ImageSets里的类别顺序和 YOLO 的data.yaml里names列表顺序必须一致否则训练出来的模型会把「全红」识别成「绿熟」。常见做法是建一个统一的classes.txtgreen breaker pink red overripe然后 VOC 转 YOLO 时按这个顺序生成 class_iddata.yaml里也按同样顺序写names。我一般会在转换脚本里加一行断言确保 VOC 里出现的所有类别名都在classes.txt中防止漏类。这个顺序陷阱在双格式数据集里特别容易翻车因为 VOC 的 XML 存的是类别名YOLO 存的是数字 id中间映射一旦错位模型学到的就是错的。3. 从 VOC 转 YOLO转换脚本、路径参数和三个边界坑虽然标题说数据集同时提供 VOC 和 YOLO 格式但实际使用中你往往需要自己重新划分训练集验证集或者把 VOC 转成 YOLO 以适配 ultralytics 的最新版本。这一章把转换脚本写透顺带把路径配置和边界情况讲清楚。3.1 转换脚本XML 解析到归一化 txt 的完整实现下面这个脚本把 VOC 的 Annotations 和 JPEGImages 转成 YOLO 的 images/labels 结构同时按 8:2 划分 train/valimport os import shutil import random import xml.etree.ElementTree as ET voc_root VOC2007 out_root yolo_dataset classes [green, breaker, pink, red, overripe] random.seed(42) for split in [train, val]: os.makedirs(f{out_root}/images/{split}, exist_okTrue) os.makedirs(f{out_root}/labels/{split}, exist_okTrue) # 收集所有有标注的样本 samples [] for xml_file in os.listdir(f{voc_root}/Annotations): stem os.path.splitext(xml_file)[0] img_path f{voc_root}/JPEGImages/{stem}.jpg if os.path.exists(img_path): samples.append(stem) random.shuffle(samples) split_idx int(len(samples) * 0.8) splits {train: samples[:split_idx], val: samples[split_idx:]} for split, stems in splits.items(): for stem in stems: tree ET.parse(f{voc_root}/Annotations/{stem}.xml) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.findall(object): cls_name obj.find(name).text.strip() if cls_name not in classes: continue # 跳过未知类别防止 id 越界 cls_id classes.index(cls_name) bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 裁剪到图像边界内处理越界框 xmin, ymin max(0, xmin), max(0, ymin) xmax, ymax min(img_w, xmax), min(img_h, ymax) if xmax xmin or ymax ymin: continue # 宽高非法丢弃 cx (xmin xmax) / 2 / img_w cy (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) if not lines: continue # 该图无有效框不写入 shutil.copy(f{voc_root}/JPEGImages/{stem}.jpg, f{out_root}/images/{split}/{stem}.jpg) with open(f{out_root}/labels/{split}/{stem}.txt, w) as f: f.write(\n.join(lines))逻辑上分四步收集有效样本、随机打乱按 8:2 划分、逐 XML 解析并归一化、写入对应目录。参数上random.seed(42)保证可复现classes列表顺序必须和data.yaml一致。三个边界坑分别是未知类别跳过防止 id 越界、越界框裁剪防止归一化坐标超出 0~1、空标注图丢弃防止生成空 txt 导致训练报错。这三个坑我在不同项目里都踩过尤其是越界框标注工具手抖一下就会产生xmax img_w的情况。3.2 data.yaml 配置path、train、val、names 四个字段怎么填转换完成后在yolo_dataset同级建一个tomato.yamlpath: ./yolo_dataset train: images/train val: images/val nc: 5 names: 0: green 1: breaker 2: pink 3: red 4: overripepath是数据集根目录train和val是相对path的路径。注意 ultralytics 不同版本对path的解析行为有差异稳妥做法是写绝对路径或者确保在数据集根目录下执行训练命令。nc必须等于names长度写错会直接报维度不匹配。如果训练时提示「No labels found」九成是train路径写错或者 labels 目录名不是labels。3.3 训练集验证集划分随机种子、类别均衡和泄漏检查8:2 随机划分有个隐患如果同一颗西红柿的多个角度被拍进数据集随机划分会导致训练集和验证集出现同一颗果实的近重复图像验证指标虚高。常见做法是按拍摄批次或图像文件名前缀分组划分而不是逐张随机。另外要检查验证集里 5 个类别是否都有样本如果某个类别在验证集里为 0mAP 计算会出问题。可以用下面命令快速核对for c in 0 1 2 3 4; do echo -n class $c in val: grep -rh ^$c yolo_dataset/labels/val | wc -l done如果某个类别数量明显偏少建议用分层抽样重新划分保证每个类别在验证集里至少有 20 个实例。4. YOLOv8 训练配置epochs、imgsz、batch 和成熟度任务的参数取舍数据集准备好之后训练本身反而是最标准化的环节。但西红柿成熟度检测有几个特殊性类别间差异小、颜色是主要判别特征、果实尺度变化大。这些都会影响超参选择。4.1 一条命令跑通训练与关键参数含义yolo detect train \ datatomato.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ patience30 \ augmentTrue \ mosaic1.0 \ hsv_h0.015 \ hsv_s0.7 \ hsv_v0.4 \ projectruns/tomato \ nameexp1modelyolov8n.pt是 nano 版本适合先跑通流程如果精度不够再换yolov8s.pt或yolov8m.pt。epochs150配合patience30表示 30 轮无提升就早停。imgsz640是默认值如果图像里西红柿占比很小可以提到 960 或 1280但显存占用会明显上升。hsv_s0.7和hsv_v0.4是颜色增强参数对成熟度任务特别重要——它能模拟不同光照下的颜色变化防止模型把「亮红色」和「暗红色」当成两个类别。mosaic1.0开启马赛克增强但成熟度任务里要小心马赛克拼接可能把不同成熟度的果实拼在一起造成标签歧义如果发现训练不稳定可以降到 0.5。4.2 成熟度任务的增强策略颜色增强要开几何增强要克制颜色增强hsv_h/hsv_s/hsv_v建议全开因为成熟度判别的核心就是颜色。几何增强里degrees旋转可以开到 10~15因为果实朝向不影响成熟度但flipud上下翻转要慎用因为西红柿通常果蒂朝上上下翻转会产生不自然的样本。mixup和copy_paste在成熟度任务里容易引入标签噪声建议先关掉等基线跑通再逐个开启做消融。我一般的顺序是先全默认跑一版基线再单独调颜色增强最后试几何增强每次只改一个变量。4.3 训练过程看什么loss 曲线、mAP50 和混淆矩阵的读法训练启动后重点看三个东西。第一是box_loss和cls_loss是否稳定下降如果 cls_loss 震荡严重多半是学习率太大或者 batch 太小。第二是mAP50和mAP50-95成熟度任务里 mAP50 通常能到 0.9 以上但 mAP50-95 可能只有 0.6~0.7因为边界框定位精度受果实遮挡影响。第三是训练结束后的混淆矩阵重点看相邻类别比如 breaker 和 pink之间的误判率如果这两个类别互相混淆严重说明颜色特征区分度不够需要增加这两类的样本或者调整增强策略。混淆矩阵在runs/tomato/exp1/目录下是一个confusion_matrix.png横轴预测、纵轴真实对角线越深越好。5. 避坑与排查标注错位、类别不均衡、显存溢出和验证指标虚高这一章记录我在西红柿成熟度检测项目里实际踩过的坑每条按现象、原因、解决来写。现象训练 loss 正常下降但推理时框全部偏移。原因VOC 转 YOLO 时图像宽高读的是 XML 里的size但部分图像实际尺寸和 XML 记录不一致导致归一化坐标错位。 解决转换脚本里不要信 XML 的 size直接用 PIL 或 OpenCV 读实际图像尺寸。加一行from PIL import Image; img_w, img_h Image.open(img_path).size替换 XML 读取。现象某个类别 mAP 始终为 0。原因类别不均衡过熟样本可能只有几十张模型直接学会了忽略该类。 解决先统计类别分布对少数类做过采样复制样本到训练集或者在 loss 里加类别权重。ultralytics 本身不直接支持类别权重可以通过复制少数类图像和标注文件实现过采样。现象训练到一半报 CUDA out of memory。原因imgsz或batch设太大或者mosaic增强在后期产生超大拼接图。 解决先把 batch 降到 8如果还爆就降 imgsz 到 512。另外 YOLOv8 的close_mosaic参数可以在最后 N 轮关闭马赛克减少显存峰值设close_mosaic10即可。现象验证集 mAP 很高但实际部署效果差。原因训练集和验证集存在近重复图像验证指标虚高。 解决按拍摄批次分组划分或者用图像哈希去重后再划分。简单做法是计算图像感知哈希把相似度高于阈值的图分到同一侧。现象模型把「半红」识别成「全红」。原因颜色增强过度hsv_v调太高导致暗红被提亮成亮红。 解决把hsv_v从 0.4 降到 0.2同时增加半红类别的样本量。成熟度任务里颜色增强要适度过犹不及。6. 进阶技巧用混淆矩阵反推标注质量以及小样本类别的过采样实现训练跑通之后真正拉开差距的是对数据的反向排查。混淆矩阵不只是看模型好坏它还能暴露标注问题。如果混淆矩阵里 green 和 breaker 互相误判率很高除了模型原因很可能是标注时这两类的边界定义不一致——不同标注员对「绿熟」和「转色」的判断标准不同。这时候要回到 VOC 的 XML 里抽查这两类的样本看 bbox 是否框到了同一颗果实的同一区域。具体做法是导出混淆矩阵对应的误判样本列表然后逐张可视化。ultralytics 训练完成后会在runs/tomato/exp1/下生成val_batch0_pred.jpg等预测可视化图直接看这些图就能发现系统性误判。如果发现某类样本的框普遍偏大或偏小说明标注规范有问题需要重新标注而不是调模型。小样本类别的过采样实现我一般用文件级复制而不是在 dataloader 里做因为 ultralytics 的数据加载逻辑对自定义 sampler 支持有限。具体命令# 假设 overripe 类别只有 80 张复制 3 倍到训练集 for f in $(grep -rl ^4 yolo_dataset/labels/train | head -80); do stem$(basename $f .txt) for i in 1 2 3; do cp yolo_dataset/images/train/$stem.jpg yolo_dataset/images/train/${stem}_dup$i.jpg cp yolo_dataset/labels/train/$stem.txt yolo_dataset/labels/train/${stem}_dup$i.txt done done这段脚本找到训练集里所有包含类别 4overripe的标注文件把对应的图像和标注复制三份并加后缀。注意复制后要重新跑一遍 2.2 节的坐标校验确保复制没有引入问题。过采样比例控制在 2~3 倍即可过度复制会导致该类别过拟合。最后一个习惯每次改完数据集或超参我都会把runs/目录按exp1、exp2这样编号保留并在tomato.yaml旁边记一行备注说明这次改了什么。这个习惯帮我省了无数次「上次那个配置是什么来着」的后悔药。西红柿成熟度检测这个方向数据质量比模型结构重要得多3241 张 5 类别的规模不算大把标注核对清楚、增强策略调对YOLOv8n 就能跑到可用水平。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网