10 分鐘 Kinect for Windows 應用入門

看看別人的創意應用

10 分鐘入門簡報及範例程式

Kinect for Windows SDK 下載網址:http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk

Kinect for Windows SDK Beta 應用條款摘要:

2011 年 6 月所提供下載的 SDK 為 Beta 版,目前提供用於 non-commercial (非商業用途),例如:研究、展示、開發測試、教學用途;也可以此 SDK 進行教學、以 blog 等型式進行分享或透過為人寫 AP 收費,亦可先行以此 SDK 開發未來作為商業使用的應用程式。詳細條款說明,請參考 SDK 下載網站及 EULA 說明。

Kinect for Windows 開發 - 邊做邊學

Kinect 應用程式開發入門 (連結外部影音播放)

曹祖聖 jimycao@syset.com


Kinect 體感遊戲在Xbox 360 上獲得極高的好評,但是對於 Windows 平台上的開發卻一直只能使用非官方的解決方案,例如 NKinect (http://nkinect.codeplex.com) 配合CL NUI SDK (http://codelaboratories.com/nui);但是微軟終於在 2011 年 6 月推出了 Kinect for Windows SDK Beta,特別是可以使用 C# 與.NET Framework 4.0 來進行開發,這對 .NET 開發人員來說無疑是 2011 年最好的消息了。

Kinect for Windows SDK : http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk

本篇文章將詳細的介紹在 Windows 7 上開發 Kinect 應用程式所需要的一切知識,包含硬體原理、開發環境安裝、SDK 的使用與 API 呼叫方式,相信可以協助你一步一步的進入 Kinect 神奇的開發領域。

一、Kinect 硬體與原理介紹

Kinect 感應器可以取得以下三種資訊:

  • 彩色影像 (透過中間那顆 RGB 鏡頭)
  • 3D 深度影像 (透過左右兩顆鏡頭)
    • 紅外線發射器和紅外線 CMOS 攝影機
  • 聲音 (透過陣列式麥克風)

Kinect 也支援追焦功能,底座馬達會隨著焦點人物而轉動 Kinect 方向 (左右各 28 度),下表是 Kinect 的詳細規格:

感應項目 有效範圍
顏色與深度 1.2 ~ 3.6 公尺
骨架追蹤 1.2 ~ 3.6 公尺
視野角度 水平 57 度、垂直 43 度
底座馬達旋轉 左右各 28 度
每秒畫格 30 FPS
深度解析度 QVGA (320 x 240)
顏色解析度 VGA (640 x 480)
聲音格式 16KHz, 16 位元 mono pulse code modulation (PCM)
聲音輸入 四麥克風陣列、24 位元類比數位轉換 (ADC)、雜音消除

二、Kinect 開發環境安裝與設定

在進行 Kinect 應用程式開發之前,你需要準備好相關的軟硬體,需求如下:

安裝 Kinect For Windows SDK 的步驟:

  1. 請不要接上 Kinect 感應器

  2. 不要開啓 Visual Studio 2010 或 Visual C# 2010 Express

  3. 依照你的 Windows 7 是 32 還是 64 位元,安裝對應的 Kinect SDK

  4. 安裝完畢後請務必要重新開機,以便系統可以正確識別 SDK 所需要的環境變數

    MSRKINECTSDK=C:\Program Files (x86)\Microsoft Research KinectSDK\

Kinect 感應器安裝:

  • 安裝 Kinect 感應器
    1. 確定 Internet 連線正常
    2. 接上 Kinect 外接電源
    3. 接上電腦 USB 接口
    4. 會自動下載必要驅動程式
  • 檢查
    • 裝置管理員
      • 會出現如下圖所示的裝置:

    • 硬體狀態
      • 主機的燈會亮起,如下圖:

安裝 Coding4Fun Kinect Toolkit

這個工具包主要是將一些在開發 Kinect 應用程式時會使用到的程式碼整理成擴充方法,讓你在開發 Kinect 應用程式時可以簡化程式碼的撰寫。

三、Kinect for Windows 架構

Kinect 的 NUI 程式庫提供應用程式取得 Kinect 感應器傳送至主機的三種資訊串流 (必須在初始化 API 時指定要接收那幾種串流):

  • 彩色影像串流
  • 深度影像串流
  • 聲音串流

下圖就是完整的 Kinect for Windows SDK 架構圖:

  1. Kinect 硬體
    這部份指的就是 Kinect 底座的馬達、三個攝影機 (RGB 攝影機、紅外線 CMOS 攝影機、紅外線發射器)、以及陣列式麥克風。
  2. Kinect 驅動程式
    核心模式下包含了以下驅動程式:
    • Microsoft Kinect Audio Array Control
    • Microsoft Kinect Camera
    • Microsoft Kinect Device
    • Kinect USB Audio
  3. NUI API
  4. 麥克風陣列 DMO 編碼器
  5. Windows 7 內建影音處理
    也就是架構圖中灰色底的部分,這就是為什麼 Kinect for Windows SDK 只支援 Windows 7 的主要原因了。

四、NUI API 初始化

要使用 Kinect API 接收感應器的資訊,是透過 Runtime 物件,因此 Kinect 應用程式的第一步就是建立一個 Runtime 物件來準備接收感應器的資料,然後呼叫 Initialize 方法進行初始化 (指定要接收那類型的資料),在應用程式結束時要呼叫 Uninitialize 方法,關閉 Kinect 設備。

// 建立一個 Runtime 物件,代表 Kinect 設備 Runtime nui = new Runtime(); // 初始化設備 nui.Initialize(); // 開始使用 Runtime 物件的方法、事件, // 取得影像、骨架資料、控制攝影機 // 關閉設備 nui.Uninitialize();

其中你可以透過 Device 物件的 Count 屬性來取得目前主機上總共連接了幾台 Kinect 設備,在建立 Runtime 物件時,可以在建構式參數中加上所要連接的 Kinect 設備編號

Device d = new Device(); if (d.Count > 0) { int index = 0; // 建立一個 Runtime 物件,index 代表 Kinect 設備的編號 Runtime nui = new Runtime(index); }

NUI API 初始化選項:

  • NUI API 使用多段式管線來取得 Kinect 的資料,也就是你可以同時透過不同的管線來接收不同類型的串流資料。
  • Runtime 物件可以額外指定要啓動那些所需要的管線,在初始化 Runtime 物件時使用 RuntimeOptions 列舉值來指定:
    • UseColor:取得彩色影像串流
    • UseDepth:取得影像影像深度串流
    • UseDepthAndPlayerIndex:從感應器取得骨架追蹤引擎產生的玩家編號來取得深度資料
    • UseSkeletalTracking:使用骨架位置資料

例如:

// 要取得彩色影像串流、深度與玩家編號的影像串流、追蹤骨架資訊 nui.Initialize( RuntimeOptions.UseColor | RuntimeOptions.UseDepthAndPlayerIndex | RuntimeOptions.UseSkeletalTracking );

五、取得彩色影像

彩色影像就是 RGB 攝影機所拍到的動態影像畫面 (如下圖左邊的影像):

  1. NUI 影像資料串流概觀:
    • 透過 NUI API,你可以修改 Kinect 感應器或取得感應器的資料。
    • 串流資料是以影像畫格 (frame) 的方式得到,所謂的畫格就是一張靜能圖片,一連串的畫格就組成了動態的影像了。
    • 在 NUI 初始化時,應用程式可以指定所要使用的串流。
    • 開啓串流時要指定額外的資訊: nui.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color);
      • 影像串流類型 (彩色還是深度串流)
      • 緩衝區數量 (1 ~ 4)
        絕大部份應用,只需要 2 個串流緩衝區,但是如果緩衝區滿了,NUI 執行時期就會丟棄較舊的畫格,以便讓新的畫格可以放入緩衝區中,所以如果電腦處理速度不夠快,最多可以指定使用 4 個串流緩衝區。
      • 串流解析度
      • 影像類型
  2. 彩色影像資料
    • 影像品質
      • 一般品質
        感應器取得 1280x1024 影像,轉換成 RGB 模式並壓縮後,才傳給 NUI 執行時期,NUI 執行時期將收到的資料解壓縮,再送給應用程式,因為使用壓縮技術,所以提升了畫格傳輸的速度到 30 FPS,但是卻降低了影像品質。
      • 高品質
        感應器不壓縮影像,直接送給 NUI 執行時期,畫格速度只能到 15 FPS,而且 NUI 需要更大的緩衝區。
    • 影像格式
      • RGB
        • 32 位元、線性 X8R8G8B8 彩色點陣圖 ( sRGB color space)
        • 應用程式在開啓串流時,需要在最後一個參數中指定 ImageType.Color 或 ImageType.ColorYuv 影像類型
      • YUV
        • 16 位元、Gamma 校正的線性 UYVY 彩色點陣圖
        • 等同RGB Gamma 校正
        • 使用較少的記憶體
        • 應用程式在開啓串流時,需要指定 ImageType.ColorYuvRaw
        • 只支援到640x480 15FPS 影像
      • 以上兩種格式都是來自同一組攝影機的資料
    • 取得影像資訊的方法有兩種:
      • 輪詢模型  (Polling Model)
        使用程式不斷的呼叫 ImageStream.GetNextFrame 方法,它會傳回 ImageFrame 物件,ImageFrame 物件中有一個 Image 屬性就是取得的影像資料;如果沒有傳回新畫格,你可以選擇等待下一張畫格到來,或者稍後再試,但是記得 NUI 影像攝影機 API 是永遠不會將同一張畫格提供兩次出來的。
      • 事件模型 (Event Model)
        處理以下兩種事件,並在事件中讀取 e.ImageFrame.Image。
        • Runtime.DepthFrameReady (深度影像接收事件)
        • Runtime.VideoFrameReady (彩色影像接收事件)

那麼該如何擷取彩色影像呢? 在使用了RuntimeOptions.UseColor 初始化 Runtime 物件後,就可以開始在 Runtime 物件的 VideoFrameReady 事件中接收彩色影像:

nui.Initialize(RuntimeOptions.UseColor); nui.VideoFrameReady += new EventHandler<ImageFrameReadyEventArgs>(nui_VideoFrameReady); nui.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color);

