近年来, 基于语音驱动的人脸动画技术在虚拟主持人、数字娱乐、人机交互以及远程会议等方面有广泛的应用。如何快速、高效的实现语音驱动的唇形自动合成,以及优化语音与唇形面部表情之间的同步是此项技术的关键。表情动画作为语音驱动人脸动画的一部分,在增加人脸动画逼真性方面起着重要的作用,但已有的工作没 有定量分析人脸表情动 画与语音之间的关系.

目前音视频模型主要集中在矢量量化的方法 (VQ)、神经网络(Neural Network,NN)、高斯混合模型 (Gaussian Mixture Mode1.GMM)、隐马尔可夫模型现代计算机2015.05中 (Hidden Markov ModeL HMM)和动态贝叶斯模型(Dv.namic Bayesian Network,DBN)的探索 ,而人脸模型主要集中在基于图像的模型、基于2D模型和基于3D模型的探索。

原博客地址

今天主要介绍的是两种语音口型动画的实现, 两种都是先从音频文件(.wav .mp3)提取出音素(主要是元音), 然后再根据不同的音素去制作不同的口型,最后不同口型之间差值得到连续的动作。

  • 基于共振峰提取元音
  • 基于神经网络提取音素

基于共振峰提取元音

这里先简单介绍一下人类发声的原理。

人在发声时,肺部收缩送出一股直流空气,经器官流至喉头声门处(即声带),使声带产生振动,并且具有一定的振动周期,从而带动原先的空气发生振动,这可以称为气流的激励过程。之后,空气经过声带以上的主声道部分(包括咽喉、口腔)以及鼻道(包括小舌、鼻腔),不同的发音会使声道的肌肉处在不同的部位,这形成了各种语音的不同音色,这可以称为气流在声道的冲激响应过程。

对于语音识别来说,重要的部分是第二个过程,因为口型就是声道形状的一部分。而这一冲激响应过程,在频谱上的表现为若干个凸起的包络峰。这些包络峰出现的频率,就被称为共振峰频率,简称为共振峰。

1. 从音频文件获取语音数据

从AudioSource处获取是实时匹配时采用的方法。AudioSource本身提供了一个GetOutputData函数,可以获取当前正在播放的语音数据段。 从AudioClip处获取是烘焙是采用的方法。AudioClip本身其实是对语音文件的一个封装,可以使用GetData函数直接获得语音数据。 这过程中也包含了分帧与窗口化的步骤。

2. 剔除无声帧

从信号处理的角度上说,这一步是一种时域分析方法。对数据帧中的所有值进行求和,如果结果大于用户预设的一个阈值(也就是AmplitudeThreshold),那么就认为这一帧是没有声音的,不对它进行后续处理。这可以节省不必要的分析过程。如果适当调高阈值,一定程度上可以降噪。

3. 获取语音数据的频域信息

你在使用一些音乐播放器时,有时候会看到一根根跳动的长条,这就是“频谱”的一种表现方式,频域信息指的就是频谱。这对于语音识别来说是非常重要的信息。 在Unity提供的API AudioSource的GetSpecturmData可以高效地获取当前播放的语音数据频谱。 如果你的项目使用了fmod的开发环境,可以使用如下获取声谱信息:

public StudioEventEmitter emiter;
FMOD.DSP m_FFTDsp;
FMOD.ChannelGroup master;
FMOD.DSP mixerHead;void Start() {emiter.Play();InitDsp();
}void InitDsp() {// 初始化均衡器 DSPRuntimeManager.CoreSystem.createDSPByType(FMOD.DSP_TYPE.FFT, out m_FFTDsp);m_FFTDsp.setParameterInt((int)FMOD.DSP_FFT.WINDOWTYPE, (int)FMOD.DSP_FFT_WINDOW.HANNING);m_FFTDsp.setParameterInt((int)FMOD.DSP_FFT.WINDOWSIZE, windowSize);RuntimeManager.CoreSystem.getMasterChannelGroup(out master);var m_Result = master.addDSP(FMOD.CHANNELCONTROL_DSP_INDEX.HEAD, m_FFTDsp);m_Result = master.getDSP(0, out mixerHead);mixerHead.setMeteringEnabled(true, true);
}void Update() {string result = null;IntPtr unmanagedData;uint length;m_FFTDsp.getParameterData((int)FMOD.DSP_FFT.SPECTRUMDATA, out unmanagedData, out length);FMOD.DSP_PARAMETER_FFT fftData = (FMOD.DSP_PARAMETER_FFT)Marshal.PtrToStructure(unmanagedData, typeof(FMOD.DSP_PARAMETER_FFT));if (fftData.spectrum != null && fftData.spectrum.Length > 0){playingAudioSpectrum = fftData.spectrum[0]; //声谱信息,针对立体音 只获取0声道频谱}
}

语音信号一般在10ms到30ms之间,我们可以把它看成是平稳的。为了处理语音信号,我们要对语音信号进行加窗,也就是一次仅处理窗中的数据。因为实际的语音信号是很长的,我们不能也不必对非常长的数据进行一次性处理。明智的解决办法就是每次取一段数据,进行分析,然后再取下一段数据。

Hanning窗函数:

W(n)=0.5∗(1.0−cos⁡(n/N))W(n) = 0.5 *(1.0 - \cos (n/N)) W(n)=0.5(1.0cos(n/N))

拿到声谱之后, 我们先使用一个高斯滤波器过滤掉噪音。通俗的讲,高斯滤波就是对整声谱进行加权平均的过程,每一个点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。

G(x)=12πσe−(x−μ)22σ2G(x) = \frac{1}{\sqrt{2\pi}\sigma} e^{-\frac{(x-\mu)^2}{2\sigma^2}} G(x)=2π

σ1e2σ2(xμ)2

其中μ\muμ, σ(σ>0)\sigma(\sigma>0)σ(σ>0)为常数, 则称X服从参数为μ\muμ, σ(σ>0)\sigma(\sigma>0)σ(σ>0)的正态分布或者高斯分布。

对于离线处理的音频可以借助了一个数学工具——离散余弦变换(DCT),它可以用来获取一个时域信息段的频域信息。它与另一个著名的数学工具——傅里叶变换是等价的,所不同的是余弦变换只获取频率信息,而舍弃了相位信息。实际上这就够了,我们并不需要相位信息。

一维DCT变换:

F(u)=c(u)∑i=0N−1f(i)cos[(i+0.5)πNu]F(u) = c(u) \sum^{N-1}_{i=0} f(i) cos \left[ \frac{(i+0.5)\pi}{N} u \right] F(u)=c(u)i=0N1f(i)cos[N(i+0.5)πu]

c(u)={1N,u=02N,u≠0c(u) = \left\{ \begin{aligned} \sqrt{ \frac{1}{N}}, {u=0} \\ \sqrt{ \frac{2}{N}}, {u \neq 0} \end{aligned} \right. c(u)=N1

,u=0N2

,u=0

其中,f(i)为原始的信号,F(u)是DCT变换后的系数,N为原始信号的点数,c(u)可以认为是一个补偿系数,可以使DCT变换矩阵为正交矩阵

二维DCT变换:

F(u,v)=c(u)c(v)∑i=0N−1f(i,j)cos[(i+0.5)πNu]cos[(j+0.5)πNv]F(u,v) = c(u)c(v) \sum^{N-1}_{i=0} f(i,j) cos\left[ {\frac{(i+0.5)\pi}{N}u}\right] cos \left[ {\frac{(j+0.5)\pi}{N}v} \right] F(u,v)=c(u)c(v)i=0N1f(i,j)cos[N(i+0.5)πu]cos[N(j+0.5)πv]

c(u)={1N,u=02N,u≠0c(u) = \left\{ \begin{aligned} \sqrt{ \frac{1}{N}}, {u=0} \\ \sqrt{ \frac{2}{N}}, {u \neq 0} \end{aligned} \right. c(u)=N1

,u=0N2

,u=0

声音是不像图像, 是一维序列。因此这里应用的是一维DCT变换。 github示例工程里对应的代码如下:

public static float[] DiscreteCosineTransform(float[] data)
{float[] result = new float[data.Length];float sumCos;for (int m = 0; m < data.Length; ++m){sumCos = 0.0f;for (int k = 0; k < data.Length; ++k){sumCos += data[k] * Mathf.Cos((Mathf.PI / data.Length) * m * (k + 0.5f));}result[m] = (sumCos > 0) ? sumCos : -sumCos;}return result;
}

4. 提取共振峰

一般来说,通过求得一段语音数据的第一、第二共振峰,就可以非常精确地得知这段语音的"元音"是什么。只求第一共振峰,也可以知道大致结果。提取共振峰的方法是,在前一步骤中获取的频谱上求出局部最大值的最大值.

public static void FindLocalLargestPeaks(float[] data, float[] peakValue, int[] peakPosition)
{int peakNum = 0;float lastPeak = 0.0f;int lastPeakPosition = 0;bool isIncreasing = false;bool isPeakIncreasing = false;for (int i = 0; i < data.Length - 1; ++i) {if (data[i] < data[i + 1]) {isIncreasing = true;}else {if (isIncreasing) {if (lastPeak < data[i])  // Peak found.{isPeakIncreasing = true;} else {if (isPeakIncreasing) {peakValue[peakNum] = lastPeak; // Local largest peak found. peakPosition[peakNum] = lastPeakPosition;++peakNum;}isPeakIncreasing = false;}lastPeak = data[i];lastPeakPosition = i;}isIncreasing = false;}if (peakNum >= peakValue.Length) break;}
}

提取语音共振峰的方法比较多,除了使用最大值法,常用的方法还有倒谱法、LPC(线性预测编码)谱估计法、LPC倒谱法等。

5. 制作元音口型

一般改变口型都是通过BlendShape, 也可以改变骨骼,或者通过Live2D。通过每帧控制参数来调节口型。例如BlendShape, 五个口型对应到日语的五个元音。在Update里分析每一帧的声谱,通过共振峰提取得到相应的元音, 然后插值表现相应的口型。通过github下载工程,本地运行同样的效果。

最后的效果就是下面展示的视频所示:

口型效果展示

神经网络提取音素

我们的想法很直接,就是基于音频生成口型动画,或者说,输入是一段音频,经过我们的系统,输出为相应的口型动画。而如果先忽略音频和动画的时序,问题就变成了将音频关键帧经过一个深度神经网络,得到一个口型关键帧。

特征表示

音频特征

对于问题1,我们先说音频特征。这里我们采用的音频特征是语音识别领域内常用的梅尔频率倒谱系数(MFCCs)。梅尔频率倒谱系数是受人的听觉系统研究成果推动而导出的声学特征,它对于声音信号处理更接近人耳对声音的分析特性,能够准确的描述语音短时功率谱的包络,从而很好的反应出声道形状。

在特征提取过程中,我们先以20ms的帧长和10ms的帧移对音频进行分帧处理,并计算每个音频帧的梅尔频率倒谱系数(系数个数为M=13)。计算完音频帧的梅尔频率倒谱系数后,我们还获取了它的一阶差分系数和二阶差分系数。该差分系数用来描述动态特征,即声学特征在相邻特征间的变化情况。

经过上述处理之后,针对每个音频帧,我们都有一个13x3维的梅尔频率倒谱特征,用于描述当前音频帧的包络和声学特征的变化信息。理论上,我们可以直接用它来表示口型帧对应的音频帧,但为了更准确的捕获音频的上下文信息,我们在实际处理时会以口型帧对应的音频帧为中心,选取前后共N=16帧的音频帧的梅尔频率倒谱特征作为当前音频帧的特征。这样,我们最终的音频特征就是一个16x13x3维的特征了。

口型特征

有了音频特征,接下来就是口型特征了。这里,我们参考 提取出40个通用音素的权重作为口型帧的表示。在具体实现时,我们按发音口型将40个音素分为了11个音素组,并针对每个音素组制作其相应的嘴型。值得一提的是,音素组数目的选取和嘴型的制作方法(基于骨骼动画或者基于BlendShape)可由使用者自行选定,这里并不会影响底层算法的实现

网络架构

该如何搭建网络呢?这里我们参考了Karras等人在SIGGRAPH 2017的论文 Audio driven Facial Animation by Joint End-to-end Learning of Pose and Emotion 中的工作,搭建了下图所示的网络架构。

该架构由输入层、谱分析网络、协同发音网络和输出层等四部分组成。输入层接受音频特征,并通过不含激活函数的卷积层做初始变换。然后谱分析网络在谱特征维度上对特征进行分析。紧接着,协同发音网络在时域上对提取的特征做进一步分析。最后,输出层通过1x1的卷积核将特征映射成口型特征。网络的详细配置见表。

数据集

实验中,我们采用了LibriSpeech和AISHELL两个大型语料数据集,其中英文音频要多于中文音频,这也使得系统对于英文的口型合成的效果要略好于中文。两个数据集上,音频特征的提取是通过我们编写的代码得到的,而口型特征的提取则分为两个部分:针对英文音频,40个音素权重是利用第三方SDK Annosoft生成的。针对中文音频,这40个音素权重是本组基于Annosoft进行改进优化之后重新生成的。

那我们为什么不直接使用第三方SDK制作口型动画呢?主要原因有两点:

  • 这两种方法均需要字幕文件(Annosoft提供不带字幕的音素标注方法,但效果很差)。
  • 这两种方法针对不同语种音频的音素标注需要的配置文件不相同,操作比较繁琐。而我们的系统针对中英文音频的音素标注可使用同一套配置,且仅需音频文件就可合成高质量的嘴型动画。

*Lipsync官方自动口型插件*

效果

在引擎中显示的效果如下:

Your user agent does not support the HTML5 Video element.

总结

我们实现了一套口型动画合成系统,该系统利用深度学习完成从语音到口型动画的映射,可以有效解决语音动画同步的难题,增强动画的真实感和逼真性。同时,该系统对于说话人和语言不敏感,对于中英文的支持普遍好于市面上的同类产品。此外,该系统由于只需要音频文件,所以极大的简化了口型动画的制作流程,减少了相关的时间成本和人员开销。

当然,该系统还存在一定的局限性,具体表现为两方面:

1、该系统没有对人说话时的情绪和说话风格做特殊处理,这导致带情绪和不带情绪说话时合成的口型动画区别不明显,其主要原因是样本数据中带情绪的音频过少,难以提取出情绪特征。

2、该系统暂时无法根据音频内容生成相应的表情动画,这主要是由于相比于口型动画,表情动画的制作会简单得多,也就没有成为我们的研究重点。需要说明的是,我们的系统支持二次编辑,允许用户在口型动画的基础上添加表情。

最后,该系统现已集成到Unity插件中,并被用于主机项目中。我们可以提供全套的口型动画生成支持或者根据现有的口型制作流程调整适配我们的口型动画生成系统,感兴趣的同学欢迎联系我们,一起交流,共同进步。

参考文献:

  • Audio-Driven-Facial-Animation
  • 跟脸有关的最新玩法是你说什么,表情包就演什么
  • 一种基于共振峰分析的语音驱动人脸动画方法
  • 基于机器学习的语音驱动人脸动画方法
  • 人脸表情动画与语音的典型相关性分析
  • 基于汉语驱动人脸语音动画的研究
  • 基于参数控制的语音驱动唇形同步人脸动画
  • AE嘴唇自动口型动画
  • lipsync-sdks annosoft
  • 详解离散余弦变换(DCT)
  • 高斯滤波 百度百科
  • 海明窗 百度百科

游戏中口型动画合成系统相关推荐

  1. Flash在移动游戏中渲染动画模型

    游戏程序 平台类型:   程序设计:   编程语言:   引擎/SDK:   源:Adobe 动画模型是在游戏中移动的对象,它们能够表示你的角色.车辆.怪物和任何其它互动对象,它们不是明确地在背景中画 ...

  2. 2d游戏中角色动画解决方案

    刚刚在cocos creator论坛中,看到有水友在update更新spriteFrame来做角色动画,其实是可以使用 cc.Animation 来做角色动画,,这是我们游戏的实现方式,给大家参考下. ...

  3. 在游戏中测试复杂的系统

    I've been testing software for a long time. In fact, my first job was as a tester. In an industry wh ...

  4. [game]暴雪游戏中的多样性日常系统

    现在<星际争霸2>和<暗黑破坏神3>中的"日常模式"(姑且这么叫吧,也可以说成是轻度游戏模式),在传统日常任务模式中增加了多样性,一方面满足了日常任务中大家 ...

  5. Unity-3D捕鱼达人小游戏开发 —— 游戏中精灵动画的制作

    创建好的精灵动画拖进归类空项目之后一定要修改z轴,还有order in layer,从1开始依次增加5,越珍贵的鱼值越大 拖入右边保存预制体,删除左边的精灵动画 将鱼.鱼死亡.子弹的精灵动画全做成预制 ...

  6. 游戏中的整容术! 《Honey Select》捏人系统剖析

    关于游戏中的捏人系统,很少有资料提到怎么做,印象中只有<天涯明月刀>分享过.前段时间关注了个VR资源分享的公众号,经常推送HS的捏人作品,所以才引发了我的好奇心,决定一探究竟. HS之所以 ...

  7. 浅谈RPG游戏中的属性系统设定

    先来张我最喜欢的Nero和Dante的帅照!!! 最近的E3展也是让广大游戏爱好者们打开眼界,小编最喜欢的鬼泣系列也将于明年春季迎来鬼泣4的正统续作鬼泣5,再加上最近小编也在自己开发着RPG游戏,所以 ...

  8. 第六章 DirectX 2D游戏和帧动画(上)

    目前,我们已经掌握了如何使用DirectX绘制四边形,纹理映射技术,以及正交摄像机的内容.对于2D游戏的开发,这些内容基本上已经足够了.2D游戏的本质就是图像游戏,2D游戏中的动画其实就是一系列连续动 ...

  9. Unity3d动画脚本 Animation Scripting(深入了解游戏引擎中的动画处理原理--旧的动画系统)

    (来自:http://blog.sina.com.cn/s/blog_409cc4b00100qmgz.html) 也许这一篇文章的内容有点枯燥,但我要说的是如果你想深入的了解游戏引擎是如何处理动画片 ...

最新文章

  1. 2020-10-29(Android 的DEX ,ODEX,ELF )
  2. ubuntu 局域网dns服务器_如何在 Ubuntu 16.04 服务器上配置内网 DNS 服务
  3. c++学习笔记之模板
  4. 哥尼斯堡的“七桥问题” (25 分)【欧拉回路模板题】
  5. 昆仑量子计算机只是云计算模拟吧,量子模拟赛题让大学生认识未来计算发展方向...
  6. 暑期训练日志----2018.8.3
  7. 13002.tcp客户端程序(python)
  8. Maven : [ERROR] Project xxx is duplicated in the reactor @
  9. 内置auth 的使用,用超级用户创建
  10. MongoDB副本集配置系列二:配置MongoDB副本集
  11. 11款超5000星 后台管理模板, 总有一款适合你!
  12. 生产管理MES系统框架
  13. smart原则_写给中学生:用SMART原则制定寒假计划
  14. 仿映客直播礼物特效制作流程
  15. Linux百度车牌识别api,PC端车牌识别SDK
  16. linux支持raid5阵列,Linux中raid磁盘阵列,磁盘阵列raid5
  17. 项目中有时候为什么加载不出来图片
  18. 打破第一范式的要求 (中英对照)Michael Rys 对 SQL Server 2005 中XML 的 评论——对微软SQL Server项目经理Michael Rys博士的采访
  19. layui 给table里面的添加图标_layui教程---table
  20. python环境准备(一)

热门文章

  1. 华硕笔记本U盘启动设置
  2. html5-embed标签的使用
  3. Java知识体系总结(2020版)
  4. iOS tintColor 与 backgroundColor
  5. 瀑布流 gank.io 美女福利
  6. 医药机械设备远程监控及故障预警诊断系统
  7. 微信浏览器取消缓存方法大全
  8. SQL Server 创建数据库基本流程
  9. 英语学习-29190410雅思图表作文写作--line grapgh
  10. vue返回顶部的组件BackTop