【数字人】2、MODA | 基于人脸关键点的语音驱动单张图数字人生成(ICCV2023)

2023-10-08 14:15

本文主要是介绍【数字人】2、MODA | 基于人脸关键点的语音驱动单张图数字人生成(ICCV2023),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

文章目录

    • 一、背景
    • 二、方法
      • 2.1 问题描述和数据预处理
      • 2.2 Mapping-Once network with Dual Attentions
      • 2.3 Facial Composer Network
      • 2.4 使用 TPE 来合成人像图片
    • 三、效果
      • 3.1 训练细节
      • 3.2 数据
      • 3.3 测评指标
      • 3.4 结果比较
    • 四、代码
      • 4.1 数据前处理
      • 4.2 训练
      • 4.3 推理

论文:MODA: Mapping-Once Audio-driven Portrait Animation with Dual Attentions

代码:https://tinyurl.com/iccv23-moda

出处:ICCV2023

贡献:

  • 提出了一个 unifided MODA 网络,能够经过一次映射来同时获得确定的唇部动作和不确定的其他面部动作
  • 是一种基于密集关键点的方法,能够同时驱动嘴、眼、头、肩的运动,更自然

和典型方法的对比:

  • Wav2Lip(MM2020) :下半张脸被模糊了(Wav2Lip-GFPGAN 使用两个模型提升输出结果的分辨率)
  • PC-AVS(CVPR2021) :基本都是正脸的图,头部变化不够多样
  • MakeItTalk(SIGGRAPH 2020):由于使用的 2D warping 所以脸部会扭曲。
  • Audio2Head(IJCAI2021):只会产生正脸的图,且由于使用的 2D warping 所以脸部会扭曲
  • SadTalker(CVPR2023):唇部同步性较好,唇部较清晰,头部运动较丰富,牙齿不够清晰,没有考虑除唇部动作和眨眼外的其他面部表情,表情比较固定
  • MODA(ICCV2023):使用一个模型(双分支)来学习确定性的【唇部】、不确定性的【眼部+面部+head+身体】的关键点,理论上能让动作看起来更自然,能保留更多的面部细节。

一、背景

在这里插入图片描述

talking head 是通过一个给定的语音信号来驱动图片,从而合成一个和语音同频的说话的视频

之前的方法 [7,29,52] 都是学习语音和图片帧之间的关系,且一般会忽略 head pose(因为他们认为 head pose 难以和面部动作分开)。

很多 3D 面部重建的方法和基于 GAN 的方法一般会估计一个中间表达(3D face shape、2D landmark、face expression parameters 等)来帮助生成

但是,这些稀疏的表达会丢失很多面部细节,导致过平滑(over-smooth)

NeRF[10,44] 以其高保真结果也受到了很多关注,但是其难以控制

虽然前面提到了这么多方法,但是生成一个真实且表情丰富的 talking vedio 仍然很难,因为人们对合成的 vedio 很敏感,所以要达到可用的效果要达到很高的标准

主要要考虑的问题如下:

  • 正确性:合成的 vedio 要和驱动的 audio 高度一致
  • 高视觉质量:合成的 vedio 要有高分辨率且包含很多细节信息
  • 多样性:说话时主要是嘴唇需要很好的和声音同步,而眨眼和头部动作时不确定的,但也需要和正常人说话的动作类似

为了实现上面三个目标,之前的方法有的将 mouth landmark 和 head pose 分开学习,使用不同的 sub-network [22,50],还有的方法只对 mouth 运动建模,head pose 是从其他 vedio 中拿来的[29,52]。但是这样 lip-sync 和其他运动会缺少关联,导致不确定的结果。

本文中,作者提出了 MODA,mapping-once network with dual attentions,是一个统一的结构来生成不同的表达,简化了步骤。

  • 为了将唇部动作和其他动作结合起来,作者设计了一个 dual-attention module 来分别学习确定性的映射(确定的 mouth movement)和概率采样(the diverse head pose/eye blinking from time-totime)。
  • transformer-based dual attention module:生成准确且多样性的表示特征
  • facial composer network:得到更准确和细节的面部 landmark
  • tenporally guided renderer:合成 vedio

二、方法

在这里插入图片描述

整体框架如图 2 所示,本文方法主要是为了生成高保真 talking head,且具有确定的 lip motion 和其他的 multi-modal motion(head pose、eye blinking、torso movements)

共包含 3 个部分:

  • 首先,给定 driven audio 和 conditioned subjects,MODA 会生成多模态和正确的语义人像部件
  • 然后,面部合成网络会将 ficial component 结合起来,并添加一些细节面部细节
  • 最后,使用具有时间位置嵌入(temporally positional embedding, TPE)的人像渲染器来合成高保真且稳定的视频

2.1 问题描述和数据预处理

给定一个长度为 t 的音频序列 A = { a 0 , a 1 , . . . , a T } A=\{a_0, a_1,...,a_T\} A={a0,a1,...,aT},其音频采样率为 r

本文的 talking portrait (说话人像)方法主要的面部是将这个音频映射到对应的视频 video clip 中, V = { I 0 , I 1 , . . . , I K } V=\{I_0,I_1,...,I_K\} V={I0,I1,...,IK},且 FPS 为 f, K = ⌊ f T / r ⌋ K=\lfloor{fT/r}\rfloor K=fT/r

由于 V 远远大于 A,很多方法提出逐步生成 V,并且引入很多中间表达 R,为了让 V 看起来更自然,那么多 R 的约束就自然很重要了

在之前的 audio-driven face 生成任务中,R 一般都是一种 face information(如 facial landmark、head pose)

为了更好的表达说话人像,本文作者定义 R 是多种不同的人像描述, R = P M , P E , P F , H , P T R=P^M, P_E, P^F, H, P^T R=PM,PE,PF,H,PT

  • 嘴部关键点 P M P^M PM:40 个
  • 眼部关键点 P E P^E PE:60 个,包括眼睛和眉毛的关键点,控制眨眼
  • 面部关键点 P F P^F PF:478 个,是密集的面部 3D 关键点,用于控制面部表情细节
  • 头部动作 H H H:6 个,head rotation ( θ , ϕ , ψ ) (\theta, \phi, \psi) (θ,ϕ,ψ),head transpose ( x , y , z ) (x,y,z) (x,y,z)
  • 躯体动作 Torso points P T P^T PT:18 个,每个肩膀 9 个

所以,整个 talking portrait 可以被写为 A→R→V,作者也是分别设计了对应的网络来实现对应的过程


