Самый быстрый способ извлечения кадров с помощью ffmpeg?

Привет, мне нужно извлечь кадры из видео с помощью ffmpeg.. Есть ли более быстрый способ сделать это, чем этот:

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

?

4 ответов


если шаг кодирования JPEG слишком интенсивен, вы всегда можете сохранить несжатые кадры как bmp-изображения:

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

это также имеет преимущество не нести больше потери качества через квантование путем перекодирования в JPEG. (PNG также без потерь, но, как правило, занимает гораздо больше времени, чем JPEG для кодирования.)


наткнулся на этот вопрос, так что вот краткое сравнение. Сравните эти два разных способа извлечения одного кадра в минуту из видео длиной 38m07s:

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

1m36.029s

это занимает много времени, потому что ffmpeg анализирует весь видеофайл, чтобы получить нужные кадры.

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 с

это примерно в 20 раз быстрее. Мы используем fast seeking, чтобы перейти к нужному индексу времени и извлечь кадр, а затем вызвать ffmpeg несколько раз для каждого индекс времени. Обратите внимание, что -accurate_seek по умолчанию и убедитесь, что вы добавить -ss перед входом видео .

обратите внимание, что лучше использовать -filter:v -fps=fps=... вместо -r as последняя может быть неточным. хотя билет помечен как основные, я все еще испытывал некоторые проблемы, поэтому лучше играть безопасно.


если вы точно знаете, какие кадры для извлечения, например 1, 200, 400, 600, 800, 1000, попробуйте использовать:

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

я использую это с трубой для монтажа Imagemagick, чтобы получить предварительный просмотр 10 кадров из любых видео. Очевидно, что номера кадров вам нужно будет выяснить, используя 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

.

небольшое пояснение:

  1. в выражениях ffmpeg + означает или и * и
  2. \, is просто избегая , символ
  3. без -vsync vfr -q:v 2 кажется, это не работает, но я не знаю, почему - кто-нибудь?

в моем случае мне нужны кадры, по крайней мере, каждый второй. Я использовал подход "искать" выше, но задавался вопросом, Могу ли я распараллелить задачу. Я использовал N процессов с подходом FIFO здесь: 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

по существу я разветвил процесс с & , но ограничил количество параллельных потоков до N.

это улучшило подход "seek to" от 26 секунд до 16 секунд в моем случае. Единственная проблема заключается в том, что основной поток не выходит чисто обратно на терминал, так как stdout затопляется.