首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

Kinect for Windows SDK 開發入門(四):景深數據處理與視覺化(上)

     編輯:關於.NET

Kinect 感測器與景深資料流

Kinect 感測器的核心功能之一在於產生三維景深數據,開發者可藉此建立各類創新應用。在著手開發前,了解 Kinect 的硬體架構至關重要。Kinect 配備紅外發射器與紅外攝像頭,能偵測人體及非人體物體(如椅子、咖啡杯)。許多商業機構與實驗室正積極研究利用景深數據進行物體偵測。

本文將詳細介紹 Kinect 紅外感測器原理、景深數據格式、景深影像的取得與顯示,以及景深影像的增強處理技術。

1. Kinect 感測器與景深影像串流

與一般輸入裝置不同,Kinect 能產生三維數據。其紅外發射器與攝像頭配合,透過紅外光投射與接收計算深度。微軟 Kinect SDK 不提供直接存取原始紅外數據流的方法,而是將紅外攝像頭捕獲的數據經運算後,輸出為景深影像幀(DepthImageFrame),由 DepthImageStream 物件提供。

DepthImageStream 的使用方式與 ColorImageStream 類似,兩者皆繼承自 ImageStream。以下示範在 WPF 應用程式中顯示景深影像的步驟:

  1. 建立新的 WPF 專案。
  2. 加入 Microsoft.Kinect.dll 參考。
  3. 在 UI 加入 Image 元素,命名為 DepthImage。
  4. 撰寫發現與釋放 KinectSensor 物件的程式碼(參考前幾篇文章)。
  5. 初始化 KinectSensor 並啟用景深串流:
private void InitializeKinectSensor(KinectSensor kinectSensor) {
    if (kinectSensor != null) {
        DepthImageStream depthStream = kinectSensor.DepthStream;
        depthStream.Enable();
        depthImageBitMap = new WriteableBitmap(
            depthStream.FrameWidth, depthStream.FrameHeight, 96, 96,
            PixelFormats.Gray16, null);
        depthImageBitmapRect = new Int32Rect(0, 0, depthStream.FrameWidth, depthStream.FrameHeight);
        depthImageStride = depthStream.FrameWidth * depthStream.FrameBytesPerPixel;
        DepthImage.Source = depthImageBitMap;
        kinectSensor.DepthFrameReady += kinectSensor_DepthFrameReady;
        kinectSensor.Start();
    }
}
  1. 處理 DepthFrameReady 事件,將景深像素資料寫入 WriteableBitmap:
void kinectSensor_DepthFrameReady(object sender, DepthImageFrameReadyEventArgs e) {
    using (DepthImageFrame depthFrame = e.OpenDepthImageFrame()) {
        if (depthFrame != null) {
            short[] depthPixelData = new short[depthFrame.PixelDataLength];
            depthFrame.CopyPixelDataTo(depthPixelData);
            depthImageBitMap.WritePixels(depthImageBitmapRect, depthPixelData, depthImageStride, 0);
        }
    }
}

執行程式後,畫面將顯示灰階景深影像:距離 Kinect 越近的物體顏色越深,越遠則越淡。

原始景深影像

2. Kinect 深度測量原理與像素深度值提取

紅外攝像頭的視野呈金字塔狀,遠處物體的視野橫截面積較大。影像解析度(如 640×480)與現實世界的物理位置並非一一對應,但每個像素的深度值確實對應該像素所代表物體到感測器的距離。

每個深度像素佔 16 位(2 位元組),但實際深度值僅使用其中 13 位(第 3 至 15 位),低 3 位保留為玩家索引。因此讀取實際深度需將像素值右移 DepthImageFrame.PlayerIndexBitmaskWidth 位(通常為 3)。

int pixelIndex = (int)(p.X + (p.Y * frame.Width));
int depth = depthPixelData[pixelIndex] >> DepthImageFrame.PlayerIndexBitmaskWidth;

為了示範,我們在 UI 加入 TextBlock 顯示滑鼠點擊位置的深度值,並保存最後一幀 DepthImageFrame 供查詢:

private DepthImageFrame lastDepthFrame;
void kinectSensor_DepthFrameReady(object sender, DepthImageFrameReadyEventArgs e) {
    if (lastDepthFrame != null) { lastDepthFrame.Dispose(); lastDepthFrame = null; }
    lastDepthFrame = e.OpenDepthImageFrame();
    if (lastDepthFrame != null) {
        depthPixelData = new short[lastDepthFrame.PixelDataLength];
        lastDepthFrame.CopyPixelDataTo(depthPixelData);
        depthImageBitMap.WritePixels(depthImageBitmapRect, depthPixelData, depthImageStride, 0);
    }
}
private void DepthImage_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
    Point p = e.GetPosition(DepthImage);
    if (depthPixelData != null && depthPixelData.Length > 0) {
        int pixelIndex = (int)(p.X + (p.Y * lastDepthFrame.Width));
        int depth = depthPixelData[pixelIndex] >> DepthImageFrame.PlayerIndexBitmaskWidth;
        int depthInches = (int)(depth * 0.0393700787);
        int depthFt = depthInches / 12;
        depthInches %= 12;
        PixelDepth.Text = string.Format("{0}mm ~ {1}'{2}\"", depth, depthFt, depthInches);
    }
}

注意:Image 控制項的寬高最好硬編碼為 640×480,避免因容器縮放導致座標對應錯誤。

實測結果顯示:牆面距離約 2.9 公尺,手部約 1.2 公尺,數值相當準確。深度值為 -1 表示該像素無法確定深度(可能物體過近)。

深度測量示例 手部深度 無效深度

3. 景深影像增強處理

3.1 灰階反轉與範圍過濾

原始景深影像多集中在暗部,對比度低。最簡單的增強方法是將像素值按位取反(~pixelData[i]),並過濾掉過近(<0)與過遠(>3.5 公尺)的雜訊,將其設為白色(0xFF)。

private void CreateLighterShadesOfGray(DepthImageFrame depthFrame, short[] pixelData) {
    short[] enhPixelData = new short[depthFrame.Width * depthFrame.Height];
    for (int i = 0; i < pixelData.Length; i++) {
        int depth = pixelData[i] >> DepthImageFrame.PlayerIndexBitmaskWidth;
        if (depth < 0 || depth > 3500) enhPixelData[i] = 0xFF;
        else enhPixelData[i] = (short)~pixelData[i];
    }
    EnhancedDepthImage.Source = BitmapSource.Create(
        depthFrame.Width, depthFrame.Height, 96, 96,
        PixelFormats.Gray16, null, enhPixelData,
        depthFrame.Width * depthFrame.BytesPerPixel);
}

反轉灰階 增強後

3.2 16 位灰階映射至 32 位彩色(Bgr32)

將 16 位深度值(0~4095)線性映射至 0~255 灰階,再以 RGB 三通道相同值顯示,可獲得更豐富的細節:

private void CreateBetterShadesOfGray(DepthImageFrame depthFrame, short[] pixelData) {
    byte[] enhPixelData = new byte[depthFrame.Width * depthFrame.Height * 4];
    for (int i = 0, j = 0; i < pixelData.Length; i++, j += 4) {
        int depth = pixelData[i] >> DepthImageFrame.PlayerIndexBitmaskWidth;
        byte gray = (depth < 0 || depth > 3500) ? (byte)0xFF : (byte)(255 * depth / 0xFFF);
        enhPixelData[j] = enhPixelData[j+1] = enhPixelData[j+2] = gray;
    }
    EnhancedDepthImage.Source = BitmapSource.Create(
        depthFrame.Width, depthFrame.Height, 96, 96,
        PixelFormats.Bgr32, null, enhPixelData,
        depthFrame.Width * 4);
}

3.3 深度數據偽彩色渲染(HSL 轉 RGB)

將深度值對應至色相環(Hue),飽和度與亮度固定,再轉為 RGB 顯示,能直觀呈現距離層次:近處藍、中間紫、遠處紅。

private void CreateColorDepthImage(DepthImageFrame depthFrame, short[] pixelData) {
    byte[] enhPixelData = new byte[depthFrame.Width * depthFrame.Height * 4];
    for (int i = 0, j = 0; i < pixelData.Length; i++, j += 4) {
        int depth = pixelData[i] >> DepthImageFrame.PlayerIndexBitmaskWidth;
        if (depth < 1200 || depth > 3500) {
            enhPixelData[j] = enhPixelData[j+1] = enhPixelData[j+2] = 0;
        } else {
            double hue = (360.0 * depth / 0xFFF) + 1200; // 簡化計算
            byte[] rgb = new byte[3];
            ConvertHslToRgb(hue, 100, 100, rgb);
            enhPixelData[j] = rgb[2];     // Blue
            enhPixelData[j+1] = rgb[1];   // Green
            enhPixelData[j+2] = rgb[0];   // Red
        }
    }
    EnhancedDepthImage.Source = BitmapSource.Create(
        depthFrame.Width, depthFrame.Height, 96, 96,
        PixelFormats.Bgr32, null, enhPixelData,
        depthFrame.Width * 4);
}

ConvertHslToRgb 為標準 HSL 轉 RGB 演算法(略)。執行結果:

原始 灰階映射 偽彩色

效能考量:背景執行緒處理

HSL 轉 RGB 需對 30 萬像素逐一運算,若在 UI 執行緒執行會嚴重阻塞介面。建議將運算移至 BackgroundWorker 或 Task.Run,完成後透過 Dispatcher.BeginInvoke 更新 UI。

4. 小結

本文介紹了 Kinect 紅外感測器產生景深資料流的原理、像素深度值的提取與單位換算、景深影像的基本顯示,以及三種增強視覺化手法:灰階反轉、16 位映射至 32 位灰階、偽彩色渲染。下一篇將深入探討玩家索引位的應用,以及如何結合景深數據擷取人體輪廓與尺寸資訊。

完整程式碼下載:KinectSDK_Depth_Image_Processing_Part1.rar

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved