![Präzises Extrahieren von Frames alle N Sekunden mit FFmpeg](https://rvso.com/image/1672321/Pr%C3%A4zises%20Extrahieren%20von%20Frames%20alle%20N%20Sekunden%20mit%20FFmpeg.png)
Die Verwendung
Ich extrahiere Bilder aus Videos mithilfe von ffmpeg
.
Ich lade alle 10 Sekunden (einschließlich) ein verkleinertes Bild hoch, das ich mit zu Montagen kombiniere imagemagick
. Diese Montagen werden wiederum verwendet, um eine Vorschau des Videos anzuzeigen, wenn man den Scrubber in einem webbasierten Videoplayer bewegt. (Berechnen, welches Bild in der Montage angezeigt werden soll).
Der Befehl
Nach einigem Herumprobieren bin ich schließlich auf den folgenden Befehl gekommen, bei dem Geschwindigkeit vor Qualität steht:
ffmpeg \
-loglevel error \
-hwaccel cuvid \
-hwaccel_output_format cuda \
-c:v h264_cuvid \
-i "$video_file" \
-r 0.1 \
-filter:v "scale_cuda=w=-1:h=100,thumbnail_cuda=2,hwdownload,format=nv12" \
-color_range 2 \
f%09d.jpg
Das scheint gut zu funktionieren. Die Aufnahmen sind hier und da um etwa ± 0,5 - 1 Sekunde abweichend, aber das ist verschmerzbar.
Das Problem
Das Problem besteht darin, dass ffmpeg
am Anfang des Videos ein zusätzliches Bild erzeugt wird. Beispiele für Dateien:
file time
f000000001.jpg 00:00:00
f000000002.jpg 00:00:00
f000000003.jpg 00:00:10
f000000004.jpg 00:00:20
f000000005.jpg 00:00:30
...
Manchmal weichen die erste und die zweite Sekunde um einige Millisekunden voneinander ab.
So wie ich es (jetzt) weiß, kann ich einfach das erste Bild löschen und mit dem Rest fortfahren, bin mir aber nicht sicher, warum das passiert und ob es ein Fehler oder etwas anderes ist.
Anders ausgedrückt: Ich muss wissen, ob der "Effekt" der ersten beiden Frameszuverlässigdamit ich es ffmpeg
auch in anderen Versionen löschen kann.
Da ich die Bilder verwende, um zu einem bestimmten Zeitpunkt einen 10-Sekunden-Schnappschuss aus dem Video anzuzeigenes ist 10 Sekunden auswenn ich das erste erzeugte Bild nicht lösche. Wenn es aus irgendeinem Grund dannnichtErstellen Sie am Anfang ein Duplikat, eine andere Version oder was auch immer. Das Löschen des ersten Bildes würde dasselbe Problem verursachen.
Montage
(Falls es von Interesse ist, die Montagen werden ungefähr so erstellt):
montage -tile 5x -geometry +0+0 -background none [file1 - file50 ] montage01.jpg
montage -tile 5x -geometry +0+0 -background none [file51 - file100] montage02.jpg
...
Befehl, den ich jetzt basierend auf der Antwort (Shell) verwende:
# Set on call or global:
file_in=sample.mp4
pix_fmt=yuvj420p
sec_snap_interval=10
nr_start=1
pfx_out=snap
ffmpeg \
-loglevel warning \
-hwaccel cuvid \
-hwaccel_output_format cuda \
-c:v h264_cuvid \
-i "$file_in" \
-pix_fmt "$pix_fmt" \
-filter:v "
scale_cuda=
w = -1 :
h = 100,
thumbnail_cuda = 2,
hwdownload,
format = nv12,
select = 'bitor(
gte(t - prev_selected_t, $sec_snap_interval),
isnan(prev_selected_t)
)'
" \
-vsync passthrough \
-color_range 2 \
-start_number "$nr_start" \
"$pfx_out%09d.jpg"
Antwort1
Durch die Verwendung -r 0.1
wird die Ausgabebildrate auf 0,1 Hz eingestellt, es ist jedoch nicht garantiert, dass genau alle 10 Sekunden ein Bild vom Eingabevideo erhalten wird (ich bin nicht sicher, warum).
Eine Möglichkeit zur Lösung dieses Problems ist die VerwendungFilter auswählen.
Beispiel (ohne GPU-Beschleunigung):
ffmpeg -i input.mp4 -vf "select=bitor(gte(t-prev_selected_t\,10)\,isnan(prev_selected_t))" -vsync 0 f%09d.jpg
bitor(gte(t-prev_selected_t\,10)
ist1
, wenn der Unterschied zwischen den „bestandenen“ Zeitstempeln größer als 10 Sekunden ist.
Wenn der Ausdruck als ausgewertet wird1
, wird der Frame „ausgewählt“ und an die Ausgabe übergeben.bitor
mitisnan(prev_selected_t)
übergibt den ersten Frame, wobeiprev_selected_t
(NaN
kein Wert hat).-vsync 0
wendet „Passthrough“ an – Jeder Frame wird mit seinem Zeitstempel vom Demuxer an den Muxer übergeben.
Hier ist ein Beispiel mit scale_cuda
und thumbnail_cuda
:
ffmpeg \
-loglevel error \
-hwaccel cuvid \
-hwaccel_output_format cuda \
-c:v h264_cuvid \
-i "$video_file" \
-filter:v "scale_cuda=w=-1:h=100,thumbnail_cuda=2,hwdownload,format=nv12,select=bitor(gte(t-prev_selected_t\,10)\,isnan(prev_selected_t))" \
-vsync 0 \
-color_range 2 \
f%09d.jpg
- Aufgrund der Filterverwendung
thumbnail_cuda
müssen wir denselect
Filter am Ende platzieren.
Testen:
Synthetisches Video mit Frame-Zähler bei 10 fps erstellen:
ffmpeg -y -f lavfi -r 10 -i testsrc=size=128x72:rate=1:duration=1000 -vf setpts=N/10/TB -vcodec libx264 -pix_fmt yuv420p input.mp4
Ausgabeframes nach Ausführung des obigen Befehls:
Wie Sie sehen, liegen die ausgewählten Frames genau alle 10 Sekunden.