在iOS 11 ARKit中获取相机视野

时间:2017-11-28 16:36:14

标签: ios swift arkit

我使用ARKit的ARSCNView在iPad上实时显示来自相机的视频。我将ARSCNView对象设置为Xcode的增强现实应用程序模板。我想知道是否有办法获得相机的视野?

@IBOutlet var sceneView: ARSCNView!

func start() {
    sceneView.delegate = self
    sceneView.session.run(ARWorldTrackingConfiguration())
    // Retrieve camera FOV here
}

2 个答案:

答案 0 :(得分:31)

有几种方法可以去这里,可能有一些错误的开始要小心。

⚠️ARKit+ SceneKit(不正确)

如果您已经通过SceneKit(ARSCNView)使用ARKit,您可能会认为ARKit会自动更新SceneKit摄影机(视图' s pointOfView' s camera)匹配ARKit使用的投影变换。这是正确的。

但是,ARKit会直接设置SCNCamera' projectionTransform。使用SCNCamerazNear以及fieldOfViewzFar的几何属性时,SceneKit会导出用于渲染的投影矩阵。但是,如果直接设置projectionTransform,则无法恢复near / far和xFov / yFov值,因此相应的SCNCamera属性无效。也就是说, sceneView.pointOfView.camera.fieldOfView和类似的API总是会返回ARKit应用程序的虚假结果

那么,你能做些什么呢?请继续阅读...

投影矩阵

AR会话通过其委托不断发布ARFrame个对象,或者您可以从中请求currentFrame。每个框架都附有一个ARCamera,用于描述成像参数,其中一个是projectionMatrix,它取决于视野。 (还有前面提到的SceneKit projectionTransform,它是相同的矩阵。)

标准3D投影矩阵包括基于垂直视场和纵横比的缩放项。具体来说,矩阵看起来像这样:

[ xScale    0     0    0  ]   xScale = yScale * aspectRatio (width/height)
[   0    yScale   0    0  ]   yScale = 1 / tan(yFov/2)
[   0       0    nf1  nf2 ]   nf1 and nf2 relate to near and far clip planes,
[   0       0     -1   0  ]     so aren't relevant to field of view

因此,您应该能够通过求解yFov等式获得yScale

let projection = session.currentFrame!.camera.projectionMatrix
let yScale = projection[1,1]
let yFov = 2 * atan(1/yScale) // in radians
let yFovDegrees = yFov * 180/Float.pi

对于水平视野,您可以乘以纵横比(具体地说,宽高比):

let imageResolution = session.currentFrame!.camera.imageResolution
let xFov = yFov * Float(imageResolution.width / imageResolution.height)
  

注意:此处,"水平"和"垂直"相对于相机图像而言,无论您的设备或AR视图UI如何定向,相机图像本身都是横向图像。

但仔细观察,您可能会注意到此处xFov / yFov之间的宽高比(以及imageResolution的宽高比)不一定与之匹配您的设备屏幕(尤其是在iPhone X上)或您正在绘制AR内容的视图。这是因为您已经测量了摄像机图像的视觉角度 ,不是您应用的AR视图。别担心,那里也有API ......

带视口的投影矩阵

ARCamera提供了两个用于获取投影矩阵的API。除了我们刚刚过去的那个,还有projectionMatrix(for:viewportSize:zNear:zFar:),其中考虑了演示。如果你想匹配的不是相机的FOV,而是FOV如何ARSCNViewARSKView(或Unity或Unreal,可能?)渲染您的AR场景,请使用此方法,传递设备方向和看到你的看法。然后做上面所有相同的数学运算:

let imageResolution = session.currentFrame!.camera.imageResolution
let viewSize = sceneView.bounds.size
let projection = session.currentFrame!.camera.projectionMatrix(for: .portrait,
    viewportSize: viewSize, zNear: zNear, zFar: zFar)
let yScale = projection[1,1] // = 1/tan(fovy/2)
let yFovDegrees = 2 * atan(1/yScale) * 180/Float.pi
let xFovDegrees = yFovDegrees * Float(viewSize.height / viewSize.width)

您为zNearzFar传递的内容并不重要,因为我们并未使用依赖于此的矩阵部分。 (您可能仍需要确保zNear < zFarzNear != zFar != 0。)

  

注意:现在,高度/宽度取决于您的视图(或者更确切地说,是您传递给projectionMatrix(for:...)的视图的属性)。在此示例中,yFov相对于UI是垂直的,因为方向为portrait,因此您乘以高度/宽度宽高比得到xFov。如果您处于横向状态,则会乘以宽度/高度。

Camera Intrinsics

敏锐的观察者可能已经注意到上述计算忽略了投影矩阵的一部分。这是因为definition of FOV angle是相机的光学属性,与3D投影无关,因此整个投影矩阵是您可能不需要的中间结果。

ARCamera还会显示一个描述相机光学属性的intrinsics矩阵。该矩阵中沿对角线的第一和第二值是摄像机图像中单个像素的水平和垂直focal length。如果您有焦距和图像宽度/高度,则可以按definition of FOV angle计算FOV:

let imageResolution = session.currentFrame!.camera.imageResolution
let intrinsics = session.currentFrame!.camera.intrinsics
let xFovDegrees = 2 * atan(Float(imageResolution.width)/(2 * intrinsics[0,0])) * 180/Float.pi
let yFovDegrees = 2 * atan(Float(imageResolution.height)/(2 * intrinsics[1,1])) * 180/Float.pi
  

注意:与使用projectionMatrix的版本一样,这取决于相机图片的尺寸和始终横向,而不是设备屏幕或者您正在显示AR内容的视图。如果您需要基于视口的内容,请向上滚动到&#34;使用Viewport&#34;的投影矩阵。

答案 1 :(得分:-4)

或者你可以这样做:

sceneView.pointOfView?.camera?.fieldOfView