如何让图片开口说话,或者让视频里的人换一段话?在抖音上有很多大神做的视频非常秀,能让美国总统说中文,嘴形都是对应的,音色也是对应的。还有一些软件,上传一个图片就能让人物说话,比如这个:D-ID。我调研的这块并不多,sd wed UI有SadTalker,我仅仅调研了两类,一种是通过一张图构建一个数字人形象,然后对口型,再加上一些动作幅度,比如眨眼睛和点头等;另一种就是对口型。
CSDN传视频真是太阴间了,都扭曲了。因为怕版权纠纷,我就不放修改后的影视作品了。并且我不想出境,所以我就不放自己的录制的视频了。Wav2Lip+GFPGAN的效果很好,达到了商用级别。很容易上手,十几分钟就能跑起代码,你们自己去试试吧。
SadTalker
stable diffusion有一个插件SadTalker,输入一个图片和一段音频,可以让图中人物说起话来,并且有一定的动作幅度。SadTalker的界面如下:
我们来测试一下SadTalker的能力,我们首先生成一场人物图,我这里使用的模型是majic,如图:
SadTalker仅仅只是对头部做处理,预处理选择“裁剪”,就是只保留头部,勾选GFPGAN面部赠强,效果如下,我们可以看到嘴形基本都对应上了,但是脸前后有一定的变形。(csdn视频扭曲)
(csdn视频扭曲)
如果预处理选择“完整”,就是头部处理结束之后,再拼接到整个图像中,可以看到头部基本就是和上面的一样。效果很差,拼接部分的马尾辫都对应不上,只有头部在动身体不动,非常诡异。(csdn视频扭曲)
(csdn视频扭曲)
SadTalker安装
sd web UI以插件的方式安装SadTalker,使用秋叶大神的整合包的话,只需要在版本管理这里安装新扩展,搜SadTalker就有。也可以在启动之后,从UI中的“扩展”这里,输入githup网址安装。安装之后需要下载模型,在sd目录extensions\SadTalker\scripts下面有download_models.sh,windows不能直接运行sh,可以记事本打开这个文件,然后从里面一一下载并放到对应的目录。这些文件都是从git下载的,挂科学上网下载速度会比较快,不然会等很久。
AnimateDiff+wav2lip
之前介绍了AnimateDiff生成动画,我们在之前AnimateDiff的基础之上让动画开口说话,之前AnimateDiff的内容在我之前的博客中。这里使用到的技术是wav2lip,它俗称对口型模型,通过名字也能看出就是把声音应用到嘴唇上。git网址:https://github.com/Rudrabha/Wav2Lip
git上也有模型的链接,下载后放到对应的目录即可。这里建议用conda做一个虚拟镜像,后面的GFPGAN也用这个环境就行。
有了之前AnimateDiff生成的动画之后,我们还需要准备一段音频,这里我们可以自己录制,也可以去找一个在线的文字转语音工具,比如这个:文字转语音-在线AI转换官网
注意,我们AnimateDiff生成动画时候选择输出格式为mp4,并且参数配置闭环这里选择A,也就是尽量保证第一帧和最后一帧一样。因为声音和动画的长度一般是不对应的,语音较长时wav2lip会重复使用动画,如果动画闭环的话,看起来会流畅些,我这里设置的是16帧,有些卡顿。由于这份代码不带脸部增强,所以脸有些模糊。
动画斗地主
上面的SadTalker使用了GFPGAN,我们也使用这个方法做脸部增强,代码和模型直接从githup上下载就行:
https://github.com/TencentARC/GFPGAN/tree/master
GFPGAN的输入输出是图片,所以这里还需要视频拆帧,然后一张图一张图的送到GFPGAN,最后再组合起来,我用ChatGPT写了拆帧的脚本,和组合的脚本,拆帧脚本输入是MP4视频如下:
def extract_frames(video_path, output_folder):
# Open the video file
vidcap = cv2.VideoCapture(video_path)
fps = vidcap.get(cv2.CAP_PROP_FPS)
print(f"Video frame rate: {fps} fps")
success, image = vidcap.read()
count = 0
# Iterate through video frames
while success:
# Save frame as JPEG file
frame_path = os.path.join(output_folder, f"frame_{count}.jpg")
cv2.imwrite(frame_path, image)
success, image = vidcap.read()
count += 1
再合并为视频比较麻烦,大家可以自己写脚本,需要输入的东西有之前的wav音频、图片帧、和帧率fps,帧率就是上个脚本打印的数值,一定要注意我这个代码中加载图片的时候是错误的,我懒得修改了就用的屏蔽的那一行,我用ChatGPT写的脚本如下:
def combine_audio_video(audio_path, frames_folder, fps, output_path):
# Load audio file
audio = AudioFileClip(audio_path)
# Load frames
frame_files = sorted(os.listdir(frames_folder)) #注意这里排序是错误的,需要修改
# frame_files = [f"frame_{count}.jpg" for count in range(30)]
frames = [ImageClip(os.path.join(frames_folder, f)).set_duration(1/fps)
for f in frame_files]
# Combine audio and frames into video
video = concatenate_videoclips(frames, method="compose")
video = video.set_audio(audio)
# Write video to output file
video.write_videofile(output_path, fps=fps)
这是增强之后效果,脸部看着好多了:
漫画增强
我这里还用AnimateDiff做了一个真人风格的动画,由于时间太长,我就用了8帧的帧率,太卡顿了,然后使用wav2lip,不使用GFPGAN的效果如下:
人斗地主
使用GFPGAN脸部增强之后,效果如下:
真人增强
wav2lip+GFPGAN的效果是很好的,上面都是AnimateDiff的劣质视频做的,效果不好