Wie kann ich mit ffmpeg schnell animierte GIFs mit niedriger Qualität erstellen?

578
Rjak

Wir erzeugen viele Miniaturbild-GIFs, deren Qualität bei weitem nicht so wichtig ist wie die Zeit, die für ihre Erstellung benötigt wird. Die Erzeugung hochwertiger GIFs mit ffmpeg ist zwar sehr gut abgedeckt, aber ich habe nicht viel Glück, um herauszufinden, wie Sie möglichst schnell qualitativ hochwertige GIFs erzeugen können.

Die Palettenberechnung beansprucht den größten Teil der Ausführungszeit mit dem folgenden Befehl (hier aus der Antwort der Multi-Chain-Filtergraphen entnommen: So erstellen Sie aus einem Videoteil direkt aus dem Web effizient ein Gif-Paletten -GIF ):

ffmpeg -y -threads 8 -r 24 -f image2 -start_number 1 -i "frames.%04d.jpg" -filter_complex "fps=24,scale=150:-1:flags=fast_bilinear,split=2 [a][b]; [a] palettegen [pal] fifo [b]; [b] [pal] paletteuse" output.gif 

Die Ausführungszeit dieses Befehls mit 1000 Frames beträgt etwa 72 Sekunden. Ungefähr 67 Sekunden davon ist der Palettendurchlauf, und dann durchbricht er die tatsächliche GIF-Generation in etwa 5 Sekunden. Ich möchte die gesamte Ausführungszeit so weit wie möglich verkürzen und bereit sein, für die Geschwindigkeit viel Bildqualität zu opfern.

3
Ihr Befehl ist syntaktisch falsch. Ihnen fehlt ein "[b]" - Label. In jedem Fall ist die Verwendung der Palette um 200⨉ langsamer, wenn Sie nach einem Schnelltest beurteilen. Sie nicht zu verwenden, ist für Sie keine Option? slhck vor 5 Jahren 1
@slhck Ich finde, dass die ffmpeg-Dokumente rund um dieses Zeug ziemlich hart sind, aber ich kam dazu, die Palette zu entfernen: `ffmpeg -y -r 24 -f image2 -start_number 1 -i" frames.% 04d.jpg " -filter: v "scale = 150: -1: flags = fast_bilinear" output.gif` Die zwei Probleme dabei: 1) Es wurden nur 13 Sekunden gespart (59 Sekunden statt 72 Sekunden) und 2) Ich bin nicht 100% sicher, dass dieser Befehl die Palettenberechnung vollständig ausschließt (nur dass die Palettenberechnung, die ich zuvor angegeben habe, nicht enthalten ist). Rjak vor 5 Jahren 0
Ja, das meinte ich grundsätzlich. Es tut mir leid, dass Sie nicht so explizit sind. Ich dachte, Sie wüssten, was der von Ihnen verwendete Filterbefehl getan hat. In meinen Tests beschleunigte das Überspringen der Palette den Befehl * signifikant *. Was hat Sie zu der ursprünglichen Schlussfolgerung geführt, dass die eigentliche Kodierung nur 5 Sekunden dauerte? slhck vor 5 Jahren 1
Beobachtung der Ausgabe, was wahrscheinlich kein guter Indikator ist. Wenn ich den Befehl ohne Palettengenerierung ausführen, wird der Indikator "Aktuelles Bild" sofort hochgefahren und wir beginnen mit der Verarbeitung von Bildern. Wenn ich das Programm mit Palettengenerierung starte, wird der aktuelle Frame bis etwa 5 Sekunden vor dem Ende der Verarbeitung auf 0 gesetzt. Dann dreht er sich sehr schnell durch die Frames. Rjak vor 5 Jahren 0
Ja, das beobachte ich auch, aber es ist dann viel schneller. Aber ich denke, es hängt von der Länge des Videos ab. Um ehrlich zu sein, ich weiß nicht, ob es einen schnelleren Weg gibt, die GIF zu generieren, als einfach `ffmpeg -i ' `. slhck vor 5 Jahren 1
Ja, es scheint sicher, als würde ich es ausschöpfen. Ich arbeite jetzt an einer anderen Methode, bei der Frames übersprungen werden, wenn der Clip lang genug ist (z. B. wenn der Clip 15.000 Frames ist, verwenden wir nur jeden 100. Frame, sodass wir eine animierte GIF mit 150 Frames haben). Die Hürde hier ist, dass ich tatsächlich mit einer GIF von 150 Frames ende, aber es scheint, dass alle Frames, die ich überspringen möchte, auch berechnet werden, so dass es die gleiche Zeit in Anspruch nimmt! Holen Sie sich das mit `-filter_complex" select = 'not (mod (n \, 100))' ", scale = 150: -1: flags = fast_bilinear" ` Rjak vor 5 Jahren 0

2 Antworten auf die Frage

2
slhck

Durch die Verwendung von palettegen/ paletteusefilters wird der Befehl langsamer ausgeführt. Der einfachste Weg, eine GIF von geringerer Qualität zu erreichen, wäre:

ffmpeg -f image2 -i "frames.%04d.jpg" output.gif 

Mit zusätzlicher Skalierung:

ffmpeg -f image2 -i "frames.%04d.jpg" -vf scale=150:-1 output.gif 

Sie können auch Frames in der Ausgabe-GIF ablegen, dh die Frames abtasten, so dass nicht alle Frames verarbeitet werden. Um zB nur einen FPS-Ausgang zu haben, verwenden Sie einen fpsFilter:

ffmpeg -i "frames.%04d.jpg" -vf "fps=fps=1,scale=150:-1" output.gif 
Ich glaube, es gibt einen Tippfehler in diesem letzten Beispiel ... sollte -vf "fps = 1, scale = 150: -1" sein. Die Ausführungszeit des zweiten Beispiels beträgt 68,48 Sekunden. Die Angabe von fps = 1 führt zu einer 1-FPS-GIF, die Quelle ist jedoch 120 Bilder pro Sekunde, sodass das Ergebnis zu langsam abgespielt wird und die Ausführungszeit nicht genug verbessert wird (59,44 Sekunden). Ich experimentiere mit dem concat loader und habe ffmpeg jeden N-ten Frame gelesen. Wenn ich dabei jeden fünften Frame mit Lanczos Neuskalierung und Palettengenerierung pro Frame abtaste, sieht die zeitliche Qualität gut aus, die Farbqualität sieht gut aus und der Befehl wird in weniger als 8 Sekunden ausgeführt. Rjak vor 5 Jahren 0
Werde die ganze Sache hier in einem Kommentar teilen, sobald ich alles getestet habe und die Ausführungszeiten habe. Rjak vor 5 Jahren 0
@Rjak Die erste Option des `fps`-Filters heißt` fps`, also sind diese gleichwertig. Wenn Ihre Quelle 120fps ist, müssen Sie "-framerate 120 -i" frames ... "` angeben, da die Standardeinstellung 24 ist. slhck vor 5 Jahren 0
1
Rjak

Ich hatte die Aufgabe, die Zeit zu verkürzen, die benötigt wurde, um ein animiertes GIF so nahe wie möglich an 30 Frames bei 150 Pixel Breite zu erzeugen. Die meisten von uns generierten Sequenzen liegen unter 1000 Frames. Wir hatten eine 15.000-Frame-Sequenz und unsere Render-Knoten brauchten 17 Minuten, um dieses GIF mit 30 Frames zu erzeugen, was inakzeptabel langsam ist.

Wir haben ffmpeg als Demuxer und Pipe zum Imagemagick benutzt. Durch mehrere Stunden des Experimentierens bin ich zu folgenden Schlussfolgerungen gekommen:

  • Die Anzahl der Eingaberahmen Sie ffmpeg Prozess fragen, ist bei weitem die meisten impactful Eingabe in Bezug auf die Ausführungsgeschwindigkeit. Wenn Sie den concat Demuxer zum Überspringen von Eingabebildern verwenden, wird dies den größten Leistungsunterschied bewirken. Mit jedem fünften Frame konnte ich die Gesamtberechnungszeit auf 1 Minute 45 Sekunden reduzieren, indem Sie die Lanczos mit hoher Qualität skalieren und die Palette pro Frame berechnen. Die Erstellung eines Vorschaubildes mit 30 Bildern dauert nun weniger als 1 Sekunde .

  • Der Algorithmus für die Neuskalierung war der nächstgrößere Performance-Impaktor (jedoch eine weit entfernte Sekunde). Die Verwendung von fast_bilinear anstelle von lanczos sparte bei allen 15.000 Frames 150 Sekunden Rechenzeit.

  • Die am wenigsten beeinflussbare Variable war die Palettenberechnung, und diese variierte mit dem Algorithmus für die Neuskalierung. Bei mehr als 15.000 Frames mit lanczos haben wir etwa 17 Sekunden Ausführungszeit eingespart, wenn die Palettenberechnung entfällt. Mit fast_bilinear haben wir rund 75 Sekunden Ausführungszeit eingespart.

Da der Algorithmus für die Neuskalierung und die Palettenberechnung vernachlässigbar waren, haben wir sie auf höchstem Niveau gehalten. Wir haben unsere Rechenzeit von 17 Minuten auf unter 1 Sekunde reduziert, meistens, indem wir ffmpeg mitteilten, das Lesen von Eingabedateien zu überspringen.

KEY TAKEAWAY: EINGEBAUTE SKIFE vs SPIELAUSGABEN

Der Grund, warum unser Prozess so lange gedauert hat, ist, dass der Frame-Drop beim Ausführen des image2-Demuxers nicht zur Ausführungszeit beiträgt. Wenn Sie das -rFlag und den fpsFilter verwenden, wirkt sich dies auf die Anzahl der Frames aus, die in der endgültigen GIF-Datei angezeigt werden, aber ffmpeg scheint immer noch mit allen 15.000 Eingaberahmen zu tun.

Die einzige Möglichkeit, die Eingabemasken von ffmpeg zu überspringen, ist der concatDemuxer.

So generiere ich jetzt in weniger als einer Sekunde animierte GIF-Miniaturbilder auf meiner Entwicklungsmaschine, indem ich Eingabefelder überspringe:

# create text file which describes the ~30 input frames we want ffmpeg to process seq -f "file 'left_frames.%04g.jpg'" 10000 500 25000 > tmp.txt  # generate the animated gif using ffmpeg only ffmpeg -f concat -i tmp.txt -filter_complex "scale=150:-1:flags=lanczos,split=2 [a][b]; [a] palettegen [pal]; [b] fifo [b]; [b] [pal] paletteuse" output.gif