Aussagekräftige Thumbnails für ein Video mit FFmpeg

80

FFmpeg kann Bilder aus Videos aufnehmen, die als Miniaturansichten zur Darstellung des Videos verwendet werden können. Die gebräuchlichsten Methoden dazu sind im FFmpeg-Wiki erfasst .

Aber ich möchte nicht in bestimmten Abständen zufällige Bilder auswählen. Ich habe einige Optionen gefunden, die Filter in FFmpeg verwenden, um Szenenänderungen zu erfassen:

Der Filter thumbnailversucht, die repräsentativsten Frames im Video zu finden:

ffmpeg -i input.mp4 -vf  "thumbnail,scale=640:360" -frames:v 1 thumb.png

und der folgende Befehl wählt nur Frames aus, die mehr als 40% der Änderungen im Vergleich zum vorherigen aufweisen (und daher wahrscheinlich Szenenänderungen sind), und generiert eine Sequenz von 5 PNGs.

ffmpeg -i input.mp4 -vf  "select=gt(scene\,0.4),scale=640:360" -frames:v 5 thumb%03d.png

Infogutschrift für die obigen Befehle an Fabio Sonnati . Die zweite schien besser zu sein, da ich n Bilder bekommen und die besten auswählen konnte. Ich habe es versucht und es hat das gleiche Bild 5 Mal erzeugt.

Weitere Nachforschungen führten mich zu:

ffmpeg -i input.mp4 -vf "select=gt(scene\,0.5)" -frames:v 5 -vsync vfr  out%02d.png

-vsync vfrstellt sicher, dass Sie verschiedene Bilder erhalten. Dies wählt immer noch das erste Bild des Videos aus. In den meisten Fällen ist das erste Bild ein Abspann / Logo und nicht aussagekräftig. Deshalb habe ich eine -ss3 hinzugefügt , um die ersten 3 Sekunden des Videos zu verwerfen.

Mein letzter Befehl sieht so aus:

ffmpeg -ss 3 -i input.mp4 -vf "select=gt(scene\,0.5)" -frames:v 5 -vsync vfr out%02d.jpg

Das war das Beste, was ich tun konnte. Mir ist aufgefallen, dass ich nur 5 Videos auswähle, die meistens vom Anfang des Videos stammen und möglicherweise wichtige Szenen verpassen, die später im Video auftreten

Ich würde gerne Ihr Gehirn für andere bessere Optionen auswählen.

d33pika
quelle
Nette Befehlsbeispiele. FWIW, ich hatte keine Probleme mit FFmpeg-generierten JPEG-Bildern unter OS X (10.8, FFmpeg 1.1 und niedriger). Ihr vorletzter Befehl funktioniert bei mir einwandfrei - ebenso der letzte - und keines dieser Befehle führt zu leeren JPG-Dateien. Ich habe mit kompiliert libopenjpeg.. nicht sicher, ob das einen Unterschied macht.
Slhck
Danke slhck. Die Frage wurde mit den Details zu ffmpeg config / version bearbeitet. Ich habe auf diesem Computer kein Upgrade auf 1.1 durchgeführt. Ich werde das tun und sehen, ob es irgendwelche Ergebnisse ändert.
d33pika
1
Also bist du auf Ubuntu? Können Sie die neueste Git Master-Version aus einem statischen Build testen oder sich selbst kompilieren und erneut ausführen? Oder den neuesten Stall. Ich habe gerade überprüft, dass der mjpegEncoder auch für mich verwendet wird, und ich habe auch überprüft , ob jpegoptimund exiv2, beide funktionieren für mich mit allen JPG-Ergebnissen Ihrer Beispielbefehle.
Slhck
1
Ich habe aktualisiert, und es funktioniert jetzt! Ich denke, die vorherige Version hatte einige Fehler.
d33pika
Können Sie fortfahren und die neue Version der Lösung veröffentlichen, vorzugsweise mit einem Link zum Änderungsprotokoll, der den aufgetretenen Fehler anzeigt und anschließend mit der neuen Version behoben wird?
Lizz

Antworten:

29

Wie wäre es, wenn Sie im Idealfall nach dem ersten> 40% -Änderungs-Frame innerhalb von 5 Zeiträumen suchen, wobei die Zeiträume die ersten, zweiten, dritten, vierten und fünften 20% des Videos sind.

Sie können es auch in 6 Zeitspannen aufteilen und die erste Zeitspanne ignorieren, um Gutschriften zu vermeiden.

In der Praxis würde dies bedeuten, dass Sie die fps auf einen niedrigen Wert setzen, während Sie Ihren Szenenwechselcheck und Ihr Argument anwenden, um das erste Bit des Videos zu löschen.

...so etwas wie:

ffmpeg -ss 3 -i input.mp4 -vf "select=gt(scene\,0.4)" -frames:v 5 -vsync vfr -vf fps=fps=1/600 out%02d.jpg
AM
quelle
1
Es werden vollständig schwarze oder weiße Bilder aufgenommen. Wie vermeide ich diese?
D33pika
1
Es gibt Filter zum Erkennen von schwarzen Frames und Sequenzen davon ( ffmpeg.org/ffmpeg-filters.html#blackframe oder ffmpeg.org/ffmpeg-filters.html#blackdetect ). Sie können keines von beiden in Ihren wachsenden Einzeiler einarbeiten, aber Sie sollten auf jeden Fall die schwarzen Rahmen entfernen (separater Schritt) und Miniaturansichten aus dem resultierenden Video extrahieren können.
AM
5
Was weiße Frames betrifft, wird es jetzt kompliziert, aber es sieht immer noch so aus, als gäbe es einen Weg: 1. Schwarze Frames entfernen 2. Negieren (Weiß wird zu Schwarz) 3. Weiße Frames entfernen 4. Erneut negieren 5. Thumbnails extrahieren (Wenn Sie es schaffen, die schwarzen oder weißen Rahmen, insbesondere in einer Zeile, zu entfernen, können Sie sie hierher zurückschicken? Ich kann sie der Antwort für zukünftige Leser hinzufügen. ... oder Sie könnten tatsächlich eine neue Frage und Antwort erstellen.) Ich würde es definitiv befürworten.)
AM
3
So vermeiden Sie langweilige Bilder wie einfaches Schwarzweiß: Generieren Sie mehr Miniaturansichten als erforderlich, komprimieren Sie sie mit JPEG, und wählen Sie die Bilder mit größerer Dateigröße aus. Dies funktioniert überraschend gut, um anständige Thumbnails zu erhalten.
Sam Watkins
2
Dieser Befehl hat nicht funktioniert, ich habe einen Fehler erhalten Unable to find a suitable output format for 'fps=fps=1/600'. Die Lösung ist, -vfvor diesem Argument hinzuzufügen (siehe stackoverflow.com/questions/28519403/ffmpeg-command-issue )
John Wiseman
7

Definieren sinnvoll ist hart , aber wenn Sie N Thumbnails effizient komplette Video - Datei vornehmen möchten Spanning das ist , was ich Thumbnails mit Benutzer hochgeladenen Inhalte auf die Produktion zu generieren verwenden.

Pseudocode

for X in 1..N
  T = integer( (X - 0.5) * D / N )  
  run `ffmpeg -ss <T> -i <movie>
              -vf select="eq(pict_type\,I)" -vframes 1 image<X>.jpg`

Wo:

  • D - Videodauer, die von ffmpeg -i <movie>alleine gelesen wurde oder ffprobedie übrigens einen netten JSON-Ausgabe-Writer hat
  • N - Gesamtzahl der gewünschten Thumbnails
  • X - Miniaturbildnummer von 1 bis N
  • T - Zeitpunkt für Tumbnail

Einfach das obige schreibt den mittleren Keyframe jeder Partition des Films auf. Wenn der Film beispielsweise 300 Sekunden lang ist und Sie 3 Miniaturansichten möchten, wird nach 50, 150 und 250 Sekunden ein Schlüsselbild benötigt. Für 5 Thumbnails wären es 30s, 90s, 150s, 210s, 270s. Sie können N in Abhängigkeit von der Filmdauer D so einstellen, dass z. B. 5-minütige Filme 3 Miniaturansichten haben, über 1 Stunde jedoch 20 Miniaturansichten.

Performance

Jeder Aufruf des obigen ffmpegBefehls benötigt einen Sekundenbruchteil (!) Für ~ 1 GB H.264. Das liegt daran, dass es sofort zur <time>Position springt (mind -ssbefore -i) und das erste Keyframe annimmt, bei dem es sich praktisch um vollständiges JPEG handelt. Es wird keine Zeit verschwendet, um den Film an die genaue Zeitposition anzupassen.

Nachbearbeitung

Sie können oben mit scaleoder jeder anderen Größenänderungsmethode mischen . Sie können auch einfarbige Rahmen entfernen oder versuchen, sie mit anderen Filtern wie zu mischen thumbnail.

gertas
quelle
2
wow, -ss Ndas vorher umzieht , -iist ein erstaunlicher Tipp. Dankeschön!
Apinstein
2