void nui_VideoFrameReady(object sender, ImageFrameReadyEventArgs e) { PlanarImage data = e.ImageFrame.Image; image1.Source = BitmapSource.Create(data.Width, data.Height, 96, 96, PixelFormats.Bgr32, null, data.Bits, data.Width * data.BytesPerPixel); }

如果覺得呼叫 BitmapSource.Create 方法太麻煩,可以使用 Coding4Fun 函式庫中的 ToBitmapSource 擴充方法

void nui_VideoFrameReady(object sender, ImageFrameReadyEventArgs e) { // 使用 Coding4Fun 函式庫,image1 是 WPF 的 Image 控制項 image1.Source = e.ImageFrame.ToBitmapSource(); }

六、取得深度影像

深度影像就是由紅外線發射器和紅外線 CMOS 攝影機共同取得的動態影像畫面 (如下圖右邊的影像):

  1. 深度資料
    在深度串流的畫格中,每一個像素 (16 位元) 的資料,表示在指定的 x, y 座標下,與最靠近感應器的物件的相對距離 (單位: mm),深度距離範圍 850mm ~ 4000mm,深度 0 則表示未知狀況,例如該位置是影子、過低反射 (玻璃)、過高反射 (鏡子)。
    • 支援以下串流
      • 640 x 480, 320 x 240, 80 x 60
    • 透過深度資料,應用程式可以
      • 追蹤使用者的動作
      • 標示出背景物件,並加以忽略
  2. 深度資料取得
    • ImageFrame.Image.Bits : 這是一個一維的 byte 陣列。
      從影像的左上角開始,先從左到右,再從上到下,每一個像素 (2 bytes) 的格式在開啓串流時由 ImageFormat 指定,例如我們要計算深度影像左上角像素 (0, 0) 所代表的距離:
      • ImageFormat.Depth
        • 第二個 byte 左位移 8 位元
        • (0,0) 距離= (int) (Bits[0] | Bits[1] << 8)
      • ImageFormat.DepthAndPlayerIndex
        在 Kinect for Windows SDK 中,Kinect 系統處理感應器資料,標定出感應器前方兩個人的外形,並且產生玩家區段對應,這個對應資料包含在深度資料中,其中像素的值對應到在該像素位置下,最靠近感應器的那一個玩家的編號。
        • (0,0) 距離= (int) (Bits[0] >> 3 | Bits[1] << 5)

        • 較低3 位元是玩家編號 (0~7)

          • 0 : 沒有玩家
          • 1 : 骨架#1
          • 2 : 骨架#2

那麼該如何擷取深度影像呢? 在使用了 RuntimeOptions. UseDepth 初始化 Runtime 物件後,就可以開始在 Runtime 物件的 DepthFrameReady 事件中接收深度影像:

nui.Initialize(RuntimeOptions.UseDepth); nui.DepthFrameReady += new EventHandler<ImageFrameReadyEventArgs>(nui_DepthFrameReady); nui.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution640x480, ImageType.Depth);

