尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

茶叶病害检测数据集VOC+YOLO格式解析及YOLOv8训练实战指南

茶叶病害检测数据集VOC+YOLO格式解析及YOLOv8训练实战指南 简介这套茶叶病害检测数据集提供了9591张真实茶树叶图像同时包含Pascal VOC与YOLO两种标注格式适合目标检测算法训练、模型评估及农业AI场景落地面向计算机视觉学习者和算法研发人员。压缩包整体约101.93MB共2000个文件其中1999个为VOC格式XML标注文件另附1个TXT使用说明对应每张图片的YOLO格式txt标注可用于YOLO系列等主流框架直接训练。数据覆盖8类常见病虫害与叶片状态包括Black rot of tea、Brown blight of tea、Leaf rust of tea、Red Spider infested tea leaf、Tea Mosquito bug infested leaf、White spot of tea、健康Tea leaf等类别标签系统完整能支撑病害识别、检测精度对比与鲁棒性实验。已有1223人学习查看数据规模与双标格式兼备可为茶叶病害智能诊断、科研课题或毕业设计提供可靠的数据基础。1. 搜到这个 7z 时先想清楚你要的不是数据集而是标注质量做茶叶病害检测的同行搜到这个标题时大多已经翻过好几轮数据集了。9591 张、8 个类别、VOC 和 YOLO 两套标注同时打包看着是开箱即用但我劝你先别急着解压训练。这类数据的标注质量参差常见情况是XML 和 txt 对不上数、类别顺序和文档不一致、部分图片带了 EXIF 方向信息导致框整体偏移。这篇笔记按我的落地流程走一遍——先读懂两套标注再转格式、跑通 yolov8 训练把训练中的坑和验证指标一起说清楚。适合手里有数据但还没跑通第一个模型的从业者也适合准备在这个方向投入的人评估这套数据到底值不值得用。2. VOC 与 YOLO 两套标注并存先从目录结构读懂这份数据2.1 解压后应该看到的目录布局拿到压缩包第一件事不是双击解压而是先看压缩包内部结构。.7z 格式在 Windows 下用 7-Zip 打开Linux 服务器上装 p7zip 后执行7z l 茶叶病害检测数据集VOCYOLO格式9591张8类别.7z列目录的作用是确认两套标注的存放路径避免解压后和一堆无关文件混在一起。常见的打包方式有几种VOC 部分按标准 VOC 结构放Annotations/、JPEGImages/、ImageSets/Main/YOLO 部分按images/、labels/、classes.txt放图片共用一份或各放一份都有可能。我先说标准的 VOC 目录长什么样因为这套数据集的 XML 标注几乎都沿用 VOC 约定VOC/ ├── Annotations/ # 每张图对应一个同名 .xml ├── JPEGImages/ # 原始图片jpg/png 混合 ├── ImageSets/ │ └── Main/ │ ├── train.txt │ ├── val.txt │ └── test.txtYOLO 部分我建议解压后立即核对labels/下的文件数量和images/是否一一对应。一个很容易踩的坑是原作者转 YOLO 格式时用了脚本但部分 XML 解析失败漏生成了几百个 txt。这个坑我在第 4 章专门展开这里记住一个判断标准——labels目录文件数应该等于images目录文件数最多允许极少量的空标签文件存在。2.2 VOC 标注XML的读取顺序与关键字段VOC 格式的核心是 XML 里size和object两块。我读取这类标注文件时顺序永远是先看size拿图片宽高再看object列表拿每个目标的类别和边界框。一个典型结构annotation filenametea_disease_0042.jpg/filename size width1280/width height960/height depth3/depth /size object nameanthracnose/name difficult0/difficult bndbox xmin312/xmin ymin405/ymin xmax520/xmax ymax633/ymax /bndbox /object /annotation三个值得注意的地方。第一filename未必和实际图片名一致有的数据集把文件名后缀写错读取脚本必须以Annotations/*.xml的 stem 去JPEGImages/里找图而不是直接信 XML 里的 filename。第二difficult为 1 的目标在 Pascal VOC 评测里是要忽略的转 YOLO 时默认跳过但如果这个数据集的 difficult 占比很高跳过会导致有效目标数量骤减需要慎重。第三bndbox里可能出现小数这种一般是标注软件输出的浮点值转整数还是保留浮点会影响后续转换精度建议保留浮点。2.3 YOLO 标注txt的归一化坐标换算YOLO txt 每行是一个目标格式固定为class_id x_center y_center width height五个值全部相对图片宽高做了归一化取值范围在 0 到 1 之间。比如一个目标在 1280×960 的图上框左上角是 (312, 405)右下角是 (520, 633)换算过程如下x_center (312 520) / 2 / 1280 0.325 y_center (405 633) / 2 / 960 0.540 width (520 - 312) / 1280 0.1625 height (633 - 405) / 960 0.2375实际读取时经常遇到两类问题。一类是坐标没有归一化整行写成0 312 405 520 633这种文件混在数据集里训练时边界框数值超过 1轻则 Loss 异常重则训练直接发散。另一类是类别 ID 从 1 开始而不是从 0 开始转换脚本里没减 1训练时 8 类数据被当成 9 类处理类别数都对不上。这两个问题我都建议在转换后用一个独立脚本全量检查一遍检查标准就是每行 5 个数值前四个都在 [0, 1] 区间最后一个整数小于类别总数。2.4 为什么同时给 VOV 与 YOLO 两份标注互相兜底而不是数据冗余不少朋友看到压缩包里两套标注第一反应是“占空间”或“是不是重复了”。实际这两套格式服务的阶段完全不同。VOC 格式的信息密度更高XML 里保存了 difficult、截断、遮挡等字段适合做数据清洗、类别统计和二次筛选YOLO 格式则被 ultralytics、Darknet 等训练框架直接消费训练代码根本不去解析 XML。我一般拿到数据后的标准动作是先用 VOC 的 XML 做统计确认 8 个类别的样本数分布和每张图的平均目标数判断这个数据集做检测任务的下限再决定是把自带的 YOLO 标注直接用来训练还是自己重新转一遍。多数情况下我会选择自己重新转——原因是原作者的转换脚本可能和我本地的图片目录命名习惯不一致重建一次虽然多花十分钟但能保证训练链路完全可控。3. 把这份数据跑通 yolov8 训练从 VOC 转 YOLO 到最小训练命令3.1 先统一 YOLO 目录布局images/labels 的 train-val-test 拆分ultralytics YOLO 对数据集目录的约定非常固定images/train、images/val、images/test分别放原始图片labels/train、labels/val、labels/test放同名 txt。图片文件名和标签文件名必须完全一致比如tea_001.jpg对应tea_001.txt。无论原始数据集自带的是ImageSets/Main/train.txt还是已经分好的目录我习惯是重新做一次划分。9500 多张图的规模按 8:1:1 划分train 大约 7600 张、val 和 test 各 950 张左右。顺序是先 shuffle 再切分保证类别分布均匀mkdir -p datasets/tea/images/{train,val,test} mkdir -p datasets/tea/labels/{train,val,test}这里不急着复制文件。先确认一个关键问题原数据集的 YOLO 标注是否已经被按某个比例分好了。如果labels/train已经存在直接沿用原划分会省事但风险是原作者可能没做同源去重——同一株茶树的连续拍摄帧可能同时进了 train 和 val导致 mAP 虚高。我一般会强制重新划分即使多花几分钟也要保证验证集的可信度。3.2 VOC 转 YOLO 脚本读取 XML、忽略 difficult、写入 txt当自带的 YOLO 标注可疑或想自己控制清洗口径时用 Python 把 VOC XML 转成 YOLO txt。这是整套流程里最值得花时间调试的一段我直接给出可复用的脚本import xml.etree.ElementTree as ET from pathlib import Path from PIL import Image, ImageOps voc_root Path(VOC) yolo_root Path(YOLO_clean) class_names [] # 后面从 XML 中自动收集 for xml_path in sorted(voc_root.glob(Annotations/*.xml)): tree ET.parse(xml_path) root tree.getroot() # 找图以 xml 的 stem 为准不信任 filename 字段 img_path voc_root / JPEGImages / f{xml_path.stem}.jpg if not img_path.exists(): img_path voc_root / JPEGImages / f{xml_path.stem}.png if not img_path.exists(): print(f[skip] missing image: {xml_path.stem}) continue # 用 PIL 读图并处理 EXIF 方向避免坐标错位 img ImageOps.exif_transpose(Image.open(img_path)) w_img, h_img img.size lines [] for obj in root.findall(object): if int(obj.findtext(difficult, 0)) 1: continue name obj.findtext(name).strip() if name not in class_names: class_names.append(name) bbox obj.find(bndbox) xmin float(bbox.findtext(xmin)) ymin float(bbox.findtext(ymin)) xmax float(bbox.findtext(xmax)) ymax float(bbox.findtext(ymax)) # VOC 是 x1,y1,x2,y2YOLO 需要中心点 宽高且必须归一化 x_center (xmin xmax) / 2.0 / w_img y_center (ymin ymax) / 2.0 / h_img w_norm (xmax - xmin) / w_img h_norm (ymax - ymin) / h_img lines.append( f{class_names.index(name)} {x_center:.6f} {y_center:.6f} f{w_norm:.6f} {h_norm:.6f} ) # 写入 txt文件名与图片保持一致 out_txt yolo_root / labels / f{xml_path.stem}.txt out_txt.parent.mkdir(parentsTrue, exist_okTrue) out_txt.write_text(\n.join(lines) \n if lines else )这段脚本里我做了三个关键决定。第一用ImageOps.exif_transpose读取图片尺寸并转正方向这样 XML 里的坐标和图片实际朝向一致避免模型训练时框错位。第二difficult1的目标直接跳过先保证训练目标都是可靠标注。第三类别名动态收集而不是硬编码 8 个名称——因为每个数据集的类别拼写习惯不同动态收集后再统一排序成固定 ID比直接套用原作者的 classes.txt 更稳妥。脚本跑完后用一行代码核对转换结果find YOLO_clean/labels -name *.txt | wc -l head -3 YOLO_clean/labels/xxx.txthead输出每行应该是类ID 浮点 浮点 浮点 浮点的格式。如果看到第二列或第三列大于 1 或小于 0说明转换脚本的归一化除错了或者原 XML 里混入了 non-VOC 格式的框。3.3 写数据集 YAML类别顺序就是 ID别和 names 错位yolov8 的数据集描述文件是 YAML内容特别简单但有一半的“训练后全错”问题出在这里。类别列表的顺序必须和 txt 里的 class_id 严格对应。path: /home/user/datasets/tea # 数据集根目录建议写绝对路径 train: images/train val: images/val test: images/test nc: 8 names: 0: tea_white_star 1: tea_anthracnose 2: tea_blister_blight 3: tea_brown_blight 4: tea_red_scab 5: tea_gray_blight 6: tea_caterpillar_damage 7: tea_mite_damage这里必须强调一个实践准则names的索引号 0 到 7必须和 3.2 脚本中class_names.index(name)的顺序一致。如果自带的 YOLO 标注已经被原作者按不同的字母顺序排过而我又按自己的顺序生成 txt就会发生“打出来的框是对的但类别标签全错位”的恶性事故。我的办法是转换完直接打印class_names然后照着这个列表一字不差地填进 YAML并且用脚本校验一遍读一个 txt 的 class_id再和 YAML 的 names 对齐看是不是同一个病害名。3.4 最小训练命令与三个关键参数目录、标注、YAML 都就绪后训练命令非常短yolo detect train \ datatea_disease.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ device0modelyolov8s.pt是自动下载的预训练权重首次运行会从官方仓库拉到本地。如果服务器不能直接访问下载地址先在本地手动下载后放到当前目录再换成model./yolov8s.pt。imgsz640是默认值茶叶病斑有相当一部分是小目标后面我会专门讲什么时候提高到 960。batch16是 24GB 左右显存的安全值如果是 V100 或更大显存可以拉到 64小显存降到 8 并把amp打开。epochs 不要一上来就 300先用 100 跑通流程看 Loss 收敛趋势再决定是否加。训练完成后runs/detect/train/weights/best.pt就是验证集上指标最好的权重。用这个权重对测试图片推理yolo predict modelruns/detect/train/weights/best.pt \ source./sample_disease_imgs \ conf0.25 \ saveTrue看到 test 目录下生成带框图片整套流程就算打通了。到这里新手已经能从原始压缩包跑出自己的第一个茶叶病害检测模型熟手则应该关注下一章的坑——我几乎每套数据集都会踩到其中一两个。4. 用这套数据集必踩的 5 个坑现象、原因、解决4.1 图片方向错位EXIF 旋转让框全部偏移现象训练时 mAP 看起来正常但推理时很多竖拍图片的检测框整体偏到画面角落横拍图片正常。原因茶叶病害图片很多来自手机或无人机拍摄JPEG 文件头里带 EXIF Orientation 字段图片实际显示方向已经被播放器自动转正但 OpenCV 的imread默认不处理这个字段。XML/TXT 里标注的坐标是在转正后的画面上标注的训练框架却按原始像素方向读图导致框和内容对不上。解决在数据预处理阶段统一转正并覆盖原图或者在数据集准备脚本里用ImageOps.exif_transpose读取尺寸。我已经在新装数据集时把这一步写成预处理流水线的一环不让 EXIF 问题进入训练链路。4.2 类别 ID 错位names 顺序和原发布者不一致现象训练正常、Loss 下降正常但预测结果显示“炭疽病”的框在图上实际是“茶饼病”并且所有类别整体偏移一位。原因原数据集的 YOLO 标注在打包前已经按原作者的类别顺序写死了 class_id。我自己转格式时如果按另一套顺序排名字txt 里存的 0 到 7 就全指向了错误的病害。解决不要信任任何classes.txt解压后立即扫描所有 XML 的name字段grep -h name VOC/Annotations/*.xml | sort | uniq -c得到真实的类别清单和数量后再按字母序或按发病部位排序固定成自己的names列表。这一步只要漏了后面所有指标都不可信。4.3 空标签文件被跳过训练集悄悄少了几百张现象训练日志里显示train: 6200 images但数据集的 train 目录明明有 7000 张图。原因部分 XML 里没有object节点或者图片后缀缺失、转换脚本找不到图直接continue导致这些图片没有对应的 txt。ultralytics 训练时会把没有标签的图片自动剔除。解决转换脚本里对无目标的样本也要生成空 txt并在日志里单独统计find YOLO_clean/labels -name *.txt -size 0 | wc -l同时对比images和labels的文件数差异超过 1% 就必须回到 3.2 的脚本排查到底是缺图还是缺标注。4.4 混淆矩阵总和对不上背景类混进了统计现象训练结束后看 confusion_matrix.png矩阵所有行的和明显大于该类别真实目标数甚至 val 集 GT 总共 5000 个目标矩阵总和算下来有 7000。原因YOLO 的混淆矩阵默认把置信度低于阈值的预测框当作背景类统计且每个 GT 只匹配一个预测框多余的预测全部落进背景行。所以矩阵总和并不等于 GT 总数而是一个和 conf 阈值、NMS 参数强相关的统计值。解决不要拿混淆矩阵的总和去反推数据集质量。要评估标注质量直接看 val 集每个类别的recall列如果某个类别 recall 低于其他类一大截优先怀疑该类别标注框数量不足或框太小。4.5 BN 崩溃小 batch 加大学习率Loss 直接 NaN现象第 0 个 epoch 的 Loss 直接显示nan然后整轮训练都是nanGPU 利用率还在正常跑。原因茶叶病害图像常见的深绿色背景像素值分布比较集中若 batch 过小比如 4BN 的统计量不稳定再加上默认lr00.01偏大很容易在第一个 step 数值溢出。解决先把lr0降为 0.001batch 提到 16 以上并显式关闭混合精度试跑一次yolo detect train datatea_disease.yaml modelyolov8s.pt \ epochs50 imgsz640 batch16 lr00.001 ampFalse能正常跑完前三个 epoch 再把ampTrue打开。BN 崩溃不能靠换预训练权重解决这是超参和 batch 的匹配问题。5. 跑验证集之前先校准这两件事mAP 与漏检排查5.1 先看 val_batch 图再看指标训练目录下会生成val_batch0_pred.jpg这类带预测框的拼接图。我要求自己先看这张图再看 mAP。原因很简单mAP 是个聚合指标它会把“某个类别整体漏检”和“某个类别框偏了”平均掉图片能直接暴露系统性问题。重点看三类痕迹一是大量 GT 框完全没被预测覆盖二是同一位置框非常小三是两个相邻病斑被并成一个框。茶叶病害检测里最典型的现象是“小病斑漏检”——一个叶片上有 20 个几像素的早期病斑预测只框出了其中 3 个大的。这类问题 mAP 下降幅度不大但实际田间使用就是不可用。看完图再去翻指标才知道下一步调什么。5.2 用混淆矩阵定位漏检类别yolov8 训练完会输出confusion_matrix.png横轴是预测类别纵轴是真实类别。这里的坑在 4.4 已经讲清楚矩阵总和不是 GT 总数但矩阵里的相对分布仍然有效。具体怎么看我给出一个固定流程先看对角线找到明显偏暗的类别那一类就是漏检重灾区再看该类别所在行落在“background”列的值如果这个值和对角线差不多甚至更大说明模型对该类别的区分度严重不足。在 8 类茶叶病害里茶饼病和炭疽病早期病斑形态极像这类混淆在矩阵里会表现为两个类别互相有大量串框。能用矩阵看出哪两类在打架比看十项指标都有用。5.3 用 F1-conf 曲线定置信度阈值yolov8 会自动画出 F1-Confidence 曲线它表达的是“置信度阈值从 0 到 1 变化时 F1 怎么变”。这个曲线的用途不是在训练时看而是决定推理时的conf参数。置信度阈值适用场景实际效果0.25默认实验对比、论文报告平衡可复现性好0.10~0.18大田普查、早期预警召回提升明显误检增加0.35~0.45专家复核、药效评估误检大幅减少漏检上升病害检测和车辆检测不同漏检一个早期病斑可能意味着整片茶园防治延迟所以我把推理阈值普遍设在 0.15 左右代价是背景误检变多。如果是做病害分级或出具报告再拉高到 0.35。这里没有绝对正确只有场景合适。5.4 按采集批次划分避免跨组泄漏打个比方同一片茶园的连续 10 帧照片光照、角度、病斑形态几乎没变化如果随机打散后一部分进 train 一部分进 valval 的精确率会虚高到接近 1.0一到新茶园立刻打回原形。解决方法是按采集批次分组组内整体划分from sklearn.model_selection import GroupShuffleSplit # images 列表每个元素是 (图片路径, 批次ID)批次ID来自文件夹或文件名前缀 split GroupShuffleSplit(n_splits1, test_size0.15, random_state42) train_idx, val_idx next(split.split(images, groups批次ID列表))这个处理对茶叶病害尤其重要因为大田采集普遍是“一小时连拍几百张”的模式不做分组划分后续任何模型上线都会有严重的泛化落差。我一般在解压后就把图片按文件名前缀或目录还原出批次信息再走上面这段逻辑。6. 让 9591 张图发挥更大价值切片、半监督与部署验证6.1 从 640 到 960切片推理救回小目标病斑茶叶早期病斑常小于 32×32 像素直接imgsz640训练会把小目标下采样到 8×8 以下特征基本损失。我的做法是先把训练尺寸提为imgsz960显存不够就batch8如果还是不够对原图做无重叠切片后单张推理再合并结果。6.2 用 best.pt 给未标注图打伪标签田间采集容易但标注贵。拿best.pt对未标注茶园图片推理筛选出置信度高于 0.5 的预测框写成 txt作为伪标签加入训练集。重复两轮能有效扩充到超过 1.5 万张训练图。只做一轮就会引入噪声我已经把“两轮伪标签 每轮人工抽检 30 张”作为固定流程。6.3 部署验证用 FP16 后必须重测 recall导出 TensorRT 或 OpenVINO 模型后不要只看导出成功就上线。FP16 推理会改变精度分布病斑边缘的小框可能因为数值误差被 NMS 误删。导完模型后用同一组 test 图对比 PT 模型和导出模型的 mAP差值超过 0.01 就退回 FP32。我现在的习惯是每拿到一套新数据先花半小时把第 2、3 章的预处理脚本完整跑一遍再谈训练。这个习惯帮我省掉过不止一次“训练三天发现标签错位”的返工。数据集的真正价值从来不在压缩包的大小而在标注能否经得起训练流程的检验。希望帮到你。本文还有配套的精品资源点击获取
返回列表