数字人解决方案——ER-NeRF实时对话数字人模型训练与项目部署

news2024/9/23 16:21:33

前言

1、算法概述

ER-NeRF是基于NeRF用于生成数字人的方法,可以达到实时生成的效果。具体来说,为了提高动态头部重建的准确性,ER-NeRF引入了一种紧凑且表达丰富的基于NeRF的三平面哈希表示法,通过三个平面哈希编码器剪枝空的空间区域。对于语音音频,ER-NeRF提出了一个区域关注模块,通过注意机制生成区域感知的条件特征。与现有方法不同,它们使用基于MLP的编码器隐式学习跨模态关系不同,注意机制建立了音频特征和空间区域之间的明确连接,以捕获本地动作的先验知识。此外,ER-NeRF引入了一种直接且快速的自适应姿势编码,通过将头部姿势的复杂变换映射到空间坐标,来优化头部和躯干的分离问题。大量实验证明,与先前方法相比,ER-NeRF的方法可以呈现更高保真度和音频嘴唇同步的数字人,细节更加逼真。
在这里插入图片描述

2.算法比较

在官方的实验可以看到,与之前的生成的数字人相比,ER-NeRF具有更好的高保真度和音频嘴唇同步的人像谈话视频,具有更真实的细节和更高的效率。
在这里插入图片描述

一、环境安装

1.环境配置

官方给的环境配置是Ubuntu 18.04, Pytorch 1.12 和CUDA 11.3,但我在win10下使用这个配置依赖,总是报了一堆莫名的错误,而且win10下在线安装pythorch3d并不容易成功,在安装过程中也报各种各样的错误。
经过几次安装测试,在win10下,最容易装上的配置依赖是: Pytorch 2.0,CUDA11.7(11.8也试过,但在训练的时候,一直卡着不动),cudnn 8.5,要本地安装pytorch3d,所以要安装Visual Studio,版本是2019或者2022都可以。Pytorch如果在官网不好下,这里我上传了一份源码到百度网盘,可以下载使用:链接:https://pan.baidu.com/s/1ZdJy8KHDBnwOOQDgWc0OPA 提取码:sxv7
安装vs要选以下这几个功能:
在这里插入图片描述

3.环境安装

#下载源码
git clone https://github.com/Fictionarry/ER-NeRF.git
cd ER-NeRF
#创建虚拟环境
conda create --name vrh python=3.10
activate vrh
#pytorch 要单独对应cuda进行安装,要不然训练时使用不了GPU
conda install pytorch==2.0.0 torchvision==0.15.0 torchaudio==2.0.0 pytorch-cuda=11.7 -c pytorch -c nvidia
conda install -c fvcore -c iopath -c conda-forge fvcore iopath
#安装所需要的依赖
pip install -r requirements.txt

#处理音频时用的
pip install tensorflow

下载pytorch3d源码,如果下载不了,按上面的百度网盘下载:链接:https://pan.baidu.com/s/178WbPBRtCl3hF8b3niCeSA 提取码:icd8

git clone https://github.com/facebookresearch/pytorch3d.git
cd pytorch3d
python setup.py install

在安装pytorch3d可能出现错误,可以看文章结尾列的常见错误。

3.项目模型下载

为了避免在运行时下不了模型或者模型下载缓慢,这里我把所需模型我这里都下载好放在网盘上,。链接:https://pan.baidu.com/s/1UteMZauIpn15nQrgRehh1g 提取码:klr7 ,如果不习惯网盘下载,也可以按下面的命令下载模型

下载人脸解析模型79999_iter.pth放到data_utils/face_parsing/这个目录

wget https://github.com/YudongGuo/AD-NeRF/blob/master/data_util/face_parsing/79999_iter.pth?raw=true -O data_utils/face_parsing/79999_iter.pth

下载头部姿态估计模型到data_utils/face_tracking/3DMM/

