使用librosa实现视频素材音乐卡点

背景

近期业务上有一个需求:需要将我们已有的一些视频素材作为数据源,结合有节奏感的BGM,输出一个能够卡点的视频素材。类似于这样的功能在很多视频剪辑软件如剪映、快影中都能够看到,它们会让用户上传多个视频,然后再让用户挑选一个喜欢的BGM,就可以快速输出一个卡点好的视频。我们利用librosa简单实现了这样的功能,并结合后台的一系列自动化工具,使用户能够快速得到大量的具有音乐卡点效果的视频素材,我们将该能力与现有系统相结合,提升了混剪视频制作效率并降低了制作成本。

本篇文章去除了业务上的一些细节,将如何利用开源工具快速实现音乐卡点功能做了总结整理。

使用到的开源工具

OpenCV:OpenCV(开源计算机视觉库)是一个开源计算机视觉和机器学习软件库。可以用于图像处理、视频处理、特征提取等很多场景。这里我们用它来获取视频帧率、尺寸、帧,并用于视频的帧级别的裁剪。

spleeter:spleeter是一个源分离器。可以用于音频的分离,将人声、鼓、贝斯和其他声音分离开来,便于后续的声音处理。

librosa:librasa是一个用于音乐和音频分析的 python 包。可以用于提取音频的采样值、采样率、节拍时间点等信息。

AudioCraft:AudioCraft 是一个用于音频生成深度学习研究的 PyTorch 库。 其中它的 MusicGen 功能可以用于生成音乐。

AudioSegment:AudioSegment 可以用于音频文件的裁剪。

整体逻辑

从整体逻辑看,整个流程不算复杂。共有如下几个步骤:

  1. 确定合适的数据源,也就是素材物料库,可以从原始物料中来,也可以从素材成片来。
  2. 离线将大量的视频做好切幕以及分类。
  3. 将上述分幕按照一定的规则导入到剪辑服务中。
  4. 剪辑服务输出成品卡点视频的地址,以供用户使用。

实现流程

BGM来自于成熟曲库

一般像剪映以及快影“剪同款”中的“卡点”功能就都算是BGM来自于成熟曲库。因为曲库中的音乐该在什么时刻进行卡点都已经是生成好了的。

后台需要做的事情仅就是将导入的多个视频裁剪为适合拼接的视频即可。这种卡点方式的实现流程比较简单。

  1. 接收用户传入的多个视频,此处校验视频时长是否满足需求。
  2. 导入音乐的卡点方案,这个卡点方案如果是时刻的数组,则可以使用 numpy 转为时间间隔的数组。如:将[1.345, 2.345, 3.456, 4.567, 5.678]转为[1.345, 1, 1.111, 1.111, 1.111]。便于后续视频的裁剪。
  3. 选择视频并进行视频裁剪,此处的视频可能是与卡点间隔一一对应的,也可能是随机或依据某个规则选择的。可以使用OpenCV进行视频裁剪,它可以实现帧级别的处理,非常适合卡点这种对时间要求较高的场景。

用OpenCV做视频裁剪的示例代码如下:

def video_stretch(input_file, output_file, dt):
    cap = cv2.VideoCapture(input_file)
    fps = cap.get(cv2.CAP_PROP_FPS)
    (w, h) = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
    # 读取 video
    video = []
    success = 1
    while success:
        success, frame = cap.read()
        if success:
            video.append(frame)
    cap.release()
    # 选择片段
    frame_cnt = int(dt * fps)
    new_fps = int(dt * fps) / dt
    start = int((len(video) - frame_cnt) / 2)
    sub_video = video[start:(start + frame_cnt)]
    # 保存到新视频
    vision_writer = cv2.VideoWriter(output_file, cv2.VideoWriter_fourcc(*'xvid'), new_fps, (w, h))
    for frame in sub_video:
        vision_writer.write(frame)
    vision_writer.release()

