Kinect for Windows SDK v2.0 开发笔记 (七)语音识别(上)

(转载请注明出处)

使用SDK: Kinect for Windows SDK v2.0 public preview

这次探讨语音识别。之前都是一节写两部分,这次反过来了,分为上下两部分。


语音识别算是使用官方SDK的一个理由吧,不然都用OpenNI了,毕竟微软在SR(语音识别)上的成果还是不错的。

首先,您需要下载语音识别的SDK运行时库以及您想支持的运行时语言,运行时语言里面SR代表语音识别,

TTS代表文本转语音,根据您的选择吧。本人选择下载了美国英语与大陆汉语。当然,坑爹的微软还专门为Kinect

准备Kinect运行时语言,支持语言较少,不过连日语都支持,竟然不支持汉语

或许仅仅是日语发音简单吧。


如果您和本人一样使用的是VS Express,那么请再下载WDK7.1,这个语音库需要一点点ATL支

持(貌似就COM智能指针),坑爹的微软在Express里面没有ATL库,为了获取合法ATL库需要下载这个

WDK7.1,里面有能合法使用的ALT库。我直接安装到C盘,请在项目里面包含这三个库的目录


语音平台、WDK、Kinect


有您自己的需要请自行包含其他库,最好将语音平台放到第一位。

目前语音识别应该只能识别PCM编码,处理后的数据是浮点编码,我们不能直接使用SDK默认提供的IStream。

是的,基于COM组件的通用性,我们只需继承IStream实现一个Stream类即可完成音频流的处理。

这个类主要实现Read方法即可,Seek方法在SR中也算比较重要不过Kinect还不支持,这个方法直接返回S_OK即可


实现这个类有两种办法,一种是基于前一节说的音频帧。自己用一个比较大的缓冲区(最好设计上是循环的),

获取音频帧再将数据写在里面,Read时将数据写入即可,比较麻烦。还有一个就是SDK提供例子里面使用的:

傀儡战术。

获取SDK提供的默认的IStream,Read的时候Read这个傀儡即可,数据获取完毕后进行转换即可,代码如下:

[cpp]  view plain  copy
  1. // IStream Read方法的实现  
  2. STDMETHODIMP KinectAudioStreamWrapper::Read(void *pBuffer, ULONG cbBuffer, ULONG *pcbRead){  
  3.     // 参数检查  
  4.     if (!pBuffer || !pcbRead) return E_INVALIDARG;  
  5.     // 在读取前未使用 m_SpeechActive 返回S_OK  
  6.     if (!m_SpeechActive){  
  7.         *pcbRead = cbBuffer;  
  8.         return S_OK;  
  9.     }  
  10.     HRESULT hr = S_OK;  
  11.     // 目标是将浮点编码转换成16位PCM编码  
  12.     INT16* const p16Buffer = reinterpret_cast<INT16*>(pBuffer);  
  13.     // 长度倍数  
  14.     const int multiple = sizeof(float) / sizeof(INT16);  
  15.     // 检查缓冲区释放足够  
  16.     auto float_buffer_size = cbBuffer / multiple;  
  17.     if (float_buffer_size > m_uFloatBuferSize){  
  18.         // 不够就重新申请内存  
  19.         m_uFloatBuferSize = float_buffer_size;  
  20.         if (m_pFloatBuffer) delete[]m_pFloatBuffer;  
  21.         m_pFloatBuffer = new float[m_uFloatBuferSize];  
  22.     }  
  23.     // 缓冲区写入进度 字节为单位  
  24.     BYTE* pWriteProgress = reinterpret_cast<BYTE*>(m_pFloatBuffer);  
  25.     // 目前读取量  
  26.     ULONG bytesRead = 0;  
  27.     // 需要读取量  
  28.     ULONG bytesNeed = cbBuffer * multiple;  
  29.     // 循环读取  
  30.     while (true){  
  31.         // 已经不需要语音的情况下  
  32.         if (!m_SpeechActive){  
  33.             *pcbRead = cbBuffer;  
  34.             hr = S_OK;  
  35.             break;  
  36.         }  
  37.         // 从包装对象获取数据  
  38.         hr = m_p32BitAudio->Read(pWriteProgress, bytesNeed, &bytesRead);  
  39.         bytesNeed -= bytesRead;  
  40.         pWriteProgress += bytesRead;  
  41.         // 检查是否足够  
  42.         if (!bytesNeed){  
  43.             *pcbRead = cbBuffer;  
  44.             break;  
  45.         }  
  46.         // 不然就睡一个时间片的时间  
  47.         Sleep(20);  
  48.     }  
  49.     // 数据处理 float -> 16bit PCM  
  50.     if (!bytesNeed){  
  51.         for (UINT i = 0; i < cbBuffer / multiple; i++) {  
  52.             float sample = m_pFloatBuffer[i];  
  53.             // 区间保证  
  54.             //sample = max(min(sample, 1.f), -1.f);  
  55.             if (sample > 1.f) sample = 1.f;  
  56.             if (sample < -1.f) sample = -1.f;  
  57.             // 数据转换  
  58.             float sampleScaled = sample * (float)SHRT_MAX;  
  59.             p16Buffer[i] = (sampleScaled > 0.f) ? (INT16)(sampleScaled + 0.5f) : (INT16)(sampleScaled - 0.5f);  
  60.         }  
  61.     }  
  62.     return hr;  
  63. }  


