Miniatures significatives pour une vidéo utilisant FFmpeg

80

FFmpeg peut capturer des images à partir de vidéos qui peuvent être utilisées en tant que vignettes pour représenter la vidéo. La plupart des manières de le faire sont décrites dans le wiki de FFmpeg .

Mais, je ne veux pas choisir des images aléatoires à certains intervalles. J'ai trouvé quelques options en utilisant des filtres sur FFmpeg pour capturer les changements de scène:

Le filtre thumbnailtente de trouver les images les plus représentatives de la vidéo:

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

et la commande suivante sélectionne uniquement les images contenant plus de 40% des modifications par rapport aux précédentes (et donc probablement des modifications de scènes) et génère une séquence de 5 fichiers PNG.

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

Crédit d'information pour les commandes ci-dessus à Fabio Sonnati . La seconde me semblait meilleure car je pouvais obtenir n images et choisir le meilleur. Je l'ai essayé et il a généré la même image 5 fois.

Une enquête plus poussée m'a amené à:

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

-vsync vfrassure que vous obtenez des images différentes. Cela choisit toujours toujours la première image de la vidéo. Dans la plupart des cas, la première image est un générique / logo et n'a pas de sens. J'ai donc ajouté un -ss3 pour supprimer les 3 premières secondes de la vidéo.

Ma dernière commande ressemble à ceci:

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

C'était le mieux que je puisse faire. J'ai remarqué que, puisque je ne sélectionne que 5 vidéos, toutes datent pour la plupart du début de la vidéo et risquent de manquer des scènes importantes qui se produisent plus tard dans la vidéo.

Je voudrais choisir votre cerveau pour toute autre meilleure option.

d33pika
la source
Bons exemples de commandes. FWIW, je n’ai rencontré aucun problème avec les images JPEG générées par FFmpeg sous OS X (10.8, FFmpeg 1.1 et versions ultérieures). Votre dernière à la dernière commande me convient - tout comme la dernière - et aucun de ces résultats ne donne lieu à des fichiers JPG vierges. J'ai compilé avec libopenjpeg.. ne sais pas si cela fait une différence.
Slhck
Merci slhck. Edité la question avec les détails de configuration / version de ffmpeg. Je n'ai pas mis à niveau à 1.1 sur cette machine. Je vais le faire et voir si cela change les résultats.
d33pika
1
Donc, vous êtes sur Ubuntu? Pouvez-vous essayer la dernière version de Git Master à partir d’une construction statique ou en vous compilant et en cours d’exécution? Ou la dernière écurie. Je viens de vérifier, il utilise également l' mjpegencodeur pour moi, et j'ai également vérifié jpegoptimet exiv2, les deux fonctionnant bien pour moi avec tous les résultats JPG de vos exemples de commandes.
Slhck
1
J'ai mis à jour, et cela fonctionne maintenant! Je suppose que la version précédente avait quelques bugs.
d33pika
Pouvez-vous continuer et publier la solution - nouvelle version, de préférence avec un lien vers changelog indiquant le bogue que vous avez rencontré et corrigé par la suite avec la nouvelle version?
Lizz

Réponses:

29

Que diriez-vous de rechercher, idéalement, la première trame> 40% de changement dans chacun des 5 intervalles de temps, où les intervalles de temps sont les 1ère, 2ème, 3ème, 4ème et 20ème parties de la vidéo.

Vous pouvez également le diviser en 6 périodes et ignorer le premier pour éviter les crédits.

En pratique, cela signifie que vous devez définir un nombre faible de fps lorsque vous appliquez votre contrôle de changement de scène et votre argument pour rejeter le premier bit de la vidéo.

...quelque chose comme:

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
UN M
la source
1
Des images entièrement noires ou blanches sont ramassées. Comment puis-je les éviter?
d33pika
1
Il existe des filtres pour détecter les cadres noirs et leurs séquences ( ffmpeg.org/ffmpeg-filters.html#blackframe ou ffmpeg.org/ffmpeg-filters.html#blackdetect ). Vous ne pouvez intégrer aucun de ces éléments dans votre one-liner en croissance, mais vous devez absolument être en mesure de supprimer les cadres noirs (étape distincte) et d’extraire les vignettes de la vidéo obtenue.
AM
5
En ce qui concerne les cadres blancs, cela devient compliqué, mais il semble toujours y avoir un moyen: 1. de supprimer les cadres noirs 2. de nier (le blanc devient noir) 3. de supprimer les cadres blancs 4. de nier de nouveau 5. d'extraire les vignettes (Si vous parvenez à effacer les cadres noir ou blanc, en particulier sur une ligne, pouvez-vous le poster ici? Je peux l'ajouter à la réponse pour les futurs lecteurs. ... ou en fait, vous pouvez créer une nouvelle question et une réponse Je l’enverrais certainement.)
AM le
3
Pour éviter les images ennuyeuses telles que le noir et blanc: générez plus de miniatures que nécessaire, compressez-les au format jpeg, puis choisissez les images avec une taille de fichier supérieure. Cela fonctionne étonnamment bien par lui-même pour obtenir des vignettes correctes.
Sam Watkins
2
Cette commande n'a pas fonctionné, j'ai eu l'erreur Unable to find a suitable output format for 'fps=fps=1/600'. La solution consiste à ajouter -vfavant cet argument (voir stackoverflow.com/questions/28519403/ffmpeg-command-issue )
John Wiseman
7

