Ich habe eine Videodatei und möchte aus (ungefähr 1500) bestimmten Zeitpunkten ein einzelnes Bild extrahieren. (Beispiel: -ss 00:50, ss 00:67, ss: 01:70 …)
Gibt es eine Möglichkeit, dies zu tun? Ich habe versucht, diesen Befehl zu verwenden:
ffmpeg.exe -ss 00:50 -i all.avi -t 00:00:01 -vframes 1 images-filename_%03d.png
Dies funktioniert bei einem einzelnen Bild gut, aber gibt es eine Möglichkeit, dies für so viele bestimmte Zeitpunkte zu tun?
Hat jemand einen Rat?
Antwort1
Diese Antwort verbessert@slhck'SAntwort, was in manchen Situationen möglicherweise nicht funktioniert.
Eine Feinheit bei der Auswahl von Frames nach (ungefährem) Zeitstempel besteht darin, dass der tatsächliche Zeitstempel des Frames möglicherweise nicht genau dem gewünschten Zeitstempel entspricht. Beispielsweise gibt es in einem Video mit einer Framerate von 23,98 (24000/1001) keinen Frame mit dem Zeitstempel 1,0 – der nächste Frame hat einen Zeitstempelwert von 1,001.
Der Ausdruck eq(t, specific_timepoint)
wird nur dann als wahr ausgewertet, wenn t
genau gleich ist specific_timepoint
. Daher wird in der oben beschriebenen Situation kein Frame ausgewählt. Als Workaround können wir den ersten Frame unmittelbar nach dem gewünschten Zeitstempel auswählen, also den Frame, dessen Zeitstempelwert größer oder gleich dem angegebenen Zeitpunkt ist, während der Zeitstempel des vorhergehenden Frames kleiner als der angegebene Zeitpunkt war. Der Auswahlausdruck für einen einzelnen Zeitpunkt lautet
lt(prev_pts * TB,
timepoint
) * gte(pts * TB,
timepoint
)
Beachten Sie, dass ich absichtlich keine kürzere Variante verwendet habe
between(
timepoint
, prev_pts*TB, pts*TB)
Das ist gleichbedeutend mit
lte(prev_pts * TB,
timepoint
) * gte(pts * TB,
timepoint
)
als ob es zwei aufeinanderfolgende Frames auswählen würde, wenn der Zeitstempelwert des (ersten) von ihnenentspricht genauder angegebene Zeitpunktwert.
Beispiel für die Auswahl der Frames, die den Zeitpunkten 1,5, 10 und 20 entsprechen oder unmittelbar darauf folgen:
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
Antwort2
Fügen Sie als Optionen für den select
Filter mehrere Zeitstempel hinzu.
ffmpeg -i input.mp4 -filter:v \
"select='eq(t\,1.5)+eq(t\,10)+eq(t\,20)'" \
-vsync drop images-filename_%03d.png
Wenn der Filter „true“ ergibt, wird ein Frame ausgegeben. Durch das Hinzufügen von Prüfungen, bei denen der Frame-Zeitstempel t
mit einem bestimmten Zeitstempel in Sekunden (z. B. 1.5
) verglichen wird, erhalten Sie alle Frames mit diesen Zeitstempeln.
Antwort3
Der OP fordert die Extraktion von Bildern für „viele bestimmte Zeitpunkte“ und gibt 3 Beispiele. Für mich sind 3 nicht viel, also habe ich die Antwort von @Leon erweitert, um die Dateieingabe vonvielZeitpunkte. Der Python-Code erstellt einen langen ffmpeg
Befehl und führt ihn dann aus. Er geht davon aus, dass Zeitpunkte das zweite und dritte Element in einer durch Kommas getrennten Textzeile sind, die mit beginnt 'Dialogue:'
, wie in der.ssa- und .ass-Dateitypenbenutzt vonAegisub. Da Untertitel-Zeitpunkte ein Bereich sind, fragt das Programm, in welchem Bruchteil dieses Bereichs Sie das Bild aufnehmen möchten. Ich habe festgestellt, dass ich normalerweise die gewünschten Bilder erhalte, wenn ich Werte zwischen 0,5 und 0,9 verwende.
'''
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