番茄病害目标检测数据集构建实战指南
2026/9/23 12:01:04 网站建设 项目流程

简介:本资源是一套专为农业智能识别场景设计的番茄病害目标检测数据集,面向深度学习初学者、计算机视觉研究者及智慧农业项目开发者,助力快速构建番茄斑萎病毒、早疫病、赤霉病与健康植株四类病害的自动化识别模型。数据集共2083张高质量田间采集图像,已按YOLO与VOC双格式组织,包含1999个txt标签文件(对应YOLO格式边界框坐标)、1个完整类别定义yaml配置文件,以及配套xml标注,训练/验证/测试集划分完备,开箱即用于YOLOv5至YOLOv10、Faster R-CNN、SSD等主流检测框架。压缩包总计2000个文件,大小139.11MB,结构规整、标注一致、类别平衡,显著降低数据预处理门槛。目前已有368人学习下载,适合开展课程设计、科研实验或农业AI落地验证,可直接支撑模型训练、评估与部署全流程。

1. 番茄病害识别数据集(目标检测):不是拿来就能训的“标准图库”,而是要亲手筛、标、验的农田黑匣子

你下载了一个叫“番茄病害识别数据集(目标检测)”的压缩包,解压后看到 train/val/test 三文件夹、images 和 labels 两目录、还有个 README.md ——恭喜,你刚踩进农业视觉落地的第一个坑:它根本不是开箱即用的 YOLO-ready 数据集,而是一份原始田间影像的粗加工半成品。真实场景里,同一张图常含多类病害(早疫病+叶霉病共存)、病斑尺度跨度极大(从像素级斑点到整片萎蔫叶片)、光照不均导致青枯病斑在阴影区几乎不可见,更别说遮挡、重叠、模糊、反光这些“农田玄学”。这个标题指向的不是某个特定公开数据集(如 PlantVillage 的分类版),而是一类面向实际部署的目标检测任务所必需的数据资产构建过程:从田间采集、病害界定、框标注规范、到适配 YOLOv8/v10 或 RT-DETR 训练 pipeline 的全流程。适合正在做智慧农技系统开发、高校农业AI课题、或农企数字化团队中负责模型落地的工程师——你不需要从零写检测头,但必须亲手把“番茄叶子上的病”变成模型能学懂的坐标+类别+置信度。


2. 为什么必须自己构建/清洗?番茄病害检测的三大数据硬伤

2.1 病害定义模糊:同一张图,三个农艺师给出三种标注结果

番茄常见病害(早疫病、晚疫病、叶霉病、灰霉病、青枯病、病毒病)在田间表现高度相似:早疫病与叶霉病都呈褐色轮纹斑;灰霉病在高湿下与叶霉病霉层混淆;病毒病花叶症状易被误标为营养缺乏。公开数据集常依赖单一专家标注,但实际部署时模型需应对跨区域农技员的判别差异。解决方案不是追求“绝对正确”,而是建立可复现的标注SOP

  • 明确病害判定依据(如:早疫病斑边缘有黄色晕圈,叶霉病背面有紫灰色绒毛状霉层);
  • 要求标注员提供病害部位照片(正/背/侧三视角);
  • 对争议样本组织3人交叉标注,仅当2人以上一致才保留。

提示:不要用 PlantVillage 分类数据集直接转目标检测——其图像多为实验室单叶拍摄,无茎秆遮挡、无自然光照变化、无多病共存,迁移到田间视频流时 mAP 直降 40%+。

2.2 框标注失真:小病斑、粘连病斑、不规则病斑的“框不住”困境

目标检测要求 bounding box 紧贴目标,但番茄病斑天然不规则:

  • 小病斑(<16×16 像素):YOLOv8 默认 anchor 尺寸(如 32×32)无法有效回归,易漏检;
  • 粘连病斑(如叶面密集褐斑):人工框常合并为一个大框,但模型需区分独立病灶以支持精准施药;
  • 不规则病斑(沿叶脉蔓延的青枯病):矩形框覆盖大量健康组织,引入强噪声。

实操对策

  • 小病斑:启用 YOLOv8 的mosaic: false+scale: 0.5预处理,放大病斑区域;
  • 粘连病斑:强制拆分为多个最小外接矩形(代码见 2.3);
  • 不规则病斑:接受“框不准但类别准”的妥协,重点保证类别标签一致性,后续用分割模型补位。

