Il modo più veloce per estrarre i frame usando ffmpeg?


118

Salve, ho bisogno di estrarre i frame dai video usando ffmpeg .. C'è un modo più veloce per farlo:

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

?


1
^ url aggiornato: trac.ffmpeg.org/wiki/Seeking
Lambart

5
Se hai cicli di CPU da risparmiare, puoi estrarre da più video in parallelo:parallel -i {} -r 1/1 {.}-%03d.bmp ::: *mpg
Ole Tange

Risposte:


156

Se la fase di codifica JPEG è troppo impegnativa per le prestazioni, è sempre possibile memorizzare i fotogrammi non compressi come immagini BMP:

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

Ciò ha anche il vantaggio di non incorrere in una maggiore perdita di qualità attraverso la quantizzazione mediante la transcodifica in JPEG. (PNG è anche senza perdita di dati, ma tende a richiedere molto più tempo del JPEG per la codifica.)


9
Ciò si traduce in molti fotogrammi che cadono sulla mia macchina. Posso dire a ffmpeg di eseguire il rendering di tutto?
Evi1M4chine

45
@ Evi1M4chine rimuovi semplicemente il parametro -r questo estrarrà tutti i frame
studioj

14
Vorrei aggiungere che mentre JPEG non è molto difficile per la CPU, le bitmap non compresse sono davvero molto difficili per l'archiviazione, quindi dubito che otterrai un throughput più elevato con BMP rispetto a JPEG.
Marcus Müller

4
Per estrarre tutti i frame:ffmpeg -r 1 file.mp4 -r 1 "$filename%03d.png"
fiatjaf

10
Vuoi dire ffmpeg -r 1 -i file.mp4 -r 1 "$filename%03d.png, vero? (ti mancava il -i)
Joschua

68

Mi sono imbattuto in questa domanda, quindi ecco un rapido confronto. Confronta questi due diversi modi per estrarre un fotogramma al minuto da un video di 38 m 07 s:

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

1m36.029s

Questo richiede molto tempo perché ffmpeg analizza l'intero file video per ottenere i fotogrammi desiderati.

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

Questo è circa 20 volte più veloce. Usiamo la ricerca veloce per andare all'indice temporale desiderato ed estrarre un frame, quindi chiamare ffmpeg più volte per ogni indice temporale. Nota che -accurate_seek è l'impostazione predefinita e assicurati di aggiungere -ssprima -idell'opzione video di input .

Nota che è meglio usare -filter:v -fps=fps=...invece di -rperché quest'ultimo potrebbe essere impreciso. Sebbene il ticket sia contrassegnato come risolto , ho comunque riscontrato alcuni problemi, quindi è meglio giocare sul sicuro.


6
Febbraio 2016: a partire da ffmpeg 2.1, l'opzione di ricerca accurata è ora predefinita - trac.ffmpeg.org/wiki/Seeking
mafrosis

1
bcnon è un pacchetto nativo di Ubuntu, invece si può usare bash: let "i = $i * 60". A proposito - ottima idea
gilad mayani

3
Buon suggerimento aggiungendo -ssprima -i. In caso contrario, l'intero video verrà decodificato e i fotogrammi non richiesti verranno scartati
MoustafaAAtta

2
Poiché questo è il top di google, vorrei sottolineare che nel 2018 questo è ancora un approccio che produce dividendi. Il miglior risultato sembra eseguirne uno ffmpegper core del tuo host - che (per bmp) produce miglioramenti quasi lineari nella velocità (fino a quando non incontri qualche altro collo di bottiglia, come il disco).
Knetic

Questa dovrebbe essere la risposta accettata, poiché la domanda riguarda il "modo più veloce". Ovviamente è necessario conoscere la variabile di uscita per il 'ciclo for' utilizzando ffprobe che penso sia ancora più veloce rispetto agli altri metodi.
iamprem

9

Se sai esattamente quali frame estrarre, ad esempio 1, 200, 400, 600, 800, 1000, prova a utilizzare:

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

Lo sto usando con una pipe al montaggio di Imagemagick per ottenere un'anteprima di 10 fotogrammi da qualsiasi video. Ovviamente i numeri di telaio che dovrai capire 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

.

Piccola spiegazione:

  1. In ffmpeg le espressioni +stanno per OR e *per AND
  2. \,è semplicemente sfuggire al ,personaggio
  3. Senza -vsync vfr -q:v 2non sembra funzionare ma non so perché - qualcuno?

4

L'ho provato. 3600 frame in 32 secondi. il tuo metodo è molto lento. Dovresti provare questo.

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

Sto provando ffmpeg -i "input URL" -vf fps=1/5 out%d.png dove l'URL di input deve essere un collegamento https.
x2212

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

1

Nel mio caso ho bisogno di frame almeno ogni secondo. Ho usato l'approccio "cerca di" sopra, ma mi chiedevo se potevo parallelizzare l'attività. Ho usato i processi N con l'approccio FIFO qui: /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

Essenzialmente ho biforcato il processo con & ma ho limitato il numero di thread simultanei a N.

Ciò ha migliorato l'approccio "cerca di" da 26 secondi a 16 secondi nel mio caso. L'unico problema è che il thread principale non esce in modo pulito al terminale poiché lo stdout viene inondato.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.