第一臺(tái) iPhone 問(wèn)世就裝有相機(jī)。在第一個(gè) SKDs 版本中,在 app 里面整合相機(jī)的唯一方法就是使用 UIImagePickerController,但到了 iOS 4,發(fā)布了更靈活的 AVFoundation 框架。
在這篇文章里,我們將會(huì)看到如何使用 AVFoundation 捕捉圖像,如何操控相機(jī),以及它在 iOS 8 的新特性。
UIImagePickerControllerUIImagePickerController 提供了一種非常簡(jiǎn)單的拍照方法。它支持所有的基本功能,比如切換到前置攝像頭,開(kāi)關(guān)閃光燈,點(diǎn)擊屏幕區(qū)域?qū)崿F(xiàn)對(duì)焦和曝光,以及在 iOS 8 中像系統(tǒng)照相機(jī)應(yīng)用一樣調(diào)整曝光。
然而,當(dāng)有直接訪問(wèn)相機(jī)的需求時(shí),也可以選擇 AVFoundation 框架。它提供了完全的操作權(quán),例如,以編程方式更改硬件參數(shù),或者操縱實(shí)時(shí)預(yù)覽圖。
AVFoundation 框架基于以下幾個(gè)類(lèi)實(shí)現(xiàn)圖像捕捉 ,通過(guò)這些類(lèi)可以訪問(wèn)來(lái)自相機(jī)設(shè)備的原始數(shù)據(jù)并控制它的組件。
AVCaptureDevice 是關(guān)于相機(jī)硬件的接口。它被用于控制硬件特性,諸如鏡頭的位置、曝光、閃光燈等。AVCaptureDeviceInput 提供來(lái)自設(shè)備的數(shù)據(jù)。AVCaptureOutput 是一個(gè)抽象類(lèi),描述 capture session 的結(jié)果。以下是三種關(guān)于靜態(tài)圖片捕捉的具體子類(lèi):
AVCaptureStillImageOutput 用于捕捉靜態(tài)圖片AVCaptureMetadataOutput 啟用檢測(cè)人臉和二維碼AVCaptureVideoOutput 為實(shí)時(shí)預(yù)覽圖提供原始幀AVCaptureSession 管理輸入與輸出之間的數(shù)據(jù)流,以及在出現(xiàn)問(wèn)題時(shí)生成運(yùn)行時(shí)錯(cuò)誤。AVCaptureVideoPreviewLayer 是 CALayer 的子類(lèi),可被用于自動(dòng)顯示相機(jī)產(chǎn)生的實(shí)時(shí)圖像。它還有幾個(gè)工具性質(zhì)的方法,可將 layer 上的坐標(biāo)轉(zhuǎn)化到設(shè)備上。它看起來(lái)像輸出,但其實(shí)不是。另外,它擁有 session (outputs 被 session 所擁有)。讓我們看看如何捕獲圖像。首先我們需要一個(gè) AVCaptureSession 對(duì)象:
let session = AVCaptureSession()
現(xiàn)在我們需要一個(gè)相機(jī)設(shè)備輸入。在大多數(shù) iPhone 和 iPad 中,我們可以選擇后置攝像頭或前置攝像頭 -- 又稱(chēng)自拍相機(jī) (selfie camera) -- 之一。那么我們必須先遍歷所有能提供視頻數(shù)據(jù)的設(shè)備 (麥克風(fēng)也屬于 AVCaptureDevice,因此略過(guò)不談),并檢查 position 屬性:
let availableCameraDevices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
for device in availableCameraDevices as [AVCaptureDevice] {
if device.position == .Back {
backCameraDevice = device
}
else if device.position == .Front {
frontCameraDevice = device
}
}
然后,一旦我們發(fā)現(xiàn)合適的相機(jī)設(shè)備,我們就能獲得相關(guān)的 AVCaptureDeviceInput 對(duì)象。我們會(huì)將它設(shè)置為 session 的輸入:
var error:NSError?
let possibleCameraInput: AnyObject? = AVCaptureDeviceInput.deviceInputWithDevice(backCameraDevice, error: &error)
if let backCameraInput = possibleCameraInput as? AVCaptureDeviceInput {
if self.session.canAddInput(backCameraInput) {
self.session.addInput(backCameraInput)
}
}
注意當(dāng) app 首次運(yùn)行時(shí),第一次調(diào)用 AVCaptureDeviceInput.deviceInputWithDevice() 會(huì)觸發(fā)系統(tǒng)提示,向用戶請(qǐng)求訪問(wèn)相機(jī)。這在 iOS 7 的時(shí)候只有部分國(guó)家會(huì)有,到了 iOS 8 拓展到了所有地區(qū)。除非得到用戶同意,否則相機(jī)的輸入會(huì)一直是一個(gè)黑色畫(huà)面的數(shù)據(jù)流。
對(duì)于處理相機(jī)的權(quán)限,更合適的方法是先確認(rèn)當(dāng)前的授權(quán)狀態(tài)。要是在授權(quán)還沒(méi)有確定的情況下 (也就是說(shuō)用戶還沒(méi)有看過(guò)彈出的授權(quán)對(duì)話框時(shí)),我們應(yīng)該明確地發(fā)起請(qǐng)求。
let authorizationStatus = AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo)
switch authorizationStatus {
case .NotDetermined:
// 許可對(duì)話沒(méi)有出現(xiàn),發(fā)起授權(quán)許可
AVCaptureDevice.requestAccessForMediaType(AVMediaTypeVideo,
completionHandler: { (granted:Bool) -> Void in
if granted {
// 繼續(xù)
}
else {
// 用戶拒絕,無(wú)法繼續(xù)
}
})
case .Authorized:
// 繼續(xù)
case .Denied, .Restricted:
// 用戶明確地拒絕授權(quán),或者相機(jī)設(shè)備無(wú)法訪問(wèn)
}
如果能繼續(xù)的話,我們會(huì)有兩種方式來(lái)顯示來(lái)自相機(jī)的圖像流。最簡(jiǎn)單的就是,生成一個(gè)帶有 AVCaptureVideoPreviewLayer 的 view,并使用 capture session 作為初始化參數(shù)。
previewLayer = AVCaptureVideoPreviewLayer.layerWithSession(session) as AVCaptureVideoPreviewLayer
previewLayer.frame = view.bounds
view.layer.addSublayer(previewLayer)
AVCaptureVideoPreviewLayer 會(huì)自動(dòng)地顯示來(lái)自相機(jī)的輸出。當(dāng)我們需要將實(shí)時(shí)預(yù)覽圖上的點(diǎn)擊轉(zhuǎn)換到設(shè)備的坐標(biāo)系統(tǒng)中,比如點(diǎn)擊某區(qū)域?qū)崿F(xiàn)對(duì)焦時(shí),這種做法會(huì)很容易辦到。之后我們會(huì)看到具體細(xì)節(jié)。
第二種方法是從輸出數(shù)據(jù)流捕捉單一的圖像幀,并使用 OpenGL 手動(dòng)地把它們顯示在 view 上。這有點(diǎn)復(fù)雜,但是如果我們想要對(duì)實(shí)時(shí)預(yù)覽圖進(jìn)行操作或使用濾鏡的話,就是必要的了。
為獲得數(shù)據(jù)流,我們需要?jiǎng)?chuàng)建一個(gè) AVCaptureVideoDataOutput,這樣一來(lái),當(dāng)相機(jī)在運(yùn)行時(shí),我們通過(guò)代理方法 captureOutput(_:didOutputSampleBuffer:fromConnection:) 就能獲得所有圖像幀 (除非我們處理太慢而導(dǎo)致掉幀),然后將它們繪制在一個(gè) GLKView 中。不需要對(duì) OpenGL 框架有什么深刻的理解,我們只需要這樣就能創(chuàng)建一個(gè) GLKView:
glContext = EAGLContext(API: .OpenGLES2)
glView = GLKView(frame: viewFrame, context: glContext)
ciContext = CIContext(EAGLContext: glContext)
現(xiàn)在輪到 AVCaptureVideoOutput:
videoOutput = AVCaptureVideoDataOutput()
videoOutput.setSampleBufferDelegate(self, queue: dispatch_queue_create("sample buffer delegate", DISPATCH_QUEUE_SERIAL))
if session.canAddOutput(self.videoOutput) {
session.addOutput(self.videoOutput)
}
以及代理方法:
func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, fromConnection connection: AVCaptureConnection!) {
let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
let image = CIImage(CVPixelBuffer: pixelBuffer)
if glContext != EAGLContext.currentContext() {
EAGLContext.setCurrentContext(glContext)
}
glView.bindDrawable()
ciContext.drawImage(image, inRect:image.extent(), fromRect: image.extent())
glView.display()
}
一個(gè)警告:這些來(lái)自相機(jī)的樣本旋轉(zhuǎn)了 90 度,這是由于相機(jī)傳感器的朝向所導(dǎo)致的。AVCaptureVideoPreviewLayer 會(huì)自動(dòng)處理這種情況,但在這個(gè)例子,我們需要對(duì) GLKView 進(jìn)行旋轉(zhuǎn)。
馬上就要搞定了。最后一個(gè)組件 -- AVCaptureStillImageOutput -- 實(shí)際上是最重要的,因?yàn)樗试S我們捕捉靜態(tài)圖片。只需要?jiǎng)?chuàng)建一個(gè)實(shí)例,并添加到 session 里去:
stillCameraOutput = AVCaptureStillImageOutput()
if self.session.canAddOutput(self.stillCameraOutput) {
self.session.addOutput(self.stillCameraOutput)
}
現(xiàn)在我們有了所有必需的對(duì)象,應(yīng)該為我們的需求尋找最合適的配置。這里又有兩種方法可以實(shí)現(xiàn)。最簡(jiǎn)單且最推薦是使用 session preset:
session.sessionPreset = AVCaptureSessionPresetPhoto
AVCaptureSessionPresetPhoto 會(huì)為照片捕捉選擇最合適的配置,比如它可以允許我們使用最高的感光度 (ISO) 和曝光時(shí)間,基于相位檢測(cè) (phase detection)的自動(dòng)對(duì)焦, 以及輸出全分辨率的 JPEG 格式壓縮的靜態(tài)圖片。
然而,如果你需要更多的操控,可以使用 AVCaptureDeviceFormat 這個(gè)類(lèi),它描述了一些設(shè)備使用的參數(shù),比如靜態(tài)圖片分辨率,視頻預(yù)覽分辨率,自動(dòng)對(duì)焦類(lèi)型,感光度和曝光時(shí)間限制等。每個(gè)設(shè)備支持的格式都列在 AVCaptureDevice.formats 屬性中,并可以賦值給 AVCaptureDevice 的 activeFormat (注意你并不能修改格式)。
iPhone 和 iPad 中內(nèi)置的相機(jī)或多或少跟其他相機(jī)有相同的操作,不同的是,一些參數(shù)如對(duì)焦、曝光時(shí)間 (在單反相機(jī)上的模擬快門(mén)的速度),感光度是可以調(diào)節(jié),但是鏡頭光圈是固定不可調(diào)整的。到了 iOS 8,我們已經(jīng)可以對(duì)所有這些可變參數(shù)進(jìn)行手動(dòng)調(diào)整了。
我們之后會(huì)看到細(xì)節(jié),不過(guò)首先,該啟動(dòng)相機(jī)了:
sessionQueue = dispatch_queue_create("com.example.camera.capture_session", DISPATCH_QUEUE_SERIAL)
dispatch_async(sessionQueue) { () -> Void in
self.session.startRunning()
}
在 session 和相機(jī)設(shè)備中完成的所有操作和配置都是利用 block 調(diào)用的。因此,建議將這些操作分配到后臺(tái)的串行隊(duì)列中。此外,相機(jī)設(shè)備在改變某些參數(shù)前必須先鎖定,直到改變結(jié)束才能解鎖,例如:
var error:NSError?
if currentDevice.lockForConfiguration(&error) {
// 鎖定成功,繼續(xù)配置
// currentDevice.unlockForConfiguration()
}
else {
// 出錯(cuò),相機(jī)可能已經(jīng)被鎖
}
在 iOS 相機(jī)上,對(duì)焦是通過(guò)移動(dòng)鏡片改變其到傳感器之間的距離實(shí)現(xiàn)的。
自動(dòng)對(duì)焦是通過(guò)相位檢測(cè)和反差檢測(cè)實(shí)現(xiàn)的。然而,反差檢測(cè)只適用于低分辨率和高 FPS 視頻捕捉 (慢鏡頭)。
編者注 關(guān)于相位對(duì)焦和反差對(duì)焦,可以參看[這篇文章](http://ask.zealer.com/post/149)。
AVCaptureFocusMode 是個(gè)枚舉,描述了可用的對(duì)焦模式:
Locked 指鏡片處于固定位置AutoFocus 指一開(kāi)始相機(jī)會(huì)先自動(dòng)對(duì)焦一次,然后便處于 Locked 模式。ContinuousAutoFocus 指當(dāng)場(chǎng)景改變,相機(jī)會(huì)自動(dòng)重新對(duì)焦到畫(huà)面的中心點(diǎn)。設(shè)置想要的對(duì)焦模式必須在鎖定之后實(shí)施:
let focusMode:AVCaptureFocusMode = ...
if currentCameraDevice.isFocusModeSupported(focusMode) {
... // 鎖定以進(jìn)行配置
currentCameraDevice.focusMode = focusMode
... // 解鎖
}
}
通常情況下,AutoFocus 模式會(huì)試圖讓屏幕中心成為最清晰的區(qū)域,但是也可以通過(guò)變換 “感興趣的點(diǎn) (point of interest)” 來(lái)設(shè)定另一個(gè)區(qū)域。這個(gè)點(diǎn)是一個(gè) CGPoint,它的值從左上角 {0,0} 到右下角 {1,1},{0.5,0.5} 為畫(huà)面的中心點(diǎn)。通??梢杂靡曨l預(yù)覽圖上的點(diǎn)擊手勢(shì)識(shí)別來(lái)改變這個(gè)點(diǎn),想要將 view 上的坐標(biāo)轉(zhuǎn)化到設(shè)備上的規(guī)范坐標(biāo),我們可以使用 AVVideoCaptureVideoPreviewLayer.captureDevicePointOfInterestForPoint():
var pointInPreview = focusTapGR.locationInView(focusTapGR.view)
var pointInCamera = previewLayer.captureDevicePointOfInterestForPoint(pointInPreview)
...// 鎖定,配置
// 設(shè)置感興趣的點(diǎn)
currentCameraDevice.focusPointOfInterest = pointInCamera
// 在設(shè)置的點(diǎn)上切換成自動(dòng)對(duì)焦
currentCameraDevice.focusMode = .AutoFocus
...// 解鎖
在 iOS 8 中,有個(gè)新選項(xiàng)可以移動(dòng)鏡片的位置,從較近物體的 0.0 到較遠(yuǎn)物體的 1.0 (不是指無(wú)限遠(yuǎn))。
... // 鎖定,配置
var lensPosition:Float = ... // 0.0 到 1.0的float
currentCameraDevice.setFocusModeLockedWithLensPosition(lensPosition) {
(timestamp:CMTime) -> Void in
// timestamp 對(duì)應(yīng)于應(yīng)用了鏡片位置的第一張圖像緩存區(qū)
}
... // 解鎖
這意味著對(duì)焦可以使用 UISlider 設(shè)置,這有點(diǎn)類(lèi)似于旋轉(zhuǎn)單反上的對(duì)焦環(huán)。當(dāng)用這種相機(jī)手動(dòng)對(duì)焦時(shí),通常有一個(gè)可見(jiàn)的輔助標(biāo)識(shí)指向清晰的區(qū)域。AVFoundation 里面沒(méi)有內(nèi)置這種機(jī)制,但是比如可以通過(guò)顯示 "對(duì)焦峰值 (focus peaking)"(一種將已對(duì)焦區(qū)域高亮顯示的方式) 這樣的手段來(lái)補(bǔ)救。我們?cè)谶@里不會(huì)討論細(xì)節(jié),不過(guò)對(duì)焦峰值可以很容易地實(shí)現(xiàn),通過(guò)使用閾值邊緣 (threshold edge) 濾鏡 (用自定義 CIFilter 或 GPUImageThresholdEdgeDetectionFilter),并調(diào)用 AVCaptureAudioDataOutputSampleBufferDelegate 下的 captureOutput(_:didOutputSampleBuffer:fromConnection:) 方法將它覆蓋到實(shí)時(shí)預(yù)覽圖上。
在 iOS 設(shè)備上,鏡頭上的光圈是固定的 (在 iPhone 5s 以及其之后的光圈值是 f/2.2,之前的是 f/2.4),因此只有改變曝光時(shí)間和傳感器的靈敏度才能對(duì)圖片的亮度進(jìn)行調(diào)整,從而達(dá)到合適的效果。至于對(duì)焦,我們可以選擇連續(xù)自動(dòng)曝光,在“感興趣的點(diǎn)”一次性自動(dòng)曝光,或者手動(dòng)曝光。除了指定“感興趣的點(diǎn)”,我們可以通過(guò)設(shè)置曝光補(bǔ)償 (compensation) 修改自動(dòng)曝光,也就是曝光檔位的目標(biāo)偏移。目標(biāo)偏移在曝光檔數(shù)里有講到,它的范圍在 minExposureTargetBias 與 maxExposureTargetBias 之間,0為默認(rèn)值 (即沒(méi)有“補(bǔ)償”)。
var exposureBias:Float = ... // 在 minExposureTargetBias 和 maxExposureTargetBias 之間的值
... // 鎖定,配置
currentDevice.setExposureTargetBias(exposureBias) { (time:CMTime) -> Void in
}
... // 解鎖
使用手動(dòng)曝光,我們可以設(shè)置 ISO 和曝光時(shí)間,兩者的值都必須在設(shè)備當(dāng)前格式所指定的范圍內(nèi)。
var activeFormat = currentDevice.activeFormat
var duration:CTime = ... //在activeFormat.minExposureDuration 和 activeFormat.maxExposureDuration 之間的值,或用 AVCaptureExposureDurationCurrent 表示不變
var iso:Float = ... // 在 activeFormat.minISO 和 activeFormat.maxISO 之間的值,或用 AVCaptureISOCurrent 表示不變
... // 鎖定,配置
currentDevice.setExposureModeCustomWithDuration(duration, ISO: iso) { (time:CMTime) -> Void in
}
... // 解鎖
如何知道照片曝光是否正確呢?我們可以通過(guò) KVO,觀察 AVCaptureDevice 的 exposureTargetOffset 屬性,確認(rèn)是否在 0 附近。
數(shù)碼相機(jī)為了適應(yīng)不同類(lèi)型的光照條件需要補(bǔ)償。這意味著在冷光線的條件下,傳感器應(yīng)該增強(qiáng)紅色部分,而在暖光線下增強(qiáng)藍(lán)色部分。在 iPhone 相機(jī)中,設(shè)備會(huì)自動(dòng)決定合適的補(bǔ)光,但有時(shí)也會(huì)被場(chǎng)景的顏色所混淆失效。幸運(yùn)地是,iOS 8 可以里手動(dòng)控制白平衡。
自動(dòng)模式工作方式和對(duì)焦、曝光的方式一樣,但是沒(méi)有“感興趣的點(diǎn)”,整張圖像都會(huì)被納入考慮范圍。在手動(dòng)模式,我們可以通過(guò)開(kāi)爾文所表示的溫度來(lái)調(diào)節(jié)色溫和色彩。典型的色溫值在 2000-3000K (類(lèi)似蠟燭或燈泡的暖光源) 到 8000K (純凈的藍(lán)色天空) 之間。色彩范圍從最小的 -150 (偏綠) 到 150 (偏品紅)。
溫度和色彩可以被用于計(jì)算來(lái)自相機(jī)傳感器的恰當(dāng)?shù)?RGB 值,因此僅當(dāng)它們做了基于設(shè)備的校正后才能被設(shè)置。
以下是全部過(guò)程:
var incandescentLightCompensation = 3_000
var tint = 0 // 不調(diào)節(jié)
let temperatureAndTintValues = AVCaptureWhiteBalanceTemperatureAndTintValues(temperature: incandescentLightCompensation, tint: tint)
var deviceGains = currentCameraDevice.deviceWhiteBalanceGainsForTemperatureAndTintValues(temperatureAndTintValues)
... // 鎖定,配置
currentCameraDevice.setWhiteBalanceModeLockedWithDeviceWhiteBalanceGains(deviceGains) {
(timestamp:CMTime) -> Void in
}
}
... // 解鎖
AVCaptureMetadataOutput 可以用于檢測(cè)人臉和二維碼這兩種物體。很明顯,沒(méi)什么人用二維碼 (編者注: 因?yàn)樵跉W美現(xiàn)在二維碼不是很流行,這里是一個(gè)惡搞。鏈接的這個(gè) tumblr 博客的主題是 “當(dāng)人們?cè)趻叨S碼時(shí)的圖片”,但是 2012 年開(kāi)博至今沒(méi)有任何一張圖片,暗諷二維碼根本沒(méi)人在用,這和以中日韓為代表的亞洲用戶群體的使用習(xí)慣完全相悖),因此我們就來(lái)看看如何實(shí)現(xiàn)人臉檢測(cè)。我們只需通過(guò) AVCaptureMetadataOutput 的代理方法捕獲的元對(duì)象:
var metadataOutput = AVCaptureMetadataOutput()
metadataOutput.setMetadataObjectsDelegate(self, queue: self.sessionQueue)
if session.canAddOutput(metadataOutput) {
session.addOutput(metadataOutput)
}
metadataOutput.metadataObjectTypes = [AVMetadataObjectTypeFace]
func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {
for metadataObject in metadataObjects as [AVMetadataObject] {
if metadataObject.type == AVMetadataObjectTypeFace {
var transformedMetadataObject = previewLayer.transformedMetadataObjectForMetadataObject(metadataObject)
}
}
更多關(guān)于人臉檢測(cè)與識(shí)別的內(nèi)容請(qǐng)查看 Engin 的文章。
最后,我們要做的是捕捉高分辨率的圖像,于是我們調(diào)用 captureStillImageAsynchronouslyFromConnection(connection, completionHandler)。在數(shù)據(jù)時(shí)被讀取時(shí),completion handler 將會(huì)在某個(gè)未指定的線程上被調(diào)用。
如果設(shè)置使用 JPEG 編碼作為靜態(tài)圖片輸出,不管是通過(guò) session .Photo 預(yù)設(shè)設(shè)定的,還是通過(guò)設(shè)備輸出設(shè)置設(shè)定的,sampleBuffer 都會(huì)返回包含圖像的元數(shù)據(jù)。如果在 AVCaptureMetadataOutput 中是可用的話,這會(huì)包含 EXIF 數(shù)據(jù),或是被識(shí)別的人臉等:
dispatch_async(sessionQueue) { () -> Void in
let connection = self.stillCameraOutput.connectionWithMediaType(AVMediaTypeVideo)
// 將視頻的旋轉(zhuǎn)與設(shè)備同步
connection.videoOrientation = AVCaptureVideoOrientation(rawValue: UIDevice.currentDevice().orientation.rawValue)!
self.stillCameraOutput.captureStillImageAsynchronouslyFromConnection(connection) {
(imageDataSampleBuffer, error) -> Void in
if error == nil {
// 如果使用 session .Photo 預(yù)設(shè),或者在設(shè)備輸出設(shè)置中明確進(jìn)行了設(shè)置
// 我們就能獲得已經(jīng)壓縮為JPEG的數(shù)據(jù)
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer)
// 樣本緩沖區(qū)也包含元數(shù)據(jù),我們甚至可以按需修改它
let metadata:NSDictionary = CMCopyDictionaryOfAttachments(nil, imageDataSampleBuffer, CMAttachmentMode(kCMAttachmentMode_ShouldPropagate)).takeUnretainedValue()
if let image = UIImage(data: imageData) {
// 保存圖片,或者做些其他想做的事情
...
}
}
else {
NSLog("error while capturing still image: \(error)")
}
}
}
當(dāng)圖片被捕捉的時(shí)候,有視覺(jué)上的反饋是很好的體驗(yàn)。想要知道何時(shí)開(kāi)始以及何時(shí)結(jié)束的話,可以使用 KVO 來(lái)觀察 AVCaptureStillImageOutput 的 isCapturingStillImage 屬性。
在 iOS 8 還有一個(gè)有趣的特性叫“分級(jí)捕捉”,可以在不同的曝光設(shè)置下拍攝幾張照片。這在復(fù)雜的光線下拍照顯得非常有用,例如,通過(guò)設(shè)定 -1、0、1 三個(gè)不同的曝光檔數(shù),然后用 HDR 算法合并成一張。
以下是代碼實(shí)現(xiàn):
dispatch_async(sessionQueue) { () -> Void in
let connection = self.stillCameraOutput.connectionWithMediaType(AVMediaTypeVideo)
connection.videoOrientation = AVCaptureVideoOrientation(rawValue: UIDevice.currentDevice().orientation.rawValue)!
var settings = [-1.0, 0.0, 1.0].map {
(bias:Float) -> AVCaptureAutoExposureBracketedStillImageSettings in
AVCaptureAutoExposureBracketedStillImageSettings.autoExposureSettingsWithExposureTargetBias(bias)
}
var counter = settings.count
self.stillCameraOutput.captureStillImageBracketAsynchronouslyFromConnection(connection, withSettingsArray: settings) {
(sampleBuffer, settings, error) -> Void in
...
// 保存 sampleBuffer(s)
// 當(dāng)計(jì)數(shù)為0,捕捉完成
counter--
}
}
這很像是單個(gè)圖像捕捉,但是不同的是 completion handler 被調(diào)用的次數(shù)和設(shè)置的數(shù)組的元素個(gè)數(shù)一樣多。
我們已經(jīng)詳細(xì)看到如何在 iPhone 應(yīng)用里面實(shí)現(xiàn)拍照的基礎(chǔ)功能(呃…不光是 iPhone,用 iPad 拍照其實(shí)也是不錯(cuò)的)。你也可以查看這個(gè)例子。最后說(shuō)下,iOS 8 允許更精確的捕捉,特別是對(duì)于高級(jí)用戶,這使得 iPhone 與專(zhuān)業(yè)相機(jī)之間的差距縮小,至少在手動(dòng)控制上。不過(guò),不是任何人都喜歡在日常拍照時(shí)使用復(fù)雜的手動(dòng)操作界面,因此請(qǐng)合理地使用這些特性。