2.3 光照与背景干扰:大棚 vs 露地、晴天 vs 阴天的域偏移黑洞

同一病害在不同光照下颜色差异巨大:

  • 大棚内白炽灯下,灰霉病霉层呈灰白色;
  • 露地正午阳光下,同一霉层泛黄绿色;
  • 阴天散射光下,早疫病斑对比度极低。
    更致命的是背景干扰:藤蔓缠绕、土壤反光、水滴镜面反射、相邻植株遮挡——这些在 ImageNet 风格数据集中不存在,却是田间检测失败主因。

数据增强必须针对性设计

  • 使用albumentations替代默认augment.py,添加RandomSunFlare(模拟大棚顶膜反光)、MotionBlur(模拟手持拍摄抖动)、HueSaturationValue(±30 色相偏移覆盖光照变异);
  • 背景替换:用真实大棚/露地背景图(非纯色)合成训练图,比例控制在 30% 样本内,避免过拟合背景纹理。

3. 从 raw 图像到 YOLOv8 可训格式:四步清洗流水线

3.1 图像预筛:剔除无效帧的 bash 脚本

田间采集的视频抽帧常含大量废片:全黑(夜间)、全白(镜头直对太阳)、严重模糊(对焦失败)、纯土/纯茎(无叶)。手动筛选效率极低,用 OpenCV 快速过滤:

#!/bin/bash # filter_invalid.sh for img in *.jpg; do # 计算图像亮度均值和方差 mean=$(ffmpeg -i "$img" -vf "crop=100:100:100:100,avgblur=2" -vstats -f null /dev/null 2>&1 | \ grep "mean:" | awk '{print $3}' | sed 's/,//') var=$(ffmpeg -i "$img" -vf "crop=100:100:100:100,avgblur=2" -vstats -f null /dev/null 2>&1 | \ grep "variance:" | awk '{print $3}' | sed 's/,//') # 亮度均值 <15(太暗)或 >230(过曝)或方差 <5(无纹理) if (( $(echo "$mean < 15 || $mean > 230 || $var < 5" | bc -l) )); then echo "Removing $img (mean=$mean, var=$var)" rm "$img" fi done

逻辑说明

  • crop=100:100:100:100截取中心区域,规避边缘畸变;
  • avgblur=2减少噪点干扰统计;
  • vstats输出亮度统计,bc -l支持浮点比较;
  • 参数可调:大棚环境可放宽mean > 230限制(反光强),露地则收紧var < 5(需更多纹理)。

3.2 病斑拆框:将粘连病斑分解为独立 bounding box 的 Python 脚本

针对标注工具(如 CVAT)导出的合并框,用 OpenCV 找连通域并生成最小外接矩形:

# split_adhesion_boxes.py import cv2 import numpy as np import json from pathlib import Path def split_connected_regions(mask_path: str, original_img: np.ndarray) -> list: """输入二值掩膜,输出多个最小外接矩形 [x,y,w,h]""" mask = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE) # 形态学闭运算连接微小断裂 kernel = np.ones((3,3), np.uint8) mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) # 找连通域 num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(mask, connectivity=8) boxes = [] for i in range(1, num_labels): # 跳过背景(label 0) x, y, w, h, area = stats[i] # 过滤小区域(<50像素)和细长区域(w/h > 10 or h/w > 10) if area < 50 or w/h > 10 or h/w > 10: continue # 转换为 YOLO 格式中心点+宽高(归一化) h_img, w_img = original_img.shape[:2] x_center = (x + w/2) / w_img y_center = (y + h/2) / h_img width_norm = w / w_img height_norm = h / h_img boxes.append([x_center, y_center, width_norm, height_norm]) return boxes # 示例:处理单张图 img_path = "images/tomato_001.jpg" mask_path = "masks/tomato_001.png" # 需提前用标注工具生成病斑掩膜 img = cv2.imread(img_path) boxes = split_connected_regions(mask_path, img) # 写入 YOLO labels 文件 label_path = Path("labels") / "tomato_001.txt" with open(label_path, "w") as f: for box in boxes: # 假设所有病斑类别 ID=0(早疫病) f.write(f"0 {box[0]:.6f} {box[1]:.6f} {box[2]:.6f} {box[3]:.6f}\n")

