KinectforWindowsSDK开发入门语音识别全Word格式.docx
《KinectforWindowsSDK开发入门语音识别全Word格式.docx》由会员分享,可在线阅读,更多相关《KinectforWindowsSDK开发入门语音识别全Word格式.docx(31页珍藏版)》请在冰豆网上搜索。
保持某一动作,然后通过语音执行。
菜单的设计也可以通过首先展示菜单项,然后让用户说出菜单项的名称来进行选择-很多Xbox中的游戏已经使用了这种方式。
可以预见,无论是程序开发者还是游戏公司,这种复合的解决方案在未来会越来越多的应用到新的交互方式中,而不用再像以前那样使用指然后点(pointandclick)这种方式来选择。
1.麦克风阵列
安装完MicrosoftKinectSDK之后,语音识别的组件会自动安装。
Kinect的麦克风阵列工作在一些语音识别的类库之上,这些类库是从Vista系统之时就有的。
他们包括语音捕获DirectX多媒体对象(DirectXMediaObject,DMO)以及语音识别API(SpeechRecognitionAPI,SAPI)。
在C#中,KinectSDK提供了对语音捕获DMO的封装。
语音捕获DMO最初是被设计用来给麦克风阵列提供API来支持一些功能如回声消除(acousticechocancellation,AEC),自动增益控制(automaticgaincontrol,AGC)和噪声抑制(noisesuppression)。
这些功能在SDK的音频控制类中可以找到。
KinectSDK中音频处理对语音捕获DMO进行了简单封装,并专门针对Kinect传感器进行了性能优化。
为了能够使用KinectSDK进行语音识别,自动安装的类库包括:
SpeechPlatformAPI,SpeechPlatformSDK和KinectforWindowsRuntimeLanguagePack。
语音识别API能够简化操作系统自带的语音识别所需的类库。
例如,如果你想通过普通的麦克风而不是Kinect麦克风阵列添加一些语音指令到桌面应用程序中去,可以使用也可以不使用KinectSDK。
Kinectforwindows运行语言包是一系列的语言模型,用来在KinectSDK和语音识别API组件之间进行互操作。
就像Kinect骨骼识别需要大量的计算模型来提供决策树信息来分析节点位置那样,语音识别API也需要复杂的模型来辅助解释从Kinect麦克风阵列接收到的语言模型。
Kinect语言包提供了这些模型来优化语音指令的识别。
1.1MSRKinectAudio
Kinect中处理音频主要是通过KinectAudioSource这个对象来完成的。
KinectAudioSource类的主要作用是从麦克风阵列中提取原始的或者经过处理的音频流。
音频流可能会经过一系列的算法来处理以提高音频质量,这些处理包括:
降噪、自动增益控制和回声消除。
KinectAudioSource能够进行一些配置使得Kinect麦克风阵列可以以不同的模式进行工作。
也能够用来探测从那个方向来的哪种音频信息最先达到麦克风以及用来强制麦克风阵列接受指定方向的音频信息。
本节尽量不会去介绍一些音频处理技术方面的较低层次的技术。
但是为了使用KinectAudioSource,了解语音捕获以及语音传输中的一些术语可能会对熟悉KinectAudioSource中的一些属性和方法有所帮助。
∙回声消除(acousticechocancellation,AEC)当用户的声音从麦克风返回时,就会产生回声。
最简单的例子就是用户在打电话时能够听到自己的声音,这些声音有一些延迟,会在对方那里重复一段时间。
回声消除通过提取发声者的声音模式,然后根据这一模式从麦克风接收到的音频中挑选出特定的音频来消除回声。
∙回声抑制(acousticechosuppression,AES)它是指通过一系列的算法来进一步消除AEC处理后所遗留的回声。
∙自动增益控制(acousticgaincontrol,AGS)它涉及到一些算法用来使用户的声音的振幅与时间保持一致。
例如当用户靠近或者或远离麦克风时,声音会出现变得响亮或更柔和,AGC通过算法使得这一过程变得更加明显。
∙波束成形(beamforming)指的是模拟定向麦克风的算法技术。
和只有一个麦克风不同,波速成形技术用于麦克风阵列中(如Kinect传感器上的麦克风阵列)使得麦克风阵列产生和使用多个固定麦克风的效果相同。
∙中心削波(centerclipping)用来移除在单向传输中经AEC处理后残留的小的回声。
∙帧尺寸(FrameSize)AEC算法处理PCM音频样本是是一帧一帧处理的。
帧尺寸是样本中音频帧的大小。
∙获取增益边界(GainBounding)该技术确保麦克风有正确的增益级别。
如果增益过高,获取到的信号可能过于饱和,会被剪切掉。
这种剪切有非线性的效果,会使得AEC算法失败。
如果增益过低,信噪比会比较低,也会使得AEC算法失败或者执行的不好。
∙噪声填充(NoiseFilling)向中心削波移除了残留的回波信号后的部分信号中添加少量的噪音。
和留下空白的沉默信号相比,这能够获得更好的用户体验。
∙噪声抑制(NS)用于从麦克风接收到的音频信号中剔除非言语声音。
通过删除背景噪音,实际讲话者的声音能够被麦克风更清楚更明确的捕获到。
∙OptibeamKinect传感器从四个麦克风中能够获得11个波束。
这11个波束是逻辑结构,而四个通道是物理结构。
Optibeam是一种系统模式用来进行波束成形。
∙信噪比(Signal-to-NoiseRatio,SNR)信号噪声比用来度量语音信号和总体背景噪声的比例,信噪比越高越好。
∙单通道(SingleChannel)Kinect传感器有四个麦克风,因此支持4个通道,单通道是一种系统模式用来关闭波束成形。
KinectAudioSource类提供一些对音频捕获多方面的较高层次的控制,虽然它并没有提供DMO中的所有功能。
KinectAudioSource中用来调整音频处理的各种属性被称之为功能(features)属性。
下表中列出了可以调整的功能属性。
KinectSDK早期的Beta版本视图提供了DMO中的所有功能以使得能够有更加强大的控制能力,但是这也极大的增加了复杂度。
SDK的正式版本提取了DMO中的所有可能的配置然后将其封装为特征属性使得我们不用关心底层的配置细节。
对于没有接触过这些底层配置属性的开发者来说,这是一种巨大的解脱。
EchoCancellationMode是一个隐藏在不起眼的名称后面神奇的技术之一。
他可能的设置如下表。
为了适应AEC,需要给EchoCancellationSpeakerIndex属性赋一个int值来指定那一个用户的噪音需要控制。
SDK会自动执行活动麦克风的发现和初始化。
BeamAngleMode对底层的DMO系统模式和麦克风阵列属性进行了抽象封装。
在DMO级别上,他决定了是由DMO还是应用程序进行波束成形。
在这一基础上KinectforWindowsSDK提供了额外的一系列算法来进行波束成行。
通常,可以将该属性设置为Adaptive,将这些复杂的操作交给SDK进行处理。
下表展示了每一个可设置值的属性。
自适应波速成形(Adaptivebeamforming)能够发挥Kinect传感器的特性优势,根据骨骼追踪所找到的游戏者,从而找出正确的声音源。
和骨骼追踪一样,Kinect的波束成形特性也能够使用手动模式,允许应用程序来设定要探测声音的方向。
要使用Kinect传感器作为定向的麦克风,需要将波束角度模式设定为Manual然后设置KinectAudioSource的ManualBeamAngle属性。
1.2语音识别
语音识别可以分为两类:
对特定命令的识别(recognitionofcommand)和对自由形式的语音的识别(recognitionoffree-formdictation)。
自由形式的语音识别需要训练软件来识别特定的声音以提高识别精度。
一般做法是让讲话人大声的朗读一系列的语料来使得软件能够识别讲话人声音的特征模式,然后根据这一特征模式来进行识别。
而命令识别(Commandrecognition)则应用了不同的策略来提高识别精度。
和必须识别说话人声音不同,命令识别限制了说话人所讲的词汇的范围。
基于这一有限的范围,命令识别可以不需要熟悉讲话人的语音模式就可以推断出说话人想要说的内容。
考虑到Kinect的特性,使用Kinect进行自由形式的语音识别没有多大意义。
KinectSDK的设计初衷是让大家能够简单容易的使用,因此,SDK提供了Microsoft.Speech类库来原生支持语音命令的识别。
Microsoft.Speech类库是Microsoft语音识别技术的服务器版本。
如果你想使用System.Speech类库中的语音识别能力,可以使用Windows操作系统内建的桌面版语音识别来通过Kinect的麦克风来建立一个自由语音识别系统。
但是通过将Kinect的麦克风和System.Speech类库组合开发的自由语音识别系统的识别效果可能不会太好。
这是因为Kinectforwindows运行时语言包,能够适应从开放空间中的声音,而不是从麦克风发出的声音,这些语言模型在System.Speech中不能够使用。
Microsoft.Speech类库的语音识别功能是通过SpeechRecognitionEngine对象提供的。
SpeechRecognitionEngine类是语音识别的核心,它负责从Kinect传感器获取处理后的音频数据流,然后分析和解译这些数据流,然后匹配出最合适的语音命令。
引擎给基本发声单元一定的权重,如果判断出发声包含特定待识别的命令,就通过事件进行进一步处理,如果不包含,直接丢掉这部分音频数据流。
我们需要告诉SpeechRecognitionEngine从一个特定的称之为语法(grammars)的对象中进行查找。
Grammar对象由一系列的单个单词或者词语组成。
如果我们不关心短语的部分内容,可以使用语法对象中的通配符。
例如,我们可能不会在意命令包含短语"
an"
apple或者"
the"
apple,语法中的通配符告诉识别引擎这两者都是可以接受的。
此外,我们还可以添加一个称之为Choices的对象到语法中来。
选择类(Choices)是通配符类(Wildcard)的一种,它可以包含多个值。
但与通配符不同的是,我们可以指定可接受的值的顺序。
例如如果我们想要识别“Givemesomefruit”我们不关心fruit单词之前的内容,但是我们想将fruit替换为其它的值,如apple,orange或者banana等值。
这个语法可以通过下面的代码来实现。
Microsoft.Speech类库中提供了一个GrammarBuilder类来建立语法(grammars)。
varchoices=newChoices();
choices.Add("
fruit"
);
apple"
orange"
banana"
vargrammarBuilder=newGrammarBuilder();
grammarBuilder.Append("
give"
me"
grammarBuilder.AppendWildcard();
grammarBuilder.Append(choices);
vargrammar=newGrammar(grammarBuilder);
语法中的单词不区分大小写,但是出于一致性考虑,要么都用大写,要么都用小写。
语音识别引擎使用LoadGrammar方法将Grammars对象加载进来。
语音识别引擎能够加载而且通常是加载多个语法对象。
识别引擎有3个事件:
SpeechHypothesized,SpeechRecognized和SpeechRecognitionRejected。
SpeechHypothesized事件是识别引擎在决定接受或者拒绝用户命令之前解释用户说话的内容。
SpeechRecognitionRejected用来处理识别命令失败时需要执行的操作。
SpeechRecognized是最重要的事件,他在引擎决定接受用户的语音命令时触发。
该事件触发时,通过SpeechRecognizedEventArgs对象参数传递一些数据。
SpeechRecognizedEventArgs类有一个Result属性,该属性描述如下:
实例化SpeechRecognitionEngine对象需要执行一系列特定的步骤。
首先,需要设置识别引擎的ID编号。
当安装了服务器版本的Microsoft语音库时,名为MicrosoftLightweightSpeechRecognizier的识别引擎有一个为SR_MS_ZXX_Lightweight_v10.0的ID值(这个值根据你所安装的语音库的不同而不同)。
当安装了KinectforWindows运行时语音库时,第二个ID为ServerSpeechRecognitionLanguage-Kinect(en-US)的语音库可以使用。
这是Kinect中我们可以使用的第二个识别语音库。
下一步SpeechRecognitionEngine需要指定正确的识别语音库。
由于第二个语音识别库的ID可能会在以后有所改变,我们需要使用模式匹配来找到这一ID。
最后,语音识别引擎需要进行配置,以接收来自KinectAudioSource对象的音频数据流。
下面是执行以上过程的样板代码片段。
varsource=newKinectAudioSource();
Func<
RecognizerInfo,bool>
matchingFunc=r=>
{
Stringvalue;
r.AdditionalInfo.TryGetValue("
Kinect"
outvalue);
return"
True"
.Equals(value,StringComparison.InvariantCultureIgnoreCase)
&
&
"
en-US"
.Equals(r.Culture.Name,StringComparison.InvariantCultureIgnoreCase);
};
RecognizerInfori=SpeechRecognitionEngine.InstalledRecognizers().Where(matchingFunc).FirstOrDefault();
varsre=newSpeechRecognitionEngine(ri.Id);
KinectSensor.KinectSensors[0].Start();
Streams=source.Start();
sre.SetInputToAudioStream(s,newSpeechAudioFormatInfo(EncodingFormat.Pcm,16000,16,1,32000,2,null));
sre.Recognize();
SetInputToAudioStream方法的第二个参数用来设置从Kinect获取的音频数据流的格式。
在上面的代码中,我们设置音频编码格式为PulseCodeModulation(PCM),每秒接收16000个采样,每个样本占16位,只有1个通道,每秒中产生32000字节数据,块对齐值设置为2。
Grammars加载到语音识别引擎后,引擎必须启动后才能进行识别,启动引擎有几种模式,可以使用同步或者异步模式启动。
另外也可以识别一次或者继续识别从KinectAudioSource传来的多条语音命令。
下表列出了开始语音识别的可选方法。
在文将使用一些简单的例子来展示如何使用KinectAudioSource和SpeechRecognitionEngine类。
2.获取音频数据
虽然KinectAudioSource类的最主要作用是为语音识别引擎提供音频数据流,但是它也可以用于其他目的。
他还能够用来录制wav文件。
下面的示例将使用KinectAudioSource来开发一个音频录音机。
使用这个项目作为录音机,读者可以修改Kinectsdk中KinectAudioSource的各个参数的默认值的来了解这些参数是如何控制音频数据流的产生。
2.1使用音频数据流
虽然使用的是Kinect的音频相关类,而不是视觉元素类,但是建立一个Kinect音频项目的过程大致是类似的。
1.创建一个名为KinectAudioRecorder的WPF应用项目。
2.添加对Microsoft.Kinect.dll和Microsoft.Speech.dll的引用。
3.在MainWindows中添加名为Play,Record和Stop三个按钮。
4.将主窗体的名称改为“AudioRecorder”
在VS的设计视图中,界面看起来应该如下:
令人遗憾的是,C#没有一个方法能够直接写入wav文件。
为了能够帮助我们生成wav文件,我们使用下面自定义的RecorderHelper类,该类中有一个称之为WAVFORMATEX的结构,他是C++中对象转换过来的,用来方便我们对音频数据进行处理。
该类中也有一个称之为IsRecording的属性来使得我们可以停止录制。
类的基本结构,以及WAVFORMATEX的结构和属性如下。
我们也需要初始化一个私有名为buffer字节数组用来缓存我们从Kinect接收到的音频数据流。
classRecorderHelper
staticbyte[]buffer=newbyte[4096];
staticbool_isRecording;
publicstaticboolIsRecording
{
get
return_isRecording;
}
set
_isRecording=value;
structWAVEFORMATEX
publicushortwFormatTag;
publicushortnChannels;
publicuintnSamplesPerSec;
publicuintnAvgBytesPerSec;
publicushortnBlockAlign;
publicushortwBitsPerSample;
publicushortcbSize;
}
为了完成这个帮助类,我们还需要添加三个方法:
WriteString,WriteWavHeader和WriteWavFile方法。
WriteWavFile方法如下,方法接受KinectAudioSource和FileStream对象,从KinectAudioSource对象中我们可以获取音频数据,我们使用FileStream来写入数据。
方法开始写入一个假的头文件,然后读取Kinect中的音频数据流,然后填充FileStream对象,直到_isRecoding属性被设置为false。
然后检查已经写入到文件中的数据流大小,用这个值来改写之前写入的文件头。
publicstaticvoidWriteWavFile(KinectAudioSourcesource,FileStreamfileStream)
varsize=0;
//writewavheaderplaceholder
WriteWavHeader(fileStream,size);
using(varaudioStream=source.Start())
//chunkaudiostreamtofile
while(audioStream.Read(buffer,0,buffer.Length)>
0&
_isRecording)
fileStream.Write(buffer,0,buffer.Length);
size+=buffer.Length;
//writerealwavheader
longprePosition=fileStream.Position;
fileStream.Seek(0,SeekOrigin.Begin);
fileStream.Seek(prePosition,SeekOrigin.Begin);
fileStream.Flush();
publicstaticvoidWriteWavHeader(Streamstream,intdataLength)
using(MemoryStreammemStream=newMemoryStream(64))
intcbFormat=18;
WAVEFORMATEXformat=new