Secara otomatis membagi file video .mov besar menjadi file yang lebih kecil dengan bingkai hitam (perubahan adegan)?

11

Saya memiliki 65 file video .mov bernama tape_01.mov, tape_02.mov, ..., tape_65.mov. Masing-masing panjangnya sekitar 2,5 jam dan membutuhkan banyak gigabytes. Itu adalah salinan digital dari apa itu kaset VHS ("film rumahan" keluarga saya).

Setiap file video .mov 2,5 jam berisi banyak "bab" (dalam arti bahwa kadang-kadang adegan berakhir dengan memudar ke layar hitam, dan kemudian adegan baru memudar).

Tujuan utama saya adalah memiliki 65 file besar diiris menjadi file yang lebih kecil dengan bab. Dan saya ingin itu dilakukan secara otomatis melalui deteksi bingkai hitam antara "adegan".

Bisakah saya menggunakan ffmpeg (atau apa pun) untuk meneruskan 65 nama file asli dan memilikinya secara otomatis beralih ke setiap video dan memotongnya, menamai potongan yang dihasilkan tape_01-ch_01.mov, tape_01-ch_02.mov, tape_01-ch_03.mov, dll? Dan saya ingin melakukannya tanpa re-encoding (saya ingin itu menjadi irisan lossless sederhana).

Bagaimana saya bisa melakukan ini?

Berikut langkah-langkah yang saya inginkan:

  1. Iterate di atas folder file .mov (bernama tape_01.mov, tape_02.mov, ..., tape_65.mov) untuk mengekspornya sebagai file mp4 (bernama tape_01.mp4, tape_02.mp4, ..., tape_65.mp4) . Saya ingin pengaturan kompresi mudah dikonfigurasikan untuk langkah ini. File .mov asli masih ada setelah file .mp4 dibuat.
  2. Iterasi file mp4: untuk setiap file mp4, buat file teks yang menentukan waktu mulai dan durasi segmen hitam antara "adegan". Setiap mp4 dapat memiliki 0 atau lebih dari istirahat adegan hitam ini. Waktu mulai dan durasi harus tepat hingga sepersekian detik. HH: MM: Format SS tidak cukup tepat.
  3. Saya kemudian akan dapat memeriksa ulang file teks ini secara manual untuk melihat apakah mereka mengidentifikasi perubahan adegan dengan benar (segmen hitam). Saya dapat menyesuaikan timing jika saya mau.
  4. Skrip lain akan membaca setiap file mp4 dan file txt-nya dan membaginya (dengan cara yang sangat cepat dan tanpa kehilangan) mp4 menjadi sebanyak mungkin sesuai dengan baris-baris file teks (timing yang ditunjukkan di sana). Perpecahan harus terjadi di tengah-tengah setiap segmen hitam. Berikut adalah contoh file mp4 (dengan audio dihapus untuk tujuan privasi) yang memiliki perubahan adegan fade-to-black. File mp4 asli harus tetap tidak tersentuh karena file mp4 yang lebih kecil dibuat. File mp4 yang lebih kecil harus memiliki skema penamaan tape_01_001.mp4, tape_01_002.mp4, tape_01_003.mp4, dll.
  5. Bonus! Akan luar biasa jika skrip lain kemudian dapat mengulangi file mp4 kecil dan menghasilkan satu gambar .png untuk masing-masing yang merupakan mosaik tangkapan layar seperti yang dicoba oleh orang ini: Thumbnail bermakna untuk Video menggunakan FFmpeg

Saya ingin skrip ini menjadi skrip shell yang dapat saya jalankan di Mac, Ubuntu, dan Windows Git Bash.

Ryan
sumber

Jawaban:

11

Berikut adalah dua skrip PowerShell untuk membagi video panjang menjadi beberapa bab kecil dengan adegan hitam.

Simpan sebagai Detect_black.ps1 dan Cut_black.ps1. Unduh ffmpeg untuk Windows dan beri tahu skrip path ke ffmpeg.exe dan folder video Anda di bawah bagian opsi.

masukkan deskripsi gambar di sini

Kedua skrip tidak akan menyentuh file video yang ada, mereka tetap tidak tersentuh.
Namun, Anda akan mendapatkan beberapa file baru di tempat yang sama dengan video input Anda

  • File log per video dengan output konsol untuk kedua perintah ffmpeg yang digunakan
  • File CSV per video dengan semua cap waktu adegan hitam untuk fine tuning manual
  • Beberapa video baru tergantung pada berapa banyak adegan hitam yang terdeteksi sebelumnya

