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

Kinect for Windows SDK 開發入門(十一):手勢識別實戰——可重用追蹤架構與七大經典手勢實作

     編輯:關於.NET

前言:從單一演算法到完整手勢生態系

上一篇介紹手勢概念與揮手演算法。本文將建立可重用的手部追蹤類別庫,並以此為基礎完整實作除揮手外的七大經典 Kinect 手勢:懸浮按鈕、下壓按鈕、磁性按鈕、劃動、磁性幻燈片、垂直捲動、通用暫停。

一、手部追蹤核心架構 (KinectCursorManager)

設計目標:高內聚、低耦合。自動管理骨骼串流 → 座標轉換 → 視覺回饋 (CursorAdorner) → 事件路由到 UI 元素。

核心類別職責

類別職責
KinectCursorEventArgs承載 X/Y/Z 座標與 CursorAdorner 參考的路由事件參數
KinectInput定義 5 大路由事件:CursorEnter/Leave/Move/Activated/Deactivated
CursorAdorner繼承 Adorner,永遠置頂顯示手勢游標,支援動畫 (漸層位移)
KinectCursorManager單例,核心引擎:骨骼平滑、主手判定、座標映射、HitTest 路由事件

關鍵演算法:主骨架與主手判定

private static Skeleton GetPrimarySkeleton(IEnumerable skeletons) {
    return skeletons.Where(s => s.TrackingState == SkeletonTrackingState.Tracked)
                    .OrderBy(s => s.Position.Z) // 最近者優先
                    .FirstOrDefault();
}
private static Joint? GetPrimaryHand(Skeleton skeleton) {
    var right = skeleton.Joints[JointType.HandRight];
    var left = skeleton.Joints[JointType.HandLeft];
    if (right.TrackingState == JointTrackingState.Tracked) {
        if (left.TrackingState != JointTrackingState.Tracked) return right;
        return left.Position.Z > right.Position.Z ? right : left; // Z 較小(較近)者為主手
    }
    return left.TrackingState == JointTrackingState.Tracked ? left : null;
}

座標映射與事件路由核心

private void UpdateCursor(Joint hand) {
    var depthPt = kinectSensor.MapSkeletonPointToDepth(hand.Position, kinectSensor.DepthStream.Format);
    float x = depthPt.X * window.ActualWidth / kinectSensor.DepthStream.FrameWidth;
    float y = depthPt.Y * window.ActualHeight / kinectSensor.DepthStream.FrameHeight;
    float z = depthPt.Depth; // 毫米
    HandleCursorEvents(new Point(x, y), z);
    cursorAdorner.UpdateCursor(new Point(x, y));
}
private void HandleCursorEvents(Point pt, double z) {
    var element = GetElementAtScreenPoint(pt, window); // VisualTreeHelper.HitTest
    if (element != null) {
        element.RaiseEvent(new KinectCursorEventArgs(KinectInput.KinectCursorMoveEvent, pt, z) { Cursor = cursorAdorner });
        if (element != lastElementOver) {
            lastElementOver?.RaiseEvent(new KinectCursorEventArgs(KinectInput.KinectCursorLeaveEvent, pt, z) { Cursor = cursorAdorner });
            element.RaiseEvent(new KinectCursorEventArgs(KinectInput.KinectCursorEnterEvent, pt, z) { Cursor = cursorAdorner });
            lastElementOver = element;
        }
    }
}

二、基礎控制項:KinectButton

繼承 Button,將 CursorEnter 映射為 Click 事件,讓傳統按鈕邏輯無縫遷移。缺點:無法區分「有意」與「無意」懸停,引出下方改良版本。

三、七大經典手勢完整實作

1. 懸浮按鈕 — 解決誤觸的標準解法

原理:DispatcherTimer 計時,進入啟動、離開停止、超時觸發 Click。
視覺回饋:游標漸層動畫倒數。

public class HoverButton : KinectButton {
    public double HoverInterval { get; set; } = 2000; // 2 秒
    protected override void OnKinectCursorEnter(...) { timer.Start(); cursor.AnimateCursor(HoverInterval); }
    protected override void OnKinectCursorLeave(...) { timer.Stop(); cursor.StopCursorAnimation(); }
    void timer_Tick(...) { timer.Stop(); RaiseEvent(new RoutedEventArgs(ClickEvent)); }
}

2. 下壓按鈕 — 模擬滑鼠點擊的 Z 軸手勢

