Le moyen le plus rapide d'extraire des cadres en utilisant ffmpeg?


118

Salut, j'ai besoin d'extraire des cadres de vidéos en utilisant ffmpeg .. Y a-t-il un moyen plus rapide de le faire que ceci:

ffmpeg -i file.mpg -r 1/1 $filename%03d.jpg

?


1
^ URL mise à jour: trac.ffmpeg.org/wiki/Seeking
Lambart

5
Si vous avez des cycles de processeur à perdre, vous pouvez extraire plusieurs vidéos en parallèle:parallel -i {} -r 1/1 {.}-%03d.bmp ::: *mpg
Ole Tange

Réponses:


156

Si l'étape de codage JPEG est trop gourmande en performances, vous pouvez toujours stocker les images non compressées sous forme d'images BMP:

ffmpeg -i file.mpg -r 1/1 $filename%03d.bmp

Cela présente également l'avantage de ne pas entraîner davantage de perte de qualité grâce à la quantification par transcodage au format JPEG. (PNG est également sans perte mais a tendance à prendre beaucoup plus de temps que JPEG à encoder.)


9
Il en résulte beaucoup de chutes d'images sur ma machine. Puis-je dire à ffmpeg de tout rendre?
Evi1M4chine

45
@ Evi1M4chine supprimez simplement le paramètre -r cela extraira toutes les images
studioj

14
Je voudrais ajouter que si JPEG n'est pas vraiment difficile pour le processeur, les Bitmaps non compressés sont vraiment très difficiles pour le stockage, donc je doute que vous obtiendrez un débit plus élevé avec BMP par rapport au JPEG.
Marcus Müller

4
Pour extraire toutes les images:ffmpeg -r 1 file.mp4 -r 1 "$filename%03d.png"
fiatjaf

10
Vous voulez dire ffmpeg -r 1 -i file.mp4 -r 1 "$filename%03d.png, non? (il vous manquait le -i)
Joschua

68

Je suis tombé sur cette question, alors voici une comparaison rapide. Comparez ces deux façons différentes d'extraire une image par minute d'une vidéo de 38 min 07 s:

time ffmpeg -i input.mp4 -filter:v fps=fps=1/60 ffmpeg_%0d.bmp

1m36.029s

Cela prend du temps car ffmpeg analyse l'intégralité du fichier vidéo pour obtenir les images souhaitées.

time for i in {0..39} ; do ffmpeg -accurate_seek -ss `echo $i*60.0 | bc` -i input.mp4   -frames:v 1 period_down_$i.bmp ; done

0m4.689s

C'est environ 20 fois plus rapide. Nous utilisons la recherche rapide pour accéder à l'index temporel souhaité et extraire une image, puis appelons ffmpeg plusieurs fois pour chaque index temporel. Notez qu'il -accurate_seek s'agit de la valeur par défaut et assurez-vous d'ajouter -ssavant l' -ioption d' entrée vidéo .

Notez qu'il est préférable d'utiliser -filter:v -fps=fps=...au lieu de -rcar ce dernier peut être inexact. Bien que le ticket soit marqué comme corrigé , j'ai toujours rencontré des problèmes, alors mieux vaut jouer en toute sécurité.


6
Février 2016: depuis ffmpeg 2.1, l'option de recherche précise est désormais par défaut - trac.ffmpeg.org/wiki/Seeking
mafrosis

1
bcn'est pas un paquet Ubuntu, on peut utiliser au lieu bash: let "i = $i * 60". BTW - excellente idée
gilad mayani

3
Bon conseil en ajoutant -ssavant -i. Sinon, toute la vidéo sera décodée et les images non requises seront supprimées
MoustafaAAtta

2
Puisqu'il s'agit du sommet de Google, j'aimerais noter qu'en 2018, c'est toujours une approche qui rapporte des dividendes. Le meilleur résultat semble être d'en exécuter un ffmpegpar cœur de votre hôte - ce qui (pour bmp) produit des améliorations presque linéaires de la vitesse (jusqu'à ce que vous rencontriez un autre goulot d'étranglement, comme le disque).
Knetic

Cela devrait être la réponse acceptée, car la question porte sur le «moyen le plus rapide». De toute évidence, vous devez connaître la variable de sortie pour la «boucle for» en utilisant ffprobe, ce qui, à mon avis, est encore plus rapide que les autres méthodes.
iamprem

9

Si vous savez exactement quelles images extraire, par exemple 1, 200, 400, 600, 800, 1000, essayez d'utiliser:

select='eq(n\,1)+eq(n\,200)+eq(n\,400)+eq(n\,600)+eq(n\,800)+eq(n\,1000)' \
       -vsync vfr -q:v 2

J'utilise ceci avec un tuyau vers le montage d'Imagemagick pour obtenir un aperçu de 10 images de toutes les vidéos. De toute évidence, les numéros de cadre que vous devrez déterminer en utilisantffprobe

ffmpeg -i myVideo.mov -vf \
    select='eq(n\,1)+eq(n\,200)+eq(n\,400)+eq(n\,600)+eq(n\,800)+eq(n\,1000)',scale=320:-1 \
    -vsync vfr -q:v 2 -f image2pipe -vcodec ppm - \
  | montage -tile x1 -geometry "1x1+0+0<" -quality 100 -frame 1 - output.png

.

Petite explication:

  1. Dans les expressions ffmpeg +signifie OR et *AND
  2. \,échappe simplement au ,personnage
  3. Sans -vsync vfr -q:v 2cela ne semble pas fonctionner mais je ne sais pas pourquoi - n'importe qui?

4

Je l'ai essayé. 3600 images en 32 secondes. votre méthode est vraiment lente. Vous devriez essayer ceci.

ffmpeg -i file.mpg -s 240x135 -vf fps=1 %d.jpg

J'essaie ffmpeg -i "input URL" -vf fps=1/5 out%d.png où l'URL d'entrée doit être un lien https.
x2212 le

ffmpeg -i file.mpg -vf fps=1 %d.jpg
Kishan Vaghela le

1

Dans mon cas, j'ai besoin de cadres au moins toutes les secondes. J'ai utilisé l'approche «chercher» ci-dessus, mais je me suis demandé si je pouvais paralléliser la tâche. J'ai utilisé les processus N avec l'approche FIFO ici: /unix/103920/parallelize-a-bash-for-loop/216475#216475

open_sem(){
  mkfifo /tmp/pipe-$$
  exec 3<>/tmp/pipe-$$
  rm /tmp/pipe-$$
  local i=$1
  for((;i>0;i--)); do
    printf %s 000 >&3
  done
}
run_with_lock(){
    local x
    read -u 3 -n 3 x && ((0==x)) || exit $x
    (
    "$@" 
    printf '%.3d' $? >&3
    )&
}
N=16
open_sem $N
time for i in {0..39} ; do run_with_lock ffmpeg -ss `echo $i` -i /tmp/input/GOPR1456.MP4  -frames:v 1 /tmp/output/period_down_$i.jpg  & done

Essentiellement, j'ai bifurqué le processus avec & mais j'ai limité le nombre de threads simultanés à N.

Cela a amélioré l'approche «chercher à» de 26 secondes à 16 secondes dans mon cas. Le seul problème est que le thread principal ne revient pas proprement vers le terminal car stdout est noyé.


En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.