Bonjour, j'ai besoin d'extraire des images de vidéos en utilisant ffmpeg. Y a-t-il un moyen plus rapide de le faire que cela:
ffmpeg -i file.mpg -r 1/1 $filename%03d.jpg
?
Si l'étape d'encodage JPEG est trop intensive 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 engendrer davantage de perte de qualité grâce à la quantification par transcodage au format JPEG. (Le format PNG est également sans perte mais a tendance à prendre beaucoup plus de temps que JPEG à encoder.)
Je suis tombé sur cette question, alors voici une comparaison rapide. Comparez ces deux manières différentes d'extraire une image par minute d'une vidéo d'une longueur de 38m07s:
time ffmpeg -i input.mp4 -filter:v fps=fps=1/60 ffmpeg_%0d.bmp
1m36.029s
Cela prend beaucoup de temps, car ffmpeg analyse l’ensemble 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 vite. Nous utilisons la recherche rapide pour atteindre l'index de temps souhaité et extraire une image, puis appelons ffmpeg plusieurs fois pour chaque index de temps. Notez que -accurate_seek
est la valeur par défaut Et assurez-vous d’ajouter -ss
avant l’option vidéo -i
.
Notez qu'il vaut mieux utiliser -filter:v -fps=fps=...
au lieu de -r
comme ce dernier peut être inexact. Bien que le ticket est marqué comme étant fixe , j'ai quand même rencontré des problèmes, il est donc préférable de jouer prudemment.
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 de Imagemagick pour obtenir un aperçu de 10 images à partir de n'importe quelle vidéo. De toute évidence, les numéros de trame que vous devez comprendre à l'aide de ffprobe
ffmpeg -i myVideo.mov -vf \
select='eq(n\,1)+eq(n\,200)+eq(n\,400)+eq(n\,600)+eq(n\,800)+eq(n\,100)',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:
+
correspond à OR et *
à AND.\,
échappe simplement au caractère ,
-vsync vfr -q:v 2
, cela ne semble pas fonctionner, mais je ne sais pas pourquoi - quelqu'un?Je l'ai essayé. 3600 image en 32 secondes. votre méthode est vraiment lente. Tu devrais essayer ça.
ffmpeg -i fichier.mpg -s 240x135 -vf fps = 1% d.jpg
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 N processus avec l'approche FIFO ici: https://unix.stackexchange.com/questions/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
En gros, j'ai lancé 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 retourne pas proprement au terminal, car stdout est inondé.