3. 基于无人机影像的作物病虫害检测

基于无人机影像的作物病虫害检测 学习卡片

任务说明

本案例面向植保无人机获取的多光谱影像,构建一个可运行的作物病虫害检测流程。输入为一张包含健康与染病区域的多光谱作物影像(红、绿、蓝、近红外四个波段),输出为标记病虫害区域的二值掩膜及可视化结果。核心目标是:通过归一化差异植被指数(NDVI)与颜色特征阈值分割,识别出因病虫害导致叶绿素含量下降的区域,并统计病虫害面积占比。

实现方案

采用Python与OpenCV实现。整体流程为:读取多光谱影像的四个波段文件 → 计算NDVI → 结合可见光颜色异常(黄化/褐变)构建联合判别条件 → 形态学去噪 → 输出掩膜与统计结果。NDVI能反映植被活力,病虫害区域通常NDVI显著低于健康区域;同时,染病叶片在可见光下呈现黄褐色,通过RGB通道比值可进一步排除土壤、阴影等非植被干扰。

多光谱病虫害检测流程 多光谱影像 R/G/B/NIR 四波段 波段对齐 裁剪/缩放 NDVI计算 (NIR-R)/(NIR+R) 联合阈值分割 NDVI + 颜色异常 形态学去噪 开运算/闭运算 检测结果输出 掩膜图 + 面积占比统计 验证观测点 掩膜与目视判读对比 阈值参数:NDVI<0.25 且 (R/B>1.3 或 G/B>1.2) 判定为病虫害区域 输入:四波段TIFF | 输出:病虫害掩膜PNG + 控制台统计

代码实现


import cv2
import numpy as np
import os

def load_multispectral(base_path):
    """读取四波段影像,返回归一化到0-1的float32数组列表"""
    bands = []
    for name in ['red', 'green', 'blue', 'nir']:
        path = os.path.join(base_path, f'{name}.tif')
        img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
        if img is None:
            raise FileNotFoundError(f'缺少波段文件: {path}')
        img = img.astype(np.float32)
        # 归一化到0-1
        max_val = img.max()
        if max_val > 0:
            img /= max_val
        bands.append(img)
    return bands  # [R, G, B, NIR]

def detect_pest_disease(red, green, blue, nir, ndvi_thresh=0.25):
    """基于NDVI和颜色特征检测病虫害区域"""
    # 计算NDVI
    denominator = nir + red + 1e-6
    ndvi = (nir - red) / denominator
    
    # 颜色异常检测:黄化/褐变叶片在RGB中R和G通道值偏高
    # 使用R/B和G/B比值识别非健康绿色
    r_over_b = red / (blue + 1e-6)
    g_over_b = green / (blue + 1e-6)
    color_anomaly = (r_over_b > 1.3) | (g_over_b > 1.2)
    
    # 联合条件:NDVI低 且 颜色异常
    pest_mask = (ndvi < ndvi_thresh) & color_anomaly
    pest_mask = pest_mask.astype(np.uint8) * 255
    
    # 形态学去噪:先开运算去除小噪点,再闭运算填充孔洞
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
    pest_mask = cv2.morphologyEx(pest_mask, cv2.MORPH_OPEN, kernel)
    pest_mask = cv2.morphologyEx(pest_mask, cv2.MORPH_CLOSE, kernel)
    
    return pest_mask, ndvi

def main():
    # 输入数据目录(需包含 red.tif, green.tif, blue.tif, nir.tif)
    data_dir = './crop_multispectral'
    
    # 加载波段
    red, green, blue, nir = load_multispectral(data_dir)
    
    # 检测病虫害
    pest_mask, ndvi = detect_pest_disease(red, green, blue, nir)
    
    # 统计结果
    total_pixels = pest_mask.size
    pest_pixels = np.count_nonzero(pest_mask)
    pest_ratio = pest_pixels / total_pixels * 100
    
    # 保存结果
    cv2.imwrite('pest_mask.png', pest_mask)
    
    # 生成可视化对比图(左:NDVI灰度,右:检测掩膜叠加)
    ndvi_display = ((ndvi + 1) / 2 * 255).astype(np.uint8)
    ndvi_color = cv2.applyColorMap(ndvi_display, cv2.COLORMAP_JET)
    
    # 在原始RGB图上叠加红色标记
    rgb_vis = np.stack([red, green, blue], axis=-1)
    rgb_vis = (rgb_vis * 255).astype(np.uint8)
    overlay = rgb_vis.copy()
    overlay[pest_mask > 0] = [0, 0, 255]  # BGR红色
    combined = np.hstack([ndvi_color, overlay])
    cv2.imwrite('detection_result.png', combined)
    
    print(f'病虫害区域像素数: {pest_pixels}')
    print(f'病虫害面积占比: {pest_ratio:.2f}%')
    print('结果文件: pest_mask.png, detection_result.png')

if __name__ == '__main__':
    main()

运行与验证

依赖安装:需要Python 3.8+、OpenCV(pip install opencv-python numpy)。

输入准备:在项目目录下创建crop_multispectral文件夹,放入四个单波段TIFF文件:red.tif、green.tif、blue.tif、nir.tif。各波段图像尺寸需一致,建议使用16位或8位无符号整数格式。

运行方式:在终端执行python pest_detection.py。

验证方法:

  1. 程序正常结束且无报错,控制台输出病虫害面积占比数值;
  2. 打开生成的pest_mask.png,白色区域应集中在作物叶片上出现黄化、枯斑的位置,土壤和阴影区域不应被大量标记;
  3. 打开detection_result.png,左侧NDVI伪彩图中蓝色/绿色区域(低NDVI)与右侧红色叠加区域应基本对应;
  4. 目视对比原始RGB影像,若检测区域与肉眼可见的病虫害症状位置吻合,且误检(如田埂、杂草)比例低于20%,则判定通过。

判定标准:检测掩膜能正确覆盖影像中明显染病的作物区域,且不把裸露土壤、灌溉沟渠等非植被区域误判为病虫害。若阈值不适用当前影像,可调整detect_pest_disease函数中的ndvi_thresh和颜色比值参数(1.3/1.2)后重新运行。