资讯动态

茶叶病害数据集实战:883张单叶图与8类标注的YOLO训练指南

发布时间:2026/10/9 4:33:50 来源:尧图企业网站定制
简介这份茶叶病害数据集面向农业图像识别、植物保护研究及深度学习目标检测方向的开发者与学习者提供VOC与YOLO双格式标注可直接用于训练和验证叶片病害检测模型。压缩包共约2000个文件包含883张jpg图片、883个VOC格式xml标注、883个YOLO格式txt标注及少量说明文件整体约200.5MB图片与标注一一对应省去格式转换的繁琐步骤。数据集覆盖8个类别包括炭疽病、藻斑病、鸟眼斑、褐枯病、灰枯病、健康叶、红叶斑和白斑总标注框数884个每张图仅含单片叶子适合单目标检测任务。所有标注均由labelImg工具以矩形框完成类别分布相对均衡其中红叶斑与白斑样本较多健康叶相对偏少便于研究者观察类别不平衡对模型的影响。目前已有887人学习下载可作为茶叶病害识别、农业智能巡检等课题的可靠数据基础。1. 茶叶病害数据集落地883 张单叶图与 8 类标注怎么用拿到一个标注好的茶叶病害数据集第一反应往往不是「太好了」而是「这 883 张图到底能不能直接喂给 YOLO」。这个包给的是 Pascal VOC 的 xml 和 YOLO 的 txt 双格式883 张 jpg 对应 883 个 xml 和 883 个 txt8 个类别总框数 884。注意这个数字图片 883 张框 884 个说明绝大多数图里只有一片叶子、一个病斑框只有一张图出现了两个框。这个细节决定了它适合做单目标检测而不是密集病斑分割。它解决的是「没有现成茶叶病害标注数据」的问题。做农业视觉的团队尤其是想验证 YOLO 在细粒度病害分类上表现的可以直接拿它跑 baseline。适合谁适合已经会跑 YOLOv5/v8 训练脚本、但缺数据的人也适合想练手 VOC 转 YOLO、检查标注质量的新手。不适合谁不适合想做病斑像素级分割的人因为标注规则明确写了只画矩形框没有分割路径的 txt。类别名是英文小写algalleaf、Anthracnose、birdeyespot、brownblight、graylight、healthy、redleafspot、whitespot。其中 healthy 只有 74 个框redleafspot 有 143 个whitespot 141 个类别不均衡是明摆着的。后面训练时如果直接跑healthy 的召回大概率会拖后腿。这个包的价值在于「省掉标注」但代价是你要自己处理不均衡和单叶场景的泛化问题。2. 拆包与格式核对VOC 和 YOLO 双格式到底怎么对应2.1 目录结构先理清别急着写 data.yaml拿到 zip 解压后常见做法是看到 images 和 labels 两个文件夹但 VOC 格式的 xml 往往单独放在 Annotations 里。这个包没有给分割路径的 txt所以只有 jpg、xml、txt 三类文件。我一般先跑一遍文件计数确认没有漏图或漏标注。# 统计三类文件数量确认是否一一对应 find . -name *.jpg | wc -l find . -name *.xml | wc -l find . -name *.txt | wc -l逻辑说明三个数字都应该是 883。如果 txt 少了几张说明有图片没转成功如果 xml 多出来可能是重复标注。参数上没什么可调的就是数数。这一步花不了一分钟但能避免后面训练时突然报「找不到 label」。2.2 VOC 的 xml 里到底存了什么随便打开一个 xml比如 firc_birdeyespot_49.xml你会看到 size、object、bndbox 这些节点。size 里是宽高和通道数object 里是 name 和 bndbox 的 xmin/ymin/xmax/ymax。这个包用的是 labelImg 标注所以 xml 结构是标准的 Pascal VOC没有旋转框也没有 difficult 标记。常见做法是写个脚本把 xml 里的类别名和框数抽出来核对摘要里给的数字。比如 Anthracnose 框数 99你就遍历所有 xml统计 name 为 Anthracnose 的 object 数量。如果对不上说明有 xml 被改过或者类别名拼写不一致。import os import xml.etree.ElementTree as ET from collections import Counter # 遍历 xml统计每个类别的框数 counter Counter() for f in os.listdir(Annotations): if not f.endswith(.xml): continue tree ET.parse(os.path.join(Annotations, f)) root tree.getroot() for obj in root.findall(object): name obj.find(name).text counter[name] 1 print(counter)逻辑说明ET.parse 读 xmlfindall(object) 拿到所有标注框name 就是类别。参数上注意如果 xml 里有 pose 或 truncated 节点不影响统计。跑完对照摘要里的框数表Anthracnose 99、algalleaf 114、birdeyespot 100、brownblight 113、graylight 100、healthy 74、redleafspot 143、whitespot 141加起来 884。如果某个类别差一两个可能是某张图里同一个类别画了两个框属于正常。2.3 YOLO txt 的归一化坐标怎么读YOLO 格式的 txt 每行是class_id x_center y_center width height全部归一化到 0-1。这个包里的 txt 和 xml 是一一对应的所以你可以用 xml 的 bndbox 反推归一化坐标验证 txt 有没有写错。常见坑是有些转换脚本会把 xmin/ymin 直接除以宽高忘了加一半的宽高得到中心点。我一般抽三张图手动算一遍。# 从 xml 的 bndbox 算 YOLO 归一化坐标和 txt 对比 import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, img_w, img_h): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text bnd obj.find(bndbox) xmin float(bnd.find(xmin).text) ymin float(bnd.find(ymin).text) xmax float(bnd.find(xmax).text) ymax float(bnd.find(ymax).text) xc (xmin xmax) / 2.0 / img_w yc (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{name} {xc:.6f} {yc:.6f} {w:.6f} {h:.6f}) return lines逻辑说明img_w 和 img_h 从 xml 的 size 节点里读不要硬编码。参数上归一化保留 6 位小数就够了YOLO 训练时不在乎再多几位。如果算出来的 xc 和 txt 里的对不上大概率是转换时用错了图片尺寸或者 xml 里的 size 和实际 jpg 尺寸不一致。后者在数据增强过的包里很常见但这个包是原始标注应该一致。2.4 类别名映射表要自己建YOLO 训练需要把类别名转成 0 到 7 的整数。这个包没有给 names 文件所以你得自己按字母序或按摘要里的顺序建。我一般按摘要里的顺序algalleaf0, Anthracnose1, birdeyespot2, brownblight3, graylight4, healthy5, redleafspot6, whitespot7。注意大小写Anthracnose 首字母大写其他都是小写写 data.yaml 时别抄错。# data.yaml 示例 path: ./tea_dataset train: images/train val: images/val nc: 8 names: [algalleaf, Anthracnose, birdeyespot, brownblight, graylight, healthy, redleafspot, whitespot]逻辑说明path 是数据集根目录train 和 val 是相对路径。nc 是类别数必须和 names 长度一致。参数上如果后面要做交叉验证可以把 train 和 val 都指向同一个 images 文件夹用 YOLO 的 split 参数自动划分但常见做法还是手动分好 train/val避免数据泄漏。3. 训练前处理划分、增强与类别不均衡的应对3.1 训练集验证集怎么分才不翻车883 张图按 8:2 分训练集 706 张验证集 177 张。但要注意同一个叶子的不同病害图不能同时出现在训练和验证里否则验证指标会虚高。这个包的命名有规律firc_birdeyespot_49.jpg、firc_Anthracnose_70.jpg前缀 firc 加类别名加序号。我一般按类别分层抽样保证每个类别在验证集里都有至少 10% 的图。import os import random from collections import defaultdict # 按类别分层划分 train/val files [f for f in os.listdir(images) if f.endswith(.jpg)] cls_files defaultdict(list) for f in files: cls f.split(_)[1] # 从文件名提取类别 cls_files[cls].append(f) train, val [], [] for cls, flist in cls_files.items(): random.shuffle(flist) split int(len(flist) * 0.8) train.extend(flist[:split]) val.extend(flist[split:]) print(len(train), len(val))逻辑说明从文件名提取类别依赖命名规范这个包是 firc_类别_序号.jpg所以 split(_)[1] 能拿到类别。参数上random.shuffle 前设个种子保证每次划分一致。如果文件名不规范就得从 xml 里读类别再分层。3.2 数据增强别乱开单叶场景有讲究这个包所有图都是单叶背景相对干净。常见做法是开 mosaic、mixup、HSV 抖动、随机翻转。但要注意mosaic 会把四张图拼成一张如果四张都是单叶拼出来就是四片叶子模型可能学到「一张图多片叶子」的模式而实际推理时你给的是一张图一片叶子。我一般把 mosaic 的概率调低到 0.3 左右或者干脆关掉用 copy-paste 增强代替。# YOLOv8 训练时关闭 mosaic 的配置片段 from ultralytics import YOLO model YOLO(yolov8n.pt) model.train( datadata.yaml, epochs100, imgsz640, mosaic0.0, # 关闭 mosaic mixup0.0, # 关闭 mixup hsv_h0.015, # 色调抖动 hsv_s0.7, # 饱和度抖动 hsv_v0.4, # 亮度抖动 fliplr0.5, # 水平翻转 flipud0.0 # 垂直翻转关掉叶子方向有意义 )逻辑说明mosaic 和 mixup 对小数据集通常有帮助但单叶场景下可能引入分布偏移。参数上hsv_h 控制色调茶叶病害的颜色是重要特征别调太大flipud 关掉是因为叶子正反面在图像里方向固定垂直翻转会造出不存在的姿态。3.3 类别不均衡healthy 只有 74 个框怎么办healthy 74 个框redleafspot 143 个差了近一倍。直接训练模型会偏向多数类。常见做法有三种过采样少数类、在 loss 里加类别权重、或者用 focal loss。YOLOv8 默认的分类 loss 是 BCE不直接支持类别权重但你可以通过复制 healthy 的图片来平衡。# 过采样 healthy 类复制到临时文件夹再合并 mkdir -p oversample/healthy for f in images/*healthy*.jpg; do cp $f oversample/healthy/ cp ${f%.jpg}.txt oversample/healthy/ 2/dev/null done # 把 oversample/healthy 里的图复制回训练集重复 2 次逻辑说明复制图片和对应的 txt让 healthy 的样本数接近其他类。参数上复制倍数看差距74 到 140 左右需要复制 2 次。注意别复制到验证集里否则验证指标失真。另一种做法是在 data.yaml 里给每个类别设权重但 YOLOv8 不直接支持得改源码不推荐新手折腾。3.4 输入尺寸选 640 还是 1024这个包的图片是单叶叶子在画面里占比较大。640 的输入下病斑可能只有几十个像素小目标检测会吃力。我一般先跑 640 看 baseline如果 redleafspot 和 whitespot 的召回低再换 1024。但 1024 的显存占用是 640 的 2.5 倍左右batch size 要相应调小。# 切换输入尺寸 model.train(datadata.yaml, epochs100, imgsz1024, batch8)逻辑说明imgsz 是训练和推理的输入尺寸必须是 32 的倍数。参数上batch 根据显存调8G 显存跑 1024 大概能到 batch8再大就 OOM。如果显存不够可以用梯度累积模拟大 batch。4. 避坑与排查标注、转换、训练里的五个血泪经验4.1 现象训练报「Label class 8 is invalid」原因data.yaml 里 nc 写成了 8但 names 列表只有 7 个或者某个 txt 里的 class_id 是 8。这个包的类别是 0 到 7如果转换脚本把类别名映射错了比如把 whitespot 映射成 8就会报这个。解决先检查 data.yaml 的 nc 和 names 长度是否一致再遍历所有 txt看有没有大于等于 nc 的 class_id。import os nc 8 for f in os.listdir(labels): if not f.endswith(.txt): continue with open(os.path.join(labels, f)) as fh: for line in fh: cid int(line.split()[0]) if cid nc: print(f, cid)4.2 现象验证集 mAP 很高但推理时什么都检测不到原因验证集和训练集划分时同一个叶子的不同病害图被分到了两边导致验证集泄漏。或者验证集的图片做了增强而推理时没有。解决按文件名前缀分组确保同一个叶子的图只出现在一边。这个包的命名里firc_类别_序号序号不同的图可能是同一片叶子的不同角度但摘要说「所有图片都是一个图片包含一个叶子」所以不同序号大概率是不同叶子。保险起见按序号分组同一序号的图只放一边。4.3 现象healthy 类几乎全漏检原因healthy 只有 74 个框训练时被多数类压制。模型倾向于把 healthy 也预测成有病斑的类别。解决过采样 healthy或者在推理时对 healthy 的置信度阈值单独调低。常见做法是先把 healthy 的图复制到训练集里让它的框数到 140 左右再重新训练。4.4 现象xml 里的 size 和 jpg 实际尺寸不一致原因标注时图片被缩放或裁剪过但 xml 里记的是原始尺寸。这个包是 labelImg 标注一般不会出现但如果你自己做了预处理就可能引入。解决用 PIL 读 jpg 的尺寸和 xml 里的 width/height 对比不一致的图要么重新标注要么在转换时用实际尺寸归一化。from PIL import Image import xml.etree.ElementTree as ET img Image.open(images/firc_birdeyespot_49.jpg) w, h img.size tree ET.parse(Annotations/firc_birdeyespot_49.xml) root tree.getroot() size root.find(size) print(w, h, size.find(width).text, size.find(height).text)4.5 现象YOLO txt 里的坐标全是 0 或 1原因转换脚本把 xmin/ymin 直接除以宽高没有加一半的宽高得到中心点或者把 xmax/xmin 搞反了。解决用 2.3 节的脚本重新算一遍和现有 txt 对比。如果对不上就批量重新生成 txt。注意备份原始 txt别直接覆盖。5. 进阶用法用这个数据集验证 YOLO 的细粒度分类能力这个包最值钱的地方不是「能跑通 YOLO」而是「能验证 YOLO 在细粒度病害上的边界」。8 个类别里birdeyespot 和 redleafspot 在颜色上接近Anthracnose 和 brownblight 在形状上接近healthy 和 graylight 在纹理上容易混。你可以拿它做消融实验换不同的 backbone、换不同的 head、加不加注意力机制看哪一类的 mAP 提升最明显。我一般会先跑一个 baseline记录每个类别的 AP。然后改 YOLOv8 的 head比如换成 efficient head再跑一遍对比 redleafspot 和 whitespot 的 AP 变化。如果提升集中在多数类说明模型只是记住了频率没有学到细粒度特征。这时候可以试试 focal loss或者把输入尺寸从 640 提到 1024。# 按类别输出 AP 的验证脚本片段 from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) metrics model.val(datadata.yaml, splitval) for i, name in enumerate(metrics.names): print(name, metrics.box.ap[i])逻辑说明metrics.box.ap 是每个类别的 AP 数组顺序和 names 一致。参数上splitval 指定验证集。如果某个类别的 AP 低于 0.3就重点看它的混淆矩阵确认是被哪个类吃掉了。另一个进阶用法是把这个数据集当预训练再迁移到更大的茶叶病害数据集上。因为它是单叶、干净背景学到的特征偏向病斑本身而不是背景。迁移时冻结 backbone只训 head往往比从头训收敛快。从那以后我每次拿到新数据集都强制先跑一遍文件计数和类别统计再抽三张图手动核对坐标。这个习惯帮我省掉了至少两次通宵排查标注错误。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