在iOS 11 ARKit中获取相机视野

har*_*ace 10 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
}
Run Code Online (Sandbox Code Playgroud)

ric*_*ter 34

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

⚠️ARKit+ SceneKit(不正确)

如果您已经在与ARKit通过SceneKit工作(ARSCNView),你可能会认为,ARKit自动更新SceneKit相机(视图的pointOfViewcamera)相匹配的投影变换使用ARKit.这是对的.

然而,ARKit直接设置SCNCameraprojectionTransform.当您使用的几何性质的工作SCNCamera就像zNearzFarfieldOfView,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
Run Code Online (Sandbox Code Playgroud)

所以你应该能够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
Run Code Online (Sandbox Code Playgroud)

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

let imageResolution = session.currentFrame!.camera.imageResolution
let xFov = yFov * Float(imageResolution.width / imageResolution.height)
Run Code Online (Sandbox Code Playgroud)

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

但是,如果仔细观察,您可能会注意到xFov/ yFovhere(和宽高比imageResolution)之间的宽高比不一定与设备屏幕(尤其是在iPhone X上)或您正在绘制AR内容的视图相匹配那是因为你测量了摄像机图像的FOV角度,而不是你应用程序AR视图的角度.别担心,这也有一个API ......

带视口的投影矩阵

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

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)
Run Code Online (Sandbox Code Playgroud)

你传递的内容zNearzFar没有关系,因为我们没有使用依赖于它的矩阵部分.(您可能仍需要确保zNear < zFarzNear != zFar != 0.)

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

相机内在

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

ARCamera还公开了一个intrinsics描述相机光学特性的矩阵.该矩阵中沿对角线的第一和第二值是摄像机图像中单个像素的水平和垂直焦距.如果您有焦距和图像宽度/高度,您可以根据FOV角度定义计算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
Run Code Online (Sandbox Code Playgroud)

注意:与使用的版本一样projectionMatrix,这取决于摄像机图像的大小和始终横向,而不是设备屏幕或您正在显示AR内容的视图.如果您需要基于视口的内容,请滚动备份到"带视口的投影矩阵".

  • 这是地球上最好的答案. (2认同)
  • 您是否知道如何将我从 cameraFormat 读取的 FieldOfView 调整为相机的当前焦点,使其看起来正确?我读取了超过 58 度的值,我认为我应该将其乘以来自相机会话的某个值或应用一些其他校正,但我不知道在哪里寻找它。 (2认同)