Unity AR Foundation 和 CoreML: 实现手部的检测和追踪

0x00 前言

Unity的AR Foundation经过上层抽象,对ARKit和ARCore这些底层接口进行了封装,从而实现了AR项目的跨平台开发能力。html

而苹果的CoreML是一个能够用来将机器学习模型与iOS平台上的app进行集成的框架。git

本文以及本文结尾处的demo工程,将介绍和演示如何使Unity的AR Foundation与苹果的CoreML一同工做,以实现使用咱们的手来和虚拟物体进行交互的功能。github

Unity AR Foundation手部检测json

 

本文参考了Gil Nakache的文章,而且所使用的机器学习模型也来自他的文章。在他的那篇文章中,他描述了如何使用Swift在iOS原平生台上实现相似的功能。swift

Version

Unity Version: 2018.3.13f1xcode

Xcode Version: 10.2.1app

The ARFoundation Plugin: 1.5.0-preview.5框架

iPhone 7: 12.3.1机器学习

 

0x01 实现

导入 AR Foundation Plugin

为了方便,我使用了本地pacakge导入的形式。这种实现方式十分简单,只须要修改工程目录下Package文件夹内的manifest.json文件,在manifest.json文件中添加本地package便可。async

"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/

相关文章
相关标签/搜索