使用AVFoundation自定义相机如何调整相机焦距

1、如何使用AVFoundation框架自定义相机请先参照iOS项目实战之自定义相机

2.1、上述帖子中有讲到,如果需要对焦,可以通过AVCaptureDevicesetFocusPointOfInterest:方法来实现,传入一个CGPoint值,但这里的CGPoint不是View上面的点,而是范围的0~1的相对百分点;

2.2、这个CGPoint如何得来呢,我们在点击屏幕时可以监听相关View的点击事件,添加一个UITapGestureRecognizer手势,在点时后,可以通过UITapGestureRecognizerlocationInView:方法得到CGPoint,这是一个绝对值,即点击了相关View上的某个点,有了这个点,再通过AVCaptureVideoPreviewLayer的一个转换方法:captureDevicePointOfInterestForPoint:可以将绝对点转换成相对点。

2.3、得到CGPoint后,就可以调用AVCaptureDevice(即摄像头)的setFocusPointOfInterest:方法来实现了,但需要注意的是,这个方法不能直接调用,要分三步曲来完成:锁定AVCaptureDevice,修改参数值,解锁AVCaptureDevice。一般形式如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/// 对焦事件
- (void)focusAtPoint:(CGPoint)point
{
????AVCaptureDevice *device = self.deviceInput.device;
????NSError *error;
????if ([device isFocusModeSupported:AVCaptureFocusModeAutoFocus] && [device isFocusPointOfInterestSupported])
????{
????????if ([device lockForConfiguration:&error]) {
????????????[device setFocusPointOfInterest:point];???? // 对焦点
????????????[device setExposurePointOfInterest:point];? // 曝光点
????????????[device setFocusMode:AVCaptureFocusModeAutoFocus];? // 自动对焦模式
????????????[device unlockForConfiguration];
????????} else {
????????????NSLog(@"Error: %@", error);
????????}
????}
}

3、自动对焦实现了,如何调节焦距呢?

3.1、这里所谓的调节焦距不是对焦的意思,而是在拍摄远景时所需要的放大(拉近)影像

3.2、有两种方式来实现,第一种方式,通过放大AVCaptureVideoPreviewLayer及AVCaptureStillImageOutput的videoScaleAndCropFactor属性来实现,简单一点讲就是把预览图层放大,这样在屏幕上就只能看到一部分影像了,然后在捕捉生成图片时做相同倍数的放大,裁剪超出部分的像素实现,这种方式好比原本拍摄的照片是4000*3000的分辨率,现在通过放大只取中间2000*1500的部分输出同样大小的照片,最终的效果就是放大了图像。

3.3、另一种方式,通过调节AVCaptureDevice的videoZoomFactor属性来实现焦距的调整,这种方式事实上是在图像取景的时候就进行了相关调整,取摄像头的部分影像进入sesstion,这样更合理,并且前面那种方式有个问题:因为是通过放大图层来模拟拉近焦距,所以实际上图层是不清晰的,对焦也没什么效果。然而第二种方式中videoZoomFactor属性需要iOS7以上才支持。

3.4、设置videoZoomFactor属性与对焦一样同样需要三部曲来实现,锁定Device、设置属性、解锁Device

1
2
3
4
5
6
7
/// 调整摄像头焦距
- (void)setDevice:(AVCaptureDevice *)device videoZoomFactor:(CGFloat)scale
{
????[device lockForConfiguration:nil];
????device.videoZoomFactor = scale;
????[device unlockForConfiguration];
}

4、关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
//
//? SACameraCtrl.m
//? Test
//
//? Created by 余西安 on 15/4/13.
//? Copyright (c) 2015年 Sian. All rights reserved.
//
?
#import "SACameraCtrl.h"
#import "SAPreview.h"
#import "SAPhotoViewCtrl.h"
#import <AVFoundation/AVFoundation.h>
#import <CoreMotion/CoreMotion.h>
#define SourcePath [[NSBundle mainBundle] pathForResource:@"SAImagePickerController.bundle" ofType:nil]
?
@interface SACameraCtrl () <UIAccelerometerDelegate>
{
????CGFloat _deviceScale;
}
// 输入流预览图层
@property (nonatomic, strong)?? AVCaptureVideoPreviewLayer? *previewLayer;
?
@property (weak, nonatomic) IBOutlet SAPreview *preview;
?
// AVCaptureSession对象来执行输入设备和输出设备之间的数据传递
@property (nonatomic, strong)?? AVCaptureSession??????????? *session;
?
// 设备输入流
@property (nonatomic, strong)?? AVCaptureDeviceInput??????? *deviceInput;
?
// 照片输出流
@property (nonatomic, strong)?? AVCaptureStillImageOutput?? *imageOutput;
?
// 摄像头焦倍
@property (nonatomic, assign)?? CGFloat???????? deviceScale;
?
// 图片输出视图
@property (nonatomic, strong)?? UIImageView???? *cameraShowView;
?
// 摄像头数组
@property (nonatomic, strong, readonly) NSArray???? *devices;?????? // AVCaptureDevices
?
// 闪光灯切换按钮
@property (weak, nonatomic) IBOutlet??? UIButton??? *flashButton;
?
@end
?
@implementation SACameraCtrl
?
- (void)viewDidLoad
{
????[super viewDidLoad];
????// 创建交互流
????self.session = [[AVCaptureSession alloc] init];
????self.session.sessionPreset = AVCaptureSessionPresetPhoto;
?????
????// 媒体输入
????AVCaptureDevice *device = [self.devices firstObject];
????[self setDevice:device FlashMode:AVCaptureFlashModeAuto];
????self.deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:NULL];
????if([self.session canAddInput:self.deviceInput])[self.session addInput:self.deviceInput];
?
????// 媒体输出
????self.imageOutput = [[AVCaptureStillImageOutput alloc] init];
????self.imageOutput.outputSettings = @{AVVideoCodecKey : AVVideoCodecJPEG};
????AVCaptureConnection * connection = [self.imageOutput connectionWithMediaType:AVMediaTypeVideo];
????connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight;
????if([self.session canAddOutput:self.imageOutput])[self.session addOutput:self.imageOutput];
?????
????// 媒体预览
????self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
????self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspect;
????[self.preview.layer addSublayer:self.previewLayer];
????[self setPreviewGesture];
}
?
?
#pragma mark - 前后摄像头
- (NSArray *)devices
{
????return [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
}
?
- (void)viewWillAppear:(BOOL)animated
{
????[super viewWillAppear:animated];
????self.deviceScale = 1.0;
????self.previewLayer.frame = self.preview.layer.bounds;
????self.navigationController.navigationBar.hidden = YES;
????[self setDevice:self.deviceInput.device videoZoomFactor:self.deviceScale];
????[self.session startRunning];
}
?
- (void)viewDidAppear:(BOOL)animated
{
????[super viewDidAppear:animated];
????[[UIApplication sharedApplication] setStatusBarHidden:YES];
}
?
- (void)viewWillDisappear:(BOOL)animated
{
????[super viewWillDisappear:animated];
????[[UIApplication sharedApplication] setStatusBarHidden:NO];
}
?
- (void)viewDidDisappear:(BOOL)animated
{
????[super viewDidDisappear:animated];
????[self.session stopRunning];
}
?
/// 媒体预览图层手势事件
- (void)setPreviewGesture
{
????// 注册一个手势事件来实现对焦功能
????UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapPreview:)];
????UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchPreview:)];
????[self.preview addGestureRecognizer:tap];
????if ([UIDevice currentDevice].systemVersion.floatValue >= 7.0) [self.preview addGestureRecognizer:pinch];
}
?
/// 预览图层点击事件
- (void)tapPreview:(UITapGestureRecognizer *)gesture
{
????CGPoint point = [gesture locationInView:gesture.view];
????CGPoint p = [self.previewLayer captureDevicePointOfInterestForPoint:point];
????[self focusAtPoint:p];
????// 对焦动画
????self.preview.aperture.center = point;
????[self.preview showAperture];
}
?
/// 焦距调整事件
- (void)pinchPreview:(UIPinchGestureRecognizer *)gesture
{
????CGFloat scale = self.deviceScale + (gesture.scale - 1);
????if (scale > 5) scale = 5;?? // 最大5倍焦距
????if (scale < 1) scale = 1;?? // 最小1倍焦距
????[self setDevice:self.deviceInput.device videoZoomFactor:scale];
????// 缩放结束时记录当前倍焦
????if (gesture.state == UIGestureRecognizerStateEnded) self.deviceScale = scale;
}
?
/// 对焦事件
- (void)focusAtPoint:(CGPoint)point
{
????AVCaptureDevice *device = self.deviceInput.device;
????NSError *error;
????if ([device isFocusModeSupported:AVCaptureFocusModeAutoFocus] && [device isFocusPointOfInterestSupported])
????{
????????if ([device lockForConfiguration:&error]) {
????????????[device setFocusPointOfInterest:point];???? // 对焦点
????????????[device setExposurePointOfInterest:point];? // 曝光点
????????????[device setFocusMode:AVCaptureFocusModeAutoFocus];? // 自动对焦模式
????????????[device unlockForConfiguration];
????????} else {
????????????NSLog(@"Error: %@", error);
????????}
????}
}
?
/// 摄像头切换事件
- (IBAction)switchCamera:(UIButton *)sender
{
????if (self.devices.count < 2) return;
?????
????NSInteger oldIndex = [self.devices indexOfObject:self.deviceInput.device];
????NSInteger newIndex = (oldIndex + 1) % self.devices.count;
????AVCaptureDevice *newDevice = [self.devices objectAtIndex:newIndex];
????AVCaptureDeviceInput *newInput = [AVCaptureDeviceInput deviceInputWithDevice:newDevice error:NULL];
????if (newInput) {
????????[self.session beginConfiguration];
????????[self.session removeInput:self.deviceInput];
????????[self.session addInput:newInput];
????????[self.session commitConfiguration];
????????self.deviceInput = newInput;
????????self.flashButton.hidden = !newDevice.isFlashAvailable;
????}
}
?
/// 取消事件
- (IBAction)cancel:(UIButton *)sender
{
????[self dismissViewControllerAnimated:YES completion:nil];
}
?
/// 拍照事件
- (IBAction)shutter:(UIButton *)sender
{
????sender.userInteractionEnabled = NO;
????AVCaptureConnection * connection = [self.imageOutput connectionWithMediaType:AVMediaTypeVideo];
????connection.videoOrientation = AVCaptureVideoOrientationPortrait;
????connection.videoScaleAndCropFactor = 1.0;?? // 获取流比例
????if (!connection) {
????????NSLog(@"take photo failed!");
????????return;
????}
?????
????[self.imageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
????????if (imageDataSampleBuffer == NULL) {
????????????return;
????????}
????????NSData * imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
????????UIImage * image = [UIImage imageWithData:imageData];
????????SAPhotoViewCtrl *photoView = [[SAPhotoViewCtrl alloc] initWithImage:image];
????????[self.navigationController pushViewController:photoView animated:NO];
????????sender.userInteractionEnabled = YES;
????}];
}
?
/// 调整摄像头焦距
- (void)setDevice:(AVCaptureDevice *)device videoZoomFactor:(CGFloat)scale
{
????[device lockForConfiguration:nil];
????device.videoZoomFactor = scale;
????[device unlockForConfiguration];
}
?
/// 闪光灯开关按钮
- (IBAction)overTurn:(UIButton *)sender
{
????sender.tag = (sender.tag + 1) % 3;
????NSString *imageName = [NSString stringWithFormat:@"camera_flashlight_%d.png", (int)sender.tag];
????[sender setBackgroundImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
????[self setDevice:self.deviceInput.device FlashMode:(2 - sender.tag)];
}
?
/// 切换闪光灯模式
- (void)setDevice:(AVCaptureDevice *)device FlashMode:(AVCaptureFlashMode)flashModel
{
????if (device.isFlashAvailable == NO) return;
????if ([device lockForConfiguration:nil]) {
????????device.flashMode = flashModel;
????}
????[device unlockForConfiguration];
}
?
@end

5、Demo下载 SAAVFoundation

Leave a Reply