【GPT写代码】动作视频切截图研究器

目录

  • 背景
    • 源代码
  • end

背景

用python写一个windows环境运行的动作视频切截图研究器,用路径浏览的方式指定待处理的视频文件,然后点击分析按钮,再预览区域显示视频预览画面,然后拖动时间轴,可以在预览区域刷新显示相应的画面,时间轴要能够显示视频总时长和当前按钮所在位置的,输入想要截取的视频时间范围和间隔的毫秒数,最后再视频所在路径保存所有截取的视频画面图片。

遇到CV2中文路径无法保存的问题
filename = os.path.join(output_dir, f"frame_{i:04d}.jpg")
cv2.imwrite(filename, frame)
filename = os.path.normpath(os.path.join(output_dir, self.video_name+f"_{i:04d}.jpg"))

源代码

import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import cv2
import os
from PIL import Image, ImageTk
import threading
import queueclass VideoSlicerApp:def __init__(self, root):self.root = rootself.root.title("动作视频切片研究器")# 窗口设置screen_width = root.winfo_screenwidth()screen_height = root.winfo_screenheight()self.root.geometry(f"{int(screen_width * 0.7)}x{int(screen_height * 0.7)}")self.root.minsize(800, 600)# 初始化变量self.video_path = tk.StringVar()self.video_name = "video"self.cap = Noneself.total_frames = 0self.fps = 0self.current_frame = 0self.original_size = (0, 0)self.preview_size = (800, 450)self.progress_queue = queue.Queue()# 创建界面self.create_widgets()self.root.bind("<Configure>", self.on_window_resize)# 定期检查进度self.root.after(100, self.check_progress)def create_widgets(self):main_frame = ttk.Frame(self.root)main_frame.pack(fill='both', expand=True, padx=10, pady=5)# 文件选择file_frame = ttk.Frame(main_frame)file_frame.pack(fill='x', pady=5)ttk.Label(file_frame, text="视频文件:").pack(side='left')ttk.Entry(file_frame, textvariable=self.video_path, width=40).pack(side='left', padx=5, fill='x', expand=True)ttk.Button(file_frame, text="浏览", command=self.browse_file).pack(side='left')# 分析按钮self.analyze_button = ttk.Button(main_frame, text="分析视频", command=self.analyze_video)self.analyze_button.pack(pady=5)# 预览区域self.preview_container = ttk.Frame(main_frame)self.preview_container.pack(fill='both', expand=True)self.preview_label = ttk.Label(self.preview_container)self.preview_label.pack(fill='both', expand=True)# 时间轴control_frame = ttk.Frame(main_frame)control_frame.pack(fill='x', pady=5)self.time_label = ttk.Label(control_frame, text="00:00:00 / 00:00:00")self.time_label.pack(side='bottom', fill='x')self.time_scale = ttk.Scale(control_frame, from_=0, to=100, command=self.update_frame)self.time_scale.pack(fill='x', padx=5)# 参数输入param_frame = ttk.Frame(main_frame)param_frame.pack(fill='x', pady=5)ttk.Label(param_frame, text="开始时间(ms):").grid(row=0, column=0, padx=5)self.start_time = ttk.Entry(param_frame, width=10)self.start_time.grid(row=0, column=1, padx=5)ttk.Label(param_frame, text="结束时间(ms):").grid(row=0, column=2, padx=5)self.end_time = ttk.Entry(param_frame, width=10)self.end_time.grid(row=0, column=3, padx=5)ttk.Label(param_frame, text="间隔(ms):").grid(row=0, column=4, padx=5)self.interval = ttk.Entry(param_frame, width=10)self.interval.grid(row=0, column=5, padx=5)# 进度条self.progress = ttk.Progressbar(main_frame, orient='horizontal', mode='determinate')self.progress.pack(fill='x', pady=5)# 保存按钮self.save_button = ttk.Button(main_frame, text="保存切片", command=self.save_slices)self.save_button.pack(pady=5)def browse_file(self):file_path = filedialog.askopenfilename(filetypes=[("视频文件", "*.mp4 *.avi *.mov")])if file_path:self.video_path.set(file_path)def analyze_video(self):if not self.video_path.get():messagebox.showerror("错误", "请先选择视频文件")returnself.video_name = os.path.splitext(os.path.basename(repr(self.video_path.get())))[0]print(self.video_name)self.cap = cv2.VideoCapture(self.video_path.get())if not self.cap.isOpened():messagebox.showerror("错误", "无法打开视频文件")returnself.fps = self.cap.get(cv2.CAP_PROP_FPS)self.total_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))self.time_scale.config(to=self.total_frames)self.original_size = (int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)),int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))self.update_preview_size()self.show_frame(0)def update_preview_size(self):container_width = self.preview_container.winfo_width()container_height = self.preview_container.winfo_height()max_width = max(container_width - 20, 100)max_height = max(container_height - 20, 100)ratio = min(max_width / self.original_size[0], max_height / self.original_size[1])self.preview_size = (int(self.original_size[0] * ratio),int(self.original_size[1] * ratio))def on_window_resize(self, event):if self.cap and self.cap.isOpened():self.update_preview_size()self.show_frame(self.current_frame)def show_frame(self, frame_num):self.current_frame = frame_numself.cap.set(cv2.CAP_PROP_POS_FRAMES, frame_num)ret, frame = self.cap.read()if ret:frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)img = Image.fromarray(frame)img = img.resize(self.preview_size, Image.Resampling.LANCZOS)img_tk = ImageTk.PhotoImage(img)self.preview_label.config(image=img_tk)self.preview_label.image = img_tkcurrent_time = frame_num / self.fpstotal_time = self.total_frames / self.fpsself.time_label.config(text=f"{self.format_time(current_time)} / {self.format_time(total_time)}")def format_time(self, seconds):ms = int((seconds - int(seconds)) * 1000)seconds = int(seconds)h = seconds // 3600m = (seconds % 3600) // 60s = seconds % 60return f"{h:02d}:{m:02d}:{s:02d}.{ms:03d}"def update_frame(self, value):frame_num = int(float(value))self.show_frame(frame_num)def save_slices(self):if not self.cap:messagebox.showerror("错误", "请先分析视频")returntry:start = int(self.start_time.get())end = int(self.end_time.get())interval = int(self.interval.get())if start >= end or interval <= 0:raise ValueErrorexcept ValueError:messagebox.showerror("输入错误", "请输入有效的时间范围\n(开始<结束,间隔>0)")return# 禁用按钮防止重复点击self.save_button["state"] = "disabled"self.analyze_button["state"] = "disabled"# 计算帧数范围start_frame = int(start * self.fps / 1000)end_frame = int(end * self.fps / 1000)interval_frames = int(interval * self.fps / 1000)total_saves = (end_frame - start_frame) // interval_framesself.progress['maximum'] = total_savesself.progress['value'] = 0save_thread = threading.Thread(target=self.process_slices,args=(start_frame, end_frame, interval_frames))save_thread.daemon = Truesave_thread.start()def process_slices(self, start_frame, end_frame, interval_frames):try:# 使用独立视频实例cap = cv2.VideoCapture(self.video_path.get())output_dir = os.path.join(os.path.dirname(self.video_path.get()),self.video_name+"_slices")if not os.path.exists(output_dir):os.makedirs(output_dir)frame_count = 0total = (end_frame - start_frame) // interval_framesfor i in range(total):frame_num = start_frame + i * interval_framescap.set(cv2.CAP_PROP_POS_FRAMES, frame_num)ret, frame = cap.read()if ret:filename = os.path.normpath(os.path.join(output_dir, self.video_name+f"_{i:04d}.jpg"))print(filename)# cv2.imwrite(filename, frame)img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)img_pil = Image.fromarray(img)img_pil.save(filename)self.progress_queue.put(("progress", i + 1))else:self.progress_queue.put(("error", f"无法读取 {frame_num} 帧"))cap.release()self.progress_queue.put(("done", total))except Exception as e:self.progress_queue.put(("error", str(e)))def check_progress(self):try:while not self.progress_queue.empty():msg_type, msg_data = self.progress_queue.get_nowait()if msg_type == "error":messagebox.showerror("保存错误", msg_data)self.reset_controls()elif msg_type == "done":self.progress['value'] = self.progress['maximum']messagebox.showinfo("完成", f"成功保存{msg_data}张切片")self.reset_controls()elif msg_type == "progress":self.progress['value'] = msg_dataexcept queue.Empty:passself.root.after(100, self.check_progress)def reset_controls(self):self.save_button["state"] = "normal"self.analyze_button["state"] = "normal"if __name__ == "__main__":root = tk.Tk()app = VideoSlicerApp(root)root.mainloop()

end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/75689.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在 .NET 8 中使用自定义令牌身份验证掌握 SignalR Hub 安全性

最近在练习做一个 Web 开发项目&#xff0c;需要使用 WebSockets 传输数据&#xff0c;实现实时通信。这是一个 React.js 项目&#xff0c;后端是 .NET。 虽然 MSDN 提供了出色的顶级文档&#xff0c;但它通常缺少高级用例所需的低级细节。 一种这样的场景是使用自定义令牌对…

[2018][note]用于超快偏振开关和动态光束分裂的all-optical有源THz超表——

前言 类型 太赫兹 + 超表面 太赫兹 + 超表面 太赫兹+超表面 期刊 O p e n A c c e s s Open Access Open

家里网络访问Github有时候打不开,解决办法

1、修改Hosts文件修改法 通过DNS查询工具&#xff08;如&#xff09;获取最新GitHub域名解析IP修改系统hosts文件&#xff08;路径&#xff1a;C:\Windows\System32\drivers\etc\hosts&#xff09;&#xff0c;添加&#xff1a;20.205.243.166 github.com 20.27.177.113 github…

MyBatis操作数据库(1)

1. MyBatis 简介 MyBatis 是一款持久层框架&#xff0c;简化了 JDBC 的复杂操作&#xff0c;通过配置和映射文件将 Java 对象与数据库表关联。核心优势&#xff1a; 自动管理资源&#xff1a;无需手动关闭连接、释放资源。 动态 SQL&#xff1a;支持参数绑定、条件查询等。 …

ModuleNotFoundError: No module named ‘matplotlib_inline‘

ModuleNotFoundError: No module named matplotlib_inline 1. ModuleNotFoundError: No module named matplotlib_inline2. matplotlib-inlineReferences 如果你在普通的 Python 脚本或命令行中运行代码&#xff0c;那么不需要 matplotlib_inline&#xff0c;因为普通的 Python…

SSL证书自动化管理(ACME协议)工作流程介绍

SSL证书自动化管理&#xff08;ACME协议&#xff09;是一种用于自动化管理SSL/TLS证书的协议&#xff0c;以下是其详细介绍&#xff1a; 一、ACME协议概述 ACME协议由互联网安全研究小组&#xff08;ISRG&#xff09;设计开发&#xff0c;旨在实现SSL证书获取流程的自动化。通…

基于FPGA的特定序列检测器verilog实现,包含testbench和开发板硬件测试

目录 1.课题概述 2.系统测试效果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 本课题采用基于伪码匹配相关峰检测的方式实现基于FPGA的特定序列检测器verilog实现,包含testbench和开发板硬件测试。 2.系统测试效果 仿真测试 当检测到序列的时候&#xf…

#管理Node.js的多个版本

在 Windows 11 上管理 Node.js 的多个版本&#xff0c;最方便的方法是使用 nvm-windows&#xff08;Node Version Manager for Windows&#xff09;。它允许你轻松安装、切换和管理多个 Node.js 版本。 &#x1f4cc; 方法 1&#xff1a;使用 nvm-windows&#xff08;推荐 ✅&a…

【已解决】Webstorm 每次使用 git pull/push 都要输入令牌/密码登录

解决办法&#xff1a;勾上【使用凭据帮助程序】&#xff08;英文&#xff1a;Use credential helper&#xff09;

大模型架构记录13【hr agent】

一 Function calling 函数调用 from dotenv import load_dotenv, find_dotenvload_dotenv(find_dotenv())from openai import OpenAI import jsonclient OpenAI()# Example dummy function hard coded to return the same weather # In production, this could be your back…

Spring Boot向Vue发送消息通过WebSocket实现通信

注意&#xff1a;如果后端有contextPath&#xff0c;如/app&#xff0c;那么前端访问的url就是ip:port/app/ws 后端实现步骤 添加Spring Boot WebSocket依赖配置WebSocket端点和消息代理创建控制器&#xff0c;使用SimpMessagingTemplate发送消息 前端实现步骤 安装sockjs-…

【嵌入式学习5】PyQt5模块介绍、创建第一个窗口

目录 1、PyQt介绍 ①特点 ②主要组件 2、创建第一个窗口 exce_() 1、PyQt介绍 PyQt 是一个用于创建图形用户界面&#xff08;GUI&#xff09;应用程序的 Python 库&#xff0c;它是 Qt 框架的 Python 绑定。 ①特点 跨平台&#xff1a;支持多种操作系统&#xff0c;包括…

封装自己的api签名sdk

api平台接口调用&#xff0c;需要通过签名去核对是不是有效的用户&#xff0c;&#xff0c;一般会给两个key&#xff0c;acceeKey 和 secretKey,第一个相当于用户名&#xff0c;第二个相当于密钥&#xff0c;&#xff0c;&#xff0c;前端通过一定的算法&#xff0c;&#xff0…

很简单 的 将字幕生成视频的 方法

一、一键将字幕生成视频的 方法 1、下载任性动图 10.7 以上版本 2、设置背景 1&#xff09;背景大小 拉伸背景到合适大小&#xff0c;或者选择右侧比例 2&#xff09;、直接空背景&#xff0c;设置背景颜色等详细信息 3&#xff09;、或者 复制或者突然图片做背景 3、设置文…

Spring 核心技术解析【纯干货版】- XXI:Spring 第三方工具整合模块 Spring-Context-Suppor 模块精讲

在企业级开发中&#xff0c;我们经常需要与 第三方工具 进行集成&#xff0c;如 邮件发送、任务调度、缓存管理等。Spring 为此提供了 Spring-Context-Support 模块&#xff0c;它封装了多个常见的第三方工具库&#xff0c;使得开发者可以更方便地将它们集成到 Spring 项目中。…

c++柔性数组、友元、类模版

目录 1、柔性数组&#xff1a; 2、友元函数&#xff1a; 3、静态成员 注意事项 面试题&#xff1a;c/c static的作用? C语言&#xff1a; C: 为什么可以创建出 objx 4、对象与对象之间的关系 5、类模版 1、柔性数组&#xff1a; #define _CRT_SECURE_NO_WARNINGS #…

主相机绑定小地图

资源初始化&#xff1a;在类中通过 property 装饰器定义主相机、小地图相机、小地图精灵等资源属性&#xff0c;便于在编辑器中赋值。在 start 方法里&#xff0c;当确认这些资源存在后&#xff0c;创建渲染纹理并设置其大小&#xff0c;将渲染纹理与小地图相机关联&#xff0c…

linux-core分析-柔性数组越界访问

文章目录 core的调用栈core分析修改修改原因柔性数组定义代码修改总结core的调用栈 vocb core 崩溃:core的大小都是573M左右 Program terminated with signal SIGSEGV, Segmentation fault. #0 0x0000007f789af0d0 in strlen () from /lib/libc.so.6[Current thread is 1 (LW…

leetcode 代码随想录 数组-区间和

题目 给定一个整数数组 Array&#xff0c;请计算该数组在每个指定区间内元素的总和。 输入&#xff1a; 第一行输入&#xff1a;为整数数组 Array 的长度 n&#xff0c;接下来 n 行&#xff0c;每行一个整数&#xff0c;表示数组的元素。随后的输入为需要计算总和的区间&…

部署nerdctl工具

nerdctl 是一个专为Containerd设计的容器管理命令行工具&#xff0c;旨在提供类似 Docker 的用户体验&#xff0c;同时支持 Containerd 的高级特性&#xff08;如命名空间、compose等&#xff09;。 1、下载安装 wget https://github.com/containerd/nerdctl/releases/downlo…