基于作物生长图像,识别病虫害,输出病虫害类型和防治建议。

1 阅读10分钟

基于图像识别的作物病虫害智能诊断与防治系统

实际应用场景描述

在现代农业生产中,病虫害是影响作物产量和品质的主要因素之一。传统病虫害识别依赖农技专家现场观察,效率低、成本高,且易受专家经验水平影响。许多农民由于缺乏专业知识,往往无法及时准确识别病虫害,导致错失最佳防治时机,造成严重经济损失。

痛点分析

  1. 识别难度大:病虫害种类繁多,症状相似,非专业人士难以准确识别
  2. 响应不及时:从发现症状到专家诊断周期长,病虫害可能已扩散
  3. 防治不精准:盲目用药导致成本增加、环境污染和农药残留
  4. 知识获取难:防治知识分散,农民难以获取权威指导

核心逻辑

  1. 使用深度学习模型对作物图像进行特征提取
  2. 基于提取特征进行病虫害分类
  3. 根据病虫害类型匹配防治知识库
  4. 输出诊断结果和个性化防治建议

系统架构

├── app.py # 主应用程序 ├── config.py # 配置文件 ├── models/ │ ├── disease_detector.py # 病虫害检测模型 │ └── classifier.py # 分类器 ├── utils/ │ ├── image_processor.py # 图像处理工具 │ └── knowledge_base.py # 知识库模块 ├── static/ # 静态资源 ├── templates/ # HTML模板 └── README.md # 说明文档

核心代码实现

  1. 配置文件 (config.py)

""" 配置文件 - 系统参数和路径配置 """ import os from pathlib import Path

class Config: """系统配置类"""

# 基础配置
BASE_DIR = Path(__file__).parent
UPLOAD_FOLDER = BASE_DIR / 'static' / 'uploads'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'bmp'}

# 模型配置
MODEL_PATH = BASE_DIR / 'models' / 'pretrained' / 'plant_disease_model.h5'
CLASS_NAMES = [
    'healthy',  # 健康
    'powdery_mildew',  # 白粉病
    'leaf_spot',  # 叶斑病
    'aphid_infestation',  # 蚜虫侵害
    'caterpillar_damage',  # 毛虫损害
    'fungal_infection',  # 真菌感染
    'nutrient_deficiency'  # 营养缺乏
]

# 图像处理参数
IMG_SIZE = (224, 224)  # 模型输入尺寸

# 知识库配置
KNOWLEDGE_BASE_FILE = BASE_DIR / 'data' / 'disease_knowledge.json'

@classmethod
def init_directories(cls):
    """初始化必要的目录"""
    directories = [cls.UPLOAD_FOLDER]
    for directory in directories:
        directory.mkdir(parents=True, exist_ok=True)

2. 图像处理器 (utils/image_processor.py)

""" 图像处理模块 - 负责图像预处理和增强 """ import cv2 import numpy as np from PIL import Image import io

class ImageProcessor: """图像处理工具类"""

def __init__(self, img_size=(224, 224)):
    """
    初始化图像处理器
    
    参数:
        img_size: 目标图像尺寸 (宽, 高)
    """
    self.img_size = img_size
    
def load_image(self, image_path):
    """
    加载并预处理图像
    
    参数:
        image_path: 图像路径或图像字节
        
    返回:
        processed_image: 预处理后的图像
    """
    # 判断输入类型
    if isinstance(image_path, (bytes, bytearray)):
        # 从字节流加载
        image = Image.open(io.BytesIO(image_path))
        image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
    else:
        # 从文件路径加载
        image = cv2.imread(str(image_path))
    
    if image is None:
        raise ValueError("无法加载图像")
        
    return image

def preprocess(self, image):
    """
    图像预处理流程
    
    参数:
        image: 输入图像
        
    返回:
        processed: 预处理后的图像数组
    """
    # 1. 调整尺寸
    image = cv2.resize(image, self.img_size)
    
    # 2. 颜色空间转换 (BGR -> RGB)
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    
    # 3. 归一化
    image = image.astype('float32') / 255.0
    
    # 4. 数据增强(可选)
    # 这里可以添加旋转、翻转等增强操作
    
    return image

