基于PySceneDetect的视频场景变换侦测与处理

[html地址](https://yangxunyu.xyz/upload/2023/11/%E5%9F%BA%E4%BA%8EPySceneDetect%E7%9A%84%E8%A7%86%E9%A2%91%E5%9C%BA%E6%99%AF%E5%8F%98%E6%8D%A2%E4%BE%A6%E6%B5%8B%E4%B8%8E%E5%A4%84%E7%90%86.html)

[ZIP下载链接](https://yangxunyu.xyz/upload/2023/11/%E5%9F%BA%E4%BA%8EPySceneDetect%E7%9A%84%E8%A7%86%E9%A2%91%E5%9C%BA%E6%99%AF%E5%8F%98%E6%8D%A2%E4%BE%A6%E6%B5%8B%E4%B8%8E%E5%A4%84%E7%90%86.zip)

剪映中集成了一个智能镜头分割的功能,其实是基于python的三方库PySceneDetect来实现的,主要用于对视频进行分析,寻找场景切换或剪辑。

在这里插入图片描述

不过一个一个处理起来比较麻烦,这里介绍一个python的三方库实现自动化批量处理。

文章目录

PySceneDetect

主要功能特征

PySceneDetect的安装和使用

参数说明

快速使用

内容感知检测

阈值检测

自适应检测

Python脚本实现

PySceneDetect

PySceneDetect集成了一些外部工具(如mkvmerge,ffmpeg),使其在执行split-video命令时,能够自动将视频切分为独立的片段。此外还可以生成视频的逐帧分析结果,被称为"统计文件",帮助确定最佳阈值或侦测特定视频的模式/其他分析方法。

PySceneDetect主要应用两种侦测方法:detect-threshold和detect-content。每种模式都有其特定的参数,它们在文档中都有详细的说明。可以利用淡入、淡出、切黑等方式来侦测场景边界,那么就应使用detect-threshold模式。如果视频中大量使用快速剪辑而无明确的场景边界,则应选择detect-content模式。

detect-threshold是将每一帧与设定的黑电平进行比较,主要用于侦测从黑色到黑色的淡入淡出效果。

detect-content则是对每一帧进行逐一比较,寻找内容的变化,主要用于侦测视频中快速切换的场景,不过这种方法的处理速度较慢。

主要功能特征

内容感知场景检测:这是一种能够检测出视频内容变化的场景检测技术,不仅仅局限于视频淡入淡出。

外部工具兼容性:检测出的场景边界可以以多种格式导出,并且默认格式可以直接复制粘贴到其他工具中,如 ffmpeg,mkvmerge 等。

视频统计分析:可以输出电子表格兼容的文件,用于分析视频文件中的趋势,以确定与特定场景检测方法或算法一起使用的最佳阈值。

可扩展和可嵌入:PySceneDetect 使用 Python 编写,并采用易于使用和可扩展的 API 进行设计,非常适合嵌入到其他程序中。

具有一系列的特性,包括导出场景列表、导出时间码、统计分析模式、输出抑制模式、保存每个检测到的场景的第一帧和最后一帧的图像,以及自动分割输入视频等。

提供了多种检测方法,包括阈值场景检测、内容感知场景检测和自适应内容场景检测。阈值场景检测是通过分析视频中平均帧强度/亮度的变化来实现的,内容感知场景检测是通过分析 HSV 颜色空间中帧之间的变化来实现的,而自适应内容场景检测是在内容感知场景检测的基础上,通过比较滚动窗口中的相邻帧来实现的。

PySceneDetect的安装和使用

打开命令行安装即可。

pip install scenedetect[opencv] --upgrade

1

参数说明

-i, --input VIDEO: 输入视频文件。还支持图像序列和URL。

-o, --output DIR: 输出目录,用于保存生成的文件(统计文件、输出视频、图像等…)。如果未设置,则默认为当前工作目录。

-f, --framerate FPS: 强制设置帧率,以帧/秒为单位(例如 -f 29.97)。禁用检查以确保所有输入视频具有相同的帧率。

-d, --downscale N: 按整数因子缩小帧的大小(例如 2、3、4…),其中帧的大小缩小为宽度/N x 高度/N(因此 -d 1 表示不进行缩小)。不设置将根据源分辨率自动缩小。

-fs, --frame-skip N: 在处理过程中跳过 N 帧(-fs 1 跳过每隔一帧,处理视频的 50%;-fs 2 处理 33% 的帧;-fs 3 处理 25% 的帧,依此类推)。以减少处理速度,但牺牲了一定的准确性。[默认值:0]

-m, --min-scene-len TIMECODE: 任何场景的最小长度。TIMECODE 可以是精确的帧数、以秒为单位后跟 ‘s’、或以 HH:MM:SS 或 HH:MM:SS.nnn 格式的时间码。[默认值:0.6秒]

--drop-short-scenes: 丢弃短于 min-scene-len 的场景,而不是将其与相邻场景合并。

--merge-last-scene: 如果最后一个场景比 min-scene-len 还要短,则将其与前一个场景合并。

-s, --stats CSV: 用于写入帧度量的统计文件 (.csv) 的路径。如果文件存在,则处理任何度量值;否则将创建一个新文件。可用于确定各种场景检测器选项的最佳值,并将帧计算缓存以加速多次检测运行。

-v, --verbosity LEVEL: 要显示的调试/信息/错误信息的级别。必须是:debug、info、warning、error、none。覆盖 -q/--quiet。使用 -v debug 提交 bug 报告。[默认值:info]

-l, --logfile LOG: 用于写入应用程序日志信息(主要用于调试)的日志文件路径。如果要提交 bug 报告,请同时设置 -v debug。如果 verbosity 是 none,则仍会生成具有 info 级别详细程度的日志文件。

-q, --quiet: 抑制 PySceneDetect 的所有输出到终端/标准输出。等同于 -v none。

-b, --backend BACKEND: 用于视频输入的后端。可以使用 -c/–config 进行配置。此系统上可用的后端:opencv、moviepy。[默认值:opencv]

-c, --config FILE: 配置文件的路径。如果未设置,则尝试从 C:\Users\pc\AppData\Local\PySceneDetect\scenedetect.cfg 加载配置文件。

-h, --help: 显示此帮助消息并退出。

参数 解释

-i, --input VIDEO [Required] 输入视频文件。也支持图像序列和URL。

-o, --output DIR 用于存放生成文件(统计文件、输出视频、图像等)的输出目录。如果未设置,默认为当前工作目录。某些命令允许覆盖此值。

-f, --framerate FPS 强制帧率,以帧/秒为单位(例如 -f 29.97)。禁用检查以确保所有输入视频具有相同的帧率。

-d, --downscale N 缩小帧的整数因子(例如 2、3、4…),其中帧被缩放为宽度/N x 高度/N(因此 -d 1 表示不缩小)。未设置时,根据源分辨率自动缩小。

-fs, --frame-skip N Skips N 帧进行处理(-fs 1 跳过每隔一帧,处理视频的 50%;-fs 2 处理 33% 的帧;-fs 3 处理 25% 等)。降低处理速度以换取准确性。 [default: 0]

-m, --min-scene-len TIMECODE 任何场景的最小长度。TIMECODE 可以指定为确切的帧数,以秒为单位后面加上 s,或者使用 HH:MM:SS 或 HH:MM:SS.nnn 格式的时间码。 [default: 0.6s]

–drop-short-scenes 丢弃短于 min-scene-len 的场景而不是将它们与相邻的场景合并。

–merge-last-scene 如果最后一个场景比 min-scene-len 短,则与前一个场景合并。

-s, --stats CSV 用于写入帧度量的统计文件路径(.csv)。如果文件存在,则会处理任何度量;否则将创建新文件。可用于确定各种场景检测器选项的最佳值,并且可以缓存帧计算以加速多个检测运行。

-v, --verbosity LEVEL 显示调试/信息/错误信息的级别。必须是:debug、info、warning、error、none。覆盖 -q/--quiet。使用 -v debug 获取错误报告。 [default: info]

-l, --logfile LOG 用于写入应用程序日志信息(主要用于调试)的日志文件路径。如果要提交错误报告,请同时设置 -v debug。如果没有设置详细程度,日志文件仍将以 info 级别生成。

-q, --quiet 抑制 PySceneDetect 输出到终端/标准输出的所有输出。等效于 -v none。

-b, --backend BACKEND 用于视频输入的后端。可以使用 -c/–config 进行配置。此系统上可用的后端有:opencv、moviepy。 [default: opencv]

-c, --config FILE 配置文件的路径。如果未设置,将尝试从 C:\Users\pc\AppData\Local\PySceneDetect\scenedetect.cfg 加载配置文件。

-h, --help 显示帮助信息。

快速使用

执行下面的命令可以快速的将视频进行切分处理,并保存到指定目录。

其中命令可以根据自己的参数进行他调配即可。

scenedetect -i video.mp4 --output output detect-adaptive split-video

1

整个的执行过程是这样的。

进行整段视频的分析。

然后在将视频进行拆分并保存。

在这里插入图片描述

还可以通过保存每个场景的图像生成对应的结果。

scenedetect --input video.mp4 --output output detect-adaptive list-scenes save-images

1

会在工作目录下生成一个video.scenes.csv文件,用来查看每一个场景中开始、中间、结束的单独帧。

在这里插入图片描述

内容感知检测

内容感知模式查看每对相邻帧之间的差异,当该差异超过阈值时触发场景中断。默认阈值(-t/--threshold)适用于内容感知模式 (detect-content),默认值为30。

scenedetect -i video.mp4 -s my_video.stats.csv list-scenes detect-content

1

此命令等价于

scenedetect -i video.mp4 -s video.stats.csv list-scenes detect-content -t 30

1

阈值检测

基于阈值的模式查看当前帧的平均强度,当强度低于阈值时触发场景中断。阈值检测 (detect-threshold)默认阈值为12。

scenedetect -i video.mp4 -s my_video.stats.mp4 detect-threshold

scenedetect -i video.mp4 -s my_video.stats.mp4 detect-threshold -t 20

1

2

阈值低于8可能会导致一些问题,尤其是对于低比特率或动态范围有限编码的视频。

自适应检测

detect-adaptive模式将计算出的每个帧的得分detect-content与其相邻帧的得分进行比较。可以通过frame-window选项配置相邻帧的数量,以及通过min-content-val选项设置最小分数变化。

在这里插入图片描述

Python脚本实现

Python API 提供了一个高级函数,可以在视频中执行内容感知场景检测。

from scenedetect import detect, ContentDetector: 从scenedetect库中导入detect和ContentDetector两个函数。detect函数用于检测视频中的场景变化,而ContentDetector函数用于指定检测的方法。

scene_list = detect('video.mp4', ContentDetector()): 这行代码执行了场景检测的过程。它调用了detect函数,并传入两个参数:视频文件名 'video.mp4',和场景检测方法ContentDetector()。然后,它将检测结果赋值给scene_list变量。

from scenedetect import detect, ContentDetector

scene_list = detect('video.mp4', ContentDetector())

1

2

执行后scene_list将是一个列表,包含了视频中所有场景的开始/结束时间。还有两个进阶的检测器,AdaptiveDetector适合处理快速相机移动,而ThresholdDetector则可以处理淡出/淡入事件。

如果想看到所有的场景可以如下调用print(scene_list)或者逐个迭代:

from scenedetect import detect, ContentDetector这一行导入了两个函数:detect和ContentDetector,它们是用于场景检测的。

场景检测:scene_list = detect('video.mp4', ContentDetector()) 这一行代码使用detect函数来检测名为my_video.mp4的视频文件中的场景,检测的方法是通过ContentDetector对象来实现的。

循环遍历场景:for i, scene in enumerate(scene_list): 这个循环遍历scene_list中检测到的每个场景。

打印场景信息:print(' Scene %2d: Start %s / Frame %d, End %s / Frame %d' % (i+1, scene[0].get_timecode(), scene[0].get_frames(), scene[1].get_timecode(), scene[1].get_frames())) 这行代码打印每个场景的信息,包括场景序号、开始时间码、开始帧数、结束时间码和结束帧数。它使用%操作符来格式化输出,%2d表示输出一个两位数的整数,%s表示输出字符串,%d表示输出整数。scene[0]表示场景的开始部分,scene[1]表示场景的结束部分。get_timecode()和get_frames()是用于获取时间码和帧数的方法。

from scenedetect import detect, ContentDetector

scene_list = detect('video.mp4', ContentDetector())

for i, scene in enumerate(scene_list):

print(' Scene %2d: Start %s / Frame %d, End %s / Frame %d' % (

i+1,

scene[0].get_timecode(), scene[0].get_frames(),

scene[1].get_timecode(), scene[1].get_frames(),))

1

2

3

4

5

6

7

如果已经安装了ffmpeg(也支持mkvmerge),可以把视频按每个场景进行分割:

从 scenedetect 中导入 detect,ContentDetector,和 split_video_ffmpeg。detect 是一个用于检测视频场景的函数,ContentDetector 是一个用于检测内容变化的类,split_video_ffmpeg 是一个用于按场景划分视频的函数。

调用 detect 函数,并将视频文件名 'my_video.mp4' 和一个 ContentDetector 实例作为参数传入。detect 函数会返回一个场景列表。

调用 split_video_ffmpeg 函数,并将视频文件名 'my_video.mp4' 和前面得到的场景列表作为参数传入。这个函数会根据场景列表将视频分割,并且每个场景会被保存为一个单独的文件。

from scenedetect import detect, ContentDetector, split_video_ffmpeg

scene_list = detect('my_video.mp4', ContentDetector())

split_video_ffmpeg('my_video.mp4', scene_list)

1

2

3

API 还提供了更高级的功能,例如使用不同的检测算法、分割输入视频等等,具有高度的可配置性,可以轻松与任何管道集成。

一个示例展示如何使用 API 来实现与上面类似的功能:

scenedetect 是一个用于自动检测和分割视频中的场景更改的库。此处,我们导入了 open_video(用于打开视频文件)、SceneManager(用于管理视频场景)、split_video_ffmpeg(用于按场景划分视频)、以及 ContentDetector(用于检测内容的变化来找出新的场景)。

定义了一个名为 split_video_into_scenes 的函数,这个函数接受两个参数:视频路径 video_path 和阈值 threshold(默认为27.0)。这个函数的目的是将输入的视频按场景进行分割。

在函数中使用 open_video 打开视频。

创建一个 SceneManager 对象。使用 ContentDetector 创建一个场景检测器,并设置阈值。这个检测器会对视频内容进行分析,当内容的变化超过设置的阈值时,就认为是新的场景开始了。

使用 detect_scenes 方法,传入视频和一个参数(是否展示处理进度),开始进行场景的检测。

检测完场景后,通过 get_scene_list 获取所有的场景列表。

使用 split_video_ffmpeg 方法将原视频根据场景列表进行分割,同时展示处理的进度。

from scenedetect import open_video, SceneManager, split_video_ffmpeg

from scenedetect.detectors import ContentDetector

from scenedetect.video_splitter import split_video_ffmpeg

def split_video_into_scenes(video_path, threshold=27.0):

# 打开视频

video = open_video(video_path)

# 创建一个场景管理器

scene_manager = SceneManager()

# 添加一个检测器

scene_manager.add_detector(

ContentDetector(threshold=threshold))

scene_manager.detect_scenes(video, show_progress=True)

scene_list = scene_manager.get_scene_list()

split_video_ffmpeg(video_path, scene_list, show_progress=True)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15