ffmpeg извлекает изображения из нескольких выбранных временных точек

ffmpeg извлекает изображения из нескольких выбранных временных точек

У меня есть видеофайл, и я хочу извлечь одно изображение из (около 1500) определенных временных точек. (expl. -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'sотвечать, который может не работать в некоторых ситуациях.

Одна из тонкостей в выборе кадров по (приблизительной) временной метке заключается в том, что фактическая временная метка кадра может не совпадать в точности с желаемой временной меткой. Например, в видео с частотой кадров 23,98 (24000/1001) нет кадра с временной меткой 1,0 — ближайший кадр имеет значение временной метки 1,001.

Выражение eq(t, specific_timepoint)оценивается как истинное только если tточно равно 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

Если фильтр оценивается как истинный, он выведет кадр, поэтому добавление проверок, сравнивающих временную метку кадра tс определенной временной меткой в ​​секундах (например, 1.5), даст вам все кадры с этими временными метками.

решение3

OP просит извлечь изображения для "множества конкретных временных точек" и приводит 3 примера. Для меня 3 — это не так много, поэтому я расширил ответ, опубликованный @Leon, чтобы разрешить ввод файловмногоtimepoints. Код python создает и затем выполняет длинную ffmpegкоманду. Он предполагает, что timepoints — это 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

Связанный контент