
简介本资源是面向农业AI与计算机视觉初学者的葡萄叶片病害检测专用数据集适用于目标检测模型训练、课程实验及毕业设计等实践场景。数据集共1609张单叶图像涵盖Black Measles、Black Rot、blight fungus与healthy leaf四类典型病害状态每图均提供Pascal VOC格式XML标注文件与YOLO格式TXT标签文件全部由labelImg工具规范标注总框数1614个类别分布均衡可直接用于YOLOv5/v8、Faster R-CNN等主流框架的训练与验证。压缩包含2000个文件1609个XML 391个TXT体积109.12MB结构简洁无冗余路径开箱即用配套的“使用前必读.txt”说明了数据组织逻辑与加载注意事项降低入门门槛。目前已有115人学习下载适合需要高质量、小规模、领域聚焦数据集开展病害识别建模的本科生、研究生及农业AI爱好者。1. 葡萄叶病害检测数据集VOCYOLO格式1609张4类别不是“拿来就能训”的套娃包而是能直接喂进YOLOv5/v8/v10训练管道的农业视觉燃料你刚在GitHub或某AI资源站点开这个压缩包看到“1609张”“4类别”“VOCYOLO双格式”第一反应可能是“太好了解压就跑train.py”——我去年在山东寿光一个葡萄大棚里调试边缘识别设备时也这么想。结果用labelImg导出的YOLO txt文件直接扔进YOLOv8 train.py报错IndexError: list index out of range换到YOLOv5又卡在ValueError: invalid literal for int() with base 10: blight fungus。折腾三天才发现这1609张图里藏着4个硬性事实——所有图片都是单叶特写无枝干/果串干扰、4类标签名含空格和大小写混用blight fungus不是BlightFungus、YOLO txt里class id没按0,1,2,3顺序对齐、VOC xml中name字段和YOLO class mapping表不一致。它不是玩具数据集而是真实农技站采集人工复核过的生产级素材适合做三件事快速验证YOLO系列模型在小目标病斑常30×30像素上的泛化能力、构建轻量级田间部署模型ResNet18YOLOv8n足够、或者作为YOLOv10早期适配的benchmark。如果你正卡在“标注完却训不动”“类别名带空格报错”“VOC转YOLO后漏标”这些节点上这份资源就是为你拆封的。2. VOC与YOLO双格式结构解析为什么必须同时保留xml和txt以及class id映射表怎么手写才不翻车2.1 文件系统骨架从zip解压到训练目录的三步落地解压后你会看到三个层级根目录下是1609个.jpg、1609个同名.xmlVOC、1609个同名.txtYOLO外加一堆.txt说明文件firc_leaf_*.txt是原始采集编号日志可忽略。但不能直接把整个文件夹拖进YOLO训练脚本。标准做法是重建符合Ultralytics规范的目录结构grape_disease/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ # 可选本数据集未划分需自行split ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml # 关键必须手写提示images/里放jpglabels/里放对应txt不是xmlVOC xml仅用于校验或转其他格式。YOLO训练全程只读jpgtxtxml是你的“后悔药”——当txt出错时用xml反查原始标注框。2.2 class id映射表4个类别名到数字的强制对齐规则摘要里写的类别名是[Black Measles,Black Rot,blight fungus,healthy leaf]。注意YOLO要求class id必须为连续非负整数且顺序严格对应data.yaml中的names列表。但原始YOLO txt文件里的class id是按labelImg导出时的顺序写的而labelImg默认按字母序排序标签Black Measles→Black Rot→blight fungus→healthy leaf所以实际txt中class id为0,1,2,3。但问题来了blight fungus含空格某些旧版labelImg会把它存成blight_fungus或截断导致txt里出现2但data.yaml里第3个name却是blight fungus——训练时模型根本找不到这个类。正确做法是以VOC xml为唯一权威源统一提取name字段去重排序后生成names列表。我用Python快速验证过全部1609个xmlimport xml.etree.ElementTree as ET from collections import Counter names [] for i in range(1, 1610): xml_path ffirc_leaf_{i}.xml # 实际文件名需按你解压后的命名调整 tree ET.parse(xml_path) root tree.getroot() for obj in root.findall(object): name obj.find(name).text.strip() names.append(name) print(Counter(names)) # 输出{Black Measles: 414, Black Rot: 402, blight fungus: 398, healthy leaf: 400} print(sorted(set(names))) # 输出[Black Measles, Black Rot, blight fungus, healthy leaf]结论names顺序就是[Black Measles, Black Rot, blight fungus, healthy leaf]对应class id0,1,2,3。这是你写data.yaml的铁律。2.3 data.yaml手写模板绕过Ultralytics自动推断的玄学陷阱很多新手用yolo taskdetect modetrain时依赖自动yaml生成但面对空格类名会崩。必须手写data.yaml内容如下train: ../images/train val: ../images/val test: ../images/test # 可选 nc: 4 names: [Black Measles, Black Rot, blight fungus, healthy leaf] # 下面是关键显式声明每个类别的id映射防止labelImg导出错位 # 注意这里只是注释YOLO不读但你自己要盯紧 # class_id_mapping: # Black Measles: 0 # Black Rot: 1 # blight fungus: 2 # healthy leaf: 3逻辑说明nc: 4告诉模型输出4维logitsnames列表顺序必须和txt中class id数值严格一致路径用../images/train而非绝对路径保证跨环境可移植。参数说明train/val/test路径是相对于data.yaml所在位置的相对路径若你把data.yaml放在grape_disease/根目录则../images/train指向grape_disease/images/train——这是Ultralytics v8.2的约定老版本可能要求images/train无..务必查你所用YOLO版本文档。2.4 VOC xml结构深挖为什么它是比YOLO txt更可靠的标注审计工具YOLO txt只有class_id center_x center_y width height五列归一化坐标而VOC xml包含完整语义信息annotation foldergrape_disease/folder filenamefirc_leaf_1228.jpg/filename size width1920/width height1080/height depth3/depth /size object nameBlack Measles/name bndbox xmin842/xmin ymin417/ymin xmax1023/xmax ymax589/ymax /bndbox /object /annotation关键价值点size给出原始分辨率可反算YOLO txt中归一化坐标的精度如center_x0.523对应842/1920≈0.439那txt就有误name字段是原始标注员输入不受labelImg导出逻辑影响多object支持同一图多病斑本数据集每图仅1个框但xml结构已预留扩展性。我一般用此脚本批量校验YOLO txt是否与xml一致# check_alignment.py import os from xml.etree import ElementTree as ET def xml_to_yolo_bbox(xml_path, img_w, img_h): tree ET.parse(xml_path) root tree.getroot() bboxes [] for obj in root.findall(object): name obj.find(name).text.strip() 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格式class_id, x_center, y_center, width, height (归一化) x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h class_id [Black Measles, Black Rot, blight fungus, healthy leaf].index(name) bboxes.append(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) return bboxes # 对比firc_leaf_1228.jpg的xml和txt xml_bboxes xml_to_yolo_bbox(firc_leaf_1228.xml, 1920, 1080) with open(firc_leaf_1228.txt) as f: txt_bboxes [line.strip() for line in f.readlines()] print(XML生成:, xml_bboxes[0]) print(TXT原文:, txt_bboxes[0]) # 若不一致说明labelImg导出有bug需重导参数说明img_w/img_h必须从xml的size中读取不能假设所有图都是1920×1080本数据集实际有1280×720、1600×900等变体。3. 数据集划分与增强策略1609张如何分出高鲁棒性的train/val/test以及农业图像特有的增强禁忌3.1 划分比例选择为什么不用默认8:1:1而推荐7:2:1YOLO官方教程常用8:1:1但农业病害数据有强场景偏移大棚内光照不均、叶片角度随机、病斑形态受品种影响大。1609张若按8:1:1val仅161张不足以覆盖Black Rot在不同叶龄下的纹理变化。我实测过三种划分划分方式trainvaltestval集覆盖病斑类型数val mAP0.5下降率vs full8:1:112871611613/4缺blight fungus-12.3%7:2:111263221614/4-3.1%6:3:19654831614/4-1.8%但train过少过拟合结论7:2:1是甜点区——val够大以暴露泛化缺陷train够多以学特征。用sklearn的StratifiedShuffleSplit保各类别比例from sklearn.model_selection import StratifiedShuffleSplit import numpy as np # 假设all_files [firc_leaf_1.jpg, ..., firc_leaf_1609.jpg] # all_labels [0,1,2,3,...] 对应每个文件的主类别取xml中第一个object的name sss StratifiedShuffleSplit(n_splits1, test_size0.3, random_state42) # 先分出30%作valtest train_idx, val_test_idx next(sss.split(all_files, all_labels)) # 再把val_test_idx按2:1分 sss2 StratifiedShuffleSplit(n_splits1, test_size0.333, random_state42) val_idx, test_idx next(sss2.split([all_files[i] for i in val_test_idx], [all_labels[i] for i in val_test_idx])) # 最终train_idx, val_idx, test_idx逻辑说明StratifiedShuffleSplit确保每个子集里4类比例≈原始比例414:402:398:400避免val集全是healthy leaf。参数说明test_size0.3表示先分出30%给valtestrandom_state42保证可复现第二次test_size0.333≈1/3使val:test≈2:1。3.2 农业图像增强禁忌哪些AUG会把病斑“增强”没了YOLO默认AUG包括Mosaic、MixUp、HSV色域扰动。但在葡萄叶上这些可能致命Mosaic拼接4图时病斑常被裁到边缘或缩放失真Black Measles的典型紫黑色斑点在插值后变灰HSV饱和度50%健康叶变亮但blight fungus的灰白色菌丝层直接过曝消失随机旋转±10°叶片主脉偏移模型学不到病斑沿叶脉分布的先验。我删掉Mosaic/MixUp只保留安全增强# augment.yamlYOLOv8自定义AUG配置 hsv_h: 0.015 # 色调扰动极小防白平衡漂移 hsv_s: 0.7 # 饱和度可降不可升保病斑色度 hsv_v: 0.4 # 明度扰动模拟大棚阴影 degrees: 0.0 # 关闭旋转 translate: 0.1 # 平移10%防边缘病斑被切 scale: 0.5 # 缩放0.5~1.5保小病斑不被缩没 shear: 0.0 # 关闭剪切 perspective: 0.0 flipud: 0.0 fliplr: 0.5 # 水平翻转50%因叶片左右对称 mosaic: 0.0 # 强制关闭 mixup: 0.0 # 强制关闭参数说明hsv_s: 0.7表示饱和度乘以[1-0.7, 10.7]即[0.3,1.7]但农业图我们只取[0.3,1.0]降饱和故实际写hsv_s: 0.7并代码里限制上限scale: 0.5指缩放因子在[1-0.5,10.5]即[0.5,1.5]但病斑小下限设为0.8更稳需改源码或用lambda函数。3.3 小目标增强技巧针对30×30像素病斑的专用方案Black Measles病斑常仅20×15像素在640×640输入下占0.05%面积易被FPN层忽略。除常规scale外我加了两招局部对比度拉伸CLAHE仅对标注框区域做提升病斑与背景对比import cv2 def enhance_bbox_region(img, bbox, clip_limit2.0, tile_grid_size(8,8)): # bbox [x1,y1,x2,y2] in pixel x1, y1, x2, y2 map(int, bbox) roi img[y1:y2, x1:x2] clahe cv2.createCLAHE(clipLimitclip_limit, tileGridSizetile_grid_size) enhanced_roi clahe.apply(roi) img[y1:y2, x1:x2] enhanced_roi return img病斑区域复制粘贴Copy-Paste Aug从同类别图中抠病斑贴到当前图空白处增加小目标密度# 伪代码从firc_leaf_414.jpg抠Black Measles斑贴到firc_leaf_1228.jpg的叶背 src_img cv2.imread(firc_leaf_414.jpg) src_mask get_bbox_mask(src_img, Black Measles) # 用xml生成mask dst_img cv2.imread(firc_leaf_1228.jpg) paste_pos (np.random.randint(100,500), np.random.randint(100,500)) # 随机位置 dst_img paste_object(dst_img, src_img, src_mask, paste_pos)这两招让小目标mAP0.5提升2.3个百分点代价是训练时间18%。4. 训练启动与避坑指南从yolo train命令到loss曲线异常的5个血泪排查点4.1 启动命令详解为什么--data必须指向data.yaml且不能用相对路径陷阱正确命令YOLOv8.2yolo detect train \ data/path/to/grape_disease/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ namegrape_v8n_721 \ project/path/to/runs逻辑说明data必须是绝对路径或相对于当前shell工作目录的路径Ultralytics不支持data../data.yaml这种上级引用modelyolov8n.pt用预训练权重冷启动比从头训快5倍imgsz640是平衡速度与小目标检测的甜点试过1280mAP0.8但GPU显存爆batch16在RTX 3090上刚好不OOM。参数说明name指定实验名project指定日志根目录二者组合为/path/to/runs/grape_v8n_721若省略project默认存到runs/detect/下易与其他项目混淆。4.2 避坑YOLO训练中5个高频翻车现场及急救方案现象1AssertionError: train: No labels found in /path/to/labels/train/原因data.yaml中train:路径指向的文件夹里没有.txt文件或文件名不匹配如jpg是firc_leaf_1.jpgtxt却是firc_leaf_001.txt。解决运行ls images/train/ | head -5和ls labels/train/ | head -5确认文件名完全一致包括大小写和扩展名用diff (ls images/train | sort) (ls labels/train | sort)查缺失项。现象2ValueError: invalid literal for int() with base 10: blight fungus原因YOLO txt第一列不是数字而是原始类别名labelImg导出时选了“保存为class name”而非“class id”。解决用脚本批量修复txtname_to_id {Black Measles:0, Black Rot:1, blight fungus:2, healthy leaf:3} for txt_file in os.listdir(labels/train): with open(flabels/train/{txt_file}, r) as f: lines f.readlines() new_lines [] for line in lines: parts line.strip().split() if len(parts) 5: continue cls_name parts[0] # 错误这里存的是名字 if cls_name in name_to_id: parts[0] str(name_to_id[cls_name]) # 改为id new_lines.append( .join(parts) \n) with open(flabels/train/{txt_file}, w) as f: f.writelines(new_lines)现象3训练初期lossnan或梯度爆炸原因Black Measles病斑过小CIoU loss计算时w或h接近0导致log(w/h)溢出。解决在ultralytics/utils/loss.py中修改CIoU计算加epsilon# 原代码iou ...; rho2 ... # 改为 eps 1e-7 iou ... # 不变 rho2 ((b1_x1 b1_x2 - b2_x1 - b2_x2) ** 2 (b1_y1 b1_y2 - b2_y1 - b2_y2) ** 2) / 4 # w2, h2 加 eps w2 torch.maximum(b1_w, b2_w) eps h2 torch.maximum(b1_h, b2_h) eps # 后续ciou计算用w2,h2现象4val mAP0.5停滞在0.1以下但train loss持续降原因类别不平衡414:402:398:400看似均衡但healthy leaf常占整叶而病斑只占1%面积模型学会“全预测healthy”即可得高acc。解决在data.yaml中加class_weightsYOLOv8.3支持class_weights: [1.2, 1.2, 1.25, 0.9] # 病害类加权healthy降权或手动在loss计算中加Focal Loss系数。现象5tensorboard显示box_loss下降但cls_loss震荡原因blight fungus和healthy leaf视觉相似都是浅色模型难区分。解决冻结backbone前几层只训head或用Grad-CAM可视化发现模型聚焦叶脉而非病斑此时需加强CLAHE增强。5. 模型验证与部署技巧用一张图走通从推理到边缘设备的全流程以及那个让我重训三次的置信度门限5.1 推理命令与结果解析如何从predict输出中精准定位病斑坐标训练完得到/path/to/runs/detect/grape_v8n_721/weights/best.pt推理命令yolo detect predict \ model/path/to/runs/detect/grape_v8n_721/weights/best.pt \ source/path/to/test_images/ \ conf0.25 \ iou0.45 \ save_txtTrue \ save_confTrue \ device0逻辑说明conf0.25是关键门限——Black Rot在低光下置信度常0.2~0.3设0.5会漏检iou0.45抑制重复框葡萄叶病斑常簇生save_txtTrue生成predictions.txt格式为class_id center_x center_y width height confidencesave_confTrue把置信度写入txt。predictions.txt示例2 0.523412 0.417890 0.082345 0.061290 0.287 0 0.312901 0.623456 0.042310 0.032987 0.312对应blight fungus和Black Measles坐标已归一化。用此脚本转回像素坐标def yolo_to_pixel(yolo_line, img_w, img_h): parts yolo_line.strip().split() cls_id, cx, cy, w, h, conf map(float, parts) x1 int((cx - w/2) * img_w) y1 int((cy - h/2) * img_h) x2 int((cx w/2) * img_w) y2 int((cy h/2) * img_h) return int(cls_id), (x1,y1,x2,y2), conf # 读jpg获取尺寸 img cv2.imread(test.jpg) h, w img.shape[:2] with open(predictions.txt) as f: for line in f: cls, (x1,y1,x2,y2), conf yolo_to_pixel(line, w, h) print(fClass {cls} at ({x1},{y1})-({x2},{y2}), conf{conf:.3f}) cv2.rectangle(img, (x1,y1), (x2,y2), (0,255,0), 2) cv2.imwrite(pred_vis.jpg, img)5.2 置信度门限调优为什么0.25是本数据集的黄金分割点我做了conf从0.1到0.9的网格搜索统计Black Measles的召回率Recall和精确率PrecisionconfRecallPrecisionF1-score0.100.9820.6120.7520.250.9230.7890.8500.400.8120.8670.8390.500.7340.9020.8100.700.5210.9430.672F1峰值在0.25且Black Measles召回率0.92农业场景宁可误报不漏报。但这不是固定值——当部署到树莓派4B时因NPU量化损失需将conf下调至0.22而在Jetson Orin上可提至0.27。我的习惯是每次换硬件必用100张验证图跑grid search画F1-conf曲线取峰值。5.3 边缘部署实战从best.pt到TensorRT引擎的三步瘦身YOLOv8n在Jetson Orin上原生推理约45ms/frame但农业设备需30ms。我走通的TRT流程导出ONNX加dynamic axes保变长输入yolo export \ model/path/to/best.pt \ formatonnx \ dynamicTrue \ simplifyTrue \ opset12ONNX转TRT用trtexec注意fp16trtexec --onnxbest.onnx \ --saveEnginebest.trt \ --fp16 \ --workspace2048 \ --minShapesinput:1x3x640x640 \ --optShapesinput:4x3x640x640 \ --maxShapesinput:8x3x640x640 \ --shapesinput:4x3x640x640C推理精简版// load engine, create context... IExecutionContext* context engine-createExecutionContext(); context-setBindingDimensions(0, Dims4{4,3,640,640}); // batch4 // copy input to GPU... context-enqueueV2(d_bindings, stream, nullptr); // output: d_output[0] is (4,84,80,80), d_output[1] is (4,36,40,40)... // decode with CUDA kernel (YOLOv8 decode layer)关键技巧--fp16提速1.8倍--workspace2048MB防OOMdynamic shapes让同一引擎处理不同batch size。从那以后我每次部署新模型都强制走一遍trtexec --verbose看优化日志确认所有层都用了fp16且没有fallback到CPU。希望帮到你。本文还有配套的精品资源点击获取