在这里插入图片描述

数据预处理:关键点提取

  • 使用 Mediapipe 抽取 478 个 3D facial keypoints
  • 使用 WHENet 估计 head pose
  • 使用 BiseNet 分割,然后抽取出肩部关键点

2.2 Mapping-Once network with Dual Attentions

在这里插入图片描述

Mapping-once 结构:如图 3 所示

  • 给定 driven audio A 和 subject condition S,MODA 的作用是使用一次前向过程来将其投影到 R 中(lip movement, eye blinking, head pose, and torso)
  • 第一步:分别使用两个 encoder 来编码 audio feature 和抽取 subject style
  • 第二步:使用一个 dual-attention module 来生成多样且确定的 motion feature
  • 第三步:分别使用 4 个 decoder 来得到对应的关键点

audio 特征处理:

  • audio feature 抽取:首先使用 Wav2Vec[30] 来抽取语音上下文信息,然后使用 MLP 映射到 s a ∈ R d × T s_a \in R^{d \times T} saRd×T,d 是一帧数据的特征维度,T 是待生成的 vedio 的 frame 的个数

  • 为了建模不同说话风格,作者使用 conditioned subject 的 facial vertices 作为输入,然后将这些 vertices 映射到 d 维向量 v s v_s vs 中作为 subject style code,这里的映射也是使用 MLP 来实现的,然后对 s a s_a sa v s v_s vs 进行结合,得到结合后的特征 s s s

    在这里插入图片描述

  • dual-attention module 的输入是 s s s s a s_a sa,输出是时序上下文 s t s_t st

    在这里插入图片描述

  • 然后,使用 4 个 MLP 来解码不同的关键点

    在这里插入图片描述

Dual-attention module:

  • specific attention branch:SpecAttn
  • probabilistic attention branch:ProbAttn

由于 talking portrait 生成任务需要从有限的驱动信息中生成多模态的输出,所以该任务具有很大的不确定性

本文方法提出的 dual-attention 模型,将这个任务解耦成了下面两个任务:

  • specific mapping :得到时序对齐的确定的 audio 和 lip movement 特征
  • probabilistic mapping:得到时序关联的概率 audio 和 other movements 特征
  • 作者使用两个子模块来分别学习不同的特征,然后使用 time-wise concatenation 来聚合这两种特征

dual-attention 的两个分支:

  • SpecAttn 分支
  • ProbAttn 分支

1、SpecAttn 分支:specific attention branch,用于捕捉 s s s 和 audio feature s a s_a sa 的实时对齐的 attention s s a s_{sa} ssa,根据 FaceFormer,本文的 SpecAttn 格式如下:

在这里插入图片描述

  • d d d s a s_a sa 的维度

  • alignment bias M A M_A MA 如下:

    在这里插入图片描述

不同于 FaceFormer 中只在自回归中使用了 cross-attention,本文在整个序列中都使用了 cross-attention,计算速度提升了 Tx

为了捕捉更丰富的时序信息,作者还在 s s s 上使用了 periodic positional encoding (PPE) 和 biased casual self-attention:

在这里插入图片描述

M T M_T MT 是一个上三角区为负无穷的矩阵,这是为了避免看到未来的帧来进行当前帧的预测

在这里插入图片描述

  • q q q 是控制序列周期的超参数
  • 这样做能够让 encoded feature s’ 包含更丰富的 空间-时序 信息,能够生成的更准确

2、ProbAttn 分支

为了生成更逼真的结果且避免过平滑,学习声音特征和人像动作之间的概率映射很重要,VAE[17] 能够建模概率生成并且在时序的生成任务上表现的比较好

所以,基于 advanced transformer Variational Autoencoder (t-VAE),本文设计了 probabilistic attention branch 来生成更多样的结果

给定特征表达 s s s,probabilistic attention 的目标是生成更多样的特征 s p a s_{pa} spa

  • 首先,将 s s s 送入 encoder(Enc),然后学习 μ \mu μ θ \theta θ 来建模 s s s
  • 然后,使用 decoder(Dec)通过 resample 来生成 multimodal 输出

在这里插入图片描述

  • Φ \Phi Φ:是 MLP
  • U ( μ , θ ) U(\mu, \theta) U(μ,θ):是高斯分布

为了让 ProbAttn 能够学习更丰富的风格,使用 KL 散度 loss 来约束 t-VAE 的特征:

在这里插入图片描述

  • d l d_l dl:是 μ \mu μ 的维度

3、整合两个 attention 的输出

在这里插入图片描述


Loss 函数:

MODA 有四个 decoder,分别生成不同部位的运动系数

所以作者使用了 multi-task 学习机制,通过最小化对应的 L 1 L_1 L1 距离来实现:

在这里插入图片描述

加上 KL loss:

在这里插入图片描述


2.3 Facial Composer Network

在这里插入图片描述

Facial composer network (FaCo-Net)的输入是 subject information S S S 、mouth point P M P^M PM 、eye point P E P^E PE

FaCo-Net 的目标:合成更精细的面部 landmark P F P^F PF

在这里插入图片描述
FaCo-Net 的结构:

  • 3 个 encoder 对 3 种特征分别编码
    • subject encoder:将 facial point S S S 映射到 style code p f p_f pf
    • P M P^M PM encoder:将 P M P^M PM 映射到和 p f p_f pf 同一空间的 p m p_m pm
    • P E P^E PE encoder:将 P E P^E PE 映射到和 p f p_f pf 同一空间的 p e p_e pe
  • 1 个 decoder 生成面部 landmark
    • 在这里插入图片描述
      Faco-Net 的作用是生成器:生成 “看起来逼真” 的 facial dense point

生成器的 loss 如下:

在这里插入图片描述

  • L G A N L_{GAN} LGAN 是 adversarial loss, z ˆ = D ( P F ) \^{z}=D(P^F) zˆ=D(PF)
    在这里插入图片描述
  • λ \lambda λ:10

判别器 D:使用 GAN 作为判别器的 backbone 来判断是真实的 facial points 还是生成的 facial points

用于优化判别器 D 的 adversarial Loss:LSGAN loss

在这里插入图片描述

  • z z z:输入为 gt face points 时,判别器的输出
  • z ˆ \^{z} zˆ:输入为 生成的 face points 时,判别器的输出

生成 facial landmarks P F P^F PF 后, P F P^F PF 会根据 head pose 来变换到 camera coordinate

torso points 和 变换后的 facial landmark 会映射到 image space 来进行写实的渲染