参数说明

  • area < 50:剔除噪点,可根据相机分辨率调整(1080p 下建议 30~100);
  • w/h > 10:过滤茎秆等细长干扰物;
  • morphologyEx(..., MORPH_CLOSE):修复病斑内部孔洞,避免过分割;
  • 关键教训:此脚本需配合高质量掩膜——若原始标注框粗糙,先用 SAM 模型生成初始掩膜再优化。

3.3 标签映射与平衡:按病害类型重采样,避免“灰霉病统治一切”

实际田间数据中,灰霉病样本常占 60%+,而青枯病不足 5%。直接训练会导致模型对稀有病害完全失效。不推荐简单过采样(引发过拟合),而采用类别感知采样

# balance_dataset.py from collections import Counter import random import shutil # 统计每类病害出现频次 label_files = list(Path("labels").glob("*.txt")) class_counts = Counter() for lf in label_files: with open(lf, "r") as f: for line in f: cls_id = int(line.split()[0]) class_counts[cls_id] += 1 # 设定目标最小频次(取第二少类别的 1.5 倍) min_target = int(sorted(class_counts.values())[1] * 1.5) balanced_files = [] for cls_id, count in class_counts.items(): if count >= min_target: # 足够的类:随机选 min_target 个样本 cls_files = [lf for lf in label_files if cls_id_in_file(lf, cls_id)] balanced_files.extend(random.sample(cls_files, min_target)) else: # 不足的类:全量保留 + 复制增强(加噪声/旋转) cls_files = [lf for lf in label_files if cls_id_in_file(lf, cls_id)] balanced_files.extend(cls_files) # 复制增强(示例:水平翻转) for lf in cls_files[:min_target-count]: new_name = lf.stem + "_flip" + lf.suffix shutil.copy(lf, Path("labels_balanced") / new_name) # 同步复制图像并翻转 img_path = Path("images") / (lf.stem + ".jpg") img_flip = cv2.flip(cv2.imread(str(img_path)), 1) cv2.imwrite(str(Path("images_balanced") / (lf.stem + "_flip.jpg")), img_flip) print(f"Balanced dataset: {len(balanced_files)} files")

逻辑说明

  • cls_id_in_file()是自定义函数,检查 txt 文件是否含指定类别;
  • 复制增强仅用于最稀有类别(如青枯病),且仅限几何变换(翻转/旋转),禁用色彩扰动(避免病征失真);
  • 血泪经验:平衡后务必验证验证集分布——若 val 集某类仍稀缺,需从 test 集匀出部分样本补充 val。

4. 避坑:番茄病害检测数据集构建的 4 个致命翻车点

4.1 现象:训练 loss 降得快,但 val mAP 停在 0.1 不动

原因:标注工具导出的.txt文件含空行或非数字字符(如 CVAT 导出时插入的注释行),YOLOv8 加载时静默跳过该样本,导致训练集实际样本数远少于预期,而验证集正常加载,造成 train/val 分布严重失衡。
解决:在train.py前插入校验脚本:

# validate_labels.sh for f in labels/*.txt; do if [[ $(wc -l < "$f") -eq 0 ]] || [[ $(grep -v "^[0-9]" "$f" | wc -l) -gt 0 ]]; then echo "Invalid label: $f" rm "$f" fi done

4.2 现象:模型在测试图上框出“健康叶片”为病害

原因:标注时未严格区分病害与生理性损伤(如日灼斑、肥害斑),而这些损伤在形态上与早疫病斑高度相似,模型学到的是“褐色斑点=病害”的错误先验。
解决:建立《番茄非病害斑鉴别手册》PDF,包含日灼斑(叶尖/叶缘规则圆形)、肥害斑(沿叶脉对称分布)、机械损伤(边缘锐利无晕圈)的高清图例,要求所有标注员考前通过 90 分测试。

4.3 现象:同一张图,不同分辨率下检测结果差异巨大(1080p 正常,480p 全漏)

原因:YOLOv8 默认输入尺寸 640×640,但田间图常为 3840×2160(4K),直接 resize 导致小病斑像素化消失;而用 letterbox 缩放又使病斑在 padding 区域变形。
解决:改用--imgsz动态适配:

  • 对 4K 图:--imgsz 1280+--rect(矩形推理,不 pad);
  • 对手机拍摄图(1920×1080):--imgsz 960
  • 关键参数--rect必须开启,否则小目标召回率断崖下跌。

