基于YOLO26的葡萄叶病害检测系统
导读
本文基于YOLO26目标检测模型,开发了一套葡萄叶病害自动检测系统,可精准识别葡萄叶片4类常见病害状态。项目包含PyQt5 GUI桌面端与Flask Web网页端双实现,支持单张图片检测、文件夹批量检测、视频检测、实时摄像头检测及结果保存五大核心功能,帮助农业种植人员快速识别葡萄叶病害,提升病害防治效率。本项目提供完整训练代码、预训练权重、UI源码和数据集配置说明,可直接部署使用,也可基于该项目二次开发,扩展至其他农业病害检测场景。
一、软件功能演示
本系统提供PyQt5 GUI桌面端和Flask Web网页端两种使用方式,核心功能包含以下5类:
演示视频
1. 单张图片检测
操作流程:点击📁 模型选择加载训练好的YOLO权重文件 → 点击🖼️ 图片检测选择本地葡萄叶图片 → 系统自动完成推理并绘制检测框,同步显示类别与置信度。
检测完成后,系统会自动统计检测结果并生成明细表格:
| 序号 | 病害类别 | 置信度 | 检测框坐标 (xmin, ymin, xmax, ymax) |
|---|---|---|---|
| 1 | Grape__BlackRot | 0.89 | (42, 61, 328, 297) |
| 2 | Grape__BlackRot | 0.76 | (152, 120, 389, 315) |
| 3 | Grape__Healthy | 0.96 | (25, 15, 410, 425) |
检测结果统计:
- 总检测数:3
- 检测类别数:2
- 主要病害:Grape__BlackRot
2. 文件夹批量检测
操作流程:将待检测的多张图片放入同一文件夹 → 点击📂 文件夹检测选择目标文件夹 → 系统批量推理所有图片,自动将检测结果保存至输出目录,并生成汇总检测报告。
批量检测适合对大量田间采集的葡萄叶样本进行统一分析,可大幅节省人工逐个检测的时间成本。
3. 视频检测
操作流程:点击🎬 视频检测选择本地葡萄叶病害采集视频 → 系统逐帧进行推理,实时在界面显示检测结果并绘制检测框与类别信息,支持暂停、停止检测操作。
4. 实时摄像头检测
操作流程:连接本地摄像头后,点击📹 实时摄像头→ 系统实时读取摄像头画面并逐帧检测,可对田间葡萄叶进行实地实时病害检测,点击⏹️ 停止检测即可结束任务。
5. 结果保存
所有检测结果均支持保存:
- 图片检测:点击
💾 保存结果,可将带检测框的结果图片保存至输出目录 - 视频/摄像头检测:系统自动将检测后的视频保存为MP4文件至输出目录
- 批量检测:自动导出包含所有检测结果的CSV报告文件
二、环境搭建
本项目基于PyTorch框架开发,提供两种环境搭建方式:
1. pip安装方式
首先手动安装PyTorch和torchvision,可从官方下载对应版本的whl包:
- torch下载地址:https://download.pytorch.org/whl/torch/
- torchvision下载地址:https://download.pytorch.org/whl/torchvision/
安装完成PyTorch后,切换至项目根目录,执行以下命令安装其余依赖:
pipinstall-rrequirements.txt项目依赖包清单如下:
aiohttp==3.14.1 albumentations==2.0.8 ax==0.52.0 clip==0.2.0 comet_ml==3.58.3 ConfigSpace==1.2.2 coremltools==9.0 dvclive==3.49.1 dx_com==2.3.0 faster_coco_eval==1.7.2 Flask==3.1.3 hub_sdk==0.0.24 ipython==8.12.3 lap==0.5.13 matplotlib==3.11.0 mct_quantizers==1.7.0 MNN==3.6.0 mobileclip==0.0.1 model_compression_toolkit==2.6.0 moviepy==2.2.12. Conda安装方式
若使用Anaconda管理环境,可通过以下步骤创建并配置环境:
# 创建新的Python环境conda create-ngrape_diseasepython=3.10conda activate grape_disease# 安装PyTorch(以CUDA 11.8为例)condainstallpytorch torchvision pytorch-cuda=11.8-cpytorch-cnvidia# 安装其余依赖pipinstall-rrequirements.txt三、模型训练、评估、预测
数据集上传说明
1. 数据集格式要求
本项目采用标准YOLO数据集格式,目录结构如下:
grape_disease_dataset/ ├── images/ │ ├── train/ # 训练集图片 │ │ ├── img1.jpg │ │ ├── img2.jpg │ │ └── ... │ ├── val/ # 验证集图片 │ │ ├── img3.jpg │ │ └── ... │ └── test/ # 测试集图片(可选) │ ├── img4.jpg │ └── ... └── labels/ ├── train/ # 训练集标签 │ ├── img1.txt │ ├── img2.txt │ └── ... ├── val/ # 验证集标签 │ ├── img3.txt │ └── ... └── test/ # 测试集标签(可选) ├── img4.txt └── ...2. YOLO格式标签说明
YOLO格式标签为每张图片对应一个同名TXT文件,每一行表示一个目标标注,格式为:
<class_id> <x_center> <y_center> <width> <height>所有坐标均为归一化坐标(取值范围0~1),示例:
0 0.428 0.357 0.512 0.468 2 0.125 0.214 0.236 0.389转换公式:
x_center = (xmin + xmax) / (2 * image_width)y_center = (ymin + ymax) / (2 * image_height)width = (xmax - xmin) / image_widthheight = (ymax - ymin) / image_height
3. 从VOC格式转换(XML转YOLO TXT)
若数据集标注为VOC格式(XML文件),可使用项目提供的xml2txt.py脚本完成格式转换,完整代码如下:
importosimportxml.etree.ElementTreeasET# 定义类别顺序,对应本项目葡萄叶病害类别categories=['Grape__BlackRot','Grape__Esca','Grape__Healthy','Grape__LeafBlight']category_to_index={category:indexforindex,categoryinenumerate(categories)}# 定义输入文件夹和输出文件夹,替换为实际路径input_folder=r'./VOCData/Annotations'# XML文件夹路径output_folder=r'./dataset/labels'# 输出TXT文件夹路径# 确保输出文件夹存在os.makedirs(output_folder,exist_ok=True)# 遍历输入文件夹中的所有XML文件forfilenameinos.listdir(input_folder):iffilename.endswith('.xml'):xml_path=os.path.join(input_folder,filename)# 解析XML文件tree=ET.parse(xml_path)root=tree.getroot()# 提取图像尺寸size=root.find('size')width=int(size.find('width').text)height=int(size.find('height').text)# 存储标签内容label_lines=[]# 遍历XML中的object标签forobjinroot.findall('object'):name=obj.find('name').textifnameincategory_to_index:category_index=category_to_index[name]else:continue# 若name不在指定类别中,跳过该objectbndbox=obj.find('bndbox')xmin=int(bndbox.find('xmin').text)ymin=int(bndbox.find('ymin').text)xmax=int(bndbox.find('xmax').text)ymax=int(bndbox.find('ymax').text)# 转换为中心点坐标和宽高(归一化)x_center=(xmin+xmax)/2.0/width y_center=(ymin+ymax)/2.0/height w=(xmax-xmin)/width h=(ymax-ymin)/height label_lines.append(f"{category_index}{x_center:.6f}{y_center:.6f}{w:.6f}{h:.6f}")# 保存标签文件txt_name=os.path.splitext(filename)[0]+'.txt'txt_path=os.path.join(output_folder,txt_name)withopen(txt_path,'w')asf:f.write('\n'.join(label_lines))print("格式转换完成!所有标签已保存到输出目录。")4. 数据集划分脚本
格式转换完成后,使用SplitDataset.py将数据集划分为训练集、验证集和测试集,默认比例为8:1:1,完整代码如下:
importosimportshutilimportrandom# 设置随机种子保证划分可复现random.seed(42)defsplit_data(image_root,label_root,output_root,train_rate=0.8,val_rate=0.1,test_rate=0.1):# 获取所有图片和标签文件名images=os.listdir(image_root)labels=os.listdir(label_root)# 匹配图片和标签(通过文件名前缀匹配)images_no_ext={os.path.splitext(img)[0]:imgforimginimages}labels_no_ext={os.path.splitext(lbl)[0]:lblforlblinlabels}# 只保留同时有图片和标签的数据matched_data=[(img_id,images_no_ext[img_id],labels_no_ext[img_id])forimg_idinimages_no_extifimg_idinlabels_no_ext]# 输出未匹配提示unmatched_images=[imgforimginimages_no_extifimgnotinlabels_no_ext]unmatched_labels=[lblforlblinlabels_no_extiflblnotinimages_no_ext]ifunmatched_images:print("未匹配标签的图片:")forimginunmatched_images:print(f"{images_no_ext[img]}")ifunmatched_labels:print("未匹配图片的标签:")forlblinunmatched_labels:print(f"{labels_no_ext[lbl]}")# 打乱数据顺序random.shuffle(matched_data)total=len(matched_data)# 划分数据集train_end=int(train_rate*total)val_end=train_end+int(val_rate*total)train_data=matched_data[:train_end]val_data=matched_data[train_end:val_end]test_data=matched_data[val_end:]# 处理训练集forimg_id,img_file,label_fileintrain_data:old_img_path=os.path.join(image_root,img_file)old_label_path=os.path.join(label_root,label_file)new_img_dir=os.path.join(output_root,'train','images')new_label_dir=os.path.join(output_root,'train','labels')os.makedirs(new_img_dir,exist_ok=True)os.makedirs(new_label_dir,exist_ok=True)shutil.copy(old_img_path,os.path.join(new_img_dir,img_file))shutil.copy(old_label_path,os.path.join(new_label_dir,label_file))# 处理验证集forimg_id,img_file,label_fileinval_data:old_img_path=os.path.join(image_root,img_file)old_label_path=os.path.join(label_root,label_file)new_img_dir=os.path.join(output_root,'val','images')new_label_dir=os.path.join(output_root,'val','labels')os.makedirs(new_img_dir,exist_ok=True)os.makedirs(new_label_dir,exist_ok=True)shutil.copy(old_img_path,os.path.join(new_img_dir,img_file))shutil.copy(old_label_path,os.path.join(new_label_dir,label_file))# 处理测试集forimg_id,img_file,label_fileintest_data:old_img_path=os.path.join(image_root,img_file)old_label_path=os.path.join(label_root,label_file)new_img_dir=os.path.join(output_root,'test','images')new_label_dir=os.path.join(output_root,'test','labels')os.makedirs(new_img_dir,exist_ok=True)os.makedirs(new_label_dir,exist_ok=True)shutil.copy(old_img_path,os.path.join(new_img_dir,img_file))shutil.copy(old_label_path,os.path.join(new_label_dir,label_file))print(f"数据集划分完成!总样本数:{total}, 训练集:{len(train_data)}, 验证集:{len(val_data)}, 测试集:{len(test_data)}")if__name__=='__main__':# 修改为实际路径image_root=r'./VOCData/JPEGImages'label_root=r'./VOCData/labels'output_root=r'./VOCData/VOdevkit'split_data(image_root,label_root,output_root)5. 配置数据集yaml文件
划分完成后,创建数据集配置yaml文件(本项目为dataset.yaml),内容如下:
# 数据集根路径path:./VOCData/VOdevkit# 替换为你的数据集实际根目录# 训练/验证/测试集相对路径train:train/images# 训练集图片路径val:val/images# 验证集图片路径test:test/images# 测试集图片路径(可选)# 类别数量nc:4# 类别名称names:0:Grape__BlackRot1:Grape__Esca2:Grape__Healthy3:Grape__LeafBlight模型训练
数据集准备完成后,执行训练脚本train.py,核心代码如下:
fromultralyticsimportYOLOif__name__=='__main__':# 加载YOLO26n预训练模型,追求更高精度可更换为yolo26s.ptmodel=YOLO('yolo26n.pt')# 开始训练results=model.train(data=r'./VOCData/VOdevkit/dataset.yaml',epochs=50,# 最大训练轮数,配合早停自动终止patience=10,# 连续10轮验证集指标无提升则触发早停batch=24,# 批次大小:12G显存建议设为24,8G显存建议改为16imgsz=512,# 输入图片尺寸workers=4,# 数据加载线程数:Windows建议4,Linux建议8device=0,# GPU设备编号,CPU训练可改为device='cpu'optimizer='SGD',# 优化器选择,SGD泛化性更优amp=True,# 开启自动混合精度训练,减少显存占用cache='disk',# 启用磁盘缓存数据,避免内存溢出save_period=11,# 每11轮保存一次权重文件scale=0.3,# 多尺度训练增强,提升模型鲁棒性rect=False,# 关闭矩形训练,配合多尺度训练使用mosaic=1.0,# 开启Mosaic数据增强)关键训练参数说明:
| 参数 | 说明 | 推荐值 |
|---|---|---|
| epochs | 最大训练轮数 | 50 |
| patience | 早停触发阈值(连续N轮验证集指标无提升则终止训练) | 10 |
| batch | 批次大小(需根据显存容量调整) | 16~24 |
| imgsz | 输入图片统一尺寸 | 512 |
| device | 训练设备(GPU编号或CPU) | 0(GPU)/ ‘cpu’(CPU) |
| amp | 是否开启自动混合精度训练(可减少显存占用) | True |
| cache | 数据集缓存方式(disk为磁盘缓存,可避免内存不足) | ‘disk’ |
执行训练命令:
python train.py模型评估
训练完成后,使用val.py对最优权重进行评估,核心代码如下:
fromultralyticsimportYOLOimportnumpyasnpfromdatetimeimportdatetime# 加载训练完成的最优权重model=YOLO(r"./runs/detect/train/weights/best.pt")# 在验证集上执行评估metrics=model.val(data=r"./VOCData/VOdevkit/dataset.yaml")# 计算平均精确率和召回率mean_p=np.mean(metrics.box.p)mean_r=np.mean(metrics.box.r)# 整理评估结果content=[]content.append("="*60)content.append(f"验证时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")content.append("="*60)content.append(f"整体 mAP50:{metrics.box.map50:.4f}")content.append(f"整体 mAP50-95:{metrics.box.map:.4f}")content.append(f"平均 Precision:{mean_p:.4f}")content.append(f"平均 Recall:{mean_r:.4f}")content.append("-"*60)content.append(f"{'类别':<16}{'Precision':<12}{'Recall':<12}{'AP50':<12}")content.append("-"*60)class_names=['Grape__BlackRot','Grape__Esca','Grape__Healthy','Grape__LeafBlight']fori,nameinenumerate(class_names):ap50=metrics.box.ap50[i]p=metrics.box.p[i]r=metrics.box.r[i]content.append(f"{name:<16}{p:.4f}{'':<4}{r:.4f}{'':<4}{ap50:.4f}")# 保存评估结果至文件withopen("val_result.txt","w",encoding="utf-8")asf:f.write("\n".join(content))# 打印评估结果print("\n".join(content))训练结果默认保存在runs/detect/train/目录下,核心输出文件说明:
| 文件/目录 | 说明 |
|---|---|
weights/best.pt | 验证集表现最优的权重文件 |
weights/last.pt | 最后一轮训练的权重文件 |
weights/epoch*.pt | 指定训练轮次的权重文件 |
args.yaml | 训练参数配置记录 |
results.csv | 每轮训练的指标数据记录 |
results.png | 训练损失曲线、指标变化曲线图 |
confusion_matrix.png | 混淆矩阵可视化图 |
confusion_matrix_normalized.png | 归一化混淆矩阵可视化图 |
BoxPR_curve.png | 精确率-召回率(PR)曲线图 |
BoxF1_curve.png | F1值变化曲线图 |
labels.jpg | 训练集标签分布可视化图 |
train_batch*.jpg | 训练批次样本数据增强效果可视化图 |
val_batch*_labels.jpg | 验证集标注数据可视化图 |
val_batch*_pred.jpg | 验证集预测结果与标注对比可视化图 |
执行评估命令:
python val.py模型预测
本项目支持多种预测方式,单张图片预测核心代码如下:
fromultralyticsimportYOLOimportos# 加载训练好的最优模型root=os.getcwd()model=YOLO(rf'{root}/runs/detect/train/weights/best.pt')# 设置待检测图片路径source=r'./img_video/test_imgs/test_grape.jpg'# 执行推理results=model.predict(source,save=True,conf=0.45,iou=0.25)# 弹窗展示检测结果results[0].show()# 保存检测结果到指定路径results[0].save(filename=r"./results/test_result.jpg")执行预测命令:
python predict.py除单张图片预测外,系统还支持文件夹批量预测、视频预测、摄像头实时预测,所有功能已集成至UI界面,可直接通过界面操作使用。
四、项目目录结构
基于YOLO26葡萄叶病害检测系统/ ├── README.md # 项目说明文档 ├── requirements.txt # Python依赖列表 ├── yolo26n.pt # YOLO26n预训练权重 ├── train.py # 模型训练入口脚本 ├── predict.py # 模型推理预测脚本 ├── val.py # 模型验证评估脚本 ├── UI/ # 界面相关代码 │ ├── web.py # Flask Web后端主程序 │ ├── GUI.py # PyQt5 GUI界面逻辑 │ ├── config.yaml # UI全局配置文件 │ ├── user.txt # 用户登录信息存储文件 │ ├── templates/ │ │ ├── index.html # Web首页 │ │ └── login.html # Web登录页 │ └── models/ │ └── best.pt # UI使用的最优模型权重 ├── img_video/ # 测试素材目录 │ ├── test_imgs/ # 测试图片文件夹 │ └── test_MP4.mp4 # 测试视频文件 ├── results/ # 推理结果输出目录 ├── runs/ # 训练输出目录 │ └── detect/train/ │ ├── args.yaml # 训练参数配置记录 │ ├── results.csv # 训练指标数据记录 │ ├── results.png # 训练指标变化曲线图 │ ├── confusion_matrix.png # 混淆矩阵可视化图 │ ├── BoxPR_curve.png # PR曲线图 │ ├── labels.jpg # 训练集标签分布可视化图 │ └── weights/ │ ├── best.pt # 最优权重文件 │ └── last.pt # 最后一轮训练权重文件 ├── VOCData/ # 数据集处理工具目录 │ ├── SplitDataset.py # 数据集划分脚本 │ ├── ViewCategory.py # VOC标注类别统计脚本 │ ├── xml2txt.py # VOC XML转YOLO TXT格式脚本 │ ├── pic to mp4.py # 图片转视频脚本 │ └── VOdevkit/ │ ├── dataset.yaml # 数据集配置文件 │ └── 数据集单独下载.txt # 数据集下载说明文件 └── ultralytics/ # YOLO框架核心源码目录 ├── cfg/ # 模型和数据集配置文件 ├── data/ # 数据加载与增强模块 ├── engine/ # 训练/预测/验证引擎模块 └── models/ # 模型定义模块五、项目README摘要
本项目是基于YOLO26目标检测模型开发的葡萄叶病害检测系统,核心特性如下:
- 支持4类葡萄叶病害识别:Black Rot、Esca、Healthy、Leaf Blight
- 提供PyQt5 GUI桌面端和Flask Web网页端双界面实现
- 支持5种检测模式:单张图片检测、文件夹批量检测、视频检测、实时摄像头检测、结果保存导出
- 提供完整的数据处理脚本、训练代码、评估代码,支持用户基于自有数据集重新训练
- 所有UI文字、参数均可通过
config.yaml全局配置,无需修改代码即可自定义系统
项目启动方式:
- 启动GUI桌面端:
cd UI && python GUI.py - 启动Web网页端:
cd UI && python web.py
文末互动区
如果您觉得本项目有帮助,欢迎点击【分享】【推荐】支持我们!
如需获取完整项目代码与数据集, 回复**「基于YOLO26葡萄叶病害」**获取。