年年有"余"

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 12768|回复: 4

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

[复制链接]
  • TA的每日心情
    奋斗
    2022-12-13 21:26
  • 签到天数: 371 天

    [LV.9]以坛为家II

    发表于 2015-5-22 14:16:41 | 显示全部楼层 |阅读模式
    本帖最后由 Sian 于 2015-5-22 14:19 编辑

    1、如何使用AVFoundation框架自定义相机请先参照 http://www.yusian.com/bbs/thread-10503-1-1.html

    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。一般形式如:
    [Objective-C] 纯文本查看 复制代码
    /// 对焦事件
    - (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的部分输出同样大小的照片,最终的效果就是放大了图像。(如果这部分看不懂,说明没有理解AVFoundation的工作原理,参照:http://www.yusian.com/bbs/thread-10580-1-1.html

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

    3.4、设置videoZoomFactor属性与对焦一样同样需要三部曲来实现,锁定Device、设置属性、解锁Device
    [Objective-C] 纯文本查看 复制代码
    /// 调整摄像头焦距
    - (void)setDevice:(AVCaptureDevice *)device videoZoomFactor:(CGFloat)scale
    {
        [device lockForConfiguration:nil];
        device.videoZoomFactor = scale;
        [device unlockForConfiguration];
    }
    4、关键代码示例:
    [Objective-C] 纯文本查看 复制代码
    //
    //  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下载
    游客,如果您要查看本帖隐藏内容请回复


  • TA的每日心情
    犯困
    2015-1-11 14:36
  • 签到天数: 5 天

    [LV.2]偶尔看看I

    发表于 2015-6-26 16:40:27 | 显示全部楼层
    看一下效果
  • TA的每日心情
    恶心
    2015-11-23 14:20
  • 签到天数: 92 天

    [LV.6]常住居民II

    发表于 2015-7-10 10:42:47 | 显示全部楼层
    学习学习{:3_52:}
    回复

    使用道具 举报

    该用户从未签到

    发表于 2015-10-12 18:31:08 | 显示全部楼层
    haoahoahao

    该用户从未签到

    发表于 2016-5-12 14:23:36 | 显示全部楼层
    看看效果,学习学习,
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    手机版|小黑屋|Archiver|iOS开发笔记 ( 湘ICP备14010846号 )

    GMT+8, 2024-3-28 17:01 , Processed in 0.055588 second(s), 25 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表