4.4 现象:导出 ONNX 模型后,C++ 推理结果与 Python 完全不同

原因:YOLOv8 导出 ONNX 时默认--dynamic开启,但 OpenCV DNN 模块不支持动态轴,导致输入 tensor shape 解析错误。
解决:导出时固定尺寸:

yolo export model=yolov8n.pt format=onnx imgsz=640 dynamic=False

并在 C++ 中严格按1,3,640,640输入,预处理必须与 Python 端完全一致(BGR→RGB→归一化→CHW)。


5. 验证你的数据集是否“真可用”:三阶质检法

5.1 第一阶:可视化质检(肉眼可判的 3 个硬指标)

ultralytics.utils.plotting.plot_images()批量渲染带框图像,重点检查:

检查项合格标准不合格示例
框紧贴性病斑边缘与框边界距离 ≤2 像素(1080p 下)框内含大片健康叶肉,或框外漏病斑
类别一致性同一病害在不同光照/角度下标签 ID 相同晴天标为 class 0(早疫),阴天标为 class 1(叶霉)
遮挡处理被茎秆遮挡 50% 的病斑仍标注,且框覆盖可见部分完全不标遮挡病斑,或框强行拉伸覆盖遮挡物

注意:此阶段发现 10% 以上样本不合格,必须返工重标——别指望模型能“学会”纠错。

5.2 第二阶:分布质检(用代码揪出隐藏偏移)

运行以下脚本,输出各类别在 train/val/test 中的占比及病斑尺寸分布:

# dataset_stats.py import numpy as np from pathlib import Path import matplotlib.pyplot as plt def analyze_distribution(label_dir: str, img_dir: str): sizes = {0:[], 1:[], 2:[]} # 按 class id 存储宽高比 splits = {"train":0, "val":0, "test":0} for split in ["train", "val", "test"]: label_split = Path(label_dir) / split img_split = Path(img_dir) / split # 统计各类别数量 for lf in label_split.glob("*.txt"): with open(lf, "r") as f: for line in f: cls_id = int(line.split()[0]) # 解析归一化宽高 _, _, w, h = map(float, line.split()[1:]) # 转回像素尺寸(需读取对应图像) img_path = img_split / (lf.stem + ".jpg") if img_path.exists(): h_img, w_img = cv2.imread(str(img_path)).shape[:2] sizes[cls_id].append((w*w_img) / (h*h_img)) # 宽高比 splits[split] = len(list(label_split.glob("*.txt"))) # 输出统计 print("Split distribution:", splits) for cls_id, ratios in sizes.items(): if ratios: print(f"Class {cls_id} aspect ratio: mean={np.mean(ratios):.2f}, std={np.std(ratios):.2f}") analyze_distribution("datasets/tomato/labels", "datasets/tomato/images")

解读指南

  • Class 0(早疫)宽高比均值 1.8±0.3,而Class 1(灰霉)为 0.6±0.1,说明两类病斑形态差异显著,模型易区分;
  • train占比 70% 但valClass 2(青枯)样本数为 0,必须从train中匀出至少 20 张补val
  • 后悔药:此脚本应在标注完成 20% 时就运行——早发现问题,早止损。

5.3 第三阶:模型反向质检(用轻量模型跑一次,看它“学到了什么”)

不训练大模型,只用yolov8n.pt在你的数据集上训 10 epoch,观察:

  • results.csvmetrics/mAP50-95(B)是否 ≥0.3?低于此值,数据质量大概率有问题;
  • confusion_matrix.png中对角线是否明显亮于非对角线?若Class 0大量误判为Class 1,说明两类标注边界模糊;
  • PR_curve.pngClass 2(稀有类)曲线是否在 recall=0.1 时 precision 已跌至 0.2?若是,证明该类样本质量差或数量不足。

我的习惯:每次新收一批田间图,必跑这 10 epoch 快检——它比人工抽检 100 张更诚实。曾有一次,快检 mAP 仅 0.12,追查发现是新采购的摄像头自动开启“美颜模式”,平滑了病斑纹理,关掉后 mAP 拉升至 0.41。

希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询