Найшвидший спосіб витягти кадри за допомогою 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.