Thumbnail bermakna untuk Video menggunakan FFmpeg

80

FFmpeg dapat menangkap gambar dari video yang dapat digunakan sebagai gambar kecil untuk mewakili video. Cara paling umum untuk melakukan itu ditangkap dalam FFmpeg Wiki .

Tapi, saya tidak ingin memilih frame acak pada beberapa interval. Saya menemukan beberapa opsi menggunakan filter pada FFmpeg untuk menangkap perubahan pemandangan:

Filter thumbnailmencoba menemukan bingkai paling representatif dalam video:

ffmpeg -i input.mp4 -vf  "thumbnail,scale=640:360" -frames:v 1 thumb.png

dan perintah berikut memilih hanya frame yang memiliki lebih dari 40% perubahan dibandingkan dengan sebelumnya (dan mungkin juga perubahan adegan) dan menghasilkan urutan 5 PNG.

ffmpeg -i input.mp4 -vf  "select=gt(scene\,0.4),scale=640:360" -frames:v 5 thumb%03d.png

Kredit info untuk perintah di atas ke Fabio Sonnati . Yang kedua tampak lebih baik karena saya bisa mendapatkan gambar dan memilih yang terbaik. Saya mencobanya dan menghasilkan gambar yang sama 5 kali.

Beberapa investigasi membuat saya:

ffmpeg -i input.mp4 -vf "select=gt(scene\,0.5)" -frames:v 5 -vsync vfr  out%02d.png

-vsync vfrmemastikan bahwa Anda mendapatkan gambar yang berbeda. Ini masih selalu mengambil frame pertama dari video, dalam banyak kasus frame pertama adalah kredit / logo dan tidak berarti, jadi saya menambahkan -ss3 untuk membuang 3 detik pertama video.

Perintah terakhir saya terlihat seperti ini:

ffmpeg -ss 3 -i input.mp4 -vf "select=gt(scene\,0.5)" -frames:v 5 -vsync vfr out%02d.jpg

Ini adalah yang terbaik yang bisa saya lakukan. Saya perhatikan bahwa karena saya hanya mengambil 5 video, semuanya kebanyakan dari awal video dan mungkin kehilangan adegan penting yang terjadi kemudian dalam video

Saya ingin memilih otak Anda untuk opsi lain yang lebih baik.

d33pika
sumber
Contoh perintah yang bagus. FWIW, saya tidak mengalami masalah dengan gambar JPEG FFmpeg yang dihasilkan pada OS X (10.8, FFmpeg 1.1 dan di bawah). Perintah kedua ke terakhir Anda berfungsi dengan baik untuk saya — demikian juga yang terakhir — dan tidak ada yang menghasilkan file JPG kosong. Saya melakukan kompilasi dengan libopenjpeg.. tidak yakin apakah itu membuat perbedaan.
slhck
Terima kasih slhck. Mengedit pertanyaan dengan detail konfigurasi / versi ffmpeg. Saya belum mengupgrade ke 1.1 pada mesin ini. Saya akan melakukannya dan melihat apakah itu mengubah hasil apa pun.
d33pika
1
Jadi Anda menggunakan Ubuntu? Bisakah Anda mencoba versi Git Master terbaru dari bangunan statis atau mengkompilasi diri Anda dan menjalankannya lagi? Atau stabil terbaru. Saya baru saja memeriksa, ia menggunakan mjpegencoder untuk saya juga, dan saya juga memeriksa jpegoptimdan exiv2, keduanya bekerja dengan baik untuk saya dengan semua hasil JPG dari perintah contoh Anda.
slhck
1
Saya memperbarui, dan berfungsi sekarang! Saya kira versi sebelumnya memiliki beberapa bug.
d33pika
Bisakah Anda melanjutkan dan memposting solusi- versi baru, lebih disukai dengan tautan ke changelog yang menunjukkan bug yang Anda temui dan kemudian diperbaiki dengan versi baru?
Lizz

Jawaban:

28

Bagaimana kalau mencari, idealnya, frame perubahan pertama> 40% dalam masing-masing 5 rentang waktu, di mana rentang waktu adalah 1, 2, 3, 4, dan 20% dari video.

Anda juga dapat membaginya menjadi 6 rentang waktu dan mengabaikan yang pertama untuk menghindari kredit.