wget https://github.com/YudongGuo/AD-NeRF/blob/master/data_util/face_tracking/3DMM/exp_info.npy?raw=true -O data_utils/face_tracking/3DMM/exp_info.npy
wget https://github.com/YudongGuo/AD-NeRF/blob/master/data_util/face_tracking/3DMM/keys_info.npy?raw=true -O data_utils/face_tracking/3DMM/keys_info.npy
wget https://github.com/YudongGuo/AD-NeRF/blob/master/data_util/face_tracking/3DMM/sub_mesh.obj?raw=true -O data_utils/face_tracking/3DMM/sub_mesh.obj
wget https://github.com/YudongGuo/AD-NeRF/blob/master/data_util/face_tracking/3DMM/topology_info.npy?raw=true -O data_utils/face_tracking/3DMM/topology_info.npy

下载01_MorphableModel.mat模型到data_utils/face_trackong/3DMM/目录

https://faces.dmi.unibas.ch/bfm/main.php?nav=1-1-0&id=details

4.运行时所需模型下载

下载这四个模型放到到用户目录xxx.cache\torch\hub\checkpoints下,如果没有这个目录,自己创建出来,如果不先下载,运行再下载的话可能会很慢或者下载不了。

https://download.pytorch.org/models/resnet18-5c106cde.pth
https://www.adrianbulat.com/downloads/python-fan/s3fd-619a316812.pth
https://www.adrianbulat.com/downloads/python-fan/2DFAN4-cd938726ad.zip
https://download.pytorch.org/models/alexnet-owt-7be5be79.pth

在这里插入图片描述
语音特征提取模型
下载deepspeech-0_1_0-b90017e8.pb放到.tensorflow\models目录

二、数据处理

1.数据准备

自己拍摄一段不大于5分钟的视频或者从网上下载不侵权的视频,视频人像单一,面对镜头,背景尽量简单,这是方便等下进行抠人像与分割人脸用的。然后视频编辑软件,只切取一部分上半身和头部的画面。按1比1切取。这里的剪切尺寸不做要求,只是1比1就可以了。
在这里插入图片描述
导出的时候,按官方要求的尺寸导出(512*512),但不一定完全按这个尺寸来,就是只要是正方形就可以,帧率是25fps。

2.获得 AU45眨眼

要获取眨眼数据,要使用OpenFace,可以直接下载OpenFace的可运行文件,然后打开OpenFace目录下的OpenFaceOffline.exe,只选择AUs这个功能就可以。
这里我把OpenFace打好包放网盘上,可以直接下载使用:链接:https://pan.baidu.com/s/15yr0aSbAmSqhOhnjd7ATwg 提取码:hxgl
在这里插入图片描述
选择要获取眨眼数据的视频:
在这里插入图片描述
运行完之后,在processed目录下就有与视频名相同的csv文件:
在这里插入图片描述

3.数据处理

在ER-NERF/data目录,创建一个与视频名同名的目录:
数据处理要花的时间跟视频长短有关,一般要1个小时以上,有两种处理方式,一种是直接一次运行所有步骤,但处理过程可能存在错误,所以建议使用第二种,按步骤来处理.

1.一次性处理数据
按自己的数据与目录来运行对应的路径
python data_utils/process.py data/anc/anc.mp4
2.分步处理

按步骤处理时,

python data_utils/process.py data/anc/anc.mp4 --task x
--task 1  #分离音频

--task 2  #生成aud_eo.npy

--task 3  #把视频拆分成图像

--task 4  #分割人像

--task 5  #提取背景图像

--task 6 #分割出身体部分

--task 7 #获取人脸landmarks lms文件 

--task 8 #获取人脸跟踪数据,这步要训练一个追踪模型,会很慢

--task 9 #保存所有数据

4.数据说明

处理完成之后,把OpenFace处理出来的眨眼数据复制到当前目录,重新命名成au.csv,把原本的aud.npy重新命名成aud_ds.npy,如果不想改数据,就改在代码里面改。
在这里插入图片描述

5.人像分割问题

