news 2026/9/28 19:50:49

葡萄成熟度检测数据集:VOC+YOLO双格式1123张

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
葡萄成熟度检测数据集:VOC+YOLO双格式1123张

简介:本资源是一个面向计算机视觉初学者与农业AI应用开发者的葡萄成熟度检测专用数据集,聚焦果实分级识别任务,适用于目标检测模型训练与算法验证。数据集共2000个文件,包含1123张高质量葡萄图像(JPG)、1123份Pascal VOC格式XML标注文件及877份YOLO格式TXT标注文件,完整覆盖“ripe grape”“semiripe grape”“unripe grape”三类成熟状态,总标注框数1133个,标注规范统一、由labelImg工具人工绘制矩形框。压缩包仅6.47MB,轻量易下载,适配YOLOv5/v8、Faster R-CNN等主流框架的快速接入与微调。目前已有385人学习下载,配套classes.txt与多组样本命名文件(如firc_grape_*.txt),便于直接加载类别定义与划分训练/验证集,显著降低数据预处理门槛,是开展水果品质智能判别项目落地的可靠基础资源。

1. 葡萄成熟度检测数据集VOC+YOLO格式1123张3类别:为什么农业AI落地卡在“看得懂葡萄”这一步?

你训练完YOLOv8模型,mAP跑到了78.5%,一上果园现场——全崩了。青葡萄被当成过熟紫葡萄框出来,转色期的半红果被漏检,甚至把藤蔓阴影当成果实打上置信度0.92的标签。不是模型不行,是它根本没见过真实葡萄园里那种光照斑驳、果串重叠、叶面反光、背景杂乱的“玄学场景”。这个标题里的葡萄成熟度检测数据集VOC+YOLO格式1123张3类别,就是专治这种落地翻车的“后悔药”:它不是网上随手扒的水果图库,而是实打实从新疆吐鲁番、山东莱芜、云南宾川三大产区果园里,用标准白平衡+固定焦距+多时段(晨雾/正午/傍晚)采集的1123张高清图;标注严格按农艺标准划分为【青绿未熟】【转色中】【紫黑成熟】三类,每张图都经两位农艺师交叉校验;更关键的是,它同时提供VOC(Pascal VOC XML)和YOLO(txt格式)双格式标注,省去你花两天写转换脚本、再花三天调边界框偏移的血泪时间。如果你正在做智慧农业硬件集成、采摘机器人视觉模块、或高校农业AI课程设计,这个数据集不是“可选”,而是你绕不开的最小可行验证基线——它不解决所有问题,但能让你第一次在真实葡萄图像上,把“检测框”真正打在“果子”上,而不是打在“光斑”或“叶脉”上。


2. 从解压到训练:用这个数据集跑通YOLOv8训练的最小闭环

这个.7z包看似简单,但直接解压就开训?大概率在第3个epoch就遇到IndexError: list index out of range。别急,我们拆解成四步:解压校验→目录结构重建→格式验证→训练启动。每一步都有坑,每一步都决定你能不能看到第一个loss下降。

2.1 解压与完整性校验:先确认你拿到的是“真数据”,不是压缩包损坏的幻觉

提示:不要用Windows自带解压工具打开.7z——它对中文路径和长文件名支持极差,常导致XML文件名乱码或丢失。必须用7-Zip(v23.01或更新)或Bandizip(v7.3+)。

# Linux/macOS下推荐命令行解压(保留原始权限与编码) 7z x "葡萄成熟度检测数据集VOC+YOLO格式1123张3类别.7z" -o./grape_dataset -y # 校验解压后核心文件数(必须严格匹配) ls ./grape_dataset/VOCdevkit/VOC2007/JPEGImages/ | wc -l # 应输出1123 ls ./grape_dataset/VOCdevkit/VOC2007/Annotations/ | wc -l # 应输出1123(.xml) ls ./grape_dataset/yolo_labels/ | wc -l # 应输出1123(.txt)

逻辑说明:

  • VOCdevkit/VOC2007/是标准Pascal VOC结构,JPEGImages/存原图,Annotations/存XML标注;
  • yolo_labels/是YOLO格式,每个.txt文件对应同名图片,每行格式为class_id center_x center_y width height(归一化坐标);
  • 校验1123这个数字是底线——少一张图,训练时DataLoader会报FileNotFoundError;多一张(比如解压重复),YOLO的train.py会因label文件缺失而静默跳过该图,导致实际只训1122张却以为训满。

参数说明:

  • -o./grape_dataset指定输出目录,避免解压到当前目录造成混乱;
  • -y自动确认覆盖,防止交互式提示中断脚本;
  • wc -l统计行数,比ls | grep .jpg | wc -l更可靠(防文件名含空格或特殊字符)。

2.2 目录结构重建:YOLOv8不吃“VOC原生结构”,必须按Ultralytics规范重排

Ultralytics官方要求YOLO训练目录必须是train/val/test三级嵌套,且images/和labels/平行存放。VOC结构是平铺的,YOLO格式是散列的,直接扔进去会报AssertionError: No images found。我一般会写一个轻量Python脚本做结构映射:

# build_yolo_structure.py import os import shutil from pathlib import Path # 定义源路径(根据你解压的实际路径修改) voc_img_dir = Path("./grape_dataset/VOCdevkit/VOC2007/JPEGImages") voc_anno_dir = Path("./grape_dataset/VOCdevkit/VOC2007/Annotations") yolo_label_dir = Path("./grape_dataset/yolo_labels") # 定义目标结构 root = Path("./grape_yolo") for split in ["train", "val", "test"]: (root / split / "images").mkdir(parents=True, exist_ok=True) (root / split / "labels").mkdir(parents=True, exist_ok=True) # 划分比例:训练集80%、验证集15%、测试集5%(农业场景建议保守,宁可val多些) all_images = sorted(list(voc_img_dir.glob("*.jpg")) + list(voc_img_dir.glob("*.jpeg"))) n_total = len(all_images) n_train = int(n_total * 0.8) n_val = int(n_total * 0.15) # 复制图片+标签(按文件名匹配) for i, img_path in enumerate(all_images): stem = img_path.stem # 找对应YOLO label(优先)或VOC XML(备用) yolo_label = yolo_label_dir / f"{stem}.txt" if yolo_label.exists(): label_path = yolo_label else: # 若无YOLO label,从VOC XML转(见2.3节) raise FileNotFoundError(f"Missing YOLO label for {stem}") if i < n_train: split = "train" elif i < n_train + n_val: split = "val" else: split = "test" # 复制图片 shutil.copy(img_path, root / split / "images" / img_path.name) # 复制label shutil.copy(label_path, root / split / "labels" / f"{stem}.txt") print(f"Built structure: train={n_train}, val={n_val}, test={n_total-n_train-n_val}")

逻辑说明:

  • 脚本不依赖cv2或xml.etree,纯路径操作,秒级完成;
  • 严格按文件名stem(不含扩展名)匹配图片与label,避免.jpg和.jpeg混用导致错配;
  • 划分采用顺序切片而非随机shuffle——农业图像有地域性(如吐鲁番图集中出现在前段),顺序划分能保证val/test包含不同产区样本,比random更贴近真实部署分布。

参数说明:

  • n_train/n_val比例可调,但不建议改成90/10:农业数据噪声大(遮挡、反光、小目标),val太小会导致early stopping失效;
  • shutil.copy比os.system("cp")更跨平台,且不会因空格路径崩溃;
  • 若运行时报Missing YOLO label,说明数据集里有图没对应txt——立即停训,检查解压是否完整(见2.1节校验)。

2.3 VOC XML → YOLO txt转换:当YOLO label缺失时的兜底方案

虽然标题说“VOC+YOLO格式”,但实测有约7张图只有VOC XML没有YOLO txt(可能是标注员漏导出)。这时不能手动补,要用脚本批量转,且必须保证坐标归一化逻辑100%正确:

# voc2yolo.py import xml.etree.ElementTree as ET from pathlib import Path def convert_voc_to_yolo(xml_path, img_width, img_height, class_names): tree = ET.parse(xml_path) root = tree.getroot() yolo_lines = [] for obj in root.findall('object'): cls_name = obj.find('name').text.strip() if cls_name not in class_names: continue # 跳过非法类别(如标注错误的"leaf") cls_id = class_names.index(cls_name) bbox = obj.find('bndbox') xmin = int(bbox.find('xmin').text) ymin = int(bbox.find('ymin').text) xmax = int(bbox.find('xmax').text) ymax = int(bbox.find('ymax').text) # YOLO要求:center_x, center_y, width, height(全部归一化到0~1) x_center = (xmin + xmax) / 2.0 / img_width y_center = (ymin + ymax) / 2.0 / img_height width = (xmax - xmin) / img_width height = (ymax - ymin) / img_height yolo_lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}") return "\n".join(yolo_lines) # 使用示例(需配合2.2脚本中的缺失处理) class_names = ["green_unripe", "veraison", "purple_ripe"] # 必须与data.yaml中顺序一致 img_dir = Path("./grape_dataset/VOCdevkit/VOC2007/JPEGImages") xml_dir = Path("./grape_dataset/VOCdevkit/VOC2007/Annotations") for xml_path in xml_dir.glob("*.xml"): stem = xml_path.stem img_path = img_dir / f"{stem}.jpg" if not img_path.exists(): img_path = img_dir / f"{stem}.jpeg" # 兼容.jpeg if not img_path.exists(): continue # 读取图片尺寸(不用opencv,用PIL更轻量) from PIL import Image w, h = Image.open(img_path).size yolo_txt = convert_voc_to_yolo(xml_path, w, h, class_names) with open(f"./grape_dataset/yolo_labels/{stem}.txt", "w") as f: f.write(yolo_txt)

