透过 AVCaptureStillImageOutput 做静态影像的撷取
转载自:http://iteches.com/archives/34497
在 iOS 6 SDK(iOS 5 SDK 以上)中捕捉摄影机的静态拍摄画面,制作类似「录像同时拍照的效果」可以透果很多方式,像是透过
UIImagePickerController 的方式,呼叫 iOS SDK 所提供的 API 来捕捉画面,或是透过
AVFoundation 的
方式,建立影像的 AVCaptureSession,并且设定对应的 Input 与 Output。而本篇文章所采用的方法属于后者,我们使用
AVCaptureStillImageOutput 来当做 AVCaptureSession 的 Output 端,输出静态影像。
在开始之前请先替您的项目加上 AVFoundation.framework,并且在对应的类别中引用此头文件。替项目加入 F ramework 的方法请参考
Xcode 4 新增 Framework 的方法 一文。
建立 AVCaptureSession
在这部份中我们要在类别的 @interface 区段中宣告一个 AVCaptureSession 型态的全局变量,方便之后的作业。
AVCaptureSession *myCaptureSession;
在建立 AVCaptureSession 的部份,其实并没有太多的设定,唯一要注意的是 setSessionPreset:
这个方法函式,它决定了摄影机捕获影像的分辨率大小,你可以使用 AVCaptureSessionPreset 的关键词来查阅各种分辨率的定义。
//建立 AVCaptureSession
myCaptureSession = [[AVCaptureSession alloc] init];
[myCaptureSession setSessionPreset:AVCaptureSessionPresetPhoto];
建立 AVCaptureDeviceInput
在建立好 AVCaptureSession 之后,接下来我们要对他的输入端做设定,你可以透过下列程序代码来取得装置上具有输入特性的硬件装置和他们实际的装置名称。
//建立 AVCaptureDeviceInput
NSArray *myDevices = [AVCaptureDevice devices];
for (AVCaptureDevice *device in myDevices) {
if ([device position] == AVCaptureDevicePositionBack) {
NSLog(@"后摄影机硬件名称: %@", [device localizedName]);
}
if ([device position] == AVCaptureDevicePositionFront) {
NSLog(@"前摄影机硬件名称: %@", [device localizedName]);
}
if ([device hasMediaType:AVMediaTypeAudio]) {
NSLog(@"麦克风硬件名称: %@", [device localizedName]);
}
}
装置上具有输入特性的硬件名称 |
下列我们就以后置镜头为例,制作 AVCaptureSession 的输入端。
//使用后置镜头当做输入
NSError *error = nil;
for (AVCaptureDevice *device in myDevices) {
if ([device position] == AVCaptureDevicePositionBack) {
AVCaptureDeviceInput *myDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (error) {
//装置取得失败时的处理程序
} else {
[myCaptureSession addInput:myDeviceInput];
}
}
}
建立 AVCaptureVideoPreviewLayer
在建立 AVCaptureSession 与设定对应的 Input
之后,我们就可以透过 AVCaptureVideoPreviewLayer 来取得摄影机所捕捉的连续画面,在
此,AVCaptureVideoPreviewLayer
所呈现的画面是连续的,并非单张的静态影像,当然你也可以略过设定 AVCaptureVideoPreviewLayer
的步骤,不显示摄影机所拍摄到的画面,这并不会有任何影响。
//建立 AVCaptureVideoPreviewLayer
AVCaptureVideoPreviewLayer *myPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:myCaptureSession];
[myPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
CGRect rect = CGRectMake(160, 180, 320, 240);
[myPreviewLayer setBounds:rect];
UIView *myView = [[UIView alloc]initWithFrame:rect];
[myView.layer addSublayer:myPreviewLayer];
[self.view addSubview:myView];
//启用摄影机
[myCaptureSession startRunning];
在上述程序代码中,
我们透过 AVCaptureSession 来建立 AVCaptureVideoPreviewLayer,并且设定它的 VideoGravity
属性为
AVLayerVideoGravityResizeAspectFill,表示所取得的影像必须以等比例的方式缩放来填满我们所指定的大小
(CGRect rect)。
到目前为止,我们还并未对 AVCaptureSession 做 Output 的设定,但是已经可以透过 AVCaptureVideoPreviewLayer 在画面上看到摄影机所拍摄的影像。下面,我们将针对静态影像的撷取来制作对应的 Output。
建立 AVCaptureStillImageOutput
要使用单张静态影像的撷取,就可以考虑使用 AVCaptureStillImageOutput 来制作你的 Output
端,否则最好是使用 AVCaptureVideoDataOutput 来当做输出端,这两个不同类型的 Output 可以做的事情也不尽相同,关于
AVCaptureVideoDataOutput 可以参考
透过 AVCaptureVideoDataOutput 做连续影像片段的撷取一文,来获得更多信息,下面我们只针对 AVCaptureStillImageOutput 做说明。
在这部份中我们要在类别的 @interface 区段中宣告一个 AVCaptureStillImageOutput 型态的全局变量,方便之后的作业。
AVCaptureStillImageOutput *myStillImageOutput;
接着,在根据以下程序代码对 AVCaptureStillImageOutput 做设定,并将此 Output 与 AVCaptureSession 做连接。
//建立 AVCaptureStillImageOutput
myStillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *myOutputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG,AVVideoCodecKey,nil];
[myStillImageOutput setOutputSettings:myOutputSettings];
[myCaptureSession addOutput:myStillImageOutput];
在设定 AVCaptureStillImageOutput 上,我们将所撷取到的影像做 JPEG 的编码以节省空间,虽然说使用
AVVideoCodec 这个关键词时,你可以看到 AVVideoCodecH264 的选项,但是他并不是静态影像的编码方式,而是使用在
Video 上的一种压缩编码,所以无法使用。
撷取单张静态影像
在成功建立好 AVCaptureSession 的 Output 之后,接下来就是制作静态影像撷取,你可以透过下列的方法函式,来与拍照的按钮做互动,取得静态影像。
AVCaptureConnection *myVideoConnection = nil;
//从 AVCaptureStillImageOutput 中取得正确类型的 AVCaptureConnection
for (AVCaptureConnection *connection in myStillImageOutput.connections) {
for (AVCaptureInputPort *port in [connection inputPorts]) {
if ([[port mediaType] isEqual:AVMediaTypeVideo]) {
myVideoConnection = connection;
break;
}
}
}
//撷取影像(包含拍照音效)
[myStillImageOutput
captureStillImageAsynchronouslyFromConnection:myVideoConnection
completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError
*error) {
//完成撷取时的处理程序(Block)
if (imageDataSampleBuffer) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
//取得的静态影像
UIImage *myImage = [[UIImage alloc] initWithData:imageData];
[self setImage:myImage];
//取得影像数据(需要ImageIO.framework 与 CoreMedia.framework)
CFDictionaryRef myAttachments = CMGetAttachment(imageDataSampleBuffer, kCGImagePropertyExifDictionary, NULL);
NSLog(@"影像属性: %@", myAttachments);
}
}];
在上述程序代码中,
我们必须先从 AVCaptureStillImageOutput 中取得正确类型的
AVCaptureConnection,接着,才利用此 AVCaptureConnection
做影像的撷取,另外,如果你希望改变撷取影像时的方向,也可以对 AVCaptureConnection 做
setVideoOrientation: 旋转影像,或 setVideoMirrored: 镜射影像。
另外,在撷影像时会包含拍照的音效,无法消除,这是基于 iOS SDK 安全条款的使用原则,如果你是想制作每秒撷取 N 张静态影像的话,最好还是使用 AVCaptureVideoDataOutput 来当做输出,避免产生连续的拍照音效。
最后,我们也可以使用 CMGetAttachment 来取得该影像的其他属性,但是这必须使用 ImageIO 与 CoreMedia 两个 Framework 才行。
|
使用 CMGetAttachment 所取的的影像属性 |