使用 Python和 FFmpeg 批量截图视频到各自文件夹中

embedded/2024/10/9 8:13:39/

在这篇博客中,我们将创建一个简单的图形用户界面 (GUI) 工具,利用 wxPythonFFmpeg 来从视频文件中批量生成截图。这个工具能够让用户选择一个文件夹,遍历其中的所有视频文件,按照视频长度将其分为四等分,然后为每个视频生成四张截图。所有生成的截图将保存在一个以视频名称命名的文件夹中,并在截图完成后自动打开该文件夹。
C:\pythoncode\new\multivideofilescreenshot.py

工具介绍
  • wxPython:用于创建桌面应用程序的图形界面。
  • FFmpeg:一个强大的多媒体处理工具,用于提取视频帧。

所有代码

import wx
import os
import subprocess
import threading
import datetime
import sysclass VideoScreenshotApp(wx.Frame):def __init__(self):wx.Frame.__init__(self, None, title="视频截图工具", size=(600, 400))# 创建面板panel = wx.Panel(self)# 创建路径选择控件self.path_label = wx.StaticText(panel, label="请选择文件夹:")self.path_textctrl = wx.TextCtrl(panel, style=wx.TE_READONLY)self.path_button = wx.Button(panel, label="选择路径")self.path_button.Bind(wx.EVT_BUTTON, self.on_select_path)# 创建文件列表控件,使用 CheckListBox 以支持多选self.file_list_ctrl = wx.CheckListBox(panel)# 创建截图按钮self.capture_button = wx.Button(panel, label="截图")self.capture_button.Bind(wx.EVT_BUTTON, self.on_capture)# 布局sizer = wx.BoxSizer(wx.VERTICAL)sizer.Add(self.path_label, 0, wx.ALL, 5)sizer.Add(self.path_textctrl, 0, wx.EXPAND | wx.ALL, 5)sizer.Add(self.path_button, 0, wx.ALL, 5)sizer.Add(self.file_list_ctrl, 1, wx.EXPAND | wx.ALL, 5)sizer.Add(self.capture_button, 0, wx.ALL | wx.ALIGN_CENTER, 5)panel.SetSizer(sizer)self.current_path = ""def on_select_path(self, event):dlg = wx.DirDialog(self, "选择文件夹", style=wx.DD_DEFAULT_STYLE)if dlg.ShowModal() == wx.ID_OK:self.current_path = dlg.GetPath()self.path_textctrl.SetValue(self.current_path)self.update_file_list()dlg.Destroy()def update_file_list(self):self.file_list_ctrl.Clear()if not self.current_path:returnfile_list = self.search_video_files(self.current_path)for filename, file_path, duration in file_list:self.file_list_ctrl.Append(f"{filename} - {str(datetime.timedelta(seconds=int(duration)))}", file_path)def search_video_files(self, directory):video_extensions = ['.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm']file_list = []for root, dirs, files in os.walk(directory):for file in files:if os.path.splitext(file)[1].lower() in video_extensions:file_path = os.path.join(root, file)duration = self.get_video_duration(file_path)file_list.append((file, file_path, duration))return file_listdef get_video_duration(self, file_path):cmd = ['ffprobe','-v', 'error','-select_streams', 'v:0','-show_entries', 'stream=duration','-of', 'default=noprint_wrappers=1:nokey=1',file_path]try:result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True)duration_str = result.stdout.strip()if not duration_str:raise ValueError("ffprobe output is empty")return float(duration_str)except subprocess.CalledProcessError as e:wx.LogError(f"ffprobe error: {e.stderr}")raiseexcept ValueError as e:wx.LogError(f"Value error: {e}")raisedef on_capture(self, event):selected_indices = self.file_list_ctrl.GetCheckedItems()if selected_indices:for index in selected_indices:file_path = self.file_list_ctrl.GetClientData(index)file_name = os.path.basename(file_path)duration = self.get_video_duration(file_path)thread = threading.Thread(target=self.capture_screenshots, args=(file_path, duration))thread.start()else:wx.MessageBox("请先选择一个或多个视频文件", "提示", wx.OK | wx.ICON_INFORMATION)def capture_screenshots(self, file_path, duration):output_dir = os.path.join(self.current_path, os.path.splitext(os.path.basename(file_path))[0])if not os.path.exists(output_dir):os.makedirs(output_dir)# 计算每张截图的时间点intervals = [duration * i / 4 for i in range(1, 5)]# 生成截图for i, timestamp in enumerate(intervals):cmd = ['ffmpeg','-ss', str(datetime.timedelta(seconds=int(timestamp))),'-i', file_path,'-vframes', '1',os.path.join(output_dir, f'screenshot_{i+1}.jpg')]subprocess.run(cmd, check=True)# 截图完成后,自动打开文件夹if sys.platform.startswith('win'):subprocess.Popen(['explorer', output_dir])elif sys.platform.startswith('darwin'):subprocess.Popen(['open', output_dir])elif sys.platform.startswith('linux'):subprocess.Popen(['xdg-open', output_dir])if __name__ == "__main__":app = wx.App(False)frame = VideoScreenshotApp()frame.Show()app.MainLoop()
代码实现

下面是我们的工具实现代码:

python">import wx
import os
import subprocess
import threading
import datetime
import sysclass VideoScreenshotApp(wx.Frame):def __init__(self):wx.Frame.__init__(self, None, title="视频截图工具", size=(600, 400))# 创建面板panel = wx.Panel(self)# 创建路径选择控件self.path_label = wx.StaticText(panel, label="请选择文件夹:")self.path_textctrl = wx.TextCtrl(panel, style=wx.TE_READONLY)self.path_button = wx.Button(panel, label="选择路径")self.path_button.Bind(wx.EVT_BUTTON, self.on_select_path)# 创建文件列表控件,使用 CheckListBox 以支持多选self.file_list_ctrl = wx.CheckListBox(panel)# 创建截图按钮self.capture_button = wx.Button(panel, label="截图")self.capture_button.Bind(wx.EVT_BUTTON, self.on_capture)# 布局sizer = wx.BoxSizer(wx.VERTICAL)sizer.Add(self.path_label, 0, wx.ALL, 5)sizer.Add(self.path_textctrl, 0, wx.EXPAND | wx.ALL, 5)sizer.Add(self.path_button, 0, wx.ALL, 5)sizer.Add(self.file_list_ctrl, 1, wx.EXPAND | wx.ALL, 5)sizer.Add(self.capture_button, 0, wx.ALL | wx.ALIGN_CENTER, 5)panel.SetSizer(sizer)self.current_path = ""def on_select_path(self, event):dlg = wx.DirDialog(self, "选择文件夹", style=wx.DD_DEFAULT_STYLE)if dlg.ShowModal() == wx.ID_OK:self.current_path = dlg.GetPath()self.path_textctrl.SetValue(self.current_path)self.update_file_list()dlg.Destroy()def update_file_list(self):self.file_list_ctrl.Clear()if not self.current_path:returnfile_list = self.search_video_files(self.current_path)for filename, file_path, duration in file_list:self.file_list_ctrl.Append(f"{filename} - {str(datetime.timedelta(seconds=int(duration)))}", file_path)def search_video_files(self, directory):video_extensions = ['.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm']file_list = []for root, dirs, files in os.walk(directory):for file in files:if os.path.splitext(file)[1].lower() in video_extensions:file_path = os.path.join(root, file)duration = self.get_video_duration(file_path)file_list.append((file, file_path, duration))return file_listdef get_video_duration(self, file_path):cmd = ['ffprobe','-v', 'error','-select_streams', 'v:0','-show_entries', 'stream=duration','-of', 'default=noprint_wrappers=1:nokey=1',file_path]try:result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True)duration_str = result.stdout.strip()if not duration_str:raise ValueError("ffprobe output is empty")return float(duration_str)except subprocess.CalledProcessError as e:wx.LogError(f"ffprobe error: {e.stderr}")raiseexcept ValueError as e:wx.LogError(f"Value error: {e}")raisedef on_capture(self, event):selected_indices = self.file_list_ctrl.GetCheckedItems()if selected_indices:for index in selected_indices:file_path = self.file_list_ctrl.GetClientData(index)file_name = os.path.basename(file_path)duration = self.get_video_duration(file_path)thread = threading.Thread(target=self.capture_screenshots, args=(file_path, duration))thread.start()else:wx.MessageBox("请先选择一个或多个视频文件", "提示", wx.OK | wx.ICON_INFORMATION)def capture_screenshots(self, file_path, duration):output_dir = os.path.join(self.current_path, os.path.splitext(os.path.basename(file_path))[0])if not os.path.exists(output_dir):os.makedirs(output_dir)# 计算每张截图的时间点intervals = [duration * i / 4 for i in range(1, 5)]# 生成截图for i, timestamp in enumerate(intervals):cmd = ['ffmpeg','-ss', str(datetime.timedelta(seconds=int(timestamp))),'-i', file_path,'-vframes', '1',os.path.join(output_dir, f'screenshot_{i+1}.jpg')]subprocess.run(cmd, check=True)# 截图完成后,自动打开文件夹if sys.platform.startswith('win'):subprocess.Popen(['explorer', output_dir])elif sys.platform.startswith('darwin'):subprocess.Popen(['open', output_dir])elif sys.platform.startswith('linux'):subprocess.Popen(['xdg-open', output_dir])if __name__ == "__main__":app = wx.App(False)frame = VideoScreenshotApp()frame.Show()app.MainLoop()
代码解释
  1. 创建主窗口

    • 使用 wx.Frame 创建主窗口,添加路径选择控件、文件列表控件以及截图按钮。
  2. 路径选择

    • on_select_path 方法允许用户选择一个文件夹,并更新文件列表。
  3. 文件列表更新

    • update_file_list 方法遍历所选文件夹中的视频文件,获取每个视频的时长,并将信息显示在 CheckListBox 中。
  4. 视频时长获取

    • get_video_duration 方法使用 ffprobe 命令来获取视频时长。
  5. 截图生成

    • on_capture 方法处理截图请求,使用多线程来生成截图,以避免阻塞主线程。
    • capture_screenshots 方法使用 ffmpeg 命令生成四张截图,并将截图保存在以视频名称命名的文件夹中。
  6. 自动打开文件夹

    • 截图完成后,自动在文件浏览器中打开保存截图的文件夹。

