ARView 是一个集成度非常高的控件,我们可以直接使用 ARView 来创建一个可预览的视频界面。这篇文章介绍的是如何在 ARView 上实现画面的缩放。

ARView 的 Camera 管理是由 ARSession 来进行的 (arview.session),目前我并未找到能够通过 ARSession 获取其内部的 Camera 相关的对象的方法。值得另辟蹊径。根据我的理解,Camera 相关的对象是对设备的直接抽象,所以这类对象应该是 Singleton 模式的,即如果我通过其他渠道获取到 Camera 对象,那么这个对象和 ARView 使用的 Camera 对象应该是同一个。

使用下面的代码获取默认的摄像头对象

1
let device = AVCaptureDevice.default(for: .video)

使用 AVCaptureDevice.videoZoomFactor 可以缩放画面。例如下面我写了一个 Pinch 操作的响应函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@objc
func handlePinchGesture(_ sender: UIPinchGestureRecognizer) {
do {
switch sender.state {
case .began:
cameraScale = device.videoZoomFactor // cameraScale 是类成员变量
case .changed:
let scale = min(max(cameraScale * sender.scale, 1), 4)
try device.lockForConfiguration()
device.videoZoomFactor = scale
device.unlockForConfiguration()
default:
break
}
} catch {}
}

这个函数可以实现对通过双指捏合操作对摄像头画面进行缩放,且可以直接影响到 ARView 的画面。

但是在成功地实现 ARView 的画面缩放之后,我发现了两个问题:

  1. Xcode 的 Console 中会输出大量的警告信息,显示摄像头的校准矩阵出现问题;
  2. 放大的画面比较粗糙

这两个现象的原因,经过我的分析是:ARKit 的 SLAM 追踪算法同摄像头参数高度耦合的,其内部模型无法适应画面的缩放。如画面如果放大到 x2,那么手机转动同样的角度,同样距离的物体相对运动的像素数量是不同的。也是出于同样的原因,ARSession 配置的摄像头是固定的,其videoZoomFactorUpscaleThreshold几乎是1。这意味着摄像头完全不能进行光学放大,我们在设置videoZoomFactor时,实质上底层只是将摄像头的内容进行scale+crop操作。