简介:这份番茄叶病害目标检测数据集面向农业智能化与计算机视觉方向的研究者、学生及算法工程师,用于训练和验证番茄叶片病害的自动识别与定位模型,可服务于智慧农业、病害防治等实际场景。资源包共1438个文件,压缩后约23.27MB,包含716张jpg图像、358个xml标注、362个txt标签,以及1个yaml配置和1个cache缓存文件,同时提供VOC与YOLO两套格式:VOC以JPEGImages配Annotations的xml边界框标注,YOLO以images配labels的txt标签,并已划分train.txt与val.txt,便于直接投入训练与验证。目前已有130人学习下载。数据集覆盖多种番茄叶病害类型,标注信息完整,兼容主流深度学习框架与YOLO等实时检测算法,读者可快速搭建训练流程、验证模型泛化能力,省去自行采集与标注的成本,适合作为目标检测入门与农业病害识别项目的实用数据基础。
1. 番茄叶病害目标检测数据集:从拿到压缩包到跑通第一轮训练
番茄叶片上那点病斑,看着简单,真做起来比想象中磨人。早疫病的同心轮纹、晚疫病的水渍状边缘、叶霉病的黄绿斑块,在自然光下互相重叠,背景还有土壤、支架、反光膜。你手里这个「番茄叶病害目标检测数据集.zip」,本质是一批已经标好边界框的叶片图像,目标类别通常是早疫病、晚疫病、叶霉病、健康叶这几类。它解决的不是「分类这张图是什么病」,而是「病斑在哪、有多大、有几处」——这对精准施药、病情分级、机器人巡检才是真正有用的输出。适合谁?做农业视觉的算法同学、想拿真实数据练 YOLO 系列目标检测的工程新手、以及需要给温室巡检设备做病害定位的开发者。这篇不聊虚的,从解压后的目录长什么样,一路讲到训练、验证、踩坑和进阶技巧。
2. 解压后先别急着训练:目录结构、标注格式与数据体检
2.1 一个典型番茄叶病害数据集的目录长什么样
拿到压缩包,第一件事不是unzip完就丢进 YOLO,而是先看清楚它到底怎么组织的。农业类目标检测数据集常见的两种结构:一种是已经切好 train/val/test 的,另一种是全部图片堆在一个目录、标注单独放。番茄叶病害数据集多数属于后者,解压后大致是这样:
tomato_leaf_disease/ ├── images/ │ ├── IMG_0001.jpg │ ├── IMG_0002.jpg │ └── ... ├── annotations/ │ ├── IMG_0001.xml # 或 .json / .txt │ ├── IMG_0002.xml │ └── ... └── classes.txt # 类别名列表,有时叫 labels.txt先跑一条命令把家底摸清楚,别凭感觉:
# 统计图片数量、格式分布、总大小 find tomato_leaf_disease/images -type f | wc -l find tomato_leaf_disease/images -type f -name "*.jpg" | wc -l find tomato_leaf_disease/images -type f -name "*.png" | wc -l du -sh tomato_leaf_disease/images # 看标注文件数量和扩展名 find tomato_leaf_disease/annotations -type f | sed 's/.*\.//' | sort | uniq -c这几条命令的含义很直白:第一条数图片总数,第二三条分别数 jpg 和 png,第四条看总体积,最后一条统计标注文件扩展名分布。为什么要先做这个?因为番茄叶病害数据集经常是多个来源拼的,jpg 和 png 混着来,标注有的是 Pascal VOC 的 xml,有的是 COCO 的 json,甚至有的子集只有图片没标注。你不先体检,后面转换脚本一跑就报错,还找不到原因。
提示:如果
classes.txt里类别名是中文,先确认训练框架的编码支持,YOLO 系列对中文类别名在部分版本会出问题,建议映射成英文或拼音。
2.2 标注格式判断:VOC、COCO 还是 YOLO txt
判断格式不用猜,打开一个标注文件看头部就行:
head -30 tomato_leaf_disease/annotations/IMG_0001.xml如果看到<annotation><object><bndbox><xmin>这种结构,就是 Pascal VOC。如果标注是.json且里面有"images"、"annotations"、"categories"三个顶层键,那是 COCO。如果标注是.txt且每行是class_id x_center y_center width height五个归一化到 0~1 的数,那已经是 YOLO 格式,省事了。
番茄叶病害数据集里 VOC 格式最常见,因为很多标注是用 LabelImg 做的,默认导出就是 VOC xml。VOC 的坐标是绝对像素值,YOLO 要的是归一化中心点加宽高,转换时必须除以图像宽高。这里有个高频翻车点:xml 里的<size>字段写的宽高,和实际图片的宽高不一致。标注工具偶尔会记录错,或者图片被后期裁剪过但 xml 没更新。所以转换前一定要用真实图片尺寸,不要信 xml 里的 size。
from PIL import Image import os, xml.etree.ElementTree as ET def check_size_mismatch(img_dir, ann_dir): """检查 xml 记录的尺寸和真实图片尺寸是否一致""" mismatches = [] for xml_file in os.listdir(ann_dir): if not xml_file.endswith('.xml'): continue tree = ET.parse(os.path.join(ann_dir, xml_file)) root = tree.getroot() size = root.find('size') xml_w = int(size.find('width').text) xml_h = int(size.find('height').text) img_name = os.path.splitext(xml_file)[0] + '.jpg' img_path = os.path.join(img_dir, img_name) if not os.path.exists(img_path): continue real_w, real_h = Image.open(img_path).size if (xml_w, xml_h) != (real_w, real_h): mismatches.append((xml_file, (xml_w, xml_h), (real_w, real_h))) return mismatches result = check_size_mismatch('tomato_leaf_disease/images', 'tomato_leaf_disease/annotations') print(f"尺寸不一致的标注文件数: {len(result)}") for r in result[:5]: print(r)这段代码遍历所有 xml,读取里面记录的宽高,再用 PIL 打开对应图片拿真实宽高,不一致就记下来。参数上没什么可调的,img_dir和ann_dir换成你的实际路径即可。跑完如果发现几十上百个不一致,别慌,转换时统一用真实图片尺寸就行,xml 里的 size 直接忽略。这一步做完,你对数据质量就有了底。
2.3 数据体检:类别分布、框大小与漏标排查
训练之前还有三件事必须做:类别是否均衡、框的尺寸分布、有没有明显漏标。番茄叶病害数据集里健康叶样本往往偏多,病斑样本偏少,直接训练模型会偏向预测健康叶。先统计一下:
import xml.etree.ElementTree as ET import os from collections import Counter def count_classes(ann_dir): counter = Counter() box_sizes = [] for f in os.listdir(ann_dir): if not f.endswith('.xml'): continue root = ET.parse(os.path.join(ann_dir, f)).getroot() for obj in root.findall('object'): name = obj.find('name').text counter[name] += 1 bbox = obj.find('bndbox') w = float(bbox.find('xmax').text) - float(bbox.find('xmin').text) h = float(bbox.find('ymax').text) - float(bbox.find('ymin').text) box_sizes.append((w, h)) return counter, box_sizes counter, sizes = count_classes('tomato_leaf_disease/annotations') print("类别分布:", counter) areas = [w * h for w, h in sizes] print(f"框面积 中位数: {sorted(areas)[len(areas)//2]:.0f} 像素") print(f"最小框面积: {min(areas):.0f} 像素")类别分布直接告诉你要不要做重采样或加权。框面积中位数和最小值告诉你 anchor 或输入分辨率该怎么设——如果大量框面积只有几百像素,输入分辨率设 416 就太小了,病斑会糊成一团,建议 640 起步。漏标排查没有全自动的可靠办法,常见做法是随机抽 20 张图,用脚本把标注框画上去,人眼过一遍:
import cv2, os, xml.etree.ElementTree as ET def draw_boxes(img_path, xml_path, out_path): img = cv2.imread(img_path) root = ET.parse(xml_path).getroot() for obj in root.findall('object'): b = obj.find('bndbox') x1, y1 = int(b.find('xmin').text), int(b.find('ymin').text) x2, y2 = int(b.find('xmax').text), int(b.find('ymax').text) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, obj.find('name').text, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(out_path, img) # 抽查前 20 张 for f in sorted(os.listdir('tomato_leaf_disease/annotations'))[:20]: name = os.path.splitext(f)[0] draw_boxes(f'tomato_leaf_disease/images/{name}.jpg', f'tomato_leaf_disease/annotations/{f}', f'check_{name}.jpg')画框脚本的逻辑就是把 xml 里的坐标读出来,用 OpenCV 画矩形和类别名,输出到新文件。重点看两类问题:病斑明明在图上但没框,或者框把整片叶子都圈进去了。前者是漏标,后者是标注粒度过粗,都会拉低模型精度。这一步花半小时,能省后面几天的调参时间。
3. 转成 YOLO 格式并跑通训练:脚本、配置与参数
3.1 VOC 转 YOLO txt 的完整脚本与四个边界坑
格式转换是绕不过去的。下面这个脚本把 VOC xml 转成 YOLO 需要的 txt,同时生成 train/val 划分文件:
import os, random, xml.etree.ElementTree as ET from PIL import Image CLASSES = ['early_blight', 'late_blight', 'leaf_mold', 'healthy'] # 按你的 classes.txt 实际内容改,顺序决定 class_id def voc_to_yolo(xml_path, img_path, out_txt): tree = ET.parse(xml_path) root = tree.getroot() # 关键:用真实图片尺寸,不用 xml 里的 size img_w, img_h = Image.open(img_path).size lines = [] for obj in root.findall('object'): name = obj.find('name').text if name not in CLASSES: continue cls_id = CLASSES.index(name) b = obj.find('bndbox') x1 = float(b.find('xmin').text) y1 = float(b.find('ymin').text) x2 = float(b.find('xmax').text) y2 = float(b.find('ymax').text) # 边界裁剪,防止坐标越界 x1, y1 = max(0, x1), max(0, y1) x2, y2 = min(img_w, x2), min(img_h, y2) # 转归一化中心点 + 宽高 cx = (x1 + x2) / 2 / img_w cy = (y1 + y2) / 2 / img_h w = (x2 - x1) / img_w h = (y2 - y1) / img_h # 过滤掉宽高为 0 的脏框 if w <= 0 or h <= 0: continue lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") with open(out_txt, 'w') as f: f.write('\n'.join(lines)) return len(lines) def build_dataset(img_dir, ann_dir, out_dir): os.makedirs(f'{out_dir}/images/train', exist_ok=True) os.makedirs(f'{out_dir}/images/val', exist_ok=True) os.makedirs(f'{out_dir}/labels/train', exist_ok=True) os.makedirs(f'{out_dir}/labels/val', exist_ok=True) names = [os.path.splitext(f)[0] for f in os.listdir(ann_dir) if f.endswith('.xml')] random.seed(42) random.shuffle(names) split = int(len(names) * 0.8) for i, name in enumerate(names): subset = 'train' if i < split else 'val' img_src = f'{img_dir}/{name}.jpg' if not os.path.exists(img_src): continue # 复制图片 import shutil shutil.copy(img_src, f'{out_dir}/images/{subset}/{name}.jpg') # 生成标注 voc_to_yolo(f'{ann_dir}/{name}.xml', img_src, f'{out_dir}/labels/{subset}/{name}.txt') build_dataset('tomato_leaf_disease/images', 'tomato_leaf_disease/annotations', 'tomato_yolo')逻辑说明:voc_to_yolo负责单个文件的坐标转换,build_dataset负责整体目录搭建和 8:2 划分。四个边界坑分别是——第一,坐标越界,xml 里 xmax 可能超过图片宽度,必须裁剪;第二,宽高为 0 的脏框,除下来是 0,YOLO 训练时会报 nan,必须过滤;第三,类别名不在 CLASSES 里的目标,直接跳过,否则 class_id 会错位;第四,图片扩展名不一定是 jpg,脚本里写死了.jpg,如果你的数据集有 png,要把这行改成动态匹配。参数上random.seed(42)保证划分可复现,0.8是训练集比例,小数据集可以调到 0.85。
3.2 data.yaml 怎么写:路径、类别数与常见报错
YOLO 训练靠一个 yaml 文件告诉它数据在哪、有几类。在tomato_yolo同级建一个tomato.yaml:
path: /absolute/path/to/tomato_yolo train: images/train val: images/val nc: 4 names: 0: early_blight 1: late_blight 2: leaf_mold 3: healthypath必须是绝对路径,这是最高频的报错来源,写相对路径 YOLO 会找不到。nc是类别数,必须和 names 的条目数一致,多一个少一个都会在训练启动时抛错。train和val是相对path的子路径。names 的键从 0 开始,顺序必须和转换脚本里 CLASSES 的顺序完全一致,否则模型学到的类别会张冠李戴。
注意:如果你的数据集类别名是中文,yaml 里建议用英文或拼音,训练完再在可视化时映射回中文。YOLO 部分版本读取中文 names 会乱码,导致验证时类别显示异常。
3.3 用 YOLOv8 跑第一轮训练:命令行与关键参数
环境配置不展开,假设你已经装好 ultralytics。第一轮训练不要一上来就调参,先用默认配置跑通,确认数据管道没问题:
yolo detect train \ data=tomato.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ project=tomato_runs \ name=baseline逐参数说:model=yolov8n.pt用 nano 版本,参数量小,先验证流程,别一上来就上 x 版本浪费时间。epochs=100对番茄叶病害这种几千张规模的数据集够用,看验证集 mAP 什么时候平了就停。imgsz=640是前面数据体检得出的结论,病斑小,分辨率不能低。batch=16看显存,8G 显存跑 640 的 nano 模型 16 没问题,爆显存就降到 8。project和name决定输出目录,方便你对比多次实验。
训练启动后重点盯三个输出:box_loss是否稳定下降、mAP50是否上升、验证集 loss 有没有反弹。如果 box_loss 一直不降,八成是标注格式有问题,回去检查 txt 里的坐标是不是归一化到了 0~1。如果 mAP50 卡在很低的值不动,检查类别顺序和 yaml 是否对得上。第一轮跑完,你会得到一个best.pt,这就是后续推理和继续训练的基础。
4. 番茄叶病害检测的避坑与排查:那些让我返工的细节
4.1 现象:训练 loss 正常但验证 mAP 极低
原因通常有两个。一是训练集和验证集的类别分布差异太大,比如验证集里几乎全是健康叶,模型在验证集上等于没学到病斑。二是标注框和图片没对齐,转换时用了 xml 里的错误尺寸,导致框整体偏移。解决:先跑一遍 2.3 的类别统计,确认两个子集分布接近;再用 2.3 的画框脚本抽查验证集图片,肉眼确认框位置正确。这两个动作做完,问题基本能定位。
4.2 现象:模型把健康叶也框出病斑
这是典型的假阳性,番茄叶病害数据集里健康叶样本多,模型容易学到「叶子=有病」的捷径。原因往往是负样本(健康叶)在训练中被过度采样,或者病斑框标注过粗,把整片叶子都圈进去了。解决:检查健康叶的标注,如果健康叶也被标了框,确认类别是否正确;在训练时适当降低健康叶的采样权重,或者增加病斑样本的增强。另一个办法是提高置信度阈值,从默认 0.25 提到 0.4 再观察。
4.3 现象:小病斑完全检测不到
早疫病早期的小斑点可能只有十几个像素,640 分辨率下经过多次下采样就没了。原因是模型的下采样倍率太高,小目标特征丢失。解决:把imgsz提到 1024 甚至 1280,代价是显存和训练时间增加;或者改用带 P2 小目标检测层的模型结构。另一个实用技巧是在数据增强里关掉 mosaic 的最后几轮,让模型在真实尺度上收敛。
4.4 现象:训练到一半显存爆了
原因通常是 batch 设太大,或者 dataloader 的 worker 数太多导致内存泄漏。解决:先把 batch 减半,观察是否还爆;如果还爆,把workers从默认 8 降到 4 或 2。番茄叶病害数据集图片分辨率高,640 输入下 batch 16 在 8G 显存上已经接近上限,别硬撑。
4.5 现象:推理时框重叠严重,同一病斑出多个框
这是 NMS(非极大值抑制)阈值的问题。默认iou=0.7对密集病斑偏松。解决:推理时把iou降到 0.5 左右,重叠框会被合并。如果病斑本身就很密集,降太多会漏检,需要在验证集上试几个值找平衡。这个参数没有万能值,跟你的病斑密度直接相关。
5. 从能跑到好用:验证策略、阈值调优与一个提点技巧
第一轮训练跑通只是起点,真正决定这个番茄叶病害检测方案能不能落地的是验证和调优。我一般会固定一个验证集,每次改完参数都在同一批图上对比,避免「这次好像好一点」的玄学判断。验证时不要只看 mAP 一个数,把 precision、recall 和每类的 AP 都拉出来看。番茄叶病害里晚疫病和早疫病视觉上接近,经常互相误判,看混淆矩阵能直接暴露这个问题。
阈值调优有个实用做法:在验证集上跑一遍推理,把置信度从 0.1 到 0.9 按 0.05 步长扫一遍,画 precision-recall 曲线,选 F1 最高的点作为部署阈值。代码不长:
from ultralytics import YOLO import numpy as np model = YOLO('tomato_runs/baseline/weights/best.pt') results = model.val(data='tomato.yaml', conf=0.001, iou=0.6) # results 里含 curves,可直接看 PR 曲线 # 实际部署阈值建议在 F1 最大处取这里conf=0.001是为了让验证时保留所有低置信度预测,方便画完整 PR 曲线,部署时再按 F1 最大点设阈值。iou=0.6是验证时的 NMS 阈值,比推理时略松,保证不漏掉真实框。
最后一个提点技巧,也是我踩坑踩出来的:番茄叶病害数据集的病斑在颜色上和健康组织差异明显,可以在训练前对图像做一次颜色增强,比如随机调整 HSV 的饱和度和亮度,让模型对光照变化更鲁棒。但别过度,饱和度调太狠会让病斑颜色失真,反而掉点。我一般把hsv_h设 0.015、hsv_s设 0.7、hsv_v设 0.4,这是 YOLO 默认增强里比较稳的一组。另外,如果你的数据集里不同来源的图片色温差异大,先做一次白平衡统一,比任何增强都管用。
说到底,番茄叶病害目标检测这件事,数据质量的决定性远大于模型选型。我见过太多人花一周调 YOLOv8 和 YOLOv11 的差异,结果标注里一堆漏标和错框,换什么模型都白搭。先把 2.3 的数据体检做扎实,把 3.1 的转换脚本跑对,再谈调参。希望帮到你。
本文还有配套的精品资源,点击获取