Il est difficile de définir le sens, mais si vous souhaitez que N vignettes couvrent efficacement l’ensemble du fichier vidéo, c’est ce que j’utilise pour générer des vignettes lors de la production avec le contenu téléchargé par l’utilisateur.

Pseudo-code

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`

Où:

  • D - durée de la vidéo lue ffmpeg -i <movie>seule ou ffprobeavec un très bon graveur de sortie JSON
  • N - nombre total de vignettes que vous voulez
  • X - numéro de vignette, de 1 à N
  • T - point de temps pour une miniature

Simplement ce qui précède écrit l'image-clé centrale de chaque partition du film. Par exemple, si le film a une longueur de 300 secondes et que vous souhaitez 3 vignettes, il faut une image clé après 50, 150 et 250 secondes. Pour 5 vignettes, il s'agirait de 30, 90, 150, 210, 270. Vous pouvez ajuster N en fonction de la durée du film D, par exemple, un film de 5 minutes aura 3 vignettes mais plus d'une heure aura 20 vignettes.

Performance

Chaque appel de la ffmpegcommande ci-dessus prend une fraction de seconde (!) Pour environ 1 Go H.264. C’est parce qu’il saute instantanément à la <time>position (à l’esprit -ssauparavant -i) et prend la première image clé qui est pratiquement complète en JPEG. Il n'y a pas de temps perdu pour rendre le film pour correspondre à la position exacte du temps.

Post-traitement

Vous pouvez mélanger ci-dessus avec scaleou toute autre méthode de redimensionnement. Vous pouvez également supprimer les cadres de couleur unie ou essayer de le mélanger à d’autres filtres thumbnail.

Gertas
la source
2
wow passer -ss Nà avant -iest une astuce incroyable. Merci!
Apinstein
2

Une fois, j’ai fait quelque chose de similaire, mais j’ai exporté toutes les images de la vidéo (en 1 ips) et les ai comparées à un utilitaire Perl que j’ai trouvé, qui calcule la différence entre les images. J'ai comparé chaque image aux vignettes précédentes, et si elle était différente de toutes les vignettes, je l'ai ajoutée à la collection de vignettes. L’avantage ici est que si votre vidéo passe de la scène A à B et qu’elle revient à A, ffmpeg exporte 2 images de A.

Eran Ben-Natan
la source
Quels facteurs avez-vous utilisé pour comparer 2 images?
d33pika
Malheureusement, je ne m'en souviens pas, c'était il y a assez longtemps. Vous devez d’abord choisir la méthode de comparaison à utiliser, puis faire quelques tests pour trouver le facteur correct. Cela ne devrait pas prendre longtemps.
Eran Ben-Natan
Je ne fais que penser à voix haute, mais n’est-ce pas une option pire? Lorsque vous comparez 2 images exportées d'une vidéo, vous en avez déjà perdu certaines informations. Je veux dire, il est plus facile de calculer la similarité à partir des informations de codec que de 2 images, n'est-ce pas? Récemment, j'ai étudié des algorithmes / bibliothèques pour comparer des images et celles-ci ne fonctionnent pas aussi bien que nécessaire.
Samuel
Eh bien, avec ffmpeg, vous pouvez extraire des images sans perte de qualité en utilisant le drapeau same_quality. Si vous utilisez des informations de codec, vous devez vérifier que vous n’obtenez pas que les Iframes. Quoi qu'il en soit, cet utilitaire perl a parfaitement fonctionné pour moi et est très configurable. Regardez ici: search.cpan.org/~avif/Image-Compare-0.3/Compare.pm
Eran Ben-Natan
2

Essaye ça

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

En utilisant cette commande, je peux générer le nombre requis de vignettes qui sont représentatives de la vidéo entière.

Chiot perdu
la source
Ne serait pas la bonne formule pour fpsêtre (no_of_frames_req * fps_of_vid) / total_video_frames?
Flolilo
Je cherche une solution opposée: sélectionner davantage de cadres dans les périodes où la caméra est plus stable et ne bouge pas? (où la différence entre les images successives est moindre et non supérieure). Y-a-t-il un moyen de faire ça?
Tina J
1

Voici ce que je fais pour générer une vignette périodique des flux m3u8 en direct à utiliser comme affiche. J'ai découvert qu'exécuter une tâche ffmpeg continue juste pour générer des vignettes consommait tout mon processeur. Je me suis donc tourné vers un cronjob toutes les 60 secondes pour générer toutes les vignettes de mes flux.

#!/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"' \
  {} \;

Si vous devez effectuer plus de 60 secondes (limitation de cronjob), vous pouvez créer une whileboucle dans votre script qui s'exécutera à jamais. Ajoutez simplement un sleep 30pour modifier la fréquence à 30 secondes. Je ne recommande toutefois pas de faire cela avec un grand nombre de vidéos, car la précédente exécution risque de ne pas s'achever avant le début de la suivante.

Idéalement avec cronjob je le lance juste toutes les 5 minutes.

chovy
la source