kinect语音识别 - CSDN
  • using Microsoft.Kinect; using Microsoft.Speech.AudioFormat; using Microsoft.Speech.Recognition; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; u
    using Microsoft.Kinect;
    using Microsoft.Speech.AudioFormat;
    using Microsoft.Speech.Recognition;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace KinectTest
    {
        class Program
        {
            static void Main(string[] args)
            {
                //创建KinectSensor对象,并获取对象赋值,启动设备
                KinectSensor KinectSensor = (from k in KinectSensor.KinectSensors
                                             where k.Status == KinectStatus.Connected
                                             select k).FirstOrDefault();
                if (KinectSensor == null)
                {
                    Console.WriteLine("No Kinect Connected\n" + "Press any key to continue.\n");
                    Console.ReadKey(true);
                    return;
                }
                KinectSensor.Start();
    
    
    
                //audioSource保存获取到的音频流
                KinectAudioSource audioSource = KinectSensor.AudioSource;
                //不采用回拨消除和抑制
                audioSource.EchoCancellationMode = EchoCancellationMode.None;
                //KienctAudioDMO不执行自动增益控制
                audioSource.AutomaticGainControlEnabled = false;
    
                //在Speech中创建语音识别引擎
                RecognizerInfo recognizerInfo= GetKinectRecognizer();
                
                //指定语音命令,创建并加载一个包含要识别词语的语法器
                using (var speechRecognitionEngine = new SpeechRecognitionEngine(recognizerInfo.Id))
                {
                    //Choices对象
                    var colors = new Choices();
                    colors.Add("red");
                    colors.Add("green");
                    colors.Add("blue");
    
                    //构造语法,语义与识别器的语义匹配
                    var grammatBuilder = new GrammarBuilder { Culture = recognizerInfo.Culture };
                    grammatBuilder.Append(colors);
                    var g = new Grammar(grammatBuilder);
    
                    //把定义好的语法元素加载到语音识别引擎中
                    speechRecognitionEngine.LoadGrammar(g);
    
                    //识别成功
                    speechRecognitionEngine.SpeechRecognized += SreSpeechRecognized;
                    speechRecognitionEngine.SpeechHypothesized += SreSpeechHypothesized;
                    //识别未成功
                    speechRecognitionEngine.SpeechRecognitionRejected += SreSpeechRecognitionRejected;
    
                    /*
                     * 识别命令:
                     * 在配置成功后,Speech将启动处理流程,引擎会自动识别出语法中的单词,
                     * 并根据识别的情况触发相应的事件
                     * 
                     * */
                    using (Stream s = audioSource.Start())
                    {
                        //指定音频源集齐特征
                        speechRecognitionEngine.SetInputToAudioStream(
                            s, new SpeechAudioFormatInfo(EncodingFormat.Pcm,
                            16000, 16, 1, 32000, 2, null));
    
                        Console.WriteLine(
                            "Recognizing speech. Say: 'red', 'green' or 'blue'. Press ENTER to stop");
    
                        //RecognizeAsync:指定异步识别,语音识别引擎会一直在后台运行,直至用户通过按键终止该进程
                        speechRecognitionEngine.RecognizeAsync(RecognizeMode.Multiple);
                        Console.ReadLine();
                        Console.WriteLine("Stopping recognizer ...");
                        //RecognizeAsyncStop:停止处理并关闭引擎
                        speechRecognitionEngine.RecognizeAsyncStop();
                    }
                }
            }
    
    
            private static RecognizerInfo GetKinectRecognizer()
            {
                
                /*
                 * SpeechRecognitionEngine提供一系列获取和管理语音识别引擎的方法
                 * InstalledRecognizers静态方法,返回一个语音识别器列表,包括当前系统上安装的所有语音识别器         
                 * RecognizerInfo类:表示语音识别引擎对象的相关信息:Name,Id,Culture          
                 * */
                foreach (RecognizerInfo recognizer in SpeechRecognitionEngine.InstalledRecognizers())
                {
                    string value;
                    recognizer.AdditionalInfo.TryGetValue("Kinect", out value);
                    if ("True".Equals(value, StringComparison.OrdinalIgnoreCase) && "en-US".Equals(recognizer.Culture.Name, StringComparison.OrdinalIgnoreCase))
                    {
                        return recognizer;
                    }
                }
                return null;                   
            }
    
            private static void SreSpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
            {
                Console.WriteLine("\nSpeech Rejected");
                if (e.Result != null)
                {
                    DumpRecordedAudio(e.Result.Audio);
                }
            }
    
    
            private static void SreSpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
            {
                Console.Write("\rSpeech Hypothesized: \t{0}", e.Result.Text);
            }
    
    
            private static void SreSpeechRecognized(object sender, SpeechRecognizedEventArgs e)
            {
                if (e.Result.Confidence >= 0.7)
                {
                    Console.WriteLine("\nSpeech Recognized: \t{0}\tConfidence:\t{1}",
                        e.Result.Text, e.Result.Confidence);
                }
                else
                {
                    Console.WriteLine("\nSpeech Recognized but confidence was too low: \t{0}",
                        e.Result.Confidence);
                    DumpRecordedAudio(e.Result.Audio);
                }
            }
    
            
            private static void DumpRecordedAudio(RecognizedAudio audio)
            {
                //将记录的单词写入wav文件
                if (audio == null) return;
    
                int fileId = 0;
                string filename;
                while (File.Exists((filename = "RetainedAudio_" + fileId + ".wav")))
                    fileId++;
    
                Console.WriteLine("\nWriting file: {0}", filename);
                using (var file = new FileStream(filename, System.IO.FileMode.CreateNew))
                    audio.WriteToWaveStream(file);
            }
        }
    }
    
    

    展开全文
  • Kinect的麦克风阵列在Kinect设备的下方。这一阵列由4个独立的水平分布在Kinect下方的麦克风组成。虽然每一个麦克风都捕获相同的音频信号,但是组成阵列可以探测到声音的来源方向。使得能够用来识别从某一个特定的...

    Kinect的麦克风阵列在Kinect设备的下方。这一阵列由4个独立的水平分布在Kinect下方的麦克风组成。虽然每一个麦克风都捕获相同的音频信号,但是组成阵列可以探测到声音的来源方向。使得能够用来识别从某一个特定的方向传来的声音。麦克风阵列捕获的音频数据流经过复杂的音频增强效果算法处理来移除不相关的背景噪音。所有这些复杂操作在Kinect硬件和Kinect SDK之间进行处理,这使得能够在一个大的空间范围内,即使人离麦克风一定的距离也能够进行语音命令的识别。

    在Kinect应用程序中,选择操作是最复杂和难以掌握的行为之一。Xbox360中最初的选择操作是通过将手放到特定的位置,然后保持一段时间。在《舞林大会》游戏中,通过一个短暂的停顿加上滑动操作来对选择操作进行了一点改进。这一改进也被应用在了Xbox的操作面板中。另外的对选择进行改进的操作包括某种特定的手势,如将胳膊举起来。

        这些问题,可以通过将语音识别指令和骨骼追踪系统结合起来产生一个复合的姿势来相对简单的解决:保持某一动作,然后通过语音执行。菜单的设计也可以通过首先展示菜单项,然后让用户说出菜单项的名称来进行选择-很多Xbox中的游戏已经使用了这种方式。可以预见,无论是程序开发者还是游戏公司,这种复合的解决方案在未来会越来越多的应用到新的交互方式中,而不用再像以前那样使用指然后点(point and click)这种方式来选择。


    麦克风阵列

    在C#中,Kinect SDK提供了对语音捕获DMO的封装。语音捕获DMO最初是被设计用来给麦克风阵列提供API来支持一些功能如回声消除(acoustic echo cancellation,AEC),自动增益控制(automatic gain control,AGC)和噪声抑制(noise suppression)。这些功能在SDK的音频控制类中可以找到。 Kinect SDK中音频处理对语音捕获DMO进行了简单封装,并专门针对Kinect传感器进行了性能优化。为了能够使用Kinect SDK进行语音识别,自动安装的类库包括:Speech Platform API, Speech Platform SDK和Kinect for Windows Runtime Language Pack。

    语音识别API能够简化操作系统自带的语音识别所需的类库。例如,如果你想通过普通的麦克风而不是Kinect麦克风阵列添加一些语音指令到桌面应用程序中去,可以使用也可以不使用Kinect SDK。

    Kinect for windows 运行语言包是一系列的语言模型,用来在Kinect SDK和语音识别API组件之间进行互操作。就像Kinect骨骼识别需要大量的计算模型来提供决策树信息来分析节点位置那样,语音识别API也需要复杂的模型来辅助解释从Kinect麦克风阵列接收到的语言模型。Kinect语言包提供了这些模型来优化语音指令的识别。

     

    Kinect中处理音频主要是通过KinectAudioSource这个对象来完成的。KinectAudioSource类的主要作用是从麦克风阵列中提取原始的或者经过处理的音频流。音频流可能会经过一系列的算法来处理以提高音频质量,这些处理包括:降噪、自动增益控制和回声消除。KinectAudioSource能够进行一些配置使得Kinect麦克风阵列可以以不同的模式进行工作。也能够用来探测从那个方向来的哪种音频信息最先达到麦克风以及用来强制麦克风阵列接受指定方向的音频信息。

    • 回声消除(acoustic echo cancellation, AEC) 当用户的声音从麦克风返回时,就会产生回声。最简单的例子就是用户在打电话时能够听到自己的声音,这些声音有一些延迟,会在对方那里重复一段时间。回声消除通过提取发声者的声音模式,然后根据这一模式从麦克风接收到的音频中挑选出特定的音频来消除回声。
    • 回声抑制(acoustic echo suppression, AES) 它是指通过一系列的算法来进一步消除AEC处理后所遗留的回声。
    • 自动增益控制(acoustic gain control, AGS) 它涉及到一些算法用来使用户的声音的振幅与时间保持一致。例如当用户靠近或者或远离麦克风时,声音会出现变得响亮或更柔和,AGC通过算法使得这一过程变得更加明显。
    • 波束成形(beamforming) 指的是模拟定向麦克风的算法技术。和只有一个麦克风不同,波速成形技术用于麦克风阵列中 (如Kinect 传感器上的麦克风阵列)使得麦克风阵列产生和使用多个固定麦克风的效果相同。
    • 中心削波(center clipping) 用来移除在单向传输中经AEC处理后残留的小的回声。
    • 帧尺寸(Frame Size) AEC算法处理PCM音频样本是是一帧一帧处理的。帧尺寸是样本中音频帧的大小。
    • 获取增益边界(Gain Bounding) 该技术确保麦克风有正确的增益级别。如果增益过高,获取到的信号可能过于饱和,会被剪切掉。这种剪切有非线性的效果,会使得AEC算法失败。如果增益过低,信噪比会比较低,也会使得AEC算法失败或者执行的不好。
    • 噪声填充(Noise Filling) 向中心削波移除了残留的回波信号后的部分信号中添加少量的噪音。和留下空白的沉默信号相比,这能够获得更好的用户体验。
    • 噪声抑制 (NS) 用于从麦克风接收到的音频信号中剔除非言语声音。通过删除背景噪音,实际讲话者的声音能够被麦克风更清楚更明确的捕获到。
    • Optibeam Kinect传感器从四个麦克风中能够获得11个波束。 这11个波束是逻辑结构,而四个通道是物理结构。Optibeam 是一种系统模式用来进行波束成形。
    • 信噪比(Signal-to-Noise Ratio,SNR) 信号噪声比用来度量语音信号和总体背景噪声的比例,信噪比越高越好。
    • 单通道(Single Channel) Kinect传感器有四个麦克风,因此支持4个通道,单通道是一种系统模式用来关闭波束成形。

     

    KinectAudioSource类提供一些对音频捕获多方面的较高层次的控制,虽然它并没有提供DMO中的所有功能。KinectAudioSource中用来调整音频处理的各种属性被称之为功能(features)属性。下表中列出了可以调整的功能属性。Kinect SDK早期的Beta版本视图提供了DMO中的所有功能以使得能够有更加强大的控制能力,但是这也极大的增加了复杂度。SDK的正式版本提取了DMO中的所有可能的配置然后将其封装为特征属性使得我们不用关心底层的配置细节。

    table1

    EchoCancellationMode是一个隐藏在不起眼的名称后面神奇的技术之一。他可能的设置如下表。为了适应AEC,需要给EchoCancellationSpeakerIndex属性赋一个int值来指定那一个用户的噪音需要控制。SDK会自动执行活动麦克风的发现和初始化。

    table2

    BeamAngleMode对底层的DMO系统模式和麦克风阵列属性进行了抽象封装。在DMO级别上,他决定了是由DMO还是应用程序进行波束成形。在这一基础上Kinect for Windows SDK提供了额外的一系列算法来进行波束成行。通常,可以将该属性设置为Adaptive,将这些复杂的操作交给SDK进行处理。

    table3

    自适应波速成形(Adaptive beamforming)能够发挥Kinect传感器的特性优势,根据骨骼追踪所找到的游戏者,从而找出正确的声音源。和骨骼追踪一样,Kinect的波束成形特性也能够使用手动模式,允许应用程序来设定要探测声音的方向。要使用Kinect传感器作为定向的麦克风,需要将波束角度模式设定为Manual然后设置KinectAudioSource的ManualBeamAngle属性。


    语音识别

    语音识别可以分为两类:对特定命令的识别(recognition of command)和对自由形式的语音的识别(recognition of free-form dictation)。自由形式的语音识别需要训练软件来识别特定的声音以提高识别精度。一般做法是让讲话人大声的朗读一系列的语料来使得软件能够识别讲话人声音的特征模式,然后根据这一特征模式来进行识别。

    而命令识别(Command recognition)则应用了不同的策略来提高识别精度。和必须识别说话人声音不同,命令识别限制了说话人所讲的词汇的范围。基于这一有限的范围,命令识别可以不需要熟悉讲话人的语音模式就可以推断出说话人想要说的内容。

    考虑到Kinect的特性,使用Kinect进行自由形式的语音识别没有多大意义。Kinect SDK的设计初衷是让大家能够简单容易的使用,因此,SDK提供了Microsoft.Speech类库来原生支持语音命令的识别。Microsoft.Speech类库是Microsoft语音识别技术的服务器版本。如果你想使用System.Speech类库中的语音识别能力,可以使用Windows操作系统内建的桌面版语音识别来通过Kinect的麦克风来建立一个自由语音识别系统。但是通过将Kinect的麦克风和System.Speech类库组合开发的自由语音识别系统的识别效果可能不会太好。这是因为Kinect for windows运行时语言包,能够适应从开放空间中的声音,而不是从麦克风发出的声音,这些语言模型在 System.Speech 中不能够使用。

    Microsoft.Speech类库的语音识别功能是通过SpeechRecognitionEngine对象提供的。SpeechRecognitionEngine类是语音识别的核心,它负责从Kinect传感器获取处理后的音频数据流,然后分析和解译这些数据流,然后匹配出最合适的语音命令。引擎给基本发声单元一定的权重,如果判断出发声包含特定待识别的命令,就通过事件进行进一步处理,如果不包含,直接丢掉这部分音频数据流。

    我们需要告诉SpeechRecognitionEngine从一个特定的称之为语法(grammars)的对象中进行查找。Grammar对象由一系列的单个单词或者词语组成。如果我们不关心短语的部分内容,可以使用语法对象中的通配符。例如,我们可能不会在意命令包含短语"an" apple或者"the" apple,语法中的通配符告诉识别引擎这两者都是可以接受的。此外,我们还可以添加一个称之为Choices的对象到语法中来。选择类(Choices)是通配符类(Wildcard)的一种,它可以包含多个值。但与通配符不同的是,我们可以指定可接受的值的顺序。例如如果我们想要识别“Give me some fruit”我们不关心fruit单词之前的内容,但是我们想将fruit替换为其它的值,如apple,orange或者banana等值。这个语法可以通过下面的代码来实现。Microsoft.Speech类库中提供了一个GrammarBuilder类来建立语法(grammars)

    var choices = new Choices();
    choices.Add("fruit");
    choices.Add("apple");
    choices.Add("orange");
    choices.Add("banana");
    
    var grammarBuilder = new GrammarBuilder();
    grammarBuilder.Append("give");
    grammarBuilder.Append("me");
    grammarBuilder.AppendWildcard();
    grammarBuilder.Append(choices);
    
    var grammar = new Grammar(grammarBuilder);

     

    语法中的单词不区分大小写,但是出于一致性考虑,要么都用大写,要么都用小写。

    语音识别引擎使用LoadGrammar方法将Grammars对象加载进来。语音识别引擎能够加载而且通常是加载多个语法对象。识别引擎有3个事件:SpeechHypothesized,SpeechRecognized和SpeechRecognitionRejected。 SpeechHypothesized事件是识别引擎在决定接受或者拒绝用户命令之前解释用户说话的内容。SpeechRecognitionRejected用来处理识别命令失败时需要执行的操作。SpeechRecognized是最重要的事件,他在引擎决定接受用户的语音命令时触发。该事件触发时,通过SpeechRecognizedEventArgs对象参数传递一些数据。SpeechRecognizedEventArgs类有一个Result属性,该属性描述如下:

    table4

    实例化SpeechRecognitionEngine对象需要执行一系列特定的步骤。首先,需要设置识别引擎的ID编号。当安装了服务器版本的Microsoft语音库时,名为Microsoft Lightweight Speech Recognizier的识别引擎有一个为SR_MS_ZXX_Lightweight_v10.0的ID值(这个值根据你所安装的语音库的不同而不同)。当安装了Kinect for Windows运行时语音库时,第二个ID为Server Speech Recognition Language-Kinect(en-US)的语音库可以使用。这是Kinect中我们可以使用的第二个识别语音库。下一步SpeechRecognitionEngine需要指定正确的识别语音库。由于第二个语音识别库的ID可能会在以后有所改变,我们需要使用模式匹配来找到这一ID。最后,语音识别引擎需要进行配置,以接收来自KinectAudioSource对象的音频数据流。下面是执行以上过程的样板代码片段。

    var source = new KinectAudioSource();
    Func<RecognizerInfo, bool> matchingFunc = r =>
    {
        String value;
        r.AdditionalInfo.TryGetValue("Kinect", out value);
        return "True".Equals(value, StringComparison.InvariantCultureIgnoreCase)
                && "en-US".Equals(r.Culture.Name, StringComparison.InvariantCultureIgnoreCase);
    };
    RecognizerInfo ri = SpeechRecognitionEngine.InstalledRecognizers().Where(matchingFunc).FirstOrDefault();
    var sre = new SpeechRecognitionEngine(ri.Id);
    
    KinectSensor.KinectSensors[0].Start();
    Stream s = source.Start();
    sre.SetInputToAudioStream(s,new SpeechAudioFormatInfo(EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null));
    sre.Recognize();

     

    SetInputToAudioStream方法的第二个参数用来设置从Kinect获取的音频数据流的格式。在上面的代码中,我们设置音频编码格式为Pulse Code Modulation(PCM),每秒接收16000个采样,每个样本占16位,只有1个通道,每秒中产生32000字节数据,块对齐值设置为2。

    Grammars加载到语音识别引擎后,引擎必须启动后才能进行识别,启动引擎有几种模式,可以使用同步或者异步模式启动。另外也可以识别一次或者继续识别从KinectAudioSource传来的多条语音命令。下表列出了开始语音识别的可选方法。

    table5

     


    获取音频数据

    虽然 KinectAudioSource 类的最主要作用是为语音识别引擎提供音频数据流,但是它也可以用于其他目的。他还能够用来录制 wav 文件。下面的示例将使用KinectAudioSource来开发一个音频录音机。使用这个项目作为录音机,读者可以修改Kinect sdk中KinectAudioSource的各个参数的默认值的来了解这些参数是如何控制音频数据流的产生

    使用音频数据流

    虽然使用的是Kinect的音频相关类,而不是视觉元素类,但是建立一个Kinect音频项目的过程大致是类似的。

    1. 创建一个名为KinectAudioRecorder的WPF应用项目。

    2. 添加对Microsoft.Kinect.dll和Microsoft.Speech.dll的引用。

    3. 在MainWindows中添加名为Play,Record和Stop三个按钮。

    4. 将主窗体的名称改为“Audio Recorder”

    在VS的设计视图中,界面看起来应该如下:

    pic1w

    令人遗憾的是,C#没有一个方法能够直接写入wav文件。为了能够帮助我们生成wav文件,我们使用下面自定义的RecorderHelper类,该类中有一个称之为WAVFORMATEX的结构,他是C++中对象转换过来的,用来方便我们对音频数据进行处理。该类中也有一个称之为IsRecording的属性来使得我们可以停止录制。类的基本结构,以及WAVFORMATEX的结构和属性如下。我们也需要初始化一个私有名为buffer字节数组用来缓存我们从Kinect接收到的音频数据流。

    class RecorderHelper
    {
        static byte[] buffer = new byte[4096];
        static bool _isRecording;
    
        public static bool IsRecording
        {
            get
            {
                return _isRecording;
            }
            set
            {
                _isRecording = value;
            }
        }
    
        struct WAVEFORMATEX
        {
            public ushort wFormatTag;
            public ushort nChannels;
            public uint nSamplesPerSec;
            public uint nAvgBytesPerSec;
            public ushort nBlockAlign;
            public ushort wBitsPerSample;
            public ushort cbSize;
        }
    }

     

    为了完成这个帮助类,我们还需要添加三个方法:WriteString,WriteWavHeader和WriteWavFile方法。WriteWavFile方法如下,方法接受KinectAudioSource和FileStream对象,从KinectAudioSource对象中我们可以获取音频数据,我们使用FileStream来写入数据。方法开始写入一个假的头文件,然后读取Kinect中的音频数据流,然后填充FileStream对象,直到_isRecoding属性被设置为false。然后检查已经写入到文件中的数据流大小,用这个值来改写之前写入的文件头。

    public static void WriteWavFile(KinectAudioSource source, FileStream fileStream)
    {
        var size = 0; 
        //write wav header placeholder
        WriteWavHeader(fileStream, size);
        using (var audioStream = source.Start())
        {
            //chunk audio stream to file
            while (audioStream.Read(buffer, 0, buffer.Length) > 0 && _isRecording)
            {
                fileStream.Write(buffer, 0, buffer.Length);
                size += buffer.Length;
    
            }
        }
    
        //write real wav header
        long prePosition = fileStream.Position;
        fileStream.Seek(0, SeekOrigin.Begin);
        WriteWavHeader(fileStream, size);
        fileStream.Seek(prePosition, SeekOrigin.Begin);
        fileStream.Flush();
    }
    
    public static void WriteWavHeader(Stream stream, int dataLength)
    {
        using (MemoryStream memStream = new MemoryStream(64))
        {
            int cbFormat = 18;
            WAVEFORMATEX format = new WAVEFORMATEX()
            {
                wFormatTag = 1,
                nChannels = 1,
                nSamplesPerSec = 16000,
                nAvgBytesPerSec = 32000,
                nBlockAlign = 2,
                wBitsPerSample = 16,
                cbSize = 0
            };
    
            using (var bw = new BinaryWriter(memStream))
            {
    
                WriteString(memStream, "RIFF");
                bw.Write(dataLength + cbFormat + 4);
                WriteString(memStream, "WAVE");
                WriteString(memStream, "fmt ");
                bw.Write(cbFormat);
    
                bw.Write(format.wFormatTag);
                bw.Write(format.nChannels);
                bw.Write(format.nSamplesPerSec);
                bw.Write(format.nAvgBytesPerSec);
                bw.Write(format.nBlockAlign);
                bw.Write(format.wBitsPerSample);
                bw.Write(format.cbSize);
    
                WriteString(memStream, "data");
                bw.Write(dataLength);
                memStream.WriteTo(stream);
            }
        }
    }
    
    static void WriteString(Stream stream, string s)
    {
        byte[] bytes = Encoding.ASCII.GetBytes(s);
        stream.Write(bytes, 0, bytes.Length);
    }

     

    使用该帮助方法,我们可以开始建立和配置KinectAudioSource对象。首先添加一个私有的_isPlaying 布尔值来保存是否我们想要播放录制的wav文件。这能够帮助我们避免录音和播放功能同事发生。除此之外,还添加了一个MediaPlayer对象用来播放录制好的wav文件。_recodingFileName用来保存最近录制好的音频文件的名称。代码如下所示,我们添加了几个属性来关闭和开启这三个按钮,他们是:IsPlaying,IsRecording,IsPlayingEnabled,IsRecordingEnabled和IsStopEnabled。为了使得这些对象可以被绑定,我们使MainWindows对象实现INotifyPropertyChanged接口,然后添加一个NotifyPropertyChanged事件以及一个OnNotifyPropertyChanged帮助方法。

        在设置各种属性的逻辑中,先判断IsRecording属性,如果为false,再设置IsPlayingEnabled属性。同样的先判断IsPlaying属性为是否false,然后在设置IsRecordingEnabled属性。前端的XAML代码如下:

    在设置各种属性的逻辑中,先判断IsRecording属性,如果为false,再设置IsPlayingEnabled属性。同样的先判断IsPlaying属性为是否false,然后在设置IsRecordingEnabled属性。前端的XAML代码如下:

    <Window x:Class="KinectRecordAudio.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Audio Recorder" Height="226" Width="405">
        <Grid Width="369" Height="170">
            <Button Content="Play" Height="44" HorizontalAlignment="Left" Margin="12,13,0,0" Name="button1" VerticalAlignment="Top" Width="114" Click="button1_Click"  IsEnabled="{Binding IsPlayingEnabled}" FontSize="18"></Button>
            <Button Content="Record" Height="44" HorizontalAlignment="Left" Margin="132,13,0,0" Name="button2" VerticalAlignment="Top" Width="110" Click="button2_Click"  IsEnabled="{Binding IsRecordingEnabled}" FontSize="18"/>
            <Button Content="Stop" Height="44" HorizontalAlignment="Left" Margin="248,13,0,0" Name="button3" VerticalAlignment="Top" Width="107" Click="button3_Click"  IsEnabled="{Binding IsStopEnabled}" FontSize="18"/>
        </Grid>
    </Window>

     

    MainWindow构造函数中实例化一个MediaPlayer对象,将其存储到_mediaPlayer变量中。因为Media Player对象在其自己的线程中,我们需要捕获播放完成的时间,来重置所有按钮的状态。另外,我们使用WPF中的技巧来使得我们的MainWindow绑定IsPlayingEnabled以及属性。我们将MainPage的DataContext属性设置给自己。这是提高代码可读性的一个捷径,虽然典型的做法是将这些绑定属性放置到各自单独的类中。

    public MainWindow()
    {
        InitializeComponent();
        this.Loaded += delegate { KinectSensor.KinectSensors[0].Start(); };
        _mplayer = new MediaPlayer();
        _mplayer.MediaEnded += delegate { _mplayer.Close(); IsPlaying = false; };
        this.DataContext = this;
    }

     

    现在,我们准备好了实例化KinectAudioSource类,并将其传递给之前创建的RecordHelper类。为了安全性,我们给RecordKinectAudio方法添加了锁。在加锁之前我们将IsRunning属性设置为true,方法结束后,将该属性设置回false。

    private object lockObj = new object();
    private void RecordKinectAudio()
    {
        lock (lockObj)
        {
            IsRecording = true;
    
            var source = CreateAudioSource();
    
            var time = DateTime.Now.ToString("hhmmss");
            _recordingFileName = time + ".wav";
            using (var fileStream =
            new FileStream(_recordingFileName, FileMode.Create))
            {
                RecorderHelper.WriteWavFile(source, fileStream);
            }
    
            IsRecording = false;
        }
    
    }
    
    private KinectAudioSource CreateAudioSource()
    {
        var source = KinectSensor.KinectSensors[0].AudioSource;
        source.BeamAngleMode = BeamAngleMode.Adaptive;
        source.NoiseSuppression = _isNoiseSuppressionOn;
        source.AutomaticGainControlEnabled = _isAutomaticGainOn;
    
        if (IsAECOn)
        {
            source.EchoCancellationMode = EchoCancellationMode.CancellationOnly;
            source.AutomaticGainControlEnabled = false;
            IsAutomaticGainOn = false;
            source.EchoCancellationSpeakerIndex = 0;
        }
    
        return source;
    }

     

    为了保证不对之前录制好的音频文件进行再次写入,我们在每个音频文件录制完了之后,使用当前的时间位文件名创建一个新的音频文件。最后一步就是录制和播放按钮调用的方法。UI界面上的按钮调用Play_Click和Record_Click方法。这些方法只是调用实际对象的Play和Record方法。需要注意的是下面的Record方法,重新开启了一个新的线程来执行RecordKinectAudio方法。

    private void Play()
    {
        IsPlaying = true;
        _mplayer.Open(new Uri(_recordingFileName, UriKind.Relative));
        _mplayer.Play();
    }
    
    private void Record()
    {
        Thread thread = new Thread(new ThreadStart(RecordKinectAudio));
        thread.Priority = ThreadPriority.Highest;
        thread.Start();
    }
    
    private void Stop()
    {
        KinectSensor.KinectSensors[0].AudioSource.Stop();
        IsRecording = false;
    }

     


    对音频数据进行处理

     

    我们可以将之前的例子进行扩展,添加一些属性配置。在本节中,我们添加对噪音抑制和自动增益属性开关的控制。

    pic2w

    <CheckBox Content="Noise Suppression" Height="16" HorizontalAlignment="Left" Margin="16,77,0,0"  VerticalAlignment="Top" Width="142" IsChecked="{Binding IsNoiseSuppressionOn}" />
    <CheckBox Content="Automatic Gain Control" Height="16" HorizontalAlignment="Left" Margin="16,104,0,0"  VerticalAlignment="Top"  IsChecked="{Binding IsAutomaticGainOn}"/>
    <CheckBox Content="AEC" Height="44" HorizontalAlignment="Left" IsChecked="{Binding IsAECOn}" Margin="16,129,0,0"  VerticalAlignment="Top" />
    private KinectAudioSource CreateAudioSource()
    {
        var source = KinectSensor.KinectSensors[0].AudioSource;
        source.BeamAngleMode = BeamAngleMode.Adaptive;
        source.NoiseSuppression = _isNoiseSuppressionOn;
        source.AutomaticGainControlEnabled = _isAutomaticGainOn;
        return source;
    }

     


    去除回声

    回声消除不单是KinectAudioSource类的一个属性,他还是Kinect的核心技术之一。要测试这一特性可能会比之前的测试要复杂。

    要测试AEC的效果,需要在界面上添加一个CheckBox。然后创建一个IsAECOn属性来设置和保存这个设置的值。最后将CheckBox的IsCheck属性绑定到IsAECOn属性上。

    if (IsAECOn)
    {
        source.EchoCancellationMode = EchoCancellationMode.CancellationOnly;
        source.AutomaticGainControlEnabled = false;
        IsAutomaticGainOn = false;
        source.EchoCancellationSpeakerIndex = 0;
    }

    上面的代码中SystemMode属性必须设置为Optibeam或者echo cancelation。需要关闭自动增益控制,因为在AEC下面不会起作用。另外,如果AutomaticGainOn属性不为false的话,需要将其设置为false。这样界面上的UI展现逻辑就有一点从冲突。AEC配置接下来需要找到我们使用的麦克风,以及正在使用麦克风的讲话人,以使得AEC算法能够从数据流中提取到需要的音频数据。


    转载于:https://www.cnblogs.com/sprint1989/p/3854211.html

    展开全文
  • Kinect开发笔记之二——语音识别

    千次阅读 2016-04-21 20:45:46
    个人觉得Kinect语音识别还是做得不错的,不过支持的7国语言居然没有汉语,所以只能用英语了。其实Kinect的demo里面有一个语音识别的demo,是控制一只龟运动的,所以我也没必要在说太多了,只是稍微说一些注意地方...

    个人觉得Kinect的语音识别还是做得不错的,不过支持的7国语言居然没有汉语,所以只能用英语了。其实Kinect的demo里面有一个语音识别的demo,是控制一只龟运动的,所以我也没必要在说太多了,只是稍微说一些注意地方好了。

    • 1、无论是安装sdk好,还是runtime platform最好下一步到底,不要改路径了,装C盘就C盘就是了。

    • 2、如果你想除了语音识别,还想TTS朗读的话,建议不要用微软建议的Microsoft Speech Platform 11,是朗读不出声的,我用的是win10,用VS2013 自带的sdk8.1就行了。

    • 3、下面的就是关键代码:

    void SpeechThread::ProcessSpeech()
    {
        const float ConfidenceThreshold = 0.3f; //阈值,大一点就准确一点
        SPEVENT curEvent = { SPEI_UNDEFINED, SPET_LPARAM_IS_UNDEFINED, 0, 0, 0, 0 };
        ULONG fetched = 0;
        HRESULT hr = S_OK;
    
        m_pSpeechContext->GetEvents(1, &curEvent, &fetched);
        while (fetched > 0)
        {
            switch (curEvent.eEventId)
            {
            case SPEI_RECOGNITION:
                if (SPET_LPARAM_IS_OBJECT == curEvent.elParamType)
                {
                    // this is an ISpRecoResult
                    ISpRecoResult* result = reinterpret_cast<ISpRecoResult*>(curEvent.lParam);
                    SPPHRASE* pPhrase = NULL;
    
                    hr = result->GetPhrase(&pPhrase);
                    if (SUCCEEDED(hr))
                    {
                        if ((pPhrase->pProperties != NULL) && (pPhrase->pProperties->pFirstChild != NULL))
                        {
                            const SPPHRASEPROPERTY* pSemanticTag = pPhrase->pProperties->pFirstChild;
                            if (pSemanticTag->SREngineConfidence > ConfidenceThreshold)
                            {
                                wcout << pSemanticTag->pszValue << endl;
                                CString str;
                                str=pSemanticTag->pszValue;
                                CWnd *pWnd = CWnd::FindWindow(NULL, _T("SpeechIdentify"));//获取目标窗口
                                pWnd->SendMessage(SPEECH_EVENT, 0, (LPARAM)str.AllocSysString());//发送消息
                            }
                        }
                        ::CoTaskMemFree(pPhrase);
                    }
                }
                break;
            }
    
            m_pSpeechContext->GetEvents(1, &curEvent, &fetched);
        }
    
        return;
    }
    • 4、因为微软的demo是win32的,所以你要移植到MFC必须开线程。
    展开全文
  • 上一篇文章介绍了Kinect语音识别的基本概念,以及一些语音处理方面的术语。在此基础上使用Kinect麦克风阵列来进行音频录制的例子说明了Kinect中音频处理的核心对象及其配置。本文将继续介绍Kinect中的语音识别,并...

     上一篇文章介绍了Kinect中语音识别的基本概念,以及一些语音处理方面的术语。在此基础上使用Kinect麦克风阵列来进行音频录制的例子说明了Kinect中音频处理的核心对象及其配置。本文将继续介绍Kinect中的语音识别,并以两个小例子来展示语音识别中的方向识别和语音命令识别。

     

    1. 使用定向麦克风进行波速追踪(Beam Tracking for a Directional Microphone)

     

        可以使用这4个麦克风来模拟定向麦克风产生的效果,这个过程称之为波束追踪(beam tracking),为此我们新建一个WPF项目,过程如下:

    1. 创建一个名为KinectFindAudioDirection的WPF项目。

    2. 添加对Microsoft.Kinect.dll和Microsoft.Speech.dll的引用。

    3. 将主窗体的名称改为“Find Audio Direction”

    4. 在主窗体中绘制一个垂直的细长矩形。

        界面上的细长矩形用来指示某一时刻探测到的说话者的语音方向。矩形有一个旋转变换,在垂直轴上左右摆动,以表示声音的不同来源方向。前端页面代码:

    <Rectangle Fill="#1BA78B" HorizontalAlignment="Left" Margin="240,41,0,39" Stroke="Black" Width="10" RenderTransformOrigin="0.5,0">
        <Rectangle.RenderTransform>
            <TransformGroup>
                <ScaleTransform/>
                <SkewTransform/>
                <RotateTransform Angle="{Binding BeamAngle}"/>
                <TranslateTransform/>
            </TransformGroup>
        </Rectangle.RenderTransform>
    </Rectangle>
    

        pic1

          上图是程序的UI界面。后台逻辑代码和之前的例子大部分都是相同的。首先实例化一个KinectAudioSource对象,然后将主窗体的DataContext赋值给本身。将BeamAngleMode设置为Adaptive,使得能够自动追踪说话者的声音。我们需要编写KinectAudioSource对象的BeamChanged事件对应的处理方法。当用户的说话时,位置发生变化时就会触发该事件。我们需要创建一个名为BeamAngle的属性,使得矩形的RotateTransform可以绑定这个属性。

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
            this.Loaded += delegate { ListenForBeamChanges(); };
        }
    
        private KinectAudioSource CreateAudioSource()
        {
            var source = KinectSensor.KinectSensors[0].AudioSource;
            source.NoiseSuppression = true;
            source.AutomaticGainControlEnabled = true;
            source.BeamAngleMode = BeamAngleMode.Adaptive;
            return source;
        }
    
        private void ListenForBeamChanges()
        {
            KinectSensor.KinectSensors[0].Start();
            var audioSource = CreateAudioSource();
            audioSource.BeamAngleChanged += audioSource_BeamAngleChanged;
            audioSource.Start();
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        private void OnPropertyChanged(string propName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    
        private double _beamAngle;
        public double BeamAngle
        {
            get { return _beamAngle; }
            set
            {
                _beamAngle = value;
                OnPropertyChanged("BeamAngle");
            }
        }
    }

        以上代码中,还需要对BeamChanged事件编写对应的处理方法。每次当波束的方向发生改变时,就更改BeamAngle的属性。SDK中使用弧度表示角度。所以在事件处理方法中我们需要将弧度换成度。为了能达到说话者移到左边,矩形条也能够向左边移动的效果,我们需要将角度乘以一个-1。代码如下:

    void audioSource_BeamAngleChanged(object sender, BeamAngleChangedEventArgs e)
    {
        BeamAngle = -1 * e.Angle;
    }

        运行程序,然后在房间里不同地方走动,可以看到矩形条会根据你的位置左右摆动。

    pic2w

     

    2. 语音命令识别

     

        在这一部分,我们将会结合KinectAudioSource和SpeechRecognitionEngine来演示语音命令识别的强大功能。为了展示语音命令能够和骨骼追踪高效结合,我们会使用语音命令向窗体上绘制图形,并使用命令移动这些图形到光标的位置。命令类似如下:

    Create a yellow circle, there.

    Create a cyan triangle, there.

    Put a magenta square, there.

    Create a blue diamond, there.

    Move that ... there.

    Put that ... there.

    Move that ... below that.

    Move that ... west of the diamond.

    Put a large green circle ... there.

     

    程序界面大致如下:

    pic4w

        和之前的应用程序一样,首先创建一下项目的基本结构:

    1. 创建一个名为KinectPutThatThere的WPF项目。

    2. 添加对Microsoft.Kinect.dll和Microsoft.Speech.dll的引用。

    3. 将主窗体的名称改为“Put That There”

    4. 添加一个名为CrossHairs.xaml的用户自定义控件。

        CrossHair用户控件简单的以十字光标形式显示当前用户右手的位置。下面的代码显示了这个自定义控件的XAML文件。注意到对象于容器有一定的偏移使得十字光标的中心能够处于Grid的零点。

    <Grid Height="50" Width="50" RenderTransformOrigin="0.5,0.5">
        <Grid.RenderTransform>
            <TransformGroup>
                <ScaleTransform/>
                <SkewTransform/>
                <RotateTransform/>
                <TranslateTransform X="-25" Y="-25"/>
            </TransformGroup>
        </Grid.RenderTransform>
        <Rectangle Fill="#FFF4F4F5" Margin="22,0,20,0" Stroke="#FFF4F4F5"/>
        <Rectangle Fill="#FFF4F4F5" Margin="0,22,0,21" Stroke="#FFF4F4F5"/>  
    </Grid>
    

         在应用程序的主窗体中,将根节点从grid对象改为canvas对象。Canvas对象使得将十字光标使用动画滑动到手的位置比较容易。在主窗体上添加一个CrossHairs自定义控件。在下面的代码中,我们可以看到将Canvas对象嵌套在了一个Viewbox控件中。这是一个比较老的处理不同屏幕分辨率的技巧。ViewBox控件会自动的将内容进行缩放以适应实际屏幕的大小。设置MainWindows的背景色,并将Canvas的颜色设置为黑色。然后在Canvas的底部添加两个标签。一个标签用来显示SpeechRecognitionEngine将要处理的语音指令,另一个标签显示匹配正确的置信度。CrossHair自定义控件绑定了HandTop和HandLeft属性。两个标签分别绑定了HypothesizedText和Confidence属性。代码如下:

    <Window x:Class="KinectPutThatThere.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:KinectPutThatThere"
            Title="Put That There"  Background="Black">
        <Viewbox>
            <Canvas x:Name="MainStage" Height="1080" Width="1920" Background="Black" VerticalAlignment="Bottom">
                <local:CrossHairs Canvas.Top="{Binding HandTop}" Canvas.Left="{Binding HandLeft}" />
                    <Label  Foreground="White"  Content="{Binding HypothesizedText}" Height="55"   FontSize="32" Width="965"  Canvas.Left="115" Canvas.Top="1025" />
                <Label Foreground="Green" Content="{Binding Confidence}" Height="55"  Width="114" FontSize="32" Canvas.Left="0" Canvas.Top="1025"  />
            </Canvas>
        </Viewbox>
    </Window>
    

       在后台逻辑代码中,让MainWindows对象实现INofityPropertyChanged事件并添加OnPropertyChanged帮助方法。我们将创建4个属性用来为前台UI界面进行绑定。

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private double _handLeft;
        public double HandLeft
        {
            get { return _handLeft; }
            set
            {
                _handLeft = value;
                OnPropertyChanged("HandLeft");
            }
    
        }
    
        private double _handTop;
        public double HandTop
        {
            get { return _handTop; }
            set
            {
                _handTop = value;
                OnPropertyChanged("HandTop");
            }
        }
    
        private string _hypothesizedText;
        public string HypothesizedText
        {
            get { return _hypothesizedText; }
            set
            {
                _hypothesizedText = value;
                OnPropertyChanged("HypothesizedText");
            }
        }
    
        private string _confidence;
        public string Confidence
        {
            get { return _confidence; }
            set
            {
                _confidence = value;
                OnPropertyChanged("Confidence");
            }
        }
    
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
    
        }
    }

    添加CreateAudioSource方法,在该方法中,将KinectAudioSource对象的AutoGainControlEnabled的属性设置为false。

     

    private KinectAudioSource CreateAudioSource()
    {
        var source = KinectSensor.KinectSensors[0].AudioSource;
        source.AutomaticGainControlEnabled = false;
        source.EchoCancellationMode = EchoCancellationMode.None;
        return source;
    }

        接下来实现骨骼追踪部分逻辑来获取右手的坐标,相信看完骨骼追踪那两篇文章后这部分的代码应该会比较熟悉。首先创建一个私有字段_kinectSensor来保存当前的KienctSensor对象,同时创建SpeechRecognitionEngine对象。在窗体的构造函数中,对这几个变量进行初始化。例外注册骨骼追踪系统的Skeleton事件并将主窗体的DataContext对象赋给自己。

    KinectSensor _kinectSensor;
    SpeechRecognitionEngine _sre;
    KinectAudioSource _source;
    
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
        this.Unloaded += delegate
        {
            _kinectSensor.SkeletonStream.Disable();
            _sre.RecognizeAsyncCancel();
            _sre.RecognizeAsyncStop();
            _sre.Dispose();
        };
        this.Loaded += delegate
        {
            _kinectSensor = KinectSensor.KinectSensors[0];
            _kinectSensor.SkeletonStream.Enable(new TransformSmoothParameters()
            {
                Correction = 0.5f,
                JitterRadius = 0.05f,
                MaxDeviationRadius = 0.04f,
                Smoothing = 0.5f
            });
            _kinectSensor.SkeletonFrameReady += nui_SkeletonFrameReady;
            _kinectSensor.Start();
            StartSpeechRecognition();
        };
    }

          在上面的代码中,我们添加了一些TransformSmoothParameters参数来使得骨骼追踪更加平滑。nui_SkeletonFrameReady方法如下。方式使用骨骼追踪数据来获取我们感兴趣的右手的关节点位置。这部分代码和之前文章中的类似。大致流程是:遍历当前处在追踪状态下的骨骼信息。然后找到右手关节点的矢量信息,然后使用SkeletonToDepthImage来获取相对于屏幕尺寸的X,Y坐标信息。

    void nui_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
    {
        using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())
        {
            if (skeletonFrame == null)
                return;
    
            var skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength];
            skeletonFrame.CopySkeletonDataTo(skeletons);
            foreach (Skeleton skeletonData in skeletons)
            {
                if (skeletonData.TrackingState == SkeletonTrackingState.Tracked)
                {
                    Microsoft.Kinect.SkeletonPoint rightHandVec = skeletonData.Joints[JointType.HandRight].Position;
                    var depthPoint = _kinectSensor.MapSkeletonPointToDepth(rightHandVec
                        , DepthImageFormat.Resolution640x480Fps30);
                    HandTop = depthPoint.Y * this.MainStage.ActualHeight / 480;
                    HandLeft = depthPoint.X * this.MainStage.ActualWidth / 640;
                }
            }
        }
    }

        上面是多有设计到手部追踪的代码。一旦我们设置好HandTop和HandLeft属性后,UI界面上的十字光标位置就会自动更新。

        接下来我们需要实现语音识别部分的逻辑。SpeechRecognitionEngine中的StartSpeechRecognition方法必须找到正确的语音识别库来进行语音识别。下面的代码展示了如何设置语音识别库预计如何将KinectAudioSource传递给语音识别引起。我们还添加了SpeechRecognized,SpeechHypothesized以及SpeechRejected事件对应的方法。SetInputToAudioStream中的参数和前篇文章中的含义一样,这里不多解释了。注意到SpeechRecognitionEngine和KinectAudioSource都是Disposable类型,因此在整个应用程序的周期内,我们要保证这两个对象都处于打开状态。

    private void StartSpeechRecognition()
    {
        _source = CreateAudioSource();
    
        Func<RecognizerInfo, bool> matchingFunc = r =>
        {
            string value;
            r.AdditionalInfo.TryGetValue("Kinect", out value);
            return "True".Equals(value, StringComparison.InvariantCultureIgnoreCase)
                && "en-US".Equals(r.Culture.Name, StringComparison.InvariantCultureIgnoreCase);
        };
        RecognizerInfo ri = SpeechRecognitionEngine.InstalledRecognizers().Where(matchingFunc).FirstOrDefault();
    
        _sre = new SpeechRecognitionEngine(ri.Id);
        CreateGrammars(ri);
        _sre.SpeechRecognized += sre_SpeechRecognized;
        _sre.SpeechHypothesized += sre_SpeechHypothesized;
        _sre.SpeechRecognitionRejected += sre_SpeechRecognitionRejected;
    
        Stream s = _source.Start();
        _sre.SetInputToAudioStream(s,
                                    new SpeechAudioFormatInfo(
                                        EncodingFormat.Pcm, 16000, 16, 1,
                                        32000, 2, null));
        _sre.RecognizeAsync(RecognizeMode.Multiple);
    }

        要完成程序逻辑部分,我们还需要处理语音识别时间以及语音逻辑部分,以使得引擎能够直到如何处理和执行我们的语音命令。SpeechHypothesized以及SpeechRejected事件代码如下,这两个事件的逻辑很简单,就是更新UI界面上的label。SpeechRecognized事件有点复杂,他负责处理传进去的语音指令,并对识别出的指令执行相应的操作。另外,该事件还负责创建一些GUI对象(实际就是命令模式),我们必须使用Dispatcher对象来发挥InterpretCommand到主UI线程中来。

    void sre_SpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
    {
        HypothesizedText += " Rejected";
        Confidence = Math.Round(e.Result.Confidence, 2).ToString();
    }
    
    void sre_SpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
    {
        HypothesizedText = e.Result.Text;
    }
    
    void sre_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
    {
        Dispatcher.BeginInvoke(new Action<SpeechRecognizedEventArgs>(InterpretCommand), e);
    }

        现在到了程序核心的地方。创建语法逻辑并对其进行解析。本例中的程序识别普通的以“put”或者“create”开头的命令。前面是什么我们不关心,紧接着应该是一个颜色,然后是一种形状,最后一个词应该是“there”。下面的代码显示了创建的语法。

    private void CreateGrammars(RecognizerInfo ri)
    {
        var colors = new Choices();
        colors.Add("cyan");
        colors.Add("yellow");
        colors.Add("magenta");
        colors.Add("blue");
        colors.Add("green");
        colors.Add("red");
    
        var create = new Choices();
        create.Add("create");
        create.Add("put");
    
        var shapes = new Choices();
        shapes.Add("circle");
        shapes.Add("triangle");
        shapes.Add("square");
        shapes.Add("diamond");
    
        var gb = new GrammarBuilder();
        gb.Culture = ri.Culture;
        gb.Append(create);
        gb.AppendWildcard();
        gb.Append(colors);
        gb.Append(shapes);
        gb.Append("there");
    
        var g = new Grammar(gb);
        _sre.LoadGrammar(g);
    
        var q = new GrammarBuilder{ Culture = ri.Culture };
        q.Append("quit application");
        var quit = new Grammar(q);
    
        _sre.LoadGrammar(quit);
    }

        上面的代码中,我们首先创建一个Choices对象,这个对象会在命令解析中用到。在程序中我们需要颜色和形状对象。另外,第一个单词是“put”或者“create”,因此我们也创建Choices对象。然后使用GrammarBuilder类将这些对象组合到一起。首先是”put”或者“create”然后是一个占位符,因为我们不关心内容,然后是一个颜色Choices对象,然后是一个形状Choices对象,最后是一个“there”单词。

        我们将这些语法规则加载进语音识别引擎。同时我们也需要有一个命令来停止语音识别引擎。因此我们创建了第二个语法对象,这个对象只有一个”Quit”命令。然后也将这个语法规则加载到引擎中。

        一旦识别引擎确定了要识别的语法,真正的识别工作就开始了。被识别的句子必须被解译,出别出来想要的指令后,我们必须决定如何进行下一步处理。下面的代码展示了如何处理识别出的命令,以及如何根据特定的指令来讲图形元素绘制到UI界面上去。

    private void InterpretCommand(SpeechRecognizedEventArgs e)
    {
        var result = e.Result;
        Confidence = Math.Round(result.Confidence, 2).ToString();
        if (result.Confidence < 95 && result.Words[0].Text == "quit" && result.Words[1].Text == "application")
        {
            this.Close();
        }
        if (result.Words[0].Text == "put" || result.Words[0].Text == "create")
        {
            var colorString = result.Words[2].Text;
            Color color;
            switch (colorString)
            {
                case "cyan": color = Colors.Cyan;
                    break;
                case "yellow": color = Colors.Yellow;
                    break;
                case "magenta": color = Colors.Magenta;
                    break;
                case "blue": color = Colors.Blue;
                    break;
                case "green": color = Colors.Green;
                    break;
                case "red": color = Colors.Red;
                    break;
                default:
                    return;
            }
    
            var shapeString = result.Words[3].Text;
            Shape shape;
            switch (shapeString)
            {
                case "circle":
                    shape = new Ellipse();
                    shape.Width = 150;
                    shape.Height = 150;
                    break;
                case "square":
                    shape = new Rectangle();
                    shape.Width = 150;
                    shape.Height = 150;
                    break;
                case "triangle":
                    var poly = new Polygon();
                    poly.Points.Add(new Point(0, 0));
                    poly.Points.Add(new Point(150, 0));
                    poly.Points.Add(new Point(75, -150));
                    shape = poly;
                    break;
                case "diamond":
                    var poly2 = new Polygon();
                    poly2.Points.Add(new Point(0, 0));
                    poly2.Points.Add(new Point(75, 150));
                    poly2.Points.Add(new Point(150, 0));
                    poly2.Points.Add(new Point(75, -150));
                    shape = poly2;
                    break;
                default:
                    return;
            }
            shape.SetValue(Canvas.LeftProperty, HandLeft);
            shape.SetValue(Canvas.TopProperty, HandTop);
            shape.Fill = new SolidColorBrush(color);
            MainStage.Children.Add(shape);
        }
    }

           方法中,我们首先检查语句识别出的单词是否是”Quit”如果是的,紧接着判断第二个单词是不是”application”如果两个条件都满足了,就不进行绘制图形,直接返回。如果有一个条件不满足,就继续执行下一步。

         InterpretCommand方法然后判断第一个单词是否是“create”或者“put”,如果不是这两个单词开头就什么也不执行。如果是的,就判断第三个单词,并根据识别出来的颜色创建对象。如果第三个单词没有正确识别,应用程序也停止处理。否则,程序判断第四个单词,根据接收到的命令创建对应的形状。到这一步,基本的逻辑已经完成,最后第五个单词用来确定整个命令是否正确。命令处理完了之后,将当前受的X,Y坐标赋给创建好的对象的位置。

        运行程序后,按照语法设定的规则就可以通过语音以及骨骼追踪协作来创建对象了。

     

    pic3w

     

        有一点遗憾的是,Kinect的语音识别目前不支持中文,即使在5月份发布的SDK1.5版本增加的识别语言种类中也没有中文,也有可能是因为目前Xbox和kinect没有在中国大陆销售的原因吧。所以大家只有用英语进行测试了。

     

    3. 结语

     

        本文接上文,用一个显示语音来源方向的例子展示了语音识别中的方向识别。用一个简单的语音识别结合骨骼追踪来演示了如何根据语音命令来执行一系列操作。在语音命令识别中详细介绍了语音识别语法对象逻辑的建立,以及语音识别引擎相关事件的处理。本文代码点击此处下载,希望本文对您了解Kinect SDK中的语音识别有所帮助。

         下文将介绍在Kinect开发中可能会用到的一些第三方类库和开发工具,敬请期待!

    展开全文
  • 1、语音识别domo场景直接运行会报错,如下图,需要安装MS Speech Platform Runtime v11下载地址https://www.microsoft.com/en-us/download/details.aspx?id=27225 最好64位和32位都下载安装, 和Kinect for Windows...
  • (转载请注明出处) 使用SDK: Kinect for Windows SDK v2.0 public preview
  • Console.WriteLine("No Kinect Connected\n" + "Press any key to continue.\n"); Console.ReadKey(true); return; } KinectSensor.Start(); KinectAudioSource audioSource = KinectSensor.AudioSource; ...
  • 上一篇文章介绍了Kinect语音识别的基本概念,以及一些语音处理方面的术语。在此基础上使用Kinect麦克风阵列来进行音频录制的例子说明了Kinect中音频处理的核心对象及其配置。本文将继续介绍Kinect中的语音识别,并...
  • Kinect for windows提供了语音识别的能力,它靠Kinect的语音采集流进行识别的,这是建立在微软的语音识虽库的基础上的,关于微软语音识别可以参考http://msdn.microsoft.com/en-us/library/hh361572(v=office.14)....
  • 众所周知,国内有很多语音识别的SDK,但是这些SDK都是偏向于中文语音识别,对于英文的识别率很低,所以经过多家SDK的对比,我选择了微软的语音识别,微软的语音识别对英文的识别率很高。下面就来说下如何来接语音...
  • 转载于:https://blog.csdn.net/dustpg/article/details/38202371使用SDK: Kinect for Windows SDK v2.0 public preview紧接上节,这次要怎么初始化Kinect呢,很简单:[cpp] view plain copy// 初始化Kinect ...
  • 语音识别Kinect

    千次阅读 2016-09-22 17:38:44
    Kinect 的麦克风阵列在 Kinect 设备的下方。 这一阵列由 4 个独立的水平分布在 Kinect 下方的麦 克风组成。虽然每一个麦克风都捕获相同的音频信号,但是组成阵列可以探测到声音的来源方向。使 得能够用来识别从某...
1 2 3 4 5 ... 20
收藏数 1,492
精华内容 596
热门标签
关键字:

kinect语音识别