2026/9/27 20:33:53

玉米叶病预测数据集:10884张VOC标注图与YOLOv8实战指南

玉米叶病预测数据集:10884张VOC标注图与YOLOv8实战指南 简介本资源为玉米叶部病害识别数据集面向从事农业图像识别、作物病害检测的算法工程师与深度学习学习者可用于训练和验证玉米叶病分类或目标检测模型。数据集采用VOC格式人工标注覆盖叶枯病、普通锈病、灰叶斑病及健康玉米叶四类样本共10884张图片标注精度支撑模型准确率达到95.7%以上。压缩包为zip格式内含2000个xml标注文件整体约520.9MBxml文件与对应图像一一匹配便于直接接入主流检测框架进行训练与评估。目前已有377人学习下载适合需要真实田间病害样本、构建分类或检测基线、验证数据增强与迁移学习效果的读者参考使用也可作为农业智能监测项目的训练数据来源。1. 玉米叶病预测数据集10884 张 VOC 标注图能撑起什么级别的落地去年秋收前一个做农业 SaaS 的朋友半夜给我打电话说客户投诉他们的病害识别功能把普通锈病认成了健康叶片喷药方案直接开错。我问他训练集哪来的他说网上扒了两千张图标签全靠文件名猜。这就是典型的翻车现场——农业视觉项目里数据集的质量比模型结构重要十倍。今天要聊的这套玉米叶病预测数据集10884 张图片、VOC 格式人工标注、覆盖叶枯病、普通锈病、灰叶斑病和健康四类宣称准确率能到 95.7% 以上。这个数字不是模型架构的功劳而是标注粒度和样本均衡度堆出来的。它适合谁适合正在做作物病害识别、想跳过数据采集和标注这个最脏最累环节的算法工程师也适合农业院校做课题、需要一份可复现基线数据的研究生。但我要先把丑话说前面拿到数据集只是起点怎么切分、怎么增强、怎么防止过拟合到背景纹理才是决定你能不能复现那个 95.7% 的关键。下面按我实际跑过的流程从数据解构到训练调参再到踩坑排查一步步拆开讲。2. 拆解 VOC 标注与四类病害的视觉边界2.1 VOC 格式在农业场景下的字段含义这套数据用的是 PASCAL VOC 的 XML 标注格式每张图对应一个同名 XML 文件。别小看这个格式农业图像里叶片重叠、病斑弥散标注框的松紧直接决定模型学到的特征是不是病斑本身。一个典型的 XML 结构如下annotation foldercorn_leaf/folder filenameIMG_2043.jpg/filename size width1280/width height960/height depth3/depth /size object namenorthern_leaf_blight/name !-- 叶枯病 -- poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin312/xmin ymin205/ymin xmax678/xmax ymax540/ymax /bndbox /object /annotation这里name字段是类别核心四类分别对应叶枯病、普通锈病、灰叶斑病和健康。bndbox是矩形框坐标注意 VOC 用的是绝对像素坐标不是归一化值。difficult字段在农业数据里要特别留意——如果标注者把被遮挡或模糊的病斑标为 difficult1训练时可以选择忽略这些样本否则模型会学到一堆噪声。我一般会先统计 difficult 的比例超过 8% 就要考虑清洗。2.2 四类病害的类间差异与标注一致性检查叶枯病和灰叶斑病在早期肉眼都表现为褐色斑点标注时最容易混。叶枯病的病斑通常从叶尖向叶基扩展呈长梭形边缘有黄色晕圈灰叶斑病的斑点更小、更密后期中心灰白。普通锈病则是橙褐色隆起孢子堆视觉特征最明显。健康叶片看似简单但如果在田间拍摄时混入了虫咬孔或机械损伤标注者可能误标为健康这就是标签噪声的来源。拿到数据后第一件事不是训练是做标注一致性抽检。我通常随机抽 200 张把 XML 里的框画回原图逐类看边界是否贴合病斑。下面这段脚本用来批量可视化import xml.etree.ElementTree as ET import cv2 import os def draw_voc_boxes(img_path, xml_path, save_path): img cv2.imread(img_path) tree ET.parse(xml_path) root tree.getroot() for obj in root.findall(object): name obj.find(name).text 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) # 不同类别用不同颜色方便肉眼区分 color_map {northern_leaf_blight: (0,0,255), common_rust: (0,255,255), gray_leaf_spot: (255,0,0), healthy: (0,255,0)} color color_map.get(name, (255,255,255)) cv2.rectangle(img, (xmin,ymin), (xmax,ymax), color, 2) cv2.putText(img, name, (xmin, ymin-5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) cv2.imwrite(save_path, img) # 批量处理示例 for xml_file in os.listdir(annotations): if xml_file.endswith(.xml): base xml_file.replace(.xml, ) draw_voc_boxes(fimages/{base}.jpg, fannotations/{xml_file}, fvis/{base}_vis.jpg)这段代码的逻辑很直白解析 XML按类别给框上色健康用绿色、叶枯病用红色、锈病用黄色、灰叶斑用蓝色。跑完抽检集后重点看两类问题——框是否把整个叶片都圈进去了过松或者只圈了病斑中心一小块过紧。过松会让模型学到背景土壤和天空过紧会丢失病斑边缘的渐变信息。我见过最离谱的标注是把整张图圈成健康问标注员他说“这片叶子整体看起来还行”。这种样本必须回炉。2.3 样本均衡度与长尾分布的处理策略10884 张图里四类不可能完全均等。我拿到的类似数据集通常健康类偏多灰叶斑病偏少。先跑个统计import xml.etree.ElementTree as ET from collections import Counter import os counter Counter() for xml_file in os.listdir(annotations): tree ET.parse(os.path.join(annotations, xml_file)) for obj in tree.getroot().findall(object): counter[obj.find(name).text] 1 print(counter) # 典型输出Counter({healthy: 4200, northern_leaf_blight: 3100, common_rust: 2400, gray_leaf_spot: 1184})如果某一类占比低于 15%直接训练会导致模型对少数类召回率极低。我的做法不是简单复制样本而是用带标注框的增强——对少数类做随机旋转、亮度扰动、局部裁剪但裁剪时必须同步变换 bbox 坐标。这里有个坑很多增强库默认只处理图像不处理 XML你得自己写坐标映射。我一般用 albumentations 配合自定义 transform或者干脆用 imgaug 的 BoundingBoxAugmenter。如果不想写代码也可以在划分训练集时对少数类采用过采样但要注意验证集和测试集必须保持原始分布否则评估指标会虚高。3. 从 VOC 到 YOLO 格式转换脚本与四个边界坑3.1 为什么训练时通常要转成 YOLO 或 COCOVOC 的 XML 适合标注工具读写但主流检测框架吃的是 YOLO 的 txt 或 COCO 的 json。YOLO 格式每行是class_id x_center y_center width height全部归一化到 0-1。转换本身不难难的是坐标取整和边界溢出。我见过有人转完发现框全偏了查了半天是忘了除以图像宽高。下面是我常用的转换脚本import xml.etree.ElementTree as ET import os # 类别映射顺序要和训练配置里的 names 一致 class_map { northern_leaf_blight: 0, common_rust: 1, gray_leaf_spot: 2, healthy: 3 } 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 if name not in class_map: continue cls_id class_map[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 max(0, min(xmin, img_w)) xmax max(0, min(xmax, img_w)) ymin max(0, min(ymin, img_h)) ymax max(0, min(ymax, img_h)) # 归一化中心点和宽高 x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_path, w) as f: f.write(\n.join(lines)) # 假设图像尺寸已知实际应从图像读取 voc_to_yolo(annotations/IMG_2043.xml, 1280, 960, labels/IMG_2043.txt)这段脚本的关键在max(0, min(...))那四行。VOC 标注时如果框贴边xmax 可能等于图像宽度归一化后是 1.0没问题但如果标注员手抖写了 1281不裁剪就会导致 YOLO 训练时坐标越界报错。另一个坑是空标注文件——健康叶片如果整张图没有病斑XML 里可能没有 object 节点转换后 txt 是空的。YOLO 训练时遇到空 txt 会跳过该图但如果你用 COCO 格式空标注需要显式处理否则评估时会被算作漏检。3.2 训练集、验证集、测试集的划分比例与随机种子划分比例我一般用 7:2:1但农业数据有个特殊性同一片田、同一天拍的照片纹理和光照高度相似。如果随机划分训练集和验证集里可能有同一株玉米的不同角度图导致验证指标虚高。正确做法是按拍摄批次或地块划分。如果数据集没提供批次信息至少要用固定随机种子并且检查划分后各类别分布是否一致。下面这个脚本按类别分层抽样import os import random from collections import defaultdict from sklearn.model_selection import train_test_split random.seed(42) # 固定种子保证可复现 # 假设所有图片路径和对应类别已整理成列表 all_files [] # [(img_path, label_path, class_id), ...] class_dict defaultdict(list) for item in all_files: class_dict[item[2]].append(item) train, val, test [], [], [] for cls_id, items in class_dict.items(): train_items, temp train_test_split(items, test_size0.3, random_state42) val_items, test_items train_test_split(temp, test_size0.33, random_state42) train.extend(train_items) val.extend(val_items) test.extend(test_items) print(f训练集 {len(train)}验证集 {len(val)}测试集 {len(test)})分层抽样的目的是让每个集合里四类比例接近。注意random_state必须固定否则每次跑出来的指标没法对比。我习惯把划分结果存成三个 txt 文件训练脚本直接读避免每次重新划分。3.3 转换后的完整性校验三个必查项转完格式别急着开训先做三项检查。第一统计生成的 txt 文件数量是否和图片数量一致缺一个都可能导致训练中途报错。第二随机抽 10 张图用脚本把 YOLO 格式的框画回去看是否和原 VOC 框重合。第三检查类别 ID 是否从 0 连续编号中间断号会让模型输出维度对不上。我一般用下面这段快速校验import os img_dir images label_dir labels img_files set(f.replace(.jpg, ) for f in os.listdir(img_dir) if f.endswith(.jpg)) label_files set(f.replace(.txt, ) for f in os.listdir(label_dir) if f.endswith(.txt)) missing_label img_files - label_files missing_img label_files - img_files print(f缺标注的图片{len(missing_label)}缺图片的标注{len(missing_img)}) # 检查类别 ID 范围 all_ids set() for txt in os.listdir(label_dir): with open(os.path.join(label_dir, txt)) as f: for line in f: if line.strip(): all_ids.add(int(line.split()[0])) print(f出现的类别 ID{sorted(all_ids)})如果missing_label不为零要么是健康叶片没标注要么是漏转。健康叶片如果整张图无病斑YOLO 里可以没有 txt但训练时要在数据配置里允许空标签否则会报错。类别 ID 必须是 0 到 3多一个少一个都要回去查映射表。4. 训练参数怎么设以 YOLOv8 为基线的可复现配置4.1 输入分辨率与 batch size 的显存权衡10884 张图原始分辨率大概率在 1280×960 左右。直接拿原图训练显存吃不消而且病斑在图中占比可能很小下采样后特征更弱。我一般把输入统一缩到 640×640这是 YOLO 系列的经典尺寸在精度和速度之间平衡较好。如果你用的是 8GB 显存的卡batch size 设 16 比较稳12GB 以上可以上 32。下面是一份我跑过的 YOLOv8 配置# corn_leaf.yaml path: ./dataset train: train.txt val: val.txt test: test.txt names: 0: northern_leaf_blight 1: common_rust 2: gray_leaf_spot 3: healthy训练命令yolo detect train datacorn_leaf.yaml modelyolov8s.pt epochs120 imgsz640 batch16 \ lr00.01 lrf0.01 momentum0.937 weight_decay0.0005 warmup_epochs3imgsz640是输入尺寸batch16根据显存调。lr0初始学习率设 0.01lrf是最终学习率因子0.01 表示降到初始的 1%。warmup_epochs3让前三个 epoch 学习率从低到高预热防止一开始梯度震荡。这些参数不是玄学是 YOLO 官方在 COCO 上验证过的默认值直接迁移到农业数据上通常不会差太远。4.2 数据增强参数别把病斑转没了YOLOv8 内置了 mosaic、mixup、随机翻转等增强。农业数据要特别注意mosaic 把四张图拼成一张如果病斑本身很小拼完后病斑占比更小模型可能学不到。我一般把mosaic概率从默认的 1.0 降到 0.5mixup直接关掉因为 mixup 会把两张叶片叠在一起病斑特征互相干扰。翻转可以保留但上下翻转要谨慎——玉米叶片在田间的自然朝向通常是斜向上上下翻转后不符合真实分布。下面是我调整后的增强配置yolo detect train datacorn_leaf.yaml modelyolov8s.pt epochs120 imgsz640 batch16 \ mosaic0.5 mixup0.0 fliplr0.5 flipud0.0 degrees10.0 translate0.1 scale0.3fliplr0.5表示一半概率水平翻转flipud0.0关闭上下翻转。degrees10.0允许小角度旋转模拟拍摄时的轻微倾斜。scale0.3允许缩放但别设太大否则病斑可能被缩到几个像素。这些参数我是在验证集上盯着 mAP50 曲线调出来的不是拍脑袋。4.3 学习率调度与早停策略120 个 epoch 不是必须跑满。我一般设patience20如果验证集 mAP 连续 20 个 epoch 不提升就停。学习率调度用余弦退火YOLOv8 默认就是不用额外改。但有个细节如果你发现训练 loss 震荡厉害先把lr0降到 0.001 试试农业数据标注噪声比 COCO 大学习率太高容易过拟合到错误标签上。另外close_mosaic10表示最后 10 个 epoch 关闭 mosaic让模型在接近真实分布的图像上微调这个对最终精度提升很明显建议保留。5. 避坑与排查95.7% 准确率背后的五个真实翻车点5.1 验证集准确率很高测试集一塌糊涂现象训练完看验证集 mAP50 有 96%换测试集掉到 78%。原因划分时没有按地块或批次分层验证集和训练集来自同一批照片背景、光照几乎一样模型记住了背景而不是病斑。解决重新按拍摄批次划分如果数据集没提供批次信息至少用图像哈希去重把相似度高的图分到同一个集合。我一般用感知哈希做去重阈值设 5 以内视为重复。5.2 健康叶片被大量误检为叶枯病现象健康类召回率只有 60%大量健康叶片被框成叶枯病。原因健康叶片上如果有虫咬孔或机械损伤标注时可能被标成叶枯病模型学到了错误特征。另外叶枯病早期病斑和健康叶片的黄色斑点视觉差异小。解决回查健康类样本把有损伤的图重新标注或剔除训练时对健康类适当增加权重或者在损失函数里对健康类调高cls权重。我试过把健康类的分类损失权重设为 1.5误检率降了 12%。5.3 训练到一半 loss 变成 NaN现象前 30 个 epoch 正常突然 loss 变 NaN训练中断。原因学习率太高导致梯度爆炸或者数据里有标注框宽高为 0 的脏样本。解决先检查转换后的 txt 里有没有0.000000的宽高有就删掉对应行然后把lr0降到 0.001加grad_clip10.0梯度裁剪。YOLOv8 默认有梯度裁剪但如果你改了优化器要确认这个参数还在。5.4 灰叶斑病召回率始终上不去现象其他三类 mAP 都过 90%灰叶斑病只有 70%。原因灰叶斑病样本量最少且病斑小、密集640 分辨率下特征被稀释。解决两个方向——一是对灰叶斑病单独过采样二是把输入分辨率提到 1024但显存要够。我一般先试过采样如果提升不明显再提分辨率。另外可以在数据增强里对灰叶斑病样本额外加一次随机裁剪让病斑在图中占比变大。5.5 模型在田间实拍图上完全失效现象测试集指标很好但拿手机去田里拍几张模型什么都认不出。原因训练集全是实验室或标准拍摄条件下的图背景干净、光照均匀田间有土壤、杂草、阴影、露水。解决如果条件允许在训练集里混入少量田间实拍图哪怕只有几百张做微调。如果没有实拍图至少加颜色抖动、随机阴影、高斯模糊等增强模拟田间退化。我通常把hsv_h0.015, hsv_s0.7, hsv_v0.4调得比默认值大一些让模型对光照变化更鲁棒。6. 把 95.7% 变成你自己的基线验证与迭代的实操习惯拿到一份标注数据集最忌讳的就是直接跑个默认配置然后宣称复现了 95.7%。那个数字是在特定划分、特定增强、特定模型下得到的换一批测试图可能就崩。我的习惯是先把数据集切成三份用 YOLOv8s 跑一个基线记录 mAP50、mAP50-95、各类召回率然后每次只改一个变量——比如只调 mosaic 概率或者只改输入分辨率——看指标怎么动。下面这张表是我在某次迭代中的记录你可以照着建自己的实验日志实验编号输入尺寸mosaic灰叶斑病召回整体 mAP50exp016401.00.680.912exp026400.50.740.928exp0310240.50.810.947exp0410240.5 过采样0.860.958从 exp01 到 exp04灰叶斑病召回率涨了 18 个百分点整体 mAP 也过了 95%。关键动作就两个降 mosaic 概率、提分辨率、对少数类过采样。没有换模型没有加注意力模块全是数据层面的调整。这也印证了我开头的观点——农业视觉项目里数据质量决定上限模型只是逼近上限的工具。最后说个我自己的教训。有一次我图省事直接用官方 COCO 预训练权重跑微调学习率没改结果模型把玉米叶片认成了“盆栽植物”。后来我把lr0从 0.01 降到 0.001冻结前几层只训检测头才正常收敛。所以别迷信预训练权重农业图像和自然图像分布差得远该保守的时候要保守。希望帮到你。本文还有配套的精品资源点击获取