Kinect V2 开发专题(3)设备信息获取与音频功能探索

【原文:http://blog.csdn.net/bbdxf/article/details/44856747

Kinect V2 开发专题(3)

1、Kinect设备信息获取

2、音频功能探索

1、Kinect设备信息获取

由于Kinect支持多台设备同连,所以,理论上,我们应该可以获取所有的设备信息,这里我们探索一下。

通过官方的AudioBasics-D2D这个Demo发现,获取Kinect设备只需要一步:

IKinectSensor* m_pKinectSensor;  GetDefaultKinectSensor(&m_pKinectSensor);

我们在接口列表中(https://msdn.microsoft.com/en-us/library/dn791996.aspx)查找,发现一个叫做 IKinectSensorCollection 的类,很显眼的是关于Sensor收集的类,我们的脚步就从这里开始。

新建一个工程,按照第二节的内容,设置属性,加入头文件。好啦,我们开始:

额,不好意思!为什么呢?因为坑x的微软在去年八月份的时候把这个功能给Cut了,所以...你是不可能用的,将来可能还会加上。我们了解下就好了,下面我找了一份旧代码大家看看就可以了。

[cpp]  view plain  copy   在CODE上查看代码片 派生到我的代码片
  1. IKinectSensorCollection* pKinectCollection = nullptr;  
  2. IEnumKinectSensor* pEnumKinect = nullptr;  
  3. IKinectSensor* pKinect = nullptr;  
  4. // 获取Kinect集合  
  5. HRESULT hr = ::GetKinectSensorCollection(&pKinectCollection);  
  6. // 获取Kinect枚举器  
  7. if (SUCCEEDED(hr)){  
  8.     hr = pKinectCollection->get_Enumerator(&pEnumKinect);  
  9. }  
  10. // 枚举Kinect  
  11. if (SUCCEEDED(hr)){  
  12.     BOOLEAN available = false;  
  13.     while (true){  
  14.         // 获取下一个  
  15.         if (SUCCEEDED(pEnumKinect->GetNext(&pKinect))){  
  16.             // 判断有效性  
  17.             pKinect->get_IsAvailable(&available);  
  18.             if (available && YourJudgmentFunc(pKinect)){  
  19.                 break;  
  20.             }  
  21.             SafeRelease(pKinect);  
  22.         }  
  23.         else  
  24.             break;  
  25.     }  
  26. }  
  27. SafeRelease(pEnumKinect);  
  28. SafeRelease(pKinectCollection);  


不要在意这些细节,这都是小事。我们可以通过 GetDefaultKinectSensor(_COM_Outptr_ IKinectSensor** defaultKinectSensor); 这个函数获取默认的Kinect设备,哈哈哈!

下面是一段测试的小程序:

[cpp]  view plain  copy   在CODE上查看代码片 派生到我的代码片
  1. #include "stdafx.h"  
  2. #include "kinect.h"  
  3.   
  4. int _tmain(int argc, _TCHAR* argv[])  
  5. {  
  6.     printf("Hello, Wellcome to kinect world!\n");  
  7.     //IKinectSensorCollection aa;  
  8.     IKinectSensor* bb;  
  9.     HRESULT hr = GetDefaultKinectSensor(&bb);  
  10.     if ( FAILED(hr) )  
  11.     {  
  12.         printf("No Kinect connect to your pc!\n");  
  13.         goto endstop;  
  14.     }  
  15.     BOOLEAN bAvaliable = 0;  
  16.     bb->get_IsAvailable(&bAvaliable);  
  17.     printf("bAvaliable: %d\n", bAvaliable);  
  18.     BOOLEAN bIsOpen = 0;  
  19.     bb->get_IsOpen(&bIsOpen);  
  20.     printf("bIsOpen: %d\n", bIsOpen);  
  21.     DWORD dwCapability = 0;   
  22.     bb->get_KinectCapabilities(&dwCapability);  
  23.     printf("dwCapability: %d\n", dwCapability);  
  24.     TCHAR bbuid[256] = { 0 };  
  25.     bb->get_UniqueKinectId(256, bbuid);  
  26.     printf("UID: %s\n",bbuid);  
  27.   
  28.   
  29. endstop:  
  30.     system("pause");  
  31.     return 0;  
  32. }  


在没有插上Kinect时的结果:

 

这里很容易发现一个奇怪的问题,获取默认Kinect设备成功,但是这个设备是不可用的,什么信息都获取不到,看来 GetDefaultKinectSensor 很不靠谱啊,需要通过 isAvaliable 判断一下才能使用。

下面,我们插上Kinect设备继续测试:

 

晕啊,傻眼了,为什么会这样子呢?我调试啊调试,然后拿着SDK中的Demo调试,甚至加上了bb->Open()调试,还是那个样子。经多方折腾,最终发现了Kinect不得不说的秘密啊!

Kinect默认是关闭状态,你必须Open之后才能判断是否有效等等内容。但是呢,Kinect的Open是需要时间的,在我的电脑上需要3S左右,否则即便isOpen为1,但是avaliable可能还是0。

再继续研究Kinect的头文件发现,它使用了rpc技术,这样导致函数的状态的回复不可能像本地机器码跑得那么快,所以你需要一定的时间等待Kinect更新自身的状态!

新的代码如下:

[cpp]  view plain  copy   在CODE上查看代码片 派生到我的代码片
  1. #include "stdafx.h"  
  2. #include "kinect.h"  
  3.   
  4. int _tmain(int argc, _TCHAR* argv[])  
  5. {  
  6.     printf("Hello, Wellcome to kinect world!\n");  
  7.     IKinectSensor* bb; //申请一个Sensor指针  
  8.     HRESULT hr = GetDefaultKinectSensor(&bb); // 获取一个默认的Sensor  
  9.     if ( FAILED(hr) )  
  10.     {  
  11.         printf("No Kinect connect to your pc!\n");  
  12.         goto endstop;  
  13.     }  
  14.     BOOLEAN bIsOpen = 0;  
  15.     bb->get_IsOpen(&bIsOpen); // 查看下是否已经打开  
  16.     printf("bIsOpen: %d\n", bIsOpen);  
  17.   
  18.     if ( !bIsOpen ) // 没打开,则尝试打开  
  19.     {  
  20.         hr = bb->Open();  
  21.         if ( FAILED(hr) )  
  22.         {  
  23.             printf("Kinect Open Failed!\n");  
  24.             goto endstop;  
  25.         }  
  26.         printf("Kinect opened! But it need sometime to work!\n");  
  27.         // 这里一定要多等会,否则下面的判断都是错误的  
  28.         printf("Wait For 3000 ms...\n");  
  29.         Sleep(3000);  
  30.     }  
  31.     bIsOpen = 0;  
  32.     bb->get_IsOpen(&bIsOpen); // 是否已经打开  
  33.     printf("bIsOpen: %d\n", bIsOpen);  
  34.     BOOLEAN bAvaliable = 0;  
  35.     bb->get_IsAvailable(&bAvaliable); // 是否可用  
  36.     printf("bAvaliable: %d\n", bAvaliable);  
  37.   
  38.     DWORD dwCapability = 0;   
  39.     bb->get_KinectCapabilities(&dwCapability); // 获取容量  
  40.     printf("dwCapability: %d\n", dwCapability);  
  41.     TCHAR bbuid[256] = { 0 };  
  42.     bb->get_UniqueKinectId(256, bbuid); // 获取唯一ID  
  43.     printf("UID: %s\n",bbuid);  
  44.   
  45.     bb->Close();  
  46. endstop:  
  47.     system("pause");  
  48.     return 0;  
  49. }  


接上Kinect结果如下:

 

哈哈,圆满完成任务。只不过,实际情况应该轮询检查Kinect的状态,不应该使用Sleep()草草替代了。

2、Kinect音频功能探索

由于Kinect音频是一个麦克风矩阵,所以可以进行声音方向的探测,这个功能比较喜人。当然,你也可以使用它结合微软的语音识别引擎,进行语音识别和控制,这个是个比较大的专题,这里不做讨论。

我们先来进行声源的判断。接着上面的内容,通过GetDefaultKinectSensor 获取默认Kinect设备,然后Open,接着就可以通过它的成员函数 get_AudioSource 获取音频源,然后根据音频源的成员函数 get_AudioBeams 获取波束列表,然后 OpenAudioBeam 获取第一个波束,为什么是第一个呢?因为现在只支持第一个。最后波束beam的成员函数get_BeamAngle 获取角度,get_BeamAngleConfidence 获取对应的可信度。

[cpp]  view plain  copy   在CODE上查看代码片 派生到我的代码片
  1. #include "stdafx.h"  
  2. #include "kinect.h"  
  3.   
  4. int _tmain(int argc, _TCHAR* argv[])  
  5. {  
  6.     printf("Hello, Wellcome to kinect world!\n");  
  7.     IKinectSensor* bb; //申请一个Sensor指针  
  8.     HRESULT hr = GetDefaultKinectSensor(&bb); // 获取一个默认的Sensor  
  9.     if ( FAILED(hr) )  
  10.     {  
  11.         printf("No Kinect connect to your pc!\n");  
  12.         goto endstop;  
  13.     }  
  14.     BOOLEAN bIsOpen = 0;  
  15.     bb->get_IsOpen(&bIsOpen); // 查看下是否已经打开  
  16.     printf("bIsOpen: %d\n", bIsOpen);  
  17.   
  18.     if ( !bIsOpen ) // 没打开,则尝试打开  
  19.     {  
  20.         hr = bb->Open();  
  21.         if ( FAILED(hr) )  
  22.         {  
  23.             printf("Kinect Open Failed!\n");  
  24.             goto endstop;  
  25.         }  
  26.         printf("Kinect opened! But it need sometime to work!\n");  
  27.         // 这里一定要多等会,否则下面的判断都是错误的  
  28.         printf("Wait For 3000 ms...\n");  
  29.         Sleep(3000);  
  30.     }  
  31.     bIsOpen = 0;  
  32.     bb->get_IsOpen(&bIsOpen); // 是否已经打开  
  33.     printf("bIsOpen: %d\n", bIsOpen);  
  34.     BOOLEAN bAvaliable = 0;  
  35.     bb->get_IsAvailable(&bAvaliable); // 是否可用  
  36.     printf("bAvaliable: %d\n", bAvaliable);  
  37.   
  38.     DWORD dwCapability = 0;   
  39.     bb->get_KinectCapabilities(&dwCapability); // 获取容量  
  40.     printf("dwCapability: %d\n", dwCapability);  
  41.     TCHAR bbuid[256] = { 0 };  
  42.     bb->get_UniqueKinectId(256, bbuid); // 获取唯一ID  
  43.     printf("UID: %s\n",bbuid);  
  44.   
  45.     // 音频数据获取  
  46.     IAudioSource* audios = nullptr;  
  47.     UINT nAudioCount = 0;  
  48.     hr = bb->get_AudioSource(&audios);  
  49.     if ( FAILED(hr) )  
  50.     {  
  51.         printf("Audio Source get failed!\n");  
  52.         goto endclose;  
  53.     }  
  54.     IAudioBeam* audiobm = nullptr;  
  55.     IAudioBeamList* audiobml = nullptr;  
  56.     audios->get_AudioBeams(&audiobml);  
  57.     audiobml->OpenAudioBeam(0, &audiobm); // 目前只支持第一个  
  58.   
  59.     float fAngle = 0.0f;  
  60.     float fAngleConfidence = 0.0f;  
  61.     while (true)  
  62.     {  
  63.         fAngle = 0.0f;  
  64.         fAngleConfidence = 0.0f;  
  65.         audiobm->get_BeamAngle(&fAngle); // 获取音频的角度,[ -0.872665f, 0.8726665f ]  
  66.         audiobm->get_BeamAngleConfidence(&fAngleConfidence); // 获取音频的可信度(0 - 1)  
  67.         printf("Angle: %3.2f (%1.2f)\n", (fAngle/3.1415926f)*180.0f, fAngleConfidence);  
  68.         Sleep(200);  
  69.     }  
  70.   
  71. endclose:  
  72.     bb->Close();  
  73. endstop:  
  74.     system("pause");  
  75.     return 0;  
  76. }  

下面进行音频数据的获取。

我们可以用一般获取录音一样获取音频流,请注意,从这里获取的音频流是原始数据:麦克风列阵获取的多声道音频,并且没有利用麦克风列阵进行降噪处理。代码可以查看SDK自带的获取原始数据的例子,因为与通用设备打交道,很麻烦,这里不做说明。

这里说的是利用自带的方法,获取经处理的音频数据。经过处理的数据信息如下:

l 编码:    32位标准浮点(IEEE FLOAT) 

l 声道:    1 

l 采样率: 16000Hz

SDK中获取处理后的音频流有两种方法,一种是音频帧,和之前的各种帧差不多:

    

[cpp]  view plain  copy   在CODE上查看代码片 派生到我的代码片
  1. // 获取音频源(AudioSource)    
  2.     if (SUCCEEDED(hr)){    
  3.         hr = m_pKinect->get_AudioSource(&pAudioSource);    
  4.     }    
  5.     // 再获取音频帧读取器    
  6.     if (SUCCEEDED(hr)){    
  7.         hr = pAudioSource->OpenReader(&m_pAudioBeamFrameReader);    
  8.     }  
  9.     // 注册临帧事件    
  10.     if (SUCCEEDED(hr)){    
  11.         m_pAudioBeamFrameReader->SubscribeFrameArrived(&m_hAudioBeamFrameArrived);    
  12.     }    

这样初始化。使用后,像之前那样,根据事件获取 AudioBeamFrameArrivedEventArgs, 再获取 AudioBeamFrameReference 音频帧引用,再获取 AudioBeamFrameList 音频帧链表,目前链表只有一个元素,直接获取 AudioBeamFrame音频帧。音频帧可能包含复数 AudioBeamSubFrame 音频副帧(比如本人这里包含2个),这个东西才能获取音频流的真正信息。

还有就是IStream,前面的这不是指C++标准库的输入流,而是COM组件的“流接口”,可读可写。初始化代码如下:

   

[cpp]  view plain  copy   在CODE上查看代码片 派生到我的代码片
  1. if (SUCCEEDED(hr))  
  2.    {  
  3.        hr = m_pKinectSensor->get_AudioSource(&pAudioSource);  
  4.    }  
  5.    if (SUCCEEDED(hr))  
  6.    {  
  7.        hr = pAudioSource->get_AudioBeams(&pAudioBeamList);  
  8.    }  
  9.    if (SUCCEEDED(hr))  
  10.    {  
  11.        hr = pAudioBeamList->OpenAudioBeam(0, &m_pAudioBeam);  
  12.    }  
  13.    if (SUCCEEDED(hr))  
  14.    {          
  15.        hr = m_pAudioBeam->OpenInputStream(&m_pAudioStream);  
  16.    }  

m_pAudioSteam就是Steam对象,使用时ISteam::Read(void*, ULONG, ULONG*)主动获取音频数据。相比而言,使用音频帧既可以主动获取,又能使用事件机制,而Stream只能主动获取。

我们这里使用第二种流的方式,编写一个Demo,代码如下:

[cpp]  view plain  copy   在CODE上查看代码片 派生到我的代码片
  1. #include "stdafx.h"  
  2. #include "kinect.h"  
  3. #define _USE_MATH_DEFINES  
  4. #include <math.h>  
  5.   
  6. int _tmain(int argc, _TCHAR* argv[])  
  7. {  
  8.     printf("Hello, Wellcome to kinect world!\n");  
  9.     IKinectSensor* bb; //申请一个Sensor指针  
  10.     HRESULT hr = GetDefaultKinectSensor(&bb); // 获取一个默认的Sensor  
  11.     if ( FAILED(hr) )  
  12.     {  
  13.         printf("No Kinect connect to your pc!\n");  
  14.         goto endstop;  
  15.     }  
  16.     BOOLEAN bIsOpen = 0;  
  17.     bb->get_IsOpen(&bIsOpen); // 查看下是否已经打开  
  18.     printf("bIsOpen: %d\n", bIsOpen);  
  19.   
  20.     if ( !bIsOpen ) // 没打开,则尝试打开  
  21.     {  
  22.         hr = bb->Open();  
  23.         if ( FAILED(hr) )  
  24.         {  
  25.             printf("Kinect Open Failed!\n");  
  26.             goto endstop;  
  27.         }  
  28.         printf("Kinect opened! But it need sometime to work!\n");  
  29.         // 这里一定要多等会,否则下面的判断都是错误的  
  30.         printf("Wait For 3000 ms...\n");  
  31.         Sleep(3000);  
  32.     }  
  33.     bIsOpen = 0;  
  34.     bb->get_IsOpen(&bIsOpen); // 是否已经打开  
  35.     printf("bIsOpen: %d\n", bIsOpen);  
  36.     BOOLEAN bAvaliable = 0;  
  37.     bb->get_IsAvailable(&bAvaliable); // 是否可用  
  38.     printf("bAvaliable: %d\n", bAvaliable);  
  39.   
  40.     DWORD dwCapability = 0;   
  41.     bb->get_KinectCapabilities(&dwCapability); // 获取容量  
  42.     printf("dwCapability: %d\n", dwCapability);  
  43.     TCHAR bbuid[256] = { 0 };  
  44.     bb->get_UniqueKinectId(256, bbuid); // 获取唯一ID  
  45.     printf("UID: %s\n",bbuid);  
  46.   
  47.     // 音频数据获取  
  48.     IAudioSource* audios = nullptr;  
  49.     UINT nAudioCount = 0;  
  50.     hr = bb->get_AudioSource(&audios);  
  51.     if ( FAILED(hr) )  
  52.     {  
  53.         printf("Audio Source get failed!\n");  
  54.         goto endclose;  
  55.     }  
  56.     IAudioBeam* audiobm = nullptr;  
  57.     IAudioBeamList* audiobml = nullptr;  
  58.     audios->get_AudioBeams(&audiobml);  
  59.     audiobml->OpenAudioBeam(0, &audiobm); // 目前只支持第一个  
  60.     IStream* stm = nullptr;  
  61.     audiobm->OpenInputStream(&stm);  
  62.     audios->Release();  
  63.     audiobm->Release();  
  64.   
  65.     float fAngle = 0.0f;  
  66.     float fAngleConfidence = 0.0f;  
  67.     ULONG lRead = 0;  
  68.     const ULONG lBufferSize =3200;  
  69.     float* fDataArr = new float[lBufferSize];  
  70.     while (true)  
  71.     {  
  72.         fAngle = 0.0f;  
  73.         fAngleConfidence = 0.0f;  
  74.         audiobm->get_BeamAngle(&fAngle); // 获取音频的角度,[ -0.872665f, 0.8726665f ]  
  75.         audiobm->get_BeamAngleConfidence(&fAngleConfidence); // 获取音频的可信度(0 - 1)  
  76.         if ( fAngleConfidence > 0.5f )  
  77.             printf("Angle: %3.2f (%1.2f)\n", (fAngle)*180.0f/static_cast<float>(M_PI), fAngleConfidence);  
  78.         // audio  data  
  79.         lRead = 0;  
  80.         memset(fDataArr, 0, lBufferSize);  
  81.         stm->Read(fDataArr, lBufferSize, &lRead);  
  82.         if ( lRead > 0 )  
  83.         {  
  84.             printf("Audio Buffer: %d\n", lRead);  
  85.         }  
  86.         Sleep(200);  
  87.     }  
  88.   
  89. endclose:  
  90.     bb->Close();  
  91. endstop:  
  92.     system("pause");  
  93.     return 0;  
  94. }  


完毕


猜你喜欢

转载自blog.csdn.net/zhazhiqiang2010/article/details/52527498
今日推荐