资讯动态

PyQt5+YOLOv5实战:从环境搭建到打包的桌面目标检测工具开发指南

发布时间:2026/9/9 2:11:25 来源:尧图企业网站定制
简介整合PyQt5与YOLOv5的多目标检测GUI项目面向刚接触PyQt5开发和YOLO算法的初学者提供一个现成的完整工程用于练手帮助读者快速上手图形界面开发与目标检测的联动实现。压缩包为zip格式共112个文件包含26个Python源码、33个pyc编译文件、25个YAML配置、3个模型权重pt以及界面ui文件、Shell脚本、示例图片和演示视频等整体大小83.46MB目录结构合理。其中py文件是项目核心逻辑yaml用于配置模型参数pt为预训练权重ui为Qt界面文件mp4展示运行效果便于按需查阅。该资源在CSDN已有8869人学习适合作为入门练手项目。项目重点展示了PyQt5常用控件的用法、界面设计与后端逻辑分离的思路并基于PyTorch框架集成了YOLOv5算法源码读者可从中掌握信号槽机制、布局管理以及网络结构与推理流程同时附带动画演示和mp4视频便于对照学习多目标检测的完整流程实现从算法到GUI应用的有效落地。 把yolov5跑通其实不难真正难的是怎么让检测能力变成一个别人愿意用的工具。我做了好几个目标检测相关的项目最后界面层都落在了pyqt5上pyqt5负责窗口、按钮、图表和交互yolov5负责算出目标框和置信度python把这两部分粘在一起。这套组合很适合做毕设、做内部工具、做小范围验收演示不依赖服务器本地双击就能跑。我见过太多人卡在“模型能出结果但不知道怎么在窗口里显示”这一步也见过不少人把视频检测直接写在界面线程里一启动程序就白屏转圈。这篇文章想把整套链路讲完整环境怎么搭、线程怎么设计、界面怎么开发、模型怎么训练优化、最后怎么打包成exe给别人用。适合正在用pyqt5yolov5做毕设的人也适合想把检测算法快速落地成桌面上工具的开发者和爱好者。1. 环境搭建版本匹配是省时间的第一步1.1 python版本为什么锁定3.10刚开始折腾这套组合时我直接在官网下了最新的python版本结果装yolov5依赖时一堆编译报错后来才发现很多C扩展包在最新的python上还没有对应的预编译wheel。折腾一圈下来我的建议非常明确只要你是做pyqt5yolov5python版本直接选3.10这是目前兼容性最稳的版本。为什么是3.10而不是3.11或3.12因为torch、opencv-python、pyqt5这些核心依赖在3.10上都有成熟的预编译包pip安装基本不用碰编译器。装python时记得勾选“Add Python to PATH”不然后面命令行敲python会提示找不到命令。装完在终端里输入python --version确认一下能正常输出版本号再继续。顺带说一句如果电脑上已经装了多个python版本建议给这个项目单独建一个虚拟环境。我习惯用python -m venv venv然后用venv\Scripts\activate激活。这不是矫情而是yolov5的依赖版本和别的项目经常打架尤其是opencv和numpy隔离好了一劳永逸。1.2 pyqt5安装和国内镜像加速pyqt5的安装本身没有技术难度唯一的痛点是默认源下载慢。我用的是清华源pip install pyqt5 -i https://pypi.tuna.tsinghua.edu.cn/simple安装完建议顺手验证一下能不能正常导入from PyQt5.QtWidgets import QApplication, QLabel import sys app QApplication(sys.argv) label QLabel(pyqt5 ok) label.show() sys.exit(app.exec_())能弹出一个窗口就说明环境没问题。这里要提前打个预防针运行时会遇到两个长得像的包一个是PyQt5一个是PyQt5-tools。PyQt5是核心库工具包只有在你想用Qt Designer拖界面时才需要。如果你习惯手写界面代码不装PyQt5-tools完全没问题。1.3 yolov5依赖torch版本别乱装yolov5的依赖集中在requirements.txt里官方推荐做法是克隆仓库后直接安装git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt但这里有个新手最容易踩的坑这个命令会尝试安装官方默认的torch版本如果你的显卡驱动和CUDA版本不匹配大概率会遇到torch导入失败。我的建议是先装torch再装其他依赖。如果你没有独立显卡或者只想先跑通流程直接装CPU版本最快推理速度够用来调试界面pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu如果你有NVIDIA显卡先去命令行跑nvidia-smi看CUDA版本然后到pytorch官网选择对应的安装命令。装完torch后再回来执行pip install -r requirements.txt这时候就不会重复装torch了。有个小细节yolov5的requirements里对numpy和opencv-python有版本范围要求如果安装时提示冲突优先保留yolov5要求的版本因为opencv版本太新有时会导致标注工具和推理脚本行为异常。2. 架构设计检测线程和界面线程必须分家2.1 卡死的根源是GUI事件循环被阻塞很多人的第一个版本是这样的点击按钮→读图片→调yolov5推理→把结果画到界面上。单张图片问题不大一但换成摄像头或者视频流窗口立刻卡死。原因是qt的界面程序运行在一个事件循环里鼠标点击、窗口重绘、按键响应都是事件必须排队被处理。如果你在事件循环里做了一次耗时推理一张图几十毫秒视频流每秒无数次界面就一直在“等”表现出来就是白屏、转圈、无响应。这不只是用户体验问题程序甚至会直接被系统判定为“未响应”而强制关闭。所以架构上的第一条铁律是耗时操作一律不进主线程。2.2 用QThread信号槽完成解耦我常用的做法是继承QThread写一个检测工作线程内部跑循环通过信号把检测结果发回主线程。核心代码如下from PyQt5.QtCore import QThread, pyqtSignal class DetectWorker(QThread): result_ready pyqtSignal(object, object) def __init__(self, model): super().__init__() self.model model self.running True def run(self): while self.running: frame self.get_frame() # 从摄像头或视频读取一帧 dets self.model(frame) # yolov5推理 self.result_ready.emit(frame, dets)主线程里只需要连接信号然后刷新界面self.worker.result_ready.connect(self.update_ui)注意一个容易忽略的点在槽函数里不能做耗时处理比如把检测结果保存到磁盘这种操作应该再丢给另一个线程或者用队列异步处理。槽函数只负责把图像转成QImage、画框、刷新QLabel这些操作都在毫秒级没问题。2.3 摄像头取流与跳帧策略摄像头实时检测还有一个隐藏问题视频流的帧率可能高于模型的推理速度。比如摄像头输出30帧每秒yolov5s在你的机器上只能跑10帧每秒如果每帧都推理累积的帧会越来越多延迟越来越大。我的做法是在循环里做跳帧控制。用一个计时器记录上次推理的时间间隔不到设定阈值就直接丢弃当前帧只保留最新帧用于下一次推理。这样能保证实时画面不卡检测频率稳定。如果想让检测结果更流畅可以加一个中间帧队列设置最大缓存为2避免内存无限增长。另外摄像头对象的读取也要在子线程里做不要在界面线程直接cap.read()。USB摄像头在某些驱动下读取会阻塞一旦阻塞界面同样会卡住。我在项目里的习惯是让DetectWorker内部创建并持有cv2.VideoCapture这样线程生命周期由自己管理界面只管收结果。3. 核心功能开发图片、视频、摄像头三合一3.1 图片检测与结果绘制所有检测功能的基础是图片检测。yolov5的模型接口很简单results model(img)但这里有两个坑必须解决否则你会在显示环节反复调试。第一个是letterbox预处理。yolov5会把输入图片等比例缩放到640×640不足的部分用灰色填充检测结果的坐标也是在这个缩放后的坐标系里算的。如果直接把结果坐标画到原图上框就会偏移。项目里要自己实现一个坐标映射先记录原图缩放比例和填充尺寸推理完再把xyxy坐标映射回原图坐标系。第二个是颜色通道顺序。OpenCV读取的图像是BGR顺序而QImage默认使用RGB顺序。直接转换会导致画面偏蓝偏红看起来很别扭。标准做法是rgb_image cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch rgb_image.shape qimg QImage(rgb_image.data, w, h, ch * w, QImage.Format_RGB888).copy()记住最后那个.copy()少了它QImage和numpy共享同一块内存当原数组被回收或修改时界面上的图像可能出现花屏、撕裂甚至崩溃。3.2 视频和摄像头检测的帧推送视频和摄像头的检测逻辑和图片几乎一样差别只在于数据来源是循环读取。我把这个循环放在DetectWorker的run方法里检测完直接emit。主线程收到结果后做三件事显示当前帧、绘制检测框、更新检测计数信息。这里要特别处理的是程序关闭时线程的退出。如果主窗口关掉了子线程还在跑程序会报“QThread: Destroyed while thread is still running”的错。规范流程是在窗口关闭事件里设置worker.running False然后worker.wait()等线程安全退出再真正销毁窗口。不处理这个程序偶尔会闪退而且只在退出时出现特别难排查。3.3 用下拉框做多模型切换项目里通常不会只跑一个模型比如既要用yolov5n做快速检测也要用yolov5s做精度更高的检测。用QComboBox做模型选择很自然切换时加载对应的.pt权文件即可。我见过不少人在这个功能上出问题切换模型时程序直接崩溃或者界面卡死。核心原因是模型加载是耗时操作不能写在currentIndexChanged信号对应的槽函数里。正确做法是下拉框切换只记录一个“待加载模型路径”然后在后台线程里完成卸载旧模型、加载新模型、更新状态栏提示这一整套操作。模型加载完成后子线程里的推理器要及时替换这里需要加一个线程锁或者简单的原子标志位防止旧推理还没结束就被替换掉导致内存访问错误。4. 热搜里的两个坑下拉框闪退与超链接自定义操作4.1 下拉框闪退的完整排查链路“pyqt5 下拉框闪退”这个关键词热度一直很高我也在这个坑里栽过跟头。现象是程序启动正常但一旦点击下拉框选择某个选项界面瞬间消失。我先说结论最常见的根因是槽函数访问了已经被释放的C对象。具体到yolov5项目里典型场景是你把模型实例作为属性绑定到了下拉框某个item上self.combo.addItem(模型A, userDatamodel_a)然后在槽函数里取出来用def on_change(self): model self.combo.currentData() result model(img) # 如果模型已经被销毁这里就会崩问题出在如果你在某个地方重新创建了模型旧的模型对象被Python垃圾回收但Qt对象如果模型内部封装了Qt资源仍然被界面层引用槽函数调用时访问的就是已经被释放的C资源程序直接崩溃。排查这个问题的思路我个人建议按下面的顺序来看报错信息。如果是RuntimeError: wrapped C/C object of type X has been deleted基本就是访问了已释放对象。用信号阻塞法验证。在更新下拉框数据时先调用blockSignals(True)更新完再blockSignals(False)排除信号被重复触发的问题。检查模型对象所有权。明确模型的唯一创建者和唯一销毁者不要在槽函数里重新赋值给局部变量更不要在一个新线程里直接修改主线程持有的检测器引用。修复方案我通常这样写self.combo.blockSignals(True) self.combo.clear() for name, path in model_list.items(): self.combo.addItem(name, userDatapath) self.combo.blockSignals(False)同时模型加载和替换必须放在线程里通过信号通知主线程刷新界面状态。4.2 让文本框里的链接执行自定义函数另一个高频需求是“pyqt5 文本框超链接点击后执行自定义操作”。默认情况下QLabel显示的文字里如果带了链接点击之后只会调用系统浏览器打开。但很多项目需要点链接执行自己的函数比如打开文件、切换页面、弹出自定义对话框。实现方式不复杂。先设置label.setOpenExternalLinks(False) label.linkActivated.connect(self.handle_link)关键点在于setOpenExternalLinks(False)。很多人不知道这个开关的作用它设成True时链接点击行为由Qt自己接管直接丢给默认浏览器设成False时程序才会发出linkActivated信号你才能在handle_link里按需处理。handle_link收到的参数就是点击链接的href值。我不建议在链接文本里直接拼路径因为中文和特殊字符到HTML里要做转义容易出错。更稳的做法是在链接里放一个简单的标识符比如#open_model_folder在槽函数里通过字典映射到真实操作。4.3 用QTextBrowser渲染检测结果要显示复杂的检测信息比如每个目标的类别、置信度、坐标列表用QLabel纯文本会显得很乱。我习惯用QTextBrowser配合HTML来渲染这样可以直接把yolov5的pandas结果转成表格样式看起来正规很多。你要注意setHtml和append之间的区别。setHtml会重置整个文档append是在末尾追加内容。所以在做连续检测结果展示时如果每帧都使用setHtml会造成闪烁因为界面要整体重排。我的做法是结果更新频率不高时用setHtml如果是视频流实时刷新用一个临时div加setHtml整体替换但把表格设计得尽量简单避免重排开销。另外QTextBrowser默认开启富文本如果你的检测类别名里有特殊字符记得用html.escape()做一下转义否则显示会错乱。5. 模型训练、超参数与版本选型5.1 用自己的数据集训练yolov5如果你的检测目标是特定场景比如车辆、口罩、工地安全帽直接用官方预训练权重效果大概率不理想需要用自己的数据集微调。数据组织格式yolov5已经固化得很标准了dataset/ images/ train/ val/ labels/ train/ val/标签是txt格式每行是class x_center y_center width height坐标是归一化后的。我第一次标注数据时用的是LabelImg标注完导出YOLO格式。要注意的是标注框坐标归一化要除以原图宽高很多人忘记这一点训练出来的框位置全偏。数据准备好后写一个yaml配置文件train: dataset/images/train val: dataset/images/val nc: 2 names: [person, car]然后开始训练python train.py --img 640 --batch 16 --epochs 150 --data mydata.yaml --weights yolov5s.pt --name myexp这里我建议至少100个epoch起步。很多人看到几十轮loss下降不明显就早早停了其实yolov5的mAP往往在100轮之后才稳定爬升。5.2 超参数解析与网络结构选择yolov5的超参数在data/hyps/hyp.scratch-low.yaml里核心几个值得关注lr0初始学习率0.01是比较常用的起点。如果训练loss震荡明显降到0.005试试。momentum动量默认0.937一般不用动。weight_decay权重衰减默认0.0005可以防止过拟合。batch_size显存允许范围内尽量大一点。我看很多人用8或16如果显存够用提到32反而更稳定。网络结构方面yolov5提供了n、s、m、l、x五个档位。结构上差异主要在depth_multiple和width_multiple这两个缩放系数上。n版本最小最快s是均衡型m和l精度更高但速度慢。做桌面应用或者边缘设备部署我强烈建议先用n或s版本跑通全流程后面再根据性能测试结果决定是否换更大的模型。模型参数量推理速度适用场景yolov5n最小最快树莓派、低配CPUyolov5s均衡快大多数桌面应用yolov5m / l较大较慢精度要求高的离线任务5.3 yolov5和yolov8的推理速度对比这个话题在社区里一直在讨论。我实测过同一台机器上yolov5s和yolov8s的差别结论是yolov5s在推理速度上仍然有优势yolov8s在精度上略好但差距没有想象中大。对比项yolov5syolov8s参数体量约7.2M约11.2MCPU推理延迟更低稍高检测精度中规中矩更好一些后续部署生态成熟稳定官方更新更积极如果你的项目重点在“交付”、“稳定”、“快速跑通”yolov5s依然是稳妥的选择。但如果你打算做更长期的项目考虑未来换检测头、做多任务或者蒸馏yolov8的代码结构更现代。还有一点yolov5训练后的.pt权重可以直接导出torchscript或onnx格式格式兼容性很好这对接下来的打包和边缘部署很有帮助。6. 打包分发把GUI应用变成可执行文件6.1 PyInstaller打包流程与常见报错开发完pyqt5yolov5程序后目标通常是打包成一个exe方便别人双击使用。我用的打包工具是PyInstallerpip install pyinstaller pyinstaller -w --onedir --name DetApp main.py-w表示不显示命令行控制台窗口--onedir是把程序打在目录里--onefile是打成单个文件。我更推荐--onedir因为启动速度快排查问题也方便毕竟torch和yolov5的库体积摆在那单文件打包启动时要解压到临时目录速度慢很多。打包最常见的报错是ModuleNotFoundError: No module named models以及yolov5相关模块找不到。这是因为PyInstaller的静态分析找不全动态导入的模块需要手动补hidden importspyinstaller -w --onedir --name DetApp main.py \ --hidden-import models.common \ --hidden-import models.experimental \ --collect-submodules utils这套参数用过很多次基本能解决yolov5的导入缺失问题。如果打包后运行发现自己训练的数据集yaml文件找不到记得用--add-data把配置文件和权重文件一并加入--add-data best.pt;. --add-data mydata.yaml;.6.2 体积优化和分发注意点打包出来的目录动辄几个GB这是正常的torch和cuDNN都很大。如果你不需要GPU推理只装了CPU版torch体积能少不少。想进一步减小体积可以试试UPX压缩PyInstaller自带支持但注意有些dll文件压缩后可能被杀毒软件误报。分发时有两件事必须处理一是确保目标机器上已经安装了对应的显卡驱动如果是CPU版就无所谓二是启动时给程序配置正确的环境变量路径特别是用--ondir模式时模型文件和yaml文件的相对路径要以exe所在目录为基准不要用开发时的绝对路径。我习惯在代码里这么处理import sys, os base_dir os.path.dirname(sys.executable) if getattr(sys, frozen, False) else os.path.dirname(__file__) model_path os.path.join(base_dir, best.pt)这样打包后只要把exe和权重文件放在同一级目录怎么移动都不会丢路径。最后再分享一个个人经验做这类工具先把最小的demo跑通再逐步加功能比一开始就追求大而全要省力。另外程序里一定要给子线程的异常留一个日志出口很多露出来的白屏、闪退、无响应问题本质都是线程里的异常没有被主线程看到。把这个口子留好排错效率至少提升一倍。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价