资讯详情

资讯详情

基于YOLOv12的茶叶病害检测系统全流程实现

茶叶病害检测这事在茶园管理里一直是个老大难。病害靠肉眼判新茶农看不出早期病斑老师傅看得准但一天也巡查不了几片茶园等病害大面积蔓延再打药产量和品相都救不回来。所以这两年做农业智能化的项目目标检测几乎成了标配方案。今天分享的这套“基于YOLOv12的茶叶病害检测系统”就是把深度学习里的实时目标检测YOLOv12、YOLO格式的病害数据集、带登录注册的图形界面完整串起来的可落地项目。它适合正在做毕业设计、课程设计的同学也适合想在茶园管理或农产品质量检测上引入视觉识别的开发者参考。从数据集怎么整理、模型怎么训练调参到写成一个能双击就打开的UI程序整个过程我都给你拆开讲一遍。1. 项目整体设计先想清楚要做什么再谈写代码1.1 系统功能模块怎么拆这类系统乍一看东西很多又是深度学习模型又是界面还有登录注册。但拆开看本质上是四件事训练端用标注好的YOLO格式数据集训练出检测模型接口端负责加载权重对图片或视频做推理界面端让普通人不用敲命令也能操作用户端做登录注册控制谁能使用系统。把模块分清楚开发和答辩都会顺畅很多。我建议按下面这张表来划分模块每个模块边界清楚后面改任何一部分都不影响其他地方模块核心职责推荐技术数据集模块采集图片、清洗、标注、增强、划分训练/验证集LabelImg、Roboflow、Python脚本模型训练模块加载预训练权重、训练模型、评估指标、导出权重PyTorch、YOLOv12开源仓库推理检测模块单张图片检测、视频流/摄像头实时检测OpenCV、PyTorch、ONNX Runtime用户模块注册账号、登录校验、会话状态管理SQLite、PyQt5界面逻辑UI交互模块上传图片、显示检测框、展示结果表格、状态栏反馈PyQt5、QSS样式第1章先把逻辑说通训练脚本只管读数据和跑训练不要掺和界面逻辑界面只负责调推理接口不要直接改模型参数。项目规模不大也不用太纠结架构但模块分清楚之后训练模型失败时你不会怀疑是UI写错了UI出问题时也不会去翻训练代码。1.2 为什么选YOLOv12而不是YOLOv5或YOLOv8YOLOv5和YOLOv8是前几年用得最顺手的版本网上资料多到不能再多。YOLOv12发布之后我之所以愿意往新版本迁移主要是看中它在Backbone和特征融合部分引入了新一轮注意力机制的设计优化对多尺度目标的表达能力比上一代更强。直观感受是茶叶害病斑点有大有小病斑小的可能只有十几个像素如果模型对小目标的特征提取能力不行小病斑非常容易漏检YOLOv12在这方面的表现比老版本有可见提升。另外一个现实原因做课程设计和毕业设计题目里带“YOLOv12”本身就是一个亮点。评委和老师更愿意看到你跟进新模型而不是守着三年前的结构。而且YOLOv12的代码虽然基于PyTorch生态但训练入口和YOLOv8差别不大有YOLOv5或v8使用经验的人迁移成本非常低。1.3 技术栈选型背后的几个理由Python是跑PyTorch模型的默认语言YOLOv12的源码和权重基本都是Python生态这个没什么好纠结的。数据库我用SQLite而不是MySQL因为这是一个单机版桌面系统不需要独立数据库服务SQLite零配置、文件型存储随项目一起拷贝就能跑对最终用户最友好。界面层我选PyQt5而不是Web方案是因为桌面环境下打包成exe之后演示方便双击即可运行不需要搭前端服务这对很多非计算机专业的用户来说更省心。这里多提一句如果之后你想把系统部署到服务器上给手机端调用那改成Flask或FastAPI后端完全没问题检测核心代码不用动只把UI层换掉就行。这也是为什么我在设计时坚持把“模型推理”和“界面展示”拆开的原因。2. 数据集构建训练效果好的关键环节2.1 病害类别先规划清楚茶叶病害种类很多但做目标检测不建议一上来就做十几个类别一是标注成本太高二是有些病害外观相似模型容易混淆。我建议第一版集中做四类高发且症状区分度较好的病害茶饼病、茶炭疽病、茶白星病、茶云纹叶枯病。它们的典型症状如下病害名称叶片典型症状检测难度参考茶饼病叶片正面凹陷背面有白色粉状物中等茶炭疽病病斑褐色不规则后期有轮纹较易茶白星病叶片出现圆形灰白色小斑点散生偏难斑小茶云纹叶枯病病斑呈云纹状带灰褐色宽带较易为什么要以这四类起步因为它们发病频率高、在视觉上各有明显特征数据相对好找答辩时也容易讲清楚。如果你有自己茶园的一手图片后续加类别就行但标注和训练流程完全不用推翻。2.2 图片收集与标注标准数据来源上开源数据集平台上有不少茶叶病害图片可以拿来练手可以先用公开数据把整个流程跑通再考虑实地补充图片。实地拍摄的时候注意覆盖不同时间段的光照、不同拍摄距离、叶片嫩叶和老叶的不同状态最好包括叶片重叠和部分遮挡的情况否则训练出来的模型在实际场景里很容易水土不服。标注工具用LabelImg或在线标注平台都行。LabelImg保存的VOC格式需要转成YOLO格式在线标注通常直接导出YOLO格式更方便。标注时守住一条原则矩形框紧贴病害区域边缘不要为了省事把几片叶子的病斑框在一起。如果病斑特别小只有几个像素这种框模型很难学到有效特征我建议干脆删掉不要硬留。理解YOLO标注格式很重要一个标注文件里每一行是这个样子0 0.512731 0.492088 0.087876 0.112203含义是类别编号0、归一化后的中心点x坐标0.5127、中心点y坐标0.4920、归一化后的框宽0.0878、归一化后的框高0.1122。归一化指的是用实际像素坐标除以图片宽高。这种格式和VOC的左上角右下角坐标差别很大脚本转换时容易出错转换完一定要抽图验证。2.3 数据增强与数据集划分标注完成之后不要急着训练。数据集规模不够或类别不均衡时先用数据增强补救。YOLO训练框架自带增强策略比如Mosaic拼图、随机左右翻转、亮度扰动、仿射变换、HSV扰动等。Mosaic对茶叶病害这种小目标场景特别有用它把四张图拼成一张变相增加了一个batch里的目标数量小病斑在训练中被“看到”的次数更多。数据划分我用一个简单脚本实现import os import random import shutil random.seed(42) src_images datasets/source/images src_labels datasets/source/labels train_img_dir datasets/tea_disease/train/images val_img_dir datasets/tea_disease/val/images train_lab_dir datasets/tea_disease/train/labels val_lab_dir datasets/tea_disease/val/labels for d in [train_img_dir, val_img_dir, train_lab_dir, val_lab_dir]: os.makedirs(d, exist_okTrue) files [f for f in os.listdir(src_images) if f.lower().endswith((.jpg, .jpeg, .png))] random.shuffle(files) val_ratio 0.15 split int(len(files) * (1 - val_ratio)) train_files files[:split] val_files files[split:] for f in train_files: shutil.copy(os.path.join(src_images, f), os.path.join(train_img_dir, f)) label_file os.path.splitext(f)[0] .txt shutil.copy(os.path.join(src_labels, label_file), os.path.join(train_lab_dir, label_file)) for f in val_files: shutil.copy(os.path.join(src_images, f), os.path.join(val_img_dir, f)) label_file os.path.splitext(f)[0] .txt shutil.copy(os.path.join(src_labels, label_file), os.path.join(val_lab_dir, label_file)) print(ftrain: {len(train_files)}, val: {len(val_files)})我习惯留出15%左右做验证集不设独立测试集。因为茶叶病害数据集本身不大再切出一块测试集会进一步减少训练数据。验证集上的mAP指标已经能反映模型大概水平等模型部署后拿真实照片再测一轮就等于做最终测试了。2.4 训练前必须做的一次标注质量检查这是我每次训练前必走的一道工序写一个十几行的脚本从数据集中随机挑几十张图把标注框画上去保存成带框图片人眼检查。这一步能挡住大量问题比如类别编号传错、框坐标越界、框画的根本不是病害。检查时重点看两点框和病斑边缘贴不贴合类别标签和病斑对不对得上。如果发现某张图的框明显偏大模型训练时会把大量背景学进去推理时误检率会升高。很多新手训练完说模型“什么都框”第一反应是调模型但我见过最多的原因其实是标注时框太大或者类别填错了。数据的问题靠调参是救不回来的。3. 训练YOLOv12模型的关键细节与参数调优3.1 环境搭建与容易踩的依赖坑训练环境我用的是Python 3.10配合PyTorch的CUDA 11.8版本显卡驱动和PyTorch的CUDA版本必须匹配这个不匹配是最常见的启动报错原因。显存8G左右用本方案训练就已经比较舒服了完全没有显卡的话用CPU训练会慢很多我一般建议至少弄一张有8G显存的显卡。环境安装命令给一套参考conda create -n yolo12 python3.10 conda activate yolo12 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python pyqt5 labelimg这里有个很实在的提醒项目路径、数据集路径、图片文件名一律不要出现中文。PyTorch和OpenCV很多函数对中文路径兼容性不好经常报找不到文件但你看路径又明明存在。我见过太多人在这里浪费一晚上。建议所有数据集放到类似D:/tea_yolo_project/datasets这种纯英文目录下。3.2 数据配置文件与训练入口YOLOv12的框架风格和Ultralytics系列很接近训练之前需要写一个数据配置文件tea_disease.yamltrain: ./datasets/tea_disease/train val: ./datasets/tea_disease/val nc: 4 names: 0: tea_bing_bing 1: tea_tan_ju_bing 2: tea_bai_xing_bing 3: tea_yun_wen_yi_ku上面names里面的英文名是给自己看的对应标注文件里的类别编号0、1、2、3。注意标注文件里是多少yaml里的顺序就要保持一致一旦错位整个训练等于白做。检查方法很简单训练前随机画几张带框图看图上的类别名和实际病害是否一致。训练脚本用下面这个结构from ultralytics import YOLO model YOLO(yolov12x.pt) model.train( datatea_disease.yaml, epochs120, imgsz640, batch16, device0, optimizerAdamW, lr00.001, patience20, nametea_disease_v12 )这个脚本会在runs/train/tea_disease_v12下输出训练日志、曲线图和权重文件。训练到一半想中断改动数据或参数后在同样的data配置下重新执行训练命令并开启resumeTrue可以接着上次的进度继续跑。3.3 训练参数到底应该怎么调训练参数是新手问得最多的。我给你一套在茶病害场景下实测下来比较稳的初始参数参数推荐值设计理由imgsz640YOLO默认训练尺寸小病斑需要相对分辨率不建议低于416batch8~168G显存用816G显存用16出现OOM就减半epochs100~150茶病害数据集几百张时100轮基本能收敛optimizerAdamW小数据集收敛快对学习率设置相对宽容lr00.001用SGD时可以适当调大AdamW下0.001起步更稳patience15~20早停机制验证指标连续不提升就提前结束pretrained自动加载默认加载yolov12x.pt预训练权重做迁移学习batch_size不是越大越好。显存溢出时训练会直接中断所有进度可能白费。我的建议是第一次跑先用batch8确认显存占用稳定后再翻倍不要一上来就堆满。学习率如果设得过大训练刚开始loss就会跳成NaN这时候第一步就是调小学习率而不是去重装环境。3.4 训练输出指标怎么看训练过程中盯住results.png里的一组曲线比盯着终端数字直观得多。主要看四个train/loss、val/loss、metrics/mAP50(B)、metrics/mAP50-95(B)。mAP50表示预测框和真实框的重叠度超过50%就算正确的准确率mAP50-95是在多个IoU阈值下的平均值要求更严格。一次健康训练的结果mAP50应该能达到0.85以上。如果mAP50只有0.2到0.3先别急着反复调参回去查数据。我遇到过最极端的情况某个类别的样本在整份数据集里只出现十几张模型直接把它忽略掉mAP当然上不去。后来补了那个类别的图片mAP立刻回升。训练完成后保留runs/train/tea_disease_v12/weights/best.pt这就是验证集上效果最好的权重。另一个last.pt是最后一轮权重我一般只拿来续训用部署时都用best.pt。3.5 导出模型用于界面部署界面程序加载.pt权重没问题但如果想提升CPU推理速度或者给没有PyTorch环境的机器用我建议导出ONNX格式yolo export modelruns/train/tea_disease_v12/weights/best.pt formatonnx imgsz640ONNX是一种开放格式可以用ONNX Runtime在CPU上跑推理免去装PyTorch的超大依赖。导出后在界面的推理代码里加载import onnxruntime as ort session ort.InferenceSession(best.onnx) input_name session.get_inputs()[0].name outputs session.run(None, {input_name: image})桌面UI直接加载.pt会更方便因为返回的结果对象里直接带类别和坐标信息但如果你打算部署到低配机器或服务器上做并发请求ONNX这条路值得优先考虑。4. UI界面和登录注册系统的落地实现4.1 界面方案到底选PyQt5还是Web标题里明确提到了“UI界面登录注册界面”这类项目十有八九是要交一个能演示的桌面程序。PyQt5是常见选择因为它在本地直接弹窗交互打包成exe后双击就能跑不需要带起一个服务器演示环境也简单。如果你更熟悉Web技术用Flask或Flet做界面也完全没问题但需要注意部署时要同时启动后端服务演示时因为环境问题翻车的概率更高。我个人做这套系统时选了PyQt5下面的实现细节都以它为例。4.2 登录注册界面一定要做好的三件事先看数据库设计。用户表我用SQLite来存CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL, salt TEXT NOT NULL, created_at TEXT DEFAULT (datetime(now,localtime)) );密码绝对不能存明文。包里只要带上数据库文件明文密码等于直接拱手送人。我用的是“随机盐sha256”的哈希方式用户注册时生成一段随机字符串作为salt再把salt拼上明文密码做哈希库里只存哈希值和盐。校验登录的时候从库里取出对应的盐对输入密码重新哈希比对一致才放行。注册流程要处理三个常见场景用户名已存在、两次密码不一致、密码长度过短。每个情况都弹出明确的中文提示不要让用户猜。登录成功后用一个全局变量或主窗口属性记录当前登录用户名主界面顶部可以显示“当前用户xxx”这个细节在答辩时很加分。登录窗口本身是一个QDialog验证通过后accept()关闭对话框然后在主程序中实例化主窗口。如果登录失败就reject()并清空输入框。这样主窗口在没有登录成功的状态下根本不会被打开。4.3 主窗口布局和检测交互怎么设计主窗口布局我建议分成四块演示时逻辑清晰顶部工具栏打开图片、打开文件夹、摄像头、退出程序左侧缩略图区预览当前文件夹里的图片列表点击切换中间图像显示区显示原始图片和画完检测框的结果图右侧结果表格每次检测输出类别、置信度、框坐标按置信度排序界面里还需要一个状态栏实时显示模型名称、单帧推理耗时、当前帧率。这样别人一眼能看出你的系统在正常工作比静态截图有说服力得多。加载模型正确的位置是主类初始化的__init__方法里只加载一次而不是每检测一张图片时重新加载。我第一次实现的时候图省事把模型加载写进了检测函数里结果每点一次按钮都要等十几秒后来把加载挪到初始化时速度明显正常。这个坑不难避但很多人会踩。画检测框和文字时我用OpenCV的rectangle和putText中英文都测试过。如果图片瘦高、病斑又小适当放大图片再识别效果会好一点。4.4 推理线程处理不好界面一定卡死PyQt5的GUI响应必须在主线程完成但YOLO推理是个耗时操作。如果直接在按钮的点击槽函数里跑模型界面上按钮会一直处于无响应状态演示时一旦碰到一张大图整个程序看起来就像死机了。正确做法是把推理放到QThread里。我写了一个简单的检测线程from PyQt5.QtCore import QThread, pyqtSignal class DetectThread(QThread): result_ready pyqtSignal(object, object) def __init__(self, model, frame): super().__init__() self.model model self.frame frame def run(self): results self.model(self.frame) self.result_ready.emit(self.frame, results)主线程里点击检测时创建线程并把线程对象保存为成员变量防止被Python垃圾回收收到result_ready信号后在槽函数里完成绘制和表格更新。对于摄像头实时检测我额外做了丢帧处理如果上一次推理还没结束下一次取到的帧直接丢掉不加入队列。这样即使推理速度只有15帧界面也不会因为积压画面而越来越卡。5. 常见问题与排查技巧实录5.1 训练阶段最常踩的四个坑训练时最容易炸的四个问题我列成速查表现象可能原因排查与解决loss一直是NaN学习率过高数据含异常值标注坐标越界优先把lr0降到0.001以下再查标注文件显存不足OOM中断batch_size太大imgsz太高batch减半或imgsz从640降到512/416训练过早自动停止patience太小数据集太小导致指标波动调大patience到30或者关掉早停mAP非常低且loss正常类别不平衡、标注框不紧、某些类别样本太少补数据、重标注先看混淆矩阵确认哪一类训练日志里还有一张混淆矩阵图如果发现某两类病害互相误判非常严重可以考虑把这两类合并成一个大类或者检查标注时是不是把相似的病斑归错了类。比如茶炭疽病和茶云纹叶枯病在症状上有相似区间标注的时候稍不留神就会混模型自然学不明白。5.2 检测阶段效果不好怎么调漏检是常见问题。默认的置信度阈值在0.25如果模型对某些小病斑不太自信输出框会被过滤掉。这时候可以把阈值降到0.15或0.2代价是误检框可能变多。如果误检太多优先怀疑训练用的数据集里框偏大把太多背景和健康叶片包了进去。小目标检测还有一个操作技巧推理前将图片尺寸等比缩放到接近训练时的imgsz。比如训练用640图片是1200宽直接喂进去模型内部虽然也会resize但不如自己先resize再推理稳定。处理完画框时把坐标再换算回原始分辨率这样能避免“框永远偏离位置”这种诡异问题。5.3 UI和部署阶段的问题打包成exe后双击闪退是我收到问题最多的一类。闪退的常见原因不是代码逻辑错了而是打包时没有把模型权重、配置文件、可能的ONNX动态库一起打进去。建议用PyInstaller时把所有资源文件放到一个resources目录通过sys._MEIPASS和当前路径拼接来定位不要写死绝对路径。摄像头打不开也有隐藏坑。笔记本自带摄像头通常索引是0USB外接摄像头可能变成1或2有的机器甚至把索引0让给了虚拟摄像头。排查方法是写个三行脚本遍历索引0到9看哪个能出画面不要瞎猜。中文路径导致模型加载失败非常隐蔽。命令行下启动可能一切正常换成双击exe后模型路径解析失败。判断方法很简单把整个项目拷到一个纯英文路径下再跑一遍如果恢复正常就彻底锁定了病因。5.4 我从这个项目里总结出的专属经验一是data.yaml里的路径尽量写相对路径。如果你把项目压缩包发别人对方解压到任意目录绝对路径会让对方直接跑不起来。二是训练过程中定期打开results.png曲线图比盯终端数字直观得多尤其是早停那一刻曲线能帮你判断模型是收敛了还是还没学好。三是无论调参怎么折腾都要先确认数据没问题数据质量不过关模型给不了你惊喜。6. 后续还能往哪些方向扩展做完这套系统之后扩展思路其实很多。最直接的方向是把桌面端改成Web服务用Flask或FastAPI把推理接口暴露出来前端用手机拍茶叶照片上传识别这在茶园巡管场景里非常实用。或者把模型接到无人机巡检画面里实时输出病斑的地理坐标配合GPS做一个病害分布热力图对茶园管理者的决策价值更大。如果还想在模型层面深化可以对茶白星病这种小目标单独训练一个高分辨率检测分支或者引入分类网络对检测出的病斑做严重程度分级。这些扩展都不需要推翻当前系统只需要在现有模块上做增量开发。我个人做完这套系统最大的感觉就是YOLOv12本身的检测能力很强但茶叶病害检测的瓶颈通常不在模型而在数据。花两周精修标注质量比花两周调参划算得多。技术上保持好奇动手时狠抓数据这就是这类项目最值得坚持的思路。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →