Kinect V2 開發專題(3)裝置資訊擷取與音頻功能探索

來源:互聯網
上載者:User
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了,所以...你是不可能用的,將來可能還會加上。我們瞭解下就好了,下面我找了一份舊代碼大家看看就可以了。

    IKinectSensorCollection* pKinectCollection = nullptr;    IEnumKinectSensor* pEnumKinect = nullptr;    IKinectSensor* pKinect = nullptr;    // 擷取Kinect集合    HRESULT hr = ::GetKinectSensorCollection(&pKinectCollection);    // 擷取Kinect列舉程式    if (SUCCEEDED(hr)){        hr = pKinectCollection->get_Enumerator(&pEnumKinect);    }    // 枚舉Kinect    if (SUCCEEDED(hr)){        BOOLEAN available = false;        while (true){            // 擷取下一個            if (SUCCEEDED(pEnumKinect->GetNext(&pKinect))){                // 判斷有效性                pKinect->get_IsAvailable(&available);                if (available && YourJudgmentFunc(pKinect)){                    break;                }                SafeRelease(pKinect);            }            else                break;        }    }    SafeRelease(pEnumKinect);    SafeRelease(pKinectCollection);


 

不要在意這些細節,這都是小事。我們可以通過 GetDefaultKinectSensor(_COM_Outptr_ IKinectSensor** defaultKinectSensor); 這個函數擷取預設的Kinect裝置,哈哈哈。

下面是一段測試的小程式:

#include "stdafx.h"#include "kinect.h"int _tmain(int argc, _TCHAR* argv[]){printf("Hello, Wellcome to kinect world!\n");//IKinectSensorCollection aa;IKinectSensor* bb;HRESULT hr = GetDefaultKinectSensor(&bb);if ( FAILED(hr) ){printf("No Kinect connect to your pc!\n");goto endstop;}BOOLEAN bAvaliable = 0;bb->get_IsAvailable(&bAvaliable);printf("bAvaliable: %d\n", bAvaliable);BOOLEAN bIsOpen = 0;bb->get_IsOpen(&bIsOpen);printf("bIsOpen: %d\n", bIsOpen);DWORD dwCapability = 0; bb->get_KinectCapabilities(&dwCapability);printf("dwCapability: %d\n", dwCapability);TCHAR bbuid[256] = { 0 };bb->get_UniqueKinectId(256, bbuid);printf("UID: %s\n",bbuid);endstop:system("pause");return 0;}


 

在沒有插上Kinect時的結果:

 

 

這裡很容易發現一個奇怪的問題,擷取預設Kinect裝置成功,但是這個裝置是停用,什麼資訊都擷取不到,看來 GetDefaultKinectSensor 很不靠譜啊,需要通過 isAvaliable 判斷一下才能使用。

下面,我們插上Kinect裝置繼續測試:

 

暈啊,傻眼了,為什麼會這樣子呢。我調試啊調試,然後拿著SDK中的Demo調試,甚至加上了bb->Open()調試,還是那個樣子。經多方折騰,最終發現了Kinect不得不說的秘密啊。

Kinect預設是關閉狀態,你必須Open之後才能判斷是否有效等等內容。但是呢,Kinect的Open是需要時間的,在我的電腦上需要3S左右,否則即便isOpen為1,但是avaliable可能還是0。

再繼續研究Kinect的標頭檔發現,它使用了rpc技術,這樣導致函數的狀態的回複不可能像本地機器碼跑得那麼快,所以你需要一定的時間等待Kinect更新自身的狀態。

新的代碼如下:

#include "stdafx.h"#include "kinect.h"int _tmain(int argc, _TCHAR* argv[]){printf("Hello, Wellcome to kinect world!\n");IKinectSensor* bb; //申請一個Sensor指標HRESULT hr = GetDefaultKinectSensor(&bb); // 擷取一個預設的Sensorif ( FAILED(hr) ){printf("No Kinect connect to your pc!\n");goto endstop;}BOOLEAN bIsOpen = 0;bb->get_IsOpen(&bIsOpen); // 查看下是否已經開啟printf("bIsOpen: %d\n", bIsOpen);if ( !bIsOpen ) // 沒開啟,則嘗試開啟{hr = bb->Open();if ( FAILED(hr) ){printf("Kinect Open Failed!\n");goto endstop;}printf("Kinect opened! But it need sometime to work!\n");// 這裡一定要多等會,否則下面的判斷都是錯誤的printf("Wait For 3000 ms...\n");Sleep(3000);}bIsOpen = 0;bb->get_IsOpen(&bIsOpen); // 是否已經開啟printf("bIsOpen: %d\n", bIsOpen);BOOLEAN bAvaliable = 0;bb->get_IsAvailable(&bAvaliable); // 是否可用printf("bAvaliable: %d\n", bAvaliable);DWORD dwCapability = 0; bb->get_KinectCapabilities(&dwCapability); // 擷取容量printf("dwCapability: %d\n", dwCapability);TCHAR bbuid[256] = { 0 };bb->get_UniqueKinectId(256, bbuid); // 擷取唯一IDprintf("UID: %s\n",bbuid);bb->Close();endstop:system("pause");return 0;}


 

接上Kinect結果如下:

 

哈哈,圓滿完成任務。只不過,實際情況應該輪詢檢查Kinect的狀態,不應該使用Sleep()草草替代了。

  2、Kinect音頻功能探索

由於Kinect音頻是一個麥克風矩陣,所以可以進行聲音方向的探測,這個功能比較喜人。當然,你也可以使用它結合微軟的語音辨識引擎,進行語音辨識和控制,這個是個比較大的專題,這裡不做討論。

我們先來進行聲源的判斷。接著上面的內容,通過GetDefaultKinectSensor 擷取預設Kinect裝置,然後Open,接著就可以通過它的成員函數 get_AudioSource 擷取音頻源,然後根據音頻源的成員函數 get_AudioBeams 擷取波束列表,然後 OpenAudioBeam 擷取第一個波束,為什麼是第一個呢。因為現在只支援第一個。最後波束beam的成員函數get_BeamAngle 擷取角度,get_BeamAngleConfidence 擷取對應的可信度。

 

#include "stdafx.h"#include "kinect.h"int _tmain(int argc, _TCHAR* argv[]){printf("Hello, Wellcome to kinect world!\n");IKinectSensor* bb; //申請一個Sensor指標HRESULT hr = GetDefaultKinectSensor(&bb); // 擷取一個預設的Sensorif ( FAILED(hr) ){printf("No Kinect connect to your pc!\n");goto endstop;}BOOLEAN bIsOpen = 0;bb->get_IsOpen(&bIsOpen); // 查看下是否已經開啟printf("bIsOpen: %d\n", bIsOpen);if ( !bIsOpen ) // 沒開啟,則嘗試開啟{hr = bb->Open();if ( FAILED(hr) ){printf("Kinect Open Failed!\n");goto endstop;}printf("Kinect opened! But it need sometime to work!\n");// 這裡一定要多等會,否則下面的判斷都是錯誤的printf("Wait For 3000 ms...\n");Sleep(3000);}bIsOpen = 0;bb->get_IsOpen(&bIsOpen); // 是否已經開啟printf("bIsOpen: %d\n", bIsOpen);BOOLEAN bAvaliable = 0;bb->get_IsAvailable(&bAvaliable); // 是否可用printf("bAvaliable: %d\n", bAvaliable);DWORD dwCapability = 0; bb->get_KinectCapabilities(&dwCapability); // 擷取容量printf("dwCapability: %d\n", dwCapability);TCHAR bbuid[256] = { 0 };bb->get_UniqueKinectId(256, bbuid); // 擷取唯一IDprintf("UID: %s\n",bbuid);// 音頻資料擷取IAudioSource* audios = nullptr;UINT nAudioCount = 0;hr = bb->get_AudioSource(&audios);if ( FAILED(hr) ){printf("Audio Source get failed!\n");goto endclose;}IAudioBeam* audiobm = nullptr;IAudioBeamList* audiobml = nullptr;audios->get_AudioBeams(&audiobml);audiobml->OpenAudioBeam(0, &audiobm); // 目前只支援第一個float fAngle = 0.0f;float fAngleConfidence = 0.0f;while (true){fAngle = 0.0f;fAngleConfidence = 0.0f;audiobm->get_BeamAngle(&fAngle); // 擷取音訊角度,[ -0.872665f, 0.8726665f ]audiobm->get_BeamAngleConfidence(&fAngleConfidence); // 擷取音訊可信度(0 - 1)printf("Angle: %3.2f (%1.2f)\n", (fAngle/3.1415926f)*180.0f, fAngleConfidence);Sleep(200);}endclose:bb->Close();endstop:system("pause");return 0;}

 

下面進行音頻資料的擷取。

我們可以用一般擷取錄音一樣擷取音頻流,請注意,從這裡擷取的音頻流是未經處理資料:麥克風列陣擷取的多頻道音訊,並且沒有利用麥克風列陣進行降噪處理。代碼可以查看SDK內建的擷取未經處理資料的例子,因為與通用裝置打交道,很麻煩,這裡不做說明。

這裡說的是利用內建的方法,擷取經處理的音頻資料。經過處理的資料資訊如下:

l 編碼:    32位標準浮點(IEEE FLOAT) 

l 聲道:    1 

l 採樣率: 16000Hz

 

SDK中擷取處理後的音頻流有兩種方法,一種是音訊框架,和之前的各種幀差不多:

    

// 擷取音頻源(AudioSource)      if (SUCCEEDED(hr)){          hr = m_pKinect->get_AudioSource(&pAudioSource);      }      // 再擷取音訊框架讀取器      if (SUCCEEDED(hr)){          hr = pAudioSource->OpenReader(&m_pAudioBeamFrameReader);      }    // 註冊臨幀事件      if (SUCCEEDED(hr)){          m_pAudioBeamFrameReader->SubscribeFrameArrived(&m_hAudioBeamFrameArrived);      }  

 

這樣初始化。使用後,像之前那樣,根據事件擷取 AudioBeamFrameArrivedEventArgs, 再擷取 AudioBeamFrameReference 音訊框架引用,再擷取 AudioBeamFrameList 音訊框架鏈表,目前鏈表只有一個元素,直接擷取 AudioBeamFrame音訊框架。音訊框架可能包含複數 AudioBeamSubFrame 音頻副幀(比如本人這裡包含2個),這個東西才能擷取音頻流的真正資訊。

還有就是IStream,前面的這不是指C++標準庫的輸入資料流,而是COM組件的“流介面”,可讀可寫。初始化代碼如下:

   

 if (SUCCEEDED(hr))    {        hr = m_pKinectSensor->get_AudioSource(&pAudioSource);    }    if (SUCCEEDED(hr))    {        hr = pAudioSource->get_AudioBeams(&pAudioBeamList);    }    if (SUCCEEDED(hr))    {        hr = pAudioBeamList->OpenAudioBeam(0, &m_pAudioBeam);    }    if (SUCCEEDED(hr))    {                hr = m_pAudioBeam->OpenInputStream(&m_pAudioStream);    }

m_pAudioSteam就是Steam對象,使用時ISteam::Read(void*, ULONG, ULONG*)主動擷取音頻資料。相比而言,使用音訊框架既可以主動擷取,又能使用事件機制,而Stream只能主動擷取。

 

我們這裡使用第二種流的方式,編寫一個Demo,代碼如下:

#include "stdafx.h"#include "kinect.h"#define _USE_MATH_DEFINES#include <math.h>int _tmain(int argc, _TCHAR* argv[]){printf("Hello, Wellcome to kinect world!\n");IKinectSensor* bb; //申請一個Sensor指標HRESULT hr = GetDefaultKinectSensor(&bb); // 擷取一個預設的Sensorif ( FAILED(hr) ){printf("No Kinect connect to your pc!\n");goto endstop;}BOOLEAN bIsOpen = 0;bb->get_IsOpen(&bIsOpen); // 查看下是否已經開啟printf("bIsOpen: %d\n", bIsOpen);if ( !bIsOpen ) // 沒開啟,則嘗試開啟{hr = bb->Open();if ( FAILED(hr) ){printf("Kinect Open Failed!\n");goto endstop;}printf("Kinect opened! But it need sometime to work!\n");// 這裡一定要多等會,否則下面的判斷都是錯誤的printf("Wait For 3000 ms...\n");Sleep(3000);}bIsOpen = 0;bb->get_IsOpen(&bIsOpen); // 是否已經開啟printf("bIsOpen: %d\n", bIsOpen);BOOLEAN bAvaliable = 0;bb->get_IsAvailable(&bAvaliable); // 是否可用printf("bAvaliable: %d\n", bAvaliable);DWORD dwCapability = 0; bb->get_KinectCapabilities(&dwCapability); // 擷取容量printf("dwCapability: %d\n", dwCapability);TCHAR bbuid[256] = { 0 };bb->get_UniqueKinectId(256, bbuid); // 擷取唯一IDprintf("UID: %s\n",bbuid);// 音頻資料擷取IAudioSource* audios = nullptr;UINT nAudioCount = 0;hr = bb->get_AudioSource(&audios);if ( FAILED(hr) ){printf("Audio Source get failed!\n");goto endclose;}IAudioBeam* audiobm = nullptr;IAudioBeamList* audiobml = nullptr;audios->get_AudioBeams(&audiobml);audiobml->OpenAudioBeam(0, &audiobm); // 目前只支援第一個IStream* stm = nullptr;audiobm->OpenInputStream(&stm);audios->Release();audiobm->Release();float fAngle = 0.0f;float fAngleConfidence = 0.0f;ULONG lRead = 0;const ULONG lBufferSize =3200;float* fDataArr = new float[lBufferSize];while (true){fAngle = 0.0f;fAngleConfidence = 0.0f;audiobm->get_BeamAngle(&fAngle); // 擷取音訊角度,[ -0.872665f, 0.8726665f ]audiobm->get_BeamAngleConfidence(&fAngleConfidence); // 擷取音訊可信度(0 - 1)if ( fAngleConfidence > 0.5f ) printf("Angle: %3.2f (%1.2f)\n", (fAngle)*180.0f/static_cast<float>(M_PI), fAngleConfidence);// audio  datalRead = 0;memset(fDataArr, 0, lBufferSize);stm->Read(fDataArr, lBufferSize, &lRead);if ( lRead > 0 ){printf("Audio Buffer: %d\n", lRead);}Sleep(200);}endclose:bb->Close();endstop:system("pause");return 0;}


完畢


 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.