翼度科技»论坛 编程开发 python 查看内容

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

3

主题

3

帖子

9

积分

新手上路

Rank: 1

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

工具介绍


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

所有代码
  1. import wx
  2. import os
  3. import subprocess
  4. import threading
  5. import datetime
  6. import sys
  7. class VideoScreenshotApp(wx.Frame):
  8.     def __init__(self):
  9.         wx.Frame.__init__(self, None, title="视频截图工具", size=(600, 400))
  10.         # 创建面板
  11.         panel = wx.Panel(self)
  12.         # 创建路径选择控件
  13.         self.path_label = wx.StaticText(panel, label="请选择文件夹:")
  14.         self.path_textctrl = wx.TextCtrl(panel, style=wx.TE_READONLY)
  15.         self.path_button = wx.Button(panel, label="选择路径")
  16.         self.path_button.Bind(wx.EVT_BUTTON, self.on_select_path)
  17.         # 创建文件列表控件,使用 CheckListBox 以支持多选
  18.         self.file_list_ctrl = wx.CheckListBox(panel)
  19.         # 创建截图按钮
  20.         self.capture_button = wx.Button(panel, label="截图")
  21.         self.capture_button.Bind(wx.EVT_BUTTON, self.on_capture)
  22.         # 布局
  23.         sizer = wx.BoxSizer(wx.VERTICAL)
  24.         sizer.Add(self.path_label, 0, wx.ALL, 5)
  25.         sizer.Add(self.path_textctrl, 0, wx.EXPAND | wx.ALL, 5)
  26.         sizer.Add(self.path_button, 0, wx.ALL, 5)
  27.         sizer.Add(self.file_list_ctrl, 1, wx.EXPAND | wx.ALL, 5)
  28.         sizer.Add(self.capture_button, 0, wx.ALL | wx.ALIGN_CENTER, 5)
  29.         panel.SetSizer(sizer)
  30.         self.current_path = ""
  31.     def on_select_path(self, event):
  32.         dlg = wx.DirDialog(self, "选择文件夹", style=wx.DD_DEFAULT_STYLE)
  33.         if dlg.ShowModal() == wx.ID_OK:
  34.             self.current_path = dlg.GetPath()
  35.             self.path_textctrl.SetValue(self.current_path)
  36.             self.update_file_list()
  37.         dlg.Destroy()
  38.     def update_file_list(self):
  39.         self.file_list_ctrl.Clear()
  40.         if not self.current_path:
  41.             return
  42.         file_list = self.search_video_files(self.current_path)
  43.         for filename, file_path, duration in file_list:
  44.             self.file_list_ctrl.Append(f"{filename} - {str(datetime.timedelta(seconds=int(duration)))}", file_path)
  45.     def search_video_files(self, directory):
  46.         video_extensions = ['.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm']
  47.         file_list = []
  48.         for root, dirs, files in os.walk(directory):
  49.             for file in files:
  50.                 if os.path.splitext(file)[1].lower() in video_extensions:
  51.                     file_path = os.path.join(root, file)
  52.                     duration = self.get_video_duration(file_path)
  53.                     file_list.append((file, file_path, duration))
  54.         return file_list
  55.     def get_video_duration(self, file_path):
  56.         cmd = [
  57.             'ffprobe',
  58.             '-v', 'error',
  59.             '-select_streams', 'v:0',
  60.             '-show_entries', 'stream=duration',
  61.             '-of', 'default=noprint_wrappers=1:nokey=1',
  62.             file_path
  63.         ]
  64.         try:
  65.             result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True)
  66.             duration_str = result.stdout.strip()
  67.             if not duration_str:
  68.                 raise ValueError("ffprobe output is empty")
  69.             return float(duration_str)
  70.         except subprocess.CalledProcessError as e:
  71.             wx.LogError(f"ffprobe error: {e.stderr}")
  72.             raise
  73.         except ValueError as e:
  74.             wx.LogError(f"Value error: {e}")
  75.             raise
  76.     def on_capture(self, event):
  77.         selected_indices = self.file_list_ctrl.GetCheckedItems()
  78.         if selected_indices:
  79.             for index in selected_indices:
  80.                 file_path = self.file_list_ctrl.GetClientData(index)
  81.                 file_name = os.path.basename(file_path)
  82.                 duration = self.get_video_duration(file_path)
  83.                 thread = threading.Thread(target=self.capture_screenshots, args=(file_path, duration))
  84.                 thread.start()
  85.         else:
  86.             wx.MessageBox("请先选择一个或多个视频文件", "提示", wx.OK | wx.ICON_INFORMATION)
  87.     def capture_screenshots(self, file_path, duration):
  88.         output_dir = os.path.join(self.current_path, os.path.splitext(os.path.basename(file_path))[0])
  89.         if not os.path.exists(output_dir):
  90.             os.makedirs(output_dir)
  91.         # 计算每张截图的时间点
  92.         intervals = [duration * i / 4 for i in range(1, 5)]
  93.         # 生成截图
  94.         for i, timestamp in enumerate(intervals):
  95.             cmd = [
  96.                 'ffmpeg',
  97.                 '-ss', str(datetime.timedelta(seconds=int(timestamp))),
  98.                 '-i', file_path,
  99.                 '-vframes', '1',
  100.                 os.path.join(output_dir, f'screenshot_{i+1}.jpg')
  101.             ]
  102.             subprocess.run(cmd, check=True)
  103.         # 截图完成后,自动打开文件夹
  104.         if sys.platform.startswith('win'):
  105.             subprocess.Popen(['explorer', output_dir])
  106.         elif sys.platform.startswith('darwin'):
  107.             subprocess.Popen(['open', output_dir])
  108.         elif sys.platform.startswith('linux'):
  109.             subprocess.Popen(['xdg-open', output_dir])
  110. if __name__ == "__main__":
  111.     app = wx.App(False)
  112.     frame = VideoScreenshotApp()
  113.     frame.Show()
  114.     app.MainLoop()
复制代码
代码实现

下面是我们的工具实现代码:
  1. import wx
  2. import os
  3. import subprocess
  4. import threading
  5. import datetime
  6. import sys
  7. class VideoScreenshotApp(wx.Frame):
  8.     def __init__(self):
  9.         wx.Frame.__init__(self, None, title="视频截图工具", size=(600, 400))
  10.         # 创建面板
  11.         panel = wx.Panel(self)
  12.         # 创建路径选择控件
  13.         self.path_label = wx.StaticText(panel, label="请选择文件夹:")
  14.         self.path_textctrl = wx.TextCtrl(panel, style=wx.TE_READONLY)
  15.         self.path_button = wx.Button(panel, label="选择路径")
  16.         self.path_button.Bind(wx.EVT_BUTTON, self.on_select_path)
  17.         # 创建文件列表控件,使用 CheckListBox 以支持多选
  18.         self.file_list_ctrl = wx.CheckListBox(panel)
  19.         # 创建截图按钮
  20.         self.capture_button = wx.Button(panel, label="截图")
  21.         self.capture_button.Bind(wx.EVT_BUTTON, self.on_capture)
  22.         # 布局
  23.         sizer = wx.BoxSizer(wx.VERTICAL)
  24.         sizer.Add(self.path_label, 0, wx.ALL, 5)
  25.         sizer.Add(self.path_textctrl, 0, wx.EXPAND | wx.ALL, 5)
  26.         sizer.Add(self.path_button, 0, wx.ALL, 5)
  27.         sizer.Add(self.file_list_ctrl, 1, wx.EXPAND | wx.ALL, 5)
  28.         sizer.Add(self.capture_button, 0, wx.ALL | wx.ALIGN_CENTER, 5)
  29.         panel.SetSizer(sizer)
  30.         self.current_path = ""
  31.     def on_select_path(self, event):
  32.         dlg = wx.DirDialog(self, "选择文件夹", style=wx.DD_DEFAULT_STYLE)
  33.         if dlg.ShowModal() == wx.ID_OK:
  34.             self.current_path = dlg.GetPath()
  35.             self.path_textctrl.SetValue(self.current_path)
  36.             self.update_file_list()
  37.         dlg.Destroy()
  38.     def update_file_list(self):
  39.         self.file_list_ctrl.Clear()
  40.         if not self.current_path:
  41.             return
  42.         file_list = self.search_video_files(self.current_path)
  43.         for filename, file_path, duration in file_list:
  44.             self.file_list_ctrl.Append(f"{filename} - {str(datetime.timedelta(seconds=int(duration)))}", file_path)
  45.     def search_video_files(self, directory):
  46.         video_extensions = ['.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm']
  47.         file_list = []
  48.         for root, dirs, files in os.walk(directory):
  49.             for file in files:
  50.                 if os.path.splitext(file)[1].lower() in video_extensions:
  51.                     file_path = os.path.join(root, file)
  52.                     duration = self.get_video_duration(file_path)
  53.                     file_list.append((file, file_path, duration))
  54.         return file_list
  55.     def get_video_duration(self, file_path):
  56.         cmd = [
  57.             'ffprobe',
  58.             '-v', 'error',
  59.             '-select_streams', 'v:0',
  60.             '-show_entries', 'stream=duration',
  61.             '-of', 'default=noprint_wrappers=1:nokey=1',
  62.             file_path
  63.         ]
  64.         try:
  65.             result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True)
  66.             duration_str = result.stdout.strip()
  67.             if not duration_str:
  68.                 raise ValueError("ffprobe output is empty")
  69.             return float(duration_str)
  70.         except subprocess.CalledProcessError as e:
  71.             wx.LogError(f"ffprobe error: {e.stderr}")
  72.             raise
  73.         except ValueError as e:
  74.             wx.LogError(f"Value error: {e}")
  75.             raise
  76.     def on_capture(self, event):
  77.         selected_indices = self.file_list_ctrl.GetCheckedItems()
  78.         if selected_indices:
  79.             for index in selected_indices:
  80.                 file_path = self.file_list_ctrl.GetClientData(index)
  81.                 file_name = os.path.basename(file_path)
  82.                 duration = self.get_video_duration(file_path)
  83.                 thread = threading.Thread(target=self.capture_screenshots, args=(file_path, duration))
  84.                 thread.start()
  85.         else:
  86.             wx.MessageBox("请先选择一个或多个视频文件", "提示", wx.OK | wx.ICON_INFORMATION)
  87.     def capture_screenshots(self, file_path, duration):
  88.         output_dir = os.path.join(self.current_path, os.path.splitext(os.path.basename(file_path))[0])
  89.         if not os.path.exists(output_dir):
  90.             os.makedirs(output_dir)
  91.         # 计算每张截图的时间点
  92.         intervals = [duration * i / 4 for i in range(1, 5)]
  93.         # 生成截图
  94.         for i, timestamp in enumerate(intervals):
  95.             cmd = [
  96.                 'ffmpeg',
  97.                 '-ss', str(datetime.timedelta(seconds=int(timestamp))),
  98.                 '-i', file_path,
  99.                 '-vframes', '1',
  100.                 os.path.join(output_dir, f'screenshot_{i+1}.jpg')
  101.             ]
  102.             subprocess.run(cmd, check=True)
  103.         # 截图完成后,自动打开文件夹
  104.         if sys.platform.startswith('win'):
  105.             subprocess.Popen(['explorer', output_dir])
  106.         elif sys.platform.startswith('darwin'):
  107.             subprocess.Popen(['open', output_dir])
  108.         elif sys.platform.startswith('linux'):
  109.             subprocess.Popen(['xdg-open', output_dir])
  110. if __name__ == "__main__":
  111.     app = wx.App(False)
  112.     frame = VideoScreenshotApp()
  113.     frame.Show()
  114.     app.MainLoop()
复制代码
代码解释

创建主窗口

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

    1. on_select_path
    复制代码
    方法允许用户选择一个文件夹,并更新文件列表。
文件列表更新

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

    1. get_video_duration
    复制代码
    方法使用
    1. ffprobe
    复制代码
    命令来获取视频时长。
截图生成

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

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

效果如下



总结

通过这个工具,你可以轻松地从多个视频文件中生成截图,而无需手动操作。
  1. wxPython
复制代码
提供了一个简单易用的界面,而
  1. FFmpeg
复制代码
则负责处理视频帧的提取。这个工具不仅对视频编辑工作有帮助,也为批量处理视频文件提供了极大的便利。
以上就是使用 Python和 FFmpeg 批量截图视频到各自文件夹中的详细内容,更多关于Python FFmpeg 批量截图视频的资料请关注脚本之家其它相关文章!

来源:https://www.jb51.net/python/32648762x.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具