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

Kinect for Windows SDK 開發入門(六):骨骼追蹤基礎(上)─ 核心物件模型與骨架繪製完整範例

     編輯:關於.NET

前言

Kinect 的深度資料雖能提供場景幾何,但若要建立真正自然、豐富的互動體驗,必須進一步辨識「人」的結構。骨骼追蹤技術正是透過深度影像的機器學習分類,輸出 20 個關節點的 3D 座標,成為 NUI 開發的核心輸入來源。本文將系統介紹骨骼追蹤的四大核心物件(SkeletonStream、SkeletonFrame、Skeleton、Joint),並以完整 WPF 範例示範如何取得資料並繪製骨架。

一、 取得骨骼資料:事件模式範例

1.1 初始化感測器與骨骼串流

private KinectSensor kinectDevice;
private Skeleton[] frameSkeletons;
private readonly Brush[] skeletonBrushes = { Brushes.Black, Brushes.Crimson, Brushes.Indigo, Brushes.DodgerBlue, Brushes.Purple, Brushes.Pink };

public MainWindow()
{
    InitializeComponent();
    KinectSensor.KinectSensors.StatusChanged += KinectSensors_StatusChanged;
    KinectDevice = KinectSensor.KinectSensors.FirstOrDefault(s => s.Status == KinectStatus.Connected);
}

public KinectSensor KinectDevice
{
    get => kinectDevice;
    set
    {
        if (kinectDevice != null)
        {
            kinectDevice.Stop();
            kinectDevice.SkeletonFrameReady -= KinectDevice_SkeletonFrameReady;
            kinectDevice.SkeletonStream.Disable();
            frameSkeletons = null;
        }
        kinectDevice = value;
        if (kinectDevice != null && kinectDevice.Status == KinectStatus.Connected)
        {
            kinectDevice.SkeletonStream.Enable();
            frameSkeletons = new Skeleton[kinectDevice.SkeletonStream.FrameSkeletonArrayLength]; // 固定長度 6
            kinectDevice.SkeletonFrameReady += KinectDevice_SkeletonFrameReady;
            kinectDevice.Start();
        }
    }
}
  • FrameSkeletonArrayLength 常數 = 6,Kinect 最多偵測 6 人,但同時完整追蹤僅 2 人。
  • 預先配置 frameSkeletons 陣列避免每幀配置記憶體。
  • 筆刷陣列為多玩家著色。

1.2 SkeletonFrameReady 事件處理與骨架繪製

private void KinectDevice_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
{
    using (var frame = e.OpenSkeletonFrame())
    {
        if (frame == null) return;
        LayoutRoot.Children.Clear(); // 簡單做法:每幀全清重繪
        frame.CopySkeletonDataTo(frameSkeletons);

        for (int i = 0; i < frameSkeletons.Length; i++)
        {
            var skeleton = frameSkeletons[i];
            if (skeleton.TrackingState != SkeletonTrackingState.Tracked) continue; // 忽略未追蹤骨架

            var brush = skeletonBrushes[i % skeletonBrushes.Length];

            // 依解剖結構分段繪製 Polyline
            DrawBone(brush, skeleton, JointType.Head, JointType.ShoulderCenter, JointType.ShoulderLeft, JointType.Spine, JointType.ShoulderRight, JointType.ShoulderCenter, JointType.HipCenter);
            DrawBone(brush, skeleton, JointType.HipLeft, JointType.HipRight);
            DrawBone(brush, skeleton, JointType.HipCenter, JointType.HipLeft, JointType.KneeLeft, JointType.AnkleLeft, JointType.FootLeft);
            DrawBone(brush, skeleton, JointType.HipCenter, JointType.HipRight, JointType.KneeRight, JointType.AnkleRight, JointType.FootRight);
            DrawBone(brush, skeleton, JointType.ShoulderLeft, JointType.ElbowLeft, JointType.WristLeft, JointType.HandLeft);
            DrawBone(brush, skeleton, JointType.ShoulderRight, JointType.ElbowRight, JointType.WristRight, JointType.HandRight);
        }
    }
}

private void DrawBone(Brush brush, Skeleton skeleton, params JointType[] joints)
{
    var polyline = new Polyline { Stroke = brush, StrokeThickness = 8 };
    foreach (var jt in joints)
        polyline.Points.Add(GetJointPoint(skeleton.Joints[jt]));
    LayoutRoot.Children.Add(polyline);
}

private Point GetJointPoint(Joint joint)
{
    var dp = KinectDevice.MapSkeletonPointToDepth(joint.Position, KinectDevice.DepthStream.Format);
    dp.X = (int)(dp.X * LayoutRoot.ActualWidth / KinectDevice.DepthStream.FrameWidth);
    dp.Y = (int)(dp.Y * LayoutRoot.ActualHeight / KinectDevice.DepthStream.FrameHeight);
    return new Point(dp.X, dp.Y);
}

骨架繪製執行畫面

觀察重點: 執行時嘗試前後移動,骨架大小會隨 Z 值(深度)自然縮放,這證明 MapSkeletonPointToDepth 內部已包含透視投影修正。若在 GetJointPoint 中強制將 joint.Position.Z = 0,映射出的 X、Y 皆為 0,可驗證深度參與座標轉換。

二、 骨骼追蹤核心物件模型深度解析

骨骼追蹤物件模型圖

2.1 SkeletonStream:資料來源與平滑控制

屬性/方法說明
Enable() / Disable()啟用/停用骨骼串流。未啟用不會產生幀、事件不觸發。
Enable(TransformSmoothParameters)啟用並套用 Holt 雙指數平滑參數(見下表)。
IsSmoothingEnabled是否已套用平滑參數。
AppChoosesSkeletonstrue 時由應用程式決定追蹤哪兩具骨架(需呼叫 ChooseSkeletons)。
ChooseSkeletons(params int[] trackingIds)指定要追蹤的 TrackingId;傳入空陣列則停止追蹤。

TransformSmoothParameters 平滑參數表

參數範圍效果
Smoothing0~1值越大越平滑,0=關閉。過大導致延遲感。
Correction0~1值越小修正越強,預測修正權重。
Prediction幀數預測未來幾幀位置,配合 Correction 使用。
JitterRadius公尺抖動半徑門檻,超過視為真實移動而非雜訊。
MaxDeviationRadius公尺最大偏離半徑,超過此範圍不視為抖動而直接採用新位置。

效能提醒: 平滑處理會增加 CPU 負載。建議依遊戲階段動態調整(如選單高平滑、動作遊戲低平滑)。

2.2 SkeletonFrame:單幀資料容器

  • 取得方式:e.OpenSkeletonFrame()(事件模式)或 stream.OpenNextFrame()(Pull 模式)。
  • CopySkeletonDataTo(Skeleton[]):深拷貝至預先配置陣列。每次呼叫產生全新 Skeleton 實例(參考不相等,但 TrackingId 相同)。
  • 關鍵欄位:
    • FrameNumber:幀序號(非連續,遞增)。
    • Timestamp:感測器啟動後毫秒數(64-bit)。
    • FloorClipPlane:地面平面係數 (A,B,C,D),Ax+By+Cz+D=0;全 0 表無法判定。

2.3 Skeleton:單一使用者骨架資料

屬性型別說明
TrackingIdint唯一追蹤編號(遞增、非連續、0 表無效佔位符)。
TrackingStateSkeletonTrackingStateTracked/PositionOnly/NotTracked。
PositionSkeletonPoint骨架中心點(≈脊椎中點),可用於最近者篩選。
ClippedEdgesFrameEdges (Flags)身體超出視野邊界標記:Bottom/Top/Left/Right。
JointsJointCollection20 個關節點集合,以 JointType 索引。

ClippedEdges 應用建議

  • 偵測到 Bottom 時提示使用者後退或調整感測器俯仰角(KinectSensor.ElevationAngle,範圍 ±27°,每秒限制調整次數保護馬達)。
  • 若不關心腳部動作,可忽略 Bottom;但 Left/Right 影響手部追蹤,建議提示使用者移至畫面中央。

2.4 Joint:關節點細節

屬性型別說明
JointTypeenum20 種關節:Head, ShoulderCenter, ShoulderLeft/Right, ElbowLeft/Right, WristLeft/Right, HandLeft/Right, Spine, HipCenter, HipLeft/Right, KneeLeft/Right, AnkleLeft/Right, FootLeft/Right。
PositionSkeletonPointX、Y、Z(公尺,骨骼座標系)。
TrackingStateJointTrackingStateTracked(高信心)、Inferred(推測、遮蔽時)、NotTracked。

關節點分佈圖 JointTrackingState 列表

重要: Inferred 狀態表示關節被遮擋,位置由骨架約束推估,精度較低。應用程式視需求決定是否採用(如手部被身體遮擋時)。

三、 結語

本文建立了骨骼追蹤開發的完整知識骨架:

  1. 資料取得流程:感測器發現 → SkeletonStream.Enable() → 事件/拉模式取得 SkeletonFrame → CopySkeletonDataTo → 遍歷 Skeleton → 讀取 Joints。
  2. 四大核心物件職責與關鍵屬性/方法對照表。
  3. 平滑參數(Holt 雙指數平滑)與 AppChoosesSkeletons 進階控制。
  4. TrackingId/TrackingState/ClippedEdges 實務判讀技巧。
  5. 完整 WPF 骨架繪製範例(含座標映射 MapSkeletonPointToDepth)。

下一篇將以「連連看」小遊戲為例,實戰演練手部追蹤判斷、命中測試、動態連線繪圖,並整理座標系轉換與鏡像處理細節,敬請期待!

作者:yangecnu(博客園)|原文連結:https://www.cnblogs.com/yangecnu/

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