2018-07-17 13:23:37 a673544319 阅读数 509
  • VR环境下独特的人机交互设计

    VR硬件飞速发展,但内容却极为稀缺,作为开发者,我们究竟该如何上手VR开发?具体到在不同的平台上怎么做?如何做工具选型?延迟、晕眩、全景、人机交互、3D动画、性能优化...无一不是痛点。又该如何将VR技术更好地运用到游戏、应用开发以及内容创作中,了解哪些是从事VR开发技能,又有哪些是为「糟粕」亟需摒弃。

    4529 人正在学习 去看看 CSDN大会

1、语音识别domo场景直接运行会报错,如下图,需要安装MS Speech Platform Runtime v11下载地址https://www.microsoft.com/en-us/download/details.aspx?id=27225 最好64位和32位都下载安装,

和Kinect for Windows SDK 2.0 EN-US language packs下载地址 https://www.microsoft.com/en-us/download/details.aspx?id=43662,具体参考链接:https://rfilkov.com/2014/08/01/kinect-v2-with-ms-sdk/comment-page-3/

2、语音输入设备改用外接麦克风,修改SpeechManager的初始化代码(后面发现并不是每次都管用,待解决。。)

2013-04-25 16:27:52 weixin_33881041 阅读数 33
  • VR环境下独特的人机交互设计

    VR硬件飞速发展,但内容却极为稀缺,作为开发者,我们究竟该如何上手VR开发?具体到在不同的平台上怎么做?如何做工具选型?延迟、晕眩、全景、人机交互、3D动画、性能优化...无一不是痛点。又该如何将VR技术更好地运用到游戏、应用开发以及内容创作中,了解哪些是从事VR开发技能,又有哪些是为「糟粕」亟需摒弃。

    4529 人正在学习 去看看 CSDN大会

Kinect for windows提供了语音识别的能力,它靠Kinect的语音采集流进行识别的,这是建立在微软的语音识虽库的基础上的,关于微软语音识别可以参考http://msdn.microsoft.com/en-us/library/hh361572(v=office.14).aspx。对别Kinect识别的语音,目前只支持如下语言:en-US,de-DE,en-AU,en-CA,en-GB,en-IE,en-NZ,es-ES,es-MX,fr-CA,fr-FR,it-IT,ja-JP(很遗憾没有中文,相信在不远的将来会有的)。

