Самый быстрый способ извлечения кадров с помощью 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
,
Маленькое объяснение:
- В выражениях ffmpeg
+
обозначает OR и *
обозначает AND -
\,
Просто побег ,
характер - Без
-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