Unity AR Foundation 和 CoreML: 實現手部的檢測和追蹤


0x00 前言

Unity的AR Foundation通過上層抽象,對ARKit和ARCore這些底層接口進行了封裝,從而實現了AR項目的跨平台開發能力。

而蘋果的CoreML是一個可以用來將機器學習模型與iOS平台上的app進行集成的框架。

本文以及本文結尾處的demo工程,將介紹和演示如何使Unity的AR Foundation與蘋果的CoreML一同工作,以實現使用我們的手來和虛擬物體進行交互的功能。

Unity AR Foundation手部檢測

 

本文參考了Gil Nakache的文章,並且所使用的機器學習模型也來自他的文章。在他的那篇文章中,他描述了如何使用Swift在iOS原生平台上實現類似的功能。

Version

Unity Version: 2018.3.13f1

Xcode Version: 10.2.1

The ARFoundation Plugin: 1.5.0-preview.5

iPhone 7: 12.3.1

 

0x01 實現

導入 AR Foundation Plugin

為了方便,我使用了本地pacakge導入的形式。這種實現方式十分簡單,只需要修改工程目錄下Package文件夾內的manifest.json文件,在manifest.json文件中添加本地package即可。

"com.unity.xr.arfoundation": "file:../ARPackages/com.unity.xr.arfoundation", 
"com.unity.xr.arkit": "file:../ARPackages/com.unity.xr.arkit

導入AR Foundation Package之后,我們就可以在場景中創建一些相關的組件了,比如AR Session、AR Session Origin等等。

之后在我們的腳本中,監聽frameReceived事件來獲取每一幀的數據。

    if (m_CameraManager != null)
    {
        m_CameraManager.frameReceived += OnCameraFrameReceived;
    }

使用Swift語言創建一個Unity插件

為了使C#語言可以和Swift語言進行交互,我們需要先創建一個Objective-C文件作為橋接。這種方式就是,C#通過[DllImport("__Internal")]來調用一個Objective-C的方法。之后,Objective-C再通過@objc來調用Swift。引入UnityInterface.h之后,Swift可以調用UnitySendMessage方法來向C#傳送數據。

這里有一個示例工程,演示了如何為Unity創建一個使用Swift的原生插件,並且在Unity中打印出“Hello, I’m Swift”。

本文所使用的Unity-ARFoundation-HandDetection工程,它的plugins文件夾的目錄結構如下:

但是,需要注意的是,Unity直接導出的Xcode工程是沒有指定Swift版本的。

因此,我們需要手動指定一個版本,或者創建一個Unity的腳本來自動設置Swift的版本。

導入 mlmodel

將HandModel添加到我們的Xcode工程中,之后它會自動生成一個Objective-C model類。但是我希望得到一個Swift的類,因此我們可以在Build Settings/CoreML Model Compiler - Code Generation Language這里將選項從Auto修改為Swift。

之后,我們會獲得一個叫做HandModel的自動生成的Swift類。

當然,如果你不想總是手動添加,同樣也可以選擇在Unity中創建一個build post processing腳本來自動添加機器學習模型。

如何從AR Foundation中獲取ARFrame Ptr

完成了以上步驟之后,基本的交互框架就已經成型了。接下來,我們就需要使用CoreML來實現手部的檢測和追蹤的具體功能了。

@objc func startDetection(buffer: CVPixelBuffer) -> Bool { //TODO self.retainedBuffer = buffer let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: self.retainedBuffer!, orientation: .right) visionQueue.async { do { defer { self.retainedBuffer = nil } try imageRequestHandler.perform([self.predictionRequest]) } catch { fatalError("Perform Failed:\"\(error)\"") } } return true }

在Swift中,我們需要一個CVPixelBuffer來創建VNImageRequestHandler以執行手部檢測。通常我們需要從ARFrame中來獲取它。

CVPixelBufferRef buffer = frame.capturedImage;

因此,下一個問題就是如何從Unity的AR Foundation的C#腳本中獲取來自ARKit的ARFrame指針,並且將其傳遞給使用Objective-C和Swift語言的Hand Detection插件。

在AR Foundation中,我們可以從XRCameraFrame中獲取nativePtr,它指向一個ARKit的結構,如下所示:

typedef struct UnityXRNativeFrame_1 { int version; void* framePtr; } UnityXRNativeFrame_1; 

並且這個framePtr指向了最新的ARFrame

具體來說,我們可以調用定義在XRCamera​Subsystem的TryGetLatestFrame方法來獲取一個XRCameraFrame實例。

cameraManager.subsystem.TryGetLatestFrame(cameraParams, out frame)

之后將nativePtr從C#傳遞給Objective-C。

m_HandDetector.StartDetect(frame.nativePtr);

在Objective-C這邊,我們會獲得一個UnityXRNativeFrame_1指針並且我們能從其中獲取ARFrame指針。

    UnityXRNativeFrame_1* unityXRFrame = (UnityXRNativeFrame_1*) ptr;
    ARFrame* frame = (__bridge ARFrame*)unityXRFrame->framePtr;
    
    CVPixelBufferRef buffer = frame.capturedImage

一旦獲取了ARFrame,接下來就來到了iOS開發的領域。創建一個VNImageRequestHandler對象並且開始執行手部檢測。一旦檢測完成,detectionCompleteHandler回調會被調用並且會通過UnitySendMessage將檢測的結果傳遞給Unity。

private func detectionCompleteHandler(request: VNRequest, error: Error?) { DispatchQueue.main.async { if(error != nil) { UnitySendMessage(self.callbackTarget, "OnHandDetecedFromNative", "") fatalError("error\(error)") } guard let observation = self.predictionRequest.results?.first as? VNPixelBufferObservation else { UnitySendMessage(self.callbackTarget, "OnHandDetecedFromNative", "") fatalError("Unexpected result type from VNCoreMLRequest") } let outBuffer = observation.pixelBuffer guard let point = outBuffer.searchTopPoint() else{ UnitySendMessage(self.callbackTarget, "OnHandDetecedFromNative", "") return } UnitySendMessage(self.callbackTarget, "OnHandDetecedFromNative", "\(point.x),\(point.y)") } } 

之后我們會獲取在viewport空間的position數據。viewport空間是相對於相機標准化的。 viewport的左下角是(0,0); 右上角是(1,1)。

一旦我們獲取了viewport空間的位置,就可以通過Unity的ViewportToWorldPoint方法將它從viewport空間轉換到world空間。傳遞給該方法的向量參數中的x、y來自Hand Detection的結果,z值則是距離相機的距離。

   var handPos = new Vector3(); handPos.x = pos.x; handPos.y = 1 - pos.y; handPos.z = 4;//m_Cam.nearClipPlane;  var handWorldPos = m_Cam.ViewportToWorldPoint(handPos); 

我們可以在Unity中使用這個世界坐標來創建新的Object,或者是將已有的Object移動到這個世界坐標。換句話說,這個Object的位置會根據手的位置而改變。

Post Process Build

正如上文說過的,我們可以在Unity中寫一個C#腳本來自動設置生成的Xcode工程中的一些屬性。例如,我們可以設置Xcode工程中Build Setting中的Swift Version屬性。我們甚至還可以將機器學習模型添加到Build Phases中,比如添加到Compile Sources Phase。這里我們會使用定義在UnityEditor.iOS.Xcode命名空間中的PBXProject類。PBXProject類提供了很多有用的方法,例如AddBuildPropertySetBuildPropertyAddSourcesBuildPhase

[PostProcessBuild] public static void OnPostProcessBuild(BuildTarget buildTarget, string path) { if(buildTarget != BuildTarget.iOS) { return; } string projPath = path + "/Unity-iPhone.xcodeproj/project.pbxproj"; var proj = new PBXProject(); proj.ReadFromFile(projPath); var targetGUID = proj.TargetGuidByName("Unity-iPhone"); //set xcode proj properties  proj.AddBuildProperty(targetGUID, "SWIFT_VERSION", "4.0"); proj.SetBuildProperty(targetGUID, "SWIFT_OBJC_BRIDGING_HEADER", "Libraries/Plugins/iOS/HandDetector/Native/HandDetector.h"); proj.SetBuildProperty(targetGUID, "SWIFT_OBJC_INTERFACE_HEADER_NAME","HandDetector-Swift.h"); proj.SetBuildProperty(targetGUID, "COREML_CODEGEN_LANGUAGE", "Swift"); //add handmodel to xcode proj build phase.  var buildPhaseGUID = proj.AddSourcesBuildPhase(targetGUID); var handModelPath = Application.dataPath + "/../CoreML/HandModel.mlmodel"; var fileGUID = proj.AddFile(handModelPath, "/HandModel.mlmodel"); proj.AddFileToBuildSection(targetGUID, buildPhaseGUID, fileGUID); proj.WriteToFile(projPath); } 

0x02 結論

使用Unity中的AR Foundation和CoreML,我們可以讓Unity Chan站在我們的手指上。

本文簡單描述了集成CoreML和AR Foundation的過程。我相信大家可以使用它們作出更有趣的內容。

這里是文中所使用的demo工程。

 

https://github.com/chenjd/Unity-ARFoundation-HandDetection

 

 

Useful Links

https://heartbeat.fritz.ai/hand-detection-with-core-ml-and-arkit-f4c8da98e88e

https://medium.com/@kevinhuyskens/implementing-swift-in-unity-53e0b668f895

http://chenjd.xyz/2019/07/22/Unity-ARFoundation-CoreML/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM