La manière la plus rapide d'extraire des cadres à l'aide de ffmpeg?
Salut je dois extraire des cadres de vidéos en utilisant ffmpeg.. Est-il un moyen plus rapide de faire que cela:
ffmpeg -i file.mpg -r 1/1 $filename%03d.jpg
?
4 réponses
si L'étape D'encodage JPEG est trop exigeante en termes de performances, vous pouvez toujours stocker les cadres non compressés en tant qu'images BMP:
ffmpeg -i file.mpg -r 1/1 $filename%03d.bmp
cela a aussi l'avantage de ne pas subir plus de perte de qualité par quantification en transcodant en JPEG. (PNG est également sans perte mais prend généralement beaucoup plus de temps que JPEG pour encoder.)
est tombé sur cette question, donc voici une comparaison rapide. Comparez ces deux façons différentes d'extraire une image par minute d'une vidéo de 38m07s de long:
time ffmpeg -i input.mp4 -filter:v fps=fps=1/60 ffmpeg_%0d.bmp
1m36.029s
cela prend du temps parce que ffmpeg analyse le fichier vidéo entier pour obtenir les images désiré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.689
C'est environ 20 fois plus rapide. Nous employons la recherche rapide pour aller à l'index de temps désiré et extraire un cadre, puis appeler 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'entrée vidéo -i
option.
notez qu'il est préférable d'utiliser -filter:v -fps=fps=...
au lieu de -r
comme ce dernier peut être inexact. bien que le billet est marqué comme fixe , j'ai quand même fait l'expérience de certains problèmes, donc mieux jouer sûr.
si vous savez exactement quels cadres 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 pour Imagemagick's montage pour obtenir 10 images de prévisualisation de toutes les vidéos. Évidemment, les numéros de trame que vous aurez besoin de comprendre en utilisant 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:
- dans les expressions ffmpeg
+
signifie Ou et*
pour et -
\,
échappe tout simplement au caractère,
1519200920" - sans
-vsync vfr -q:v 2
ça ne semble pas marcher mais je ne sais pas pourquoi-quelqu'un?
dans mon cas, j'ai besoin de cadres au moins chaque seconde. J'ai utilisé l'approche "chercher à" ci-dessus, mais je me suis demandé si je pouvais faire un parallèle avec la tâche. J'ai utilisé la méthode n 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=
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 limité le nombre de threads concurrents à N.
cela a amélioré le "chercher à" approche de 26 secondes à 16 secondes dans mon cas. Le seul problème est que le thread principal ne sort pas proprement de nouveau vers le terminal puisque stdout est inondé.