masukkan deskripsi gambar di sini


Skrip pertama yang dijalankan: Detect_black.ps1

 ### Options __________________________________________________________________________________________________________
$ffmpeg = ".\ffmpeg.exe"            # Set path to your ffmpeg.exe; Build Version: git-45581ed (2014-02-16)
$folder = ".\Videos\*"              # Set path to your video folder; '\*' must be appended
$filter = @("*.mov","*.mp4")        # Set which file extensions should be processed
$dur = 4                            # Set the minimum detected black duration (in seconds)
$pic = 0.98                         # Set the threshold for considering a picture as "black" (in percent)
$pix = 0.15                         # Set the threshold for considering a pixel "black" (in luminance)

### Main Program ______________________________________________________________________________________________________

foreach ($video in dir $folder -include $filter -exclude "*_???.*" -r){

  ### Set path to logfile
  $logfile = "$($video.FullName)_ffmpeg.log"

  ### analyse each video with ffmpeg and search for black scenes
  & $ffmpeg -i $video -vf blackdetect=d=`"$dur`":pic_th=`"$pic`":pix_th=`"$pix`" -an -f null - 2> $logfile

  ### Use regex to extract timings from logfile
  $report = @()
  Select-String 'black_start:.*black_end:' $logfile | % { 
    $black          = "" | Select  start, end, cut

    # extract start time of black scene
    $start_s     = $_.line -match '(?<=black_start:)\S*(?= black_end:)'    | % {$matches[0]}
    $start_ts    = [timespan]::fromseconds($start_s)
    $black.start = "{0:HH:mm:ss.fff}" -f ([datetime]$start_ts.Ticks)

    # extract duration of black scene
    $end_s       = $_.line -match '(?<=black_end:)\S*(?= black_duration:)' | % {$matches[0]}
    $end_ts      = [timespan]::fromseconds($end_s)
    $black.end   = "{0:HH:mm:ss.fff}" -f ([datetime]$end_ts.Ticks)    

    # calculate cut point: black start time + black duration / 2
    $cut_s       = ([double]$start_s + [double]$end_s) / 2
    $cut_ts      = [timespan]::fromseconds($cut_s)
    $black.cut   = "{0:HH:mm:ss.fff}" -f ([datetime]$cut_ts.Ticks)

    $report += $black
  }

  ### Write start time, duration and the cut point for each black scene to a seperate CSV
  $report | Export-Csv -path "$($video.FullName)_cutpoints.csv" –NoTypeInformation
}

bagaimana cara kerjanya

Skrip pertama beralih melalui semua file video yang cocok dengan ekstensi yang ditentukan dan tidak cocok dengan pola *_???.*, karena bab video baru dinamai <filename>_###.<ext>dan kami ingin mengecualikannya.

Itu mencari semua adegan hitam dan menulis timestamp mulai dan durasi adegan hitam ke file CSV baru bernama <video_name>_cutpoints.txt

Hal ini juga menghitung poin dipotong seperti yang ditunjukkan: cutpoint = black_start + black_duration / 2. Nantinya, video akan tersegmentasi pada stempel waktu ini.

File cutpoints.txt untuk video contoh Anda akan menunjukkan:

start          end            cut
00:03:56.908   00:04:02.247   00:03:59.578
00:08:02.525   00:08:10.233   00:08:06.379

Setelah berlari, Anda dapat memanipulasi titik potong secara manual jika diinginkan. Jika Anda menjalankan skrip lagi, semua konten lama ditimpa. Hati-hati saat mengedit secara manual dan menyimpan pekerjaan Anda di tempat lain.

Untuk video contoh, perintah ffmpeg untuk mendeteksi adegan hitam adalah

$ffmpeg -i "Tape_10_3b.mp4" -vf blackdetect=d=4:pic_th=0.98:pix_th=0.15 -an -f null

Ada 3 angka penting yang dapat diedit di bagian opsi skrip

  • d=4 berarti hanya adegan hitam yang lebih panjang dari 4 detik yang terdeteksi
  • pic_th=0.98 adalah ambang batas untuk menganggap gambar sebagai "hitam" (dalam persen)
  • pix=0.15menetapkan ambang untuk mempertimbangkan piksel sebagai "hitam" (dalam pencahayaan). Karena Anda memiliki video VHS lama, Anda tidak memiliki adegan yang sepenuhnya hitam di video Anda. Nilai default 10 tidak akan berfungsi dan saya harus meningkatkan ambang sedikit

