Найшвидший спосіб витягти кадри за допомогою ffmpeg?


118

Привіт, мені потрібно витягти кадри з відео, використовуючи ffmpeg .. Чи є більш швидкий спосіб зробити це:

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

?


1
^ оновлена ​​URL: trac.ffmpeg.org/wiki/Seeking
Lambart

5
Якщо у вас є запасні цикли процесора, ви можете витягнути паралельно з декількох відео:parallel -i {} -r 1/1 {.}-%03d.bmp ::: *mpg
Ole Tange,

Відповіді:


156

Якщо крок кодування JPEG занадто інтенсивний, ви завжди можете зберігати кадри, нестиснуті як зображення BMP:

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

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


9
Це призводить до того, що на моїй машині потрапляє багато кадрів. Чи можу я сказати ffmpeg зробити все?
Evi1M4chine

45
@ Evi1M4chine просто видаліть параметр -r, це витягне всі кадри
studioj

14
Я хотів би додати, що, хоча JPEG не дуже важкий для процесора, нестиснені Bitmaps дійсно важкі для зберігання, тому я сумніваюся, що ви отримаєте більш високу пропускну здатність з BMP порівняно з JPEG.
Маркус Мюллер

4
ffmpeg -r 1 file.mp4 -r 1 "$filename%03d.png"
Витягнути

10
Ви маєте на увазі ffmpeg -r 1 -i file.mp4 -r 1 "$filename%03d.png, правда? (ви пропустили -i)
Джошуа

68

Я зіткнувся з цим питанням, тому ось коротке порівняння. Порівняйте ці два різні способи витягування одного кадру в хвилину з відео довжиною 38м07:

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

1м36.029с

Це займе багато часу, оскільки 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

0м4.689с

Це приблизно в 20 разів швидше. Ми використовуємо швидку прагнення перейти до потрібного індексу часу і витягнути кадр, а потім викликаємо ffmpeg кілька разів для кожного індексу часу. Зверніть увагу, що -accurate_seek це за замовчуванням , і переконайтеся, що ви додали -ssперед -iопцією вхідного відео .

Зауважте, що краще використовувати -filter:v -fps=fps=...замість того -r, що останні можуть бути неточними. Незважаючи на те, що квиток позначений як фіксований , я все-таки відчув деякі проблеми, тож краще зіграйте в безпеці.


6
Лютий 2016: станом на ffmpeg 2.1, точний варіант пошуку зараз за замовчуванням - trac.ffmpeg.org/wiki/Seeking
mafrosis

1
bcне є рідним пакетом Ubuntu, натомість можна використовувати bash : let "i = $i * 60". BTW - відмінна ідея
gilad mayani

3
Добре додавання чайових -ssраніше -i. В іншому випадку все відео буде декодовано, а непотрібні кадри будуть відкинуті
MoustafaAAtta

2
Оскільки це вершина google, я хочу зазначити, що у 2018 році це все-таки підхід, який приносить дивіденди. Найкращим результатом, здається, є запуск одного ffmpegна ядро ​​вашого хоста - що (для bmp) дає майже лінійне поліпшення швидкості (поки ви не потрапите на якесь інше вузьке місце, наприклад, диск).
Кнетик

Це має бути прийнятою відповіддю, оскільки питання про "найшвидший шлях". Очевидно, вам потрібно знати змінну виходу для "для циклу", використовуючи ffprobe, який, на мою думку, все ще швидший порівняно з іншими методами.
iamprem

9

Якщо ви точно знаєте, які кадри витягти, наприклад, 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\,1000)',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. \,просто втеча від ,персонажа
  3. Без -vsync vfr -q:v 2нього це, здається, не працює, але я не знаю, чому - хтось?

4

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

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

Я намагаюся, ffmpeg -i "input URL" -vf fps=1/5 out%d.png де вхідною URL-адресою має бути https-посилання.
x2212

ffmpeg -i file.mpg -vf fps=1 %d.jpg
Кішань Вагела

1

У моєму випадку мені потрібні кадри щонайменше щосекунди. Я використовував підхід "прагнути" вище, але цікавився, чи зможу я паралельно поставити завдання. Я використав N процесів із FIFO підходом тут: /unix/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
    (
    "$@" 
    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 затоплюється.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.