如何使用 Python和 FFmpeg 批量截圖視頻到各自文件夾中
在這篇博客中,我們將創(chuàng)建一個(gè)簡(jiǎn)單的圖形用戶界面 (GUI) 工具,利用 wxPython 和 FFmpeg 來(lái)從視頻文件中批量生成截圖。這個(gè)工具能夠讓用戶選擇一個(gè)文件夾,遍歷其中的所有視頻文件,按照視頻長(zhǎng)度將其分為四等分,然后為每個(gè)視頻生成四張截圖。所有生成的截圖將保存在一個(gè)以視頻名稱命名的文件夾中,并在截圖完成后自動(dòng)打開(kāi)該文件夾。
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="請(qǐng)選擇文件夾:")
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("請(qǐng)先選擇一個(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)打開(kāi)文件夾
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="請(qǐng)選擇文件夾:")
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("請(qǐng)先選擇一個(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)打開(kāi)文件夾
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í)長(zhǎng),并將信息顯示在CheckListBox中。
視頻時(shí)長(zhǎng)獲取:
get_video_duration方法使用ffprobe命令來(lái)獲取視頻時(shí)長(zhǎng)。
截圖生成:
on_capture方法處理截圖請(qǐng)求,使用多線程來(lái)生成截圖,以避免阻塞主線程。capture_screenshots方法使用ffmpeg命令生成四張截圖,并將截圖保存在以視頻名稱命名的文件夾中。
自動(dòng)打開(kāi)文件夾:
- 截圖完成后,自動(dòng)在文件瀏覽器中打開(kāi)保存截圖的文件夾。
效果如下

總結(jié)
通過(guò)這個(gè)工具,你可以輕松地從多個(gè)視頻文件中生成截圖,而無(wú)需手動(dòng)操作。wxPython 提供了一個(gè)簡(jiǎn)單易用的界面,而 FFmpeg 則負(fù)責(zé)處理視頻幀的提取。這個(gè)工具不僅對(duì)視頻編輯工作有幫助,也為批量處理視頻文件提供了極大的便利。
以上就是使用 Python和 FFmpeg 批量截圖視頻到各自文件夾中的詳細(xì)內(nèi)容,更多關(guān)于Python FFmpeg 批量截圖視頻的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- python調(diào)用系統(tǒng)ffmpeg實(shí)現(xiàn)視頻截圖、http發(fā)送
- Python使用ffmpeg合成視頻、音頻的實(shí)現(xiàn)方法
- Python 如何利用ffmpeg 處理視頻素材
- python調(diào)用ffmpeg命令行工具便捷操作視頻示例實(shí)現(xiàn)過(guò)程
- ffmpeg+Python實(shí)現(xiàn)B站MP4格式音頻與視頻的合并示例代碼
- Python3 ffmpeg視頻轉(zhuǎn)換工具使用方法解析
- python ffmpeg任意提取視頻幀的方法
- python+ffmpeg視頻并發(fā)直播壓力測(cè)試
相關(guān)文章
python實(shí)現(xiàn)圖片轉(zhuǎn)字符小工具
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)圖片轉(zhuǎn)字符小工具,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04
Python實(shí)現(xiàn)類似jQuery使用中的鏈?zhǔn)秸{(diào)用的示例
chained calls鏈?zhǔn)秸{(diào)用其實(shí)多是指一種方法鏈的程序?qū)懛?這里我們來(lái)看一下Python實(shí)現(xiàn)類似jQuery使用中的鏈?zhǔn)秸{(diào)用的示例,首先說(shuō)明一下什么是鏈?zhǔn)秸{(diào)用:2016-06-06
python 使用正則表達(dá)式判斷圖片路徑是否是超鏈接的示例
在Python中,判斷一個(gè)給定的字符串(假設(shè)為圖片路徑)是否是網(wǎng)頁(yè)鏈接(URL),你可以通過(guò)檢查該字符串是否符合URL的基本格式來(lái)實(shí)現(xiàn),以下是一個(gè)使用正則表達(dá)式來(lái)判斷給定字符串是否為網(wǎng)頁(yè)鏈接(URL)的示例,感興趣的朋友跟隨小編一起看看吧2024-08-08
詳解分布式任務(wù)隊(duì)列Celery使用說(shuō)明
這篇文章主要介紹了詳解分布式任務(wù)隊(duì)列Celery使用說(shuō)明,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11
python matplotlib 畫(huà)dataframe的時(shí)間序列圖實(shí)例
今天小編就為大家分享一篇python matplotlib 畫(huà)dataframe的時(shí)間序列圖實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11
python plt.plot bar 如何設(shè)置繪圖尺寸大小
這篇文章主要介紹了python plt.plot bar 設(shè)置繪圖尺寸大小的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
python 成功引入包但無(wú)法正常調(diào)用的解決
這篇文章主要介紹了python 成功引入包但無(wú)法正常調(diào)用的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03