逻辑说明:

  • x_center等计算必须用float除法,整数除法(//)会导致坐标截断;
  • .6f保留6位小数是YOLOv8解析要求,少于6位可能被截断(如0.1234变成0.123);
  • class_names顺序必须与后续data.yaml中names:字段完全一致,错一位整个类别就全乱。

参数说明:

  • class_names列表值来自数据集文档(通常PDF说明里有),若无文档,打开几个XML看<name>标签内容;
  • Image.open().size比cv2.imread().shape快10倍,且不加载像素数据,纯读头信息;
  • 转换后务必用head -n1 ./grape_dataset/yolo_labels/xxx.txt检查首行格式是否为0 0.456789 0.321456 0.123456 0.098765。

2.4 启动YOLOv8训练:用ultralytics官方CLI,但必须改这3个参数

别用yolo train data=xxx.yaml一键到底——农业小目标(葡萄单粒直径常<20px)和高重叠(果串密集)会让默认配置直接过拟合。必须显式指定:

yolo detect train \ data=./grape_yolo/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ workers=4 \ optimizer=auto \ lr0=0.01 \ lrf=0.1 \ hsv_h=0.015 \ hsv_s=0.7 \ hsv_v=0.4 \ degrees=0 \ translate=0.1 \ scale=0.5 \ shear=0 \ perspective=0 \ flipud=0.0 \ fliplr=0.5 \ mosaic=1.0 \ mixup=0.1 \ copy_paste=0.1 \ auto_augment='randaugment' \ erasing=0.4 \ cfg=./ultralytics/cfg/default.yaml

逻辑说明:

  • mosaic=1.0强制开启马赛克增强——对葡萄串重叠场景提升显著,但mixup=0.1要压低,避免不同成熟度葡萄在mixup后产生伪标签;
  • hsv_s=0.7大幅增强饱和度扰动,对抗果园光照不均(阴天/正午色温差异);
  • erasing=0.4是关键:随机擦除0.4面积,模拟叶片遮挡,让模型学会从局部特征(果梗、表皮纹理)判断成熟度,而非依赖完整果实轮廓。

参数说明:

  • batch=16是RTX 3090/4090的甜点值,若用A100需调至32,V100则降至8;
  • lr0=0.01比默认0.001高10倍——因为1123张图量小,学习率太低收敛慢;
  • cfg=指向自定义配置文件,里面要关掉close_mosaic=10(默认30epoch关闭,太晚),改为close_mosaic=20。

3. 避坑指南:葡萄检测项目里最痛的5个翻车现场与解法

农业AI项目最怕“训得漂亮,跑得稀烂”。这5个坑,是我带3个果园落地项目踩出来的血泪经验,每个都附带现象→原因→解法三段论,拒绝模棱两可。

3.1 现象:val mAP飙升到85%,但测试集上漏检率超40%

原因:训练集和测试集按文件名排序划分(见2.2节),而采集时同一棵树的图连续编号,导致test集全是“宾川产区傍晚图”,模型没见过“吐鲁番正午强光图”。
解法:改用sklearn.model_selection.train_test_split按产区+时段分层抽样。在2.2脚本中替换划分逻辑:

# 新增产区/时段标签(需提前整理csv) import pandas as pd meta = pd.read_csv("./grape_dataset/meta.csv") # 列:filename, region, time_of_day from sklearn.model_selection import StratifiedGroupKFold sgkf = StratifiedGroupKFold(n_splits=5, shuffle=True, random_state=42) # 用region+time_of_day作为stratify key

3.2 现象:训练loss震荡剧烈,第50epoch突然nan

原因:葡萄图像存在大量极小目标(单粒葡萄<16x16像素),YOLOv8默认anchor_t=4.0对小目标不敏感,导致梯度爆炸。
解法:在data.yaml中添加anchor_t: 2.0,并用utils/autoanchor.py重新计算anchor:

python ultralytics/utils/autoanchor.py --file ./grape_yolo/data.yaml --grid 0.3 --mode k-means

注意:--grid 0.3表示只对宽高比<0.3的目标聚类(竖长型葡萄串),避免被藤蔓干扰。

3.3 现象:检测框精准,但成熟度分类全错(青果标成紫果)

原因:三类葡萄颜色光谱重叠度高(尤其转色期),RGB空间无法区分,而YOLO默认用RGB输入。
解法:在model.yaml中插入HSV通道分支:

# 在backbone后加HSV head - [-1, 1, Conv, [32, 3, 1]] # HSV conv - [-1, 1, nn.Sigmoid, []] # 归一化到0~1 - [[-3, -1], 1, Concat, [1]] # 拼接RGB+HSV特征

然后重训——实测转色期分类准确率从62%→89%。

3.4 现象:部署到Jetson Orin后FPS仅3,远低于标称15

原因:默认YOLOv8导出onnx时用dynamic_axes,Orin的TensorRT引擎无法优化动态shape。
解法:导出时固定input shape:

yolo export model=yolov8n.pt format=onnx imgsz=640,640 dynamic=False opset=12

再用trtexec --onnx=model.onnx --shapes=input:1x3x640x640生成engine。

3.5 现象:夜间红外图检测全失效(果园装了热成像补光)

原因:数据集只有可见光图,模型没见过红外特征。
解法:不做域迁移,直接用grape_yolo微调红外图——只需200张红外标注图,冻结backbone前3层,只训head:

yolo train data=ir_grape.yaml model=yolov8n.pt pretrained=True freeze=3

关键:ir_grape.yaml中nc: 3保持不变,但names:改为红外特征描述(如["cool_green", "warm_veraison", "hot_purple"])。


4. 验证与进阶:用混淆矩阵+Grad-CAM定位模型“看不懂”的具体葡萄

训完模型不能只看mAP——农业场景要回答:“模型在哪类葡萄上犯错?为什么错?” 这需要两个动作:生成细粒度混淆矩阵,再用Grad-CAM可视化决策依据。

4.1 农业专用混淆矩阵:不只是类别对错,还要看“错得多离谱”

标准混淆矩阵只显示青果→紫果这类错误,但农业关心的是成熟度跃迁距离:把青果判成转色果(误差1级)可接受,判成紫果(误差2级)就是严重误判。所以要构建3×3的“成熟度距离矩阵”:

预测\真实青绿未熟转色中紫黑成熟
青绿未熟320428
转色中2829537
紫黑成熟1241280
# eval_with_distance.py from sklearn.metrics import confusion_matrix import numpy as np # 加载预测结果(val集) preds = np.load("./runs/detect/train/predictions.npy") # shape=(N,6) [x,y,x,y,conf,cls] targets = np.load("./grape_yolo/val/labels.npy") # shape=(N,5) [cls,x,y,w,h] # 计算成熟度距离误差(0=正确,1=隔1级,2=隔2级) distances = [] for i in range(len(preds)): p_cls = int(preds[i, -1]) t_cls = int(targets[i, 0]) dist = abs(p_cls - t_cls) distances.append(dist) # 输出统计 print(f"Accuracy: {np.mean(np.array(distances)==0):.3f}") print(f"1-level error: {np.mean(np.array(distances)==1):.3f}") print(f"2-level error: {np.mean(np.array(distances)==2):.3f}")

关键洞察:若2-level error > 15%,说明模型把青果和紫果的底层特征学混了——大概率是训练时hsv_v扰动太强(见2.4节),需回调到0.2。

4.2 Grad-CAM热力图:揪出模型“看哪”和“看错”的证据链

YOLO本身不支持Grad-CAM(因无分类head),但我们可以hack detection head的cls_logits:

# gradcam_for_yolo.py import torch import cv2 from pytorch_grad_cam import GradCAM from pytorch_grad_cam.utils.image import show_cam_on_image # 加载模型(必须用eval模式) model = YOLO("yolov8n.pt").model.eval() target_layers = [model.model[-1].cv3[0]] # 指向cls分支最后一层conv # 预处理单张图 img = cv2.imread("./grape_yolo/val/images/IMG_001.jpg") img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img_tensor = torch.from_numpy(img_rgb).permute(2,0,1).float() / 255.0 img_tensor = img_tensor.unsqueeze(0) # add batch dim # Grad-CAM cam = GradCAM(model=model, target_layers=target_layers, use_cuda=True) grayscale_cam = cam(input_tensor=img_tensor, target_category=None) # 叠加热力图 cam_image = show_cam_on_image(img_rgb.astype(np.float32)/255.0, grayscale_cam[0], use_rgb=True) cv2.imwrite("gradcam_result.jpg", cv2.cvtColor(cam_image, cv2.COLOR_RGB2BGR))

解读方法:

  • 若热力图集中在果梗或叶柄,说明模型靠连接部位判断(合理);
  • 若热力图在果粒中心但呈环形(边缘亮、中心暗),说明模型在学表皮反光特征(对成熟度判断有效);
  • 若热力图覆盖整串但避开单粒,说明模型在学“串级形状”,此时需加强单粒标注(联系数据集作者补标)。

4.3 一个硬核技巧:用YOLO输出的bbox坐标反推葡萄糖度(Brix值)

这不是玄学——农艺研究表明,葡萄成熟度与果粒纵横比(width/height)强相关:青果近球形(比≈1.0),紫果因重力拉长(比≈0.7~0.8)。我们可以用YOLO输出的bbox直接估算:

# brix_estimator.py def estimate_brix(box_w, box_h, confidence): """ box_w, box_h: YOLO输出的归一化宽高(0~1) confidence: 检测置信度(过滤低质量框) 返回预估Brix值(°Bx) """ if confidence < 0.5: return 0 aspect_ratio = box_w / (box_h + 1e-6) # 防除零 # 经验公式(基于吐鲁番赤霞珠标定) if aspect_ratio > 0.95: brix = 12.0 + (aspect_ratio - 0.95) * 20.0 # 青果区间 elif aspect_ratio > 0.75: brix = 18.0 + (aspect_ratio - 0.75) * 15.0 # 转色区间 else: brix = 22.0 + (0.75 - aspect_ratio) * 10.0 # 成熟区间 return max(12.0, min(26.0, brix)) # 截断到合理范围 # 在推理循环中调用 results = model("IMG_001.jpg") for r in results[0].boxes: w, h = r.xywh[0][2], r.xywh[0][3] # 归一化宽高 conf = r.conf[0].item() brix = estimate_brix(w, h, conf) print(f"Estimated Brix: {brix:.1f}°Bx")

为什么有效:

  • 不依赖额外传感器,纯视觉估算,适合低成本硬件;
  • 公式系数已用200组实测Brix值标定(R²=0.87),比单纯分类更贴近采收决策需求;
  • confidence过滤掉低质量框,避免藤蔓误检拉低精度。

我习惯在每次果园部署前,用这个脚本跑一遍测试集,把brix_error > ±1.5°Bx的样本挑出来,人工复核——往往发现是标注偏差(如把半青半紫果标成纯紫),这时就反馈给数据集作者更新标注。农业AI没有银弹,但有可迭代的闭环。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 19:50:09

四天做出六件AI作品:腾讯Mini鹅科创营走进这所深圳民办学校

深圳湾的红树林与候鸟&#xff0c;成为了一群青少年的AI创作灵感。近日&#xff0c;AI城市共创Mini鹅科创营深圳专场结营仪式在腾讯总部园区举行&#xff0c;活动由腾讯青科实训营主办&#xff0c;深圳明湾学校协同支持。来自粤港澳多地的学员完成为期四天的高密度PBL项目式实战…

作者头像 李华
网站建设 2026/9/28 19:48:19

电控简历没回音?10个开源项目帮你补齐工程经历,秋招突围

秋招的群里天天有人问"投了电控方向几十份简历&#xff0c;怎么一点回音都没有"&#xff0c;说实话&#xff0c;这个问题我在带项目的时候见得太多了。学历漂亮、绩点不错&#xff0c;可简历往技术岗那一栏看&#xff0c;除了课程设计和课设实验之外&#xff0c;几乎…

作者头像 李华
网站建设 2026/9/28 19:47:45

I2C从模式设计:时钟延展与死锁恢复的鲁棒性实现

1. 从模式设计到底层总线&#xff1a;为什么时钟延展和死锁恢复值得单独拎出来讲I2C从模式设计这件事&#xff0c;很多做过STM32或者Linux驱动的人都有体会&#xff1a;主机模式跑通不难&#xff0c;真正让人掉头发的是从模式。主机模式下时钟是你自己发的&#xff0c;你想快就…

作者头像 李华
网站建设 2026/9/28 19:47:42

电子信息工程四年规划:从C语言到STM32与FPGA的进阶路线

电子信息工程这个专业&#xff0c;每年都有大量学生到了大三才开始慌——发现自己既没做过一个完整的项目&#xff0c;也说不清楚自己到底想走嵌入式还是芯片方向&#xff0c;考研和就业的准备混在一起&#xff0c;最后两头都没抓牢。我带过几届学弟学妹做项目&#xff0c;也见…

作者头像 李华