Jika ada yang salah, periksa logfile terkait yang disebut <video_name>__ffmpeg.log. Jika baris berikut tidak ada, tambah angka yang disebutkan di atas hingga Anda mendeteksi semua adegan hitam:

[blackdetect @ 0286ec80]
 black_start:236.908 black_end:242.247 black_duration:5.33877



Skrip kedua untuk dijalankan: cut_black.ps1

### Options __________________________________________________________________________________________________________
$ffmpeg = ".\ffmpeg.exe"            # Set path to your ffmpeg.exe; Build Version: git-45581ed (2014-02-16)
$folder = ".\Videos\*"              # Set path to your video folder; '\*' must be appended
$filter = @("*.mov","*.mp4")        # Set which file extensions should be processed

### Main Program ______________________________________________________________________________________________________

foreach ($video in dir $folder -include $filter -exclude "*_???.*" -r){

  ### Set path to logfile
  $logfile = "$($video.FullName)_ffmpeg.log"

  ### Read in all cutpoints from *_cutpoints.csv; concat to string e.g "00:03:23.014,00:06:32.289,..."  
  $cuts = ( Import-Csv "$($video.FullName)_cutpoints.csv" | % {$_.cut} ) -join ","

  ### put together the correct new name, "%03d" is a generic number placeholder for ffmpeg
  $output = $video.directory.Fullname + "\" + $video.basename + "_%03d" + $video.extension

  ### use ffmpeg to split current video in parts according to their cut points
  & $ffmpeg -i $video -f segment -segment_times $cuts -c copy -map 0 $output 2> $logfile        
}

bagaimana cara kerjanya

Skrip kedua akan mengulangi semua file video dengan cara yang sama dengan skrip pertama. Bunyinya di hanya dipotong cap waktu dari yang sesuai cutpoints.txtdari video.

Selanjutnya, ia mengumpulkan nama file yang cocok untuk file bab dan memberitahu ffmpeg untuk membagi video. Saat ini video diiris tanpa re-encoding (supercepat dan tanpa kehilangan). Karena ini, mungkin ada 1-2s ketidaktepatan dengan stempel waktu titik potong karena ffmpeg hanya dapat memotong di key_frames. Karena kita hanya menyalin dan tidak menyandikan ulang, kita tidak bisa menyisipkan key_frames sendiri.

Perintah untuk video contoh adalah

$ffmpeg -i "Tape_10_3b.mp4" -f segment -segment_times "00:03:59.578,00:08:06.379" -c copy -map 0 "Tape_10_3b_(%03d).mp4"

Jika ada yang salah, lihat di ffmpeg.log yang sesuai



Referensi



Melakukan

  • Tanyakan OP apakah format CSV lebih baik daripada file teks sebagai file cut point, sehingga Anda dapat mengeditnya dengan Excel sedikit lebih mudah
    »Diimplementasikan
  • Menerapkan cara untuk memformat stempel waktu sebagai [jj]: [mm]: [dd], [milidetik] daripada hanya beberapa detik
    »Diimplementasikan
  • Menerapkan perintah ffmpeg untuk membuat file arsitektur png untuk setiap bab
    »Diimplementasikan
  • Rumuskan apakah -c copycukup untuk skenario OP atau kita perlu menyandikan ulang sepenuhnya.
    Sepertinya Ryan sudah di atasnya .
nixda
sumber
Sungguh jawaban yang teliti! Saya menghargainya! Sayangnya, saya sedang menggunakan mac sekarang dan harus terlebih dahulu mencari tahu bagaimana menerjemahkan ini ke Bash.
Ryan
Saya belum bisa membuatnya bekerja di PowerShell sama sekali. File log tetap kosong.
Ryan
Saya akan melihat apa yang dapat saya unggah untuk membantu. Tetap disini. Terima kasih atas minat Anda!
Ryan
Saya menambahkan sampel mp4 ke pertanyaan. Terima kasih atas bantuan Anda!
Ryan
Untuk tujuan privasi, saya tidak akan mengunggah MOV sumber sebenarnya. Saya ingin memotongnya dan menghapus audio (seperti yang saya lakukan untuk mp4). Jadi itu tidak akan menjadi sumber asli yang sebenarnya. Meski begitu, itu akan terlalu besar untuk diunggah. Dan langkah pemecahan adegan mungkin harus terjadi pada file mp4, bukan file MOV untuk keperluan ruang disk. Terima kasih!
Ryan
3