效果如下

在这里插入图片描述

总结

通过这个工具,你可以轻松地从多个视频文件中生成截图,而无需手动操作。wxPython 提供了一个简单易用的界面,而 FFmpeg 则负责处理视频帧的提取。这个工具不仅对视频编辑工作有帮助,也为批量处理视频文件提供了极大的便利。


http://www.ppmy.cn/embedded/97941.html

相关文章

最小路径和[中等]

优质博文:IT-BLOG-CN 一、题目 给定一个包含非负整数的m x n网格grid,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 说明:每次只能向下或者向右移动一步。 示例 1: 输入:grid [[…

经纬恒润亮相第四届焉知汽车年会,功能安全赋能域控

8月初,第四届焉知汽车年会在上海举行。此次年会围绕当下智能电动汽车的热点和焦点,聚焦于智能汽车场景应用、车载通信、激光雷达、智能座舱、功能安全、电驱动系统等多个领域,汇聚了来自OEM、科技公司、零部件供应商、测试认证机构、政府院校…

VMware Esxi 7.0 安装P40显卡疑难杂症小诊断

第一章、小叙 今天安装一台X99主板的机器,操作系统是VMware Esxi 7.0,配备一张P40显卡,显卡已在Esxi硬件中识别到,但是无法安装驱动,安装完驱动之后无法分配给虚拟机,如图所示为识别的硬件。 第二章、安装显…

力扣Hot100-final关键字,常量,抽象类(模板方法设计模式),接口

(一)final关键字 (2)常量 使用static final 修饰的成员变量被称为常量 作用:;通常用于记录系统的配置信息 注意:产量命名要求:单词大写,下划线连接多个单词 产量优势…

Spark大数据分析案例

目录 案例概述环境搭建1. Spark单机环境2. Spark集群环境 数据集数据预处理 Spark作业编写提交Spark作业 数据可视化可能遇到的问题及解决方法结论 案例概述 本案例将介绍如何在单机和集群环境下使用Apache Spark进行大数据分析,最终使用Python实现数据的可视化。我…

【面试题系列Vue02】Vue Router 路由都有哪些模式?各模式之间有什么区别?

官方解析 Vue Router 路由有三种模式: hash 模式:使⽤ URL 中的 hash(即 # 后面的内容)来作为路由路径。 在这种模式下,页面不会重新加载,只会更新 hash 值,并触发路由变化,从而渲…

Electron项目依赖管理:最佳实践与常见错误

问题一 问题描述: 输入命令 pnpm add electron 后, electron 包在执行 postinstall 脚本时,尝试从网络上下载 Electron 二进制文件,但由于网络问题(如连接超时或代理设置问题),导致下载失败。 λ pnpm a…

FreeRTOS 快速入门(三)之任务管理

目录 一、任务创建与删除1、什么是任务2、创建任务3、任务的删除 二、任务优先级和 Tick1、任务优先级2、Tick3、 修改优先级 三、任务状态1、阻塞状态(Blocked)2、暂停状态(Suspended)3、就绪状态(Ready)4、状态转换 四、Delay 函数五、空闲任务及其钩子函数1、介绍2、使用钩子…