Автоматично розділити великі .mov відеофайли на менші файли на чорних кадрах (зміни сцени)?


11

У мене є 65 .mov відеофайлів з назвою tape_01.mov, tape_02.mov, ..., tape_65.mov. Кожен - близько 2,5 годин і займає багато гігабайт. Вони є цифровими копіями того, що були стрічками VHS ("домашні фільми" моєї родини).

Кожен 2,5-годинний .mov-файл .mov містить багато "глав" (в тому сенсі, що іноді сцена закінчується бляклим до чорного екрану, а потім нова сцена сходить у світ).

Моя основна мета - 65 великих файлів, розрізаних на менші файли за розділами. І я хочу, щоб це було зроблено автоматично через виявлення чорних кадрів між "сценами".

Чи можу я використати ffmpeg (або що-небудь інше), щоб передати 65 оригінальних імен файлів і автоматично перетворити їх на кожне відео та порубати його, називаючи отримані фрагменти tape_01-ch_01.mov, tape_01-ch_02.mov, tape_01-ch_03.mov, тощо? І я хочу, щоб це було без повторного кодування (я хочу, щоб це був простий зріз без втрат).

Як я можу це зробити?

Ось такі кроки, які я хочу:

  1. Ітерація над папкою файлів .mov (з іменем tape_01.mov, tape_02.mov, ..., tape_65.mov), щоб експортувати їх як файли mp4 (названі tape_01.mp4, tape_02.mp4, ..., tape_65.mp4) . Я хочу, щоб налаштування стиснення було легко налаштувати для цього кроку. Оригінальні файли .mov все ще мають існувати після створення файлів .mp4.
  2. Ітерація над файлами mp4: для кожного файлу mp4 генеруйте текстовий файл, який визначає час початку та тривалість чорних сегментів між "сценами". Кожен mp4 може мати 0 або більше цих чорних обривів сцени. Час початку та тривалість повинні бути точними до частки секунди. HH: MM: Формат SS недостатньо точний.
  3. Тоді я зможу вручну двічі перевірити ці текстові файли, щоб побачити, чи правильно вони визначають зміни сцени (чорні сегменти). Я можу коригувати таймінги, якщо хочу.
  4. Інший скрипт прочитає кожен mp4-файл та його txt-файл і розділить (на дуже швидкий і без втрат спосіб) mp4 на стільки фрагментів, скільки потрібно, виходячи з рядків текстового файлу (терміни, зазначені там). Розщеплення має відбуватися посередині кожного чорного сегмента. Ось зразок файлу mp4 (зі звуком, видаленим для цілей конфіденційності), який змінює сцену, що переходить у чорний колір Оригінальні файли mp4 повинні залишатися недоторканими, оскільки створюються менші файли mp4. Менші файли mp4 повинні мати схему іменування tape_01_001.mp4, tape_01_002.mp4, tape_01_003.mp4 тощо.
  5. Бонус! Було б дивовижно, якби інший скрипт міг потім перебирати невеликі файли mp4 і генерувати одне .png зображення для кожного, що є мозаїкою скріншотів, як це намагається ця людина: Значущі ескізи для відео за допомогою FFmpeg

Я хотів би, щоб ці сценарії були скриптами оболонки, які я можу запускати в Mac, Ubuntu та Windows Git Bash.

Відповіді:


11

Ось два сценарії PowerShell для розділення довгих відеозаписів на менші глави за допомогою чорних сцен.

Збережіть їх як Detect_black.ps1 та Cut_black.ps1. Завантажте ffmpeg для Windows та скажіть сценарію шлях до вашого ffmpeg.exe та папки відео у розділі параметрів.

введіть тут опис зображення

Обидва сценарії не торкаються існуючих відеофайлів, вони залишаються недоторканими.
Однак ви отримаєте пару нових файлів там же, де є вхідні відео

  • Логфайл на відео з консольним виходом для обох використаних команд ffmpeg
  • CSV-файл на відео з усіма часовими позначками чорних сцен для ручної тонкої настройки
  • Кілька нових відео в залежності від того, скільки чорних сцен раніше було виявлено

введіть тут опис зображення


