iOS 上的相机捕捉

753 查看

第一台 iPhone 问世就装有相机。在第一个 SKDs 版本中,在 app 里面整合相机的唯一方法就是使用 UIImagePickerController,但到了 iOS 4,发布了更灵活的 AVFoundation 框架。

在这篇文章里,我们将会看到如何使用 AVFoundation 捕捉图像,如何操控相机,以及它在 iOS 8 的新特性。

概述

AVFoundation vs. UIImagePickerController

UIImagePickerController 提供了一种非常简单的拍照方法。它支持所有的基本功能,比如切换到前置摄像头,开关闪光灯,点击屏幕区域实现对焦和曝光,以及在 iOS 8 中像系统照相机应用一样调整曝光。

然而,当有直接访问相机的需求时,也可以选择 AVFoundation 框架。它提供了完全的操作权,例如,以编程方式更改硬件参数,或者操纵实时预览图。

AVFoundation 相关类

AVFoundation 框架基于以下几个类实现图像捕捉 ,通过这些类可以访问来自相机设备的原始数据并控制它的组件。

  • AVCaptureDevice 是关于相机硬件的接口。它被用于控制硬件特性,诸如镜头的位置、曝光、闪光灯等。
  • AVCaptureDeviceInput 提供来自设备的数据。
  • AVCaptureOutput 是一个抽象类,描述 capture session 的结果。以下是三种关于静态图片捕捉的具体子类:
    • AVCaptureStillImageOutput 用于捕捉静态图片
    • AVCaptureMetadataOutput 启用检测人脸和二维码
    • AVCaptureVideoOutput 为实时预览图提供原始帧
  • AVCaptureSession 管理输入与输出之间的数据流,以及在出现问题时生成运行时错误。
  • AVCaptureVideoPreviewLayer 是 CALayer 的子类,可被用于自动显示相机产生的实时图像。它还有几个工具性质的方法,可将 layer 上的坐标转化到设备上。它看起来像输出,但其实不是。另外,它拥有 session (outputs 被 session 所拥有)。

设置

让我们看看如何捕获图像。首先我们需要一个 AVCaptureSession 对象:

现在我们需要一个相机设备输入。在大多数 iPhone 和 iPad 中,我们可以选择后置摄像头或前置摄像头 — 又称自拍相机 (selfie camera) — 之一。那么我们必须先遍历所有能提供视频数据的设备 (麦克风也属于 AVCaptureDevice,因此略过不谈),并检查position 属性:

然后,一旦我们发现合适的相机设备,我们就能获得相关的 AVCaptureDeviceInput 对象。我们会将它设置为 session 的输入:

注意当 app 首次运行时,第一次调用 AVCaptureDeviceInput.deviceInputWithDevice() 会触发系统提示,向用户请求访问相机。这在 iOS 7 的时候只有部分国家会有,到了 iOS 8 拓展到了所有地区。除非得到用户同意,否则相机的输入会一直是一个黑色画面的数据流。

对于处理相机的权限,更合适的方法是先确认当前的授权状态。要是在授权还没有确定的情况下 (也就是说用户还没有看过弹出的授权对话框时),我们应该明确地发起请求。

如果能继续的话,我们会有两种方式来显示来自相机的图像流。最简单的就是,生成一个带有 AVCaptureVideoPreviewLayer 的 view,并使用 capture session 作为初始化参数。

AVCaptureVideoPreviewLayer 会自动地显示来自相机的输出。当我们需要将实时预览图上的点击转换到设备的坐标系统中,比如点击某区域实现对焦时,这种做法会很容易办到。之后我们会看到具体细节。

第二种方法是从输出数据流捕捉单一的图像帧,并使用 OpenGL 手动地把它们显示在 view 上。这有点复杂,但是如果我们想要对实时预览图进行操作或使用滤镜的话,就是必要的了。

为获得数据流,我们需要创建一个 AVCaptureVideoDataOutput,这样一来,当相机在运行时,我们通过代理方法captureOutput(_:didOutputSampleBuffer:fromConnection:) 就能获得所有图像帧 (除非我们处理太慢而导致掉帧),然后将它们绘制在一个 GLKView 中。不需要对 OpenGL 框架有什么深刻的理解,我们只需要这样就能创建一个 GLKView: