ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

番茄叶病害目标检测数据集实战:从数据准备到YOLO训练与切片推理

番茄叶病害目标检测数据集实战:从数据准备到YOLO训练与切片推理 简介这份番茄叶病害目标检测数据集面向农业AI与计算机视觉方向的研究者、学生及算法工程师用于训练和验证番茄叶片病害的自动识别与定位模型可服务于智慧农业、病害防治等实际场景。资源包共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方便直接接入YOLO等主流检测框架。目前已有129人学习下载。数据集覆盖多种番茄叶病害类型标注信息完整读者可据此快速搭建训练与验证流程省去格式转换与数据划分的繁琐工作适合作为课程设计、科研实验或算法对比的即用型数据基础。1. 番茄叶病害目标检测数据集从拿到压缩包到跑通第一条训练命令你从网上拖下来一个「番茄叶病害目标检测数据集.zip」解压一看里面是 images、labels、data.yaml 三件套或者更糟——只有一堆按类别分文件夹的原始图片连标注都没有。这时候大部分人的第一反应是打开 YOLO 官方文档照着 coco128 的示例改路径然后训练脚本一跑loss 不降mAP 趴在地上不动。问题往往不在模型而在数据集本身的结构、类别映射和标注质量。番茄叶病害检测这个任务本质上是把叶片上的早疫病、晚疫病、叶霉病、斑枯病等病斑框出来并分类属于典型的细粒度小目标检测。它适合谁适合手头有农业图像、想用 YOLO 系列快速验证落地可行性的算法工程师也适合做智慧农业课题、需要一份能复现的检测基线的高校同学。这一章先把「这个数据集到底该怎么用」讲清楚后面几章再拆结构、转格式、调参数、排坑。2. 拆开压缩包先别急着训练番茄叶病害数据集的目录结构与类别体系拿到一个目标检测数据集我一般不会立刻写训练脚本而是先花二十分钟把目录结构和标注格式摸清楚。番茄叶病害数据集常见的组织方式有两种一种是已经切分好 train/val/test 的 YOLO 格式每张图对应一个同名 txt另一种是原始采集状态图片按病害类别放在不同文件夹里标注可能是 VOC XML、LabelMe JSON甚至只有分类标签没有检测框。这两种情况的处理路径完全不同前者可以直接进训练后者必须先做格式转换和框的校验。2.1 判断数据集是检测格式还是分类格式先看有没有 labels 目录以及 labels 里的 txt 是不是每行五个值class_id x_center y_center width height。如果是说明已经是 YOLO 检测格式归一化坐标可以直接用。如果图片文件夹名就是类别名且没有对应的 txt 或 xml那这个「数据集」其实是分类数据集不能直接拿来做目标检测需要先补标注。番茄叶病害这个场景里很多公开数据是从分类数据集演化来的作者只标了整张图的病害类别没有框出病斑位置。这种情况下你有两条路一是用标注工具重新框二是退而求其次做整图分类但那就不是目标检测了。判断命令很简单进到解压目录跑一下# 统计图片数量和标注文件数量快速判断是否成对 find . -name *.jpg -o -name *.png | wc -l find . -name *.txt | wc -l # 看一个标注文件的内容确认是不是 YOLO 五列格式 head -n 5 labels/train/xxx.txt如果图片数和 txt 数差距很大或者 txt 里出现?xml开头说明格式不统一需要先转换。这一步不做后面训练报的错会非常隐蔽比如「找不到标签」但其实是路径大小写问题或者类别全被映射成 0。2.2 类别映射表番茄叶病害的 class_id 必须固定YOLO 训练时data.yaml 里的 names 列表顺序就是 class_id 的映射依据。番茄叶病害常见类别包括细菌性斑点病、早疫病、晚疫病、叶霉病、斑枯病、健康叶。不同数据集给的类别名可能用英文、拉丁学名或拼音顺序也不一样。我一般会先统计所有 txt 里出现过的 class_id再和 data.yaml 对照确保没有越界。import os from collections import Counter label_dir labels/train counter Counter() for f in os.listdir(label_dir): if f.endswith(.txt): with open(os.path.join(label_dir, f)) as fp: for line in fp: if line.strip(): cid int(line.split()[0]) counter[cid] 1 print(sorted(counter.items()))这段代码输出每个 class_id 的框数量。如果出现 data.yaml 里没定义的 id比如 names 只有 6 类但统计出 id7训练时就会报 index out of range。参数上label_dir 换成你的实际路径counter 统计的是框级别数量不是图片数量用来判断类别是否极度不平衡。番茄叶病害里健康叶的框通常远少于病斑框如果健康叶样本太少可以考虑合并或过采样否则模型会把健康叶误检成病斑。2.3 用一张图可视化验证标注是否对齐在正式训练前我习惯抽 5 到 10 张图把框画出来看一眼。很多数据集的血泪经验是标注文件里的坐标是归一化的但有人误用了像素坐标或者 x/y 写反导致框全部跑到图像外面。可视化能一眼看出来。import cv2 import os img_path images/train/xxx.jpg label_path labels/train/xxx.txt img cv2.imread(img_path) h, w img.shape[:2] with open(label_path) as f: for line in f: cid, x, y, bw, bh map(float, line.split()) x1 int((x - bw / 2) * w) y1 int((y - bh / 2) * h) x2 int((x bw / 2) * w) y2 int((y bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(check.jpg, img)逻辑说明YOLO 格式的 x、y 是框中心点归一化坐标bw、bh 是归一化宽高乘以图像宽高后还原成像素坐标。如果画出来的框和病斑位置对不上或者框的大小明显异常说明标注有问题。参数上cv2.rectangle 的线宽 2 只是显示用不影响训练。这一步花几分钟能省掉后面几小时的无效训练。3. 把番茄叶病害数据集转成 YOLO 可训练格式脚本、切分与 data.yaml如果你拿到的数据集不是标准 YOLO 结构这一步就是必经之路。常见输入格式有三种VOC XML、LabelMe JSON、以及按类别分文件夹的分类数据集。番茄叶病害数据里VOC XML 和分类文件夹最多。转换的核心是把各种框坐标统一成归一化的 cx cy w h并且按 8:1:1 或 7:2:1 切分 train/val/test。切分时要注意同一片叶子的多张图不能跨集否则验证集精度会虚高。3.1 VOC XML 转 YOLO txt 的完整脚本VOC 格式里框是 xmin ymin xmax ymax 的绝对像素坐标类别名在 object/name 里。转换时要建立类别名到 id 的映射并且做归一化。import os import xml.etree.ElementTree as ET classes [bacterial_spot, early_blight, late_blight, leaf_mold, septoria_leaf_spot, healthy] class_to_id {c: i for i, c in enumerate(classes)} def convert(xml_path, out_txt, img_w, img_h): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in class_to_id: continue bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) cx (xmin xmax) / 2 / img_w cy (ymin ymax) / 2 / img_h bw (xmax - xmin) / img_w bh (ymax - ymin) / img_h lines.append(f{class_to_id[name]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) with open(out_txt, w) as f: f.write(\n.join(lines))逻辑说明classes 列表顺序必须和 data.yaml 的 names 完全一致否则类别会错位。img_w、img_h 从对应图片读取不能硬编码。如果 XML 里出现 classes 之外的类别名脚本里直接 continue 跳过避免训练时报未知类别。参数上归一化保留 6 位小数足够YOLO 内部会再处理。转换后建议再跑一次 2.2 的统计脚本确认类别分布合理。3.2 按叶片维度切分 train/val避免同叶多图泄漏番茄叶病害数据采集时同一片叶子可能拍了正反面、不同角度多张图。如果随机按图片切分同一片叶子的图可能同时出现在训练集和验证集导致验证 mAP 虚高实际部署翻车。我一般会从文件名或目录里提取叶片 ID按叶片 ID 切分。import os import random from collections import defaultdict pairs [] # (img_path, label_path, leaf_id) for f in os.listdir(images/all): if f.endswith(.jpg): leaf_id f.split(_)[0] # 假设文件名格式为 leafID_xxx.jpg pairs.append((f, f.replace(.jpg, .txt), leaf_id)) leaf_groups defaultdict(list) for p in pairs: leaf_groups[p[2]].append(p) leaf_ids list(leaf_groups.keys()) random.seed(42) random.shuffle(leaf_ids) n len(leaf_ids) train_ids leaf_ids[:int(n * 0.8)] val_ids leaf_ids[int(n * 0.8):] def write_list(ids, split): with open(f{split}.txt, w) as f: for lid in ids: for img, lbl, _ in leaf_groups[lid]: f.write(f./images/all/{img}\n) write_list(train_ids, train) write_list(val_ids, val)逻辑说明leaf_id 的提取规则要根据实际文件名调整常见的是下划线或短横线前一段。random.seed 固定后切分可复现。参数上8:2 是常见比例数据量少时可以 7:3验证集至少保证每类有几十个框。切分完再统计一次 train 和 val 的类别分布如果某个病害在 val 里一个框都没有需要重新调 seed 或手动补。3.3 data.yaml 的五个必填字段与路径坑YOLO 训练读的是 data.yaml里面 path、train、val、nc、names 五个字段。番茄叶病害数据集常见坑是 path 用了相对路径但训练时工作目录不对或者 train 指向的是文件夹而不是 txt 列表。path: /data/tomato_leaf train: train.txt val: val.txt nc: 6 names: [bacterial_spot, early_blight, late_blight, leaf_mold, septoria_leaf_spot, healthy]path 是数据集根目录train 和 val 可以是 txt 列表也可以是图片文件夹。如果 train 写成images/trainYOLO 会自动去找同级的 labels/train要求文件名一一对应。nc 必须等于 names 长度多一个少一个都会报错。names 里的顺序就是 class_id不能和转换脚本里的 classes 顺序不一致。改完 yaml 后用python -c import yaml; print(yaml.safe_load(open(data.yaml)))确认能正常解析避免缩进或中文冒号问题。4. 番茄叶病害检测训练参数怎么设从 yolov8 到小目标病斑的调参清单数据集准备好之后训练本身反而简单难的是参数怎么设才能让病斑这种小目标收敛。番茄叶病害的框通常只占整图面积的百分之几属于小目标检测范畴。默认的 YOLO 参数在 coco 上表现好但直接搬到病斑上召回率会明显偏低。这一章给出一套我实际用过的参数组合并解释每个参数为什么这么设。4.1 输入分辨率与 batch size 的权衡病斑小输入分辨率就不能太低。YOLO 默认 imgsz640对于叶片占满整图、病斑只有几十像素的情况640 下病斑可能只剩几个像素特征提取困难。我一般会把 imgsz 提到 1024 或 1280但显存占用会平方级增长。batch size 相应降到 8 或 4。yolo detect train \ datadata.yaml \ modelyolov8n.pt \ imgsz1024 \ batch8 \ epochs150 \ lr00.01 \ lrf0.01 \ patience30 \ device0参数说明imgsz1024 是精度和显存的折中如果显存不够先降 batch 再降 imgsz。lr0 是初始学习率0.01 适合从预训练权重微调如果从头训练可以降到 0.001。lrf 是最终学习率比例0.01 表示衰减到初始的百分之一。patience30 表示 30 轮 mAP 不升就早停避免过拟合。device0 指定第一块 GPU多卡用 0,1。4.2 针对小目标的 anchor 与数据增强设置YOLOv8 本身是 anchor-free 的不需要手动设 anchor但数据增强对小目标影响很大。默认的 mosaic 增强会把四张图拼成一张病斑变得更小有时候反而有害。我一般会降低 mosaic 概率同时开启 copy_paste 或 mixup 来增加正样本。yolo detect train \ datadata.yaml \ modelyolov8s.pt \ imgsz1024 \ batch8 \ epochs200 \ mosaic0.5 \ mixup0.1 \ copy_paste0.1 \ scale0.3 \ fliplr0.5 \ hsv_h0.015 \ hsv_s0.7 \ hsv_v0.4参数说明mosaic0.5 表示一半概率做马赛克增强比默认 1.0 低减少小目标进一步缩小。mixup 和 copy_paste 各 0.1增加样本多样性。scale0.3 允许随机缩放模拟不同拍摄距离。hsv 三个参数是颜色抖动番茄叶片在不同光照下颜色差异大适当增强能提升泛化。如果验证集 mAP 波动大先把 mosaic 降到 0.3 再试。4.3 训练过程看什么指标mAP50 与 mAP50-95 的差距训练日志里重点看两个指标mAP50 和 mAP50-95。mAP50 是 IoU 阈值 0.5 时的平均精度mAP50-95 是 0.5 到 0.95 每隔 0.05 取一次的平均。番茄叶病害检测里如果 mAP50 高但 mAP50-95 低说明框的位置不够准病斑边界模糊导致定位误差大。这时候可以检查标注框是否过松或过紧或者提高 imgsz。# 训练结束后在验证集上单独评估 yolo detect val \ modelruns/detect/train/weights/best.pt \ datadata.yaml \ imgsz1024 \ batch8val 命令会输出每类的 P、R、mAP50、mAP50-95。如果某一类特别低比如 septoria_leaf_spot先看这一类在训练集里的框数量是不是太少再考虑单独过采样或调整类别权重。YOLO 本身不直接支持类别权重但可以通过复制含少类别的图片来间接实现。5. 番茄叶病害数据集训练避坑五条血泪排查记录这一章记录我在番茄叶病害检测项目里实际踩过的五个坑每个都按现象、原因、解决来写。这些坑在别的目标检测数据集上也通用但番茄叶病害因为病斑小、类别细、采集环境杂触发概率更高。5.1 现象训练 loss 正常下降但 mAP 始终为 0原因data.yaml 里的 names 顺序和标注文件里的 class_id 不一致导致模型学到的类别和验证时对不上。更隐蔽的情况是标注文件里的 class_id 从 1 开始而 YOLO 要求从 0 开始所有框都被当成背景或错类。解决跑一遍 2.2 的统计脚本确认 class_id 最小值是 0最大值小于 nc。如果从 1 开始批量把所有 txt 的第一列减 1。改完重新训练mAP 立刻正常。5.2 现象验证集 mAP 很高但拿新拍的叶片图测试全漏检原因训练集和验证集来自同一批采集光照、背景、叶片品种高度相似模型过拟合到采集环境。番茄叶病害公开数据集很多是在实验室白背景下拍的田间图一测就崩。解决在数据增强里加大 hsv 和 scale 范围或者手动混入一些田间背景图。如果条件允许留出一部分不同时间、不同地点采集的图做测试集不要混进训练。验证集 mAP 只能作为参考真实场景测试才是准绳。5.3 现象训练到一半报 CUDA out of memory原因imgsz 提到 1024 后batch8 在某些显卡上刚好卡在显存边缘训练过程中缓存碎片累积导致 OOM。或者 dataloader 的 workers 太多每个 worker 都占显存。解决先把 batch 降到 4如果还报就降 imgsz 到 768。workers 设成 4 或 8不要设成 CPU 核心数。另外可以在训练命令里加ampTrue开启混合精度显存占用能降三成左右。5.4 现象某些图片的框画出来是反的或者跑到图像外原因标注时 x 和 y 写反或者用了像素坐标没归一化。VOC 转 YOLO 时如果 img_w 和 img_h 取错也会导致框比例异常。解决用 2.3 的可视化脚本抽查发现反框就回到原始标注修正。如果是转换脚本的 bug检查 img_w 和 img_h 是不是从对应图片读取的不要用固定值。批量修正可以用脚本把所有框的 cx 和 cy 互换但前提是确认确实是写反而不是别的问题。5.5 现象训练日志里 cls_loss 一直很高降不下去原因类别不平衡严重健康叶的框远多于病斑框模型倾向于把所有框预测成健康叶。或者某些类别的标注框质量差边界模糊模型学不到稳定特征。解决先统计每类框数量如果差距超过 10 倍对少类别做过采样。把少类别的图片复制多份文件名加后缀避免覆盖。另外检查少类别的标注框是不是普遍偏大或偏小必要时重新标注一批高质量框。cls_loss 高不一定是模型问题很多时候是数据问题。6. 番茄叶病害检测的进阶技巧用切片推理把漏检的小病斑捞回来训练完模型在验证集上 mAP50 可能到 0.85 以上但实际推理时一张大图里的小病斑还是漏。这是因为 YOLO 在推理时把整图缩放到 imgsz小病斑再次被缩小。解决思路是切片推理把大图切成有重叠的小块每块单独推理再把框映射回原图做 NMS。这个技巧在遥感图像目标检测里很常见搬到番茄叶病害上同样有效。6.1 切片推理的实现与重叠参数核心是把原图按固定步长切成 patch每个 patch 缩放到 imgsz 推理最后把框坐标加回偏移量。重叠是为了避免病斑正好落在切片边界被切掉。import cv2 import numpy as np from ultralytics import YOLO model YOLO(best.pt) img cv2.imread(test.jpg) h, w img.shape[:2] patch 1024 stride 768 # 重叠 256 像素 boxes [] for y in range(0, h, stride): for x in range(0, w, stride): x2 min(x patch, w) y2 min(y patch, h) crop img[y:y2, x:x2] results model(crop, imgsz1024, verboseFalse) for r in results: for box in r.boxes: bx1, by1, bx2, by2 box.xyxy[0].tolist() boxes.append([bx1 x, by1 y, bx2 x, by2 y, box.conf.item(), box.cls.item()]) # 对 boxes 做 NMS def nms(boxes, iou_thres0.5): boxes sorted(boxes, keylambda b: b[4], reverseTrue) keep [] while boxes: best boxes.pop(0) keep.append(best) boxes [b for b in boxes if iou(best, b) iou_thres] return keep def iou(a, b): x1 max(a[0], b[0]); y1 max(a[1], b[1]) x2 min(a[2], b[2]); y2 min(a[3], b[3]) inter max(0, x2 - x1) * max(0, y2 - y1) area_a (a[2] - a[0]) * (a[3] - a[1]) area_b (b[2] - b[0]) * (b[3] - b[1]) return inter / (area_a area_b - inter 1e-6) final nms(boxes)逻辑说明stride 小于 patch 产生重叠重叠区域越大边界病斑越不容易漏但推理次数增加。patch 和 imgsz 保持一致避免二次缩放。NMS 的 iou_thres 设 0.5 到 0.6太低会误删相邻病斑太高会保留重复框。参数上如果病斑特别小patch 可以降到 640stride 设 480但推理时间会成倍增加。6.2 切片推理的代价与适用边界切片推理的代价是推理时间。一张 4000x3000 的图patch1024、stride768大约要跑 20 到 30 个切片单张推理时间从几十毫秒涨到一秒以上。所以它适合离线检测或对实时性要求不高的场景比如手机拍照后上传分析。如果要做实时视频流还是得靠提高 imgsz 和模型量化来平衡。另一个边界是切片推理对已经训练好的模型有效但如果模型本身没学好小目标切片也救不回来。所以顺序是先保证训练集里小病斑的标注质量再用切片推理做后处理。我一般会在验证集上对比整图推理和切片推理的召回率如果切片后召回提升不到 5 个百分点说明模型本身对小目标已经够好没必要增加计算量。6.3 一个验证切片是否有效的习惯每次改完推理逻辑我会固定抽 20 张验证集图片分别跑整图推理和切片推理统计每类的召回数量。如果切片后某类召回明显上升说明该类病斑确实小如果反而下降可能是 NMS 把正确框删了需要调 iou_thres。这个对比表不用写进代码手动记一下就行但能帮你判断切片到底值不值得上。类别整图召回切片召回变化early_blight42519late_blight38479septoria_leaf_spot253611healthy60600这张表是我在一个番茄叶病害项目里的真实记录切片推理对病斑类召回提升明显对健康叶没影响。看到这个差距我才决定在部署时默认走切片。但如果你的数据里病斑本身就大整图召回已经 90 以上切片就是白费算力。做番茄叶病害检测这几年我最大的习惯是拿到任何数据集先画框、再统计、后训练三步缺一不可。很多翻车不是模型不行是数据没摸透。希望帮到你。本文还有配套的精品资源点击获取
返回列表