当前位置: 首页 > news >正文

基于YOLO目标检测模型的视频推理GUI工具

功能:

        在YOLO目标检测推理测试过程中,频繁修改视频路径和模型路径会带来不便。以下方法可以简化这一流程,避免重复操作。

        通过GUI页面动态设置路径,减少硬编码带来的麻烦。这种方法提高了代码的灵活性和可维护性,这样可以避免每次测试时手动修改源代码。

使用方法:

        将下方代码复制后直接执行,程序会弹出交互界面。在界面中选择权重文件、视频文件及保存路径(可选设置置信度阈值),即可开始推理。

功能说明

  • 权重文件:加载训练好的模型权重
  • 视频文件:指定待处理的输入视频
  • 保存路径:自定义结果输出位置
  • 置信度:可调节检测结果的置信度阈值(默认0.5)
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import os, sys, threading, subprocess, cv2
from pathlib import Path
from ultralytics import YOLOclass App(tk.Tk):def __init__(self):super().__init__()self.title("YOLOv11 视频推理(实时显示)")self.geometry("540x360")self.resizable(False, False)# ---------- 权重 ----------tk.Label(self, text="权重文件 (.pt / .onnx):").place(x=20, y=20)self.ent_w = tk.Entry(self, width=45)self.ent_w.place(x=180, y=20)tk.Button(self, text="浏览", command=lambda: self.browse(self.ent_w, "pt_onnx")).place(x=460, y=16)# ---------- 视频文件 ----------tk.Label(self, text="视频文件:").place(x=20, y=60)self.ent_i = tk.Entry(self, width=45)self.ent_i.place(x=180, y=60)tk.Button(self, text="浏览", command=lambda: self.browse(self.ent_i, "video")).place(x=460, y=56)# ---------- 输出文件夹 ----------tk.Label(self, text="结果保存到:").place(x=20, y=100)self.ent_o = tk.Entry(self, width=45)self.ent_o.place(x=180, y=100)tk.Button(self, text="浏览", command=lambda: self.browse(self.ent_o, "dir")).place(x=460, y=96)# ---------- 置信度 ----------tk.Label(self, text="置信度阈值:").place(x=20, y=140)self.scale_conf = tk.Scale(self, from_=0.01, to=1.0, resolution=0.01,orient=tk.HORIZONTAL, length=300)self.scale_conf.set(0.35)self.scale_conf.place(x=180, y=120)# ---------- 复选框 ----------self.var_box = tk.BooleanVar(value=True)tk.Checkbutton(self, text="在结果上画框", variable=self.var_box).place(x=20, y=180)self.var_save = tk.BooleanVar(value=False)tk.Checkbutton(self, text="保存结果视频", variable=self.var_save).place(x=220, y=180)# ---------- 运行 / 进度 ----------self.btn_run = tk.Button(self, text="开始推理", width=15, command=self.run_thread)self.btn_run.place(x=20, y=220)self.pb = ttk.Progressbar(self, length=480, mode='determinate')self.pb.place(x=20, y=260)# ---------- 日志 ----------self.txt = tk.Text(self, height=4, width=70, state="disabled")self.txt.place(x=20, y=300)# 线程退出标志self.stop_flag = False# ---------------- 工具 ----------------def browse(self, entry, kind):if kind == "pt_onnx":f = filedialog.askopenfilename(filetypes=[("权重文件", "*.pt *.onnx")])elif kind == "video":f = filedialog.askopenfilename(filetypes=[("视频文件", "*.mp4 *.avi *.mov *.mkv")])else:f = filedialog.askdirectory()if f:entry.delete(0, tk.END)entry.insert(0, f)def log(self, msg):self.txt.configure(state="normal")self.txt.insert(tk.END, msg + "\n")self.txt.see(tk.END)self.txt.configure(state="disabled")# ---------------- 推理 ----------------def run_thread(self):if not self.validate():returnself.btn_run.config(state="disabled")self.stop_flag = Falsethreading.Thread(target=self.infer, daemon=True).start()def validate(self):for e in (self.ent_w, self.ent_i, self.ent_o):if not e.get():messagebox.showerror("提示", "请完整填写路径!")return Falsereturn Truedef infer(self):try:w_path = self.ent_w.get()ext = Path(w_path).suffix.lower()if ext == '.pt':model = YOLO(w_path)elif ext == '.onnx':model = YOLO(w_path, task='detect')else:raise ValueError("权重必须是 .pt 或 .onnx")video_path = Path(self.ent_i.get())out_dir = Path(self.ent_o.get())out_dir.mkdir(parents=True, exist_ok=True)cap = cv2.VideoCapture(str(video_path))if not cap.isOpened():raise ValueError("无法打开视频文件")fps = int(cap.get(cv2.CAP_PROP_FPS))width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))fourcc = cv2.VideoWriter_fourcc(*'mp4v')save_path = out_dir / f"{video_path.stem}_result.mp4"writer = cv2.VideoWriter(str(save_path), fourcc, fps, (width, height)) if self.var_save.get() else Noneself.pb["maximum"] = total_framesself.log(f"视频总帧数:{total_frames},开始推理...")for idx in range(total_frames):if self.stop_flag:          # 用户可随时关闭窗口中断breakret, frame = cap.read()if not ret:break# 推理results = model.predict(frame, conf=self.scale_conf.get(), save=False, verbose=False)[0]if self.var_box.get():out_frame = results.plot()h, w = out_frame.shape[:2]max_h = 720  # 想要的最大高度if h > max_h:new_w = int(w * max_h / h)out_frame = cv2.resize(out_frame, (new_w, max_h))else:out_frame = results.orig_imgh, w = out_frame.shape[:2]max_h = 720  # 想要的最大高度if h > max_h:new_w = int(w * max_h / h)out_frame = cv2.resize(out_frame, (new_w, max_h))# 实时显示(支持中文路径)# cv2.namedWindow("YOLOv11 实时推理", cv2.WINDOW_NORMAL)cv2.imshow("YOLOv11 实时推理", out_frame)if cv2.waitKey(1) & 0xFF == ord('q'):   # 按 q 也可提前退出self.stop_flag = Trueif writer:writer.write(out_frame)self.pb["value"] = idx + 1if idx % fps == 0:          # 每 1 秒写一次日志,避免刷屏self.log(f"已推理 {idx + 1}/{total_frames} 帧")cap.release()if writer:writer.release()cv2.destroyAllWindows()if self.stop_flag:self.log("用户中断推理")else:self.log("推理完成!")if self.var_save.get():subprocess.Popen(f'explorer /select,"{save_path}"')messagebox.showinfo("完成", "推理结束!")except Exception as e:messagebox.showerror("错误", str(e))finally:self.btn_run.config(state="normal")if __name__ == "__main__":if getattr(sys, 'frozen', False):os.chdir(sys._MEIPASS)App().mainloop()

 

 