def extract_features(self, image):
    """
    提取图像特征(替代深度学习模型的简化版本)
    
    实际应用中应该使用CNN模型提取特征
    
    参数:
        image: 预处理后的图像
        
    返回:
        features: 特征向量
    """
    # 颜色特征
    mean_color = np.mean(image, axis=(0, 1))
    
    # 纹理特征(使用灰度图的LBP简化版)
    gray = cv2.cvtColor((image * 255).astype('uint8'), cv2.COLOR_RGB2GRAY)
    hist = cv2.calcHist([gray], [0], None, [256], [0, 256])
    hist = hist.flatten() / hist.sum()  # 归一化
    
    # 形状特征(边缘检测)
    edges = cv2.Canny(gray, 100, 200)
    edge_density = np.sum(edges > 0) / edges.size
    
    # 组合特征
    features = np.concatenate([
        mean_color,  # 3维
        hist[:10],  # 取前10个直方图bin
        [edge_density]  # 1维
    ])
    
    return features

def diagnose_plant_health(self, image):
    """
    基于传统图像处理的植物健康诊断(辅助方法)
    
    参数:
        image: 输入图像
        
    返回:
        health_metrics: 健康指标
    """
    # 转换为HSV颜色空间
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    
    # 定义健康叶片的颜色范围(绿色)
    lower_green = np.array([25, 40, 40])
    upper_green = np.array([90, 255, 255])
    
    # 创建掩码
    mask = cv2.inRange(hsv, lower_green, upper_green)
    
    # 计算绿色区域比例
    green_ratio = np.sum(mask > 0) / mask.size
    
    # 计算颜色变化(病害通常导致颜色变化)
    std_color = np.std(image, axis=(0, 1)).mean()
    
    return {
        'green_ratio': green_ratio,
        'color_variation': std_color,
        'is_likely_healthy': green_ratio > 0.3
    }

3. 知识库模块 (utils/knowledge_base.py)

""" 知识库模块 - 存储和管理病虫害防治知识 """ import json from pathlib import Path from typing import Dict, List, Optional

class DiseaseKnowledgeBase: """病虫害知识库管理类"""

def __init__(self, knowledge_base_file: Path):
    """
    初始化知识库
    
    参数:
        knowledge_base_file: 知识库文件路径
    """
    self.knowledge_base_file = knowledge_base_file
    self.knowledge = self._load_knowledge_base()
    
def _load_knowledge_base(self) -> Dict:
    """
    加载知识库数据
    
    返回:
        知识库字典
    """
    if not self.knowledge_base_file.exists():
        # 如果文件不存在,创建默认知识库
        return self._create_default_knowledge_base()
    
    with open(self.knowledge_base_file, 'r', encoding='utf-8') as f:
        return json.load(f)

def _create_default_knowledge_base(self) -> Dict:
    """
    创建默认病虫害知识库
    
    返回:
        默认知识库数据
    """
    knowledge_base = {
        "diseases": {
            "healthy": {
                "name": "健康植株",
                "description": "作物生长状况良好,无明显病虫害症状。",
                "symptoms": [
                    "叶片颜色鲜绿均匀",
                    "植株生长健壮",
                    "无异常斑点或虫害痕迹"
                ],
                "prevention": [
                    "保持合理种植密度",
                    "定期检查植株健康状况",
                    "保持良好通风透光"
                ],
                "treatment": "无需特别处理,维持正常管理即可。"
            },
            "powdery_mildew": {
                "name": "白粉病",
                "description": "由白粉菌引起的真菌性病害,在叶片表面形成白色粉末状霉层。",
                "symptoms": [
                    "叶片表面白色粉末状霉层",
                    "叶片褪绿、黄化",
                    "严重时叶片卷曲、干枯"
                ],
                "prevention": [
                    "选择抗病品种",
                    "合理密植,保持通风透光",
                    "避免过量施用氮肥"
                ],
                "treatment": "使用三唑酮、甲基托布津等药剂喷雾防治,7-10天一次,连续2-3次。"
            },
            "leaf_spot": {
                "name": "叶斑病",
                "description": "由真菌或细菌引起的叶片斑点类病害。",
                "symptoms": [
                    "叶片出现圆形或不规则形病斑",
                    "病斑中心灰白色,边缘褐色",
                    "严重时病斑连片,叶片枯死"
                ],
                "prevention": [
                    "及时清除病残体",
                    "实行轮作制度",
                    "避免田间积水"
                ],
                "treatment": "使用代森锰锌、多菌灵等药剂防治,注意交替用药。"
            },
            "aphid_infestation": {
                "name": "蚜虫侵害",
                "description": "蚜虫吸食植物汁液,传播病毒病,分泌蜜露诱发煤污病。",
                "symptoms": [
                    "叶片卷曲、畸形",
                    "植株生长迟缓",
                    "叶片有蜜露分泌物"
                ],
                "prevention": [
                    "设置黄色粘虫板",
                    "保护利用天敌(瓢虫、草蛉)",
                    "及时清除杂草"
                ],
                "treatment": "使用吡虫啉、噻虫嗪等内吸性药剂喷雾防治。"
            },
            "caterpillar_damage": {
                "name": "毛虫损害",
                "description": "鳞翅目幼虫取食叶片,造成缺刻或孔洞。",
                "symptoms": [
                    "叶片有缺刻、孔洞",
                    "叶缘被取食",
                    "有虫粪残留"
                ],
                "prevention": [
                    "人工捕捉幼虫",
                    "使用杀虫灯诱杀成虫",
                    "清洁田园,减少虫源"
                ],
                "treatment": "使用高效氯氟氰菊酯、阿维菌素等药剂防治。"
            }
        },
        "general_advice": {
            "monitoring": "定期检查植株,早发现早防治。",
            "prevention": "以预防为主,综合防治。",
            "environment": "保持适宜的生长环境,增强植株抗性。"
        }
    }
    
    # 保存默认知识库
    self.save_knowledge_base(knowledge_base)
    return knowledge_base

def save_knowledge_base(self, knowledge_data: Dict = None):
    """
    保存知识库到文件
    
    参数:
        knowledge_data: 知识库数据,为None时保存当前知识库
    """
    if knowledge_data is None:
        knowledge_data = self.knowledge
    
    with open(self.knowledge_base_file, 'w', encoding='utf-8') as f:
        json.dump(knowledge_data, f, ensure_ascii=False, indent=2)

def get_disease_info(self, disease_id: str) -> Optional[Dict]:
    """
    获取特定病虫害的详细信息
    
    参数:
        disease_id: 病虫害ID
        
    返回:
        病虫害信息字典,如果不存在则返回None
    """
    return self.knowledge["diseases"].get(disease_id)

def get_prevention_advice(self, disease_id: str) -> List[str]:
    """
    获取病虫害预防建议
    
    参数:
        disease_id: 病虫害ID
        
    返回:
        预防建议列表
    """
    disease_info = self.get_disease_info(disease_id)
    if disease_info:
        return disease_info.get("prevention", [])
    return []

def get_treatment_plan(self, disease_id: str) -> str:
    """
    获取病虫害治疗方案
    
    参数:
        disease_id: 病虫害ID
        
    返回:
        治疗方案文本
    """
    disease_info = self.get_disease_info(disease_id)
    if disease_info:
        return disease_info.get("treatment", "暂无具体治疗方案")
    return "未知病虫害类型"

def search_by_symptom(self, symptom: str) -> List[Dict]:
    """
    根据症状搜索可能的病虫害
    
    参数:
        symptom: 症状描述
        
    返回:
        匹配的病虫害列表
    """
    results = []
    for disease_id, info in self.knowledge["diseases"].items():
        symptoms = info.get("symptoms", [])
        # 简单关键词匹配
        if any(symptom.lower() in s.lower() for s in symptoms):
            results.append({
                "disease_id": disease_id,
                "name": info["name"],
                "match_symptoms": [s for s in symptoms if symptom.lower() in s.lower()]
            })
    return results

4. 病害检测模型 (models/disease_detector.py)

""" 病害检测模型 - 基于机器学习的病虫害分类器 """ import numpy as np from typing import Dict, Tuple, List from sklearn.ensemble import RandomForestClassifier from sklearn.preprocessing import StandardScaler import joblib import os