2.4 使用 TPE 来合成人像图片

在这里插入图片描述

最后就是要将前面得到的输出来渲染出人像,如图 2

作者使用 U-Net-like 的带 TPE 的渲染器 G R G_R GR 来生成高保真且稳定的视频

TPE :

在这里插入图片描述

然后使用 G R G_R GR来渲染 t-frame 的结果 I t I_t It

  • I t c I_t^c Itc:是 frame index t 时的 condition image
  • I r I_r Ir:是 reference image

三、效果

3.1 训练细节

训练细节:

  • 超参数 ( β 1 , β 2 ) = ( 0.9 , 0.99 ) (\beta_1, \beta_2)=(0.9,0.99) (β1,β2)=(0.9,0.99)
  • 学习率:10^-4
  • 单卡 3090:三个部分分别需要 (30, 2, 6) 小时,(200,300,100)epoch,(32,32,4) batch
  • 测试时,选择最小的验证 loss 的模型
  • 使用滑动窗口来处理任意长度的视频(window size 300,stirde 150)

3.2 数据

作者使用的 HDTF 和 LSP 数据,video 的平均长度为 1-5 分钟,并且作者将其处理成了 25 fps

作者随机选择 80% 的视频作为训练集,其他的作为测试集,也就是有 132 个训练视频,32 个测试视频

所有视频以人脸为中心,被 resize 成 512x512 大小

数据预处理:

  • 首先,使用 Mediapipe 对所有视频提取 478 个 3D facial landmarks
  • 然后,使用开源方法估计 head pose H,且根据 head pose,将上面的 3D facial landmarks 投影到 canonical space
  • 接着,使用 face parsing 方法来根据分割结果估计出 torso 的 boundary

3.3 测评指标

  • LMD:mouth landmark distance,衡量生成的视频的唇部正确性
  • LMD-v:velocity of mouth landmark distance,衡量生成的视频的唇部正确性
  • MA:衡量预测的 mouth area 和真实的 mouth area 的 IoU
  • confidence score from SyncNet:衡量 audio-video 的合成
  • Natural Image Quality Evaluator (NIQE) :衡量图像的质量,能够捕捉图像的细节

3.4 结果比较

和 SOTA 结果的定量比较:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

User Study:

在这里插入图片描述

消融实验:

dual-attention 的消融实验效果:

  • 使用 LSTM 代替 dual-attention,LSTM 无法获得 multimodal 的结果,且 diverse score 降低到了 0
  • 移除 specific attention branch,移除后,MODA 生成的唇部运动结果过平滑
  • 移除两个 attention branch

在这里插入图片描述

FaCo-Net 消融实验:该模型的目标是为了为渲染器生成自然且连续的表达特征

作者通过移除该模块,直接使用 facial dense landmark 来代替 eye landmark 和 mouth landmark,如图 6a 展示了没有 FaCo-Net 的结果,唇部区域联系不太正常,且丢失了一些脸部细节

在这里插入图片描述

在这里插入图片描述

TPE 消融实验

作者使用时序一致性衡量方式来衡量 frame-wise consistency(TCM),

在这里插入图片描述

  • O t O_t Ot 表示 reference video(O)第 t 帧
  • V t V_t Vt 表示 generated video (V) 第 t 帧
  • warp(.) 表示使用 optical flow 的 warping function
  • 图 6b 展示了 with/without TPE 的对比效果,可以看出使用 TPE 能够让输出视频更稳定

在这里插入图片描述

在这里插入图片描述


本文方法的限制:

  • 不能很好的泛化到不同的目标人物或 out-of-domain audio
  • 对于新的人物要重新训练渲染部分的模型

单卡 3090 训练时间和测试时间对比:

在这里插入图片描述

四、代码

4.1 数据前处理

git clone https://github.com/DreamtaleCore/MODA.git

1、装环境

我按照官方给出的方法没有装成功,是一步步按 conda 的命令装的

2、下载 HDTF 数据

这里目前只找到了 HDTF 的数据:

有下载 HDTF 工具的 github 路径:https://github.com/universome/HDTF

  • 下载方式:python download.py --output_dir /path/to/output/dir --num_workers 8
  • 注意:要科学上网,需要安装 ffmpeg、youtubu-dl,否则会报错,报错原因可以去下载路径下的 log 中去看
  • 注意:将 download.py 的第 168 行修改成 video_selection = f"best[ext={video_format}]",才能保证下载的视频有声音,否则下载的视频没有声音

3、处理数据

处理数据在 MODA/data_prepare/ 目录下:

第一步:先编译 3DDFA-V2 的环境:

cd 3DDFA-V2
bash build.sh
cd ..

我用 MODA 自带下来的 3DDFA-V2 无法 build,自己重新 clone 了一份 3DDFA_V2 才 build 成功

 sh ./build.sh

第二步:下载 face-parsing 的模型并上传到 face-parsing/res/cp

第三步:执行处理代码:

python process.py -i your/video/dir -o your/output/dir

报错 1 :这里 step0 第 42 行的路径没有写入权限,导致无法在程序运行中间写入,换成有权限的目录

在这里插入图片描述

报错 2:unrecognized option 'crf'

在这里插入图片描述
这常见于在使用 ffmpeg 时使用到了 libx264,但在实际的编译过程中并有指定编译 libx264 参数,默认不会编译这一部分组件,从而产生报错。

可以使用 apt 安装 ffmpeg :

sudo apt install ffmpeg    //通过 apt 安装 ffmpeg

或者如下方式解决:

conda install x264
conda install x264 ffmpeg -c conda-forge

但我都没有解决,然后我就把 -crf 参数舍弃了哈哈哈

修改 step0 中的 line 51 如下:

# cvt_wav_cmd = 'ffmpeg -i ' + vfp + f' -vf scale={args.target_h}:{args.target_w} -crf 2 ' + args.out_video_fp + ' -y' # 无法处理 crf 参数
cvt_wav_cmd = 'ffmpeg -i ' + vfp + f' -vf scale={args.target_h}:{args.target_w} '+ args.out_video_fp + ' -y' # 注意 {args.target_w} 后的空格

报错 3:no module named 'FaceBoxes'

暂且将这里改成了绝对路径,得以解决

在这里插入图片描述

报错 4:找不到 viz_pose2

因为我这里用了 3DDFA_V2 源码,源码中没有这个函数,所以我从 MODA 中重新拷了这个函数,解决了

报错 5:

Could not find a backend to open `/mnt/cpfs/dataset/tuxiangzu/Face_Group/WM/project/MODA/HDTF_PROCESS/RD_Radio11_000/video.mp4`` with iomode `r?`

在这里插入图片描述

 python -m pip install imageio[ffmpeg]python -m pip install imageio[pyav]

报错 6:

找不到 step2 中的 3DDFA-V2/config/mb1_120x120.yml,这里没发现作者写成了非下划线,改了好久才发现,我们使用的是 3DDFA_V2 是这样写的,注意修改

在这里插入图片描述

报错 7:onnxruntime.InferenceSession 报错

在这里插入图片描述

按上面的提示添加对应参数:

在这里插入图片描述
报错 8 : 找不到 config 中写的路径, No such file or directory: 'weights/mb1_120x120.pth'No such file or directory: 'configs/bfm_noneck_v3.pkl'

不知道是编译问题还是怎么的,相对路径都不起作用,暂且将 mb1_120x120.yml 中的路径都改为绝对路径

报错 9:module 'numpy' has no attribute 'long',改为 np.longlong()

numpy.long 在 numpy 1.20中被弃用,并在 numpy 1.24 中被删除,可以尝试 numpy.longlong
在这里插入图片描述

在这里插入图片描述

报错 10:AttributeError: module 'numpy' has no attribute 'int'.

在这里插入图片描述

修改为 np.int_,然后重新编译 sh ./build.sh

在这里插入图片描述

报错 11:ModuleNotFoundError: No module named 'RobustVideoMatting'

在这里插入图片描述

在这里插入图片描述

报错 12:其实是提示,但这里也最好改一下,在 step5 中 加上 n_init 这个参数:

在这里插入图片描述
在这里插入图片描述

最后就愉快的跑起来啦,我这里其实很多问题都是相对路径找不到的锅~

预估跑完 HDTF 的 167 个视频需要一两天时间,8线程

在这里插入图片描述

训练时报的错误:缺少 shoulder-billboard.npy

其实可以看到在整个数据处理过程是没有运行 step6 这个文件的,也就是没有从 shoulder.npy 生成 shoulder-billboard.npy,所以训练时候在 audio2repr_dataset.py 中是找不到这个文件的

但作者这里代码和实现逻辑有些出入,没有专门生成 shoulder.py 而是将其写入了 feature.npz 中,可以通过如下方式来调用,所以可以在 step5 后面加入 step6,将 process.py 中的 force_update=False,就是如果已有需要生成的文件时,不执行步骤,这样就能只执行 step6,不执行其他步骤了,生成对应的 shoulder-billboard.npy 就可以了。

process.py
在这里插入图片描述
step6.py

将 62 行注释,添加 64 行

在这里插入图片描述

这里下载的视频数据有些被损坏,有些没有内容,需要删除:

  • WDA_MaggieHassan_000.mp4
  • WRA_PeterKing_000.mp4

4.2 训练

首先,建立自己的 train.txt 和 val.txt

这里作者写的是随机选取的,代码里也没有写是怎么选的,所以我这里也就先随机选了一些:

import os
import random
datapath = 'MODA/assets/dataset/HDTF/HDTF_PROCESS'
dir_list = os.listdir(datapath)
val_list_num = random.sample([x for x in range(0, len(dir_list))], 32)
with open('assets/dataset/HDTF/train.txt', 'w') as f1:with open('assets/dataset/HDTF/val.txt', 'w') as f2:for i, dirs in enumerate(dir_list):if i in val_list_num:f2.write('HDTF_PROCESS/' + dirs + '\n')else:f1.write('HDTF_PROCESS/' + dirs + '\n')

得到的 txt 中放的就是这样的路径:

在这里插入图片描述

报错 1:Expected more than 1 value per channel when training, got input size [1,128]

这里的原因应该是最后一个 batch=1 了,所以这里设置丢弃最后一个就行了

MODA/dataset/__init__.py 的 self.dataloader 中的 drop_last=True 打开

在这里插入图片描述

模型结构:

model [MODAModel] was created
---------- Networks initialized -------------
[Network MODA] Total number of parameters : 96.718 M
-----------------------------------------------
---------- Networks initialized -------------
DataParallel((module): MODANet((audio_encoder): Wav2Vec2Model((feature_extractor): Wav2Vec2FeatureEncoder((conv_layers): ModuleList((0): Wav2Vec2GroupNormConvLayer((conv): Conv1d(1, 512, kernel_size=(10,), stride=(5,), bias=False)(activation): GELUActivation()(layer_norm): GroupNorm(512, 512, eps=1e-05, affine=True))(1): Wav2Vec2NoLayerNormConvLayer((conv): Conv1d(512, 512, kernel_size=(3,), stride=(2,), bias=False)(activation): GELUActivation())(2): Wav2Vec2NoLayerNormConvLayer((conv): Conv1d(512, 512, kernel_size=(3,), stride=(2,), bias=False)(activation): GELUActivation())(3): Wav2Vec2NoLayerNormConvLayer((conv): Conv1d(512, 512, kernel_size=(3,), stride=(2,), bias=False)(activation): GELUActivation())(4): Wav2Vec2NoLayerNormConvLayer((conv): Conv1d(512, 512, kernel_size=(3,), stride=(2,), bias=False)(activation): GELUActivation())(5): Wav2Vec2NoLayerNormConvLayer((conv): Conv1d(512, 512, kernel_size=(2,), stride=(2,), bias=False)(activation): GELUActivation())(6): Wav2Vec2NoLayerNormConvLayer((conv): Conv1d(512, 512, kernel_size=(2,), stride=(2,), bias=False)(activation): GELUActivation())))(feature_projection): Wav2Vec2FeatureProjection((layer_norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)(projection): Linear(in_features=512, out_features=768, bias=True)(dropout): Dropout(p=0.1, inplace=False))(encoder): Wav2Vec2Encoder((pos_conv_embed): Wav2Vec2PositionalConvEmbedding((conv): Conv1d(768, 768, kernel_size=(128,), stride=(1,), padding=(64,), groups=16)(padding): Wav2Vec2SamePadLayer()(activation): GELUActivation())(layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(dropout): Dropout(p=0.1, inplace=False)(layers): ModuleList((0): Wav2Vec2EncoderLayer((attention): Wav2Vec2Attention((k_proj): Linear(in_features=768, out_features=768, bias=True)(v_proj): Linear(in_features=768, out_features=768, bias=True)(q_proj): Linear(in_features=768, out_features=768, bias=True)(out_proj): Linear(in_features=768, out_features=768, bias=True))(dropout): Dropout(p=0.1, inplace=False)(layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(feed_forward): Wav2Vec2FeedForward((intermediate_dropout): Dropout(p=0.1, inplace=False)(intermediate_dense): Linear(in_features=768, out_features=3072, bias=True)(intermediate_act_fn): GELUActivation()(output_dense): Linear(in_features=3072, out_features=768, bias=True)(output_dropout): Dropout(p=0.1, inplace=False))(final_layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True))(1): Wav2Vec2EncoderLayer((attention): Wav2Vec2Attention((k_proj): Linear(in_features=768, out_features=768, bias=True)(v_proj): Linear(in_features=768, out_features=768, bias=True)(q_proj): Linear(in_features=768, out_features=768, bias=True)(out_proj): Linear(in_features=768, out_features=768, bias=True))(dropout): Dropout(p=0.1, inplace=False)(layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(feed_forward): Wav2Vec2FeedForward((intermediate_dropout): Dropout(p=0.1, inplace=False)(intermediate_dense): Linear(in_features=768, out_features=3072, bias=True)(intermediate_act_fn): GELUActivation()(output_dense): Linear(in_features=3072, out_features=768, bias=True)(output_dropout): Dropout(p=0.1, inplace=False))(final_layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True))(2): Wav2Vec2EncoderLayer((attention): Wav2Vec2Attention((k_proj): Linear(in_features=768, out_features=768, bias=True)(v_proj): Linear(in_features=768, out_features=768, bias=True)(q_proj): Linear(in_features=768, out_features=768, bias=True)(out_proj): Linear(in_features=768, out_features=768, bias=True))(dropout): Dropout(p=0.1, inplace=False)(layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(feed_forward): Wav2Vec2FeedForward((intermediate_dropout): Dropout(p=0.1, inplace=False)(intermediate_dense): Linear(in_features=768, out_features=3072, bias=True)(intermediate_act_fn): GELUActivation()(output_dense): Linear(in_features=3072, out_features=768, bias=True)(output_dropout): Dropout(p=0.1, inplace=False))(final_layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True))(3): Wav2Vec2EncoderLayer((attention): Wav2Vec2Attention((k_proj): Linear(in_features=768, out_features=768, bias=True)(v_proj): Linear(in_features=768, out_features=768, bias=True)(q_proj): Linear(in_features=768, out_features=768, bias=True)(out_proj): Linear(in_features=768, out_features=768, bias=True))(dropout): Dropout(p=0.1, inplace=False)(layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(feed_forward): Wav2Vec2FeedForward((intermediate_dropout): Dropout(p=0.1, inplace=False)(intermediate_dense): Linear(in_features=768, out_features=3072, bias=True)(intermediate_act_fn): GELUActivation()(output_dense): Linear(in_features=3072, out_features=768, bias=True)(output_dropout): Dropout(p=0.1, inplace=False))(final_layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True))(4): Wav2Vec2EncoderLayer((attention): Wav2Vec2Attention((k_proj): Linear(in_features=768, out_features=768, bias=True)(v_proj): Linear(in_features=768, out_features=768, bias=True)(q_proj): Linear(in_features=768, out_features=768, bias=True)(out_proj): Linear(in_features=768, out_features=768, bias=True))(dropout): Dropout(p=0.1, inplace=False)(layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(feed_forward): Wav2Vec2FeedForward((intermediate_dropout): Dropout(p=0.1, inplace=False)(intermediate_dense): Linear(in_features=768, out_features=3072, bias=True)(intermediate_act_fn): GELUActivation()(output_dense): Linear(in_features=3072, out_features=768, bias=True)(output_dropout): Dropout(p=0.1, inplace=False))(final_layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True))(5): Wav2Vec2EncoderLayer((attention): Wav2Vec2Attention((k_proj): Linear(in_features=768, out_features=768, bias=True)(v_proj): Linear(in_features=768, out_features=768, bias=True)(q_proj): Linear(in_features=768, out_features=768, bias=True)(out_proj): Linear(in_features=768, out_features=768, bias=True))(dropout): Dropout(p=0.1, inplace=False)(layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(feed_forward): Wav2Vec2FeedForward((intermediate_dropout): Dropout(p=0.1, inplace=False)(intermediate_dense): Linear(in_features=768, out_features=3072, bias=True)(intermediate_act_fn): GELUActivation()(output_dense): Linear(in_features=3072, out_features=768, bias=True)(output_dropout): Dropout(p=0.1, inplace=False))(final_layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True))(6): Wav2Vec2EncoderLayer((attention): Wav2Vec2Attention((k_proj): Linear(in_features=768, out_features=768, bias=True)(v_proj): Linear(in_features=768, out_features=768, bias=True)(q_proj): Linear(in_features=768, out_features=768, bias=True)(out_proj): Linear(in_features=768, out_features=768, bias=True))(dropout): Dropout(p=0.1, inplace=False)(layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(feed_forward): Wav2Vec2FeedForward((intermediate_dropout): Dropout(p=0.1, inplace=False)(intermediate_dense): Linear(in_features=768, out_features=3072, bias=True)(intermediate_act_fn): GELUActivation()(output_dense): Linear(in_features=3072, out_features=768, bias=True)(output_dropout): Dropout(p=0.1, inplace=False))(final_layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True))(7): Wav2Vec2EncoderLayer((attention): Wav2Vec2Attention((k_proj): Linear(in_features=768, out_features=768, bias=True)(v_proj): Linear(in_features=768, out_features=768, bias=True)(q_proj): Linear(in_features=768, out_features=768, bias=True)(out_proj): Linear(in_features=768, out_features=768, bias=True))(dropout): Dropout(p=0.1, inplace=False)(layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(feed_forward): Wav2Vec2FeedForward((intermediate_dropout): Dropout(p=0.1, inplace=False)(intermediate_dense): Linear(in_features=768, out_features=3072, bias=True)(intermediate_act_fn): GELUActivation()(output_dense): Linear(in_features=3072, out_features=768, bias=True)(output_dropout): Dropout(p=0.1, inplace=False))(final_layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True))(8): Wav2Vec2EncoderLayer((attention): Wav2Vec2Attention((k_proj): Linear(in_features=768, out_features=768, bias=True)(v_proj): Linear(in_features=768, out_features=768, bias=True)(q_proj): Linear(in_features=768, out_features=768, bias=True)(out_proj): Linear(in_features=768, out_features=768, bias=True))(dropout): Dropout(p=0.1, inplace=False)(layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(feed_forward): Wav2Vec2FeedForward((intermediate_dropout): Dropout(p=0.1, inplace=False)(intermediate_dense): Linear(in_features=768, out_features=3072, bias=True)(intermediate_act_fn): GELUActivation()(output_dense): Linear(in_features=3072, out_features=768, bias=True)(output_dropout): Dropout(p=0.1, inplace=False))(final_layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True))(9): Wav2Vec2EncoderLayer((attention): Wav2Vec2Attention((k_proj): Linear(in_features=768, out_features=768, bias=True)(v_proj): Linear(in_features=768, out_features=768, bias=True)(q_proj): Linear(in_features=768, out_features=768, bias=True)(out_proj): Linear(in_features=768, out_features=768, bias=True))(dropout): Dropout(p=0.1, inplace=False)(layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(feed_forward): Wav2Vec2FeedForward((intermediate_dropout): Dropout(p=0.1, inplace=False)(intermediate_dense): Linear(in_features=768, out_features=3072, bias=True)(intermediate_act_fn): GELUActivation()(output_dense): Linear(in_features=3072, out_features=768, bias=True)(output_dropout): Dropout(p=0.1, inplace=False))(final_layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True))(10): Wav2Vec2EncoderLayer((attention): Wav2Vec2Attention((k_proj): Linear(in_features=768, out_features=768, bias=True)(v_proj): Linear(in_features=768, out_features=768, bias=True)(q_proj): Linear(in_features=768, out_features=768, bias=True)(out_proj): Linear(in_features=768, out_features=768, bias=True))(dropout): Dropout(p=0.1, inplace=False)(layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(feed_forward): Wav2Vec2FeedForward((intermediate_dropout): Dropout(p=0.1, inplace=False)(intermediate_dense): Linear(in_features=768, out_features=3072, bias=True)(intermediate_act_fn): GELUActivation()(output_dense): Linear(in_features=3072, out_features=768, bias=True)(output_dropout): Dropout(p=0.1, inplace=False))(final_layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True))(11): Wav2Vec2EncoderLayer((attention): Wav2Vec2Attention((k_proj): Linear(in_features=768, out_features=768, bias=True)(v_proj): Linear(in_features=768, out_features=768, bias=True)(q_proj): Linear(in_features=768, out_features=768, bias=True)(out_proj): Linear(in_features=768, out_features=768, bias=True))(dropout): Dropout(p=0.1, inplace=False)(layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)(feed_forward): Wav2Vec2FeedForward((intermediate_dropout): Dropout(p=0.1, inplace=False)(intermediate_dense): Linear(in_features=768, out_features=3072, bias=True)(intermediate_act_fn): GELUActivation()(output_dense): Linear(in_features=3072, out_features=768, bias=True)(output_dropout): Dropout(p=0.1, inplace=False))(final_layer_norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)))))(audio_encoder_head): MLP((layers): Sequential((0): Linear(in_features=768, out_features=128, bias=True)(1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(2): LeakyReLU(negative_slope=0.2)(3): Linear(in_features=128, out_features=128, bias=True)))(subject_encoder_head): MLP((layers): Sequential((0): Linear(in_features=1434, out_features=128, bias=True)(1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(2): LeakyReLU(negative_slope=0.2)(3): Linear(in_features=128, out_features=128, bias=True)(4): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(5): LeakyReLU(negative_slope=0.2)(6): Linear(in_features=128, out_features=128, bias=True)))(temporal_body): DualTemporalMoudleV2((short_layer): TemporalAlignedBlock((decoder): TransformerDecoder((layers): ModuleList((0): TransformerDecoderLayer((self_attn): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=128, out_features=128, bias=True))(multihead_attn): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=128, out_features=128, bias=True))(linear1): Linear(in_features=128, out_features=128, bias=True)(dropout): Dropout(p=0.1, inplace=False)(linear2): Linear(in_features=128, out_features=128, bias=True)(norm1): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(norm2): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(norm3): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(dropout1): Dropout(p=0.1, inplace=False)(dropout2): Dropout(p=0.1, inplace=False)(dropout3): Dropout(p=0.1, inplace=False))(1): TransformerDecoderLayer((self_attn): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=128, out_features=128, bias=True))(multihead_attn): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=128, out_features=128, bias=True))(linear1): Linear(in_features=128, out_features=128, bias=True)(dropout): Dropout(p=0.1, inplace=False)(linear2): Linear(in_features=128, out_features=128, bias=True)(norm1): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(norm2): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(norm3): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(dropout1): Dropout(p=0.1, inplace=False)(dropout2): Dropout(p=0.1, inplace=False)(dropout3): Dropout(p=0.1, inplace=False))(2): TransformerDecoderLayer((self_attn): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=128, out_features=128, bias=True))(multihead_attn): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=128, out_features=128, bias=True))(linear1): Linear(in_features=128, out_features=128, bias=True)(dropout): Dropout(p=0.1, inplace=False)(linear2): Linear(in_features=128, out_features=128, bias=True)(norm1): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(norm2): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(norm3): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(dropout1): Dropout(p=0.1, inplace=False)(dropout2): Dropout(p=0.1, inplace=False)(dropout3): Dropout(p=0.1, inplace=False))))(ppe): PeriodicPositionalEncoding((dropout): Dropout(p=0.1, inplace=False)))(long_layer): TemporalVAEBlock((embedding): PositionalEncoding((dropout): Dropout(p=0.1, inplace=False))(encoder): TransformerEncoder((layers): ModuleList((0): TransformerEncoderLayer((self_attn): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=128, out_features=128, bias=True))(linear1): Linear(in_features=128, out_features=128, bias=True)(dropout): Dropout(p=0.1, inplace=False)(linear2): Linear(in_features=128, out_features=128, bias=True)(norm1): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(norm2): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(dropout1): Dropout(p=0.1, inplace=False)(dropout2): Dropout(p=0.1, inplace=False))(1): TransformerEncoderLayer((self_attn): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=128, out_features=128, bias=True))(linear1): Linear(in_features=128, out_features=128, bias=True)(dropout): Dropout(p=0.1, inplace=False)(linear2): Linear(in_features=128, out_features=128, bias=True)(norm1): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(norm2): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(dropout1): Dropout(p=0.1, inplace=False)(dropout2): Dropout(p=0.1, inplace=False))(2): TransformerEncoderLayer((self_attn): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=128, out_features=128, bias=True))(linear1): Linear(in_features=128, out_features=128, bias=True)(dropout): Dropout(p=0.1, inplace=False)(linear2): Linear(in_features=128, out_features=128, bias=True)(norm1): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(norm2): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(dropout1): Dropout(p=0.1, inplace=False)(dropout2): Dropout(p=0.1, inplace=False))))(decoder): TransformerDecoder((layers): ModuleList((0): TransformerDecoderLayer((self_attn): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=128, out_features=128, bias=True))(multihead_attn): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=128, out_features=128, bias=True))(linear1): Linear(in_features=128, out_features=128, bias=True)(dropout): Dropout(p=0.1, inplace=False)(linear2): Linear(in_features=128, out_features=128, bias=True)(norm1): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(norm2): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(norm3): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(dropout1): Dropout(p=0.1, inplace=False)(dropout2): Dropout(p=0.1, inplace=False)(dropout3): Dropout(p=0.1, inplace=False))(1): TransformerDecoderLayer((self_attn): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=128, out_features=128, bias=True))(multihead_attn): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=128, out_features=128, bias=True))(linear1): Linear(in_features=128, out_features=128, bias=True)(dropout): Dropout(p=0.1, inplace=False)(linear2): Linear(in_features=128, out_features=128, bias=True)(norm1): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(norm2): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(norm3): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(dropout1): Dropout(p=0.1, inplace=False)(dropout2): Dropout(p=0.1, inplace=False)(dropout3): Dropout(p=0.1, inplace=False))(2): TransformerDecoderLayer((self_attn): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=128, out_features=128, bias=True))(multihead_attn): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=128, out_features=128, bias=True))(linear1): Linear(in_features=128, out_features=128, bias=True)(dropout): Dropout(p=0.1, inplace=False)(linear2): Linear(in_features=128, out_features=128, bias=True)(norm1): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(norm2): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(norm3): LayerNorm((128,), eps=1e-05, elementwise_affine=True)(dropout1): Dropout(p=0.1, inplace=False)(dropout2): Dropout(p=0.1, inplace=False)(dropout3): Dropout(p=0.1, inplace=False))))(out): Sequential((0): Linear(in_features=128, out_features=128, bias=True))(to_mu): Linear(in_features=128, out_features=128, bias=True)(to_logvar): Linear(in_features=128, out_features=128, bias=True)(decode_latent): Linear(in_features=128, out_features=128, bias=True)))(lipmotion_tail): MLP((layers): Sequential((0): Linear(in_features=256, out_features=512, bias=True)(1): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(2): LeakyReLU(negative_slope=0.2)(3): Linear(in_features=512, out_features=120, bias=True)))(eyemovement_tail): MLP((layers): Sequential((0): Linear(in_features=256, out_features=256, bias=True)(1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(2): LeakyReLU(negative_slope=0.2)(3): Linear(in_features=256, out_features=256, bias=True)(4): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(5): LeakyReLU(negative_slope=0.2)(6): Linear(in_features=256, out_features=180, bias=True)))(headmotion_tail): MLP((layers): Sequential((0): Linear(in_features=256, out_features=256, bias=True)(1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(2): LeakyReLU(negative_slope=0.2)(3): Linear(in_features=256, out_features=256, bias=True)(4): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(5): LeakyReLU(negative_slope=0.2)(6): Linear(in_features=256, out_features=7, bias=True)))(torsomotion_tail): MLP((layers): Sequential((0): Linear(in_features=256, out_features=256, bias=True)(1): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(2): LeakyReLU(negative_slope=0.2)(3): Linear(in_features=256, out_features=256, bias=True)(4): BatchNorm1d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(5): LeakyReLU(negative_slope=0.2)(6): Linear(in_features=256, out_features=54, bias=True))))
)
[Network MODA] Total number of parameters : 96.718 M

在这里插入图片描述

lip decoder:MLP

Sequential((0): Linear(in_features=256, out_features=512, bias=True)(1): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(2): LeakyReLU(negative_slope=0.2)(3): Linear(in_features=512, out_features=120, bias=True)
)

batch norm 输出后的特征(x_1)基本都一样了

layer_0 的输出:

在这里插入图片描述

layer_1 的输出:全为负值

在这里插入图片描述

推测是模型根本没训练好,可能是学习率的问题,也可能是 target 的问题

这里把学习率从原本的 1e-4 调到了 1e-3 和 1e-5,都没有什么改变,loss 很大,尤其是 headmotion loss 大概在几十万,所以这里 target 的训练应该是有问题的

所以我又去看了看为什么 loss 这么大,发现 target_headmotion 和 target_torsomotion 的数据分布范围很大:

在这里插入图片描述
在这里插入图片描述

可以看看其他的 target 还是比较小的:

在这里插入图片描述
在这里插入图片描述

去 audio2repr_dataset.py 中看看数据是怎么处理的:

在这里插入图片描述
data: len=17,这里的 1200 表示 batch=2,每个 batch 帧数为 600

  • data_list[file_index][0]:audio_array,tensor([-0.8657, -0.9239, -0.8294, …, -0.0095, -0.0519, -0.1292]),torch.Size([640128])
  • data_list[file_index][1]:av_rate,533
  • data_list[file_index][2]:face_vertices,torch.Size([1200, 478, 3])
  • data_list[file_index][3]:face_vert_ref 均值,[478, 3]
  • data_list[file_index][4]:face_vert_ref 方差,[478, 3]
  • data_list[file_index][5]:face_headposes,[1200, 3]
  • data_list[file_index][6]:face_head_ref 均值,[3]
  • data_list[file_index][7]:face_head_ref 方差, [3]
  • data_list[file_index][8]:face_transposes, [1200, 3]
  • data_list[file_index][9]:face_trans_ref 均值, [3]
  • data_list[file_index][10]:face_trans_ref 方差, [3]
  • data_list[file_index][11]:face_scales, [1200, 1]
  • data_list[file_index][12]:face_scale_ref 均值, [1]
  • data_list[file_index][13]:face_scale_ref 方差, [1]
  • data_list[file_index][14]:torso_info, [1200, 18, 3]
  • data_list[file_index][15]:torso_info_ref 均值, [18, 3]
  • data_list[file_index][16]:torso_info_ref 方差, [18, 3]

4.3 推理

先使用 mediapipe 来提取面部关键点

# 一段从 utils.py 截出来的代码片,只是展示操作方式而已
import mediapipe as mp
mp_drawing_styles = mp.solutions.drawing_styles
mp_connections = mp.solutions.face_mesh_connections
def get_semantic_indices():semantic_connections = {'Contours':     mp_connections.FACEMESH_CONTOURS,'FaceOval':     mp_connections.FACEMESH_FACE_OVAL,'LeftIris':     mp_connections.FACEMESH_LEFT_IRIS,'LeftEye':      mp_connections.FACEMESH_LEFT_EYE,'LeftEyebrow':  mp_connections.FACEMESH_LEFT_EYEBROW,'RightIris':    mp_connections.FACEMESH_RIGHT_IRIS,'RightEye':     mp_connections.FACEMESH_RIGHT_EYE,'RightEyebrow': mp_connections.FACEMESH_RIGHT_EYEBROW,'Lips':         mp_connections.FACEMESH_LIPS,'Tesselation':  mp_connections.FACEMESH_TESSELATION}def get_compact_idx(connections):ret = []for conn in connections:ret.append(conn[0])ret.append(conn[1])return sorted(tuple(set(ret)))semantic_indexes = {k: get_compact_idx(v) for k, v in semantic_connections.items()}return semantic_indexes

generate_feature.py 得到的面部信息如下:

{
'Contours': [0, 7, 10, 13, 14, 17, 21, 33, 37, 39, 40, 46, 52, 53, 54, 55, 58, 61, 63, 65, 66, 67, 70, 78, 80, 81, 82, 84, 87, 88, 91, 93, 95, 103, 105, 107, 109, 127, 132, 133, 136, 144, 145, 146, 148, 149, 150, 152, 153, 154, 155, 157, 158, 159, 160, 161, 162, 163, 172, 173, 176, 178, 181, 185, 191, 234, 246, 249, 251, 263, 267, 269, 270, 276, 282, 283, 284, 285, 288, 291, 293, 295, 296, 297, 300, 308, 310, 311, 312, 314, 317, 318, 321, 323, 324, 332, 334, 336, 338, 356, 361, 362, 365, 373, 374, 375, 377, 378, 379, 380, 381, 382, 384, 385, 386, 387, 388, 389, 390, 397, 398, 400, 402, 405, 409, 415, 454, 466], 
'FaceOval': [10, 21, 54, 58, 67, 93, 103, 109, 127, 132, 136, 148, 149, 150, 152, 162, 172, 176, 234, 251, 284, 288, 297, 323, 332, 338, 356, 361, 365, 377, 378, 379, 389, 397, 400, 454], 
'LeftIris': [474, 475, 476, 477], 
'LeftEye': [249, 263, 362, 373, 374, 380, 381, 382, 384, 385, 386, 387, 388, 390, 398, 466], 
'LeftEyebrow': [276, 282, 283, 285, 293, 295, 296, 300, 334, 336], 
'RightIris': [469, 470, 471, 472], 
'RightEye': [7, 33, 133, 144, 145, 153, 154, 155, 157, 158, 159, 160, 161, 163, 173, 246], 
'RightEyebrow': [46, 52, 53, 55, 63, 65, 66, 70, 105, 107], 
'Lips': [0, 13, 14, 17, 37, 39, 40, 61, 78, 80, 81, 82, 84, 87, 88, 91, 95, 146, 178, 181, 185, 191, 267, 269, 270, 291, 308, 310, 311, 312, 314, 317, 318, 321, 324, 375, 402, 405, 409, 415], 
'Tesselation': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467]}

这篇关于【数字人】2、MODA | 基于人脸关键点的语音驱动单张图数字人生成(ICCV2023)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/165854

相关文章

从去中心化到智能化:Web3如何与AI共同塑造数字生态

在数字时代的演进中,Web3和人工智能(AI)正成为塑造未来互联网的两大核心力量。Web3的去中心化理念与AI的智能化技术,正相互交织,共同推动数字生态的变革。本文将探讨Web3与AI的融合如何改变数字世界,并展望这一新兴组合如何重塑我们的在线体验。 Web3的去中心化愿景 Web3代表了互联网的第三代发展,它基于去中心化的区块链技术,旨在创建一个开放、透明且用户主导的数字生态。不同于传统

AI一键生成 PPT

AI一键生成 PPT 操作步骤 作为一名打工人,是不是经常需要制作各种PPT来分享我的生活和想法。但是,你们知道,有时候灵感来了,时间却不够用了!😩直到我发现了Kimi AI——一个能够自动生成PPT的神奇助手!🌟 什么是Kimi? 一款月之暗面科技有限公司开发的AI办公工具,帮助用户快速生成高质量的演示文稿。 无论你是职场人士、学生还是教师,Kimi都能够为你的办公文

阿里开源语音识别SenseVoiceWindows环境部署

SenseVoice介绍 SenseVoice 专注于高精度多语言语音识别、情感辨识和音频事件检测多语言识别: 采用超过 40 万小时数据训练,支持超过 50 种语言,识别效果上优于 Whisper 模型。富文本识别:具备优秀的情感识别,能够在测试数据上达到和超过目前最佳情感识别模型的效果。支持声音事件检测能力,支持音乐、掌声、笑声、哭声、咳嗽、喷嚏等多种常见人机交互事件进行检测。高效推

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

usaco 1.2 Name That Number(数字字母转化)

巧妙的利用code[b[0]-'A'] 将字符ABC...Z转换为数字 需要注意的是重新开一个数组 c [ ] 存储字符串 应人为的在末尾附上 ‘ \ 0 ’ 详见代码: /*ID: who jayLANG: C++TASK: namenum*/#include<stdio.h>#include<string.h>int main(){FILE *fin = fopen (

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];

poj 2349 Arctic Network uva 10369(prim or kruscal最小生成树)

题目很麻烦,因为不熟悉最小生成树的算法调试了好久。 感觉网上的题目解释都没说得很清楚,不适合新手。自己写一个。 题意:给你点的坐标,然后两点间可以有两种方式来通信:第一种是卫星通信,第二种是无线电通信。 卫星通信:任何两个有卫星频道的点间都可以直接建立连接,与点间的距离无关; 无线电通信:两个点之间的距离不能超过D,无线电收发器的功率越大,D越大,越昂贵。 计算无线电收发器D

hdu 1102 uva 10397(最小生成树prim)

hdu 1102: 题意: 给一个邻接矩阵,给一些村庄间已经修的路,问最小生成树。 解析: 把已经修的路的权值改为0,套个prim()。 注意prim 最外层循坏为n-1。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstri