[FFmpeg Tutorial] Lấy ảnh từ video với FFmpeg

Posted on October 14th, 2018

Đã bao giờ bạn xem một video và thấy "ôi cô diễn viên xinh quá" hay "cảnh này đẹp quá". Rồi sau đó, bạn muốn lưu giữ lại làm kỉ niệm. Nhưng làm bằng cách nào đây? Tua đến vị trí đó rồi chụp lại màn hình? Không. Vì bạn có thể lấy ảnh từ video với FFmpeg một cách dễ dàng.

Thật đấy, tin mình đi và bắt đầu nhé!

Lấy ra tất cả các ảnh từ video

Cách đơn giản nhất để lấy ảnh từ video là trích xuất ra tất cả các ảnh. Để làm việc này, bạn có thể sử dụng câu lệnh sau:

ffmpeg -i mini-input.mp4 img%d.jpg -hide_banner

Trong đó:

  • -i là cờ xác định file đầu vào, sau đó là tên file đầu vào (ví dụ: mini-input.mp4). Ngoài ra, bạn cũng có thể sử dụng video với các định dạng khác như: webm, flv, mkv,...
  • img%d.jpg là tham số dùng để xác định tên của file ảnh đầu ra. Ở đây, mình sử dụng file đầu ra dạng jpg. Tuy nhiên, bạn cũng có thể xuất ra file png hoặc bmp,...
  • -hide_banner: dùng để ẩn những thông tin không cần thiết liên quan đến FFmpeg như phiên bản, các thư viện sử dụng,... như mình đã nói trong bài viết về lấy thông tin video bằng FFmpeg.

Ở đây, với định dạng file đầu ra là img%d thì các file ảnh đầu ra sẽ có tên lần lượt là: img1.jpg, img2.jpg, img3.jpg,... img10.jpg,... img100.jpg.... Nghĩa là độ dài kí tự không bằng nhau.

Nếu muốn độ dài của chúng bằng nhau, bạn có thể sửa lại câu lệnh trên một chút. Thay vì dùng img%d.jpg thì bạn dùng img%nd.jpg (với n là số tự nhiên). Cái này tương tự như lệnh printf trong lập trình C vậy.

Ví dụ: nếu bạn sử dụng img%3d.jpg thì ảnh đầu ra sẽ tên lần lượt là: img001.jpg, img002.jpg, img003.jpg,...

Lấy ra một vài ảnh từ video

Giả sử một video có framerate là 25 fps, độ dài 60 phút thì số lượng frame sẽ là: 25 _ 60 _ 60 = 90000. Như vậy là quá nhiều phải không?

Trong khi bạn chỉ muốn lấy ra một vài bức ảnh trong đó, thì việc sử dụng cách trên là quá mất thời gian. Thậm chí, máy bạn còn có thể bị đơ, thiếu bộ nhớ,....

Vì vậy, để lấy ra một vài bức ảnh từ video, bạn có thể sử dụng câu lệnh:

ffmpeg -i mini-input.mp4 -ss 00:00:02.000 -vframes 5 img%d.jpg

Trong đó:

  • -i mini-input.mp4 dùng để xác định file đầu vào.
  • -ss là cờ dùng để xác định thời điểm bạn muốn lấy ảnh từ video. Tiếp sau đó là thời gian theo định dạng như trong câu lệnh trên (ví dụ: 00:00:02.000 ứng với thời điểm 2 giây).
  • -vframes là cờ dùng để xác định số frame muốn lấy ra. Sau đó là số lượng frame (trong ví dụ trên là 5 frame).
  • img%d.jpg là định dạng file đầu ra, giống như phần trên.

Lấy ra các ảnh cách nhau nhất định

Ngoài các cách trên, bạn có thể lấy ảnh từ video dựa trên một khoảng cách thời gian nhất định. Ví dụ bạn có thể lấy ra tất cả các ảnh cách nhau mỗi 1 giây.

Khi đó, bạn có thể dùng câu lệnh sau:

ffmpeg -i mini-input.mp4 -vf fps=1 img%d.jpg

Trong đó:

  • -i mini-input.mp4 dùng để xác định file đầu vào.
  • -vf là cờ dùng để áp dụng video filter vào câu lệnh này. Sau đó là công thức áp dụng. Ở đây mình dùng fps=1, nghĩa là lấy 1 ảnh sau mỗi 1 giây. Ngoài ra, bạn có thể sử dụng fps=1/n, với n là số tự nhiên. Khi đó, FFmpeg sẽ lấy ra 1 ảnh sau mỗi n giây.
  • img%d.jpg là định dạng file đầu ra, giống như phần trên.

Câu lệnh này có tác dụng khi bạn muốn lấy ra một số ảnh của video đầy đủ để làm một video dạng tóm tắt.

Lấy ra các ảnh là keyframes

Các phương pháp trên rất tốt nhưng bị một vấn đề là: chất lượng frame ảnh lấy ra có khả năng sẽ không tốt.

Vì đối với việc encode video, các frame sẽ được lưu trữ thành các frame dạng I, P, B. Trong đó, chỉ có frame dạng I là tương ứng với ảnh đầy đủ (là keyframe), còn các frame P và B chỉ lưu trữ sự thay đổi giữa các frame.

Do đó, để lấy ảnh từ video tốt nhất, bạn nên lấy ra các ảnh là keyframes bằng câu lệnh:

ffmpeg -i mini-input.mp4 -vf "select=eq(pict_type\,I)" -vsync vfr img%d.jpg

Trong đó:

  • -i mini-input.mp4 dùng để xác định file đầu vào.
  • -vf là cờ dùng để áp dụng video filter vào câu lệnh này. Nhưng khác với câu lệnh phần trên, ở đây, mình sử dụng công thức "select=eq(pict_type,I)". Tạm dịch là: "lựa chọn ra các frame kiểu I".
  • -vsync vfr dùng để nói với bộ filter sử dụng cơ chế đồng bộ bitrate. Thực ra, mình cũng không hiểu rõ cờ này cho lắm. Chỉ biết là nếu như không sử dụng cờ này thì kết quả có thể sẽ chứa những frame không phải là keyframe.
  • img%d.jpg là định dạng file đầu ra, giống như phần trên.

Lời kết

Như vậy là mình đã giới thiệu với bạn 4 cách để lấy ảnh từ video với FFmpeg. Mỗi cách có một tác dụng riêng. Tùy từng trường hợp mà bạn có thể chọn ra cách hợp lý nhất.

Nếu có gì sai sót, mình rất mong nhận được sự góp ý từ bạn.

Tham khảo: ffmpeg document.

★ Nếu bạn thấy bài viết này hay thì hãy theo dõi mình trên Facebook/Youtube để nhận được thông báo khi có bài viết mới nhất nhé: