YOLOv11多光谱叶片分割与虫害计数实战:从训练到边缘部署
发布时间:2026/9/30 10:00:29 作者:尧图编辑部 阅读量:1,286

简介一份面向农业智能化与计算机视觉开发者的实战型PDF技术文档系统讲解如何基于YOLOv11与多光谱图像处理完成农业病虫害检测系统的开发。YOLOv11凭借单阶段检测特性兼顾速度与精度使系统在实时监测场景中具备明显优势。文档针对传统人工检测效率低、结果主观性强、难以实时监测等农业痛点以叶片分割与虫害计数两大任务为主线覆盖多光谱数据采集与预处理、YOLOv11基础理论、叶片分割算法实现、虫害计数方法设计、系统开发与集成、实验结果分析与典型应用场景等完整技术链路并涵盖数据增强、模型融合与评估指标等细节帮助读者建立从算法到工程落地的认知适合算法工程师、农业科研人员及高校学生按目录快速查阅。资源为PDF格式共1个文件压缩包大小约2.02MB文档共40页文字、图表、目录显示完整支持大纲跳转和章节定位目前已有101人浏览学习可作为农业病虫害检测项目入门参考。1. 农业病虫害检测的硬骨头为什么是YOLOv11多光谱田间拍回来的照片要么背景全是土和杂草要么叶片被阴影压成一片死黑。做病虫害检测的同行都有体会用普通RGB图训练出来的分割模型换个地块、换套光照就崩虫体小到只有十几个像素数都数不清。农业病虫害检测实战里把多光谱信息和YOLOv11的实例分割能力结合起来走“多光谱叶片分割虫害计数”这条路是这几年我们验证下来最稳的方向。多光谱能把健康叶片和病斑从光谱上分开YOLOv11的分割头负责把叶片轮廓抠出来计数交给掩膜分析而不是靠数框框。这套方法适合正在做植保信息化、智能虫情测报或者想用视觉模型替代人工田间调查的团队。读完这篇你能拿到一条从数据采集、模型训练、计数统计到边缘设备部署的完整落地路径。2. 多光谱数据从哪来波段选择、采集设备与标注规范2.1 多光谱和RGB的区别五个波段还是越宽越好多光谱不是玄学它的核心价值在于把“颜色”扩展成“光谱响应曲线”。普通RGB相机把400到700nm可见光压缩成三个通道而多光谱传感器在同样的视场里多采了几个窄带波段。水稻稻瘟病、小麦条锈病这类病害发病初期叶片在RGB图里只是微微发黄人眼很难判断但红边波段700到750nm和近红外波段750到900nm的反射率变化非常明显。这也是为什么植保方向的公开数据集大多带RedEdge和NIR通道。选波段的时候一个常见误区是“波段越多越好”。我做过的方案里用的是典型植保无人机多光谱相机的五通道配置蓝450nm中心波长、绿560nm、红650nm、红边730nm、近红外840nm。为什么不直接上十几个波段的高光谱数据量、标定成本和模型复杂度都会失控而且YOLOv11的输入端按通道数接受图像通道太多会把训练时间拖垮。五个波段里蓝绿红合成RGB图喂给模型做外观特征红边和近红外作为额外通道参与植被指数计算这个组合在绝大多数田间场景下已经够用。通道中心波长典型半高宽在叶片分割里的用途Blue450nm35nm合成RGB基础通道提供纹理细节Green560nm40nm正常叶绿素反射峰区分活体叶片Red650nm40nm叶绿素吸收带病斑区反射率偏高RedEdge730nm40nm病害早期敏感波段红边位移检测NIR840nm50nm叶片含水量与细胞结构区分枯叶与背景多光谱数据的采集方式有两种无人机搭载多光谱相机飞正射或者地面手持多光谱相机贴近叶片拍。无人机适合田块级虫情普查地面拍摄适合叶片级分割和虫害细粒度计数后者和“叶片分割”这个目标更匹配。采集时有个硬性要求多光谱相机的光照传感器必须直视天空每次起飞前做一次白板校准否则太阳高度角变化会让各波段反射率失去可比性。把这种校准视为拍照流程的一部分不要指望软件后处理能完全救回来。2.2 叶片分割的标注规范掩膜格式与类目设计多光谱数据拿到手之后要先做预处理才能进入训练管线。常见做法是把原始五通道TIFF拆成单波段蓝绿红合成为RGB图红边和近红外分别存为两个单通道灰度图然后五通道直接拼成一个5通道Tensor送入YOLOv11。有人会把RGB、红边、近红外做成三图并列的多流输入也有人把红边NIR合成伪彩色图工程上最稳妥的还是直接拼通道YOLOv11的输入层改一个参数就能接住。标注规范是这套方案能不能复现的关键。叶片分割的标注用COCO的polygon格式一个叶片一个实例病斑区域单独标一个类不要混在叶片轮廓里。类目设计上至少分四类正常叶片、病斑叶片、虫害叶片、背景。“虫害叶片”和“病斑叶片”的差别在于虫害叶片通常有咬噬边缘或虫粪附着分割模型需要靠纹理细节区分。标注时要放大到单个叶片占画面至少30%再勾轮廓否则小面积类的边缘会被标注员随手画成多边形训练完掩膜边界全是锯齿。{ info: { description: multispectral leaf segmentation dataset, channels: 5, width: 1280, height: 960 }, categories: [ {id: 0, name: normal_leaf, supercategory: leaf}, {id: 1, name: diseased_leaf, supercategory: leaf}, {id: 2, name: pest_leaf, supercategory: leaf}, {id: 3, name: background, supercategory: background} ], annotations: [ { id: 1, image_id: 1, category_id: 1, segmentation: [[310, 210, 315, 260, 355, 250, 360, 205]], area: 2670.5 } ] }上面这个JSON是COCO格式标注的最小骨架。segmentation字段是polygon坐标点按先x后y排列闭合点不重复。标注完成后要统计类别平衡度田间数据非常容易出现正常叶片占80%、虫害叶片只有5%的长尾分布这时不要急着训练先用基于类别的加权裁剪增强把少数类的小图在每轮随机多采样几次。这一步不做好模型会乖乖把所有叶片全预测成正常叶片。2.3 从5通道TIFF到YOLOv11能吃的输入预处理三步走多光谱相机导出的一般是16位TIFFYOLOv11默认接3通道8位图直接硬喂会出问题。第一步做辐射定标把DN值乘以相机出厂标定系数转成反射率第二步做波段拉伸把五个波段分别截取2%到98%的直方图分位点线性映射到0到255。这里有个我踩过的坑如果五个波段各自独立拉伸会破坏波段间的相对亮度关系红边和近红外的数值域会被拉跑。正确做法是先按NIR波段的统计量算出一个全局映射再把这个映射套用到所有波段。import cv2 import numpy as np from glob import glob # 5波段TIFF读取和全局拉伸 def load_and_stretch(tiff_path, percent2): img cv2.imread(tiff_path, cv2.IMREAD_UNCHANGED) # 16位 bands np.split(img, 5, axis-1) # 假设TIFF按波段存储 # 以NIR波段为基准计算全局分位数 nir bands[4].astype(np.float32) low np.percentile(nir, percent) high np.percentile(nir, 100 - percent) stretched [] for b in bands: b np.clip((b.astype(np.float32) - low) / (high - low), 0, 1) stretched.append((b * 255).astype(np.uint8)) return np.concatenate(stretched, axis-1) # H, W, 5 data load_and_stretch(field_001.tiff) print(data.shape, data.dtype) # (960, 1280, 5) uint8这段代码的要点在全局拉伸。percent取2意味着用2%到98%的分位数压制极端反光点反光叶片上的镜面反射不会把动态范围全部吃掉。最终输出的5通道数组前3通道是RGB后2通道是RedEdge和NIR。训练增强上要注意不要给多光谱通道做RandomBrightnessContrast波段间的比例关系太敏感我只用几何增强旋转、翻转、随机缩放0.8到1.2倍。颜色类增强只对RGB部分做轻微HSV扰动RedEdge和NIR不参与。3. 跑通YOLOv11叶片分割网络结构、环境配置和训练命令3.1 为什么选YOLOv11的分割头网络结构里藏着对田间数据的妥协YOLOv11延续了Ultralytics系列anchor-free的设计分割分支和检测分支共用主干和颈部只在Head尾部多出一条输出掩膜的卷积路径。它用C3k2模块替代了早期版本的C3模块在保持感受野的同时把计算量降下来PSA注意力机制放在深层Stage能提升分割任务对细小病斑的敏感度。这些结构上的改进带来的直接结果是在同样精度下模型体积更小在NVIDIA Jetson这类边缘设备上能把推理帧率提上去这一点对田间部署很重要。YOLOv11官方给出的分割模型从yolo11n-seg到yolo11x-seg参数覆盖从几MB到上百MB。农业现场我一般选yolo11n-seg作为起点做快速验证等确认数据没问题之后再用yolo11m-seg提精度。不要一上来就上最大的x版本多光谱五通道输入本身就把参数量放大了x版本在Jetson Nano上跑不动最后还得回头压缩。# 激活虚拟环境并安装ultralytics conda create -n agri-yolov11 python3.10 -y conda activate agri-yolov11 pip install ultralytics torch torchvision --index-url https://download.pytorch.org/whl/cu118安装时有两个注意点。一是Python版本别选3.12某些多光谱图像处理库的C扩展还没适配二是PyTorch和CUDA版本要看推理设备如果在Jetson上跑主机装CUDA 11.8版本设备端用JetPack自带的PyTorch两边版本不要强行统一。装完后用yolo predict modelyolo11n-seg.pt sourcebush.jpg跑一次自带权重确认推理链路通。3.2 最小训练命令改三个参数让模型接受5通道输入要让YOLOv11接受多光谱输入需要动两个地方模型配置里把ch从3改成5数据集配置里把nc改成4。常见的做法是直接改yaml文件不推荐在训练脚本里动态改模型通道数那样可复现性差。# leaf_seg.yaml path: /data/multispectral_leaf train: images/train val: images/val nc: 4 names: 0: normal_leaf 1: diseased_leaf 2: pest_leaf 3: background# yolo11n-seg-multispectral.yaml # 基于官方yolo11n-seg.yaml修改只改ch nc: 4 ch: 5 scales: # 模型缩放系数 n: [0.50, 0.25, 1024]# 开始训练 yolo train \ modelyolo11n-seg-multispectral.yaml \ dataleaf_seg.yaml \ epochs200 \ imgsz640 \ batch8 \ workers4 \ device0 \ optimizerSGD \ lr00.01 \ cacheram这段训练命令用SGD而不是默认的AdamW原因是多光谱数据标注成本高、数据量普遍偏小SGD配合warmup能收敛到更平滑的极小值测试集mAP一般比AdamW高1到2个点。batch设为8是因为5通道输入显存占用比同样尺寸的RGB大不少12GB显存卡上用8比较稳。cacheram把整个数据集加载进内存多光谱数据读取比RGB慢不做这个训练时会频繁等磁盘IO。imgsz先用640把流程跑通后续小目标优化再提1024。训练时打开两个观察指标mask_mAP0.5和mask_mAP0.5:0.95前者看分割轮廓的整体准确性后者看掩膜边界贴合度。田间叶片分割的验收底线一般是mAP0.5达到0.8以上、mAP0.5:0.95达到0.5以上达不到就先检查标注质量而不是盲目加训练轮数。3.3 小目标优化虫体只有十几个像素时模型为什么漏检虫害计数场景里蚜虫、蓟马这类小型害虫在640尺寸图像里只有十几个像素YOLOv11的默认检测头在最深层输出特征图下采样32倍后这些小目标几乎消失。社区里常用的解法有三个按见效速度排序切图推理、增加P2检测层、提高输入分辨率三个可以叠加用。切图推理是成本最低的优化。把1024尺寸的输入切成四张512的小图分别推理再把结果映射回原图坐标小目标相当于被放大了一倍。代价是推理时间翻四倍但对课题组联排分析离线图片来说完全值得。增加P2层要改模型结构在neck部分把第2层输出接入检测头YOLOv11的yaml文件里加一层C2PSA和对应输出节点这部分官方文档没有现成模板改起来需要看模型参数量变化来调宽度系数。# 提高分辨率重新训练通常比改结构收益更直接 yolo train \ modelleaf_seg_last.pt \ dataleaf_seg.yaml \ epochs50 \ imgsz1024 \ batch4 \ device0 \ resumeFalseimgsz从640提到1024的收益在虫害计数场景非常明显标注里的密集小虫目标会被激活更多anchor位置。注意batch要相应减半显存不足时先把workers降到2。实测经验是640到1024能让小目标的recall提升约10个百分点代价是单张推理时间增加但离线分析场景几乎不在乎。换到实时测报场景时再考虑用切图推理和TensorRT压缩拉回帧率。4. 虫害计数不靠数框从分割掩膜到密度统计的完整管线4.1 为什么不用检测框数数重叠虫体会把计数变成玄学有段时间我直接用检测分支的box数量当虫口数结果一塌糊涂。蚜虫密集聚集时几十只虫挤在一片叶子背面检测框互相重叠NMS之后只剩几个框。换成分割掩膜之后每个虫体是一个独立的连通域即使虫体之间有轻微粘连也能通过轮廓分析拆开。这就是“叶片分割虫害计数”这个组合的底层逻辑分割模型负责把虫体从叶片背景中分离出来计数交给二值图像分析不再依赖检测框。import cv2 import numpy as np def count_pests(mask, min_area8, max_area800): # mask: 模型输出的二值掩膜背景为0虫体为255 mask_u8 (mask.squeeze() * 255).astype(np.uint8) # 形态学开运算去掉单像素噪点 kernel cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) cleaned cv2.morphologyEx(mask_u8, cv2.MORPH_OPEN, kernel) # 连通域分析 num, labels, stats, centroids cv2.connectedComponentsWithStats(cleaned, connectivity8) valid_count 0 valid_areas [] for i in range(1, num): area stats[i, cv2.CC_STAT_AREA] if min_area area max_area: valid_count 1 valid_areas.append(area) return valid_count, np.array(valid_areas), centroids[1:][valid_areas] # 假设pest_mask来自模型输出的分割掩膜 count, areas, cents count_pests(pest_mask) print(f检出的虫口数: {count}, 平均面积: {areas.mean():.1f} px)这段计数代码有三个关键参数。min_area8用于过滤分割噪声点多光谱红边通道有时会把土壤颗粒误分为虫体小于8像素的噪点直接丢掉max_area800用于排除粘连成片的虫团如果叶片背面虫害密集成片这个阈值要放宽后配合分水岭做区域拆分。连通域用8邻域而不用4邻域因为4邻域容易把细长的虫腿断开导致一只虫被数成两只。面积和重心的输出可以用来计算虫体大小分布蚜虫和叶蝉的体积差异能辅助区分种群。4.2 把计数结果写回推理结果保存带统计的JSON和可视化YOLOv11的推理结果默认只保存标注图但田间调查需要的是能直接导入统计分析软件的数据。常见做法是把每个叶片的病斑面积、虫口数、虫体平均面积按叶片实例聚合输出成JSON。用Ultralytics的Results对象直接取masks属性做坐标偏移映射后再跑连通域统计。from ultralytics import YOLO model YOLO(best.pt) results model.predict( sourcetest_field/, imgsz1024, conf0.35, saveTrue, save_txtTrue, projectruns/pest_count, nameexp1 ) summary [] for res in results: masks res.masks.data.cpu().numpy() # 每个实例一个掩膜 class_ids res.boxes.cls.cpu().numpy().astype(int) names res.names for i, mask in enumerate(masks): cls_id class_ids[i] if names[cls_id] ! pest_leaf: continue # 提取该实例在整图坐标系里的掩膜块 binary mask 0.5 pest_count, areas, cents count_pests(binary.astype(np.uint8)) summary.append({ image: res.path.split(/)[-1], leaf_area_px: int(binary.sum()), pest_count: int(pest_count), pest_avg_area_px: float(areas.mean()) if len(areas) else 0, }) import json with open(runs/pest_count/summary.json, w) as f: json.dump(summary, f, indent2)predict参数里conf0.35要重点说明。分割模型的mask置信度偏低默认0.25在密集虫害场景会混入大量背景噪声0.5又容易把边缘模糊的小虫整块滤掉我调试下来0.3到0.4之间最稳具体数值要按验证集调。saveTrue保存可视化结果save_txtTrue会把每个实例的归一化坐标存为txt这两个参数是排查模型输出时最常用的对照材料。如果要把计数结果绘制到原图上用Results对象的plot()方法就能拿到带掩膜的图像再用cv2.putText把虫口数写到图片左上角。这个可视化版本不是给人看的是给资方和植保专家看的他们需要一眼确认“数出来的虫和图上标的位置对得上”。4.3 验证计数精度不要只看mAP用人工计数对比模型分割mAP再高也不等于虫口数准确。我吃过这个亏掩膜mAP到了0.85人工复核时发现模型把叶片背面的虫粪颗粒当成了虫体计数虚高。正确做法是专门建一个计数验证集每张图拍完后人工数一遍和模型输出做对比用平均绝对百分比误差MAPE和决定系数R²两个指标评判。之前交付的虫情测报系统里验证标准是MAPE低于15%才算验收通过达不到就回去调min_area阈值最差情况才考虑换更大的模型。计数验证还需要划出“混淆区间”。人工数10只以下和人工数200只以上的图模型误差形态完全不同低密度主要错在漏检高密度主要错在粘连。优化时不要只用全量MAPE分密度段去看误差来源低密度段提升分辨率高密度段改善分割边界的收缩程度。这两个方向的改动是不同的混在一起调参容易两头不讨好。5. 常见问题与避坑记录五通道数据训练与部署的五个坑5.1 多光谱通道全堆进去精度反而比纯RGB更差现象把五个通道直接拼起来训练了一个百轮模型验证集mAP只有0.62回到纯RGB三通道重新训练同样数据mAP反而到了0.70。这看起来违反直觉多光谱信息明明是增强怎么成了负优化。原因训练数据量不足时五个通道的参数量把模型自由度过分放大红边和近红外通道的标注噪声也被模型完整吃进去。尤其是标注了背景类的场景土壤在不同波段的反射分布差异巨大模型学会了用波段组合模式去猜背景而不是真正理解叶片边界。解决先用RGB部分加载COCO预训练权重把多光谱新增的两个通道的输入层做权重重置然后冻结主干前10层只训练分割头和neck部分。等损失曲线平稳后解冻全部层用小学习率0.0001微调。这个策略能把多光谱的增益真正发挥出来通常比全量从头训练高出4到5个mAP点。5.2 16位TIFF直接喂进模型所有叶片预测成背景现象训练loss不下降预测结果全图都是黑色掩膜一片空白。检查输入图像发现大部分区域全黑或过曝。原因OpenCV读16位TIFF返回的dtype是uint16模型内部默认按uint8的0到255范围做归一化16位数据的数值范围是0到65535模型把所有像素当成“大于1”的异常值归一化后几乎全部压到1信息全丢。解决手写预处理脚本把五通道TIFF按2%到98%分位数拉伸成uint8再拼接成5通道输入。这个坑在2.3节的处理流程里已经解决但常有同学跳过预处理直接开始训练强烈建议训练前用cv2.imwrite输出一张RGB合成图目视检查。5.3 切图推理后小目标计数重复一个虫体被数成三只现象对大图做滑窗切图推理把小图结果拼回原图后切图边缘的虫体出现重复计数最多能重复三倍。原因切图窗口之间通常有20到50像素的重叠用来避免目标在边缘被截断。但合并结果时没有做跨图去重落在重叠区的虫体在两三张切图里各被识别一次。解决合并阶段按物体中心点坐标聚类两中心点距离小于10像素的认为是同一个虫体。实现方式是在4.1节的连通域分析上叠加一个去重循环把全部切图的检测结果先收集到同一坐标系然后按中心点做空间哈希去重。另外一个更省事的方案是让切图重叠区保持0像素把确实被截断的虫体交给后续的掩膜拼接逻辑处理但这样就损失了切图增强召回率的意义。5.4 换了个太阳角度模型预测结果大面积漂移现象上午10点采集的数据训练出的模型放到下午4点的田块上预测病斑叶片被大量识别为背景虫害叶片漏检一半。原因多光谱反射率对光照角度和太阳高度角极其敏感。上午采集时红边波段的值域整体偏高模型学到了这个统计特征下午太阳角度变化后红边值域偏移模型认为所有样本分布异常输出趋于保守。解决预测前不做任何图像增强直接调整模型推理时的对比度归一化参数把输入图像的分位数中心化到训练集统计值。根治办法是在采集阶段固定每天的采集时段比如只做上午9点到11点的窗口另外每次飞行前做白板校正。如果必须覆盖全天场景训练数据必须包含不同时段样本哪怕牺牲一部分单时段精度也要保全天鲁棒性。5.5 Jetson Nano上部署推理显存直接溢出现象在Jetson Nano 4GB版部署yolo11m-seg模型TensorRT推理时CUDA out of memory换成n分支勉强能跑但帧率只有3FPS。原因YOLOv11分割模型比检测模型多一条掩膜分支显存占用高出一大截。直接用官方PyTorch权重做推理时动态shape的显存分配没有优化4GB内存根本不够。解决导出TensorRT engine时固定输入尺寸禁止动态batch。batch_size固定为1输入尺寸固定为640而不是1024开启FP16量化。再极致一些可以把掩膜输出插值操作挪到CPU上做GPU只保留网络推理部分。实测固定shape后显存占用可以从4.6GB压到1.8GB帧率从3FPS提到15FPS配合2.3节的切图推理能勉强满足实时监测。如果目标是Rockchip这类ARM Linux平台常见做法是在yocto系统里交叉编译TensorRT或使用Rockchip的NPU工具链但别指望直接复用NVIDIA的engine文件。6. 把东西交出去系统部署的验证方式和最后一步校准模型在开发机上跑通只是完成了一半田间系统要交付的是稳定输出。我的习惯是先在室内测试集上跑全量数据确认没有类别崩溃和掩膜空洞问题后再带着便携屏和模型去种植户的大棚里做三组现场验证一组正常光照、一组遮阴、一组喷灌后叶片带水珠。带水珠的工况最容易暴露问题水珠在近红外波段恰好是高反射体会伪装成虫体边缘。部署时导出TensorRT固定shape模型之后我会额外写一个很小的校验脚本把导出前后模型的输出结果做逐通道对比允许误差控制在1e-2以内。这个步骤能抓出TensorRT对某些自定义算子的精度退化。把最后一段推理结果保存脚本里加上一张“置信度直方图”每隔30分钟统计一次输出分数分布如果分布和训练时差异明显说明现场工况已经漂移出训练数据覆盖范围系统会在后台提醒重新采集数据微调。最后一步是把虫口数换算成发生程度。植保上一般按单叶虫口数和病斑面积占比分成轻、中、重三级模型输出的绝对数值并不直接等于防治决策信号。我现在交付的系统里都会加一段归一化逻辑把虫口数除以检测叶片的总面积得到虫口密度再查分级表输出预警等级避免叶片大小差异导致的计数误判。这个细节是多个项目甲方回头找我们改需求的共同原因第一次做时很容易漏。希望这篇开发指南能帮你在自己的多光谱数据集上少走弯路从模型训练到最终交付都能稳一点。本文还有配套的精品资源点击获取