ARTICLE DETAIL

资讯详情

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

番茄叶片病害识别数据集构建:从标注到YOLOv8训练实战

番茄叶片病害识别数据集构建:从标注到YOLOv8训练实战 简介这份番茄叶片病害识别数据集专为目标检测与农业智能化场景设计包含枯萎病、花叶病毒、红蜘蛛感染三种常见病害共计6446张图片的标注信息可直接用于YOLO系列、Faster Rcnn、SSD等主流检测模型的训练。压缩包共2000个文件核心为1999个txt格式标注文件并附1个指定类别信息的yaml配置文件整体压缩包大小约为247.69MB压缩包为zip格式。数据已按训练集、验证集、测试集划分配置好支持YOLO与VOC两种常见标注格式可无缝对接YOLOv5至YOLOv10等算法训练。目前已有393人学习下载适合需要快速开展农业视觉实验的研究者和开发者。该资源能显著减少数据整理与格式转换成本让大家将精力集中于模型调参与病害识别效果对比是入门和进阶目标检测任务的实用数据集。1. 番茄叶片病害识别数据集到底是什么目标检测任务里最容易被低估的一环很多刚接触目标检测的工程师习惯先拿COCO或者VOC练手等真到了番茄叶片病害识别这个场景第一反应往往也是先套一个YOLO模型结果训练完拉到温室里一测漏检、误检满天飞。问题大多不在网络结构而在数据集本身同一片番茄叶上早疫病和晚疫病的病斑颜色、边缘形态非常接近不同光照下差别更大标注错一个框模型后面就会一起翻车。所以番茄叶片病害识别数据集的核心价值是用边界框告诉模型“病斑在哪里、属于哪一类”它决定了模型能不能区分出健康叶、早疫病和晚疫病也决定了项目从实验到落地要花多少精力。适合谁做凡是手上只有一堆番茄叶片照片、想自己标一份数据然后用目标检测训练一个病害识别模型的从业者都能直接从数据集构建这条线入手。数据到位模型训练只是下一步的执行问题。2. 番茄叶片病害识别数据集的结构与制作从田间图片到YOLO能读懂的txt2.1 目标检测数据集里必须有的三类文件图片、标注与类别映射先明确一件事目标检测数据集不是“一堆图片”而是一套图片加上对应的标注文件再加上一份类别映射。以常见做法为例有人用COCO2017数据集结构那样的大JSON做标注也有人用Pascal VOC的XML文件保存每个病斑的框。但对YOLOv8训练自己的数据集来说最顺手的是图片目录旁边放一个同名的txt文件每行写“类别编号、归一化中心x、归一化中心y、归一化宽、归一化高”。训练时模型读txt就能知道每个病斑的目标框。手工标注阶段我更推荐先用LabelImg存成VOC XML因为画框时能直接看到文件内容方便复查等标注做完了再统一转换成YOLO格式。这样做的原因很简单XML是给人看的txt是给模型吃的中间必须有一道转换环节。没有这一步训练程序根本不知道哪个框对应哪张图。下面这个脚本就是最常见的转换做法。import xml.etree.ElementTree as ET from pathlib import Path def convert_voc_to_yolo(xml_path, out_dir, class_names): tree ET.parse(xml_path) root tree.getroot() size root.find(size) width int(size.find(width).text) height int(size.find(height).text) out_lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_names: continue class_id class_names.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) x_center (xmin xmax) / 2 / width y_center (ymin ymax) / 2 / height w (xmax - xmin) / width h (ymax - ymin) / height out_lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) txt_path out_dir / (xml_path.stem .txt) txt_path.write_text(\n.join(out_lines), encodingutf-8) class_names [early_blight, late_blight, healthy] xml_dir Path(annotations) out_dir Path(labels) out_dir.mkdir(exist_okTrue) for xml_file in xml_dir.glob(*.xml): convert_voc_to_yolo(xml_file, out_dir, class_names)逻辑说明脚本先读XML里的原始图片宽高再把每个object的bndbox坐标转成归一化的中心点坐标和宽高最后写到同名txt文件里。这样生成的标注文件不依赖图片绝对路径换机器训练也不会报错。参数说明class_names列表的顺序非常关键它决定了每个类别对应的编号。后面写data.yaml时names必须和这里保持一致否则训练时类别编号对不上模型会大声报错。注释里的continue是在过滤没用的object比如标注文件里混了一些无关类别时直接跳过比报错好。2.2 转换完先查样本量类别分布统计脚本数据集做出来不是用来收藏的训练的成效和每个类别的实例数直接相关。所以我每次转换完都会先跑一个统计脚本看每个类别有多少个标注框。很多人只统计图片数量忽略框数量这是不对的一张叶片上可能同时有5个早疫病病斑和1个健康叶片框按图片计数会把类别不平衡的问题掩盖掉。from collections import Counter from pathlib import Path counts Counter() total_boxes 0 for label_file in Path(labels).glob(*.txt): for line in label_file.read_text().strip().splitlines(): parts line.split() if len(parts) ! 5: continue class_id int(parts[0]) counts[class_id] 1 total_boxes 1 class_names [early_blight, late_blight, healthy] for class_id in sorted(counts): print(f{class_names[class_id]:12}: {counts[class_id]} boxes) print(ftotal boxes: {total_boxes})逻辑说明逐行读标签文件只取每行的第一个类别编号做累加最后输出各类别的框数量。这个脚本肉眼扫一遍就能看出早疫病是不是只有区区几十个框。参数说明我一般把一个类别少于总框数5%的情况视为严重不平衡需要后续补样本或增强。注意这个统计是直接用转换后的txt如果转换脚本里做了过滤这里统计的数量就是模型真实看到的数量。2.3 标注质量从源头控制一张图里该有几个框先定规范数据集的完整性比标注速度更重要。我身边不少人在标注番茄叶片时凭感觉病斑边界深色的部分才标浅色晕染区域不标结果同一个病斑在不同人手里框大小差出一倍。番茄叶片病害识别的目标检测任务里病斑边界往往是渐变的并不像人检测车辆那样有明确的车身轮廓。我一般会定一条规则边框必须包含整个变色区域宁可稍微外扩不要只包住深色核心。因为模型的训练目标是把病斑和健康组织区分开如果框太紧模型学到的特征只覆盖病斑中心边缘形态的纹理信息就丢了。另一个容易忽视的点是叶柄和背景杂物要不要标。标准做法是只标叶片病斑健康叶片如果不做分类任务可以不标背景框因为YOLO默认不标注的区域就是背景。3. 用YOLOv8训练自己的番茄叶片病害数据集从data.yaml到训练参数3.1 环境安装与数据目录组织数据集准备好的下一步就是用YOLOv8训练自己的数据集。常见做法是装ultralytics这个Python包一条命令就能拉起训练。安装本身没有太多玄学关键是数据目录要按YOLO的规矩来pip install ultralytics安装完成后建议把数据集组织成下面这样的目录tomato_leaf/ ├── train/ │ ├── images/ │ └── labels/ ├── val/ │ ├── images/ │ └── labels/ └── test/ ├── images/ └── labels/逻辑说明YOLO训练时只要求train和val两个目录test目录用于最终验证。images和labels要平行放图片和txt同名比如001.jpg对应001.txt。目录结构不对是最常见的报错来源模型没开始训练先把路径吃了。参数说明这里我一般不放images和labels的子目录再套一层版本号因为data.yaml里的路径越简单越不容易错。如果之前用过VOC格式train和val下还可能各有一个annotations目录训练前必须确认YOLO读的是labels而不是XML。3.2 写data.yaml类别顺序和路径是命门YOLOv8支持通过一份yaml文件指定数据集路径和类别下面是番茄叶片病害识别项目里最朴素的一份path: /home/plant/tomato_leaf train: train/images val: val/images test: test/images nc: 3 names: 0: early_blight 1: late_blight 2: healthy逻辑说明path是数据集根目录train和val写的是相对path的图片目录路径。训练程序会把path和train拼接成完整路径所以path必须写死不能写相对路径建议直接写绝对路径。参数说明nc是类别数和names的长度必须一致。names里的顺序就是前面转换脚本里class_names的顺序编号0对应early_blight。如果转换脚本里class_names顺序是early_blight、late_blight、healthy这里就不能把healthy写在0号位置否则模型会把健康叶片当成早疫病来学。3.3 训练命令关键参数imgsz、batch与patience数据集和yaml都准备好后直接跑训练命令。我一般用yolov8s作为起步模型而不是yolov8n因为番茄叶片病斑的纹理细节比通用目标更复杂模型容量太小容易欠拟合。yolo detect train \ data/home/plant/tomato_leaf/data.yaml \ modelyolov8s.pt \ imgsz800 \ batch16 \ epochs100 \ patience15 \ projecttomato_leaf_runs \ nameexp_early_blight逻辑说明这行命令会把模型在指定数据集上训练100轮每轮用16张图输入分辨率是800×800如果验证集指标连续15轮不提升就提前停止。训练完成后权重存在tomato_leaf_runs/exp_early_blight/weights/下best.pt是验证集最优权重last.pt是最后一次迭代权重。参数说明imgsz我为什么用800而不是默认640因为番茄叶片病斑往往只占整张图的很小比例尤其用无人机或手机拍整株番茄时一个早疫病病斑可能只有几十个像素640分辨率下这些信息会被严重压缩。imgsz提升会拉高显存占用和训练时延但换来的是小目标召回率明显上升。batch大小主要看显卡显存16G显存跑yolov8s、imgsz800时batch16基本满负荷。patience设成15比较合适设太小会让训练在指标刚卡住时就提前收场设太大容易白耗时间。3.4 训练后先看什么混淆矩阵和PR曲线怎么读训练完成后不要只盯着训练日志里的loss。先开results.png里面有几张图是必须看的混淆矩阵、PR曲线、F1曲线。混淆矩阵可以看出模型是否把早疫病和晚疫病搞混这两个类别的病斑颜色接近是最常见的误检组合。PR曲线则看每个类别的准确率和召回率在不同置信度下的表现如果early_blight的PR曲线整体偏低说明这个类别的训练样本量或特征区分度有问题。验证命令也很简单用best.pt在val目录上跑一遍yolo detect val \ modeltomato_leaf_runs/exp_early_blight/weights/best.pt \ datatomato_leaf/data.yaml输出的mAP50是IoU0.5时的平均精度mAP50-95是更严格的指标。番茄叶片病害识别场景里病斑小且边界不明确mAP50-95通常比通用目标检测难看很多这并不代表模型不可用更值得关注的是每个类别的mAP而不是只看一个总的数字。4. 自己造一份番茄叶片病害数据集标注工具、增强方式与样本平衡4.1 标注工具选型LabelImg和X-AnyLabeling的取舍没有现成数据集时最绕不开的就是人工标注。目标检测常用标注工具里我最早用的是LabelImg稳定、轻量、输出VOC XML适合几百张图片以内的项目。但样本量上万后一个人拖框会手酸而且后期可追溯性差。后来换到X-AnyLabeling因为它支持基于已有模型的自动预标注先让模型标一轮人工只负责修正效率高不少。工具输出格式自动预标注适合场景LabelImgVOC XML不支持小规模数据、严格人工标注X-AnyLabelingJSON/XML/VOC支持加载模型预标中大规模数据、快速迭代选型理由如果只是先验证可行性直接用LabelImg画2-3百张就够如果是奔着落地去建议直接上X-AnyLabeling用预标注模型先把明显病斑标出来再人工修正边界。但要注意预标注模型如果来自通用目标检测它对番茄病斑的框往往偏大漏标不能全信修正量并不会减少太多。4.2 标注质量检查用脚本找出空标注、越界框和重复框人工标注一定会出错所以训练前必须用脚本扫一遍。我最常做的是检查三类问题空标注、越界框、行格式错误。下面这段代码可以一次性把这些常见问题找出来。from pathlib import Path def check_labels(label_dir, img_dir): problems [] for txt in label_dir.glob(*.txt): img_path img_dir / (txt.stem .jpg) if not img_path.exists(): problems.append((missing image, txt.name)) lines txt.read_text().strip().splitlines() if len(lines) 0: problems.append((empty label, txt.name)) for line in lines: parts line.split() if len(parts) ! 5: problems.append((bad line, txt.name)) continue try: class_id, xc, yc, w, h map(float, parts) except ValueError: problems.append((non numeric, txt.name)) continue if w 0 or h 0: problems.append((zero size, txt.name)) if xc - w / 2 0 or xc w / 2 1: problems.append((x out of range, txt.name)) if yc - h / 2 0 or yc h / 2 1: problems.append((y out of range, txt.name)) return problems problems check_labels(Path(labels), Path(images)) for p in problems: print(p)逻辑说明每个问题都输出文件名和问题类型然后人工决定是改标注还是删图。越界框在YOLO格式里很容易检查因为中心点和宽高归一化后都应该落在0到1之间如果中心点框的一半超出边界说明框画到了图像外。空标注通常是因为XML转换时object被过滤掉了也可能是标注时框被误删。参数说明img_dir这里用的是jpg后缀如果你的图片是png需要同步改后缀。更严谨的做法是去读XML里的filename字段但对批处理来说这个脚本已经够用。4.3 离线增强不只是旋转和翻转mosaic与混合增强的适用边界拿到标注后很多人第一反应是做数据增强。番茄叶片病害识别里光照变化和拍摄角度变化非常大所以增强方向要贴近真实采集场景。我一般用albumentations做离线增强因为能同时处理图片和bbox不会出现图片转了但框没转的笑话。import albumentations as A transform A.Compose([ A.HorizontalFlip(p0.5), A.RandomBrightnessContrast(p0.3), A.Rotate(limit15, p0.5), ], bbox_paramsA.BboxParams(formatyolo, label_fields[class_labels])) # 示例用法 # augmented transform(imageimage, bboxesbboxes, class_labelslabels)逻辑说明HorizontalFlip增强叶片的左右朝向Rotate模拟不同拍摄角度RandomBrightnessContrast模拟田间光照波动。所有增强都同步修改bbox坐标不需要额外写映射关系。参数说明Rotate的limit我故意设成15度而不是45度因为番茄叶片病斑本身有方向性旋转太多会让模型学到不真实的形态。如果使用YOLOv8自带的mosaic增强要注意训练时mosaic会拼接四张图对小目标检测有帮助但在病斑密集的叶片图片上拼接后病斑边缘会发生错位有时反而让模型学到拼接痕迹。所以在线训练时如果发现验证集效果波动大可以调小mosaic概率或关闭。5. 番茄叶片病害数据集避坑指南五条血泪经验5.1 病斑小目标导致训练后漏检现象训练结束后在验证集上mAP不低但把模型接到一段现场视频里叶片边缘或远处的病斑经常没框出来。原因番茄叶片病斑在一张4096×3072的手机原图里可能只占几十个像素下采样到640时几乎消失。加上标注时如果框只包住病斑中心后期训练时IOU过小模型很难学习到完整特征。解决数据集制作阶段就把分辨率问题想清楚训练时imgsz用800或更大。如果病斑实在太小可以先把病斑密集的区域裁出来作为独立图片再补标注。裁图要注意坐标偏移把图片裁出来之后标注框要根据裁剪区域重新换算。这个坎跨不过去后面所有指标都不可信。5.2 训练集和验证集来自同一批照片导致指标虚高现象训练损失很快收敛验证mAP冲到0.95但模型到另一片大棚里表现崩盘。原因数据划分时用随机切分同一个番茄叶片经不同亮度变换后分别进了训练集和验证集两个集合之间的信息大量重叠模型相当于提前见过“考试答案”。解决按采集批次划分数据最好把同一次拍摄、同一个大棚的数据整体划到同一个split里。我一般会先按图像文件名前缀分组再对组做随机分配确保训练集和验证集没有来自同一批照片。5.3 类别不平衡早疫病样本只有几十张怎么办现象混淆矩阵里早疫病的召回率稳定垫底健康叶片和晚疫病却很高。原因一个数据集里晚疫病框有两千个早疫病框只有六十个模型优化过程中早疫病贡献的loss太小几乎没有学习动力。解决首先补数据哪怕补几十张也比硬调参数有效。其次做离线增强时对少数类样本多生成一些变体但增强不能解决所有问题旋转和翻转只是重排已有特征如果早疫病和晚疫病的视觉差异本来就小增强带来的多样性也不大。更实际的做法是承认做不了三分类先做“病斑检测”二分类再人工区分病害类型这样对落地更稳。5.4 标注框过紧导致GIoU损失波动现象训练中前10轮loss降得很快之后GIoU曲线开始震荡预测框总是比病斑小一圈或者反复抖动。原因病斑边缘渐晕标注时每张图的框边界紧贴着深色部分不同标注者收紧尺度不一致模型学到的是一个模糊边界。解决标注规范里写明“病斑框必须包含整个变色区域”并在标注工具里把图片放大到能清楚看到过渡带。训练完用脚本统计所有标注框的宽高分布把宽或高小于10像素的框找出来重点复查。这类框通常出现在叶片边缘很容易被忽略。5.5 下载的开源数据集和本地病害种类对不上现象网上找到一份番茄叶片病害数据集直接拿来训练在本地温室测试基本没法用。原因开源数据集的拍摄背景、叶片品种、光环境和本地完全不同有的数据集里只有切下来的叶片特写没有田间背景导致模型学到的是“叶片构图”而不是病斑特征。解决把开源数据集当作预训练基础而不是最终训练集。我会用开源数据先训练一轮再用本地采集的图片做fine-tune而且本地数据至少要占最终训练数据的20%。如果本地类别和开源类别有交叉但不完全一致先合并或删除不确定类别不要强行保留。6. 用一张未标注的现场照片验证模型推理脚本与mAP复算6.1 导出模型并用一张现场照片做推理训练集的指标再漂亮也代表不了实际效果。我每次训练完的最后一件事是拿一张完全没有进入过训练流程的现场照片跑推理。from ultralytics import YOLO model YOLO(tomato_leaf_runs/exp_early_blight/weights/best.pt) result model( field_photo.jpg, conf0.25, iou0.45, imgsz800 ) result[0].save(field_photo_pred.jpg)逻辑说明模型加载best.pt后对field_photo.jpg做推理输出结果保存为一张带框的新图。conf是置信度阈值低于0.25的目标不会显示iou是NMS使用的IOU阈值控制重叠框的合并强度。在这份代码中imgsz必须和训练时保持一致或更高否则小病斑特性会发生偏移。6.2 用测试集复算mAP而不是只信训练日志训练日志里显示的指标来自验证集但验证集参与过早停策略指标难免偏乐观。我习惯再留一个完全没碰过的test集训练结束后单独复算一次。yolo detect val \ modeltomato_leaf_runs/exp_early_blight/weights/best.pt \ datatomato_leaf/data.yaml \ splittest逻辑说明YOLOv8读到data.yaml里的test字段后会用test目录做推理和mAP计算。如果test集的mAP比val低超过10个点说明数据划分存在泄漏或者test集的场景分布有差异需要回去检查划分逻辑。6.3 数据集的持续扩展是落地分水岭我现在每跑完一个番茄叶片病害识别项目都会把“现场验证”写进验收标准拿着模型到没去过的大棚拍一组照片看它能不能把早疫病和晚疫病分开。如果漏检集中在某一种光照或某类叶龄就专门去补这些样本回到标注工具里补框再重训一遍。这个闭环才是番茄叶片病害识别数据集真正发挥价值的时刻它让模型不断贴近真实环境也让我不再依赖那份网上找来的老旧开源数据集。希望帮到你。本文还有配套的精品资源点击获取
返回列表