ffmpeg は選択した複数の時点から画像を抽出する

ffmpeg は選択した複数の時点から画像を抽出する

ビデオファイルがあり、特定の時点(約 1500)から 1 つの画像を抽出したいと考えています。(例:-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 枚の写真にはうまく機能しますが、これほど多くの特定の時点に対してこれを行う方法はありますか?

誰かアドバイスをくれませんか?

答え1

この回答はフォロー答え状況によっては機能しない可能性があります。

タイムスタンプ(近似値)でフレームを選択する際の微妙な点は、フレームの実際のタイムスタンプが、目的のタイムスタンプと正確に一致しない可能性があることです。たとえば、フレームレート 23.98(24000/1001)のビデオでは、タイムスタンプが 1.0 のフレームは存在しません。最も近いフレームのタイムスタンプ値は 1.001 です。

式は、が と正確に等しいeq(t, specific_timepoint)場合にのみ true と評価されます。したがって、上記の状況ではフレームの選択に失敗します。回避策として、目的のタイムスタンプの直後の最初のフレーム、つまり、タイムスタンプ値が指定されたタイムポイント以上で、その前のフレームのタイムスタンプが指定されたタイムポイント未満であるフレームを選択できます。単一のタイムポイントの選択式は次のようになります。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)

連続する2つのフレームのうち最初のフレームのタイムスタンプ値が完全に一致する指定されたタイムポイント値。

タイムポイント 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コマンドを構築して実行します。タイムポイントは、で始まるコンマ区切りのテキスト行の2番目と3番目の項目である'Dialogue:'と想定しています。.ssa およ​​び .ass ファイルタイプによって使われたエギスブ字幕のタイムポイントは範囲なので、プログラムはその範囲の何分の1で画像をキャプチャするかを尋ねます。私は通常、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

関連情報