基于 YOLO11 的番茄九 类 病害智能识别系统
让一张番茄叶片照片告诉农户:“这是什么病、有多严重、怎么治。”
一、背景

1.1 行业痛点
番茄是大棚和大田里产量与产值都最重要的茄科作物之一。但它有个出了名的毛病:怕病——早疫、晚疫、叶霉、斑枯、花叶病毒、黄化曲叶病毒、红蜘蛛、潜叶蛾……每一种都能在一周内把一茬番茄的产量直接打掉一半。
而真正的痛点在田间识别:
- 农户/合作社最常见的处理方式仍然是"看叶子 → 凭经验喷药",结果误判 / 漏判很常见,错喷药不仅不治病,还伤叶;
- 一线农技员人少地广,根本跑不过来;
- 已有的"农业大模型"或多分类模型多偏图像分类,给不出"病害位置 + 严重程度 + 防治建议"这种结构化结果。
本项目用 YOLO11n 把"番茄叶片病害检测"做成一个九类目标检测 + 防治知识库的组合方案,让识别结果不只是一个标签,而是一份能直接喂给农户的"病害体检报告"。
视频演示与源码 下载

包含: 📦完整项目源码 📦预训练模型权重
🗂️数据集
1.2 项目运行效果


1.3 项目介绍
核心创新点:
| 维度 | 说明 |
|---|---|
| 病害覆盖 | 9 类常见番茄叶片病害 + 健康叶片对照,覆盖真菌、病毒、虫害三大类 |
| 知识库联动 | 内置每类病害的"症状描述 + 严重等级 + 防治建议",UI 上结果直接附带防治方案 |
| 严重程度分级 | healthy / mild / moderate / severe 四档,按等级用不同颜色高亮 |
| 多输入 | Streamlit 单图 / 批量 / 视频 / 摄像头四类输入,巡田、连拍、监控视频都能消费 |
| 行业话术 | UI 用番茄红 + 叶片绿,配合配置文件 class_severity / disease_info 自动渲染 |

九类病害:
番茄早疫病 / 番茄晚疫病 / 番茄叶霉病 / 番茄斑枯病
番茄花叶病毒病 / 番茄黄化曲叶病毒病
番茄红蜘蛛病 / 番茄潜叶蛾
健康叶片
技术栈:
| 层 | 选型 |
|---|---|
| 检测模型 | YOLO11n(ultralytics 官方,预训练 yolo11n.pt) |
| 训练框架 | ultralytics ≥ 8.3 |
| 后端推理 | Python 3.10 + PyTorch |
| 前端 | Streamlit + Plotly |
| 视频解码 | OpenCV + imageio-ffmpeg |
| 部署形态 | Streamlit / Docker;可下沉到田间巡检 PAD |
二、设计框架
┌────────────────────────────────────────────┐
│ 农户 / 农技员 / 智慧农业大屏 │
│ 单叶照片 / 田间连拍 / 巡田视频 / 摄像头 │
└─────────────┬──────────────────────────────┘
▼ HTTP(默认 8501)
┌────────────────────────────────────────────┐
│ Streamlit 应用 app/streamlit_app.py │
│ ├─ infer_image_path() 单帧推理 │
│ ├─ render_disease_info(cls) 调防治建议库 │
│ └─ render_batch_analysis() 类别 + 严重程度 │
└─────────────┬──────────────────────────────┘
▼ ultralytics.YOLO
┌────────────────────────────────────────────┐
│ YOLO11n 推理引擎 │
│ model/best.pt(9 类病害 + 健康) │
└────────────────────────────────────────────┘
YOLO11 网络结构(一图速通):
输入 640×640
│
┌───▼──── Backbone(C3k2 + SPPF) ────┐
│ 叶片纹理 / 斑点特征提取 │
└───┬───────────────────────────────┘
│
┌───▼──── Neck(FPN + PAN) ─────────┐
│ 斑点(小目标)+ 整叶(大目标)融合 │
└───┬───────────────────────────────┘
│
┌───▼── Head(Decoupled + DFL) ─────┐
│ 9 类分类 + DFL 边框回归 │
└───┬───────────────────────────────┘
│
输出:N×(x1,y1,x2,y2,conf,病害类)
为什么选 YOLO11n:番茄病害的视觉特征横跨多尺度——红蜘蛛是叶背针尖大小的小点,叶霉病是大块黄斑,潜叶蛾是细长银白色潜道,DFL 回归能让边框对这些差异极大的目标都框得准;同时 5MB 级别权重适合下沉到田间 PAD/手机。
检测流程:
叶片图 ──▶ letterbox 640 ──▶ YOLO11n 推理
│
▼
conf ≥ 0.25 + NMS(IoU 0.7)
│
▼
result.plot() 画框
│
▼
查询 disease_info[类名] → 拼出"症状+建议+严重等级"
三、代码结构(核心 50 行)
# app/streamlit_app.py(节选)
from pathlib import Path
import streamlit as st, pandas as pd, yaml
from ultralytics import YOLO
ROOT = Path(__file__).parent.parent
MODEL_PATH = ROOT / "model" / "best.pt"
CFG = yaml.safe_load(open(ROOT / "configs" / "project.yaml", encoding="utf-8"))
DISEASE_DB = CFG["app"]["disease_info"] # ① 病害知识库
SEVERITY = {
"healthy": "🌿 健康", "mild": "🟢 轻度",
"moderate": "🟠 中度", "severe": "🔴 重度"}
@st.cache_resource(show_spinner=False)
def load_model(p: str) -> YOLO:
return YOLO(p) # ② 权重只加载一次
def infer_image_path(model: YOLO, image_path: Path, conf: float):
result = model.predict(str(image_path), conf=conf, verbose=False)[0] # ③ 推理
annotated = result.plot()
rows = []
if result.boxes is not None:
for box in result.boxes:
cls_id = int(box.cls.item())
x1, y1, x2, y2 = [float(v) for v in box.xyxy[0].tolist()]
rows.append({
"class_name": result.names[cls_id],
"confidence": round(float(box.conf.item()), 4),
"x1": round(x1, 1), "y1": round(y1, 1),
"x2": round(x2, 1), "y2": round(y2, 1),
})
return annotated[:, :, ::-1], pd.DataFrame(rows) # ④ BGR→RGB + 结构化结果
def render_disease_info(class_names: list[str]) -> None: # ⑤ 防治知识
for name in dict.fromkeys(class_names): # 去重保序
info = DISEASE_DB.get(name)
if not info: continue
sev = SEVERITY.get(info["severity"], "")
st.markdown(f"### {info['icon']} {name} {sev}")
st.write(f"**症状**:{info['symptoms']}")
st.write(f"**防治建议**:{info['advice']}")
# ---------- Streamlit 入口(单图分支) ----------
st.set_page_config(page_title=CFG["app"]["title"], layout="wide")
conf = st.sidebar.slider("置信度阈值", 0.1, 0.9, 0.25, 0.05)
model = load_model(str(MODEL_PATH))
up = st.file_uploader("上传番茄叶片图片", type=["jpg", "jpeg", "png", "webp", "bmp"])
if up is not None:
annotated, df = infer_image_path(model, Path(up.name), conf)
st.image(annotated, caption="检测结果")
st.dataframe(df)
render_disease_info(df["class_name"].tolist()) # 拼出"症状 + 防治建议"
5 段解析:
DISEASE_DB—— 9 类病害的"症状 + 严重程度 + 防治建议"全部从 yaml 配置注入,业务专家修订知识库不动一行 Python 代码。@st.cache_resource—— 把 YOLO 权重缓存到会话级,手机端浏览器刷新页面响应能从 ~2s 降到几十毫秒。model.predict(...)[0]—— ultralytics 标准一行调用,自动 letterbox、NMS。- 把每个 box 拆成结构化记录,下游图表、CSV 导出、知识库联动统一基于这份 DataFrame。
render_disease_info()—— 用检测到的类名查 DISEASE_DB,把图像分类升级成"病害体检报告"——这是和普通检测项目最大的差异点。
四、训练效果
模型基于 yolo11n.pt 在 5000 张番茄叶片图(覆盖 8 类病害 + 健康对照)上训练 200 个 epoch(实际收敛在 187 epoch 附近),imgsz=640、 batch \=16。最终训练表现:
| 指标 | 数值 |
|---|---|
| Precision | 0.921 |
| Recall | 0.862 |
| mAP@0.5 | 0.944 |
| mAP@0.5:0.95 | 0.857 |
训练曲线总览:

box / cls / dfl 三路损失稳定下降,mAP 在第 100 个 epoch 后已稳定在 0.94+ 水平;mAP@0.5:0.95 也有 0.86,说明病斑边框定位质量过关。
PR 曲线:

PR 曲线在大部分 Recall 区间保持高 Precision——多数病害类别都能稳定召回;少量交叉类别(如早疫病 vs 斑枯病,斑点形态相近)会有可见的下降,符合视觉先验。
归一化混淆矩阵:

对角线整体接近 1,主要混淆集中在"早疫病 ↔ 斑枯病""叶霉病 ↔ 早疫病"两组——它们在视觉上确实非常接近,下一步可以通过加入显微特写样本、边缘高分辨率裁剪进一步压平。
五、项目总结
做对了什么:
- 从"分类"升级到"检测 + 知识库":识别完直接吐出"病在哪 + 多严重 + 怎么治",这是真正能塞给农户用的形态。
- 数据构造合理:8 病 + 1 健康 + 5000 张样本,避开了只做单一病害带来的过拟合。
- 轻量化前端:Streamlit + 配置驱动,没有花哨依赖,田间 PAD 浏览器直接跑,不需要部署原生 App。
还能怎么做:
- 加上"严重度回归"——同一种病按"轻/中/重"再细分,提高用药建议精度;
- 接入气象 / 棚内温湿度,辅助预测晚疫、灰霉等湿度敏感病害;
- 多作物迁移:同一套 YOLO11n 模板覆盖到黄瓜、辣椒、茄子等同科作物,复用 80% 代码与 UI。
一句话总结: 用 YOLO11n + Streamlit 把"番茄叶片诊病"做成一份带防治方案的体检报告,是 5MB 模型撬动十亩大棚的典型样例。