十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

基于YOLOv11的叶片病斑识别与严重度分级实战

基于YOLOv11的叶片病斑识别与严重度分级实战 简介聚焦农业病虫害智能化防治场景基于YOLOv11的叶片病斑识别与分级系统开发实录以51页PDF呈现完整的系统建设过程面向计算机视觉初学者、目标检测研究者和农业信息化开发人员针对传统病斑识别效率低、分级标准不统一等痛点给出从算法原理、数据准备到工程落地的系统性参考。资源共1个PDF文件压缩包仅2.3MB支持目录章节跳转与阅读器大纲快速定位内容涵盖引言、农业病虫害背景、YOLOv11算法演进与创新点、系统总体设计、数据采集与预处理、模型训练与优化、功能模块开发、系统测试评估等环节目录结构清晰便于按需查阅。目前已有66人学习/下载读者既可从零理解YOLOv11骨干网络设计、多尺度特征融合、损失函数改进等内容也能借鉴分层系统架构、数据增强策略、mAP评估指标、病斑面积与数量综合分级思路以及防治建议生成方法直接支撑毕业设计、课程项目或农业检测系统的预研与开发。1. 农业病害识别的难点不在检测而在“分级”这一跳把 YOLOv11 用在叶片病斑识别上真正卡住团队的往往不是模型精度而是“检测出来之后怎么办”。病斑检测框拉出来只是第一步农林植保人员真正关心的是“这棵作物病害到几级了该不该打药打多大剂量”。从“有病斑”到“病害严重度分级”中间隔着面积占比计算、阈值标定、置信度与多帧稳定性判断等一系列工程问题。这篇实战记录围绕 YOLOv11 的叶片病斑识别与分级系统展开覆盖数据准备、模型训练、分级判定逻辑和部署排错适合正在做农业视觉项目、想少走弯路的算法工程师和全栈开发。2. 数据准备与标注YOLOv11 训练前必须先想清楚的三件事2.1 病斑样本采集背景多样性比数量更重要农业现场和公开数据集的最大区别在于光照、遮挡和叶片姿态。水稻稻瘟病、玉米大斑病、葡萄霜霉病在自然光下的表现差异极大如果训练集里全是实验室白背景的叶片特写模型到了田里基本不能用。采集时需要注意三点不同生育期的叶片都要覆盖因为老叶和新叶的病斑颜色、纹理差异明显拍摄角度要包含俯拍、侧拍和 45 度斜拍光线条件要涵盖晴天直射、阴天散射和遮阴环境。采集数量上我一般建议每个病害类别至少准备 800 到 1500 张原始图。不要一味追求数量如果 800 张图已经覆盖了不同品种、不同地区和不同光照效果通常比 3000 张单一背景下拍摄的图更好。数据清洗时要去掉严重过曝、失焦和叶片占比过小的图片这类样本只会让 YOLOv11 在训练时学到噪声。2.2 标注粒度单框还是多框直接决定分级上限标注策略要提前想清楚因为它直接决定后续分级系统能不能落地。常见的做法有两种一种是整片叶子一个框框内同时包含多个病斑另一种是每个独立病斑一个框框出所有可见病斑。用 YOLOv11 做分级强烈建议采用第二种按病斑实例标注而不是按叶片标注。原因在于分级判定的核心指标是“病斑面积占叶片面积的比例”。如果每个病斑单独一个框实际部署时可以通过所有检测框的面积累加除以叶片轮廓包围面积得到近似严重度。如果只标叶片级框模型输出的置信度只能代表“有没有病”无法反映“有多少病”分级精度会很差。用 LabelImg 或 X-AnyLabeling 标注时类别定义建议按病害名称区分比如 Rice_Blight、Corn_Leaf_Spot不要用 Disease_1、Disease_2 这种抽象命名后续训练好的模型做推理结果展示时直接映射中文名称更方便。2.3 数据增强参数设置避免小目标被过度增样YOLOv11 内置的 mosaic、mixup、copy_paste 增强对叶片病斑这类小目标检测很有帮助但参数要克制。实际踩过的一个坑是 mosaic 概率设得过高导致病斑被拼接边界切割小目标只剩半截模型训练 loss 一直降不下去。我一般会把 mosaic 设置在 0.5 到 0.7mixup 在 0.2 左右copy_paste 保持默认关闭因为叶片边缘不规则copy 出来的病斑粘贴后边界锯齿明显反而增加误检。对于图像尺度YOLOv11 默认输入是 640x640叶片病斑很多在 20x20 像素以下属于典型小目标。如果设备显存允许直接调到 960x960 或 1280x1280 是最省事且有效的小目标优化手段。下面是 YOLOv11 训练前的一份基础增强配置使用 ultralytics 库训练时直接写到 data.yaml 里调用train: ./datasets/leaf_disease/train val: ./datasets/leaf_disease/val nc: 4 names: [rice_blast, corn_leaf_spot, grape_downy_mildew, apple_scab] # 增强参数 hsv_h: 0.015 hsv_s: 0.6 hsv_v: 0.4 degrees: 15.0 translate: 0.1 scale: 0.4 shear: 5.0 perspective: 0.0001 flipud: 0.4 fliplr: 0.5 mosaic: 0.6 mixup: 0.15degrees设为 15 而不是 90 或 180是因为叶片病斑在自然状态下不会发生完全倒置旋转太大会让模型学习到不真实的病斑朝向。flipud设置 0.4 而不是默认的 0.5同样基于农业场景的物理约束。scale0.4 保证目标在缩放后依然能覆盖 50% 到 140% 的原始范围既模拟了相机远近变化又没有过度拉伸导致病斑变形无法辨认。3. 用 YOLOv11 训练叶片病斑检测模型从配置到收敛3.1 环境配置与预训练权重选择YOLOv11 的环境配置相比早期版本已经简化了很多Conda 创建 Python 3.10 环境后直接装 ultralytics 即可。依赖层面注意一点PyTorch 的 CUDA 版本要和本机驱动匹配Linux 下用nvidia-smi查看驱动支持的 CUDA 版本然后去 PyTorch 官网选对应的安装命令。conda create -n yolov11 python3.10 conda activate yolov11 pip install ultralytics torch torchvision --index-url https://download.pytorch.org/whl/cu121这里固定了 CUDA 12.1 的 PyTorch 轮子。如果本机驱动只支持 CUDA 11.8就得把cu121改成cu118否则训练时报CUDA error: no kernel image is available这是环境配置环节最常见的报错。预训练权重选择上YOLOv11 按网络深度和宽度分为 n、s、m、l、x 五个规格。叶片病斑检测属于小目标密集场景n 和 s 的特征图分辨率虽然够但下采样到 P3 时小目标信息流失偏多。建议从yolo11m.pt起步精度与速度平衡最好。显存低于 8G 再考虑yolo11s.pt代价是小病斑召回率会掉 2 到 4 个点。3.2 训练命令与关键超参数解释启动训练的命令非常直接yolo train modelyolo11m.pt dataleaf_disease.yaml epochs200 imgsz960 batch16 device0 patience40 optimizerAdamW lr00.001 lrf0.01 warmup_epochs3各参数的设置思路如下imgsz960为了照顾小病斑。640 输入下很多初期病斑只有 8 到 12 像素到了 960 输入下能到 12 到 18 像素检测头更容易提取到有效特征。epochs200配合patience40早停在 160 到 170 轮附近触发是正常的不要硬跑满 200 轮。optimizerAdamWYOLOv11 在中小数据集上用 AdamW 收敛比 SGD 快推荐先用 AdamW 跑一轮再把模型迁移到 SGD 微调。用同样的数据SGD 最终精度通常高 0.5 到 1 个 mAP但训练时间多 40%。训练日志里盯着两个指标一个是box_loss另一个是cls_loss。前 20 轮box_loss从 1.6 下降缓慢属于正常现象因为 warmup 阶段学习率在爬坡。到了 60 轮左右如果box_loss还在 1.0 以上说明数据标注噪声较大优先检查标注框是否出现了大面积偏移。3.3 小目标优化切分检测与注意力机制改进YOLOv11 原版在叶片病斑这类密集小目标上直接跑往往 mAP 能到 0.7 以上但 recall 偏低漏检集中在 10x10 像素以下的细碎病斑。两个可行的优化方向按投入产出比排序第一是滑窗切分把原图切成 4 份 640x640 的块分别推理再合并结果。这个方案不需要改模型结构部署时也容易实现。切分会产生重复框用 NMS 合并时 IoU 阈值要降到 0.3否则块边缘的同一病斑会被输出两次。切分方式对 960 输入的模型收益不明显但如果部署端只能跑 640 输入这个方案能提升小目标 recall 约 8 个点。第二是给检测头加显式的注意力模块。比较轻量的做法是在主干网络最后一个特征层后拼接一个坐标注意力模块代码示例如下import torch import torch.nn as nn class CoordAtt(nn.Module): def __init__(self, inp, reduction16): super().__init__() self.pool_h nn.AdaptiveAvgPool2d((None, 1)) self.pool_w nn.AdaptiveAvgPool2d((1, None)) self.conv nn.Sequential( nn.Conv2d(inp, inp // reduction, 1, biasFalse), nn.BatchNorm2d(inp // reduction), nn.SiLU(), ) self.conv_h nn.Conv2d(inp // reduction, inp, 1, biasFalse) self.conv_w nn.Conv2d(inp // reduction, inp, 1, biasFalse) def forward(self, x): b, c, h, w x.shape x_h self.pool_h(x).permute(0, 1, 3, 2) x_w self.pool_w(x) x_cat torch.cat([x_h, x_w], dim2) x_cat self.conv(x_cat) identity x_cat x_h, x_w torch.split(identity, [h, w], dim2) x_h self.conv_h(x_h.permute(0, 1, 3, 2)) x_w self.conv_w(x_w) return x * x_h.sigmoid() * x_w.sigmoid()把这个模块插入到 YOLOv11 主干网络的第 6 层输出后也就是原本直接进入 SPPF 的特征图之前。坐标注意力关注的是“哪里有病斑”和“病斑在空间上的分布关系”对叶片这种背景复杂、目标细碎的场景比较有效。实测在葡萄霜霉病数据集上 mAP50 提升约 2.3 个点参数量只增加不到 1MB。4. 分级系统实现从检测框到严重度判定的完整链路4.1 分级标准定义先和植保专家对齐阈值分级系统不是纯算法的产物严重度阈值必须参考植保领域的通用标准。叶片病斑严重度分级通常参考国家农业技术推广服务中心发布的病害调查规范一般按病斑面积占叶片总面积比例划分成四级级别病斑面积占比防治建议0 级0%无需防治1 级1% - 10%观察可暂不打药2 级11% - 25%建议施药3 级26% - 50%立即施药4 级 50%重点防治或拔除实际开发中不要把这个阈值写死在代码里要做成配置项因为不同作物的经济阈值差异很大。比如小麦锈病在 5% 的严重度就需要打药而苹果斑点落叶病到 20% 以上才需要干预。分级系统的配置中心里放一张阈值表通过接口动态更新比每次改代码重新部署要灵活得多。4.2 分级判定逻辑YOLOv11 推理结果的后处理YOLOv11 的推理结果是一个列表每个元素包含box、conf、cls三个关键属性。分级判定逻辑如下from ultralytics import YOLO model YOLO(runs/train/leaf_disease/weights/best.pt) results model.predict(field_photo.jpg, imgsz960, conf0.25, iou0.45) def grade_severity(results, leaf_contour_area, threshold_config): total_disease_area 0 lesion_list [] for result in results: boxes result.boxes for box in boxes: conf float(box.conf[0]) cls int(box.cls[0]) x1, y1, x2, y2 [float(v) for v in box.xyxy[0]] lesion_area (x2 - x1) * (y2 - y1) # 置信度低于0.4的检测框不计入分级面积 if conf 0.4: continue total_disease_area lesion_area lesion_list.append({ bbox: [x1, y1, x2, y2], conf: conf, class: cls, area: lesion_area }) severity_ratio total_disease_area / leaf_contour_area for level, (low, high) in threshold_config.items(): if low severity_ratio * 100 high: return level, severity_ratio, lesion_list return 4级, severity_ratio, lesion_list threshold_config { 0级: (0, 1), 1级: (1, 10), 2级: (10, 25), 3级: (25, 50), 4级: (50, 101) } level, ratio, lesions grade_severity(results, 45000, threshold_config) print(f病害分级结果: {level}, 病斑面积占比: {ratio * 100:.2f}%)这段代码里leaf_contour_area是叶片轮廓面积建议用语义分割模型单独输出而不是用检测框面积代替。原因很简单检测框是矩形叶片的真实轮廓不规则矩形框面积会膨胀 20% 到 40%直接导致严重度偏高。实践中可以先跑一个轻量级分割模型拿到叶片的 mask再统计 mask 内的非零像素数作为真实面积。阈值表的high区间用了开闭区间的写法目的是避免边界条件错乱。4.3 检测结果保存与可视化输出开发阶段需要把 YOLOv11 的推理结果落盘方便检查分级判定哪里出了问题。常见做法是保存三种产物标注后的图片、结构化的 JSON 结果、用于回放的裁剪图。import json import cv2 # 保存标注图片 annotated results[0].plot() cv2.imwrite(output/annotated.jpg, annotated) # 保存 JSON 结果 output { image: field_photo.jpg, grade: level, disease_ratio: ratio, lesions: lesions } with open(output/result.json, w, encodingutf-8) as f: json.dump(output, f, ensure_asciiFalse, indent2) # 裁剪每个病斑区域单独保存 for i, lesion in enumerate(lesions): x1, y1, x2, y2 [int(v) for v in lesion[bbox]] crop cv2.imread(field_photo.jpg)[y1:y2, x1:x2] cv2.imwrite(foutput/lesion_{i}_{lesion[class]}.jpg, crop)results[0].plot()是 ultralytics 内置的可视化方法会在图上画出检测框、类别标签和置信度生成文件后交给前端展示或存入数据库。JSON 里不仅要有坐标和置信度还要有计算好的面积这样前端在展示病斑详情时不需要再传原图重新推理一遍。裁剪图主要留给农技人员做人工复核这一步很重要自动分级系统上线初期人工复核的比例建议保持在 20% 以上。5. 模型部署与排错YOLOv11 落地到实际采集设备的最后一公里5.1 模型转换与推理速度优化训练好的 PyTorch 模型不能直接用在大田巡检设备上常用的转换路径是导出为 ONNX或者进一步转成 TensorRT 引擎在 NVIDIA 设备上加速yolo export modelbest.pt formatonnx imgsz960 opset12 simplifyTrueONNX 导出后部署端用 ONNX Runtime 推理。注意opset12是为了兼容老旧设备上的 ONNX Runtime 版本如果部署环境允许直接默认 opset17 也没有问题。转成 TensorRT FP16 后在 Jetson Orin Nano 上推理一张 960x960 的图片YOLOv11m 大约能从 280ms 降低到 95ms接近实时。如果对速度还不满意可以把conf阈值从 0.25 提到 0.35过滤掉低置信度框后后处理阶段能省下 NMS 的大量计算。5.2 小目标漏检的现场排错思路模型上线后反馈最多的就是漏检尤其是病害初期的针尖大小病斑。排查时先看一段现场采集的视频截出漏检的病斑图然后用model.predict()单张图测试对比漏检病斑在特征图上的激活情况。使用 YOLOv11 自带的特征图可视化功能能快速确认是哪个层丢失了目标信息import torch from ultralytics import YOLO model YOLO(best.pt) model.eval() img cv2.imread(漏检样本.jpg) img_tensor torch.from_numpy(img.transpose(2, 0, 1)).float().unsqueeze(0) / 255.0 # 注册hook获取主干网络中间层输出 activations {} def hook_fn(name): def hook(module, input, output): activations[name] output.detach().cpu() return hook model.model.model[6].register_forward_hook(hook_fn(layer6)) model.predict(img_tensor, imgsz960) print(特征图尺寸:, activations[layer6].shape)如果第 6 层输出的特征图中小病斑位置的激活值整体偏低说明问题出在特征提取阶段此时要回到训练侧调整提高imgsz、增强小目标区域的重采样权重、或者使用切分推理。如果第 6 层特征图有激活但最终输出没有检测框问题在检测头优先检查置信度阈值是否设置过高或者类别样本不均衡导致某个类别被抑制。5.3 多帧结果平滑与分级稳定性大田巡检中相机和叶片之间会有相对晃动单帧推理的严重度结果波动会很大。上一帧分级是“1级”下一帧可能因为轻微抖动变成“2级”。工程师视角可以解决问题但农技人员不会接受一个来回跳动的系统。解决方案是加一个滑动窗口滤波对连续 5 帧的严重度数值取中位数from collections import deque import statistics severity_buffer deque(maxlen5) def predict_stable(frame): results model.predict(frame, imgsz960, conf0.25, iou0.45) # 计算单帧严重度 grade, ratio, lesions grade_severity(results, leaf_area, threshold_config) severity_buffer.append(ratio) stable_ratio statistics.median(severity_buffer) stable_level ratio_to_level(stable_ratio, threshold_config) return stable_level, stable_ratiodeque(maxlen5)保证缓冲区不会无限增长取中位数而不是平均值是因为中位数对异常值不敏感可以避免某个瞬间的病斑误检引发分级跳变。还有一个细节是叶片检测必须先做追踪只对同一片叶子连续多帧的结果做滤波否则不同叶片之间的面积差异会把滤波结果彻底扰乱。接入 YOLOv11 自带的 ByteTrack 即可给每个叶片分配一个 track_id分级系统按 track_id 维护各自的滑动窗口缓冲区。这个方案的稳定性在实测中能显著提升抖动跳级的问题基本消失。本文还有配套的精品资源点击获取
返回列表