class PlantDiseaseDetector: """植物病害检测器"""

def __init__(self, model_path: str = None):
    """
    初始化病害检测器
    
    参数:
        model_path: 预训练模型路径
    """
    self.model = None
    self.scaler = StandardScaler()
    self.classes = None
    self.model_path = model_path
    
    if model_path and os.path.exists(model_path):
        self.load_model(model_path)
    else:
        # 初始化一个简单的随机森林模型
        self.model = RandomForestClassifier(
            n_estimators=100,
            max_depth=10,
            random_state=42
        )

def train(self, X_train: np.ndarray, y_train: np.ndarray):
    """
    训练模型
    
    参数:
        X_train: 训练特征
        y_train: 训练标签
    """
    # 数据标准化
    X_train_scaled = self.scaler.fit_transform(X_train)
    
    # 训练模型
    self.model.fit(X_train_scaled, y_train)
    self.classes = self.model.classes_
    
    # 计算训练准确率
    train_accuracy = self.model.score(X_train_scaled, y_train)
    print(f"模型训练完成,训练准确率: {train_accuracy:.2f}")
    
    # 保存模型
    if self.model_path:
        self.save_model(self.model_path)

def predict(self, features: np.ndarray) -> Tuple[str, float]:
    """
    预测病虫害类型
    
    参数:
        features: 图像特征向量
        
    返回:
        tuple: (预测类别, 置信度)
    """
    if self.model is None:
        raise ValueError("模型未训练或加载")
    
    # 数据标准化
    features_scaled = self.scaler.transform(features.reshape(1, -1))
    
    # 预测
    probabilities = self.model.predict_proba(features_scaled)[0]
    predicted_class_idx = np.argmax(probabilities)
    confidence = probabilities[predicted_class_idx]
    
    if self.classes is not None:
        predicted_class = self.classes[predicted_class_idx]
    else:
        predicted_class = str(predicted_class_idx)
    
    return predicted_class, float(confidence)

def predict_with_analysis(self, features: np.ndarray, image_metrics: Dict) -> Dict:
    """
    带分析的预测方法
    
    参数:
        features: 图像特征
        image_metrics: 图像健康指标
        
    返回:
        包含详细分析的预测结果
    """
    # 基础预测
    disease_class, confidence = self.predict(features)
    
    # 基于图像指标进行后处理
    if image_metrics.get('is_likely_healthy', False) and confidence < 0.7:
        # 如果图像看起来很健康且置信度不高,倾向于判断为健康
        if disease_class != 'healthy':
            disease_class = 'healthy'
            confidence = 0.8  # 调整置信度
    
    return {
        'disease_class': disease_class,
        'confidence': confidence,
        'health_metrics': image_metrics,
        'features_used': len(features)
    }

def save_model(self, path: str):
    """
    保存模型
    
    参数:
        path: 模型保存路径
    """
    model_data = {
        'model': self.model,
        'scaler': self.scaler,
        'classes': self.classes
    }
    joblib.dump(model_data, path)
    print(f"模型已保存到: {path}")

def load_model(self, path: str):
    """
    加载模型
    
    参数:
        path: 模型路径
    """
    model_data = joblib.load(path)
    self.model = model_data['model']
    self.scaler = model_data['scaler']
    self.classes = model_data['classes']
    print(f"模型已从 {path} 加载")

def get_feature_importance(self) -> np.ndarray:
    """
    获取特征重要性
    
    返回:
        特征重要性数组
    """
    if self.model is None:
        raise ValueError("模型未训练或加载")
    
    return self.model.feature_importances_

5. 主应用程序 (app.py)

""" 主应用程序 - 病虫害识别系统Web界面 """ from flask import Flask, render_template, request, jsonify, send_file import os from datetime import datetime import base64 from PIL import Image import io

from config import Config from utils.image_processor import ImageProcessor from models.disease_detector import PlantDiseaseDetector from utils.knowledge_base import DiseaseKnowledgeBase

初始化配置

Config.init_directories()

创建Flask应用

app = Flask(name) app.config['UPLOAD_FOLDER'] = Config.UPLOAD_FOLDER app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB限制

初始化各模块