Ich habe einmal etwas Ähnliches gemacht, aber ich habe alle Frames des Videos (in 1 fps) exportiert und sie mit einem gefundenen Perl-Dienstprogramm verglichen, das den Unterschied zwischen Bildern berechnet. Ich habe jeden Frame mit vorherigen Thumbnails verglichen. Wenn er sich von allen Thumbnails unterscheidet, habe ich ihn der Thumbnails-Sammlung hinzugefügt. Der Vorteil hierbei ist, dass ffmpeg 2 Frames von A exportiert, wenn sich Ihr Video von Szene A nach B bewegt und diese zu A zurückkehren.

Eran Ben-Natan
quelle
Mit welchen Faktoren haben Sie 2 Bilder verglichen?
d33pika
Leider erinnere ich mich nicht, es war ziemlich lange her. Sie müssen zuerst entscheiden, welche Vergleichsmethode verwendet werden soll, und dann einige Tests durchführen, um den richtigen Faktor zu finden. Das sollte nicht lange dauern.
Eran Ben-Natan
Ich denke hier nur laut nach, aber ist das nicht eine schlechtere Option? Wenn Sie zwei exportierte Bilder aus einem Video vergleichen, haben Sie bereits einige Informationen verloren. Ich meine, es ist einfacher, Ähnlichkeiten aus Codec-Informationen zu berechnen als aus 2 Bildern, nicht wahr? Vor kurzem habe ich einige Algorithmen / Bibliotheken untersucht, um Bilder zu vergleichen, und sie funktionieren nicht so gut, wie es möglicherweise erforderlich ist.
Samuel
Nun, mit ffmpeg können Sie Frames ohne Qualitätsverlust mit dem same_quality-Flag extrahieren. Wenn Sie Codec-Informationen verwenden, müssen Sie sicherstellen, dass Sie nicht nur die Iframes erhalten. Wie auch immer, dieses Perl-Dienstprogramm hat bei mir einwandfrei funktioniert und ist sehr konfigurierbar. Schauen Sie hier: search.cpan.org/~avif/Image-Compare-0.3/Compare.pm
Eran Ben-Natan
2

Versuche dies

 ffmpeg -i input.mp4 -vf fps= no_of_thumbs_req/total_video_time out%d.png

Mit diesem Befehl kann ich die erforderliche Anzahl von Miniaturansichten generieren, die für das gesamte Video repräsentativ sind.

Verlorenes Hündchen
quelle
Wäre das nicht die richtige Formel für fpssein (no_of_frames_req * fps_of_vid) / total_video_frames?
Flolilo
Ich suche nach einer gegenteiligen Lösung: Wählen Sie mehr Bilder aus Zeiten, in denen die Kamera stabiler ist und sich nicht bewegt. (wo der Unterschied zwischen aufeinanderfolgenden Frames kleiner ist, nicht größer). Gibt es eine Möglichkeit, das zu tun?
Tina J
1

Hier ist, was ich tue, um ein periodisches Vorschaubild für Live-m3u8-Streams zu generieren, das als Poster verwendet werden kann. Ich habe festgestellt, dass eine fortlaufende ffmpeg-Task zum Generieren von Thumbnails meine gesamte CPU auffrisst. Stattdessen führe ich alle 60 Sekunden einen Cronjob aus, der alle Thumbnails für meine Streams generiert.

#!/usr/bin/env bash

## this is slow but gives better thumbnails (takes about 1+ minutes for 20 files)
#-vf thumbnail,scale=640:360 -frames:v 1 poster.png

## this is faster but thumbnails are random (takes about 8 seconds for 20 files)
#-vf scale=640:360 -frames:v 1 poster.png
cd ~/path/to/streams

find . -type f \
  -name "*.m3u8" \
  -execdir sh -c 'ffmpeg -y -i "$0" -vf scale=640:360 -frames:v 1 "poster.png"' \
  {} \;

Wenn Sie häufiger als 60 Sekunden arbeiten müssen (Beschränkung von Cronjob), können Sie eine whileSchleife in Ihrem Skript erstellen, die für immer ausgeführt wird. Fügen Sie einfach a hinzu sleep 30, um die Frequenz auf 30 Sekunden zu ändern. Ich empfehle dies jedoch nicht mit einer großen Anzahl von Videos, da der vorherige Lauf möglicherweise nicht abgeschlossen ist, bevor der nächste startet.

Idealerweise führe ich Cronjob nur alle 5 Minuten aus.

chovy
quelle