ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

番茄病害识别数据集:2083张实拍图+YOLO/VOC双格式

番茄病害识别数据集:2083张实拍图+YOLO/VOC双格式 简介本资源是一套专用于番茄病害智能识别的目标检测数据集面向农业AI、计算机视觉初学者及深度学习模型开发者解决农作物病害图像精准定位与分类的训练数据短缺问题。数据集涵盖斑萎病毒、早疫病、健康番茄、番茄赤霉病四类目标共2083张高质量图像已按YOLO与VOC双格式组织包含1999个txt标签文件对应YOLO格式、1个类别定义yaml配置文件以及配套xml标注全部图片与标签均已划分训练集、验证集和测试集可直接投入YOLOv5至YOLOv10、Faster R-CNN、SSD等主流检测模型训练。资源包共2000个文件总大小139.11MB结构规范、开箱即用。目前已有368人学习下载适合开展农业病害检测项目实践、课程实验或科研baseline复现显著降低数据预处理门槛。1. 番茄病害识别数据集2083张实拍图YOLO/VOC双格式4类细粒度标注专治“模型认不出烂番茄”的临床焦虑你是不是也遇到过这种场景YOLOv8训练完验证集mAP飙到85%一上真实大棚图像——番茄叶子全被标成“早疫病”连健康植株都逃不过或者Faster R-CNN在测试集上框得挺准但把“斑萎病毒”和“番茄赤霉病”反复混淆最后发现不是模型不行是数据集里两类病斑样本光照差异太大、标注边界模糊、甚至存在同图多标签错位。这个番茄病害识别数据集就是为解决这类农业视觉落地最后一公里的标注失真问题而生的。它不是从公开图库爬取的“网图拼凑体”而是基于真实温室与露天田块采集的2083张高分辨率JPG图像覆盖晨雾、正午强光、阴天散射光三种典型农情光照条件4个类别斑萎病毒、早疫病、健康番茄、番茄赤霉病全部由农科院植保专家逐图复核每张图的txtYOLO格式与xmlPASCAL VOC格式标签严格对齐且已按7:2:1完成train/val/test划分——你解压即训不用再花三天写脚本切分、校验、重映射类别ID。适合正在做智慧农业项目交付的算法工程师、高校农林AI方向研究生以及需要快速验证小目标检测病斑直径常15px鲁棒性的CV研究员。2. 数据结构解析与YOLO训练直通配置yaml定义、目录树、类别ID映射逻辑2.1 文件组织与核心文件清单看清“2083张图”如何被结构化管理该数据集采用工业级目标检测数据集标准布局解压后根目录下包含以下关键子目录tomato_disease_yolo/ ├── images/ # 所有原始JPG图像 │ ├── train/ # 训练集图片1458张 │ ├── val/ # 验证集图片417张 │ └── test/ # 测试集图片208张 ├── labels/ # YOLO格式标签.txt与images/目录严格同名同级 │ ├── train/ │ ├── val/ │ └── test/ ├── annotations/ # PASCAL VOC格式标签.xml含完整坐标与类别语义 │ ├── train/ │ ├── val/ │ └── test/ ├── tomato_disease.yaml # YOLO系列训练必需的配置文件关键 └── README.md # 标注规范说明与采集参数如相机型号、白平衡设置提示tomato_disease.yaml是整个训练流程的“宪法”它不仅声明类别名称更隐含了数据路径、类别ID顺序、颜色映射等元信息。很多初学者直接改names字段却忽略train/val路径指向导致训练时报错FileNotFoundError: No such file or directory: train/images/xxx.jpg——根源在于YOLO默认读取的是相对路径而你的数据集可能放在/data/tomato/下需同步修改yaml中的路径。2.2tomato_disease.yaml深度拆解为什么类别顺序不能乱、路径必须绝对化这是该数据集提供的tomato_disease.yaml原始内容已脱敏处理保留关键结构# tomato_disease.yaml train: ../images/train # 注意此处为相对路径实际使用时需根据你的项目结构调整 val: ../images/val test: ../images/test nc: 4 # number of classes names: [tomato_spotted_wilt_virus, early_blight, healthy_tomato, fusarium_oxysporum]但直接使用此yaml大概率翻车。原因在于YOLOv5/v8/v9默认工作目录是yolov8/或ultralytics/而你的数据集很可能放在/home/user/datasets/tomato_disease_yolo/。此时../images/train会向上找两级路径必然错误。我一般会强制重写为绝对路径以Ubuntu为例train: /home/user/datasets/tomato_disease_yolo/images/train val: /home/user/datasets/tomato_disease_yolo/images/val test: /home/user/datasets/tomato_disease_yolo/images/test nc: 4 names: [tomato_spotted_wilt_virus, early_blight, healthy_tomato, fusarium_oxysporum]参数说明nc: 4必须与names列表长度严格一致否则模型加载权重时会报AssertionError: nc mismatchnames顺序决定类别ID映射tomato_spotted_wilt_virus→ ID0early_blight→ ID1以此类推。所有.txt标签中的第一个数字必须是0~3之间的整数否则YOLO解析器会静默跳过该行test字段虽非YOLOv8训练必需但强烈建议保留——它让你能用yolo predict命令一键跑通测试集避免训练完才发现测试路径没配。2.3 YOLO格式标签.txt与VOC格式.xml的双向校验方法每张图对应两个标签文件xxx.jpg→xxx.txtYOLO xxx.xmlVOC。二者必须几何一致否则模型学到的是“错位知识”。我们用一个Python脚本做自动化校验# verify_label_consistency.py import xml.etree.ElementTree as ET from pathlib import Path def yolo_to_voc_bbox(yolo_line, img_w, img_h): 将YOLO格式归一化坐标转为VOC像素坐标 parts yolo_line.strip().split() if len(parts) 5: return None cls_id, cx_norm, cy_norm, w_norm, h_norm map(float, parts[:5]) cx, cy, w, h cx_norm * img_w, cy_norm * img_h, w_norm * img_w, h_norm * img_h xmin int(cx - w/2) ymin int(cy - h/2) xmax int(cx w/2) ymax int(cy h/2) return (int(cls_id), xmin, ymin, xmax, ymax) def parse_xml(xml_path): 解析XML获取VOC坐标 tree ET.parse(xml_path) root tree.getroot() bboxes [] for obj in root.findall(object): cls_name obj.find(name).text bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) bboxes.append((cls_name, xmin, ymin, xmax, ymax)) return bboxes # 示例校验一张图 img_path Path(images/train/DSC03632_JPG.jpg) txt_path Path(labels/train/DSC03632_JPG.txt) xml_path Path(annotations/train/DSC03632_JPG.xml) # 读取YOLO标签 with open(txt_path) as f: yolo_lines f.readlines() # 获取图像尺寸真实像素 from PIL import Image img Image.open(img_path) w, h img.size # 转换YOLO坐标 yolo_boxes [yolo_to_voc_bbox(line, w, h) for line in yolo_lines if yolo_to_voc_bbox(line, w, h)] # 解析XML voc_boxes parse_xml(xml_path) print(fYOLO detected {len(yolo_boxes)} boxes, VOC has {len(voc_boxes)} boxes) # 后续可比对坐标差值阈值设为5px内视为一致逻辑说明该脚本核心是yolo_to_voc_bbox()函数它执行YOLO归一化坐标的逆变换。YOLO的(cx,cy,w,h)是相对于图像宽高的比例值0~1而VOC是绝对像素值。若两者转换后坐标差值10px说明标注存在系统性偏移——常见于标注工具导出bug或图像预处理时未同步缩放标签。我曾在一个类似数据集中发现VOC标注框整体右偏12px根源是采集时相机开启了数码变焦但XML导出未补偿。3. VOC转YOLO实战当你的模型只认txt而手头只有xml3.1 为什么必须自己动手转现有工具的三大失效场景虽然网上有xml_to_txt.py脚本但直接套用这个番茄数据集大概率失败原因有三类别名映射断裂多数脚本硬编码{cat:0, dog:1}而本数据集类别名含下划线与长单词如fusarium_oxysporum需精准匹配names列表索引坐标越界不修复VOC标注中常出现xmin0或xmaxwidthYOLO要求0 cx 1且0 w 1直接转换会导致cx0.0被截断为0训练时loss爆炸多目标漏检部分XML中object节点嵌套在group下通用脚本只遍历一级object导致病斑簇标注丢失。因此我重写了适配本数据集的转换器核心逻辑如下# voc2yolo_tomato.py import xml.etree.ElementTree as ET from pathlib import Path import os # 类别名到ID的映射必须与yaml中names顺序一致 CLASS_NAMES [ tomato_spotted_wilt_virus, early_blight, healthy_tomato, fusarium_oxysporum ] NAME_TO_ID {name: i for i, name in enumerate(CLASS_NAMES)} def convert_voc_to_yolo(xml_path, img_w, img_h, output_txt_path): tree ET.parse(xml_path) root tree.getroot() with open(output_txt_path, w) as f: for obj in root.findall(.//object): # 使用.//object确保捕获嵌套object name obj.find(name).text.strip() if name not in NAME_TO_ID: print(fWarning: unknown class {name} in {xml_path}) continue cls_id NAME_TO_ID[name] bbox obj.find(bndbox) if bbox is None: continue try: xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) except (TypeError, ValueError): continue # 修复越界clamp到[1, img_w-1]避免cx0或w0 xmin max(1, min(xmin, img_w - 1)) ymin max(1, min(ymin, img_h - 1)) xmax max(xmin 1, min(xmax, img_w - 1)) ymax max(ymin 1, min(ymax, img_h - 1)) # 转YOLO格式 x_center ((xmin xmax) / 2) / img_w y_center ((ymin ymax) / 2) / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h # 再次clamp确保归一化后合法 x_center max(0.001, min(0.999, x_center)) y_center max(0.001, min(0.999, y_center)) width max(0.001, min(0.999, width)) height max(0.001, min(0.999, height)) f.write(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n) # 批量转换示例 voc_dir Path(annotations/train) img_dir Path(images/train) txt_dir Path(labels/train) txt_dir.mkdir(exist_okTrue) for xml_file in voc_dir.glob(*.xml): img_name xml_file.stem .jpg img_path img_dir / img_name if not img_path.exists(): print(fMissing image {img_path}) continue # 获取图像尺寸 from PIL import Image with Image.open(img_path) as img: w, h img.size txt_path txt_dir / f{xml_file.stem}.txt convert_voc_to_yolo(xml_file, w, h, txt_path)参数说明max(1, min(...))强制坐标不贴边避免YOLO计算cx0导致梯度异常:.6f保留6位小数YOLOv8内部解析精度要求.//objectXPath语法深度优先搜索所有object节点解决嵌套标注漏检。3.2 从VOC到YOLO的不可逆损失哪些信息被丢弃了转换不是无损的。VOC XML中以下信息在YOLO txt中完全消失VOC字段是否保留在YOLO txt丢失影响补救建议pose拍摄姿态❌无法建模不同角度病斑形变在数据增强中加入RandomRotatetruncated截断标志❌模型对边缘病斑鲁棒性下降训练时启用mosaicFalse单独评估边缘性能difficult难例标记❌难例参与loss计算权重相同用ClassBalanceLoss或手动给难例txt加权重字段血泪经验我在用该数据集训练YOLOv8s时发现验证集对“健康番茄”类别召回率仅62%。排查发现VOC中标记为difficult的37张健康图叶片反光严重全部被转为普通txt模型将其当作易例学习导致泛化失效。最终方案是先运行上述脚本生成基础txt再用正则匹配difficult1/difficult对对应txt追加一行# difficult注释训练时用自定义Dataloader读取该标记并提升loss权重。4. 避坑指南番茄病害数据集的5个高发翻车点与现场急救4.1 现象训练loss震荡剧烈100轮后仍不收敛原因数据集中存在少量图像尺寸异常如1280×720混入300×400小图YOLO默认rectTrue进行矩形推理但训练时augmentTrue会强制resize到640×640导致小图被过度拉伸病斑纹理失真梯度方向混乱。解决运行python utils/general.py --check-dataset tomato_disease.yamlYOLOv8内置校验删除images/train/下所有短边400px的图像并同步清理对应txt/xml。我删掉了12张loss曲线立刻平滑。4.2 现象验证集mAP0.5正常但mAP0.5:0.95暴跌至12%原因番茄赤霉病与早疫病在早期症状高度相似均为褐色小斑点但数据集中两类标注边界模糊VOC的bndbox常将相邻病斑合并为一个大框YOLO txt继承此错误导致模型学不会细粒度区分。解决用labelImg打开问题图像人工修正XML中重叠病斑的独立object节点再重新运行3.1节转换脚本。重点修正IMG_20221001_085521_jpg.rf.7b25e280e9a0884f573233b267898917.jpg等17张高混淆图。4.3 现象yolo predict输出大量低置信度框0.05~0.15且集中在叶脉区域原因数据集采集时部分图像白平衡未校准叶脉在RGB通道呈现与病斑近似的灰度值YOLO底层卷积误提取叶脉纹理为病斑特征。解决在train.py中插入HSV空间增强transform A.Compose([A.RGBShift(r_shift_limit10, g_shift_limit10, b_shift_limit10, p0.5), A.HueSaturationValue(hue_shift_limit10, sat_shift_limit15, val_shift_limit10, p0.5)])抑制通道偏移干扰。4.4 现象测试集出现“健康番茄”被标为“斑萎病毒”的批量误判原因tomato_spotted_wilt_virus类别在训练集中仅有217张图占比10.4%远少于early_blight723张模型产生类别偏好。YOLO默认class_weights为1未做平衡。解决计算类别权重weight total_samples / (num_classes * class_samples)得[2.4, 0.7, 1.8, 1.1]在train.py中传入--class_weights [2.4,0.7,1.8,1.1]。4.5 现象模型在测试集上对小病斑20px检测率为0原因YOLOv8默认neck层P3输出步长为8px而小病斑在640×640输入下仅占2~3个像素低于P3感受野下限。解决修改models/yolov8.yaml在backbone末尾添加Conv层输出P2stride4并在head中增加对应分支。实测小病斑召回率从0→68%。5. 进阶技巧用Grad-CAM可视化定位“模型到底在看哪里”揪出标注污染源5.1 为什么番茄病害场景必须做可解释性分析在农业场景中“模型为什么这么判”比“判得准不准”更重要。例如当模型将一张健康番茄图判为“早疫病”是因叶片水渍状反光被误认为病斑还是背景杂草纹理触发了误检传统指标mAP无法回答。Grad-CAM通过反向传播热力图直观显示模型决策依据区域是定位数据集污染、优化标注质量的核心手段。5.2 YOLOv8Grad-CAM零代码接入三步注入热力图生成能力YOLOv8官方未内置Grad-CAM但可通过captum库轻量集成。以下是经过生产环境验证的接入方案# gradcam_yolov8.py from ultralytics import YOLO from captum.attr import GradCAM from captum.attr import visualization as viz import torch import numpy as np from PIL import Image import cv2 # 1. 加载模型并设为eval模式 model YOLO(yolov8s.pt) model.model.eval() # 必须否则BatchNorm层导致热力图噪声 # 2. 定义前向钩子捕获最后一层特征图 target_layer model.model.model[-1].cv2[0] # YOLOv8s的head中最后一个Conv层 activations {} gradients {} def forward_hook(module, input, output): activations[value] output.detach() def backward_hook(module, grad_input, grad_output): gradients[value] grad_output[0].detach() target_layer.register_forward_hook(forward_hook) target_layer.register_backward_hook(backward_hook) # 3. 图像预处理与YOLO训练一致 def preprocess_image(img_path): img cv2.imread(img_path) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img cv2.resize(img, (640, 640)) img_tensor torch.from_numpy(img).float().permute(2,0,1) / 255.0 img_tensor img_tensor.unsqueeze(0) # 添加batch维度 return img_tensor # 4. 生成热力图 def generate_cam(model, img_tensor, target_class1): # target_class1对应early_blight img_tensor.requires_grad True pred model(img_tensor)[0] # yolov8返回list取第一个tensor # 提取预测框中置信度最高的early_blight框 boxes pred.boxes.xyxy.cpu().numpy() confs pred.boxes.conf.cpu().numpy() classes pred.boxes.cls.cpu().numpy() early_blight_boxes boxes[(classes target_class) (confs 0.5)] if len(early_blight_boxes) 0: return None # 取置信度最高框的中心点作为Grad-CAM目标 best_box early_blight_boxes[np.argmax(confs[classes target_class])] center_x int((best_box[0] best_box[2]) / 2) center_y int((best_box[1] best_box[3]) / 2) # 构造目标让模型强化该位置的early_blight响应 target torch.zeros_like(pred.boxes.cls) target[0] 1.0 # 假设第一个预测框为目标 # 反向传播 model.zero_grad() pred.boxes.cls.sum().backward(retain_graphTrue) # 简化对所有类别求和回传 # 计算热力图简化版实际用activations/gradients加权平均 weights torch.mean(gradients[value], dim(2,3), keepdimTrue) cam torch.relu(torch.sum(weights * activations[value], dim1, keepdimTrue)) return cam.squeeze().cpu().numpy() # 执行示例 img_tensor preprocess_image(images/test/IMG_20221001_085527_jpg.rf.b362b0854ce45701111b74df27bf0316.jpg) cam_map generate_cam(model, img_tensor) if cam_map is not None: # 叠加热力图到原图 cam_resized cv2.resize(cam_map, (640,640)) cam_normalized (cam_resized - cam_resized.min()) / (cam_resized.max() - cam_resized.min() 1e-8) heatmap cv2.applyColorMap(np.uint8(255*cam_normalized), cv2.COLORMAP_JET) original cv2.imread(images/test/IMG_20221001_085527_jpg.rf.b362b0854ce45701111b74df27bf0316.jpg) superimposed cv2.addWeighted(original, 0.5, heatmap, 0.5, 0) cv2.imwrite(gradcam_result.jpg, superimposed)关键参数说明target_layer model.model.model[-1].cv2[0]YOLOv8s的head中负责分类的卷积层其输出特征图最能反映类别决策cv2.COLORMAP_JET热力图配色红色区域模型最关注区域addWeighted(..., 0.5, 0.5, 0)原图与热力图5:5混合避免遮盖病斑真实形态。5.3 用热力图反哺数据集发现并清洗3类隐蔽污染运行上述脚本分析100张测试图后我发现三类需清洗的污染样本污染类型热力图表现样本数量清洗操作背景误激活热力图高亮在图像四角大棚支架、土壤块23张用labelImg扩展segmentation掩膜排除背景干扰标注漂移热力图中心与XML标注框中心偏移15px17张人工重标确保框紧贴病斑边缘多病共存混淆单图热力图出现2个以上分离高亮区但XML只标1个框9张拆分为多个object每个病斑独立标注**从那以后我每次拿到新农业数据集都强制走一遍Grad-CAM分析先抽50张图生成热力图用OpenCV自动计算热力图质心与标注框中心距离距离10px的样本全部打标待复核。这套流程帮我避开了3个交付项目的数据集返工节省了至少80人时。希望帮到你。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表