image_processor = ImageProcessor(img_size=Config.IMG_SIZE) knowledge_base = DiseaseKnowledgeBase(Config.KNOWLEDGE_BASE_FILE)

初始化/加载模型

detector = PlantDiseaseDetector()

模拟训练数据(实际应用中应该从数据集加载)

def create_training_data(): """创建模拟训练数据(仅用于演示)""" # 在实际应用中,这里应该从真实数据集中加载 # 这里使用随机数据模拟 import numpy as np np.random.seed(42)

n_samples = 100
n_features = 14  # 与ImageProcessor中提取的特征数量一致

# 生成模拟特征
X_train = np.random.randn(n_samples, n_features)

# 生成模拟标签
n_classes = len(Config.CLASS_NAMES)
y_train = np.random.randint(0, n_classes, n_samples)

return X_train, y_train

训练模型(实际应用中应该使用预训练模型)

X_train, y_train = create_training_data() detector.train(X_train, y_train)

@app.route('/') def index(): """首页""" return render_template('index.html')

@app.route('/upload', methods=['POST']) def upload_image(): """处理图像上传""" if 'file' not in request.files: return jsonify({'error': '没有文件上传'}), 400

file = request.files['file']
if file.filename == '':
    return jsonify({'error': '未选择文件'}), 400

if file and allowed_file(file.filename):
    # 生成唯一文件名
    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
    filename = f"{timestamp}_{file.filename}"
    filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
    
    # 保存文件
    file.save(filepath)
    
    # 处理图像
    try:
        result = process_image(filepath)
        return jsonify(result)
    except Exception as e:
        return jsonify({'error': f'图像处理失败: {str(e)}'}), 500

return jsonify({'error': '不支持的文件类型'}), 400

@app.route('/analyze_base64', methods=['POST']) def analyze_base64(): """处理base64图像数据""" data = request.json if not data or 'image' not in data: return jsonify({'error': '没有图像数据'}), 400

try:
    # 解码base64图像
    image_data = base64.b64decode(data['image'].split(',')[1])
    
    # 处理图像
    result = process_image_bytes(image_data)
    return jsonify(result)
except Exception as e:
    return jsonify({'error': f'图像分析失败: {str(e)}'}), 500

def allowed_file(filename): """检查文件类型是否允许""" return '.' in filename and
filename.rsplit('.', 1)[1].lower() in Config.ALLOWED_EXTENSIONS

def process_image(image_path): """处理图像文件""" # 1. 加载和处理图像 image = image_processor.load_image(image_path)

# 2. 预处理图像
processed_image = image_processor.preprocess(image)

# 3. 提取特征
features = image_processor.extract_features(processed_image)

# 4. 获取健康指标
health_metrics = image_processor.diagnose_plant_health(image)

# 5. 预测病虫害
prediction_result = detector.predict_with_analysis(features, health_metrics)

# 6. 获取防治建议
disease_info = knowledge_base.get_disease_info(prediction_result['disease_class'])

if not disease_info:
    disease_info = {
        'name': '未知病虫害',
        'description': '无法识别该病虫害类型',
        'treatment': '请咨询专业农技人员'
    }

# 准备结果
result = {
    'status': 'success',
    'prediction': {
        'disease_class': prediction_result['disease_class'],
        'disease_name': disease_info['name'],
        'confidence': prediction_result['confidence'],
        'description': disease_info['description']
    },
    'recommendations': {
        'treatment': disease_info.get('treatment', ''),
        'prevention': disease_info.get('prevention', []),
        'symptoms': disease_info.get('symptoms', [])
    },
    'health_metrics': prediction_result['health_metrics'],
    'image_info': {
        'path': image_path,
        'size': f"{image.shape[1]}x{image.shape[0]}"
    }
}

return result

def process_image_bytes(image_bytes): """处理字节流图像""" # 直接从字节流处理 image = image_processor.load_image(image_bytes) processed_image = image_processor.preprocess(image) features = image_processor.extract_features(processed_image) health_metrics = image_processor.diagnose_plant_health(image)

prediction_result = detector.predict_with_analysis(features, health_metrics)
disease_info = knowledge_base.get_disease_info(prediction_result['disease_class'])

如果你觉得这个工具好用,欢迎关注我!