当分步处理数据时,到第四步是人像分割,这个分割如果没有分割好,就会影响训练的效果,比如下面的图像,第一个下巴分割的位置不对,第二个地方是把白色的衣服错误的分割成背景了,这里可以借助别人工具进行分割,我这里​​Segment-and-Track Anything进行分割,效果会好很多,关于​Segment-and-Track Anything可以看我之前的博客:​Segment-and-Track Anything——通用智能视频分割、跟踪、编辑算法解读与源码部署
在这里插入图片描述
上面那张图像使用Segment-and-Track Anything分割出来的效果:
在这里插入图片描述
Segment-and-Track Anything分割出来的图像,要转换成模型所需要的数据格式,下面是我用来转的C++代码,可以用参考改自己的数据:

int main()
{

	for (int i = 0; i < 3418; i++)
	{
		std::string name = "";

		if (i < 10)
		{
			name = "tao6_masks/0000" + std::to_string(i) + ".png";
		}
		else if (i > 9 && i < 100)
		{
			name = "tao6_masks/000" + std::to_string(i) + ".png";
		}
		else if (i > 99 && i < 1000)
		{
			name = "tao6_masks/00" + std::to_string(i) + ".png";
		}
		else if (i > 999 && i < 10000)
		{
			name = "tao6_masks/0" + std::to_string(i) + ".png";
		}

		cv::Mat cv_src = cv::imread(name);

		cv::Mat cv_seg(cv_src.size(), CV_8UC3, cv::Scalar(255, 255, 255));
		cv::Mat cv_neck(cv_src.size(), CV_8UC3, cv::Scalar(0, 0, 0));
		cv::Mat cv_body = cv_neck.clone();
		cv::Mat cv_face = cv_neck.clone();

		for (int i = 0; i < cv_src.rows; i++)
		{
			for (int j = 0; j < cv_src.cols; j++)
			{
				if (cv_src.at<cv::Vec3b>(i, j)[2] == 140 &&
					cv_src.at<cv::Vec3b>(i, j)[1] == 238 &&
					cv_src.at<cv::Vec3b>(i, j)[0] == 157)
				{
					cv_neck.at<cv::Vec3b>(i, j)[2] = 0;
					cv_neck.at<cv::Vec3b>(i, j)[1] = 255;
					cv_neck.at<cv::Vec3b>(i, j)[0] = 0;
				}
			}
		}

		cv::Mat element_n = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(7, 7));
		morphologyEx(cv_neck, cv_neck, cv::MORPH_DILATE, element_n);   //结果保存到自身

		for (int i = 0; i < cv_neck.rows; i++)
		{
			for (int j = 0; j < cv_neck.cols; j++)
			{
				if (cv_neck.at<cv::Vec3b>(i, j)[2] == 0 &&
					cv_neck.at<cv::Vec3b>(i, j)[1] == 255 &&
					cv_neck.at<cv::Vec3b>(i, j)[0] == 0)
				{
					cv_seg.at<cv::Vec3b>(i, j)[2] = 0;
					cv_seg.at<cv::Vec3b>(i, j)[1] = 255;
					cv_seg.at<cv::Vec3b>(i, j)[0] = 0;
				}
			}
		}

		for (int i = 0; i < cv_src.rows; i++)
		{
			for (int j = 0; j < cv_src.cols; j++) {
				if (cv_src.at<cv::Vec3b>(i, j)[2] == 152 &&
					cv_src.at<cv::Vec3b>(i, j)[1] == 212 &&
					cv_src.at<cv::Vec3b>(i, j)[0] == 77)
				{
					cv_body.at<cv::Vec3b>(i, j)[2] = 255;
					cv_body.at<cv::Vec3b>(i, j)[1] = 0;
					cv_body.at<cv::Vec3b>(i, j)[0] = 0;
				}
			}
		}

		cv::Mat element_b = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(7, 7));
		cv::morphologyEx(cv_body, cv_body, cv::MORPH_DILATE, element_b);
		cv::morphologyEx(cv_body, cv_body, cv::MORPH_OPEN, element_b);

		for (int i = 0; i < cv_body.rows; i++)
		{
			for (int j = 0; j < cv_body.cols; j++) {
				if (cv_body.at<cv::Vec3b>(i, j)[2] == 255 &&
					cv_body.at<cv::Vec3b>(i, j)[1] == 0 &&
					cv_body.at<cv::Vec3b>(i, j)[0] == 0)
				{
					cv_seg.at<cv::Vec3b>(i, j)[2] = 255;
					cv_seg.at<cv::Vec3b>(i, j)[1] = 0;
					cv_seg.at<cv::Vec3b>(i, j)[0] = 0;
				}
			}
		}

		for (int i = 0; i < cv_src.rows; i++)
		{
			for (int j = 0; j < cv_src.cols; j++)
			{
				if (cv_src.at<cv::Vec3b>(i, j)[2] == 251 &&
					cv_src.at<cv::Vec3b>(i, j)[1] == 231 &&
					cv_src.at<cv::Vec3b>(i, j)[0] == 252)
				{
					cv_face.at<cv::Vec3b>(i, j)[2] = 0;
					cv_face.at<cv::Vec3b>(i, j)[1] = 0;
					cv_face.at<cv::Vec3b>(i, j)[0] = 255;
				}
			}
		}
		cv::Mat element_f = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
		cv::morphologyEx(cv_face, cv_face, cv::MORPH_DILATE, element_f);
		cv::morphologyEx(cv_face, cv_face, cv::MORPH_OPEN, element_b);

		for (int i = 0; i < cv_face.rows; i++)
		{
			for (int j = 0; j < cv_face.cols; j++)
			{
				if (cv_face.at<cv::Vec3b>(i, j)[2] == 0 &&
					cv_face.at<cv::Vec3b>(i, j)[1] == 0 &&
					cv_face.at<cv::Vec3b>(i, j)[0] == 255)
				{
					cv_seg.at<cv::Vec3b>(i, j)[2] = 0;
					cv_seg.at<cv::Vec3b>(i, j)[1] = 0;
					cv_seg.at<cv::Vec3b>(i, j)[0] = 255;
				}
			}
		}

		cv::imwrite("mask/" + std::to_string(i) + ".png", cv_seg);
		/*cv::imshow("src", cv_neck);
		cv::imshow("seg", cv_seg);
		cv::waitKey();*/
	}
}

三、模型训练

1.头部训练

python main.py data/vrh/ --workspace trial_vrh/ -O --iters 100000

这步训练完成之后,
在这里插入图片描述

2.微调嘴型动作

python main.py data/vrh/ --workspace trial_vrh/ -O --iters 125000 --finetune_lips --patch_size 32

运行完成之后,会保存最后的一个模型,这个模型下一步训练身体时用到:
在这里插入图片描述

3.训练身体

训练身体时,导入上一步生成的头部模型,模型路径和名称按项目按自己环境生成的结果来写:

python main.py data/vrh/ --workspace trial_vrh_torso/ -O --torso --head_ckpt trial_vrh/ngp_ep0041.pth --iters 200000

下面的模型就是我们最终需要的模型:
在这里插入图片描述

四、测试项目

1.测试

python main.py data/vrh/ --workspace trial_vrh/ -O --test 
python main.py data/vrh/ --workspace trial_vrh_torso/ -O --torso --test 

2.使用音频进行推理

这里要提取音频的特征才能进行推理,提取特征可以参考数据处理第二步:

python main.py data/vrh/ --workspace trial_vrh_torso/ -O --torso --test --test_train --aud <audio>.npy

五、常见错误

pytorch4d安装时常见错误

subprocess.CalledProcessError: Command ‘[‘ninja’, ‘-v’]’ returned non-zero exit status 1.

出现这个错误时,找到dist-packages/torch/utils/cpp_extension.py这个文件,找到command = [‘ninja’, ‘-v’],改成 command = [‘ninja’, ‘–version’],改了效果如下:
在这里插入图片描述

   # command = ['ninja', '-v']
    command = ['ninja', '--version']

AttributeError: ‘Upsample’ object has no attribute ‘recompute_scale_factor’

把cuda改成11.7就可以解决这个错误。

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

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

相关文章

开源任务调度框架

本文主要介绍一下任务调度框架Flowjob的整体结构&#xff0c;以及整体的心路历程。 功能介绍 flowjob主要用于搭建统一的任务调度平台&#xff0c;方便各个业务方进行接入使用。 项目在设计的时候&#xff0c;考虑了扩展性、稳定性、伸缩性等相关问题&#xff0c;可以作为公司…

