Der schnellste Weg, um Frames mit ffmpeg zu extrahieren?


117

Hallo, ich muss mit ffmpeg Frames aus Videos extrahieren. Gibt es einen schnelleren Weg als diesen:

ffmpeg -i file.mpg -r 1/1 $filename%03d.jpg

?



1
^ aktualisierte URL: trac.ffmpeg.org/wiki/Seeking
Lambart

5
Wenn Sie CPU-Zyklen übrig haben, können Sie parallel aus mehreren Videos extrahieren:parallel -i {} -r 1/1 {.}-%03d.bmp ::: *mpg
Ole Tange

Antworten:


155

Wenn der JPEG-Codierungsschritt zu leistungsintensiv ist, können Sie die unkomprimierten Frames jederzeit als BMP-Bilder speichern:

ffmpeg -i file.mpg -r 1/1 $filename%03d.bmp

Dies hat auch den Vorteil, dass durch Quantisierung durch Transcodierung in JPEG kein weiterer Qualitätsverlust entsteht. (PNG ist ebenfalls verlustfrei, die Codierung dauert jedoch in der Regel viel länger als die von JPEG.)


9
Dies führt dazu, dass viel Frame auf meine Maschine fällt. Kann ich ffmpeg anweisen, alles zu rendern?
Evi1M4chine

45
@ Evi1M4chine entfernen Sie einfach den -r-Parameter, um alle Frames zu extrahieren
studioj

14
Ich möchte hinzufügen, dass JPEG zwar die CPU nicht wirklich belastet, unkomprimierte Bitmaps den Speicher jedoch sehr stark belasten. Ich bezweifle, dass Sie mit BMP im Vergleich zu JPEG einen höheren Durchsatz erzielen.
Marcus Müller

4
So extrahieren Sie alle Frames:ffmpeg -r 1 file.mp4 -r 1 "$filename%03d.png"
Fiatjaf

10
Du meinst ffmpeg -r 1 -i file.mp4 -r 1 "$filename%03d.png, richtig? (Sie haben das verpasst -i)
Joschua

68

Kam über diese Frage, also hier ein kurzer Vergleich. Vergleichen Sie diese beiden Möglichkeiten, um ein Bild pro Minute aus einem 38 m07 langen Video zu extrahieren:

time ffmpeg -i input.mp4 -filter:v fps=fps=1/60 ffmpeg_%0d.bmp

1m36.029s

Dies dauert lange, da ffmpeg die gesamte Videodatei analysiert, um die gewünschten Frames zu erhalten.

time for i in {0..39} ; do ffmpeg -accurate_seek -ss `echo $i*60.0 | bc` -i input.mp4   -frames:v 1 period_down_$i.bmp ; done

0m4.689s

Das ist ungefähr 20 mal schneller. Wir verwenden die schnelle Suche, um zum gewünschten Zeitindex zu gelangen und einen Frame zu extrahieren, und rufen dann für jeden Zeitindex mehrmals ffmpeg auf. Beachten Sie, dass dies -accurate_seek die Standardeinstellung ist , und stellen Sie sicher, dass Sie -ssvor der Eingabevideooption hinzufügen -i.

Beachten Sie, dass es besser ist , zu verwenden , -filter:v -fps=fps=...anstatt -rwie diese ungenau sein können. Obwohl das Ticket als fest markiert ist , sind dennoch einige Probleme aufgetreten. Gehen Sie also besser auf Nummer sicher.


6
Februar 2016: Ab
Mafrosis

1
bcist kein natives Ubuntu-Paket, stattdessen kann man bash verwenden : let "i = $i * 60". Übrigens - ausgezeichnete Idee
Gilad Mayani

3
Guter Tipp Zugabe -ssvor -i. Andernfalls wird das gesamte Video dekodiert und die nicht erforderlichen Frames werden verworfen
MoustafaAAtta

