我有一個視訊文件,喜歡從(大約 1500)個特定時間點提取單個圖像。
有沒有辦法做到這一點?我嘗試使用這個命令:
ffmpeg.exe -ss 00:50 -i all.avi -t 00:00:01 -vframes 1 images-filename_%03d.png
這對於單張圖片非常有效,但是有沒有辦法在如此多的特定時間點上做到這一點呢?
有人有建議嗎?
答案1
這個答案改進了@slhck的回答,在某些情況下可能無法工作。
透過(近似)時間戳選擇幀的一個微妙之處是幀的實際時間戳可能不完全等於所需的時間戳。例如,在幀速率為 23.98 (24000/1001) 的影片中,沒有時間戳為 1.0 的幀 - 最接近的幀的時間戳值為 1.001。
eq(t, specific_timepoint)
僅當t
完全等於時,此表達式的計算結果為 true specific_timepoint
。因此,在上述情況下將無法選擇幀。作為解決方法,我們可以選擇緊接在所需時間戳之後的第一幀,即時間戳值大於或等於指定時間點的幀,而其前一幀的時間戳小於指定時間點。單一時間點的選擇表達式為
lt(prev_pts * TB,
timepoint
) * gte(pts * TB,
timepoint
)
請注意,我故意不使用較短的變體
between(
timepoint
, prev_pts*TB, pts*TB)
這相當於
lte(prev_pts * TB,
timepoint
) * gte(pts * TB,
timepoint
)
因為當其中一個(第一個)的時間戳記值時,它將選擇兩個連續的幀完全匹配指定的時間點值。
選擇與時間點 1.5、10 和 20 相對應或緊接在後的幀的範例:
ffmpeg -i input.mp4 -filter:v \
"select='lt(prev_pts*TB\,1.5)*gte(pts*TB\,1.5) \
+lt(prev_pts*TB\,10)*gte(pts*TB\,10) \
+lt(prev_pts*TB\,20)*gte(pts*TB\,20)'" \
-vsync drop images-filename_%03d.png
答案2
新增多個時間戳作為select
過濾器的選項。
ffmpeg -i input.mp4 -filter:v \
"select='eq(t\,1.5)+eq(t\,10)+eq(t\,20)'" \
-vsync drop images-filename_%03d.png
如果過濾器評估為 true,它將輸出一個幀,因此添加將幀時間戳t
與以秒為單位的特定時間戳進行比較的檢查(例如,1.5
)將為您提供這些時間戳處的所有幀。
答案3
OP 要求提取「很多特定時間點」的圖像,並給出了 3 個範例。對我來說,3 並不是很多,所以我擴展了 @Leon 發布的答案,以允許文件輸入很多時間點。 python 程式碼建構並執行一個長ffmpeg
命令。它假設時間點是以 開頭的逗號分隔文字行中的第二項和第三項'Dialogue:'
,如.ssa 和 .ass 檔案類型被使用過埃吉蘇。由於字幕時間點是一個範圍,因此程式會詢問您想要在該範圍的哪一部分擷取影像。我發現我通常使用 0.5 和 0.9 的值來獲得我想要的圖像
'''
Splits a video into images at timepoints defined in a subtitle file
supply: .mp4 video and .ass subtitle file into the program directory
output: .png files within an img directory (which program makes if you don't)
to run: enter the .mp4 & .ass filename (same) and the timepoint fraction
timepoint fraction=0.5 means midway between each subtitle start & end point
code constructs and then executes a long ffmpeg command based on this post:
https://superuser.com/a/1330042/752104
'''
import datetime as dt
import os
os.system("mkdir img")
def get_sec(stringHMS):
timedeltaObj = dt.datetime.strptime(
stringHMS, "%H:%M:%S.%f") - dt.datetime(1900, 1, 1)
return timedeltaObj.total_seconds()
defName = 'm'
defFrac = '0.5'
prompt1 = 'Enter file name for both .mp4 and .ass file [' + defName + ']:'
prompt2 = 'Fraction between subtitle start & end points (0-1)[' + \
defFrac + ']:'
fname = input(prompt1)
if fname == '':
fname = defName
fh = open(fname + ".ass")
frac = input(prompt2)
if frac == '':
frac = defFrac
cmd = "ffmpeg -i " + fname + ".mp4 -filter:v \"select='" # construct ffmpeg cmd
plus = ""
for line in fh:
if not line.startswith('Dialogue:'):
continue
timePt = line.split(",")
t1 = get_sec(timePt[1])
t2 = get_sec(timePt[2])
t = str(t1 + float(frac) * (t2 - t1))
cmd = cmd + plus + "lt(prev_pts*TB\," + t + ")*gte(pts*TB\," + t + ")"
plus = " +" # plus is added only after the first lt()
cmd = cmd + "'\" -vsync drop img/img%03d.png"
os.system(cmd) # execute the ffmpeg command