这里裁剪的片段是取的中间的片段,实际上也可以在此处加入算法,判断从何处截取视频。

  1. 使用 ffmpeg 做视频的拼接,示例代码如下:
concat_cmd = "concat:" + "|".join(
            [f"{workspaceMix}/bgm_stuck/tmp_ts/{scen_list_idx}_{i}.ts" for i in range(len(choose_list))])
cmd = 'ffmpeg -y -i \\"{concat_cmd}\\" -i "{audio_path}" -vcodec copy -absf aac_adtstoasc {output_file}'
os.system(cmd)

其中,{workspaceMix} 是剪辑机器上的工作空间。{scen_list_idx}_{i}.ts 是待拼接的视频片段。

ffmpeg 的拼接支持的比较简单,如果需要在视频间增加转场特效,还需要另外调用相应能力。

BGM来自于AI生成或用户上传

这种场景与上述BGM来自于成熟曲库的场景相比,多了一个在音乐中标记卡点时刻的步骤。而这一步的实现还是比较有挑战的,如果标记的不合理,那么最终呈现出的卡点效果就会很差。目前看起来剪映和快影还没有支持该功能,或许也是因为出片质量不是很好保障吧。

这里仅介绍一下AI音乐生成以及对应音乐的卡点时刻标记。用户上传音乐场景类似。

  1. AI生成音乐使用的是 AudioCraft 的 MusicGen 功能,我们只需要输入一段 Prompt,就可以得到相应的音乐。比如,我用的Prompt为:cheerful music without drums。可以得到如下音乐:
  1. 利用 Separator 将音乐做音轨分离,提取出音乐的纯粹节拍部分。代码如下:
sp = Separator(params_descriptor="spleeter:4stems")
sp.separate_to_file(orig_path, music_ai_path, codec="wav")

其中,参数params_descriptor="spleeter:4stems"表示将该段音频分离为人声、鼓、贝斯和其他声音。spleeter 还有一些别的参数可供选择。详细可参考:https://research.deezer.com/projects/spleeter.html

  1. 使用 librosa 加载鼓点音频文件,并获取到对应的音频时序数组以及采样率。代码如下:
drums, sampling_rate = librosa.load(drum_path)

其中,drum_path 是鼓点音频文件的路径,drumssampling_rate分别表示音频时序数组以及采样率,示例分别为:[0.0012345, 0.0023456, 0.0034567, …] 和 44100。

  1. 使用 librosa 获取鼓点音频的节拍时刻。代码如下:
estimated_bpm, beats = librosa.beat.beat_track(y=drums, sr=sampling_rate, units='time')

其中drumssampling_rate分别表示前面获取到的音频时序数组以及采样率。units 表示节拍位置的单位,可选time和frames,time 表示以时间为单位,frames 表示以帧数为单位。

estimated_bpm表示估计的全局节奏,以每分钟节拍为单位。beats是节拍时刻的数组。

librosa.beat.beat_track 具体的检测算法可以参考文档:http://labrosa.ee.columbia.edu/projects/beattrack/

  1. 使用 librosa 获取得到脉冲信号所在帧,使用上述 beats 计算得到节奏所在帧,并将脉冲信号所在帧与节奏所在帧进行匹配,得到节奏点。代码如下:
# 获取得到脉冲信号所在帧
onset_env = librosa.onset.onset_strength(y=y, sr=sr, hop_length=512, aggregate=np.median)
peaks = librosa.util.peak_pick(onset_env, 1, 1, 1, 1, 0.8, 5)
# 创建一个节拍值1/4、2/4、3/4、4/4的数组
M = beats * [[1 / 4], [2 / 4], [3 / 4]]
M = M.flatten()
M = np.sort(M)
# 局部脉冲与节拍点做10%的去误差,得到节奏点
L = []
for i in M:
	for j in peaks:
		if i * 0.9 < j < i * 1.1:
	    L.append(j)