Inilah bonusnya: Skrip PowerShell ke-3 ini menghasilkan gambar thumbnail mosaik

Anda akan menerima satu gambar per bab video yang semuanya terlihat seperti contoh di bawah ini.

### Options __________________________________________________________________________________________________________
$ffmpeg = ".\ffmpeg.exe"       # Set path to your ffmpeg.exe; Build Version: git-45581ed (2014-02-16)
$folder = ".\Videos\*"         # Set path to your video folder; '\*' must be appended
$x = 5                         # Set how many images per x-axis
$y = 4                         # Set how many images per y-axis
$w = 384                       # Set thumbnail width in px (full image width: 384px x 5 = 1920px,)

### Main Program ______________________________________________________________________________________________________

foreach ($video in dir $folder -include "*_???.mp4" -r){

  ### get video length in frames, an ingenious method
  $log = & $ffmpeg -i $video -vcodec copy -an -f null $video 2>&1   
  $frames = $log | Select-String '(?<=frame=.*)\S+(?=.*fps=)' | % { $_.Matches } | % { $_.Value }  
  $frame = [Math]::floor($frames / ($x * $y))

  ### put together the correct new picture name
  $output = $video.directory.Fullname + "\" + $video.basename + ".jpg"

  ### use ffmpeg to create one mosaic png per video file
  ### Basic explanation for -vf options:  http://trac.ffmpeg.org/wiki/FilteringGuide
#1  & $ffmpeg -y -i $video -vf "select=not(mod(n\,`"$frame`")),scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output
#2  & $ffmpeg -y -i $video -vf "yadif,select=not(mod(n\,`"$frame`")),scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output 
    & $ffmpeg -y -i $video -vf "mpdecimate,yadif,select=not(mod(n\,`"$frame`")),scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output       

#4  & $ffmpeg -y -i $video -vf "select='gt(scene\,0.06)',scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 -vsync vfr $output  
#5  & $ffmpeg -y -i $video -vf "yadif,select='gt(scene\,0.06)',scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 -vsync vfr $output  
#6  & $ffmpeg -y -i $video -vf "mpdecimate,yadif,select='gt(scene\,0.06)',scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 -vsync vfr $output  

#7  & $ffmpeg -y -i $video -vf "thumbnail,scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output
#8  & $ffmpeg -y -i $video -vf "yadif,thumbnail,scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output
#9  & $ffmpeg -y -i $video -vf "mpdecimate,yadif,thumbnail,scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output
}

Gagasan utamanya adalah mendapatkan aliran gambar yang berkelanjutan melalui video yang lengkap. Kami melakukan ini dengan opsi pilih ffmpeg .

Pertama, kami mengambil jumlah frame total dengan metode cerdik (misalnya 2000) dan membaginya melalui jumlah thumbnail standar kami (misalnya 5 x 4 = 20). Jadi kami ingin menghasilkan satu gambar setiap 100 frame sejak itu2000 / 20 = 100

Perintah ffmpeg yang dihasilkan untuk menghasilkan thumbnail bisa terlihat seperti

ffmpeg -y -i input.mp4 -vf "mpdecimate,yadif,select=not(mod(n\,100)),scale=384:-1,tile=5x4" -frames:v 1 output.png

Dalam kode di atas, Anda melihat 9 -vfkombinasi berbeda yang terdiri dari

  • select=not(mod(n\,XXX)) di mana XXX adalah framerate yang dihitung
  • thumbnail yang memilih frame paling representatif secara otomatis
  • select='gt(scene\,XXX)+ di -vsync vfrmana XXX adalah ambang yang harus Anda mainkan
  • mpdecimate- Hapus frame duplikat dekat. Bagus terhadap adegan hitam
  • yadif- Deinterlace gambar input. Tidak tahu mengapa, tetapi itu berhasil

Versi 3 adalah pilihan terbaik menurut saya. Semua yang lain dikomentari, tetapi Anda masih bisa mencobanya. Saya dapat menghapus sebagian besar thumbnail yang buram menggunakan mpdecimate, yadifdan select=not(mod(n\,XXX)). Ya!

Untuk video contoh Anda, saya mendapatkan pratinjau ini

masukkan deskripsi gambar di sini
klik untuk memperbesar

masukkan deskripsi gambar di sini
klik untuk memperbesar

Saya mengunggah semua thumbnail yang dibuat oleh versi-versi itu. Lihatlah mereka untuk perbandingan lengkap.

nixda
sumber
Sangat keren! Saya akan lihat ini. Saya menghabiskan berjam-jam hari ini juga mencoba select='gt(scene\,0.4)'untuk bekerja. Dan saya juga tidak bisa mulai fps="fps=1/600"bekerja. Omong-omong, apakah Anda punya ide tentang superuser.com/questions/725734 ? Terima kasih banyak untuk semua bantuan Anda. Saya sangat senang membantu keluarga saya menemukan banyak kenangan lama.
Ryan
Sudahkah Anda mencoba 5 varian encode lainnya yang saya daftarkan di bagian bawah ? Saya menambahkan contoh yang berfungsi untuk opsi "adegan". Lupakan filter FPS. Saya berhasil menghapus sebagian besar jempol yang buram :)
nixda
0

Hargai kedua skrip, cara terbaik untuk membagi video.

Namun, saya memiliki beberapa masalah dengan video yang tidak menunjukkan waktu yang tepat sesudahnya dan saya tidak dapat melompat ke waktu tertentu. Salah satu solusinya adalah dengan demux dan kemudian mux stream menggunakan Mp4Box.

Cara lain yang lebih mudah bagi saya adalah menggunakan mkvmerge untuk membelah. Oleh karena itu, kedua skrip harus diubah. Untuk detect_black.ps1, hanya "* .mkv" yang harus ditambahkan ke opsi filter, tetapi itu tidak harus jika Anda mulai hanya dengan file mp4.

### Options        
$mkvmerge = ".\mkvmerge.exe"        # Set path to mkvmerge.exe
$folder = ".\*"                     # Set path to your video folder; '\*' must be appended
$filter = @("*.mov", "*.mp4", "*.mkv")        # Set which file extensions should be processed

### Main Program 
foreach ($video in dir $folder -include $filter -exclude "*_???.*" -r){

  ### Set path to logfile
  $logfile = "$($video.FullName)_ffmpeg.log"

  ### Read in all cutpoints from *_cutpoints.csv; concat to string e.g "00:03:23.014,00:06:32.289,..."  
  $cuts = ( Import-Csv "$($video.FullName)_cutpoints.csv" | % {$_.cut} ) -join ","

  ### put together the correct new name, "%03d" is a generic number placeholder for mkvmerge
  $output = $video.directory.Fullname + "\" + $video.basename + "-%03d" + ".mkv"

  ### use mkvmerge to split current video in parts according to their cut points and convert to mkv
  & $mkvmerge -o $output --split timecodes:$cuts $video
}

Fungsinya sama, tetapi tidak ada masalah dengan waktu video sejauh ini. Terima kasih atas inspirasinya.

Andy Gebauer
sumber
-2

Saya harap saya salah, tetapi saya tidak berpikir apa yang Anda minta itu mungkin. Mungkin dimungkinkan saat melakukan encoding ulang video, tetapi sebagai "irisan lossless sederhana" saya tidak tahu cara apa pun.

Banyak program pengeditan video akan memiliki fitur analisis otomatis atau sesuatu yang setara yang mungkin dapat membagi video Anda pada bingkai hitam, tetapi ini akan memerlukan pengodean ulang video.

Semoga berhasil!

tbenz9
sumber
Sangat mungkin. Anda dapat mengiris video dengan cara lossless tanpa masalah (mis. Dengan muxer segmen di ffmpeg , lihat juga wiki ffmpeg pada pemotongan video ). Setiap program pengeditan video yang layak harus dapat melakukannya. Satu-satunya masalah adalah mendeteksi perubahan adegan.
slhck
1
Sebenarnya kamu tidak bisa. Masalah terbesar mungkin adalah keyframe. Semua klip harus dimulai dengan bingkai kunci agar dapat ditampilkan dengan benar pada pemutaran bahkan dalam alat pengeditan. Jika frame hitam tidak dimulai dengan keyframe maka akan ada ketidakcocokan saat menggunakan pemotongan lossless.
JasonXA