深度学习系列64:数字人wav2lip详解

1. 整体流程

第一步,加载视频/图片和音频/tts。用melspectrogram将wav文件拆分成mel_chunks。
第二步,调用face_detect模型,给出人脸检测结果(可以改造成从文件中读取),包装成4个数组batch:img_batch(人脸),mel_batch(语音),frame_batch(原图),coords_batch(坐标)
第三步,加载模型,进行计算。这个模型目前看下来就是简单的resnet,没有transfomer。另外mask也不是用分割模型,而是直接将图片下半部分全部作为mask😄,然后将mask图片拼接到原图片的色彩通道上作为输入。
第四步:预测出来的人脸拼接到原图上,输出位视频。

2. 优缺点

优点:极其简单,一个人脸检测模型+一个基于CNN的lipsync模型,速度很快。
缺点:嘴唇经常是歪的,而且有变形;牙齿不断在闪烁。经过图像增强后,我们取出截图如下:
在这里插入图片描述

3. 其他版本

3.1 Easy_Wav2Lip

这个版本相当好用。首先执行python install.py来下载模型文件。然后配置一下config.ini,执行python run.py即可。
生成配置文件的代码可以在目录下的Easy_Wav2Lip_v8.3.ipynb中来修改;也可以通过执行python GUI.py打开图形界面来修改:
在这里插入图片描述
执行代码的入口仍然是inference.py。这里说明一下分支内容:

  1. 基础人脸检测模型为RetinaFace,模型文件为checkpoints/mobilenet.pth。
  2. 如果使用Imporved模式,会调用load_sr()方法加载sr_model(gfpgan做super resolution,参数文件);如果使用Enhanced,会进行upscale。具体的表现是:如果仅使用imporved模式,嘴部会比较模糊;使用enhanced模式会得到清晰度统一的视频。
  3. 如果mouth_tracking为true,则会调用复杂一些的create_tracked_mask;否则仅启用create_mask
  4. 模型可选用"Wav2Lip", "Wav2Lip_GAN"两种。

在github的项目文件里面有一个ipynb文件可供学习。

3.2 Wav2Lip-fast

使用如下代码执行:
python inference.py --checkpoint_path <ckpt> --face <video.mp4> --audio <an-audio-source> --multiplier <multiplier-to-fasten-process>
这里的multiplier,指的是每隔多少帧进行一次face detection。
简化版代码如下:

import cv2,audio,face_detection,subprocess,torch,platform,sys
from models import Wav2Lip
from tqdm import tqdm
import numpy as np
facefile = '../openheygen/video-retalking/examples/face/2.mp4'
audiofile = '../openheygen/video-retalking/examples/audio/1.wav'
checkpoint_path = 'checkpoints/wav2lip_gan.pth'
base_name = facefile.split('/')[-1]
device = 'mps'
fps = 25
mel_step_size = 16
multiplier = 1
img_size = 96
face_det_batch_size = 16
batch_size = 128
wav = audio.load_wav(audiofile, 16000)
mel = audio.melspectrogram(wav)
mel_chunks = []
mel_idx_multiplier = 80./fps 
detector = face_detection.FaceAlignment(face_detection.LandmarksType._2D,flip_input=False, device=device)def face_detect(images, multiplier=1):predictions = []batch_size = face_det_batch_sizefor i in range(0, len(images), batch_size * multiplier):predictions.extend(detector.get_detections_for_batch(np.array(images[i:i + batch_size]), multiplier))results = []pady1, pady2, padx1, padx2 = [0, 10, 0, 0]for rect, image in zip(predictions, images):y1 = max(0, rect[1] - pady1)y2 = min(image.shape[0], rect[3] + pady2)x1 = max(0, rect[0] - padx1)x2 = min(image.shape[1], rect[2] + padx2)results.append([x1, y1, x2, y2])boxes = np.array(results)results = [[image[y1: y2, x1:x2], (y1, y2, x1, x2)] for image, (x1, y1, x2, y2) in zip(images, boxes)]return results def datagen(frames, mels, multiplier):img_batch, mel_batch, frame_batch, coords_batch = [], [], [], []face_det_results = face_detect(frames, multiplier) for i, m in enumerate(mels):idx = i%len(frames)frame_to_save = frames[idx].copy()face, coords = face_det_results[idx].copy()face = cv2.resize(face, (img_size, img_size))img_batch.append(face)mel_batch.append(m)frame_batch.append(frame_to_save)coords_batch.append(coords)if len(img_batch) >= batch_size:img_batch, mel_batch = np.asarray(img_batch), np.asarray(mel_batch)img_masked = img_batch.copy()img_masked[:, img_size//2:] = 0img_batch = np.concatenate((img_masked, img_batch), axis=3) / 255.mel_batch = np.reshape(mel_batch, [len(mel_batch), mel_batch.shape[1], mel_batch.shape[2], 1])yield img_batch, mel_batch, frame_batch, coords_batchimg_batch, mel_batch, frame_batch, coords_batch = [], [], [], []if len(img_batch) > 0:img_batch, mel_batch = np.asarray(img_batch), np.asarray(mel_batch)img_masked = img_batch.copy()img_masked[:, img_size//2:] = 0img_batch = np.concatenate((img_masked, img_batch), axis=3) / 255.mel_batch = np.reshape(mel_batch, [len(mel_batch), mel_batch.shape[1], mel_batch.shape[2], 1])yield img_batch, mel_batch, frame_batch, coords_batchdef load_model(path):model = Wav2Lip()print("Load checkpoint from: {}".format(path)) #torch.load(checkpoint_path)checkpoint = torch.load(path,map_location=torch.device(device))s = checkpoint["state_dict"]new_s = {}for k, v in s.items():new_s[k.replace('module.', '')] = vmodel.load_state_dict(new_s)model = model.to(device)return model.eval()print('step1: read files...')    
if facefile.split('.')[-1] in ['png','jpg','jpeg']:full_frames = [cv2.imread(facefile)]
else:full_frames = []video_stream = cv2.VideoCapture(facefile)fps = video_stream.get(cv2.CAP_PROP_FPS)while 1:still_reading, frame = video_stream.read()if not still_reading:video_stream.release()breakfull_frames.append(frame)
i = 0
while 1:start_idx = int(i * mel_idx_multiplier)if start_idx + mel_step_size > len(mel[0]):mel_chunks.append(mel[:, len(mel[0]) - mel_step_size:])breakmel_chunks.append(mel[:, start_idx : start_idx + mel_step_size])i += 1
full_frames = full_frames[:len(mel_chunks)]
gen = datagen(full_frames.copy(), mel_chunks, multiplier)print('step2: load model and predict lip...')
results =[]
model = load_model(checkpoint_path)
frame_h, frame_w = full_frames[0].shape[:-1]
for i, (img_batch, mel_batch, frames, coords) in enumerate(tqdm(gen,total=int(np.ceil(float(len(mel_chunks))/batch_size)))):img_batch = torch.FloatTensor(np.transpose(img_batch, (0, 3, 1, 2))).to(device)mel_batch = torch.FloatTensor(np.transpose(mel_batch, (0, 3, 1, 2))).to(device)with torch.no_grad():pred = model(mel_batch, img_batch)pred = pred.cpu().numpy().transpose(0, 2, 3, 1) * 255.for p, f, c in zip(pred, frames, coords):y1, y2, x1, x2 = cp = cv2.resize(p.astype(np.uint8), (x2 - x1, y2 - y1))f[y1:y2, x1:x2] = presults.append(f)print('step3: write file with audio...')
import matplotlib.pyplot as plt
out = cv2.VideoWriter('temp.mp4', cv2.VideoWriter_fourcc(*'mp4v'), fps, (frame_w, frame_h))
for pp in results:out.write(pp)
out.release()
command = 'ffmpeg -loglevel error -y -i {} -i {} -strict -2 -q:v 1 {}'.format(audiofile, 'temp.mp4', 'result.mp4')
subprocess.call(command, shell=platform.system() != 'Windows')
from IPython.display import HTML
display(HTML("""<video height=400 controls><source src=result.mp4 type="video/mp4"></video>"""))

如果需要的话,可以进行一次画质增强:

sys.path.insert(0, 'third_part/GFPGAN')
from third_part.GFPGAN.gfpgan import GFPGANer
restorer = GFPGANer(model_path='checkpoints/GFPGANv1.3.pth', upscale=1, arch='clean', channel_multiplier=2, bg_upsampler=None)
final_results = []
for r in tqdm(results):final_results.append(restorer.enhance(r, has_aligned=False, only_center_face=True, paste_back=True)[2])  import matplotlib.pyplot as plt
plt.imshow(cv2.cvtColor(final_results[0], cv2.COLOR_BGR2RGB))
out = cv2.VideoWriter('temp.mp4', cv2.VideoWriter_fourcc(*'mp4v'), fps, (frame_w, frame_h))
for pp in final_results:out.write(pp)
out.release()
command = 'ffmpeg -loglevel error -y -i {} -i {} -strict -2 -q:v 1 {}'.format(audiofile, 'temp.mp4', 'result.mp4')
subprocess.call(command, shell=platform.system() != 'Windows')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/4861.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

html显示PDF并兼容IE浏览器的解决方案

方案一、vue-pdf插件 缺点&#xff1a;IE11显示空白&#xff0c;编译后的Edge测试环境可以正常线上&#xff0c;打到线上报错&#xff0c;谷歌和百分浏览器显示完美 1、vue 只显示核心代码&#xff0c;需要安装vue-pdf插件 <vue-pdf :src"ivcPdfUrl"></v…

交换机的八大功能

VLAN(Virtual Local Area Network&#xff0c;虚拟局域网)&#xff0c;逻辑上将设备隔离成不同局域网&#xff0c;不受物理线路的限制。不同VLAN之间互相隔离 优点 ①有效控制广播域范围; ②增强局域网的安全性; ③灵活构建虚拟局域网。 02端口隔离 端口隔离功能可以限制一个…

Spring声明式事务(@Transactional)原理之-ProxyTransactionManagementConfiguration

文章目录 目录 文章目录 前言 一、切入点以及切面的匹配规则 1.1 TransactionAttributeSourcePointcut事务的切入点匹配 二、TransactionInterceptor切面的具体逻辑 2.1 声明式事务实现的具体逻辑 总结 前言 上一篇文章已经说过了声明式事务的原理其实就是SpringAop动态…

P4 vscode 插件

Perforce for VS Code 并安装 Perforce:Client 填写perforce 客户端中你自己的workspace 名称Perforce:User p4的用户名Perforce:Password p4的密码Perforce:Port p4连接的服务器地址和端口&#xff0c; 例如 127.0.0.1&#xff1a;80 为了方便开发&#xff0c;对于代码工程&…

C语言基础:初识指针(二)

当你不知道指针变量初始化什么时&#xff0c;可以初始化为空指针 int *pNULL; 我们看NULL的定义&#xff0c;可以看出NULL是0被强制转化为Void* 类型的0&#xff1b;实质还是个0&#xff1b; 如何避免野指针&#xff1a; 1. 指针初始化 2. 小心指针越界 3. 指针指向空间…

2024五一杯ABC题资料合集+代码+参考论文!!!

一.赛题思路 (赛题出来以后第一时间在群内分享) 二.比赛日期和时间 比赛开始时间:2024年5月1日(周三)10;00 比赛结束时间:2024年5月4日(周六&#xff09;12:00 三.将在明天分享的资料合集如下 4.我们这边会分享22年的题目完整全部的过程 5.数学建模的29个通用模型及MATLAB…

Python网络数据抓取(3):Beautiful Soup

Beautiful Soup 这个库通常被称为Beautiful Soup 4&#xff08;BS4&#xff09;。它主要用来从HTML或XML文件中抓取数据。此外&#xff0c;它也用于查询和修改HTML或XML文档中的数据。 现在&#xff0c;让我们来了解如何使用Beautiful Soup 4。我们将采用上一节中使用的HTML数据…

使用PaddlePaddle和Ernie模型来计算文本数据的向量表示

import paddle 2from paddlenlp.transformers import ErnieTokenizer, ErnieModel 3import numpy as np 4import json 5 6# 设置PaddlePaddle的全局随机种子 7paddle.seed(1234) 8 9# 初始化分词器 10tokenizer ErnieTokenizer.from_pretrained(ernie-3.0-tiny-medium-v2-zh) …

实验室温湿度设备巡检的意义

温湿度监控设备的正常运行对于实验室的科研工作来说至关重要&#xff0c;然而&#xff0c;我们常常会遇到设备出现故障或者探头不灵敏的情况&#xff0c;这给实验室的工作带来了诸多困扰&#xff0c;为了解决这些问题&#xff0c;对实验室温湿度设备进行巡检管理也是一种非常有…

前端vue如何生成二维码

有时候有需要链接直接生成二维码在手机上看的需求&#xff0c;比如下载&#xff0c;比如信息&#xff0c;比如excel 下面先引入包 import QRCode from qrcode; 然后上代码 // 将res转换成二维码const qrCodeData JSON.stringify(res); // 将res转换为字符串作为二维码数据// …

数据结构 -- 双向链表

谁说我没有死过? 出生以前, 太阳已无数次起落. 悠久的时光被悠久的虚无吞没, 又以我生日的名义卷土重来. --史铁生 目录 1. 前言2. 双向链表的结构3. 双向链表的实现4. 完整代码5. 总结 正文开始 1. 前言 双向链表是一种常见的数据结构&#xff0c;它与单向链表相比&#x…

Java+playwright+testNG实现UI自动化测试

今天来讲讲使用Java结合最新的playwright来做UI自动化测试 目前网上大部分都是关于使用Python做自动化的教程&#xff0c;Java的比较少一些&#xff0c;但是我认为使用Java做自动化还是有优点的&#xff0c;性能就好一点&#xff0c;当然大家根据实际需求来。 一、 普通UI测试 …

【python】去除水印的几种方式

1、python调用FFMEPG的delogo函数去除水印 要使用Python调用FFmpeg的delogo filter去除视频水印&#xff0c;你需要使用subprocess模块运行FFmpeg命令。以下是一个简单的Python脚本示例&#xff1a; import subprocessdef remove_watermark(input_video, output_video, logo_…

MacPro(M1,M2芯片)Java开发和常用工具开源软件合集

目录 Java开发软件1 IDE1.1 idea1.2 Vs Code 2 开发工具2.1 数据库数据库模型管理数据库连接客户端 2.2 SSH/Telnet/Serial/Shell/Sftp客户端2.3 MarkDown编辑器2.3 代码片段管理粘贴 3小工具3.1 截图贴图3.2 Mac下修改hosts文件的图形化界面软件 Java开发软件 1 IDE 1.1 ide…

如果把软路由的网段更换成169.254.0.0/16会咋样?

前言 这几天有小伙伴在折腾软路由系统&#xff0c;然后问题就来了。 他咨询的是&#xff1a;为啥电脑连接软路由之后&#xff0c;无法访问软路由的管理页&#xff1f; 嗯。。。确实不是什么大事。但不注意看&#xff0c;还以为软路由没有正常获取到ip。 熟悉网络的小伙伴们都…

教程推荐:手机应用自动化

手机应用程序的自动化通常涉及使用专门设计的自动化框架和工具。对于Android和iOS平台&#xff0c;以下是一些常用的自动化工具&#xff1a; Android: Espresso: Espresso是谷歌官方支持的自动化测试框架。它适用于写UI测试来模拟用户对Android应用的交云。Espresso工作在应用…

Python中的map()和filter()函数:深入解析与使用场景

Python中的map()和filter()函数&#xff1a;深入解析与使用场景 在Python编程中&#xff0c;map()和filter()是两个非常实用的内置函数&#xff0c;它们可以帮助我们更高效地处理数据。这两个函数都是高阶函数&#xff0c;可以接受一个函数作为参数&#xff0c;并应用于序列&a…

实例解释:溢出和进位是咋回事?不能胡来吧!

有学生给我一段程序&#xff0c;就在运行中标志位的“怪异”表现提出问题。   程序不难懂&#xff1a; assume cs:codesg codesg segment start:mov al,0fchadd al,05h ;结果不溢出mov al,0f5hadd al,87h ;结果溢出mov ax,4c00hint 21h codesg ends end start难懂的是&a…

设计模式- 访问者模式(Visitor Pattern)结构|原理|优缺点|场景|示例

设计模式&#xff08;分类&#xff09; 设计模式&#xff08;六大原则&#xff09; 创建型&#xff08;5种&#xff09; 工厂方法 抽象工厂模式 单例模式 建造者模式 原型模式 结构型&#xff08;7种&#xff09; 适配器…

leetcode-有效括号序列-94

题目要求 思路 1.使用栈的先进后出的思路&#xff0c;存储前括号&#xff0c;如果st中有对应的后括号与之匹配就说明没问题 2.有两个特殊情况就是字符串第一个就是后括号&#xff0c;这个情况本身就是不匹配的&#xff0c;还有一种是前面的n个字符串本身是匹配的&#xff0c;这…