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

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

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

?

Ответ 1

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

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

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

Ответ 2

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

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 перед исходным видео -i.

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

Ответ 3

Если вы точно знаете, какие кадры извлечь, например, 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 + обозначает OR и * обозначает AND
  2. \, Просто побег , характер
  3. Без -vsync vfr -q:v 2, похоже, не работает, но я не знаю почему - кто-нибудь?

Ответ 4

В моем случае мне нужны кадры хотя бы каждую секунду. Я использовал подход "искать" выше, но задавался вопросом, могу ли я распараллелить задачу. Я использовал 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=$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

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

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

Ответ 5

Я попробовал это. 3600 кадров за 32 секунды. Ваш метод действительно медленный. Вы должны попробовать это.

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