¿La forma más rápida de extraer marcos usando ffmpeg?

118

Hola, necesito extraer fotogramas de videos usando ffmpeg. ¿Hay una forma más rápida de hacerlo que esta?

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

?

Stpn
fuente
1
^ URL actualizada: trac.ffmpeg.org/wiki/Seeking
Lambart
5
Si tiene ciclos de CPU de sobra, puede extraer de varios videos en paralelo:parallel -i {} -r 1/1 {.}-%03d.bmp ::: *mpg
Ole Tange

Respuestas:

155

Si el paso de codificación JPEG requiere demasiado rendimiento, siempre puede almacenar los fotogramas sin comprimir como imágenes BMP:

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

Esto también tiene la ventaja de no incurrir en una mayor pérdida de calidad a través de la cuantificación mediante la transcodificación a JPEG. (PNG tampoco tiene pérdida, pero tiende a tardar mucho más en codificarse que JPEG)

Mike multimedia
fuente
9
Esto da como resultado que se caiga una gran cantidad de marcos en mi máquina. ¿Puedo decirle a ffmpeg que renderice todo?
Evi1M4chine
45
@ Evi1M4chine simplemente elimine el parámetro -r, esto extraerá todos los cuadros
studioj
14
Me gustaría agregar que si bien JPEG no es realmente difícil para la CPU, los mapas de bits sin comprimir son realmente difíciles para el almacenamiento, por lo que dudo que obtenga un mayor rendimiento con BMP en comparación con JPEG.
Marcus Müller
4
Para extraer todos los fotogramas:ffmpeg -r 1 file.mp4 -r 1 "$filename%03d.png"
fiatjaf
10
¿Quieres decir ffmpeg -r 1 -i file.mp4 -r 1 "$filename%03d.png, verdad? (te faltaba el -i)
Joschua
68

Encontré esta pregunta, así que aquí hay una comparación rápida. Compare estas dos formas diferentes de extraer un fotograma por minuto de un video de 38 min 07 s de duración:

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

1 min 36,029 s

Esto lleva mucho tiempo porque ffmpeg analiza todo el archivo de vídeo para obtener los fotogramas deseados.

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

0 min 4.689 s

Esto es aproximadamente 20 veces más rápido. Usamos la búsqueda rápida para ir al índice de tiempo deseado y extraer un marco, luego llamamos a ffmpeg varias veces para cada índice de tiempo. Tenga en cuenta que -accurate_seek es el predeterminado y asegúrese de agregar -ssantes de la -iopción de video de entrada .

Tenga en cuenta que es mejor usarlo en -filter:v -fps=fps=...lugar de -rya que este último puede ser inexacto. Aunque el boleto está marcado como fijo , todavía experimenté algunos problemas, así que es mejor ir a lo seguro.

azul naranja
fuente
6
Febrero de 2016: a partir de ffmpeg 2.1, la opción de búsqueda precisa ahora es predeterminada - trac.ffmpeg.org/wiki/Seeking
mafrosis
1
bcno es un paquete de Ubuntu nativo, en lugar uno puede usar bash: let "i = $i * 60". Por cierto - excelente idea
gilad mayani
3
Buen consejo añadiendo -ssantes -i. De lo contrario, se decodificará todo el video y se descartarán los fotogramas no necesarios
MoustafaAAtta
2
Dado que este es el mejor de Google, me gustaría señalar que en 2018 este sigue siendo un enfoque que rinde dividendos. El mejor resultado parece ser ejecutar uno ffmpegpor núcleo de su host, lo que (para bmp) produce mejoras casi lineales en la velocidad (hasta que llega a algún otro cuello de botella, como el disco).
Knetic
Esta debería ser la respuesta aceptada, ya que la pregunta es sobre la 'forma más rápida'. Obviamente, necesita conocer la variable de salida para el 'bucle for' usando ffprobe, que creo que aún es más rápido en comparación con los otros métodos.
iamprem
9

Si sabe exactamente qué fotogramas extraer, por ejemplo, 1, 200, 400, 600, 800, 1000, intente usar:

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

Estoy usando esto con una tubería para el montaje de Imagemagick para obtener una vista previa de 10 cuadros de cualquier video. Obviamente, los números de fotogramas que necesitará averiguar usandoffprobe

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

.

Pequeña explicación:

  1. En ffmpeg, las expresiones +representan OR y *AND
  2. \,es simplemente escapar del ,personaje
  3. Sin -vsync vfr -q:v 2él no parece funcionar, pero no sé por qué, ¿alguien?
Voy
fuente
4

Lo intenté. 3600 fotogramas en 32 segundos. tu método es realmente lento. Deberías probar esto.

ffmpeg -i file.mpg -s 240x135 -vf fps=1 %d.jpg
Kübra
fuente
Estoy intentando ffmpeg -i "input URL" -vf fps=1/5 out%d.png que la URL de entrada sea un enlace https.
x2212
ffmpeg -i file.mpg -vf fps=1 %d.jpg
Kishan Vaghela
1

En mi caso, necesito fotogramas al menos cada segundo. Usé el enfoque de 'buscar' anterior, pero me pregunté si podría paralelizar la tarea. Utilicé los procesos N con enfoque FIFO aquí: /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

Esencialmente bifurqué el proceso con & pero limité el número de subprocesos concurrentes a N.

Esto mejoró el enfoque de 'buscar' de 26 segundos a 16 segundos en mi caso. El único problema es que el hilo principal no sale limpiamente de regreso a la terminal ya que stdout se inunda.

Tycon
fuente
0

Esto funcionó para mi

ffmpeg -i file.mp4 -vf fps=1 %d.jpg

Kishan Vaghela
fuente