如何将视觉框架坐标系转化为ARKit?

我正在使用ARKit(与SceneKit)添加虚拟对象(例如球)。 我使用Vision框架跟踪真实世界对象(例如脚),并在视觉请求完成处理器方法中接收其更新的位置。

let request = VNTrackObjectRequest(detectedObjectObservation: lastObservation, completionHandler: self.handleVisionRequestUpdate) 

我想用虚拟replace被跟踪的真实世界对象(例如,用立方体代替脚),但是我不知道如何将坐标系不同的BoundingBox矩形(我们在视觉请求完成中接收到的)replace为场景套件节点。

下面是视觉请求完成处理程序的代码:

  private func handleVisionRequestUpdate(_ request: VNRequest, error: Error?) { // Dispatch to the main queue because we are touching non-atomic, non-thread safe properties of the view controller DispatchQueue.main.async { // make sure we have an actual result guard let newObservation = request.results?.first as? VNDetectedObjectObservation else { return } // prepare for next loop self.lastObservation = newObservation // check the confidence level before updating the UI guard newObservation.confidence >= 0.3 else { return } // calculate view rect var transformedRect = newObservation.boundingBox //How to convert transformedRect into AR Coordinate self.node.position = SCNVector3Make(?.worldTransform.columns.3.x, ?.worldTransform.columns.3.y, } } 

请指导我转移坐标系。

要考虑的主要是边界矩形在2D图像中,而ARKit的场景是3D。 这意味着,直到你select一个深度,它没有被定义在三维边界矩形的位置。

你应该做的是对现场进行命中testing,从二维坐标到三维:

 let box = newObservation.boundingBox let rectCenter = CGPoint(x: box.midX, y: box.midY) let hitTestResults = sceneView.hitTest(rectCenter, types: [.existingPlaneUsingExtent, .featurePoint]) // Pick the hitTestResult you need (nearest?), get position via worldTransform 

假设矩形位于水平面上,可以对所有四个angular落的场景执行命中testing,并使用其中的三个angular来计算矩形的宽度,高度,中心和方向。

我有一个在GitHub上提供的演示应用程序,它的确如此: https : //github.com/mludowise/ARKitRectangleDetection

VNRectangleObservation的矩形angular的坐标将根据图像的大小以及不同的坐标而定,具体取决于手机的旋转。 您需要将它们乘以视图大小,然后根据手机的旋转进行反转:

 func convertFromCamera(_ point: CGPoint, view sceneView: ARSCNView) -> CGPoint { let orientation = UIApplication.shared.statusBarOrientation switch orientation { case .portrait, .unknown: return CGPoint(x: point.y * sceneView.frame.width, y: point.x * sceneView.frame.height) case .landscapeLeft: return CGPoint(x: (1 - point.x) * sceneView.frame.width, y: point.y * sceneView.frame.height) case .landscapeRight: return CGPoint(x: point.x * sceneView.frame.width, y: (1 - point.y) * sceneView.frame.height) case .portraitUpsideDown: return CGPoint(x: (1 - point.y) * sceneView.frame.width, y: (1 - point.x) * sceneView.frame.height) } } 

然后你可以在所有4个angular落进行命中testing。 在执行命中testing时使用types.existingPlaneUsingExtent非常重要,以便ARKit为水平面返回命中。

 let tl = sceneView.hitTest(convertFromCamera(rectangle.topLeft, view: sceneView), types: .existingPlaneUsingExtent) let tr = sceneView.hitTest(convertFromCamera(rectangle.topRight, view: sceneView), types: .existingPlaneUsingExtent) let bl = sceneView.hitTest(convertFromCamera(rectangle.bottomLeft, view: sceneView), types: .existingPlaneUsingExtent) let br = sceneView.hitTest(convertFromCamera(rectangle.bottomRight, view: sceneView), types: .existingPlaneUsingExtent) 

然后它变得有点复杂

由于每个命中testing可能会返回0到n的结果,因此您需要过滤掉包含在不同平面上的任何命中testing。 你可以通过比较每个ARHitTestResult的锚来做到这ARHitTestResult

 hit1.anchor == hit2.anchor 

此外,您只需要四个angular中的三个来识别矩形的尺寸,位置和方向,因此如果一个angular没有返回任何命中testing结果,则可以。 看看我在这里如何做到这一点。

您可以从左右angular之间的距离(顶部或底部)计算矩形的宽度。 同样,您可以从顶部和底部angular落(左侧或右侧)之间的距离计算矩形的高度。

 func distance(_ a: SCNVector3, from b: SCNVector3) -> CGFloat { let deltaX = ax - bx let deltaY = ay - by let deltaZ = az - bz return CGFloat(sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ)) } let width = distance(right, from: left) let height = distance(top, from: bottom) 

您可以通过从矩形的对angular(topLeft&bottomRight或topRight&bottomLeft)获取中点来计算其位置:

 let midX = (c1.x + c2.x) / 2 let midY = (c1.y + c2.y) / 2 let midZ = (c1.z + c2.z) / 2 let center = SCNVector3Make(midX, midY, midZ) 

您还可以从左右angular落(顶部或底部)计算矩形的方向(沿y轴旋转):

 let distX = right.x - left.x let distZ = right.z - left.z let orientation = -atan(distZ / distX) 

然后把这些放在一起,并在矩形上叠加显示一些东西。 这是一个通过SCNNode来显示虚拟矩形的SCNNode

 class RectangleNode: SCNNode { init(center: SCNVector3, width: CGFloat, height: CGFloat, orientation: Float) { super.init() // Create the 3D plane geometry with the dimensions calculated from corners let planeGeometry = SCNPlane(width: width, height: height) let rectNode = SCNNode(geometry: planeGeometry) // Planes in SceneKit are vertical by default so we need to rotate // 90 degrees to match planes in ARKit var transform = SCNMatrix4MakeRotation(-Float.pi / 2.0, 1.0, 0.0, 0.0) // Set rotation to the corner of the rectangle transform = SCNMatrix4Rotate(transform, orientation, 0, 1, 0) rectNode.transform = transform // We add the new node to ourself since we inherited from SCNNode self.addChildNode(rectNode) // Set position to the center of rectangle self.position = center } }