ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

茶叶病害数据集VOC+YOLO格式883张8类别:从格式转换到YOLOv8训练全流程

2026/9/24 18:56:03 拓冰建站 浏览量
茶叶病害数据集VOC+YOLO格式883张8类别:从格式转换到YOLOv8训练全流程 简介这份茶叶病害数据集面向农业图像识别、植物保护研究及深度学习目标检测方向的开发者与学习者提供可直接用于模型训练与验证的标注数据。资源共883张jpg图片每张仅含单片叶子配套883个VOC格式xml与883个YOLO格式txt标注文件另含少量说明文本压缩包约200.5MB总计2000个文件兼容Pascal VOC与YOLO两种主流训练流程。标注由labelImg完成以矩形框标出8类病害炭疽病、藻斑病、鸟眼斑、褐斑病、灰斑病、健康叶、红叶斑及白斑总框数884其中红叶斑与白斑样本较多健康叶相对偏少便于按类别分布调整采样策略。目前已有886人学习下载适合作为目标检测课程实验、病害分类对比或数据增强练习的现成素材能省去从零采集与标注的成本。1. 茶叶病害数据集 VOCYOLO 格式 883 张 8 类别从拿到压缩包到跑通训练茶园里最怕的不是虫是病。叶片上刚冒出针尖大的褐点三五天就能蔓延成一片焦枯等人眼看出来往往已经错过最佳防治窗口。我去年帮一个做智慧农业的团队处理过一批茶园巡检图他们手里攒了 883 张标注好的茶叶病害图片8 个类别VOC 和 YOLO 两种格式都给了。这个数据集的价值不在于图片多而在于它把「叶片病斑」这种小目标、类间差异细微的场景做成了可直接训练的形态。如果你正在找烟草病虫害数据集 yolo 那类农业检测数据或者想拿一份真实农业场景数据练手 yolo 训练自己的数据集这份茶叶病害数据是个不错的起点。它适合两类人一是刚学完 yolo 环境配置、想跑通第一个非 COCO 数据集的算法新手二是做农业巡检落地、需要快速验证检测方案可行性的工程师。下面我按拿到压缩包之后的真实操作顺序把格式转换、环境搭建、训练调参和踩坑记录讲清楚。2. VOC 与 YOLO 双格式到底差在哪先搞懂再动手2.1 两种标注格式的坐标逻辑差异VOC 格式的标注文件是 XML每个目标用bndbox记录xmin、ymin、xmax、ymax四个值这是绝对像素坐标原点在图片左上角。YOLO 格式则是每张图对应一个.txt文件每行一个目标格式是类别索引 x_center y_center width height后四个值全部是相对于图片宽高的归一化值范围 0 到 1。这个差异决定了你不能直接把 XML 丢给 YOLO 训练必须做一次坐标换算。换算公式很直接x_center (xmin xmax) / 2 / img_widthy_center (ymin ymax) / 2 / img_heightwidth (xmax - xmin) / img_widthheight (ymax - ymin) / img_height。看起来简单但实际转换时图片尺寸读取错误、类别名和索引映射错位是两个高频翻车点。2.2 8 个类别与数据分布要先摸清拿到数据集第一件事不是急着转格式而是统计类别分布。883 张图分 8 类平均下来每类一百来张但农业数据集的类别不平衡往往很严重——健康叶片可能占了一大半某些病害只有几十张。你需要先跑一遍统计脚本看清楚每类多少张、每张图平均几个目标框。如果某类样本少于 50 张训练时就要考虑数据增强或者类别权重调整。下面这段脚本同时完成类别统计和 VOC 转 YOLO 两件事我一般会先跑统计部分确认分布再执行转换。import os import xml.etree.ElementTree as ET from collections import Counter from PIL import Image # 类别列表按你的数据集实际类别顺序填写 classes [病斑A, 病斑B, 病斑C, 病斑D, 病斑E, 病斑F, 病斑G, 健康] class_to_id {name: i for i, name in enumerate(classes)} def convert_voc_to_yolo(xml_dir, img_dir, out_dir): os.makedirs(out_dir, exist_okTrue) counter Counter() for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() # 图片文件名VOC 里通常在 filename 节点 img_name root.find(filename).text img_path os.path.join(img_dir, img_name) # 用 PIL 读真实尺寸不要信 XML 里的 size 节点 with Image.open(img_path) as im: w, h im.size lines [] for obj in root.findall(object): cls_name obj.find(name).text.strip() if cls_name not in class_to_id: continue counter[cls_name] 1 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) # 归一化并限制在 0-1防止标注越界 xc min(max((xmin xmax) / 2 / w, 0), 1) yc min(max((ymin ymax) / 2 / h, 0), 1) bw min(max((xmax - xmin) / w, 0), 1) bh min(max((ymax - ymin) / h, 0), 1) lines.append(f{class_to_id[cls_name]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) # 输出同名 txt txt_name os.path.splitext(xml_file)[0] .txt with open(os.path.join(out_dir, txt_name), w) as f: f.write(\n.join(lines)) print(类别分布, counter) convert_voc_to_yolo(./Annotations, ./JPEGImages, ./labels)这段代码的关键点有三个。第一图片尺寸用 PIL 实时读取而不是用 XML 里size节点记录的值因为标注工具偶尔会写错尺寸用错尺寸会导致所有框偏移。第二归一化后做了min(max(...))截断防止个别越界标注产生大于 1 的坐标YOLO 训练时遇到这种值会直接报错。第三类别名到索引的映射必须和后续训练时的data.yaml完全一致顺序错了模型学出来的类别就是乱的。转换完成后你会得到和图片同名的.txt文件放在labels目录下。2.3 目录结构要按 YOLO 约定摆好YOLO 训练对目录结构有固定要求常见做法是建一个数据集根目录下面分images和labels各自再分train、val有时候还有test。图片和标注文件必须同名只是扩展名不同。我一般按 8:1:1 切分883 张图大约训练集 706 张、验证集 88 张、测试集 89 张。切分时要注意同一片茶园、同一批次拍的图不能同时出现在训练和验证集里否则验证指标会虚高。下面这段脚本完成切分和目录搬运。import os import shutil import random random.seed(42) # 固定随机种子保证可复现 img_dir ./JPEGImages lbl_dir ./labels out_root ./tea_dataset all_imgs [f for f in os.listdir(img_dir) if f.lower().endswith((.jpg, .png, .jpeg))] random.shuffle(all_imgs) n len(all_imgs) n_train int(n * 0.8) n_val int(n * 0.1) splits { train: all_imgs[:n_train], val: all_imgs[n_train:n_train n_val], test: all_imgs[n_train n_val:] } for split, files in splits.items(): img_out os.path.join(out_root, images, split) lbl_out os.path.join(out_root, labels, split) os.makedirs(img_out, exist_okTrue) os.makedirs(lbl_out, exist_okTrue) for f in files: shutil.copy(os.path.join(img_dir, f), os.path.join(img_out, f)) txt os.path.splitext(f)[0] .txt src_txt os.path.join(lbl_dir, txt) if os.path.exists(src_txt): shutil.copy(src_txt, os.path.join(lbl_out, txt)) else: print(缺失标注, txt) print(切分完成, {k: len(v) for k, v in splits.items()})跑完这段数据集就摆成了 YOLO 能直接吃的形态。如果打印出「缺失标注」说明有图片没有对应的 txt要么是原数据集漏标要么是转换时类别名没匹配上被跳过了需要回去检查。3. 用 YOLOv8 在本地跑通茶叶病害训练的最小流程3.1 环境配置与依赖安装YOLOv8 对环境的要求不算苛刻Python 3.8 到 3.11 都能跑PyTorch 版本跟 CUDA 匹配就行。我一般用 conda 建一个干净环境避免和系统里的包打架。如果你搜 yolo v8 anaconda 环境配置要求核心就是三件事Python 版本、PyTorch 与 CUDA 对应、ultralytics 包。下面这套命令是我在 Ubuntu 和 Windows 上都验证过的。conda create -n tea_yolo python3.10 -y conda activate tea_yolo # 根据你的 CUDA 版本去 PyTorch 官网选对应命令这里以 CUDA 11.8 为例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python pillow装完之后跑一句yolo checks它会打印出 PyTorch 版本、CUDA 是否可用、设备信息。如果显示CUDA:0说明 GPU 能用显示CPU就说明 CUDA 没配好训练会慢几十倍。CPU 训练 883 张图跑 100 轮大概要几个小时GPU 上十几分钟就能出结果所以能上 GPU 尽量上。3.2 data.yaml 的写法与类别顺序YOLOv8 靠一个data.yaml文件找到数据和类别。这个文件里names的顺序必须和转换脚本里的classes列表完全一致差一个位置模型就学错。path写数据集根目录train、val、test写相对路径。下面是我这份茶叶病害数据集的配置。path: ./tea_dataset train: images/train val: images/val test: images/test nc: 8 names: 0: 病斑A 1: 病斑B 2: 病斑C 3: 病斑D 4: 病斑E 5: 病斑F 6: 病斑G 7: 健康nc是类别数这里 8 类就写 8。names可以用列表形式也可以像上面这样用字典字典形式更直观不容易数错。写完存成tea.yaml放在项目根目录。3.3 启动训练与关键参数含义训练命令本身很短但参数怎么设决定了你能不能跑出可用模型。下面这条是我在茶叶病害数据上常用的起手配置。yolo detect train \ datatea.yaml \ modelyolov8s.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ patience30 \ projectruns/tea \ nameexp1modelyolov8s.pt用的是预训练权重小目标检测场景下 s 版本比 n 版本召回更好又不像 m 那么吃显存。imgsz640是输入尺寸茶叶病斑在叶片上占比小如果你显存够可以提到 800 甚至 1024小目标召回会明显改善。batch16在 8G 显存上比较稳显存不够就降到 8。lr00.01是初始学习率用预训练权重时这个值合适如果你从头训练可以降到 0.001。patience30表示验证指标 30 轮不提升就早停避免过拟合。训练过程中重点看mAP50和mAP50-95两个指标前者宽松后者严格农业检测里 mAP50 到 0.85 以上基本可用。3.4 训练完怎么验证和推理训练结束后权重存在runs/tea/exp1/weights/best.pt。验证用yolo detect val推理单张图用yolo detect predict。下面这条命令对测试集跑一遍输出每类的精确率和召回率。yolo detect val modelruns/tea/exp1/weights/best.pt datatea.yaml splittest如果某类召回特别低比如某个病斑类只有 0.4先别急着调模型回去看这类样本是不是太少或者标注框是不是画得太松。农业病害的类间差异本来就小标注质量对结果的影响比模型结构大得多。4. 训练茶叶病害模型最容易踩的五个坑4.1 类别索引错位导致模型全学偏现象训练 loss 正常下降但验证时所有预测框的类别都是错的或者某一类永远不出现。原因VOC 转 YOLO 时的classes列表顺序和data.yaml里的names顺序不一致比如转换时「健康」排第 8yaml 里排第 1。解决转换脚本和 yaml 用同一份类别列表最好把类别定义抽成一个单独的classes.txt两边都读这个文件从源头杜绝不一致。4.2 图片尺寸读错导致框整体偏移现象训练能跑但预测框位置系统性偏移比如都往左上角偏。原因转换时用了 XML 里size节点记录的宽高而实际图片尺寸和记录值不符归一化分母错了。解决一律用 PIL 或 OpenCV 实时读取图片真实尺寸不要信任标注文件里的尺寸字段。转换完可以抽几张图用可视化脚本画框检查框和病斑对齐才算过。4.3 小目标病斑在 640 尺寸下召回低现象大块病斑检测很准针尖大的小病斑大量漏检。原因输入尺寸 640 时原图上几十像素的病斑缩到特征图上只剩几个像素特征太弱。解决把imgsz提到 800 或 1024同时开启 YOLOv8 的mosaic和copy_paste增强让模型多见小目标。如果显存不够用yolov8m换yolov8s不一定有用优先提输入尺寸。4.4 验证集指标虚高但实际部署翻车现象验证集 mAP 很高拿到新茶园图片上检测效果差很多。原因切分数据时没有按拍摄批次或茶园分组同一片叶子的相似图同时进了训练和验证集模型其实在「背答案」。解决切分前先按拍摄时间、地点、批次分组整组进训练或验证保证验证集和训练集来自不同分布。这个坑在农业数据集里极其常见血泪经验。4.5 标注框过松导致定位不准现象类别分类对了但框比病斑大一圈IoU 上不去。原因标注时框画得太随意把周围健康组织也框进去了。解决农业病害标注要贴着病斑边缘画宁可略紧不要过松。如果数据集已经这样了训练时把box损失权重适当调高或者用yolov8的close_mosaic在最后几轮关掉增强让模型更关注精确定位。5. 把 883 张用到极致小样本农业检测的进阶技巧883 张 8 类平均每类一百出头这个量级做检测属于小样本。想把效果再往上推我一般从三个方向下手。第一是离线增强训练时 YOLO 自带的增强是随机的你可以在转换阶段额外生成一批增强图比如随机旋转、亮度扰动、局部裁剪把训练集扩到两三千张。注意增强只对训练集做验证集保持原样。第二是迁移学习策略先用 COCO 预训练权重跑 50 轮冻结 backbone再解冻全量微调 100 轮这样小样本下收敛更稳。第三是推理阶段的技巧部署时把置信度门限从默认 0.25 调到 0.35 到 0.4能过滤掉大量低置信度假阳性农业场景里误报比漏报更让人头疼。验证模型是否真的可用我习惯做一个混淆矩阵加 PR 曲线的组合检查。YOLOv8 验证时会自动生成confusion_matrix.png和PR_curve.png重点看两类健康叶片有没有被误判成病斑以及两个症状相近的病斑类之间有没有互相混淆。如果健康类误报多说明模型对健康叶片的特征学得不够需要补健康样本如果两个病斑类互相混淆说明类间差异太小要么合并类别要么在标注时把区分特征标得更明确。最后说个我自己的习惯每次训完模型我都会挑 20 张验证集里预测最差的图一张张看是标注问题还是模型问题。十次里有六七次是标注框画歪了或者类别标错了。农业数据集的质量参差不齐与其反复调参不如先把标注清洗一遍。这个习惯帮我省下的时间比换模型结构多得多。希望帮到你。本文还有配套的精品资源点击获取