文章转载自:

http://1cc1539c.phxns.cn
http://Ctner5UQ.phxns.cn
http://aILZG2HY.phxns.cn
http://pgogVXK5.phxns.cn
http://rn1rHv8J.phxns.cn
http://g69ryU6C.phxns.cn
http://pCPN7Ld3.phxns.cn
http://vH8YUhXW.phxns.cn
http://LVHpIMfV.phxns.cn
http://CiKKnokI.phxns.cn
http://n55wQikj.phxns.cn
http://3Jy42qVg.phxns.cn
http://pope4JTe.phxns.cn
http://6qOSygl7.phxns.cn
http://tFjtYb5u.phxns.cn
http://ObT8JlCI.phxns.cn
http://qURowAKe.phxns.cn
http://llDFA5FH.phxns.cn
http://WSlvCJnO.phxns.cn
http://0q4g91nu.phxns.cn
http://1oHRsEMc.phxns.cn
http://190zfFyf.phxns.cn
http://02xeaR9v.phxns.cn
http://MFeTnJzw.phxns.cn
http://2CVzndFj.phxns.cn
http://xqpcxmJr.phxns.cn
http://i0RAGMMs.phxns.cn
http://JXF2ob4Q.phxns.cn
http://1MfrWcHk.phxns.cn
http://wcoSBUeG.phxns.cn
http://www.dtcms.com/a/368404.html

相关文章:

  • latex公式符号与字体
  • SQL Server事务隔离级别
  • SQL高效处理海量GPS轨迹数据:人员gps轨迹数据抽稀实战指南
  • 查询语言的进化:SQL之后,为什么是GQL?数据世界正在改变
  • 概念 | C标准库STL,C运行时库CRT
  • JAiRouter 配置文件重构纪实 ——基于单一职责原则的模块化拆分与内聚性提升
  • ZooKeeper架构深度解析:分布式协调服务的核心设计与实现
  • ResNet 迁移学习---加速深度学习模型训练
  • Django REST framework:SimpleRouter 使用指南
  • Vue3 频率范围输入失焦自动校验实现
  • 删除元素(不是删除而是覆盖)快慢指针 慢指针是覆盖位置,快指针找元素
  • 代码随想录算法训练营第三天| 链表理论基础 203.移除链表元素 707.设计链表 206.反转链表
  • 结合机器学习的Backtrader跨市场交易策略研究
  • 前端开发vscode插件 - live server
  • 码农的“必修课”:深度解析Rust的所有权系统(与C++内存模型对比)
  • 【Python基础】 17 Rust 与 Python 运算符对比学习笔记
  • 云手机可以息屏挂手游吗?
  • 会话管理巅峰对决:Spring Web中Cookie-Session、JWT、Spring Session + Redis深度秘籍
  • 腾讯云大模型训练平台
  • iPhone17全系优缺点分析,加持远程控制让你的手机更好用!
  • 数据泄露危机逼近:五款电脑加密软件为企业筑起安全防线
  • 阿里云vs腾讯云按量付费服务器
  • DocuAI深度测评:自动文档生成工具如何高效产出规范API文档与数据库表结构文档?
  • React JSX 语法讲解
  • 工厂办公环境如何实现一台服务器多人共享办公
  • 从 0 到 1 学 sed 与 awk:Linux 文本处理的两把 “瑞士军刀”
  • VNC连接服务器实现远程桌面-针对官方给的链接已经失效问题
  • 【Web】理解CSS媒体查询
  • 编写前端发布脚本
  • 无密码登录与设备信任:ABP + WebAuthn/FIDO2