2
Da dies die Spitze von Google ist, möchte ich darauf hinweisen, dass dies auch 2018 ein Ansatz ist, der Dividenden bringt. Das beste Ergebnis scheint darin zu bestehen, einen ffmpegpro Kern Ihres Hosts auszuführen - was (für bmp) zu nahezu linearen Geschwindigkeitsverbesserungen führt (bis Sie auf einen anderen Engpass wie die Festplatte stoßen).
Knetic

Dies sollte die akzeptierte Antwort sein, da es sich bei der Frage um den schnellsten Weg handelt. Natürlich müssen Sie die Exit-Variable für die 'for-Schleife' mit ffprobe kennen, was meiner Meinung nach im Vergleich zu den anderen Methoden immer noch schneller ist.
iamprem

9

Wenn Sie genau wissen, welche Frames extrahiert werden sollen, z. B. 1, 200, 400, 600, 800, 1000, versuchen Sie Folgendes:

select='eq(n\,1)+eq(n\,200)+eq(n\,400)+eq(n\,600)+eq(n\,800)+eq(n\,1000)' \
       -vsync vfr -q:v 2

Ich verwende dies mit einer Pipe zu Imagemagicks Montage, um eine Vorschau von 10 Bildern von Videos zu erhalten. Offensichtlich die Rahmennummern, die Sie mit herausfinden müssenffprobe

ffmpeg -i myVideo.mov -vf \
    select='eq(n\,1)+eq(n\,200)+eq(n\,400)+eq(n\,600)+eq(n\,800)+eq(n\,1000)',scale=320:-1 \
    -vsync vfr -q:v 2 -f image2pipe -vcodec ppm - \
  | montage -tile x1 -geometry "1x1+0+0<" -quality 100 -frame 1 - output.png

.

Kleine Erklärung:

  1. In ffmpeg stehen Ausdrücke +für OR und* für AND
  2. \, entkommt einfach dem , Charakter
  3. Ohne -vsync vfr -q:v 2es scheint nicht zu funktionieren, aber ich weiß nicht warum - jemand?

4

Ich versuchte es. 3600 Frame in 32 Sekunden. Ihre Methode ist sehr langsam. Sie sollten dies versuchen.

ffmpeg -i file.mpg -s 240x135 -vf fps=1 %d.jpg

Ich versuche, ffmpeg -i "input URL" -vf fps=1/5 out%d.png wo die Eingabe-URL ein https-Link sein muss.
x2212

ffmpeg -i file.mpg -vf fps=1 %d.jpg
Kishan Vaghela

1

In meinem Fall brauche ich mindestens jede Sekunde Frames. Ich habe oben den Ansatz "Suchen nach" verwendet, mich aber gefragt, ob ich die Aufgabe parallelisieren könnte. Ich habe hier die N-Prozesse mit FIFO-Ansatz verwendet: /unix/103920/parallelize-a-bash-for-loop/216475#216475

open_sem(){
  mkfifo /tmp/pipe-$$
  exec 3<>/tmp/pipe-$$
  rm /tmp/pipe-$$
  local i=$1
  for((;i>0;i--)); do
    printf %s 000 >&3
  done
}
run_with_lock(){
    local x
    read -u 3 -n 3 x && ((0==x)) || exit $x
    (
    "$@" 
    printf '%.3d' $? >&3
    )&
}
N=16
open_sem $N
time for i in {0..39} ; do run_with_lock ffmpeg -ss `echo $i` -i /tmp/input/GOPR1456.MP4  -frames:v 1 /tmp/output/period_down_$i.jpg  & done

Im Wesentlichen habe ich den Prozess mit & gegabelt, aber die Anzahl der gleichzeitigen Threads auf N begrenzt.

Dies verbesserte in meinem Fall den Suchansatz von 26 Sekunden auf 16 Sekunden. Das einzige Problem ist, dass der Haupt-Thread nicht sauber zum Terminal zurückkehrt, da stdout überflutet wird.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.