Перший запуск сценарію: 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
}

Як це працює

Перший скрипт повторюється через усі відеофайли, які відповідають вказаному розширенню та не відповідають шаблону *_???.*, оскільки нові відео глави були названі, <filename>_###.<ext>і ми хочемо їх виключити.

Він здійснює пошук усіх чорних сцен і записує часову позначку часу і тривалість чорної сцени у новий файл з назвою CSV <video_name>_cutpoints.txt

Він також розраховує вирізати точки , як показано на малюнку: cutpoint = black_start + black_duration / 2. Пізніше відео стає сегментованим на цих часових позначках.

Файл cutpoints.txt для вашого зразкового відео відображатиметься:

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

Після запуску ви можете маніпулювати точками зрізу вручну, якщо бажаєте. Якщо запустити сценарій ще раз, весь старий вміст буде перезаписаний. Будьте уважні під час редагування вручну та зберігайте свою роботу в іншому місці.

Для зразкового відео команда ffmpeg для виявлення чорних сцен є

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

У розділі параметрів сценарію можна редагувати 3 важливі номери

  • d=4 означає, що виявляються лише чорні сцени, довші ніж 4 секунди
  • pic_th=0.98 є порогом розгляду картини як "чорної" (у відсотках)
  • pix=0.15встановлює поріг для розгляду пікселя як "чорного" (в яскравості). Оскільки у вас є старі відео VHS, у вас немає абсолютно чорних сцен. Значення за замовчуванням 10 не працюватиме, і мені довелося трохи підвищити поріг

Якщо щось піде не так, перевірте відповідний файл журналу, який називається <video_name>__ffmpeg.log. Якщо відсутні наступні рядки, збільште числа, згадані вище, поки не виявите всі чорні сцени:

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



Другий сценарій для запуску: 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        
}

Як це працює

Другий скрипт повторює всі відеофайли так само, як це зробив перший сценарій. Він читається лише у вирізаних часових позначках із відповідного cutpoints.txtвідео.

Потім він збирає відповідне ім'я для файлів глав і повідомляє ffmpeg сегментувати відео. Наразі відео нарізаються без перекодування (надшвидкі та без втрат). Через це може виникнути неточність 1-2-х часових позначок точки зрізу, оскільки ffmpeg може вирізати лише у key_frames. Оскільки ми просто копіюємо та не перекодуємо, ми не можемо вставляти key_frames самостійно.

Команда для зразкового відео буде

$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"

Якщо щось піде не так, перегляньте відповідний файл ffmpeg.log



Список літератури



Зробити

  • Запитайте ОП, чи формат CSV кращий, ніж текстовий файл, як файл зрізу, так що ви можете редагувати їх за допомогою Excel трохи простіше
    »Реалізовано
  • Реалізуйте спосіб форматування часових позначок у вигляді [год]: [мм]: [сс], [мілісекунд], а не лише секунди
    »
  • Реалізуйте команду ffmpeg для створення mosaik png-файлів для кожної глави
    »Реалізовано
  • Розробіть, якщо -c copyцього достатньо для сценарію ОП або нам потрібно повністю перекодувати.
    Здається, Райан уже на ньому .

Яка ґрунтовна відповідь! Я ціную це! На жаль, я зараз на mac і мені потрібно спершу розібратися, як перекласти це на Bash.
Райан

Я взагалі не зміг змусити це працювати в PowerShell. Файли журналу залишаються порожніми.
Райан

Я побачу, що я можу завантажити, щоб бути корисним. Слідкуйте за налаштуваннями. Дякуємо за ваш інтерес!
Райан

Я додав зразок mp4 до питання. Спасибі за вашу допомогу!
Райан

З метою конфіденційності я не завантажую справжнє джерело MOV. Я хотів би розрізати його та видалити звук (як це робив для mp4). Так що це не було б справжнім оригінальним джерелом. Навіть так, це було б занадто великим для завантаження. І крок поділу сцени, мабуть, повинен відбуватися у файлах mp4 замість MOV-файлів для цілей дискового простору. Дякую!
Райан

3

Ось бонус: Цей третій сценарій PowerShell генерує мозаїчне мініатюрне зображення

Ви отримаєте по одному зображенню на кожне відео, яке виглядає на прикладі нижче.

### 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
}

Основна ідея - отримати безперервний потік зображень над повним відео. Це робимо за допомогою параметра ffmpeg select .

Спочатку ми отримуємо загальний підрахунок кадрів геніальним методом (наприклад, 2000) і ділимо його на наш підрахунок мініатюр (наприклад, 5 x 4 = 20). Тому ми хочемо створювати одне зображення кожні 100 кадрів2000 / 20 = 100

Отримана команда ffmpeg для створення мініатюри може виглядати так

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

У наведеному вище коді ви бачите 9 різних -vfкомбінацій, що складаються з

  • select=not(mod(n\,XXX)) де XXX - обчислений кадр
  • thumbnail який автоматично вибирає найбільш репрезентативні кадри
  • select='gt(scene\,XXX)+ -vsync vfrде XXX - поріг, з яким потрібно грати
  • mpdecimate- Видаліть майже повторювані кадри. Добре проти чорних сцен
  • yadif- Деінтерлінг вхідного зображення. Не знаю чому, але це працює

Версія 3 - найкращий вибір на мою думку. Усі інші коментуються, але ви все одно можете їх спробувати. Я був в змозі видалити більшу частину розмитих мініатюр з допомогою mpdecimate, yadifі select=not(mod(n\,XXX)). Ага!

Для вашого зразкового відео я отримую ці попередні перегляди

введіть тут опис зображення
Натисніть, щоб збільшити

введіть тут опис зображення
Натисніть, щоб збільшити

Я завантажив усі мініатюри, створені цими версіями. Погляньте на них для повного порівняння.


Дуже круто! Я погляну на це. Я сьогодні також витрачав години, намагаючись дістатися select='gt(scene\,0.4)'до роботи. І я не міг дістатися fps="fps=1/600"на роботу. До речі, чи маєте ви якесь уявлення про superuser.com/questions/725734 ? Дуже дякую за всю вашу допомогу. Я дуже схвильований тим, що допомагаю моїй родині виявити тони старих спогадів.
Райан

Ви спробували ті 5 інших варіантів кодування, які я вказав внизу ? Я додав робочий приклад для параметра "сцена". Забудьте про фільтр FPS. Мені вдалося видалити більшу частину розмитих великих пальців :)
nixda

0

Вдячні за обидва сценарії, чудовий спосіб розділити відео.

Тим не менш, у мене виникли проблеми з тим, що відео не відображає правильний час після цього, і я не зміг перейти до конкретного часу. Одним з рішень було демонтаж, а потім мукс потоків за допомогою Mp4Box.

Іншим, більш простим способом для мене було використання mkvmerge для розщеплення. Тому обидва сценарії довелося змінити. Для detect_black.ps1 до параметра фільтра потрібно було додати лише "* .mkv", але це не обов'язково, якщо ви починаєте лише з 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
}

Функція однакова, але досі немає проблем із відео. Дякую за натхнення


-2

Я сподіваюся, що я помиляюся, але не думаю, що можливе ваше запитання. Це може бути можливим під час перекодування відео, але як "простий фрагмент без втрат" я не знаю жодного способу.

У багатьох програмах для редагування відео буде функція автоматичного аналізу або щось еквівалентне, що може розділити ваші відео на чорних кадрах, але для цього знадобиться повторне кодування відео.

Удачі!


Це абсолютно можливо. Ви можете без проблем зрізати відео без проблем (наприклад, із сегментальним муксером у ffmpeg , див. Також вікі-файли ffmpeg про різання відео ). Будь-яка гідна програма для редагування відео повинна це вміти. Єдине питання - виявлення змін сцени.
slhck

1
Насправді ти не можеш. Найбільші проблеми можуть бути ключовими кадрами. Усі кліпи потрібно починати з ключового кадру, щоб можна було правильно відтворити під час відтворення навіть в інструментах редагування. Якщо чорні кадри не починаються із кадрів ключових кадрів, тоді не буде сумісності при використанні скорочень без втрат.
JasonXA
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.