原理:記錄進入時手部深度 handDepth,若當前 Z < handDepth - Threshold (預設 100mm) 視為下壓。

public class PushButton : KinectButton {
    public double PushThreshold { get; set; } = 100;
    double _baseDepth;
    protected override void OnKinectCursorEnter(...) => _baseDepth = e.Z;
    protected override void OnKinectCursorMove(...) { if (e.Z < _baseDepth - PushThreshold) RaiseEvent(new RoutedEventArgs(ClickEvent)); }
}

3. 磁性按鈕 — 懸浮按鈕的體驗升級版

核心創新:手進入區域 → 游標自動吸附至按鈕中心 (動畫過渡) → 手離開 → 游標釋放回手部位置。
技術難點:需計算按鈕在視覺樹中的絕對中心座標,並暫時覆寫游標追蹤 (UpdateCursor(pt, true))。

protected override void OnKinectCursorEnter(...) {
    var center = GetButtonCenter(); // TransformToAncestor(Window)
    var lockPt = new Point(center.X - cursor.Width/2, center.Y - cursor.Height/2);
    AnimateCursorToLockPosition(e, cursor, lockPt); // DoubleAnimation Canvas.Left/Top
}
protected override void OnKinectCursorLeave(...) {
    AnimateCursorAwayFromLockPosition(e, cursor, lockPt); // 動畫回手部位置並解除覆寫
}

4. 劃動 — 速度與軌跡的雙重檢查

在 KinectCursorManager 內建 GesturePoint 追蹤清單 (X, Y, Z, Time)。核心邏輯 HandleGestureTracking:

  • 新點偏離起始 Y 軸超過 swipeDeviation → 失敗清空。
  • 時間超過 swipeTime → 移除最舊點 (滑動視窗)。
  • X 軸位移超過 swipeLength (正負表方向) → 觸發 swipeDetected 事件。

5. 磁性幻燈片 — Xbox 儀表板級的終極手勢

混合體驗:磁性吸附 (無需等待) + 劃動觸發 (立即回應)。
實作:繼承 MagnetButton,OnKinectCursorEnter 向 Manager 註冊劃動參數與事件,swipeDetected 觸發 Click、swipeOutofBoundDetected 取消註冊。

public class MagneticSlide : MagnetButton {
    protected override void OnKinectCursorEnter(...) {
        InitializeSwipe(); // 向 Manager 註冊參數與事件
        base.OnKinectCursorEnter(...);
    }
    void kinectMgr_swipeDetected(...) { DeInitializeSwipe(); RaiseEvent(new RoutedEventArgs(ClickEvent)); }
    void kinectMgr_swipeOutofBoundDetected(...) { DeInitializeSwipe(); RaiseEvent(new SwipeOutOfBoundsEventArgs()); }
}

6. 垂直捲動 — 姿勢而非手勢,更自然、不疲勞

原理:計算肩-肘-腕向量與垂直線夾角。
區間設計:中性區 ±20° (無動作) → 向上/向下分級觸發捲動速度。
優勢:避免水平劃動的「回手抵銷」問題,長時間操作不易疲勞。

垂直捲動角度示意

7. 通用暫停 — 系統級保留手勢

左臂抬起 45° (與軀幹夾角)。實作同垂直捲動,僅換左手關節點與角度閾值。微軟建議作為暫停/選單喚出保留手勢,避免與應用手勢衝突。

四、測試畫面與原始碼

整合上述控制項於單一視窗,即可驗證所有手勢互動流程。

手勢測試介面

手勢測試介面2

五、展望:手勢識別的未來

  • 硬體升級:下一代 Kinect 更高解析度、手指關節點、臉部特徵點 (嘴唇、眼睛)。
  • 精細手勢:辨識手指彎曲、捏合,支援手語、複雜符號系統。
  • 情境感知:結合身體語言、語氣、表情,主動推測使用者意圖 (如:疲勞時自動調暗燈光、播放舒緩音樂)。
  • 隱形化:如同自動門般融入環境,使用者無需刻意學習特定動作,自然互動即得服務。

六、結語

本文從架構級的手部追蹤庫出發,逐層建構七大經典手勢控制項,涵蓋計時、深度、吸附、軌跡、角度等核心技術。這套類別庫可直接引入專案,加速 Kinect 自然使用者介面開發。下一階段將探索臉部辨識、全像投影與第三方引擎整合,敬請期待!

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