下列代码为语音识别:

  1. KinectSensor kinectsensor = null;  
  2.        SpeechRecognitionEngine speechEngine;  
  3.        private void Form1_Shown(object sender, EventArgs e)  
  4.        {  
  5.            //从Kinect集合中找到连接上的Kinect  
  6.            foreach (KinectSensor ks in KinectSensor.KinectSensors)  
  7.            {  
  8.                //找到连接的Kinect  
  9.                if (ks.Status == KinectStatus.Connected)  
  10.                {  
  11.                    kinectsensor = ks;  
  12.                    kinectsensor.Start();//开始工作,即可以采集摄像头和红外摄像头信息                
  13.                    this.Text = "Kinect开始工作……";  
  14.  
  15.                    break;  
  16.                }  
  17.  
  18.            }  
  19.            if (kinectsensor != null)  
  20.            {  
  21.                //主音识别对象  
  22.                RecognizerInfo recognizer = GetKinectRecognizer();  
  23.                if (null != recognizer)  
  24.                {  
  25.                    this.speechEngine = new SpeechRecognitionEngine(recognizer.Id);  
  26.                    //读取xml 配置文件的语言库  
  27.                    using (var memoryStream = new MemoryStream(Encoding.ASCII.GetBytes(Properties.Resources.SpeechGrammar)))  
  28.                    {  
  29.                        var g = new Grammar(memoryStream);  
  30.                        speechEngine.LoadGrammar(g);  
  31.                    }  
  32.                    #region 程序中用代码添加识别字库  
  33.                    //var directions = new Choices();  
  34.                    //directions.Add(new SemanticResultValue("した", "した"));  
  35.                    //directions.Add(new SemanticResultValue("うえ", "うえ"));  
  36.                    //directions.Add(new SemanticResultValue("はじめ", "はじめ"));  
  37.                    //directions.Add(new SemanticResultValue("おわり", "おわり"));  
  38.  
  39.                    //var grammarbuilder = new GrammarBuilder { Culture = recognizer.Culture };  
  40.                    //grammarbuilder.Append(directions);  
  41.  
  42.                    //Grammar grammar = new Grammar(grammarbuilder);  
  43.                    //speechEngine.LoadGrammar(grammar);  
  44.                    #endregion  
  45.  
  46.                    //订阅语音识别委托实例  
  47.                    speechEngine.SpeechRecognized += SpeechRecognized;  
  48.                    //设置语音识别流  
  49.                    speechEngine.SetInputToAudioStream(kinectsensor.AudioSource.Start(), new SpeechAudioFormatInfo(EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null));  
  50.                    //开始识别  
  51.                    speechEngine.RecognizeAsync(RecognizeMode.Multiple);  
  52.                }  
  53.            }  
  54.        }  
  55.        /// <summary>  
  56.        /// 从对象中获取要使用的识别对象  
  57.        /// </summary>  
  58.        /// <returns></returns>  
  59.        private static RecognizerInfo GetKinectRecognizer()  
  60.        {  
  61.            //从系统所有语单识别集合中找到自己使用的语音识别对象  
  62.            foreach (RecognizerInfo recognizer in SpeechRecognitionEngine.InstalledRecognizers())  
  63.            {  
  64.                string value;  
  65.                //判断是否为Kinect的语音识别对象(微软的语音识别,有为Kinect的,也有非Kinect的,所以要判断是否为Kinect)  
  66.                recognizer.AdditionalInfo.TryGetValue("Kinect"out value);//en-US为英主    ja-JP为日语  
  67.                //第二个条件为采用那种语言  
  68.                if ("True".Equals(value, StringComparison.OrdinalIgnoreCase) && "en-US".Equals(recognizer.Culture.Name, StringComparison.OrdinalIgnoreCase))  
  69.                {  
  70.                    return recognizer;  
  71.                }  
  72.            }  
  73.            return null;  
  74.        }  
  75.  
  76.        private void SpeechRecognized(object sender, SpeechRecognizedEventArgs e)  
  77.        {  
  78.            const double ConfidenceThreshold = 0.4;  
  79.            //e.Result.Confidence为词语识别率,值为0到1,值越大,识别率越准确  
  80.            if (e.Result.Confidence >= ConfidenceThreshold)  
  81.            {  
  82.                //匹配的词语  
  83.                switch (e.Result.Semantics.Value.ToString())  
  84.                {  
  85.                    //匹配xml ,为英语识别  
  86.                    case "Next":  
  87.                        Message_Lab.Text = "向下";  
  88.                        break;  
  89.                    case "Previous":  
  90.                        Message_Lab.Text = "向上";  
  91.                        break;  
  92.                    case "Run":  
  93.                        Message_Lab.Text = "开始";  
  94.                        break;  
  95.                    case "Close":  
  96.                        Message_Lab.Text = "停止";  
  97.                        break;  
  98.                    //匹配xml ,为日语识别  
  99.                    //case "した":  
  100.                    //    Message_Lab.Text = "向下";  
  101.                    //    break;  
  102.                    //case "うえ":  
  103.                    //    Message_Lab.Text = "向上";  
  104.                    //    break;  
  105.                    //case "はじめ":  
  106.                    //    Message_Lab.Text = "开始";  
  107.                    //    break;  
  108.                    //case "おわり":  
  109.                    //    Message_Lab.Text = "停止";  
  110.                    //    break;  
  111.                }  
  112.            }  
  113.        }  
  114.  
  115.        private void Form1_FormClosing(object sender, FormClosingEventArgs e)  
  116.        {  
  117.            if (kinectsensor != null && kinectsensor.Status == KinectStatus.Connected)  
  118.            {  
  119.                kinectsensor.AudioSource.Stop();  
  120.                kinectsensor.Stop();//结束Kinect采集工作  
  121.                MessageBox.Show("Kinect结束工作!");  
  122.  
  123.            }  
  124.        }  

其中语音识别的匹配xml为:

  1. <grammar version="1.0" xml:lang="en-US" root="rootRule" tag-format="semantics/1.0-literals" xmlns="http://www.w3.org/2001/06/grammar"> 
  2.   <rule id="rootRule"> 
  3.     <one-of> 
  4.       <item> 
  5.         <tag>Next</tag> 
  6.         <one-of> 
  7.           <item> next </item> 
  8.           <item> n </item> 
  9.           <item> down </item> 
  10.           <item> qian </item>     
  11.         </one-of> 
  12.       </item> 
  13.       <item> 
  14.         <tag>Previous</tag> 
  15.         <one-of> 
  16.           <item> previous </item> 
  17.           <item> p </item> 
  18.           <item> up </item> 
  19.           <item> hou </item>       
  20.         </one-of> 
  21.       </item> 
  22.       <item> 
  23.         <tag>Run</tag> 
  24.         <one-of> 
  25.           <item> run </item> 
  26.           <item> open </item> 
  27.           <item> r </item>       
  28.         </one-of> 
  29.       </item> 
  30.       <item> 
  31.         <tag>Close</tag> 
  32.         <one-of> 
  33.           <item> close </item> 
  34.           <item> exit </item> 
  35.           <item> c </item>         
  36.         </one-of> 
  37.       </item> 
  38.     </one-of> 
  39.   </rule> 
  40. </grammar> 

 

2016-09-22 17:38:44 gaojinjingg 阅读数 2110
  • VR环境下独特的人机交互设计

    VR硬件飞速发展,但内容却极为稀缺,作为开发者,我们究竟该如何上手VR开发?具体到在不同的平台上怎么做?如何做工具选型?延迟、晕眩、全景、人机交互、3D动画、性能优化...无一不是痛点。又该如何将VR技术更好地运用到游戏、应用开发以及内容创作中,了解哪些是从事VR开发技能,又有哪些是为「糟粕」亟需摒弃。

    4529 人正在学习 去看看 CSDN大会
Kinect 的麦克风阵列在 Kinect 设备的下方。 这一阵列由 4 个独立的水平分布在 Kinect 下方的麦
克风组成。虽然每一个麦克风都捕获相同的音频信号,但是组成阵列可以探测到声音的来源方向。使
得能够用来识别从某一个特定的方向传来的声音。 麦克风阵列捕获的音频数据流经过复杂的音频增强
效果算法处理来移除不相关的背景噪音。 所有这些复杂操作在 Kinect 硬件和 Kinect SDK 之间进行处
理,这使得能够在一个大的空间范围内,即使人离麦克风一定的距离也能够进行语音命令的识别。
在 Kinect 第一次作为 Xbox360 的外设发布时,骨骼追踪和语音识别是 Kinect SDK 最受开发者
欢迎的特性,但是相比骨骼追踪,语音识别中麦克风阵列的强大功能有一点被忽视了。一部分原因归
于 Kinect 中的令人兴奋的骨骼追踪系统,另一部分原因在于 Xbox 游戏操控面板以及 Kinect 体感游
戏没有充分发挥 Kinect 音频处理的优点。
作为一个开始使用 Kinect 进行应用开发的开发者,Kinect 上的麦克风阵列的出现使得基于 Kinect
应用程序的功能更加强大。虽然 Kinect 的视觉分析令人印象深刻,但是仍然不能很好的对马达进行
控制。当我们从一种人机交互界面切换到另一种人机交互界面:如从命令行交互应用程序到标签页交
互界面,再到鼠标图形用户界面或者触摸交互界面时,每一种交互界面都提供了各种最基本的更加容
易实现的操作,这个操作就是选择。进一步,可以说,每一种交互界面都改进了我们对对象进行选择
的能力。奇怪的是,Kinect 破坏了这一趋势。
这些问题,可以通过将语音识别指令和骨骼追踪系统结合起来产生一个复合的姿势来相对简单
的解决:保持某一动作,然后通过语音执行。菜单的设计也可以通过首先展示菜单项,然后让用户说
出菜单项的名称来进行选择-很多 Xbox 中的游戏已经使用了这种方式。可以预见,无论是程序开发
者还是游戏公司,这种复合的解决方案在未来会越来越多的应用到新的交互方式中,而不用再像以前
那样使用指然后点(point and click)这种方式来选择。
麦克风阵列
安装完 Microsoft Kinect SDK 之后,语音识别的组件会自动安装。Kinect 的麦克风阵列工作在
一些语音识别的类库之上,这些类库是从 Vista 系统之时就有的。他们包括语音捕获 DirectX 多媒体
对象(DirectX Media Object,DMO)以及语音识别 API(Speech Recognition API,SAPI)。
在 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 语言包
提供了这些模型来优化语音指令的识别。
MSR Kinect Audio
Kinect 中处理音频主要是通过 KinectAudioSource 这个对象来完成的。KinectAudioSource 类
的主要作用是从麦克风阵列中提取原始的或者经过处理的音频流。 音频流可能会经过一系列的算法来
处理以提高音频质量,这些处理包括:降噪、自动增益控制和回声消除。KinectAudioSource 能够进
行一些配置使得 Kinect 麦克风阵列可以以不同的模式进行工作。也能够用来探测从那个方向来的哪
种音频信息最先达到麦克风以及用来强制麦克风阵列接受指定方向的音频信息。
本节尽量不会去介绍一些音频处理技术方面的较低层次的技术。但是为了使用
KinectAudioSource,了解语音捕获以及语音传输中的一些术语可能会对熟悉KinectAudioSource中
的一些属性和方法有所帮助。
  回声消除(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 个通道,单通道是一种系
统模式用来关闭波束成形。
2015-04-08 10:38:03 u012371137 阅读数 626
  • VR环境下独特的人机交互设计

    VR硬件飞速发展,但内容却极为稀缺,作为开发者,我们究竟该如何上手VR开发?具体到在不同的平台上怎么做?如何做工具选型?延迟、晕眩、全景、人机交互、3D动画、性能优化...无一不是痛点。又该如何将VR技术更好地运用到游戏、应用开发以及内容创作中,了解哪些是从事VR开发技能,又有哪些是为「糟粕」亟需摒弃。

    4529 人正在学习 去看看 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;
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);
        }
    }
}


2015-12-15 11:23:54 qq1175421841 阅读数 817
  • VR环境下独特的人机交互设计

    VR硬件飞速发展,但内容却极为稀缺,作为开发者,我们究竟该如何上手VR开发?具体到在不同的平台上怎么做?如何做工具选型?延迟、晕眩、全景、人机交互、3D动画、性能优化...无一不是痛点。又该如何将VR技术更好地运用到游戏、应用开发以及内容创作中,了解哪些是从事VR开发技能,又有哪些是为「糟粕」亟需摒弃。

    4529 人正在学习 去看看 CSDN大会

 上一篇文章介绍了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开发中可能会用到的一些第三方类库和开发工具,敬请期待!

没有更多推荐了,返回首页