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


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

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

?

4 66

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.689s

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

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

если вы точно знаете, какие кадры для извлечения, например 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.

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