自动将大型.mov视频文件拆分为黑框(较小的场景)的较小文件?


9

我有65个.mov视频文件名为tape_01.mov,tape_02.mov,...,tape_65.mov。每个长约2.5小时,占用数千兆字节。它们是VHS录像带(我家的“家庭电影”)的数字副本。

每个2.5小时.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) 。我希望在此步骤中轻松配置压缩设置。创建.mp4文件后,原始.mov文件仍应存在。
  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的视频有意义的缩略图

我希望这些脚本是shell脚本,我可以在Mac,Ubuntu和Windows Git Bash中运行。

Answers:


10

以下是两个PowerShell脚本,可以通过黑色场景将长视频分成较小的章节。

将它们保存为Detect_black.ps1和Cut_black.ps1。下载 适用于Windows的ffmpeg 并在选项部分下告诉脚本ffmpeg.exe和视频文件夹的路径。

enter image description here

这两个脚本都不会触及现有的视频文件,它们保持不变。
但是,您将在输入视频所在的同一位置获得几个新文件

  • 每个视频的日志文件,其中包含用于两个ffmpeg命令的控制台输出
  • 每个视频的CSV文件,包含所有黑色场景的时间戳,用于手动微调
  • 一些新视频取决于之前检测到的黑色场景数量

enter image description here


要运行的第一个脚本: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-2s不准确,因为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



参考



去做

  • 询问OP,如果CSV格式比作为剪切点文件的文本文件更好,那么您可以更轻松地使用Excel编辑它们
    »已实施
  • 实现一种将时间戳格式化为[hh]的方法:[mm]:[ss],[毫秒]而不是秒
    »已实施
  • 实现一个ffmpeg命令为每一章创建mosaik png文件
    »已实施
  • 详细说明 -c copy 对于OP的情况或我们需要完全重新编码是足够的。
    好像Ryan已经在它上面了

多么彻底的答案!我很感激!不幸的是,我现在正在使用Mac,需要先弄清楚如何将其转换为Bash。
Ryan

我根本无法在PowerShell中使用它。日志文件保持空白。
Ryan

我会看到我可以上传的内容对您有所帮助。敬请关注。谢谢你的关注!
Ryan

我在问题中添加了一个示例mp4。谢谢你的帮助!
Ryan

出于隐私目的,我不会上传真正的源MOV。我想要砍掉它并删除音频(就像我为mp4做的那样)。所以它无论如何都不会是一个真正的原始来源。即使这样,上传也太大了。无论如何,场景分割步骤应该发生在mp4文件而不是MOV文件上,以用于磁盘空间。谢谢!
Ryan

3

这是奖励:此第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做到这一点 选择 选项。

首先,我们使用巧妙的方法(例如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是最好的选择。所有其他人都被注释掉了,但你仍然可以尝试。我能够使用删除大多数模糊的缩略图 mpdecimateyadifselect=not(mod(n\,XXX))。是啊!

为您 示例视频 我得到了这些预览

enter image description here
点击放大

enter image description here
点击放大

我上传了 所有缩略图 由这些版本创建。看看它们进行全面比较。


很酷!我来看看这个。我今天浪费了几个小时试图得到 select='gt(scene\,0.4)' 上班。我无法得到 fps="fps=1/600" 工作要么。顺便问一下,你有什么想法吗? superuser.com/questions/725734 ?非常感谢您的帮助。我非常兴奋能帮助我的家人发现大量的旧记忆。
Ryan

你有没有尝试过 我列在底部的其他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中的段muxer , 也可以看看 关于切割视频的ffmpeg wiki )。任何体面的视频编辑程序都应该能够做到。唯一的问题是检测场景变化。
slhck

1
其实你做不到。最大的问题可能是关键帧。所有剪辑都需要以关键帧开始,以便即使在编辑工具中也可以在播放时正确渲染。如果黑框不以关键帧开始,那么在使用无损剪切时会出现不兼容问题。
JasonXA
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.