简介面向茶叶病害检测与识别任务的数据集内含9591张茶叶图像的Pascal VOC与YOLO双格式标注覆盖茶黑腐病、茶褐枯病、茶锈病、红蜘蛛为害叶、茶小绿叶蝉为害叶、健康茶叶、茶白星病及未分类病害共8个类别可直接用于YOLO系列目标检测模型的训练与评估。资源包共2000个文件以VOC格式XML标注文件为主体并搭配YOLO格式txt标注及说明文本压缩后约102MB目录结构清晰便于批处理解析。已有1223人学习浏览适合从事茶树病害监测、智慧农业视觉分析场景的开发者或研究者使用。下载后可快速获得图像与标注的对应关系省去从零整理标签格式的耗时也有助于训练多类别病害分类器支撑病害预警与田间管理决策。1. 茶叶病害检测数据集9591 张 VOCYOLO 双格式拿来就能训做农业视觉检测的人都有体会找数据集最折磨人的往往不是图不够而是标注格式不统一。有的给 VOC XML有的只给 COCO JSON还有的给一堆剪裁好的图类别名全是拼音缩写。这份茶叶病害检测数据集把这事省了——9591 张叶片图像8 个类别压缩包里同时放了 VOC 和 YOLO 两套标注解压后 YOLOv5、YOLOv8 直接开训不用再写格式转换。适合做茶叶病害识别落地的工程人员、想换一份规整数据练目标检测流程的新手以及需要标注基准做算法对比的研究者。下面我把目录结构、标签对应、训练参数和踩坑记录逐条拆开讲。2. 八类病害标签与双格式对应看清标注再动手2.1 8 个类别怎么定义先背下来拿到数据集第一件事不是解压而是先看类别表。这份数据的 8 个类别对应茶叶生产里最常见的叶部病害外加一个健康类具体如下class_idVOC/YOLO 标签名中文名病斑特征0healthy健康叶片无明显病斑1anthracnose茶炭疽病褐色近圆形病斑边缘有黄晕2blister_blight茶饼病叶面凹陷、叶背凸起幼叶高发3white_scab茶白星病灰白色小圆点散生4red_leaf_spot茶赤叶斑病不规则红褐色斑块5gray_blight茶轮斑病同心轮纹外围有晕圈6sooty_mold茶煤病黑色煤污层多在叶面7brown_blight茶云纹叶枯病大型云纹状枯斑这个顺序不是随便排的它在整套数据里是固定的。VOC 的 XML 里name字段写的是字符串比如 anthracnoseYOLO 的 txt 里写的是对应编号 1。两者靠上面这张表对齐。我自己拿这种双格式数据的第一习惯是把这张表截图贴在项目笔记最前面后面所有脚本、yaml、训练日志都基于它避免中途改类别顺序导致全部标注错位。健康叶片这个类很多人会忽略但它恰恰是田间落地的关键。实际巡检时大部分框选出来的是没病的叶子如果训练集里没有足够多的健康样本模型就会把一切叶片都当病叶报误报率高到没法用。所以看到数据集里包含 healthy说明做标注的人考虑了真实场景分布这是加分项。2.2 同一个目标在 XML 和 TXT 里的两种写法VOC 格式的核心是一个 XML 文件对应一张图框的信息以像素坐标存在bndbox里。随便抽一张图它的标注长这样annotation folderJPEGImages/folder filenametea_01234.jpg/filename size width640/width height480/height depth3/depth /size object nameanthracnose/name bndbox xmin128/xmin ymin96/ymin xmax340/xmax ymax312/ymax /bndbox /object object namehealthy/name bndbox xmin400/xmin ymin220/ymin xmax590/xmax ymax430/ymax /bndbox /object /annotation一个object就是一个目标name是类别字符串bndbox里是左上角和右下角的像素坐标。如果一张图里有多个病害区域就会有多个 object 节点。要注意filename只写文件名不带路径实际读取时靠目录去定位。YOLO 格式则完全不同它是每张图对应一个同名 txt每行五个数类别编号、中心点 x、中心点 y、框宽、框高全部归一化到 0~1。上面这个 XML 转成 YOLO 后是两行1 0.365625 0.425000 0.331250 0.450000 0 0.773438 0.677083 0.296875 0.437500第一行 1 对应 anthracnose中心点 x(128340)/2/6400.3656中心点 y(96312)/2/4800.425。第二行 0 对应 healthy算法一样。这就是为什么 YOLO 训练时看不到像素坐标所有框都被压进了 0~1 区间模型预测的也是归一化坐标推理后再乘回原图尺寸。2.3 目录结构两套标注怎么对应解压后你会看到两个顶层目录一个 VOC 风格一个 YOLO 风格大致是这样VOC/ ├── Annotations/ # 每张图一个 xml ├── JPEGImages/ # 全部原图 └── ImageSets/ └── Main/ ├── train.txt ├── val.txt └── test.txt YOLO/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/两套结构的逻辑不同。VOC 的ImageSets/Main里的 txt 只存图片文件名不带扩展名标注全部堆在Annotations靠 XML 里的 filename 关联。YOLO 则是按 train/val/test 分目录图片和标签目录一一对应找标签的规则就是把images/train/a.jpg的路径替换成labels/train/a.txt。这份数据的两套划分是对齐的也就是说 VOC 里ImageSets/Main/train.txt列出的图和 YOLO 里images/train下的图是同一批。这个细节在做算法对比时很重要——如果你自己重新抽样两套格式就不等价了后面比出来的结果差异到底是标注格式造成的还是抽样造成的说不清。提示拿到手先核对一下两套划分的文件数是否一致。方法很简单分别对两个 train 列表排序后 diff几秒钟的事。3. 从 7z 到能跑训的环境解压、数量校验与格式转换脚本3.1 先解压Windows 和 Linux 两条路压缩包是 7z 格式不是 zip。Windows 上 7-Zip 应该都有了右键压缩包选「解压到当前文件夹」就行。这里有个细节如果下载下来是好几个分卷比如.7z.001、.7z.002必须保证所有分卷躺在同一目录再解压只拖第一个文件进去会提示缺少分卷。Linux 服务器上更常见的情况是没装 p7zip直接7z命令会报 command not found。按照下面两步走# 安装 p7zip 工具集 sudo apt install p7zip-full # 先测试压缩包完整性再解压 7z t tea_disease.7z 7z x tea_disease.7z -o/home/user/datasets/参数说明7z t是 test 模式只校验压缩包有没有损坏不实际写文件7z x会保留压缩包里的目录层级结构解压出来的目录和打包时一致。很多人会手滑用7z ee是把所有文件平铺到一个目录不保留子目录对这份数据来说会把 JPEGImages 和 Annotations 混在一起后面脚本全乱。我习惯把 test 和 x 连着跑test 过了再 x不然后面训练到一半才发现某个文件损坏返工成本太高。另外要注意 7-Zip 和 p7zip 的版本。老版本对较新的压缩算法支持不全解压理论上应该成功的包也会报错。Windows 下把 7-Zip 升到 21.x 以上Linux 下确保 p7zip-full 是最新源里的版本这类莫名报错基本能消掉八成。3.2 解压后的数量校验数据集的简介写的是 9591 张解压完第一件事就是对账。一个很简单的思路分别数图片和标签两者数量一致再和 9591 对对不上就说明解压或传输有问题。脚本不用复杂几行 Python 就够import os for split in [train, val, test]: img_dir fYOLO/images/{split} lab_dir fYOLO/labels/{split} # 只数 jpg按实际情况改后缀 n_img len([f for f in os.listdir(img_dir) if f.lower().endswith(.jpg)]) n_lab len([f for f in os.listdir(lab_dir) if f.endswith(.txt)]) print(f{split}: images{n_img}, labels{n_lab}, matched{n_img n_lab})这个脚本就是最朴素的对账逻辑三个 split 各输出一行。如果 images 和 labels 数量不等说明某张图缺标注或者标注文件是空文件但还在目录里。出现这种情况直接删掉对应图片和空标签不要让空标注流进训练——YOLO 训练时会把空 txt 当成「无目标」图数据分布被悄悄带偏。数量对完还要看后缀大小写。部分图像可能是.JPG或.png上面的 lower() 已经把大小写差异抹掉了比直接 endswith(.jpg) 稳。后缀大小写不一致是压缩包传输时最常见的隐性坑macOS 和 Linux 之间尤其容易碰到。3.3 VOC 转 YOLO 脚本顺序固定、坐标边界兜底其实这份数据已经给了 YOLO 格式正常情况下不需要再转。但做工程的人经常会遇到三种情况需要自己转换类别顺序、重新划分 train/val、或者把 VOC 目录里新增的图片合并进来。所以脚本还是要备一份核心逻辑就是把 XML 里的像素框归一化成 YOLO 的 cx、cy、w、himport os import xml.etree.ElementTree as ET # 顺序写死禁止用 set 去重后转 list CLASSES [healthy, anthracnose, blister_blight, white_scab, red_leaf_spot, gray_blight, sooty_mold, brown_blight] def voc_to_yolo(xml_path, out_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in CLASSES: continue # 未知类别直接跳过避免污染 cls_id CLASSES.index(name) box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) # 像素坐标转归一化中心点宽高 cx ((x1 x2) / 2) / w cy ((y1 y2) / 2) / h bw (x2 - x1) / w bh (y2 - y1) / h # 越界兜底防止训练时报 out of bounds 警告 cx min(max(cx, 0.0), 1.0) cy min(max(cy, 0.0), 1.0) bw min(max(bw, 0.0), 1.0) bh min(max(bh, 0.0), 1.0) if bw 0.005 or bh 0.005: continue # 面积过小的框没有训练价值 lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) with open(out_path, w) as f: f.write(\n.join(lines) \n) xml_dir VOC/Annotations out_dir YOLO/labels/train os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): stem os.path.splitext(xml_file)[0] voc_to_yolo(os.path.join(xml_dir, xml_file), os.path.join(out_dir, stem .txt))逻辑说明CLASSES列表的顺序就是目标类别编号这个顺序必须写死千万不能用set()去重后转 list——集合是无序的每次跑出的映射都不一样。root.iter(object)遍历一张图里的所有目标一个目标生成一行。归一化公式前面说过中心点两端坐标均值除以图宽高。后面那段 clamp 和面积过滤是保命用的很多第三方标注工具会导出略微越界的框比如 xmax 比图宽还大 1 像素不兜底的话 YOLO 训练时会刷一堆 out of bounds 警告严重的直接把 loss 带歪。参数说明xml_dir和out_dir按实际路径改CLASSES要和你最终训练用的 names 完全一致。如果数据集原始标签里某个名字不在列表里代码会静默跳过日志不会报错所以转完后建议抽 5~10 个 txt 人工看一下首列数字是否合理别偷懒。4. 用它训练 YOLOv8dataset.yaml、损失函数与参数经验4.1 dataset.yaml 的写法和路径陷阱拿到一份规整的 YOLO 格式数据训练第一步是写数据集描述文件。YOLOv8 用的是 YAML核心就两件事告诉框架图片在哪、标签对应的类别名是什么# tea_disease.yaml path: D:/datasets/tea_disease train: images/train val: images/val test: images/test nc: 8 names: 0: healthy 1: anthracnose 2: blister_blight 3: white_scab 4: red_leaf_spot 5: gray_blight 6: sooty_mold 7: brown_blightpath是数据根目录train/val/test相对它定位。这里有三个很常见的坑。第一path用相对路径时是相对「当前工作目录」解析的不是相对 yaml 文件所在目录如果你在项目根目录跑命令、yaml 放在数据集目录里路径会解析错。我习惯直接用绝对路径或者把 yaml 丢到数据集根目录下再写相对路径。第二names的顺序必须和标签 txt 第一列的数字对齐一旦错位训练不报错但预测结果全乱。第三nc必须等于 names 的长度多写少写都会触发对齐错误。如果是在 Docker 或远程服务器上训练这份数据的双格式价值就体现出来了——VOC 目录可以作为备份存档YOLO 目录直接挂载进容器不需要在镜像里再装转换工具。数据不动、环境只读跑完一个实验销毁容器下一轮重新挂载保证实验可复现。4.2 训练命令与参数范围YOLOv8 的命令行训练很直接一条命令跑到底yolo detect train \ datatea_disease.yaml \ modelyolov8m.pt \ epochs200 \ imgsz640 \ batch16 \ patience40 \ lr00.001 \ device0 \ projectruns/tea_diseasemodelyolov8m.pt表示从预训练权重开始微调而不是随机初始化。9591 张图对农业细粒度病害来说数量不算多迁移学习收益明显尤其前几十个 epoch 收敛速度快很多。预训练权重会从官方自动下载到~/.cache第一次跑需要联网后面都走本地缓存。如果网络受限手动下载对应的.pt文件放到工作目录再指定路径也行。关键参数我给一个参考范围具体按显存和任务调参数参考值说明imgsz640数据原图接近这个分辨率不宜再放大batch8~328G 显存用 m 模型建议 8~16epochs150~300农业病害类 imgsz 小epoch 可以给足patience30~50早停轮数配合 epochs 用lr00.001~0.01迁移学习建议从 0.001 起步optimizerAdamW / SGD小数据集上 SGDmomentum 更稳显存估算有个粗公式显存占用大约正比于 batch × imgsz²。batch 16、imgsz 640 用 m 模型8G 显存基本顶满再往上就 out of memory。此时降 batch 到 8、换 n 或 s 模型更保险不要硬上大模型——农业病害这种细粒度分类模型容量不是瓶颈数据和标注质量才是。4.3 训练时看什么损失曲线与混淆矩阵训练启动后很多人只盯着 mAP 那个数其实最有信息量的是三条损失曲线。YOLOv8 的损失函数拆成三块box_loss是边界框回归损失用 CIoU 变体衡量预测框和真实框的重合度cls_loss是分类损失二值交叉熵dfl_loss是分布焦点损失负责框边界的精细回归。训练过程中results.png里这三条曲线都会画出来。判断标准有两个。第一看 train 和 val 曲线的间距train loss 一直降、val loss 降到某点开始回升是过拟合的信号此时加数据增强或提前早停val loss 全程剧烈抖动大概率是 lr 太高或 batch 太小先降 lr 再看。第二看混淆矩阵是不是对角占优。训练结束后runs/tea_disease/下会生成confusion_matrix.png对角线亮、其余位置暗就是正常状态。茶叶病害里最容易混淆的组合是 anthracnose 和 red_leaf_spot两者都是褐红色斑如果这对格子很亮说明特征差异在 640 分辨率下不够明显考虑截取病斑局部图做二次分类或者叠加一个细粒度分类头。提示训练完别急着删 runs 目录。weights/best.pt和weights/last.pt都要留best 是验证集上最优的权重last 是最后一轮的有时候 last 反而在真实场景上更稳。5. 避坑指南双格式数据集最容易翻车的五个位置5.1 7z 解压到一半报错分卷、版本与完整性现象解压到 30% 或 80% 弹出「数据错误」或「无法作为自解压文件运行」有时还提示密码错误但包根本没设密码。 原因最常见的是分卷没下全或者下载过程中文件被截断7z 校验文件头时就中止了。其次是 7-Zip 版本太老不支持较新的压缩算法。密码提示多半是文件本身损坏导致的误报压缩包损坏时错误信息会乱跳。 解决先把所有分卷放进同一目录跑一遍7z t看完整性Windows 升级 7-Zip 到 21.x 以上Linux 用 p7zip-full 最新版确认没问题再7z x。这套流程我现在拿到任何 7z 包都先走一遍属于最低成本的后悔药。5.2 类别编号对不上loss 正常但 mAP 为 0现象训练曲线非常正常loss 稳定下降验证时 mAP 却是 0预测结果类别全乱比如把煤病全预测成健康。 原因类别编号错位。txt 里第一列是数字yaml 里 names 是字符串两者靠顺序对齐。只要转换脚本的 CLASSES 顺序和训练 yaml 的 names 顺序不一致整个标签体系就整体平移一位。 解决以 VOC 的 XML 为基准抽 3~5 个文件人工比对一次。具体做法是找一个 XML 里nameanthracnose的框看对应 txt 同索引行首列是不是 1。这个检查 30 秒做完能省掉一整轮无效训练。5.3 空标注与越界框现象训练时刷大量WARNING: label file ... out of bounds或者数据里存在 0 字节 txt训练日志里对应图片永远没有 loss 贡献。 原因第三方标注工具导出时贴边框的 xmax 会略大于图宽空 txt 是标注员标完又删掉目标后留下的残留文件。 解决把 3.3 节脚本里的 clamp 逻辑跑一遍强制把坐标压回 [0,1]同时清理 0 字节标签和对应图片。检查命令很简单find YOLO/labels -name *.txt -size 0把空文件全列出来核对后删除。5.4 训练 loss 变 NaN 与 BN 崩溃现象训练一两个 epoch 后 loss 突然变成 NaN或日志里出现 BN running statistics 相关的异常后续 epoch 全部无效。 原因三个高频诱因——学习率过高导致梯度爆炸、batch 太小让 BN 统计值不稳、预训练权重和被改过的模型结构不匹配比如手动改了 nc 但没对应调整网络头。 解决先把 lr0 降到 0.001 试探 20 个 epoch能跑再拉回来batch 至少 8换预训练权重时直接用对应版本的 yolov8n.pt 或 yolov8m.pt不要自己改结构。BN 崩溃这种问题降 lr 永远是最快的止血手段。5.5 混淆矩阵数值总和不是 1现象训练完打开 confusion_matrix.png标题写着 normalized但每行加起来不是 100%甚至矩阵里所有数字加起来不到 1。 原因这个图默认是「行归一化」每个真实类别单独归一每行之和为 1。但背景列也算作一个预测类所以某类被误判为背景的比例也占一行里的份额。如果不确认归一化方向光凭直觉觉得「总和不对」很容易误判模型好坏。 解决读图前先看 colorbar 范围normalized 版的最大值是 1.0要算自己关心的指标直接看 val 输出的 precision、recall、mAP50 数字不要拿图上的色块猜。这个属于「看着像 bug 其实是特性」的典型我第一次看到也差点以为数据有问题。6. 把模型接进真实场景推理脚本与帧间投票6.1 推理脚本训练结束后先用没进过训练集的现场照片验证一遍再看真实视频流。推理脚本很短from ultralytics import YOLO model YOLO(runs/tea_disease/weights/best.pt) results model.predict( sourcefield_samples/, # 放几张现场实拍图 conf0.25, iou0.6, saveTrue, save_txtTrue, projectoutputs/field_test, )conf是置信度阈值低于 0.25 的框不输出iou0.6是 NMS 合并阈值同一位置的多余框会被压掉。田间巡检场景我会把 conf 抬到 0.4 左右宁可少框不可乱框因为误报框出现在健康叶片上工人得专门跑过去看一次才知道是错的。6.2 帧间投票单张图稳定不代表视频稳定。扫茶园时同一片叶子会出现在连续多帧里我给每帧输出加一个投票窗口连续几帧出现同一类别才保留from collections import deque window deque(maxlen5) # 滑动窗口保留最近 5 帧 def vote(frame_boxes, min_votes3): window.append(frame_boxes) if len(window) min_votes: return [] counter {} for boxes in window: for cls_id, _ in boxes: counter[cls_id] counter.get(cls_id, 0) 1 return [c for c, n in counter.items() if n min_votes]窗口 5 帧、阈值 3 次相当于目标要被持续检出约一秒才上报无人机晃动和叶片反光造成的偶发误报能压掉大半窗口太长又会让快速移动镜头的响应变迟钝3~5 帧是我试过的平衡点。这轮做下来我养成了一个固定习惯拿到任何数据集压缩包先7z t再解压解压完先数图片和标签、抽查三五个标注然后才谈训练参数模型上了现场也不看单帧效果先跑一遍帧间投票看稳定性。这份双格式数据把最麻烦的格式转换省掉了剩下的坑基本都在解压完整性和标签对应上按这个流程走都能提前挡住。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?