请注意,语音识别引擎初始化时要获取一定的音频数据才能完成,很可能花上几秒甚至十几秒来初始化,这很蛋疼,

不知道微软怎么想的。为此,我们增加一个变量表示SR是否处于激活状态,没被激活时,返回假数据欺骗SR引擎即可。

这样几乎就无需花时间等待初始化了。


我们这里语音识别使用的方法是载入静态SRGS语法文件,当然可以载入动态语法,不过详细的请翻阅官方文档。

至于SRGS的语法可以翻看W3C的文档,也可以看微软的文档

好,让我们写一个SRGS语法文件吧,SRGS是基于xml的,后缀名就使用xml吧。我这里使用的是汉语,

毕竟文字处理上,汉语优势很大,几个字就能表示一个句子。经本人测试,汉语里面可以识别英语,但是识别率

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <grammar version="1.0" xml:lang="zh-CN" mode="voice" root="根语音" xmlns="http://www.w3.org/2001/06/grammar" tag-format="semantics/1.0">  
  3.     <rule id="根语音" scope="public">  
  4.         <one-of>  
  5.             <item>前进</item>  
  6.         </one-of>  
  7.         <one-of>  
  8.             <item>后退</item>  
  9.         </one-of>  
  10.     </rule>  
  11. </grammar>  


 
  这样差不多就是最简单的SRGS文件吧,重要的有: 
  

开头, xml的标准,使用utf8即可

grammar标签是主标签,里面xml:lang属性选择您的语言,大陆汉语就是zh-CN了。还有就是root,表示根标签的名字,

SR引擎就从这里识别基本短语,其他的照写即可。

每个规则使用rule标签,多选一使用one-of标签,基本短语使用item,规则引用使用ruleref,tag标签能使用字符串或者

貌似js的脚本标上自己想要的数据,还有token标签,这些可以查看文档了解。

我们的目标实现下面短语:

1.我在XX YY发现了ZZ AA BB

比如我在脚(XX)上(YY)发现了两(ZZ)枚(AA)高爆穿甲弹(BB)

XX表示地点,这个信息很重要

YY表示相对方位,这个信息不重要

ZZ表示数量,这个信息很重要

AA表示量词,几乎没用

BB表示物品,这个信息很重要

[html]  view plain  copy
  1. <rule id="发现东西">  
  2.     <example> 我在脚上发现两枚穿甲弹 </example>  
  3.   
  4.     <item>我在</item>  
  5.     <ruleref uri="#地点"/>  
  6.     <ruleref uri="#相对位置"/>  
  7.     <item>发现</item>  
  8.     <item repeat="0-1"></item>  
  9.     <ruleref uri="#数量"/>  
  10.     <ruleref uri="#量词"/>  
  11.     <ruleref uri="#目标物体"/>  
  12. </rule>  
  13.   
  14. <rule id="地点">  
  15.     <example> 脚 </example>  
  16.     <example> 房子 </example>  
  17.   
  18.     <one-of>  
  19.         <item></item>  
  20.         <item>房子</item>  
  21.         <item></item>  
  22.         <item></item>  
  23.     </one-of>  
  24. </rule>  
  25.   
  26. <rule id="相对位置">  
  27.     <example> 上 </example>  
  28.   
  29.     <one-of>  
  30.         <item></item>  
  31.         <item>上面</item>  
  32.         <item>里面</item>  
  33.         <item>旁边</item>  
  34.         <item>附近</item>  
  35.     </one-of>  
  36. </rule>  
  37.   
  38. <rule id="数量">  
  39.     <example> 两 </example>  
  40.   
  41.     <one-of>  
  42.         <item></item>  
  43.         <item></item>  
  44.         <item></item>  
  45.         <item></item>  
  46.         <item></item>  
  47.         <item></item>  
  48.         <item></item>  
  49.         <item></item>  
  50.         <item></item>  
  51.         <item></item>  
  52.         <item></item>  
  53.     </one-of>  
  54. </rule>  
  55.   
  56. <rule id="量词">  
  57.     <example> 枚 </example>  
  58.   
  59.     <one-of>  
  60.         <item></item>  
  61.         <item></item>  
  62.         <item></item>  
  63.         <item></item>  
  64.         <item></item>  
  65.         <item></item>  
  66.         <item></item>  
  67.         <item></item>  
  68.         <item></item>  
  69.         <item></item>  
  70.     </one-of>  
  71. </rule>  
  72.   
  73. <rule id="目标物体">  
  74.     <example> 坦克 </example>  
  75.   
  76.     <one-of>  
  77.         <item>高爆穿甲弹</item>  
  78.         <item>穿甲弹</item>  
  79.         <item>坦克</item>  
  80.         <item>铅笔</item>  
  81.         <item>电脑</item>  
  82.         <item>苹果</item>  
  83.         <item>锤子</item>  
  84.         <item>手机</item>  
  85.         <item>阿姆斯特朗回旋加速喷气式阿姆斯特朗炮</item>  
  86.     </one-of>  
  87. </rule>  


这部分就差不多了,再在“根语言”里面更新:

[html]  view plain  copy
  1. <rule id="根语音" scope="public">  
  2.     <one-of>  
  3.         <item>  
  4.             <ruleref uri="#发现东西"/>  
  5.         </item>  
  6.     </one-of>  
  7. </rule>  

如果再增加一个,"战况"


[html]  view plain  copy
  1. <rule id="根语音" scope="public">  
  2.     <one-of>  
  3.         <item>  
  4.             <ruleref uri="#发现东西"/>  
  5.         </item>  
  6.         <item>  
  7.             <ruleref uri="#战况"/>  
  8.         </item>  
  9.     </one-of>  
  10. </rule>  


[html]  view plain  copy
  1. </pre><p></p><pre>  
就这样写。

在实际使用时,会遇到非常多的同义词,或者选择支很多。如果把识别的字符串进行比较的话很吃力,我们就可以用tag标签

于是在“数量” 那里可以这样写:out = 10;就能对外输出10,

[html]  view plain  copy
  1. <rule id="数量">  
  2.         <example> 两 </example>  
  3.   
  4.         <one-of>  
  5.             <item><tag>out=1;</tag></item>  
  6.             <item><tag>out=2;</tag></item>  
  7.             <item><tag>out=2;</tag></item>  
  8.             <item><tag>out=3;</tag></item>  
  9.             <item><tag>out=4;</tag></item>  
  10.             <item><tag>out=5;</tag></item>  
  11.             <item><tag>out=6;</tag></item>  
  12.             <item><tag>out=7;</tag></item>  
  13.             <item><tag>out=8;</tag></item>  
  14.             <item><tag>out=9;</tag></item>  
  15.             <item><tag>out=10;</tag></item>  
  16.         </one-of>  
  17.     </rule>  