UART中的奇偶校验和粘性奇偶校验(stick parity)

1.UART传输 UART传输分为起始位&#xff0c;数据位&#xff0c;奇偶校验位&#xff0c;停止位&#xff0c;我们这里详细介绍一下奇偶校验位。 2.奇偶校验位 奇偶校验是一种常见的校验位方法&#xff0c;用于检测数据传输中的错误。 奇校验&#xff1a;数据位的1的个数加上附加…

TensorFlow入门(十八、激活函数)

激活函数是什么? 单个神经元的网络模型: 用计算公式表达如下: 即在神经元中,输入的x通过与权重w相乘,与偏置量b求和后,还被作用了一个函数,这个函数就是激活函数。 激活函数的作用 如果没有激活函数,整个神经元模型就是一个简单的线性方程。而在现实生活中,线性方程能解决的事…

常见Http请求形式

一、请求参数的类型 我们在做boot项目时&#xff0c;常常会向接口发起请求&#xff0c;有些请求需要附带一些参数&#xff0c;比如说分页查询&#xff0c;就需要带上pageNum(当前页)和pageSize(页面大小)等参数 有两种方式可以传递这样的参数 query类型&#xff0c;参数通过…

【Linux系统KVM虚拟机实战】LVM逻辑卷之磁盘扩容

【Linux系统KVM虚拟机实战】LVM逻辑卷之磁盘扩容 一、LVM与KVM介绍1.1 LVM介绍1.2 KVM介绍1.2.1 KVM简介1.2.2 KVM优点二、本次实践介绍2.1 本次实践简介2.2 环境规划三、虚拟机环境检查3.1 检查KVM虚拟机磁盘空间3.2 KVM虚拟机检查系统情况3.3 检查物理磁盘分区3.4 查看PV状态…

②. GPT错误:图片尺寸写入excel权限错误

꧂问题最初 ꧁ input输入图片路径 print图片尺寸 大小 长宽高 有颜色占比>0.001的按照大小排序将打印信息存储excel表格文件名 表格路径 图片大小 尺寸 颜色类型 占比信息input输入的是文件就处理文件 是文件夹&#x1f4c1;就处理文件。路径下的图片 1. 是处理本路径图片 …

数据结构-----哈夫曼树和哈夫曼编码

目录 前言 哈夫曼树的实现 1.储存结构 2.创建初始化哈夫曼树 3.完整哈夫曼树创建 4.计算总权值 哈夫曼编码 完整代码 前言 前面我们学习过了哈夫曼树和哈夫曼编码的基础知识&#xff08;链接&#xff1a;哈夫曼树哈夫曼编码必知必会知识_Gretel Tade的博客-CSDN博客&…

内排序算法

排序算法是面试中常见的问题&#xff0c;不同算法的时间复杂度、稳定性和适用场景各不相同。按照数据量和存储方式可以将排序算法分为 内排序&#xff08;Internal Sorting&#xff09;和 外排序&#xff08;External Sorting&#xff09;。 内排序是指对所有待排序的数据都可…

wifi管理软件 WiFi Signal mac中文介绍

WiFi Signal mac是一款WiFi信号强度监测工具&#xff0c;它可以帮助用户实时监测WiFi信号的强度、频率、噪声等信息&#xff0c;并提供详细的图表和统计数据。 WiFi Signal可以自动扫描附近的WiFi网络&#xff0c;并显示它们的信号强度和频率。用户可以通过WiFi Signal来找到最…

Android斩首行动——应用层开发Framework必知必会

前言 相信做应用层业务开发的同学&#xff0c;都跟我一样&#xff0c;对Framework”深恶痛绝“。确实如此&#xff0c;如果平日里都在做应用层的开发&#xff0c;那么基本上我们很少会去碰Framework的知识。但生活所迫&#xff0c;面试总是逃不过这一关的&#xff0c;所以作为…

第二证券:A股公司首批三季报出炉 柏楚电子、平煤股份业绩一增一减

