web-dev-qa-db-fra.com

Miniatures significatives pour une vidéo utilisant FFmpeg

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 FFmpeg Wiki .

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 thumbnail tente 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% de 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 vfr veille à ce que vous obteniez 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 -ss 3 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.

75
d33pika

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 tout en appliquant 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
25
A.M.

Définir significatif est difficile, mais si vous voulez que N vignettes recouvre efficacement l’ensemble du fichier vidéo, c’est ce que j’utilise pour générer des vignettes sur 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 à partir de ffmpeg -i <movie> seul ou de ffprobe qui a un Nice writer de sortie JSON btw
  • 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 dure 300 secondes et que vous souhaitez 3 vignettes, il faut une image clé après 50, 150 et 250. Pour 5 vignettes, il s'agirait de 30, 90, 150, 210, 270. Vous pouvez ajuster N en fonction de la durée de la vidéo D, par ex. Un film de 5 minutes aura 3 vignettes mais plus d'une heure aura 20 vignettes.

Performance

Chaque appel de la commande ffmpeg ci-dessus prend une fraction de seconde (!) Pour environ 1 Go H.264. En effet, il passe instantanément à la position <time> (esprit -ss avant -i) et prend la première image clé qui est pratiquement complète au format 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 scale ou toute autre méthode de redimensionnement. Vous pouvez également supprimer les cadres de couleur unie ou essayer de le mélanger à d’autres filtres tels que thumbnail.

6
gertas

J'ai déjà 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.

2
Eran Ben-Natan

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.

2
LostPuppy

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 mange tout mon processeur, alors je lance un cronjob toutes les 60 secondes qui génère 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 boucle while dans votre script, qui s'exécutera à jamais. Ajoutez simplement un sleep 30 pour changer 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.

1
chovy