void nui_DepthFrameReady(object sender, ImageFrameReadyEventArgs e) { // 使用 Coding4Fun 函式庫,image2 是 WPF 的 Image 控制項 image2.Source = e.ImageFrame.ToBitmapSource(); }

七、骨架追蹤

Kinect 的骨架追蹤系統可以在感應器可視範圍內主動追蹤最多兩位玩家的骨架,骨架追蹤系統可以在感應器可視範圍內主動追蹤最多兩位玩家的骨架,如果某一個玩家被主動追蹤,那麼在呼叫 SkeletonEngine.GetNextFrame 時會取得該玩家完整的骨架資訊,預設只有前兩個玩家是主動被追蹤的。背動骨架追蹤預設是啓動的,可以額外追蹤 4 個玩家,但是由於處理速度的因素,背動追蹤下的骨架資訊有限。

NUI 骨架API 提供最多兩個玩家的骨架位置與方向的資訊,應用程式取得的是一堆座標的集合,稱為骨架關節位置 (skeleton joint positions),因此在初始化 NUI 時一定要指定要使用骨架資料,並且啓動骨架追蹤。

NUI API 初始化骨架追蹤的方式:

Runtime nui = new Runtime(); nui.Initialize(RuntimeOptions.UseSkeletalTracking); nui.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs>( nui_SkeletonFrameReady);

骨架資訊取得方式和取得彩色影像資訊類似,可以使用輪詢模型 (呼叫 SkeletonEngine.GetNextFrame 方法) 或事件模型 (在 SkeletonFrameReady 事件中讀取 e.SkeletonFrame.Skeletons 屬性)。

void nui_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e) { // 迴圈有可能跑多次 (多個玩家被追蹤) foreach(SkeletonData data in e.SkeletonFrame.Skeletons) { // 只要處理正在被追蹤的玩家 if (data.TrackingState == SkeletonTrackingState.Tracked) { JointCollection jc = data.Joints; Vector v = jc[JointID.Head].Position; // 處理關節座標 v } } }

其中你可以透過 SkeletonData 的Joints 集合來取得某一個玩家的所有關節的 3D 座標,你可以使用 JointID 這個列舉來指定要取得那一個關節的座標,例如 SkeletonData.Joints[JointID.Head].Position 可以取得玩家頭部的 3D 座標(Vector 型別),所有的關節位置與名稱如下圖:

關節資料說明:

  • 每次最多追蹤兩個玩家
    • 從六個玩家中選兩個
  • 每一個玩家有一個<x, y, z> 的關節座標集合
    • 單位:公尺 (Joint.Position.X, Y, Z, …)
  • 每一個關節都有一個對應的狀態
    • SkeletonTrackingState
      • Tracked – 表示被追蹤到了
      • PositionOnly – 表示被裁切掉、難以判斷、無法100% 確定
      • NotTracked – 表示該關節沒有被追蹤到 (很少發生,但是程式要注意一下這個狀態)

如果你要將關節 3D 座標轉換成螢幕上的 2D 座標時,例如要在畫面上繪製人體骨架位置,可以使用以下這個函式:

Point GetDisplayPosition(Joint joint) { float depthX, depthY; nui.SkeletonEngine.SkeletonToDepthImage( joint.Position, out depthX, out depthY); // 轉換到 320 x 240 空間系統 depthX = Math.Max(0, Math.Min(depthX * 320, 320)); depthY = Math.Max(0, Math.Min(depthY * 240, 240)); int colorX, colorY; nui.NuiCamera.GetColorPixelCoordinatesFromDepthPixel( ImageResolution.Resolution640x480, new ImageViewArea(), (int)depthX, (int)depthY, (short)0, out colorX, out colorY); return new Point(colorX * picColorVideo.Width / 640, colorY * picColorVideo.Height / 480); }

另外,由於有時候因為人體的快速動作,例如微小但是高頻率發生的抖動、或者突然的大跳動,都會造成關節的雜訊,因此可以啓動關節平順化的處理功能:

  • nui.SkeletonEngine.TransformSmooth = true;
    • 如果還要進一步做細步微調,可以使用TransformSmoothParameters 屬性來調整:

八、總結

Kinect for Windows SDK 的出現為個人電腦平台帶來了全新的應用程式操作方式,操作電腦不再侷限於鍵盤和滑鼠,透過 Kinect,不只是遊戲,只要你有創意,就可以在 Windows 7 平台上開發出有趣又有商機的 Kinect 應用。

【範例資源下載】