Cách nhanh nhất để trích xuất khung hình bằng ffmpeg?


118

Xin chào, tôi cần trích xuất khung hình từ video bằng ffmpeg .. Có cách nào nhanh hơn để thực hiện việc này không:

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

?



1
^ cập nhật url: trac.ffmpeg.org/wiki/Seeking
Lambart

5
Nếu bạn có chu kỳ CPU để rảnh rỗi, bạn có thể trích xuất từ nhiều video song song:parallel -i {} -r 1/1 {.}-%03d.bmp ::: *mpg
Ole Tange

Câu trả lời:


155

Nếu bước mã hóa JPEG quá chuyên sâu về hiệu suất, bạn luôn có thể lưu trữ các khung hình không nén dưới dạng ảnh BMP:

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

Điều này cũng có lợi thế là không làm giảm chất lượng nhiều hơn thông qua lượng tử hóa bằng cách chuyển mã sang JPEG. (PNG cũng không mất dữ liệu nhưng có xu hướng mất nhiều thời gian hơn JPEG để mã hóa.)


9
Điều này dẫn đến rất nhiều khung hình bị giảm trên máy của tôi. Tôi có thể yêu cầu ffmpeg kết xuất mọi thứ không?
Evi1M4chine

45
@ Evi1M4chine chỉ cần xóa tham số -r, điều này sẽ trích xuất tất cả các khung hình
studioj

14
Tôi muốn nói thêm rằng mặc dù JPEG không thực sự khó trên CPU, nhưng Bitmap không nén thực sự khó lưu trữ, vì vậy tôi nghi ngờ bạn sẽ nhận được thông lượng cao hơn với BMP so với JPEG.
Marcus Müller

4
Để giải nén tất cả các khung hình:ffmpeg -r 1 file.mp4 -r 1 "$filename%03d.png"
fiatjaf

10
Ý bạn là ffmpeg -r 1 -i file.mp4 -r 1 "$filename%03d.png, phải không? (bạn đã bỏ lỡ -i)
Joschua

68

Gặp phải câu hỏi này, vì vậy đây là một so sánh nhanh. So sánh hai cách khác nhau này để trích xuất một khung hình mỗi phút từ video dài 38m07 giây:

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

1m36.029 giây

Quá trình này mất nhiều thời gian vì ffmpeg phân tích cú pháp toàn bộ tệp video để có được các khung hình mong muốn.

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 phút4,689 giây

Điều này nhanh hơn khoảng 20 lần. Chúng tôi sử dụng tính năng tìm kiếm nhanh để đi đến chỉ mục thời gian mong muốn và trích xuất một khung, sau đó gọi ffmpeg nhiều lần cho mỗi chỉ mục thời gian. Lưu ý rằng đó -accurate_seek là mặc định và đảm bảo bạn thêm -sstrước -itùy chọn video đầu vào .

Lưu ý rằng nó tốt hơn để sử dụng -filter:v -fps=fps=...thay vì -rnhư sau này có thể không chính xác. Mặc dù vé được đánh dấu là đã sửa , tôi vẫn gặp một số vấn đề, vì vậy tốt hơn hãy chơi nó an toàn.


6
Tháng 2 năm 2016: kể từ ffmpeg 2.1, tùy chọn tìm kiếm chính xác hiện là mặc định - trac.ffmpeg.org/wiki/Seeking
mafrosis

1
bckhông phải là một gói phần mềm Ubuntu bản xứ, thay vào đó người ta có thể sử dụng bash: let "i = $i * 60". BTW - ý tưởng tuyệt vời
gilad mayani

3
Mẹo hay bổ sung -sstrước -i. Nếu không, toàn bộ video sẽ được giải mã và các khung unrequired sẽ bị loại bỏ
MoustafaAAtta

2
Vì đây là đầu trang của google, tôi muốn lưu ý rằng vào năm 2018, đây vẫn là một cách tiếp cận mang lại cổ tức. Kết quả tốt nhất dường như là chạy một ffmpegtrên mỗi lõi của máy chủ lưu trữ của bạn - điều này (đối với bmp) mang lại những cải thiện gần như tuyến tính về tốc độ (cho đến khi bạn gặp phải một số nút cổ chai khác, như đĩa).
Knetic

Đây phải là câu trả lời được chấp nhận, vì câu hỏi là về 'cách nhanh nhất'. Rõ ràng bạn cần biết biến thoát cho 'vòng lặp for' bằng cách sử dụng ffprobe mà tôi nghĩ vẫn nhanh hơn so với các phương pháp khác.
iamprem

9

Nếu bạn biết chính xác khung nào cần trích xuất, ví dụ: 1, 200, 400, 600, 800, 1000, hãy thử sử dụng:

select='eq(n\,1)+eq(n\,200)+eq(n\,400)+eq(n\,600)+eq(n\,800)+eq(n\,1000)' \
       -vsync vfr -q:v 2

Tôi đang sử dụng cái này với một đường ống để dựng phim Imagemagick để có được 10 khung hình xem trước từ bất kỳ video nào. Rõ ràng là số khung bạn sẽ cần tìm ra bằng cách sử dụngffprobe

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

.

Giải thích nhỏ:

  1. Trong biểu thức ffmpeg +là viết tắt của OR và *AND
  2. \,chỉ đơn giản là thoát khỏi ,nhân vật
  3. Nếu không có -vsync vfr -q:v 2nó dường như không hoạt động nhưng tôi không biết tại sao - có ai không?

4

Tôi đã thử nó. 3600 khung hình trong 32 giây. phương pháp của bạn thực sự chậm. Bạn nên thử điều này.

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

Tôi đang thử ffmpeg -i "input URL" -vf fps=1/5 out%d.png URL đầu vào phải là liên kết https.
x2212

ffmpeg -i file.mpg -vf fps=1 %d.jpg
Kishan Vaghela

1

Trong trường hợp của tôi, tôi cần khung hình ít nhất mỗi giây. Tôi đã sử dụng cách tiếp cận 'seek to' ở trên nhưng tự hỏi liệu tôi có thể thực hiện song song nhiệm vụ không. Tôi đã sử dụng N quy trình với phương pháp FIFO tại đây: /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

Về cơ bản, tôi đã tách quy trình với & nhưng giới hạn số luồng đồng thời đối với N.

Điều này đã cải thiện cách tiếp cận 'tìm kiếm' từ 26 giây xuống 16 giây trong trường hợp của tôi. Vấn đề duy nhất là luồng chính không thoát sạch trở lại thiết bị đầu cuối vì stdout bị ngập.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.