恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
番茄健康叶片检测数据集:从标注到YOLOv8训练部署全流程
首页
资讯中心
/
番茄健康叶片检测数据集:从标注到YOLOv8训练部署全流程
番茄健康叶片检测数据集:从标注到YOLOv8训练部署全流程
发布时间:2026/10/14 14:18:56
做智慧农业视觉项目第一步永远是数据而不是模型。这几个月我在折腾番茄叶片的健康检测最终整理出一份3100张的番茄健康叶片检测数据集按YOLO格式标注可以直接喂给YOLOv5、YOLOv8甚至更新的版本训练。写这篇东西是想把从采集、标注、训练到部署这整条链路里的经验和坑一次性说清楚给正在做农业视觉、自定义数据集训练YOLOv8的同学一份可以抄作业的参考。先聊一个很多人容易忽略的点数据集的质量上限其实决定了模型的天花板。网络结构、训练技巧只能帮你逼近这个上限突破不了它。所以这篇文章的重心我会放在数据本身和训练细节上而不是去追那些花哨的模型变体。1. 从大棚巡检到目标检测这个数据集为什么先解决叶片识别1.1 番茄叶部检测在设施农业里的位置番茄是国内设施蔬菜里种植面积非常大的品类从定植到拉秧几乎每天都要巡检。北方大棚里最常见的几类叶部病害——早疫病、晚疫病、叶霉病——早期症状恰恰都表现在叶片上。叶片面积大、特征出现早、拍摄成本低所以做番茄健康检测第一优先级一定是叶片而不是果实。传统做法是农技员逐株翻叶看一亩地看完差不多半小时。规模上来之后人工巡检跟不上漏检、误判在所难免。智慧农业的落地思路就是用轨道巡检机器人、无人机或者固定位相机拍照先用视觉模型做初筛把可疑植株推送给人复查。这个流程里叶片检测是第一层滤网滤网漏了后面全是白费。1.2 健康与病叶的标注边界必须卡死这3100张图我按两个类别组织healthy_leaf健康叶片和diseased_leaf病叶。这个设定很关键——检测“健康”的前提是先定义清楚“健康”和“不健康”的边界。健康叶片的标准是叶色均匀主脉清晰没有明显褪绿、病斑、坏死或霉状物。病叶则包括早疫病斑、晚疫水渍状斑、叶霉病霉层、褪绿卷曲等等。真正难处理的是边界情况举几个我踩过的例子底部老叶自然黄化到底算不算病叶我明确归为健康理由是生理性老化不是病害。叶绳勒出的机械损伤印痕不算病叶因为不传染。日灼白斑面积超过叶面积三分之一算病叶因为光合能力已经严重受损。这些规则如果不定清楚两个人能标出两套标准后续模型训练出来就是一个不稳定的分类器。我在数据集的说明文档里专门放了一节“边界样本示例”把这类图单独截图存档标注的时候拿出来对照。1.3 3100张这个规模够不够用目标检测的数据量不能只看张数更准确的说法是看“目标样本量”。一张图里可能包含3到5片叶子所以3100张影像的标注框总数远大于3100个。对二分类叶片检测来说实践中单类别有效目标数达到1500到3000个就能训练出一个可用的baseline。这3100张里的目标数足够加上Mosaic、MixUp等增强训练出来的模型在正常场景下泛化没问题。但我不建议为了凑数量去拍同一株的连拍图。同一个叶片连续拍十张信息量跟拍一张几乎没有区别。真正的多样性来自不同光照、不同品种、不同生育期、不同背景。水分都要靠数据采集来补单靠copy是骗不过模型的。2. 采集、清洗与标注决定模型上限的不是网络结构2.1 拍摄场景设计与多样性控制我在大棚内外的采集重点控制了几个变量光照条件顺光、阴天、侧逆光都拍。正午强光直射会造成叶片高光过曝边缘难以辨认这类图可以作为难例少量加入但比例控制在5%以内。拍摄角度水平平视、俯拍、斜45度都覆盖。巡检机器人处于不同高度时视角差异很大单一视角训练的模型换到机器人上会掉点。生育期苗期小叶、旺盛期展开大叶、结果期叶色略深的老叶都要有。不同生育期叶片大小和颜色不一样缺了某一阶段模型到收获期就会失灵。分辨率原始图使用高像素拍摄训练前统一缩放。目标在图中占比如果太小后面训练时要考虑切图或者加大输入尺寸这个我在第4部分展开说。采集还有一个隐性要求背景自然。我特意保留了大棚滴灌带、垄沟、杂草、遮阳网这些复杂背景让模型学会关注叶片本身而不是背景。如果全都是在白色背景下拍的“标准照”部署到真实棚里很容易背景误检。2.2 标注工具与YOLO格式转换的核心细节标注工具我用的是LabelImg和X-AnyLabeling。前者简单稳定后者界面更好用都导出VOC XML格式。这里必须强调一个粒度选择框整片叶片而不是框病斑。这个任务的定义是叶片级健康判断目标是判断“这片叶子健不健康”。如果去框病斑就变成了两阶段任务既要定位病斑又要判断叶片是否健康复杂度上去了数据标注成本也翻倍。对于基础巡检场景叶片级的判断已经够用。我实际用下来这个粒度训练出的模型稳定性更好误检反而少。输出VOC XML之后要转成YOLO的txt格式因为ultralytics默认读txt。转换公式不复杂# 将voc xml的绝对坐标转换为yolo txt的归一化坐标 # 假设bbox [xmin, ymin, xmax, ymax], img_width, img_height为图像尺寸 x_center (xmin xmax) / 2 / img_width y_center (ymin ymax) / 2 / img_height box_width (xmax - xmin) / img_width box_height (ymax - ymin) / img_height # 写入txtclass_id x_center y_center box_width box_height # 注意所有值必须在0到1之间X_center、y_center、宽高全部归一化类别写在最前面。这个公式虽然简单但很容易出低级错误——比如忘记除以图片宽高或者宽高写反最后训练时要么报错要么损失爆炸。目录组织也直接影响会不会踩坑。我用的是这样tomato_leaf/ ├── images/ │ ├── train/ # 2100 images │ ├── val/ # 700 images │ └── test/ # 300 images └── labels/ ├── train/ # 同名txt ├── val/ └── test/图片和标签必须同名同前缀扩展名不同。这一点看起来基础但我见过太多人因为文件名带空格、中文、前缀不统一导致数据集加载为空。2.3 数据清洗脚本与同源泄漏问题标注完成之后不能直接开训先做一轮清洗。我写了一个简单的Python脚本做三件事import os from PIL import Image def check_labels(label_dir, img_dir): for label_file in os.listdir(label_dir): if not label_file.endswith(.txt): continue # 1. 检查是否存在对应图片 img_name label_file.replace(.txt, .jpg) img_path os.path.join(img_dir, img_name) if not os.path.exists(img_path): print(f缺少图片: {img_path}) # 2. 检查标签内容 with open(os.path.join(label_dir, label_file)) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(f格式错误: {label_file}) break class_id, xc, yc, w, h parts # 3. 检查坐标是否越界 if not all(0 float(v) 1 for v in (xc, yc, w, h)): print(f坐标越界: {label_file}: {line.strip()})坐标越界、空标签文件、图片文件损坏这三类问题脚本跑一遍基本能筛干净。这里多花一小时后面训练能少折腾一整天。数据划分这里有个比重容易被忽视的细节同源图片泄漏。如果你从同一棵番茄的不同角度连拍了好几张按文件名随机分train和val很可能同一株的图同时出现在两边。这不是严格的泄漏但会让验证集虚高训练出的模型真实场景推广能力大打折扣。我按拍摄批次和时间分组划分保证同一植株的图片不跨集合验证指标才是可信的。3. 用YOLOv8训练自己的数据集从环境到跑通的完整链路3.1 环境准备conda虚拟环境与ultralytics很多人会在自带的基础Python环境里直接装包迟早依赖打架。我强制建议用conda建独立环境版本号固定可复现conda create -n yolo python3.10 -y conda activate yolo pip install ultralytics注意pip install ultralytics会自动拉取适配的torch版本但有时拉的是CPU版。如果你有NVIDIA显卡建议手动装对应CUDA版本的torch再装ultralyticspip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install ultralytics在Windows上如果遇到pycocotools编译报错可以直接装预编译版pip install pycocotools-windows。这些坑不大但卡人。3.2 data.yaml配置路径与类别顺序的坑数据集的配置文件是训练入口我贴一份能直接用的path: /datasets/tomato_leaf train: images/train val: images/val test: images/test names: 0: healthy_leaf 1: diseased_leaf有两点必须注意。第一path字段建议写绝对路径。相对路径依赖于你执行命令时的工作目录换目录跑就找不到数据报“No labels found”。第二names的索引顺序绝对不能改。你的txt标签文件里写的是0和1names列表的顺序必须和标签一致。如果0对应healthy_leaf你就不能把0写回diseased_leaf否则训练的是“健康标签实际模型学病叶”的错位模型。3.3 训练命令与关键参数选择我用YOLOv8s作为起点数据量不算大s模型足够没必要直接上yolov8l或x后者在这个量级的数据上容易过拟合训练时间还长。命令如下yolo detect train \ datatomato_leaf.yaml \ modelyolov8s.pt \ epochs120 \ imgsz640 \ batch16 \ patience30 \ projectruns/tomato \ nameexp1modelyolov8s.pt很关键这是用COCO预训练权重做迁移学习相当于模型已经见过边框、纹理、形状这些通用概念我们只让它在番茄叶片的分布上做微调。从零开始训练的效果通常远不如迁移学习。imgsz的选择要考虑目标尺寸。我的原始图里单张叶片大概占整幅图的10%到30%640的输入尺寸足够。如果目标很小、占比不到5%就要考虑提升到960甚至1280或者做切图推理。代价是显存占用成倍增长。batch由显存决定。16G显存从16起步显存不足就降一半到8再不行就降到4。YOLOv8对batch没那么敏感但batch太小训练波动明显loss曲线会抖得更厉害。3.4 训练日志到底应该盯哪个数训练开始后控制台每一轮会打印一长串指标P精确率、R召回率、mAP50、mAP50-95、各类loss。很多人只盯着mAP50看我建议同时关注R农业场景下漏检比误检更致命——病叶漏检等于错过防治窗口后面做模型评估时我会专门讲这个优先级问题。还有一个容易忽视的字段是patience30。如果连续30轮验证集mAP没有提升训练会提前终止。这是好事不是坏事。它帮你省时间也说明模型已经收敛到一个平台。如果你发现训练到第80轮就被提前停了但指标还没达标问题大概率在数据侧而不是训练轮数不够。看loss曲线时正确的做法是关注训练loss和验证loss之间的差距是否在拉大。如果训练loss持续下降、验证loss不降反升这就是过拟合信号。解决办法不是继续训而是回去检查数据多样性、增加增强强度或者换个更轻量的模型。4. 评估指标与农业场景的“容错优先级”4.1 mAP50和mAP50-95的区别农业场景看哪个目标检测里最常说的两个指标mAP50和mAP50-95。mAP50是预测框和真实框的IoU超过0.5就算命中相对宽松mAP50-95是把IoU阈值从0.5到0.95每隔0.05算一个mAP再平均非常严格。很多初学者看完mAP50觉得模型已经很好一部署到真实场景就翻车原因就在这。叶片这类非刚性物体边界本来就模糊预测框和人工标注框的IoU很难做到0.9以上所以mAP50-95天然不会太高。我做农业巡检评估时主要看mAP50和R召回率mAP50-95作为稳定性参考。只要mAP50稳定在0.75以上召回率能达到0.8以上这个模型就具备落地潜质不需要也没必要去追那0.05的mAP提升。4.2 农业场景的查全率优先策略这里说一个和通用目标检测反着来的决策逻辑。通用场景里比如安防或者物流误检和漏检的代价都很高所以追求P和R的平衡。但农业巡检不一样健康叶被误检为病叶植保人员多跑一趟实际成本是人工复查的时间不算大。病叶被漏检一旦错过早疫病的早期防治窗口病斑扩散到全棚直接减产。所以部署时我的策略是降低置信度阈值让模型多报、宁可错杀。具体操作上推理时把conf从默认的0.25降到0.1或0.15把“疑似病叶”的框全部送人工复审环节。一个棚里模型可能标出一两百个可疑区域人工抽检重点区域效率和纯人工巡检完全是两个量级。NMS非极大值抑制的iou阈值也值得调。默认0.7对密集叶片场景偏高叶片重叠时容易把相邻叶片的框合并成一个。实测降到0.5到0.6密集场景下的召回率会提升代价是验证时mAP略降但这对农业场景是划算的。4.3 从混淆矩阵定位错误根源训练完看混淆矩阵是个好习惯它比mAP能告诉你更多。我自己的训练结果里健康叶和病叶交错的那一格如果高于10%说明标注阶段的边界规则出了问题回去看那批争议样本是哪一类如果是背景被误检成叶片问题就出在采集阶段背景单一模型把滴灌带当成了叶片解决方法是补充复杂背景的负样本图——注意负样本指的是完全不包含叶片的空背景图YOLO训练需要这类图帮你明确“什么是背景”。5. 踩坑记录从报错到错检的完整排查链路5.1 数据集加载类报错的根因排查先说一个高频报错No labels found in ...。这个报错一出现绝大多数情况不是你真的没标数据而是目录结构或路径出了问题。我排查过一次完整的链路按下面顺序走第一步检查data.yaml的path字段是否指向了包含images和labels两个兄弟目录的根目录。train和val写的是images/train、images/valultralytics会自动从同级的labels/train、labels/val读取标签txt。如果你目录结构里labels并不在images的兄弟位置就会报找不到标签。第二步检查图片和txt文件名是否严格一致。大小写、下划线、空格都会导致匹配失败。Linux和Windows对文件名的大小写敏感程度不一样在Windows上跑通的代码上传到Linux服务器上跑立即报错因为Photos里是.JPGtxt里存的是.jpg大小写不匹配。第三步检查训练集里是否有一张图的标签文件为空如果整个train目录里全部是空标签也会报类似错误。清洗脚本里增加空文件检查可以提前拦住。5.2 训练指标不收敛的几个常见原因Loss爆炸或者NaN先检查标签坐标。偶尔会发生归一化坐标超过1或者出现负数的情况典型原因是转换脚本里图片宽高读错了比如读了缩放后的尺寸但坐标还是原始图的。这类坏标签会在训练中被数据加载器读取一旦碰到极端值loss直接变成NaN。训练loss下降很慢、mAP上不去看三类问题标注一致性差。同一个叶片正面标病叶、背面标健康叶模型学不到稳定特征。这个只能回标注阶段解决。背景干扰太强。大棚里滴灌带、枯草、泥土和叶片颜色接近时模型分不清。补充背景多样性或检查是不是负样本数量不足。样本严重不平衡。病叶样本远少于健康叶模型会倾向于把所有框都判为健康叶召回率极低。处理方式是补充病叶样本或者对diseased_leaf类别加权。显存不足要区分情况。如果报CUDA out of memory最简单是batch减半、imgsz从640降到512。但如果batch降到4依然爆显存就要怀疑是不是同时开了多个进程或者Pycharm里另一个终端还在跑着旧训练任务占着显存。用nvidia-smi看一眼最直观。5.3 训练效果好、实际检测烂的问题模型在验证集mAP很高拿到真实场景一测发现错检一堆。这类问题常见原因有三个一是验证集与训练集太接近同源图片泄漏让指标虚高解决方法是严格按时间/植株分组划分数据。二是阈值得设置不对。训练和评估时的默认参数是conf0.25、iou0.7实际推理时如果没有显式设置参数行为可能和验证时不一样。我推理时通常会显式写conf0.15 iou0.5这样预期可控。三是光照域差太大。在阴天拍的训练集拿到晴天强光下测模型很容易把高光叶片判成异常。数据采集时加入多光照条件部署端如果固定安装可以在固定角度和光照下重新微调一次效果会好很多。我对常见问题整理了一个直接可查的汇总表现象常见根因处理办法No labels found路径错误或文件名不一致检查data.yaml path核对图片与txt同名loss为NaN标签坐标越界或图片损坏清洗脚本全量扫描剔除坏样本训练快但mAP上不去标注一致性差或类别不平衡重审标注边界规则补充少数类样本验证指标高但实测烂同源泄漏或光照域差严格分组划分采集多光照数据显存爆掉batch过大或其他进程占卡降batch和imgsznvidia-smi查占用背景被当成叶片训练背景单一负样本不足补充无目标背景图进入训练集6. 数据集之外从叶片检测到智慧农业落地的下一步6.1 从检测到分割病斑面积占比是更高维的诉求叶片级的检测框能回答“哪片叶子可能病了”但回答不了“病得多严重”。病害等级评估通常需要病斑面积占叶片面积的比例这就得从检测模型换到实例分割模型。好消息是YOLOv8自带分割分支YOLOv8-seg数据标注也不必从头来——检测框的标注无法直接生成分割掩码但你可以基于已训练好的检测模型对叶片区域裁剪后请标注员在裁剪区域内做多边形标注工作量会小很多。训练分割模型时yaml的配置结构基本一致只是模型换成yolov8s-seg.pt。得到分割结果后计算病斑像素占叶片像素的比例就能生成更细的病情等级。这个方向是我接下来打算做的它和植保上“防治阈值”的概念能对上。6.2 ONNX导出与边缘端部署的细节训练完成后导出常用格式是ONNX命令很简单yolo export modelbest.pt formatonnx imgsz640但有几个细节要留意。导出时imgsz必须和训练时的imgsz保持一致否则精度会掉。导出的ONNX默认输入形状是动态的如果你要在TensorRT里跑静态batch可以指定--dynamicFalse让输入固定这样推理效率更高代价是输入尺寸被锁死。在边缘设备上部署时我实测在Jetson AGX Orin这类设备上把ONNX转成TensorRT引擎后推理帧率能提升1.5到2倍。搭建YOLO环境的同学都懂AGX Orin上装环境和编TensorRT引擎本身就是一门学问核心是先确保JetPack版本和PyTorch、TensorRT版本匹配再用trtexec生成engine文件最后在推理脚本里加载。另一个部署细节是推理输入的数据预处理必须和训练时一致。YOLO训练用的是RGB三通道、归一化到0到1的输入推理时如果读成BGR或者忘记归一化输出置信度会明显异常。这种问题不说清楚很多人会在部署阶段卡很久。6.3 拓展数据集的几个方向当前这份3100张的番茄健康叶片数据集解决了基础检测需求但要真正走向生产还需要在几个维度继续扩展品种覆盖。不同番茄品种的叶形、叶色差异明显同一个模型跨品种使用准确率会掉。地域环境。北方大棚和南方露天的光照、湿度差异大病害表现也不同。更多病害种类。早疫病、晚疫病、叶霉病之外还有病毒病、灰霉病等类别数量上去了模型能覆盖的植保场景才完整。多模态数据融合。单纯图像识别有局限下一步是结合温湿度、土壤墒情、光谱数据做多模态分析。叶片图像给出“是什么病”环境数据给出“为什么高发”这才是智慧农业完整的一环。6.4 我个人实操中的几点体会最后说几句实在的。第一数据清洗和标注规范这两项工作至少要占整个项目40%以上的时间。很多人觉得标注完就能训练了结果被坏标签、边界不一致搞得反复返工。标注意见文档一定要留好尤其是边界样本的处理规则否则下次扩充数据时你会忘得一干二净。第二迁移学习不是万能的。COCO预训练权重提供的是通用视觉特征到了叶片这种细粒度分类上它给你的提升其实有限。真正决定模型效果的一是数据质量二是你标注的一致性和覆盖度三是部署时阈值参数跟场景匹配。第三做农业数据集时我在标注过程中会给每张图记录拍摄时间、品种、叶位等信息这些元数据现在看似没用真到做多模态分析和模型跨场景评测时它们能帮你快速定位模型的失败模式省下大量重复采集的时间。这份数据集当前已经支持通用的YOLO目标检测训练希望我上面踩过的这些坑能让你在这个基础上少走几步弯路。