2012-06-09 18 views
52

Ciao ho bisogno di estrarre fotogrammi dai video tramite ffmpeg .. C'è un modo più veloce per farlo che questo:Il modo più veloce per estrarre i frame usando ffmpeg?

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

?

+1

http://ffmpeg.org/trac/ffmpeg/wiki/Seeking%20with%20FFmpeg può essere utile a voi – rogerdpack

+0

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

+0

Se si dispone di CPU cicli da risparmiare, puoi estrarre da più video in parallelo: 'parallel -i {} -r 1/1 {.} -% 03d.bmp ::: * mpg' –

risposta

65

Se il passo di codifica JPEG è troppo le prestazioni ad alta intensità, si può sempre memorizzare i fotogrammi non compressi come immagini BMP:

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

Questo ha anche il vantaggio di non incorrere in ulteriori perdite di qualità attraverso quantizzazione da transcodifica in JPEG. (PNG è anche senza perdita di dati, ma tende a richiedere molto più tempo rispetto a JPEG per la codifica.)

+0

grazie! Proverò a questo – Stpn

+0

questo sembra emettere un frame in più che c'è nel video (numero che posso ottenere con il filtro showinfo o ffprobe) – Guig

+5

Ciò risulta in * un sacco * di frame dropping sulla mia macchina. Posso dire a ffmpeg di rendere tutto? – Evi1M4chine

32

È venuto attraverso questa domanda, quindi ecco un confronto veloce. Confrontare questi due modi diversi per estrarre un fotogramma al minuto da un video 38m07s lunga:

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

1m36.029s

Questo richiede molto 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

Si tratta di circa 20 volte più veloce. Usiamo la ricerca rapida per andare all'indice del tempo desiderato e estrarre un frame, quindi chiamare ffmpeg più volte per ogni indice di tempo. Notare che -accurate_seekis the default e assicurarsi di aggiungere -ss prima dell'opzione video di input -i.

Si noti che è meglio usare -filter:v -fps=fps=... invece di -r come the latter may be inaccurate. Anche se the ticket is marked as fixed, io ancora ho esperienza alcuni problemi, quindi meglio giocare sul sicuro.

+4

feb 2016: a partire da ffmpeg 2.1, l'opzione di ricerca precisa è ora predefinita - https: //trac.ffmpeg .org/wiki/Seeking – mafrosis

+0

'bc' non è un pacchetto nativo di Ubuntu, invece si può usare bash:' let "i = $ i * 60" '. BTW - ottima idea –

+1

Un buon consiglio aggiungendo '-ss' prima di' -i'. In caso contrario, l'intero video verrà decodificato e i fotogrammi non richiesti verranno scartati – MoustafaAAtta

6

Se si sa esattamente che incornicia per estrarre, ad esempio 1, 200, 400, 600, 800, 1000, provare 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 

Sto usando questo con un tubo di montaggio di ImageMagick per ottenere 10 anteprima di fotogrammi da qualsiasi video. Ovviamente i numeri di telaio avrete bisogno di capire utilizzando 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 

.

piccola spiegazione:

  1. Nelle espressioni ffmpeg + acronimo di OR e * per E
  2. \, è semplicemente sfuggire alla , carattere
  3. Senza -vsync vfr -q:v 2 non sembra funzionare, ma io non so perché - qualcuno?
+1

Uno '' 'troppo. Dovrebbe essere: ' '' ffmpeg -i $ 1 -vf \ select = 'eq (n \, 1) + eq (n \, 200) + eq (n \, 400) + eq (n \, 600) + eq (n \, 800) + eq (n \, 100) ', scala = 320: -1 \ -vsync vfr -q: v 2 -f image2pipe -vcodec ppm - \ | montage -tile x1 -geometry "1x1 + 0 + 0 <" -quality 100 -frame 1 - output.png'''' – cauchy

0

Nel mio caso ho bisogno di frame almeno ogni secondo. Ho usato l'approccio "cerca di" sopra, ma mi sono chiesto se potevo parallelizzare il compito.Ho usato i processi N con approccio FIFO qui: 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 
    (
    "[email protected]" 
    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 biforcato il processo con & ma limitato il numero di thread simultanei a N.

Questo ha migliorato la 'cercano di' approccio da 26 secondi a 16 secondi nel mio caso. L'unico problema è che il thread principale non esce in modo pulito sul terminale poiché lo stdout viene allagato.

Problemi correlati