ARKit實現人體動作捕捉功能

Apple 已經成為動作捕捉行業的新玩家!可以說,其推出的 ARKit 將和移動設備 Kinect 相媲美。人物遮擋功能以及動作預測是 ARKit 框架的核心部分。本部分向你展示如何利用 iPhone 和 iPad 設備開發動作捕捉應用程序。

ARKit實現人體動作捕捉功能

前提條件

因為我們開發的是 Apple 系統的應用,所以需要使用 Mac 計算機開發應用程序並利用 iOS 設備運行它們。

硬件

在硬件方面,你需要一個兼容 MacOS Catalina 系統的 MacOS 計算機。此外,動作捕捉應用程序需要 Apple A12 仿生處理器(Apple A12 Bionic processors)才能正常運行。以下 Mac 計算機和 iOS 設備都需要具備的開發條件:

ARKit實現人體動作捕捉功能

對於這個指南,我使用的是一臺 Mac Mini 主機和一個 11 寸 iPad Pro。

軟件

為了運行順利,您需要在您的 Mac 電腦中安裝如下軟件:

Unity3D 2019 mac 2019.1.5f1

MacOS Catalina 10.15 (Beta)

XCode 11 (Beta)

您的 IOS 設備需要更新至 iOS 13 (Beta)系統或者 iPadOS 13(Beta)系統。

正如你所看到的,在編寫這篇文章時,大多數軟件都是測試版(Beta)。請記住,設備可能會變得不穩定或無響應,因此要特別小心,不要丟失有價值的數據。新的文章將伴隨著 ARKit3、iOS13 和 MacOS10.15 的公開發布。

