ARTICLE DETAIL

资讯详情

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

蔬菜目标检测数据集:7100张VOC+YOLO双格式实战指南

蔬菜目标检测数据集:7100张VOC+YOLO双格式实战指南 简介这是一份面向计算机视觉开发者、目标检测学习者与农业智能化项目团队的多类别蔬菜检测数据集覆盖26种常见蔬菜可用于训练与评估YOLO、Faster R-CNN等检测模型也适合作为课程设计、毕业设计或算法对比实验的数据基础。资源包共约2000个文件以Pascal VOC格式的xml标注文件为主并配有对应的YOLO格式txt标注与jpg原图压缩包整体约544MB目录结构清晰便于直接接入主流检测框架。标注类别涵盖牛油果、豆类、甜菜、甜椒、西兰花、卷心菜、胡萝卜、花椰菜、芹菜、玉米、黄瓜、茄子、大蒜、辣椒、洋葱、豌豆、土豆、南瓜、萝卜、沙拉菜、番茄、西葫芦等其中牛油果、甜菜等类别框数较多类别分布具有一定不均衡性适合用于研究长尾分布与数据增强策略。目前已有402人学习下载可为蔬菜识别、智能分拣与农业场景检测提供现成的标注数据支撑。1. 蔬菜目标检测数据集7100 张 VOCYOLO 双格式到底解决了什么做过农业视觉项目的人大概都有过这种体验模型在公开的 COCO 上跑得挺欢一换到自家大棚的番茄、黄瓜、辣椒上就集体翻车要么把青椒认成黄瓜要么把叶片阴影当成病害斑。根子往往不在网络结构而在数据——通用数据集里根本没有「26 种蔬菜」这种细粒度类别也没有大棚这种密集遮挡、光照剧烈变化的场景。这份 7100 张、VOC 与 YOLO 双格式的蔬菜数据集解决的正是「从零标注太贵、拿通用集迁移又不准」这个卡点。它适合三类人想入门目标检测但缺真实数据练手的新手、做智慧农业/采摘机器人需要快速验证算法的工程师、以及要拿蔬菜场景做课程设计或论文实验的学生。VOC 给的是标准 XML 标注YOLO 给的是归一化 txt两套格式覆盖了从 PyTorch 原生检测框架到 ultralytics 系训练脚本的主流入口省掉最耗时的格式转换环节。2. 先搞懂 VOC 与 YOLO 两套标注格式的差异拿到一个压缩包第一件事不是急着解压训练而是搞清楚里面两套标注各自长什么样、坐标系怎么定义。很多人训练报错、框全偏、mAP 死活上不去追到根上都是格式理解错了。2.1 VOC 的 XML 结构与字段含义VOC 格式每张图对应一个同名.xml文件核心是object节点。一个典型文件长这样annotation foldervegetables/folder filename000123.jpg/filename size width640/width height480/height depth3/depth /size object nametomato/name !-- 类别名26 种之一 -- poseUnspecified/pose truncated0/truncated !-- 是否被截断0/1 -- difficult0/difficult !-- 是否难样本评估时可忽略 -- bndbox xmin112/xmin !-- 左上角 x绝对像素 -- ymin88/ymin xmax305/xmax !-- 右下角 x -- ymax260/ymax /bndbox /object /annotation这里的关键点bndbox用的是绝对像素坐标原点在左上角xmax/ymax是框的右下角而非宽高。difficult字段在 VOC 官方评估里会被跳过但大多数 YOLO 训练脚本直接忽略它如果你做的是学术对比实验要留意这个差异。name字段必须和你的类别列表严格一致大小写、空格、中文全角都会导致类别映射错位。2.2 YOLO 的 txt 结构与归一化坐标YOLO 格式每张图对应一个.txt每行一个目标五个字段0 0.325000 0.362500 0.301563 0.358333 2 0.710938 0.520833 0.187500 0.291667五个数字依次是class_id center_x center_y width height。注意三点坐标全部归一化到 0~1即除以图像宽高center_x/center_y是框中心点而非左上角class_id是从 0 开始的整数索引对应一个classes.txt或data.yaml里的类别顺序。归一化带来的好处是图像缩放、letterbox 填充后标注不用改坏处是一旦你搞混了「中心点」和「左上角」框会整体偏移半个宽高训练 loss 会卡在一个很高的值下不去。2.3 两种格式的坐标换算关系把 VOC 转 YOLO 的公式记牢后面写脚本、查错都靠它center_x (xmin xmax) / 2 / width center_y (ymin ymax) / 2 / height w (xmax - xmin) / width h (ymax - ymin) / height反向转换就是乘回宽高再还原左上右下。实际项目里我一般会写一个双向转换脚本因为标注工具LabelImg 出 VOClabelme 出 JSONultralytics 系要 YOLO来回切换是常态。换算本身不难难的是边界情况框贴到图像边缘时xmax可能等于width归一化后是 1.0某些框架会因为这个值报越界稳妥做法是 clip 到[0, 1]。3. 用脚本把 VOC 批量转成 YOLO 并校验数据集号称双格式但实际拿到手经常只有一套是完整的或者你想统一到 YOLO 再训练。这一章给一套能直接抄的转换 校验流程。3.1 转换脚本与类别映射import os import xml.etree.ElementTree as ET from pathlib import Path # 26 类蔬菜顺序必须固定训练和推理都用这一份 CLASSES [tomato, cucumber, pepper, eggplant, cabbage, carrot, radish, spinach, lettuce, broccoli, cauliflower, celery, onion, garlic, ginger, potato, sweet_potato, pumpkin, zucchini, okra, pea, bean, corn, mushroom, asparagus, artichoke] cls2id {c: i for i, c in enumerate(CLASSES)} def voc_to_yolo(xml_path, img_w, img_h, out_path): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in cls2id: print(f[跳过] 未知类别 {name} in {xml_path}) continue bnd obj.find(bndbox) xmin float(bnd.find(xmin).text) ymin float(bnd.find(ymin).text) xmax float(bnd.find(xmax).text) ymax float(bnd.find(ymax).text) # 归一化并 clip防止贴边越界 cx min(max((xmin xmax) / 2 / img_w, 0.0), 1.0) cy min(max((ymin ymax) / 2 / img_h, 0.0), 1.0) w min(max((xmax - xmin) / img_w, 0.0), 1.0) h min(max((ymax - ymin) / img_h, 0.0), 1.0) if w 0 or h 0: continue # 丢弃退化框 lines.append(f{cls2id[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) Path(out_path).write_text(\n.join(lines), encodingutf-8) return len(lines)逻辑说明先建类别到 id 的固定映射保证训练、验证、推理三处一致解析 XML 时对每个 object 取绝对坐标按公式归一化clip到[0,1]是防越界的后悔药宽高为 0 的退化框直接丢否则训练时 IoU 计算会出 NaN。参数上CLASSES的顺序就是最终data.yaml里names的顺序一旦定了不要中途改改了等于所有历史权重作废。3.2 用 PIL 读真实尺寸而不是猜上面脚本的img_w/img_h必须来自图像本身不能想当然写 640×480。数据集里混入不同分辨率的图是常态from PIL import Image def convert_folder(xml_dir, img_dir, out_dir): os.makedirs(out_dir, exist_okTrue) ok, skip 0, 0 for xml_file in Path(xml_dir).glob(*.xml): stem xml_file.stem img_file Path(img_dir) / f{stem}.jpg if not img_file.exists(): skip 1 continue with Image.open(img_file) as im: w, h im.size # 真实宽高别用 EXIF 里的 n voc_to_yolo(xml_file, w, h, Path(out_dir) / f{stem}.txt) ok 1 if n 0 else 0 print(f转换完成 {ok} 张跳过 {skip} 张)Image.open拿到的size是(width, height)顺序别写反写反了框会整体旋转 90 度这是新手最常见的翻车点之一。3.3 转换后的三项校验转完不校验等于没转。我一般跑三个检查校验项方法期望结果坐标范围遍历所有 txt检查每个值是否在 [0,1]无越界值类别分布统计每个 class_id 出现次数无某类为 0长尾不过分图文配对检查每张 jpg 是否有同名 txt一一对应无孤儿文件类别分布尤其要看26 类蔬菜里番茄、黄瓜这类常见样本可能上千洋蓟、芦笋可能只有几十张。长尾太严重时要么对稀有类做过采样要么在 loss 里加类别权重否则模型会直接躺平只预测高频类。4. 用 ultralytics 跑通第一版蔬菜检测训练格式统一到 YOLO 后最省事的路径是 ultralytics 系YOLOv8/v11 同一套 API。这一章给最小可跑通的配置和参数解释。4.1 data.yaml 怎么写path: /data/vegetables # 数据集根目录 train: images/train # 相对 path 的训练图目录 val: images/val nc: 26 # 类别数必须和 names 长度一致 names: 0: tomato 1: cucumber # ... 依次到 25nc和names长度不一致是最常见的启动报错来源报错信息往往只提示 index 越界不直接说类别数错了排查时先看这里。train/val路径是相对path的用绝对路径也行但混用容易出错。4.2 训练命令与关键参数yolo detect train \ data/data/vegetables/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ workers8 \ projectruns/veg \ nameexp1参数逐个说model从预训练权重起步蔬菜这种细粒度任务从头训基本没戏迁移学习是标配imgsz640是速度和精度的平衡点小目标多可以上 960 但显存翻倍batch16看显存调8G 卡跑 640 一般能到 16lr00.01是 SGD 的初始学习率换 AdamW 要降到 1e-3 量级patience20是早停验证集 20 轮不涨就停省时间也防过拟合。workers是数据加载进程数设成 CPU 核数的 70% 左右设太大反而因为进程切换变慢。4.3 训练过程该盯哪几个指标启动后终端会刷一屏指标别只看 loss。重点看三个metrics/mAP50是 IoU 0.5 下的平均精度蔬菜检测一般能到 0.8 以上算可用metrics/mAP50-95更严格反映框的定位精度和 mAP50 差距过大说明框回归没学好train/box_loss和val/box_loss的差距验证 loss 早早回升就是过拟合信号该加数据增强或减模型容量了。如果 mAP50 卡在 0.3 以下不动八成是标注格式或类别映射出了问题回头查第 3 章的校验。5. 蔬菜检测训练里最容易踩的五个坑这一章全是血泪经验每条按「现象 → 原因 → 解决」写遇到对应症状直接对号入座。5.1 现象loss 一直 NaN 或突然爆炸原因通常是标注里有退化框宽或高为 0或坐标越界导致 IoU 计算除零。也可能是学习率设太大前几个 batch 梯度就炸了。解决先跑第 3.3 节的坐标范围校验把非法框清掉学习率从 0.01 降到 0.001 试一轮如果 loss 正常下降说明就是 lr 问题。另外混合精度训练AMP在个别卡上也会引发 NaN可以临时关掉ampFalse定位。5.2 现象mAP 正常但推理时框全偏原因几乎都是训练和推理的预处理不一致。训练时用了 letterbox 填充推理时如果直接 resize 到 640×640长宽比变了框自然偏。解决推理时保持和训练相同的 letterbox 逻辑ultralytics 的predict默认会处理但如果你自己写前处理就要注意。还有一种情况是归一化坐标反算时用错了图像尺寸用了原图尺寸去乘归一化值但实际喂给网络的是缩放后的图。5.3 现象某些蔬菜类别完全检测不出来原因一是该类样本太少模型没学到特征二是类别名在转换时被跳过拼写不一致、大小写不同。解决先统计类别分布确认稀有类有多少张少于 100 张的类考虑数据增强旋转、色彩抖动、马赛克或从其他来源补充。同时检查转换日志里有没有「未知类别」的跳过记录有的话统一类别名重新转。5.4 现象验证集 mAP 很高但实际场景一塌糊涂原因是训练集和验证集同分布但和真实场景不同分布。比如数据集里都是大棚正面拍摄实际部署是俯拍或侧拍域偏移直接让模型失效。解决划分验证集时故意留出不同光照、不同角度的子集别随机划分如果真实场景数据能拿到哪怕几十张也加进训练做微调效果比调任何超参都明显。5.5 现象训练速度慢得离谱GPU 利用率上不去原因通常是数据加载成了瓶颈workers设太小或磁盘 IO 慢也可能是imgsz设太大、batch 太小导致 GPU 等数据。解决workers调到 CPU 核数的 70%把数据集放到 SSD 上用nvidia-smi看 GPU 利用率低于 60% 基本就是数据管道卡了。另外开启缓存cacheTrue可以把整个数据集读进内存小数据集7100 张完全放得下速度提升立竿见影。6. 把 7100 张用到极致分层采样与难例挖掘数据集规模不算大26 类分下来平均每类不到 300 张直接训容易在稀有类上欠拟合。我一般用两个技巧把这份数据的价值榨干。第一个是分层采样划分验证集。随机划分会让某些稀有类在验证集里一张都没有mAP 统计直接失真。按类别分层保证每个类在验证集里至少有 10% 且不少于 5 张import random from collections import defaultdict def stratified_split(txt_dir, val_ratio0.1, min_val5): cls_files defaultdict(list) for txt in Path(txt_dir).glob(*.txt): with open(txt) as f: for line in f: cid int(line.split()[0]) cls_files[cid].append(txt.stem) break # 一张图只按首个类别归类简化处理 val_set set() for cid, stems in cls_files.items(): random.shuffle(stems) n_val max(min_val, int(len(stems) * val_ratio)) val_set.update(stems[:n_val]) return val_set逻辑是按类别分组后各自抽验证集min_val5保证稀有类也有评估样本。参数val_ratio一般 0.1~0.2数据少就取 0.1。第二个是难例挖掘。第一轮训练完用模型在训练集上推理把置信度低或预测框和真值 IoU 低的样本挑出来人工复核标注是否有误或者对这些样本做额外增强再训一轮。蔬菜场景里难例往往是密集遮挡的叶片间果实、逆光下的深色蔬菜这些样本补对了mAP 能涨好几个点。最后说个习惯我每次拿到新数据集第一件事永远是可视化 20 张标注框用脚本把框画到图上肉眼看一遍。这一步花不了十分钟但能提前发现坐标错位、类别错标、图像损坏这些致命问题比训到一半才发现强太多。数据集的质量永远比模型结构更决定上限希望帮到你。本文还有配套的精品资源点击获取
返回列表