也能使用字符串。但是数据处理是数字才方便,不是么


类似地,我们将“发现东西”更新至:

[html]  view plain  copy
  1. <rule id="发现东西">  
  2.     <example> 我在脚上发现两枚穿甲弹 </example>  
  3.   
  4.     <item>我在</item>  
  5.     <ruleref uri="#地点"/>  
  6.     <tag> out.地点 = rules.地点; </tag>  
  7.     <ruleref uri="#相对位置"/>  
  8.     <item>发现</item>  
  9.     <item repeat="0-1"></item>  
  10.     <ruleref uri="#数量"/>  
  11.     <tag> out.数量 = rules.数量; </tag>  
  12.     <ruleref uri="#量词"/>  
  13.     <ruleref uri="#目标物体"/>  
  14.     <tag> out.物体 = rules.目标物体; </tag>  
  15. </rule>  



毕竟是脚本,看就能看懂。 out.A = rules.B,AB不一定相同,但是B一定和前面引用的id一样


现在我们增加一个短语:“战况” 比如


我方击毁敌方厕所


也是很简单,我就将代码直接放在这了:

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <grammar version="1.0" xml:lang="zh-CN" mode="voice" root="根语音" xmlns="http://www.w3.org/2001/06/grammar" tag-format="semantics/1.0">  
  3.     <rule id="根语音" scope="public">  
  4.         <one-of>  
  5.             <item>  
  6.                 <ruleref uri="#发现东西"/>  
  7.                 <tag> out.发现东西 = rules.发现东西; </tag>  
  8.             </item>  
  9.             <item>  
  10.                 <ruleref uri="#战况"/>  
  11.                 <tag> out.战况 = rules.战况; </tag>  
  12.             </item>  
  13.         </one-of>  
  14.     </rule>  
  15.   
  16.     <rule id="战况">  
  17.         <example> 我们击毁了敌方厕所 </example>  
  18.         <example> 他们击穿我方的装甲 </example>  
  19.           
  20.         <ruleref uri="#人物对象"/>  
  21.         <tag> out.主语 = rules.人物对象; </tag>  
  22.         <ruleref uri="#战况动词"/>  
  23.         <tag> out.谓语 = rules.战况动词; </tag>  
  24.         <item repeat="0-1"></item>  
  25.         <ruleref uri="#人物对象"/>  
  26.         <tag> out.对象 = rules.人物对象; </tag>  
  27.         <item repeat="0-1"></item>  
  28.         <ruleref uri="#战况名词"/>  
  29.         <tag> out.宾语 = rules.战况名词; </tag>  
  30.     </rule>  
  31.       
  32.     <rule id="人物对象">  
  33.         <example> 我们 </example>  
  34.         <example> 他们 </example>  
  35.   
  36.         <one-of>  
  37.             <item>我们<tag>out=0;</tag></item>  
  38.             <item>他们<tag>out=1;</tag></item>  
  39.             <item>我方<tag>out=0;</tag></item>  
  40.             <item>敌方<tag>out=1;</tag></item>  
  41.         </one-of>  
  42.     </rule>  
  43.       
  44.     <rule id="战况动词">  
  45.         <example> 击毁 </example>  
  46.         <example> 击败 </example>  
  47.   
  48.         <one-of>  
  49.             <item>击毁<tag>out=0;</tag></item>  
  50.             <item>击败<tag>out=1;</tag></item>  
  51.             <item>击穿<tag>out=2;</tag></item>  
  52.         </one-of>  
  53.     </rule>  
  54.       
  55.     <rule id="战况名词">  
  56.         <example> 装甲 </example>  
  57.         <example> 计算机 </example>  
  58.   
  59.         <one-of>  
  60.             <item>装甲<tag>out=0;</tag></item>  
  61.             <item>厕所<tag>out=1;</tag></item>  
  62.             <item>计算机<tag>out=2;</tag></item>  
  63.             <item>电脑<tag>out=2;</tag></item>  
  64.             <item>核弹发射井<tag>out=3;</tag></item>  
  65.         </one-of>  
  66.     </rule>  
  67.   
  68.     <rule id="发现东西">  
  69.         <example> 我在脚上发现两枚穿甲弹 </example>  
  70.   
  71.         <item>我在</item>  
  72.         <ruleref uri="#地点"/>  
  73.         <tag> out.地点 = rules.地点; </tag>  
  74.         <ruleref uri="#相对位置"/>  
  75.         <item>发现</item>  
  76.         <item repeat="0-1"></item>  
  77.         <ruleref uri="#数量"/>  
  78.         <tag> out.数量 = rules.数量; </tag>  
  79.         <ruleref uri="#量词"/>  
  80.         <ruleref uri="#目标物体"/>  
  81.         <tag> out.物体 = rules.目标物体; </tag>  
  82.     </rule>  
  83.   
  84.     <rule id="地点">  
  85.         <example> 脚 </example>  
  86.         <example> 房子 </example>  
  87.   
  88.         <one-of>  
  89.             <item><tag>out=0;</tag></item>  
  90.             <item>房子<tag>out=1;</tag></item>  
  91.             <item><tag>out=2;</tag></item>  
  92.             <item><tag>out=3;</tag></item>  
  93.         </one-of>  
  94.     </rule>  
  95.   
  96.     <rule id="相对位置">  
  97.         <example> 上 </example>  
  98.   
  99.         <one-of>  
  100.             <item></item>  
  101.             <item>上面</item>  
  102.             <item>里面</item>  
  103.             <item>旁边</item>  
  104.             <item>附近</item>  
  105.         </one-of>  
  106.     </rule>  
  107.       
  108.     <rule id="数量">  
  109.         <example> 两 </example>  
  110.   
  111.         <one-of>  
  112.             <item><tag>out=1;</tag></item>  
  113.             <item><tag>out=2;</tag></item>  
  114.             <item><tag>out=2;</tag></item>  
  115.             <item><tag>out=3;</tag></item>  
  116.             <item><tag>out=4;</tag></item>  
  117.             <item><tag>out=5;</tag></item>  
  118.             <item><tag>out=6;</tag></item>  
  119.             <item><tag>out=7;</tag></item>  
  120.             <item><tag>out=8;</tag></item>  
  121.             <item><tag>out=9;</tag></item>  
  122.             <item><tag>out=10;</tag></item>  
  123.         </one-of>  
  124.     </rule>  
  125.   
  126.     <rule id="量词">  
  127.         <example> 枚 </example>  
  128.   
  129.         <one-of>  
  130.             <item></item>  
  131.             <item></item>  
  132.             <item></item>  
  133.             <item></item>  
  134.             <item></item>  
  135.             <item></item>  
  136.             <item></item>  
  137.             <item></item>  
  138.             <item></item>  
  139.             <item></item>  
  140.         </one-of>  
  141.     </rule>  
  142.       
  143.     <rule id="目标物体">  
  144.         <example> 坦克 </example>  
  145.   
  146.         <one-of>  
  147.             <item>高爆穿甲弹<tag>out=0;</tag></item>  
  148.             <item>穿甲弹<tag>out=1;</tag></item>  
  149.             <item>坦克<tag>out=2;</tag></item>  
  150.             <item>铅笔<tag>out=3;</tag></item>  
  151.             <item>电脑<tag>out=4;</tag></item>  
  152.             <item>苹果<tag>out=5;</tag></item>  
  153.             <item>锤子<tag>out=6;</tag></item>  
  154.             <item>手机<tag>out=7;</tag></item>  
  155.             <item>阿姆斯特朗回旋加速喷气式阿姆斯特朗炮<tag>out=8;</tag></item>  
  156.         </one-of>  
  157.     </rule>  
  158. </grammar>  


这次就到这里了,这次的SRGS范例希望帮助各位,下一节再写C++部分代码吧。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值