104 votes

Des vignettes significatives pour une vidéo à l'aide de FFmpeg

FFmpeg peut capturer des images à partir de vidéos qui peuvent être utilisées comme vignettes pour représenter la vidéo. Les méthodes les plus courantes pour ce faire sont décrites dans le fichier 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 essaie 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 seulement les images qui ont plus de 40% de changements par rapport à la précédente (et donc probablement des changements de scène) et génère une séquence de 5 PNGs.

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 semblait meilleure car elle permettait d'obtenir n images et de choisir la meilleure. Je l'ai essayé et il a généré la même image 5 fois.

Quelques recherches supplémentaires m'ont conduit à :

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

-vsync vfr vous permet d'obtenir des images différentes. Cela choisit toujours la première image de la vidéo, dans la plupart des cas, la première image est le générique/logo et n'a pas de sens, donc j'ai ajouté une option -ss 3 pour rejeter les 3 premières secondes de la vidéo.

Ma commande finale 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 pouvais faire. J'ai remarqué que, puisque je n'ai choisi que 5 vidéos, elles sont toutes principalement du début de la vidéo et peuvent manquer des scènes importantes qui se produisent plus tard dans la vidéo.

J'aimerais que vous me fassiez part de toute autre meilleure option.

35voto

srcspider Points 2623

Pourquoi ne pas rechercher, idéalement, la première image présentant une variation de plus de 40 % dans chacune des 5 périodes de temps, les périodes de temps étant les 1er, 2e, 3e, 4e et 5e 20 % de la vidéo ?

Vous pourriez aussi le diviser en 6 périodes et ne pas tenir compte de la première pour éviter les crédits.

Dans la pratique, cela signifie qu'il faut fixer le nombre d'images par seconde à un chiffre faible tout en appliquant votre contrôle de changement de scène et votre argument pour rejeter le premier morceau 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

13voto

Iambert Points 21

Définition de significatif c'est difficile mais si vous voulez faire N vignettes couvrant efficacement tout le fichier vidéo, c'est ce que j'utilise pour générer des vignettes en production avec du contenu téléchargé par les utilisateurs.

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 ffprobe qui a un bon rédacteur de sortie JSON.
  • N - nombre total de vignettes souhaitées
  • X - numéro de la vignette, de 1 à N
  • T - point de temps pour l'ongle du pouce

Il suffit de noter l'image clé centrale de chaque partition du film. Par exemple, si le film dure 300s et que vous voulez 3 vignettes, il faut une image clé après 50s, 150s et 250s. Pour 5 vignettes, ce sera 30s, 90s, 150s, 210s, 270s. Vous pouvez ajuster N en fonction de la durée du film D, c'est-à-dire qu'un film de 5 minutes aura 3 vignettes mais qu'un film de plus d'une heure aura 20 vignettes.

Performance

Chaque invocation de l'élément ci-dessus ffmpeg prend une fraction de seconde ( !) pour ~1GB H.264. C'est parce qu'elle passe instantanément à <time> position (esprit -ss avant -i ) et prend la première image clé qui est pratiquement un JPEG complet. Il n'y a aucune perte de temps pour le rendu de la vidéo afin de correspondre à la position temporelle exacte.

Post-traitement

Vous pouvez mélanger ce qui précède avec scale ou toute autre méthode de redimensionnement. Vous pouvez également supprimer les cadres de couleur unie ou essayer de le combiner avec d'autres filtres tels que thumbnail .

4voto

LostPuppy Points 141

Essayez ceci

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

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

2voto

Oudin Points 331

J'ai déjà fait quelque chose de similaire, mais j'ai exporté toutes les images de la vidéo (en 1 fps) et je les ai comparées avec un utilitaire perl que j'ai trouvé qui calcule la différence entre les images. Je comparais chaque image aux vignettes précédentes, et si elle était différente de toutes les vignettes, je l'ajoutais à 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 exportera 2 images de A.

1voto

MrSmith42 Points 101

Voici ce que je fais pour générer une vignette périodique pour les flux m3u8 en direct afin de l'utiliser comme affiche. J'ai découvert que l'exécution d'une tâche ffmpeg continue juste pour générer des vignettes consomme tout mon processeur, donc à la place je lance un cronjob toutes les 60 secondes qui génère toutes les vignettes pour 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 avez besoin de faire plus fréquemment que 60 secondes (limitation de cronjob), alors vous pouvez faire un while dans votre script et il s'exécutera pour toujours. Ajoutez simplement un sleep 30 pour changer la fréquence à 30 secondes. Je ne recommande pas de faire cela avec un grand nombre de vidéos, car le cycle précédent peut ne pas être terminé avant que le suivant ne commence.

Idéalement, avec cronjob, je l'exécute toutes les 5 minutes.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X