相机的工作原理
轻轻一按,你的相机就把光子转换为了比特,于是一张相片就保存到了你的 iPhone 里,像这样的神奇能力是如何实现的呢?
让我们假设一下你身处室外,环顾四周。三亿里之外,太阳无时无刻不在发射光子。它们需要花上 8 分钟之久才能到达我们舒适的星球。有一些光子撞击到你周围的物体,并反射到你眼睛的视网膜上,这会使你的大脑以这些物体为基准,创建一副图像,我们将其称之为视觉。
摄影要做的就是捕获这幅图像。摄影是在 200 年前被发明的,但在此之前的好几千年里,人类已经通过绘画的方式来捕捉他们所看到的东西了。
我们中的很多人其实每天都相机不离身:当今智能手机已经是最常用的相机之一了。在数码摄影的时代之前,摄影通常是记录在纸上或者胶卷上的。而今天,摄影将光转换为比特信息。
快门速度,感光度 (ISO),光圈
在我们深入将光子转换为 JPEG 文件之前,我们先来看看相片的一些基本概念。这些概念不仅在过去的胶卷摄影时有效,在今天也是同样的。在不久之前,几乎所有的摄影都是通过胶卷完成的。与今天使用数码感光器不同,那时候成像依赖的是受光照影响的生化反应。但因为在拍摄相片中其他所有东西都遵循同样的原则,所以对笨重的胶卷相机和胶卷摄影适用的规律一样会在使用 iPhone 拍摄时适用。
进光量
拍摄一张相片的过程有时候被称之为曝光。曝光也指单位面积上光的数量。为了相片亮度合适,光的数量也需要在一定的范围内。
如果我们没有捕获足够的光,那么相片就会欠曝 - 图像被湮没在图像传感器或者胶卷的固有噪声中。
如果我们捕获的光太多,图像就会过曝 - 图片传感器/胶卷所接受的光过于饱和,无法区分不同的光的数量,这意味着几乎所有区域看上去曝光程度都是一样的。
当拍摄照片时,我们必须将相机调整到光线的量既不太高也不太低,在欠曝图像中,即使我们尝试将它调亮,图像的暗部依然被 “卡” 在黑色,我们没有办法让图像中的笔确实拥有不同的颜色。过曝图像中也有大部分区域被卡在白色或者灰色:编织带和硬币细节已经完全丢失了。
曝光档数
有三个要素可以影响曝光的进光量:快门速度,光圈和感光度 (ISO)。
在摄影中,通过调整这三者中任意一个来让进光量翻倍或者减半,就叫做改变了 “一档” 曝光。每一档曝光对于这三者 (快门速度,光圈和曝光度) 来说对应的是不同的数字变化。但如果我们将快门速度调整一档,我们需要通过调整 ISO 或者光圈一档来进行补偿,才能获取同样的进光量。
光圈
相机 (更确切地说是相机的镜头) 的光圈是用来衡量到达图像感应器的光所通过的通孔的大小的。光圈值以 F 比例 (焦比) 来进行标定,比如 ƒ/5.6,其中 5.6 表示镜头焦距与光圈 (也就是通孔) 的有效直径的比例。
可能 F 比例会让人有点迷惑。F 比例所对应的一档指的是当前值与根号 2 (√₂ ≃ 1.4) 相乘或者相除。在 ƒ/4 时,我们所得到的光的量是 ƒ/2.8 时的一半。
除了影响进光量,光圈还会对景深造成影响。这和对焦有关系。相机中的光学系统会将与相机一定距离范围内的物体渲染清晰。当我们改变光圈时,这个距离范围将变宽或者变窄。我们可以用这个特性来达到一些很好的效果 (译者注:比如人像的背景虚化等)。但很遗憾的是,我们不能调整 iPhone 的光圈大小。
结合起来说
现在我们知道一些基础知识了,那么相机实际上是如何捕捉图像的呢?
在你的 iPhone 相机里面,有一个图像传感器。这个部分就相当于我们眼睛里的视网膜。图像传感器可以将光或者光子转换为电信号。
图像传感器是由海量的独个的像素传感器串起来的巨大矩形区域。我们可以将每个像素传感器想象成一个装电荷的桶。当光子撞击到像素传感器的光二极管时,它们将在这个像素的桶中缓慢地积攒电荷。最后,每个像素都会有它自己的一小桶电子。这些电荷的数量是依赖于光子数量的 - 或者说是决定于打到这个特定的点上的光的强度。
因为我们有一个像素传感器的二维阵列,我们现在就拥有能够反应出所有这些位置的光的强度的一组二维电荷阵列了。
现在,我们需要明白两件事情:第一,我们需要有重置这些电荷的能力;其次,一旦像素传感器曝光了,我们需要能够读出这些电荷的数量。重置这件事情可以全局地对所有像素一起进行。但是对这样的八百万个小电荷,我们倾向于单独处理,以将它们转换为伏特量级的量,以方便读取。
数码相机通常会去移动一行中的像素:图像传感器首先读取一行中第一个电荷桶中的电荷,然后所有的桶将它们中存放的电荷转移给相邻的前一个桶。这样第一个电荷桶现在就保存了本来在第二个桶中的电荷,并准备好了再次被读取。重复这样的步骤,所有像素的值都将被读入到传感器的行中。
正在被读取的桶,或者说像素传感器中的值将会被一个模数转换器 (ADC) 转换为数字信号。ADC 的输出是一个对应每个像素传感器所接收到的进光量的数字。最终,这些值被传递到一个数字图像处理器中进行处理。
iOS 上的相机捕捉
古早的 iOS 开发如果想使用 iPhone 上的相机,只能使用 UIImagePickerController
,幸好,后来有了更灵活的 AVFoundation
。
关于 AVFoundation
,我自己写了一个简单的相机使用框架 NJCamera,如果不需要多么复杂的功能的话,可以试试.
使用 AVFoundation
的注意事项
首先 iOS10 之后,
AVCapturePhotoOutput
被用来替代AVCaptureStillImageOutput
, 所以如果你需要捕获照片,那么AVCapturePhotoOutput
是更好的选择。AVCaptureSession
的所有调用都是线程阻塞的,所以我们最好创建一条线程来异步调用。AVCaptureMovieFileOutput
和AVCaptureVideoDataOutput
不能同时使用,所以如果你需要同时处理视频帧和视频录制的话,可能需要自己实现了 QAQ经过测试,事实上把所有
AVCaptureSession
实例的操作都放到同一个线程中,会有效提高销毁再创建的速度,如果需要在不同的页面操作多个相机实例的话,这也许是个实用的小技巧。如果对相机的初始化速度要求很高的话,那么最好把
AVCaptureSession
的操作提前到 VC 的页面出现之前。AVCaptureVideoPreviewLayer
持有AVCaptureSession
实例,AVCaptureSession
实例 持有 所有 outputs。如果你需要对实时预览图进行操作或使用滤镜的话,那么就需要使用
AVCaptureVideoDataOutput
获得每一帧的图片进行处理,并绘制到 view 上显示出来。(比如使用OpenGL
)
分级捕获
分级捕获,英文全称叫 Bracketed Capture,是 iOS8 就有的 API,所以在 AVCapturePhotoOutput
还没出世的时候就已经诞生了。分级捕获,其实际上就是在拍照的时候设置多组参数(目前支持曝光和 ISO ),系统会拍出多张图片,AVCapturePhotoCaptureDelegate的photoOutput(_:didFinishProcessingPhoto:error:)
方法会跟着成倍回调,调用者可以使用某种算法(如 HDR )对图片进行融合,得到一张令人满意的图片。
1 | let bracketedStillImageSettings = [ |
调用完拍照后,AVCapturePhotoCaptureDelegate的photoOutput(_:didFinishProcessingPhoto:error:)
会回调三次,从左到右曝光度分别为-1,0,1。
然后利用某种 HDR 算法,将三张图融合即可。