ffmpeg从几个选定的时间点提取图片

ffmpeg从几个选定的时间点提取图片

我有一个视频文件,想从(大约 1500 个)特定时间点提取单个图像。(例如 -ss 00:50、ss 00:67、ss: 01:70 ...)

有办法吗?我尝试使用这个命令:

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)计算结果才为真。因此,在上述情况下,它将无法选择帧。作为一种解决方法,我们可以选择紧随所需时间戳之后的第一帧,即时间戳值大于或等于指定时间点的帧,而其前一帧的时间戳小于指定时间点。单个时间点的选择表达式将是tspecific_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

如果过滤器的计算结果为真,它将输出一个帧,因此添加检查将帧时间戳t与以秒为单位的特定时间戳(例如1.5)进行比较,将为您提供这些时间戳的所有帧。

答案3

楼主要求提取“很多特定时间点”的图像,并给出了 3 个例子。对我来说,3 个并不多,所以我扩展了 @Leon 的回答,允许输入很多时间点。python 代码构造并执行一个长ffmpeg命令。它假设时间点是以 开头的逗号分隔的文本行中的第 2 和第 3 个项目'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

相关内容