L = list(set(L))
L.sort()
# 取前20个点,不够20个则全取
if len(L) > 20:
	point_list = librosa.frames_to_time(L[:20], sr=sr)
else:
	point_list = librosa.frames_to_time(L[:len(L)], sr=sr)

这里我们有个假设,就是AI生成或用户上传的音乐一定是2/4拍或3/4拍或4/4拍中的一种。这样才能通过该方法来找到节奏点。最终输出的节奏点是以脉冲信号为准的,因为有可能音乐的节奏点没有脉冲信号,我们要的卡点时刻一定是要有脉冲信号的。

librosa.util.peak_pick 的具体检测算法可以参考文档:https://librosa.org/doc/main/generated/librosa.util.peak_pick.html

  1. 使用AudioSegment 进行音频文件的裁剪,具体怎么裁剪这个就看业务需求了,参考代码如下:
# 音乐裁剪,设置开始结束时间
end_time = point_list[len(point_list) - 1] + start_time
start_time = start_time * 1000
end_time = end_time * 1000
sound = AudioSegment.from_mp3(music_path)
word = sound[start_time:end_time]
# 音乐储存路径
word.export('movie/music.wav', format="wav")
  1. 将节奏点时刻数组作为卡点方案,以及待剪辑的视频作为输入。这样后续的流程就和“BGM来自于成熟曲库”的流程一样了。

总结

本文总结了如何利用一些开源工具快速的实现视频素材BGM卡点的功能。限于篇幅,有很多细节都没有涉及到,比如更多酷炫转场的实现、相似素材场景聚类的实现等。后续如果有机会,会继续续写分享。

相关参考资料

OpenCV文档:https://docs.opencv.org/4.x/d9/df8/tutorial_root.html

spleeter:https://research.deezer.com/projects/spleeter.html

librosa:https://librosa.org/doc/latest/index.html

AudioCraft:https://audiocraft.metademolab.com/

AudioSegment:https://pydub.com/

虚拟人的快速实现:语音驱动图片

背景

由于业务需要,近期调研了如何使用开源工具快速的实现一个虚拟人,这个虚拟人来自于一个图片,我们需要做的是要让这个图片动起来,且图中人物的口型能够跟对应的语音对应上。为此,我了解几个开源的工具或模型,最终选择使用SadTalker来实现该功能。

本文是对SadTalker的使用以及其用到的基础模型做一总结。希望能够对想要实现该功能的同学有所帮助。

要达成的目标

需要有个开源工具,能够实现如下的输入输出,效果要尽可能的好,并且方便我们将该服务本地化。

输入:一段文字脚本、一个音色模型、一张人物图片

输出:一个视频,该视频中的人物能够开口说话,并且用指定的音色说出文字脚本的内容

SadTalker相关信息

Github地址:https://github.com/OpenTalker/SadTalker

hugging face地址:https://huggingface.co/spaces/vinthony/SadTalker

SadTalker的使用

关于SadTalker的使用,其官方文档已经写的比较全面了,这里不再赘述。只提一下其中一些参数的使用要点。

  1. 如果要用preprocess的full参数,则一定要加上–still参数,不然效果会很糟糕。
  2. free-view Mode参数可以用于控制头像的转动,其中参数的详细含义如下:

input_yawinput_pitch 和 input_roll 通常用于描述物体或相机在三维空间中的旋转角度,也被称为欧拉角或俯仰、偏航和翻滚角。

  • input_yaw 表示绕垂直于地面的轴旋转的角度,也称为偏航角。通常以正北方向为0度,顺时针方向为正方向,逆时针方向为负方向。
  • input_pitch 表示绕水平轴旋转的角度,也称为俯仰角。通常以水平面为0度,向上旋转为正方向,向下旋转为负方向。
  • input_roll 表示绕前后轴旋转的角度,也称为翻滚角。通常以垂直于水平面的轴为0度,向右旋转为正方向,向左旋转为负方向。
  1. 就我目前来看,如果我们需要头部动起来,那么还是不设置旋转参数的效果更好。

SadTalker使用到的模型

模型介绍

shape_predictor_68_face_landmarks.dat

shape_predictor_68_face_landmarks.dat是一个基于dlib库的人脸关键点检测模型文件,可以用于检测人脸的68个关键点,包括眼睛、眉毛、鼻子、嘴巴等部位的位置信息。这个模型文件在很多人脸识别、表情识别、人脸姿态估计等领域都有广泛的应用。(基于随机森林的回归算法)

Deep3DFaceReconstruction

**Deep3DFaceReconstruction**是一种基于深度学习的人脸三维重建技术。它通过利用深度学习算法对人脸图像进行分析和处理,从而实现对人脸的三维重建。这种技术可以广泛应用于计算机视觉、虚拟现实、增强现实等领域,为人们带来更加逼真的视觉体验。

Deep3DFaceReconstruction的核心技术是利用深度学习算法对人脸图像进行分析和处理,从而提取出人脸的三维信息。具体来说,它通过对大量的人脸图像进行训练,学习到了一种高效的特征提取方法,可以快速准确地提取出人脸的关键特征,包括面部轮廓、眼睛、鼻子、嘴巴等。然后,它通过对这些特征进行三维重建,从而得到了一个高度逼真的人脸三维模型。

Deep3DFaceReconstruction具有许多优点,例如可以实现快速高效的三维重建、可以处理不同角度和光照条件下的人脸图像、可以处理不同种族和性别的人脸图像等。此外,它还可以应用于人脸识别、人脸动画、人脸表情识别等领域,具有广泛的应用前景。

auido2pose_00140-model.pth

auido2pose_00140-model.pth是一个训练好的PyTorch模型文件,用于音频到人体姿态的转换。该模型可以根据输入的音频数据,预测出对应的人体姿态信息。在这个文件中,auido2pose_00140-model.pth表示训练过程中的第140个epoch。在每个epoch中,模型会对训练数据进行一次完整的训练,以更新模型参数。通常情况下,训练的epoch次数越多,模型的预测效果会越好,但是也会增加训练时间和计算资源的开销。该模型文件可以被应用于很多领域,如虚拟现实、运动分析、人体姿态识别等。(PoseVAE模型,基于transformer模型的)

auido2exp_00300-model.pth

auido2exp_00300-model.pth是一个训练好的模型文件,用于将音频数据转换为对应的面部表情。该模型通过分析音频数据和面部表情数据之间的关系,可以预测出对应的面部表情信息。auido2exp_00300-model.pth可以被应用于很多领域,如虚拟现实、面部表情识别、情感分析等。通过分析音频数据和面部表情数据之间的关系,该模型可以实现对面部表情的快速、准确的预测。(ExpNet模型,基于transformer模型的)

wav2lip.pth

wav2lip.pth是一个基于深度学习的语音和口型同步技术的模型文件,可以将音频和视频中的语音和口型同步,生成逼真的合成视频。该模型使用了深度学习中的卷积神经网络和循环神经网络等技术,实现了对音频和视频的特征提取和匹配。wav2lip.pth可以被应用于很多领域,如虚拟现实、视频编辑、电影制作等。通过将音频和视频进行同步,可以实现更加逼真的人机交互和视频合成效果,提高用户体验。(基于循环神经网络)

mapping_00109-model.pth.tar

mapping_00109-model.pth.tar的作用是将输入音频中学习到的逼真的3D运动系数转换为对应的基础向量,从而生成一个逼真的3D人脸。具体来说,mappingNet模型学习从显式的3DMM运动系数(头部姿态和表情)到隐式的基础向量之间的关系,并将这个关系应用于输入音频中学习到的逼真的3D运动系数,从而生成对应的基础向量。最后,这个基础向量与3DMM模型中的基础向量相结合,生成一个逼真的3D人脸。(基于GAN神经网络)

facevid2vid_00189-model.pth.tar

facevid2vid_00189-model.pth.tar是一个基于深度学习的人脸动作转换模型,可以将输入的人脸视频转换成指定动作的人脸视频。简单来说,可以将上述3D模型视频转为穿了图片皮肤的视频。(基于GAN神经网络)

GFPGANv1.4.pth

GFPGANv1.4.pth是一种图像超分辨率重建模型,用于将低分辨率图像(LR)提升至高分辨率图像(HR)。它是由腾讯优图实验室提出的一种基于生成对抗网络(GAN)的方法,可以在不失真的情况下提高图像质量。GFPGANv1.4.pth是该模型的一个预训练权重文件,可以用于对新的低分辨率图像进行重建。该模型可以应用于许多领域,如数字娱乐、医学影像、安防监控等。(基于GAN神经网络)

处理流程中的相关模型

  1. 对原始图片进行裁剪,这一步用到shape_predictor_68_face_landmarks.dat算法模型识别出人脸,并使用CV2进行裁剪处理。
  2. 将原始图片中的人脸识别成一个3D人脸模型系数,并存储在mat格式的文件中。
  3. 3DMM提出给定视频中的眨眼动作。(将视频中的人脸识别成一个3D人脸模型系数,并存储在mat格式的文件中。)
  4. 3DMM提出给定视频中的姿势动作。(将视频中的人脸识别成一个3D人脸模型系数,并存储在mat格式的文件中。)
  5. 从音频中提取出姿势、表情以及唇形,并存储在mat格式的文件中。
  6. 使用上述系数渲染出一个3D人脸模型,并用ffmpeg命令将音频与视频做结合。
  7. 将视频与图片做结合。
  8. 效果增强,使视频变得更加清晰。

模型处理的时延测试

音频时长:10秒

处理总时间:366054毫秒(6分钟)

模型处理时间总占比:97.20%

测试机器配置:半张NVIDIA Tesla T4显卡、显存:8GB

处理过程模型处理时间(单位:毫秒)处理时间占比
图片的预处理shape_predictor_68_face_landmarks.dat
epoch_20.pth92962.54%
眨眼视频的处理shape_predictor_68_face_landmarks.dat
epoch_20.pth4094811.19%
姿势视频的处理shape_predictor_68_face_landmarks.dat
epoch_20.pth300448.21%
音频提出系数auido2exp_00300-model.pth
auido2pose_00140-model.pth
wav2lip.pth10940.30%
3D人脸模型渲染mapping_00109-model.pth.tar12231233.41%
视频与图片做结合facevid2vid_00189-model.pth.tar66461.82%
效果增强gfpgan14544939.73%

可以看到,模型处理过程中,最耗时的是3D人脸模型渲染以及效果增强,两者耗时占比超70%。

SadTalker的部署与服务

SadTalker的项目代码是用Python编写的,我们可以用FastAPI很快速的把它改造为能够通过HTTP对外提供服务的方式,并且在服务器中用uvicorn启动该服务。这样就可以在自己的本地环境中稳定的提供服务了,在有代码变更的情况下,也无需手动重启服务。

我们的服务还与腾讯云COS存储打通了,语音和图片从COS中读取,并且将处理成功的视频文件写入到COS中,并将链接地址返回。

总结

本文是对SadTalker的使用以及其用到的基础模型做了一下总结,其中包括了模型的介绍以及各模型处理时长的试验结果,但并没有涉及到模型的原理以及优化细节。对于针对于业务的优化,可以后续视情况来续写分享。

参考资料

SadTalker Github地址:https://github.com/OpenTalker/SadTalker

SadTalker hugging face地址:https://huggingface.co/spaces/vinthony/SadTalker

SadTalker最佳实践:https://github.com/OpenTalker/SadTalker/blob/main/docs/best_practice.md

FastAPI:https://github.com/tiangolo/fastapi