Dalam praktiknya, ini berarti mengatur fps ke angka rendah sambil menerapkan pemeriksaan perubahan adegan Anda dan argumen Anda untuk membuang sedikit pertama video.

...sesuatu seperti:

ffmpeg -ss 3 -i input.mp4 -vf "select=gt(scene\,0.4)" -frames:v 5 -vsync vfr -vf fps=fps=1/600 out%02d.jpg
SAYA
sumber
1
Gambar yang benar-benar hitam atau putih diambil. Bagaimana cara saya menghindarinya?
d33pika
1
Ada filter untuk mendeteksi bingkai hitam dan urutannya ( ffmpeg.org/ffmpeg-filters.html#blackframe atau ffmpeg.org/ffmpeg-filters.html#blackdetect ). Anda tidak dapat menggunakan keduanya ke dalam one-liner Anda yang sedang tumbuh, tetapi Anda harus dapat menghapus frame hitam (langkah terpisah) dan mengekstrak thumbnail dari video yang dihasilkan.
AM
5
Sedangkan untuk frame putih, nah sekarang semakin rumit, tapi sepertinya masih ada cara: 1. strip out black frames 2. negate (putih berubah menjadi hitam) 3. strip out white frames 4. negate lagi 5. ekstrak thumbnail (Jika Anda berhasil menghapus bingkai hitam atau putih, terutama pada satu baris, dapatkah Anda mempostingnya kembali di sini? Saya dapat menambahkannya ke jawaban untuk pembaca masa depan ... ... atau sebenarnya Anda dapat membuat pertanyaan dan jawaban baru itu, saya pasti akan mengangkatnya.)
AM
3
Untuk menghindari gambar yang membosankan seperti hitam putih biasa: buat lebih banyak gambar kecil daripada yang Anda butuhkan, kompres dengan jpeg, dan pilih gambar dengan ukuran file lebih besar. Ini bekerja sangat baik dengan sendirinya untuk mendapatkan thumbnail yang layak.
Sam Watkins
2
Perintah ini tidak berfungsi, saya mendapat kesalahan Unable to find a suitable output format for 'fps=fps=1/600'. Solusinya adalah menambahkan -vfsebelum argumen itu (lihat stackoverflow.com/questions/28519403/ffmpeg-command-issue )
John Wiseman
7

Mendefinisikan bermakna itu sulit, tetapi jika Anda ingin membuat thumbnail N efisien menjangkau seluruh file video ini adalah apa yang saya gunakan untuk menghasilkan thumbnail pada produksi dengan konten yang diunggah pengguna.

Kode semu

for X in 1..N
  T = integer( (X - 0.5) * D / N )  
  run `ffmpeg -ss <T> -i <movie>
              -vf select="eq(pict_type\,I)" -vframes 1 image<X>.jpg`

Dimana:

  • D - durasi video dibaca dari ffmpeg -i <movie>sendiri atau ffprobeyang memiliki JSON bagus penulis keluaran btw
  • N - jumlah total thumbnail yang Anda inginkan
  • Angka thumbnail X, dari 1 hingga N
  • T - titik waktu untuk thumbnail

Cukup yang di atas menuliskan frame kunci tengah dari setiap partisi film. Misal, jika film berdurasi 300an dan Anda ingin 3 thumbnail maka diperlukan satu bingkai kunci setelah 50an, 150an, dan 250an. Untuk 5 thumbnail, 30-an, 90-an, 150-an, 210-an, 270-an. Anda dapat menyesuaikan N tergantung pada durasi film D, misalnya film 5 menit akan memiliki 3 thumbnail tetapi lebih dari 1 jam akan memiliki 20 thumbnail.

Performa

Setiap doa dari ffmpegperintah di atas membutuhkan sepersekian detik (!) Untuk ~ 1GB H.264. Itu karena ia langsung melompat ke <time>posisi (pikiran -sssebelumnya -i) dan mengambil bingkai kunci pertama yang praktis menyelesaikan JPEG. Tidak ada waktu yang terbuang untuk membuat film agar sesuai dengan posisi waktu yang tepat.

Pengolahan pasca

Anda dapat mencampur di atas dengan scaleatau metode pengubahan ukuran lainnya. Anda juga dapat menghapus bingkai warna solid atau mencoba mencampurnya dengan filter lain seperti thumbnail.

gertas
sumber
2
wow pindah -ss Nke sebelumnya -iadalah tip yang luar biasa. Terima kasih!
apinstein
2

Saya pernah melakukan sesuatu yang serupa, tetapi saya mengekspor semua bingkai video (dalam 1 fps) dan membandingkannya dengan utilitas perl yang saya temukan yang menghitung perbedaan antara gambar. Saya membandingkan setiap frame dengan thumbnail sebelumnya, dan jika berbeda dari semua thumbnail, saya menambahkannya ke koleksi thumbnail. Keuntungannya di sini adalah jika video Anda bergerak dari adegan A ke B dan mereka kembali ke A, ffmpeg akan mengekspor 2 frame A.

Eran Ben-Natan
sumber
Faktor apa yang Anda gunakan untuk membandingkan 2 gambar?
d33pika
Sayangnya saya tidak ingat, itu sudah lama sekali. Anda harus memutuskan terlebih dahulu metode perbandingan mana yang digunakan, dan kemudian melakukan beberapa tes untuk menemukan faktor yang benar. Ini seharusnya tidak butuh waktu lama.
Eran Ben-Natan
Saya hanya berpikir keras di sini tapi bukankah ini pilihan yang lebih buruk? Saat Anda membandingkan 2 gambar yang diekspor dari video, Anda telah kehilangan beberapa informasi darinya. Maksud saya, lebih mudah untuk menghitung kesamaan dari informasi codec daripada dari 2 gambar, bukan? Baru-baru ini saya telah melihat beberapa algoritma / perpustakaan untuk membandingkan gambar dan mereka tidak berfungsi sebaik yang diperlukan.
Samuel
Nah, dengan ffmpeg Anda dapat mengekstraksi frame tanpa kehilangan kualitas menggunakan flag same_quality. Jika Anda menggunakan informasi codec, Anda perlu memastikan bahwa Anda tidak hanya mendapatkan Iframes. Pokoknya perl utilitas itu bekerja dengan baik bagi saya, dan sangat dapat dikonfigurasi. Lihat di sini: search.cpan.org/~avif/Image-Compare-0.3/Compare.pm
Eran Ben-Natan
2

Coba ini

 ffmpeg -i input.mp4 -vf fps= no_of_thumbs_req/total_video_time out%d.png

Dengan menggunakan perintah ini, saya dapat menghasilkan jumlah thumbnail yang diperlukan yang mewakili seluruh video.

Anak anjing hilang
sumber
Bukankah formula yang tepat untuk fpsmenjadi (no_of_frames_req * fps_of_vid) / total_video_frames?
flolilo
Saya mencari solusi yang berlawanan: pilih lebih banyak frame dari periode di mana kamera lebih stabil dan tidak bergerak? (di mana perbedaan antara frame berturut-turut kurang, tidak lebih tinggi). Apakah ada cara untuk melakukan itu?
Tina J
1

Inilah yang saya lakukan untuk membuat thumbnail berkala untuk streaming live m3u8 untuk digunakan sebagai poster. Saya menemukan menjalankan tugas ffmpeg terus menerus hanya untuk menghasilkan thumbnail memakan semua CPU saya, jadi saya menjalankan cronjob setiap 60 detik yang menghasilkan semua thumbnail untuk stream saya.

#!/usr/bin/env bash

## this is slow but gives better thumbnails (takes about 1+ minutes for 20 files)
#-vf thumbnail,scale=640:360 -frames:v 1 poster.png

## this is faster but thumbnails are random (takes about 8 seconds for 20 files)
#-vf scale=640:360 -frames:v 1 poster.png
cd ~/path/to/streams

find . -type f \
  -name "*.m3u8" \
  -execdir sh -c 'ffmpeg -y -i "$0" -vf scale=640:360 -frames:v 1 "poster.png"' \
  {} \;

Jika Anda perlu melakukan lebih dari 60 detik (pembatasan cronjob), maka Anda dapat melakukan whileloop pada skrip Anda dan itu akan dijalankan selamanya. Tambahkan saja sleep 30untuk mengubah frekuensi menjadi 30 detik. Saya tidak menyarankan melakukan ini dengan sejumlah besar video, karena tayangan sebelumnya mungkin tidak selesai sebelum yang berikutnya dimulai.

Idealnya dengan cronjob, saya menjalankannya setiap 5 menit.

kenyal
sumber