10月10日晚&#xff0c;柏楚电子、平煤股份拉开了A股公司三季报发表序幕。来自激光切开控制体系赛道的柏楚电子&#xff0c;前三季度营收、净利润均完结较大崎岖增加&#xff1b;焦煤龙头企业平煤股份&#xff0c;受煤价跌落连累成果&#xff0c;前三季度营收、净利润均有所下降…

Java架构师缓存性能优化

目录 1 缓存的负载策略2 缓存的序列化问题3 缓存命中率低4 缓存对数据库高并发访问5 缓存数据刷新的策略6 何时写缓存7 批量数据来更新缓存8 缓存数据过期的策略9 缓存数据如何恢复10 缓存数据如何迁移11 缓存冷启动和缓存预热1 缓存的负载策略 如果说我们在缓存架构设计当中啊…

优思学院|八大浪费深度剖析

在工作流程中消除浪费是精益思想的目标。在深入探讨八大浪费之前&#xff0c;了解浪费的定义至关重要。浪费是指工作流程中的任何行动或步骤&#xff0c;这些行动或步骤不为客户增加价值。换句话说&#xff0c;浪费是客户不愿意为其付费的任何过程。 最初的七大浪费&#xff0…

第83步 时间序列建模实战:Catboost回归建模

基于WIN10的64位系统演示 一、写在前面 这一期&#xff0c;我们介绍Catboost回归。 同样&#xff0c;这里使用这个数据&#xff1a; 《PLoS One》2015年一篇题目为《Comparison of Two Hybrid Models for Forecasting the Incidence of Hemorrhagic Fever with Renal Syndr…

Nerf 学习笔记

Nerf 学习笔记 Step 1&#xff1a;相机 Rays 行进(ray marching)Step 2&#xff1a;收集查询点Step 3&#xff1a;将查询点投射到高维空间(位置编码)Step 4&#xff1a;神经网络推理和体渲染神经网络推理体渲染计算损失 Reference: 搞懂神经辐射场 Neural Radiance Fields (Ne…

如何在一个传统的html中,引入vueJs并使用vue复制组件?

如何在一个传统的html中&#xff0c;引入vueJs并使用vue复制组件&#xff1f; 1.1 引言1.2 背景1.3 解决方案1.3.1 解决方案一&#xff1a;直接使用clipboard(不推荐仅供参考学习)1.3.2 解决方案二&#xff1a;封装指令js库后使用 (推荐) 1.1 引言 这篇博文主要分享如何在一个…

Springboot给每个接口设置traceId,并添加到返回结果中

原理 slf4j有个MDC的类&#xff0c;是ThreadLocal的实现&#xff0c;保存在这里的变量会绑定到某个请求线程&#xff0c;于是在该请求的线程里的日志代码都可以使用设入的变量。 实现 一、引入依赖 这个是可选项&#xff0c;用于生成唯一uid&#xff0c;我人懒&#xff0c…

一文带你了解 Linux 的 Cache 与 Buffer

目录 前言一、Cache二、Buffer三、Linux 系统中的 Cache 与 Buffer总结 前言 内存的作用是什么&#xff1f;简单的理解&#xff0c;内存的存在是为了解决高速传输设备与低速传输设备之间数据传输速度不和谐而设立的中间层&#xff08;学过计算机网络的应该都知道&#xff0c;这…

【实战】kubeadmin安装kubernetes集群

文章目录 前言服务器介绍准备工作设置服务器静态ip修改host关闭防火墙和swap修改所需的内核参数 部署步骤安装containerd安装cri工具&#xff08;效果等同于docker&#xff09; 安装kubernetes集群安装网络插件flannel安装可视化面板kuboard&#xff08;可选&#xff09; 下期预…

42. QT中开发Android配置QFtp功能时遇到的编译问题

1. 说明 此问题仅适用在QT中开发Android程序时&#xff0c;需要适用QFtp功能的情况。一般情况下&#xff0c;如果开发的是Windows或者Linux系统下的程序&#xff0c;可能不会出现该问题。 2. 问题 【Android】在将QFtp的相关代码文件加入到项目中后&#xff0c;编译项目时会…