转载于:https://blog.csdn.net/dustpg/article/details/38202371
使用SDK: Kinect for Windows SDK v2.0 public preview
紧接上节,这次要怎么初始化Kinect呢,很简单:
- // 初始化Kinect
- HRESULT ThisApp::init_kinect(){
- IAudioSource* pAudioSource = nullptr;
- IAudioBeamList* pAudioBeamList = nullptr;
- // 查找当前默认Kinect
- HRESULT hr = ::GetDefaultKinectSensor(&m_pKinect);
- // 绅士地打开Kinect
- if (SUCCEEDED(hr)){
- hr = m_pKinect->Open();
- }
- // 获取音频源
- if (SUCCEEDED(hr)){
- hr = m_pKinect->get_AudioSource(&pAudioSource);
- }
- // 获取音频链表
- if (SUCCEEDED(hr)){
- hr = pAudioSource->get_AudioBeams(&pAudioBeamList);
- }
- // 获取音频
- if (SUCCEEDED(hr)){
- hr = pAudioBeamList->OpenAudioBeam(0, &m_pAudioBeam);
- }
- // 获取输入音频流
- if (SUCCEEDED(hr)){
- IStream* pStream = nullptr;
- hr = m_pAudioBeam->OpenInputStream(&pStream);
- // 利用傀儡生成包装对象
- m_p16BitPCMAudioStream = new KinectAudioStreamWrapper(pStream);
- SafeRelease(pStream);
- }
- SafeRelease(pAudioBeamList);
- SafeRelease(pAudioSource);
- return hr;
- }
![](https://i-blog.csdnimg.cn/blog_migrate/107fe5684766c777023389926db1692c.jpeg)
- // 初始化语音识别
- HRESULT ThisApp::init_speech_recognizer(){
- HRESULT hr = S_OK;
- // 创建语音输入流
- if (SUCCEEDED(hr)){
- hr = CoCreateInstance(CLSID_SpStream, nullptr, CLSCTX_INPROC_SERVER, __uuidof(ISpStream), (void**)&m_pSpeechStream);;
- }
- // 与我们的Kinect语音输入相连接
- if (SUCCEEDED(hr)){
- WAVEFORMATEX wft = {
- WAVE_FORMAT_PCM, // PCM编码
- 1, // 单声道
- 16000, // 采样率为16KHz
- 32000, // 每分钟数据流 = 采样率 * 对齐
- 2, // 对齐 : 单声道 * 样本深度 = 2byte
- 16, // 样本深度 16BIT
- 0 // 额外数据
- };
- // 设置状态
- hr = m_pSpeechStream->SetBaseStream(m_p16BitPCMAudioStream, SPDFID_WaveFormatEx, &wft);
- }
- // 创建语音识别对象
- if (SUCCEEDED(hr)){
- ISpObjectToken *pEngineToken = nullptr;
- // 创建语言识别器
- hr = CoCreateInstance(CLSID_SpInprocRecognizer, nullptr, CLSCTX_INPROC_SERVER, __uuidof(ISpRecognizer), (void**)&m_pSpeechRecognizer);
- if (SUCCEEDED(hr)) {
- // 连接我们创建的语音输入流对象
- m_pSpeechRecognizer->SetInput(m_pSpeechStream, TRUE);
- // 创建待识别语言 这里选择大陆汉语(zh-cn)
- // 目前没有Kinect的汉语语音识别包 有的话可以设置"language=804;Kinect=Ture"
- hr = SpFindBestToken(SPCAT_RECOGNIZERS, L"Language=804", nullptr, &pEngineToken);
- if (SUCCEEDED(hr)) {
- // 设置待识别语言
- m_pSpeechRecognizer->SetRecognizer(pEngineToken);
- // 创建语音识别上下文
- hr = m_pSpeechRecognizer->CreateRecoContext(&m_pSpeechContext);
- // 适应性 ON! 防止因长时间的处理而导致识别能力的退化
- if (SUCCEEDED(hr)) {
- hr = m_pSpeechRecognizer->SetPropertyNum(L"AdaptationOn", 0);
- }
- }
- }
- SafeRelease(pEngineToken);
- }
- // 创建语法
- if (SUCCEEDED(hr)){
- hr = m_pSpeechContext->CreateGrammar(1, &m_pSpeechGrammar);
- }
- // 加载静态SRGS语法文件
- if (SUCCEEDED(hr)){
- hr = m_pSpeechGrammar->LoadCmdFromFile(s_GrammarFileName, SPLO_STATIC);
- }
- // 激活语法规则
- if (SUCCEEDED(hr)){
- hr = m_pSpeechGrammar->SetRuleState(nullptr, nullptr, SPRS_ACTIVE);
- }
- // 设置识别器一直读取数据
- if (SUCCEEDED(hr)){
- hr = m_pSpeechRecognizer->SetRecoState(SPRST_ACTIVE_ALWAYS);
- }
- // 设置对识别事件感兴趣
- if (SUCCEEDED(hr)){
- hr = m_pSpeechContext->SetInterest(SPFEI(SPEI_RECOGNITION), SPFEI(SPEI_RECOGNITION));
- }
- // 保证语音识别处于激活状态
- if (SUCCEEDED(hr)){
- hr = m_pSpeechContext->Resume(0);
- }
- // 获取识别事件
- if (SUCCEEDED(hr)){
- m_p16BitPCMAudioStream->SetSpeechState(TRUE);
- m_hSpeechEvent = m_pSpeechContext->GetNotifyEventHandle();
- }
- #ifdef _DEBUG
- else
- printf_s("init_speech_recognizer failed\n");
- #endif
- return hr;
- }
需注意几点:
1.SetBaseStream时填写WAVEFORMATEX时,PCM数据格式的填写。
2.SpFindBestToken初始化时选择区域,是16进制的,大陆汉语是0x0804。如果Kinect提供的附加的语音包,可以追加
设置Kinect=Ture,使用分号分开。
3.LoadCmdFromFile设置加载SRGS的语法文件,还能设置是否动态。
4.GetNotifyEventHandle用来获取语音识别事件句柄,这个句柄不由程序员关闭,请勿主动CloseHandle
等待SR事件触发时
- // 音频处理
- void ThisApp::speech_process() {
- // 置信阈值
- 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) {
- ISpRecoResult* result = reinterpret_cast<ISpRecoResult*>(curEvent.lParam);
- SPPHRASE* pPhrase = nullptr;
- // 获取识别短语
- hr = result->GetPhrase(&pPhrase);
- if (SUCCEEDED(hr)) {
- // XXXXXXXXXXXXXXXXXXX
- ::CoTaskMemFree(pPhrase);
- }
- }
- break;
- }
- m_pSpeechContext->GetEvents(1, &curEvent, &fetched);
- }
- return;
- }
获取短语成功后可以使用
- WCHAR* pwszFirstWord;
- result->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE, &pwszFirstWord, nullptr);
- // XXX
- ::CoTaskMemFree(pwszFirstWord);
SPPHRASE结构体可谓相当复杂,重要的有
SPPHRASEPROPERTY* 指针,是个树指针,每个节点都有SREngineConfidence表示置信度
父节点的置信度表示这个支总体大致的置信度,子节点表示本节短语的置信度。一般使用父节点的置信度即可。
为了表示一个SR事件识别是否准确,可以设置一个置信阈值,大于该阈值才认为识别准确。可以是经验值,
例如0.3,0.4等,还可以是动态的,由环境指定或者干脆由玩家制定。
在这,我们假设说一句“我们击毁了敌方厕所”,根据上节提供的SRGS。
pPhrase->pProperties大致是:
"_value"
|------>"战况"
|----------->"主语"
|
"谓语"
|
"对象"
|
"宾语"
每个节点的pszValue成员能获取字符数据,比如我们设置的out = 0;则这个就是"0",
vValue获取识别后的数据,比如vValue.lVal获取long类型的数据,可以自行查看,毕竟全是数据。
具体怎么实现那真是太随意,各位可以自行发挥。这里就给个半成品吧:
- // 音频处理
- void ThisApp::speech_process() {
- // 置信阈值
- 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) {
- ISpRecoResult* result = reinterpret_cast<ISpRecoResult*>(curEvent.lParam);
- SPPHRASE* pPhrase = nullptr;
- // 获取识别短语
- hr = result->GetPhrase(&pPhrase);
- if (SUCCEEDED(hr)) {
- #ifdef _DEBUG
- // DEBUG时显示识别字符串
- WCHAR* pwszFirstWord;
- result->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE, &pwszFirstWord, nullptr);
- _cwprintf(pwszFirstWord);
- ::CoTaskMemFree(pwszFirstWord);
- #endif
- pPhrase->pProperties;
- const SPPHRASEELEMENT* pointer = pPhrase->pElements + 1;
- if ((pPhrase->pProperties != nullptr) && (pPhrase->pProperties->pFirstChild != nullptr)) {
- const SPPHRASEPROPERTY* pSemanticTag = pPhrase->pProperties->pFirstChild;
- #ifdef _DEBUG
- _cwprintf(L" 置信度:%d%%\n", (int)(pSemanticTag->SREngineConfidence*100.f));
- #endif
- if (pSemanticTag->SREngineConfidence > ConfidenceThreshold) {
- speech_behavior(pSemanticTag);
- }
- }
- ::CoTaskMemFree(pPhrase);
- }
- }
- break;
- }
- m_pSpeechContext->GetEvents(1, &curEvent, &fetched);
- }
- return;
- }
- // 语音行为
- void ThisApp::speech_behavior(const SPPHRASEPROPERTY* tag){
- if (!tag) return;
- if (!wcscmp(tag->pszName, L"战况")){
- enum class Subject{
- US = 0,
- Enemy
- } ;
- enum class Predicate{
- Destroy = 0,
- Defeat,
- Breakdown
- };
- // 分析战况
- union Situation{
- struct{
- // 主语
- Subject subject;
- // 谓语
- Predicate predicate;
- // 对象
- int object2;
- // 宾语
- int object;
- };
- UINT32 data[4];
- };
- Situation situation;
- auto obj = tag->pFirstChild;
- auto pointer = situation.data;
- // 填写数据
- while (obj) {
- *pointer = obj->vValue.lVal;
- ++pointer;
- obj = obj->pNextSibling;
- }
- // XXX
- }
- else if (!wcscmp(tag->pszName, L"发现东西")){
- // 发现东西
- }
- }
好啦,语音识别就到此为止了,范例如下:
依然是控制台程序,请不要点击X退出程序,而是按任意键退出。
下载地址:点击这里
关于面部识别与可视化手势:
查看SDK时相信大家也看到了,在SDK中还有
Kinect.Face.h
Kinect.VisualGestureBuilder.h
其中“Kinect.VisualGestureBuilder.h”相对应的lib与dll只有x64版本,不知道是微软偷懒还是因为某个特性只能x64能使用,
不过希望是偷懒吧,毕竟几乎没开发过64位程序。8字节指针感觉太浪费了。
但是这两个:没有发现C++部分的官方文档与例子,估计目前是给C#用的。目前使用了一下,某些函数/方法均是返回:
路径未找到
比如手势识别的
IVisualGestureBuilderFrameSource::AddGestures
面部识别的
CreateHighDefinitionFaceFrameSource
只能坐等SDK更新或者高手解答了,那么《Kinect for Windows SDK v2.0 开发笔记》在这里暂时结束
,感谢大家的支持,SDK更新时再见
- 本文已收录于以下专栏:
- Kinect for Windows SDK v2.0 开发笔记