如果你著急,請在 GitHub 上下載完整的源代碼(代碼地址:https://github.com/LightBuzz/Body-Tracking-ARKit )。

繼續閱讀,瞭解如何創建自己的動作捕捉應用程序!

詳細步驟

說的足夠多了…讓我們駛入 ARKit 的魔法世界吧。在你的電腦上,打開 Unity3D 2019.1 並且創建一個新的工程文件。

ARKit實現人體動作捕捉功能

步驟一:設置主要的場景

Unity3D 將從一個空場景開始。在添加任何視覺對象(visual objects)或編寫任何代碼之前,我們首先需要導入適當的資源包(dependencies)。骨架跟蹤功能是 ARKit 工具包的一部分。因此,我們需要導入 ARKit 和 ARFoundation 依賴包。

現在,創建一個新場景並添加 AR Session 和 AR Session Origin 對象(在 Unity 中創造 AR 場景,首先做的都是這兩件事情)。這些對象控制 iOS 相機的同時也會提供大量的 ARKit 其他體驗功能。

ARKit實現人體動作捕捉功能

另外,添加一個空的遊戲對象,例如命名它為:Human Body Tracking,並附加一個新的 C#腳本(HumanBodyTracking.cs)。

場景的結構看起來是這樣的:

ARKit實現人體動作捕捉功能

步驟二:設置骨架

視覺元素已經到位,我們現在可以開始添加一些交互性。打開HumanBodyTracking.cs腳本,引用ARHumanBodyManager 類。ARHuman Body Manager 是分析攝像機數據以檢測人體的主要腳本。代碼如下:

<code>[SerializeField] private ARHumanBodyManager humanBodyManager;/<code>

為了顯示關節,我們將使用一些簡單的 Unity3D 球體材質。每個球體將對應於特定的關節模式。添加一個 C# Dictionary 類,以逐幀(frame-by-frame)更新關節數據。代碼如下:

<code>private Dictionary bodyJoints;/<code>

最後,添加對骨架的用戶界面元素的引用。我們需要球體材質作為關節,線材質作為骨骼。代碼如下:

<code>[SerializeField] private GameObject jointPrefab;
[SerializeField] private GameObject lineRendererPrefab;
private LineRenderer[] lineRenderers;
private Transform[][] lineRendererTransforms;/<code>

你可以在 GitHub 上找到 HumanBodyTracking.cs 這個類完整的 C#代碼。(GitHub 地址:https://github.com/LightBuzz/Body-Tracking-ARKit/blob/master/body-tracking-arkit/Assets/Scripts/HumanBodyTracking.cs )

步驟三:動作捕捉檢測

這是教程中最重要的部分!ARKit 已經使動作捕捉變得非常容易實現。你所需要的就是用 ARHumanBodyManger 對象並且訂閱到humanBidiesChanged 事件。

<code>private void OnEnable()
{
    humanBodyManager.humanBodiesChanged += OnHumanBodiesChanged;
}
private void OnDisable()
{
    humanBodyManager.humanBodiesChanged -= OnHumanBodiesChanged;
}/<code>

humanBidiesChanged 事件就好像是實現動作捕捉功能的咒語。動作捕捉的信息是事件參數的一部分。下面將告訴您如何獲取動作:

<code>
private void OnHumanBodiesChanged(ARHumanBodiesChangedEventArgs eventArgs)
{
    foreach (ARHumanBody humanBody in eventArgs.added)
    {
        UpdateBody(humanBody);
    }
    foreach (ARHumanBody humanBody in eventArgs.updated)
    {
        UpdateBody(humanBody);
    }
}/<code>

很簡單,對不對?所以,讓我們完成以上操作,並在先前創建的 Unity 用戶界面中顯示骨架。

注意:筆者在寫這篇文章時,ARKit 僅僅支持單個人物的動作捕捉。

步驟四:展示骨架

以下的代碼會更新相機中關節的位置。在 iOS 相機攝像頭中,球體材質和線材質都會被覆蓋(overlayed)。

<code>
private void UpdateBody(ARHumanBody arBody)
{
    if (jointPrefab == null) return;
    if (arBody == null) return;
    if (arBody.transform == null) return;
    InitializeObjects(arBody.transform);
    NativeArray joints = arBody.joints;
    
    foreach (KeyValuePair item in bodyJoints)
    {
        UpdateJointTransform(item.Value, joints[(int)item.Key]);
    }
    for (int i = 0; i < lineRenderers.Length; i++)
    {
        lineRenderers[i].SetPositions(lineRendererTransforms[i]);
    }
}
/<code>

Apple 支持 92 種關聯模式(指數)。然而,不是所有的關聯模式都能被追蹤到!大多數是根據它們相鄰關節的位置推斷出來的。為了您的方便,我選擇了 14 種關聯模式,這樣能夠和 Kinect 相機公平比較。下面是如何連接合適的關節並形成人體骨骼的代碼:

<code>private void InitializeObjects(Transform arBodyT)
{
    if (bodyJoints == null)
    {
        bodyJoints = new Dictionary
        {
            { JointIndices3D.head_joint, Instantiate(jointPrefab, arBodyT).transform },
            { JointIndices3D.neck_1_joint, Instantiate(jointPrefab, arBodyT).transform },
            { JointIndices3D.left_arm_joint, Instantiate(jointPrefab, arBodyT).transform },
            { JointIndices3D.right_arm_joint, Instantiate(jointPrefab, arBodyT).transform },
            { JointIndices3D.left_forearm_joint, Instantiate(jointPrefab, arBodyT).transform },
            { JointIndices3D.right_forearm_joint, Instantiate(jointPrefab, arBodyT).transform },
            { JointIndices3D.left_hand_joint, Instantiate(jointPrefab, arBodyT).transform },
            { JointIndices3D.right_hand_joint, Instantiate(jointPrefab, arBodyT).transform },
            { JointIndices3D.left_upLeg_joint, Instantiate(jointPrefab, arBodyT).transform },
            { JointIndices3D.right_upLeg_joint, Instantiate(jointPrefab, arBodyT).transform },
            { JointIndices3D.left_leg_joint, Instantiate(jointPrefab, arBodyT).transform },
            { JointIndices3D.right_leg_joint, Instantiate(jointPrefab, arBodyT).transform },
            { JointIndices3D.left_foot_joint, Instantiate(jointPrefab, arBodyT).transform },
            { JointIndices3D.right_foot_joint, Instantiate(jointPrefab, arBodyT).transform }
        };
        lineRenderers = new LineRenderer[]
        {
            Instantiate(lineRendererPrefab).GetComponent(), // head neck
            Instantiate(lineRendererPrefab).GetComponent(), // upper
            Instantiate(lineRendererPrefab).GetComponent(), // lower
            Instantiate(lineRendererPrefab).GetComponent(), // right
            Instantiate(lineRendererPrefab).GetComponent() // left
        };
        lineRendererTransforms = new Transform[][]
        {
            new Transform[] { bodyJoints[JointIndices3D.head_joint], bodyJoints[JointIndices3D.neck_1_joint] },
            new Transform[] { bodyJoints[JointIndices3D.right_hand_joint], bodyJoints[JointIndices3D.right_forearm_joint], bodyJoints[JointIndices3D.right_arm_joint], bodyJoints[JointIndices3D.left_arm_joint], bodyJoints[JointIndices3D.left_forearm_joint], bodyJoints[JointIndices3D.left_hand_joint]},
            new Transform[] { bodyJoints[JointIndices3D.right_foot_joint], bodyJoints[JointIndices3D.right_leg_joint], bodyJoints[JointIndices3D.right_upLeg_joint], bodyJoints[JointIndices3D.left_upLeg_joint], bodyJoints[JointIndices3D.left_leg_joint], bodyJoints[JointIndices3D.left_foot_joint] },
            new Transform[] { bodyJoints[JointIndices3D.right_arm_joint], bodyJoints[JointIndices3D.right_upLeg_joint] },
            new Transform[] { bodyJoints[JointIndices3D.left_arm_joint], bodyJoints[JointIndices3D.left_upLeg_joint] }
        };
        for (int i = 0; i < lineRenderers.Length; i++)
        {
            lineRenderers[i].positionCount = lineRendererTransforms[i].Length;
        }
    }
}/<code>

ARKit 會給我們在 3D 空間中,關節的位置以及旋轉度!下面代碼中反映出在 2D 界面中是如何更新座標,位置和球體的旋轉:

<code>private void UpdateJointTransform(Transform jointT, XRHumanBodyJoint bodyJoint)
{
    jointT.localScale = bodyJoint.anchorScale;
    jointT.localRotation = bodyJoint.anchorPose.rotation;
    jointT.localPosition = bodyJoint.anchorPose.position;
}/<code>

現在!就讓我們建立並運行我們的工程在實際的 iOS 設備中吧!

步驟五:建立並部署應用

最後,我們需要在實際的設備中建立和運行工程。ARKit 是 IOS 和 IpadOS 的一部分,我們不能測試我們的代碼在 MacOS 中(儘管我很想看到這樣一款模擬器。

在 Unity 中,選擇File->Build Settings。點擊 iOS 建立目標並點擊Build鍵。你將需要指定一個位置去存儲所生成的工程,並耐心等待直到 Unity 完成搭建過程。

Unity 將創造一個 XCode 工程(.xcodeproj)。用 XCode 11 Beta 打開這個工程。如果您使用 XCode11 Beta 之前的版本,將會有一個錯誤提示並且工程無法正常運行。

當這個工程被髮布後,需要提供你的 iOS 開發證書,連接你的 iOS 13 設備,並且點擊 Run 鍵。這樣,這個項目將會被部署在設備中。


分享到:


相關文章: