如何使用 Python和 FFmpeg 批量截圖視頻到各自文件夾中
在這篇博客中,我們將創(chuàng)建一個(gè)簡單的圖形用戶界面 (GUI) 工具,利用 wxPython
和 FFmpeg
來從視頻文件中批量生成截圖。這個(gè)工具能夠讓用戶選擇一個(gè)文件夾,遍歷其中的所有視頻文件,按照視頻長度將其分為四等分,然后為每個(gè)視頻生成四張截圖。所有生成的截圖將保存在一個(gè)以視頻名稱命名的文件夾中,并在截圖完成后自動(dòng)打開該文件夾。
C:\pythoncode\new\multivideofilescreenshot.py
工具介紹
- wxPython:用于創(chuàng)建桌面應(yīng)用程序的圖形界面。
- FFmpeg:一個(gè)強(qiáng)大的多媒體處理工具,用于提取視頻幀。
所有代碼
import wx import os import subprocess import threading import datetime import sys class VideoScreenshotApp(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, title="視頻截圖工具", size=(600, 400)) # 創(chuàng)建面板 panel = wx.Panel(self) # 創(chuàng)建路徑選擇控件 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) # 創(chuàng)建文件列表控件,使用 CheckListBox 以支持多選 self.file_list_ctrl = wx.CheckListBox(panel) # 創(chuàng)建截圖按鈕 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: return file_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_list def 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}") raise except ValueError as e: wx.LogError(f"Value error: {e}") raise def 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("請先選擇一個(gè)或多個(gè)視頻文件", "提示", 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) # 計(jì)算每張截圖的時(shí)間點(diǎn) 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) # 截圖完成后,自動(dòng)打開文件夾 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()
代碼實(shí)現(xiàn)
下面是我們的工具實(shí)現(xiàn)代碼:
import wx import os import subprocess import threading import datetime import sys class VideoScreenshotApp(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, title="視頻截圖工具", size=(600, 400)) # 創(chuàng)建面板 panel = wx.Panel(self) # 創(chuàng)建路徑選擇控件 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) # 創(chuàng)建文件列表控件,使用 CheckListBox 以支持多選 self.file_list_ctrl = wx.CheckListBox(panel) # 創(chuàng)建截圖按鈕 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: return file_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_list def 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}") raise except ValueError as e: wx.LogError(f"Value error: {e}") raise def 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("請先選擇一個(gè)或多個(gè)視頻文件", "提示", 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) # 計(jì)算每張截圖的時(shí)間點(diǎn) 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) # 截圖完成后,自動(dòng)打開文件夾 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()
代碼解釋
創(chuàng)建主窗口:
- 使用
wx.Frame
創(chuàng)建主窗口,添加路徑選擇控件、文件列表控件以及截圖按鈕。
路徑選擇:
on_select_path
方法允許用戶選擇一個(gè)文件夾,并更新文件列表。
文件列表更新:
update_file_list
方法遍歷所選文件夾中的視頻文件,獲取每個(gè)視頻的時(shí)長,并將信息顯示在CheckListBox
中。
視頻時(shí)長獲取:
get_video_duration
方法使用ffprobe
命令來獲取視頻時(shí)長。
截圖生成:
on_capture
方法處理截圖請求,使用多線程來生成截圖,以避免阻塞主線程。capture_screenshots
方法使用ffmpeg
命令生成四張截圖,并將截圖保存在以視頻名稱命名的文件夾中。
自動(dòng)打開文件夾:
- 截圖完成后,自動(dòng)在文件瀏覽器中打開保存截圖的文件夾。
效果如下
總結(jié)
通過這個(gè)工具,你可以輕松地從多個(gè)視頻文件中生成截圖,而無需手動(dòng)操作。wxPython
提供了一個(gè)簡單易用的界面,而 FFmpeg
則負(fù)責(zé)處理視頻幀的提取。這個(gè)工具不僅對視頻編輯工作有幫助,也為批量處理視頻文件提供了極大的便利。
以上就是使用 Python和 FFmpeg 批量截圖視頻到各自文件夾中的詳細(xì)內(nèi)容,更多關(guān)于Python FFmpeg 批量截圖視頻的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python實(shí)現(xiàn)圖片轉(zhuǎn)字符小工具
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)圖片轉(zhuǎn)字符小工具,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04Python實(shí)現(xiàn)類似jQuery使用中的鏈?zhǔn)秸{(diào)用的示例
chained calls鏈?zhǔn)秸{(diào)用其實(shí)多是指一種方法鏈的程序?qū)懛?這里我們來看一下Python實(shí)現(xiàn)類似jQuery使用中的鏈?zhǔn)秸{(diào)用的示例,首先說明一下什么是鏈?zhǔn)秸{(diào)用:2016-06-06python 使用正則表達(dá)式判斷圖片路徑是否是超鏈接的示例
在Python中,判斷一個(gè)給定的字符串(假設(shè)為圖片路徑)是否是網(wǎng)頁鏈接(URL),你可以通過檢查該字符串是否符合URL的基本格式來實(shí)現(xiàn),以下是一個(gè)使用正則表達(dá)式來判斷給定字符串是否為網(wǎng)頁鏈接(URL)的示例,感興趣的朋友跟隨小編一起看看吧2024-08-08python matplotlib 畫dataframe的時(shí)間序列圖實(shí)例
今天小編就為大家分享一篇python matplotlib 畫dataframe的時(shí)間序列圖實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11python plt.plot bar 如何設(shè)置繪圖尺寸大小
這篇文章主要介紹了python plt.plot bar 設(shè)置繪圖尺寸大小的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06