AVFoundation的自定义相机

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AVFoundation的自定义相机相关的知识,希望对你有一定的参考价值。

最近由于工作项目的功能需求,需要用到AVFoundation来自定义相机的功能。所以花了点时间去研究了一下。

 

在网上可以找到很多实用AVFoundation的自定义相机自定义相机的博文,虽然功能的确是完成了,但是距离我的目标还是有不少的差距的。

首先声明,预览的照片是正方形的,拍摄结果最后得到的图片也是正方形的,再好多的博文上我都没有找到该怎么设置才可以得到正方形的图片。

我是首先拍一张照片,然后在使用图片裁剪的方式得到的正方行图片。好了,接下来就让我们开始吧。

 

首先要了解这几个类的用处

/**
 *  负责输入和输出设备之间的数据传递
 */
@property (nonatomic,strong) AVCaptureSession *captureSession;
/**
 *  负责从AVCaptureDevice获得输入数据
 */
@property (nonatomic,strong) AVCaptureDeviceInput * captureDeviceInput;
/**
 *  照片输出流
 */
@property (nonatomic,strong) AVCaptureStillImageOutput * captureStillImageOutput;
/**
 *  相机拍摄预览图层
 */
@property (nonatomic,strong) AVCaptureVideoPreviewLayer * captureVideoPreviewLayer;

了解了这几个类的用处,我们就可以开始了

1、初始化相机

    //初始化会话
    _captureSession=[[AVCaptureSession alloc]init];
    _captureSession.sessionPreset = AVCaptureSessionPresetPhoto;
    
    //获得输入设备
    AVCaptureDevice *captureDevice= [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    if (!captureDevice) return;
    
    NSError *error=nil;
    //根据输入设备初始化设备输入对象,用于获得输入数据
    _captureDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:captureDevice error:&error];
    if (error)return;
    
    //初始化设备输出对象,用于获得输出数据
    _captureStillImageOutput=[[AVCaptureStillImageOutput alloc]init];
    NSDictionary *outputSettings = @{AVVideoCodecKey:AVVideoCodecJPEG};
    [_captureStillImageOutput setOutputSettings:outputSettings];//输出设置
    
    //将设备输入添加到会话中
    if ([_captureSession canAddInput:_captureDeviceInput]) {
        [_captureSession addInput:_captureDeviceInput];
    }
    
    //将设备输出添加到会话中
    if ([_captureSession canAddOutput:_captureStillImageOutput]) {
        [_captureSession addOutput:_captureStillImageOutput];
    }
    
    //创建视频预览层,用于实时展示摄像头状态
    _captureVideoPreviewLayer=[[AVCaptureVideoPreviewLayer alloc]initWithSession:self.captureSession];
    
    CALayer *layer=self.viewContainer.layer;
    layer.masksToBounds=YES;
    
    _captureVideoPreviewLayer.frame= CGRectMake(0, 0, SCREEN_MAX_WEITH, SCREEN_MAX_WEITH);
    
    _captureVideoPreviewLayer.videoGravity=AVLayerVideoGravityResizeAspectFill;//填充模式
    //将视频预览层添加到界面中
    [layer addSublayer:_captureVideoPreviewLayer];

实现了以上的代码,就可以让实时预览摄像头捕捉道德图像了。 对了  还需要调用

startRunning方法才行,不让是不会有效果的。

 

有了预览图层,自然就是拍照了

- (void)captureStillImageAsynchronouslyFromConnection:(AVCaptureConnection *)connection completionHandler:(void (^)(CMSampleBufferRef imageDataSampleBuffer, NSError *error))handler;

这个方法就是拍照获取图像的了。这个虽然可以获取到图像,但是获取的图形并不是正方形的,图像的分辨率是由

sessionPreset这个属性决定的。但是并没有可以设置正方形图片的选项。所以还是要自己想办法。

 

故此,我给UIImage实现了一个分类,给它添加了一个截成正方形的方法

- (UIImage*)cropImageWithRect:(CGRect)cropRect
{
    CGRect drawRect = CGRectMake(-cropRect.origin.x , -cropRect.origin.y, self.size.width * self.scale, self.size.height * self.scale);
    UIGraphicsBeginImageContext(cropRect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextClearRect(context, CGRectMake(0, 0, cropRect.size.width, cropRect.size.height));
    [self drawInRect:drawRect];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

调用这个方法后,可以得到一张正方的图片。到现在。我需要的功能就完成一半了。然后就是一些其他的设置了,如切换摄像头,开启闪光灯等。

切换摄像头

    AVCaptureDevice *currentDevice=[self.captureDeviceInput device];
    AVCaptureDevicePosition currentPosition=[currentDevice position];
    // [self removeNotificationFromCaptureDevice:currentDevice];
    AVCaptureDevice *toChangeDevice;
    AVCaptureDevicePosition toChangePosition=AVCaptureDevicePositionFront;
    if (currentPosition==AVCaptureDevicePositionUnspecified||currentPosition==AVCaptureDevicePositionFront) {
        toChangePosition=AVCaptureDevicePositionBack;
    }
    toChangeDevice=[self getCameraDeviceWithPosition:toChangePosition];
    // [self addNotificationToCaptureDevice:toChangeDevice];
    //获得要调整的设备输入对象
    AVCaptureDeviceInput *toChangeDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:toChangeDevice error:nil];
    
    //改变会话的配置前一定要先开启配置,配置完成后提交配置改变
    [self.captureSession beginConfiguration];
    //移除原有输入对象
    [self.captureSession removeInput:self.captureDeviceInput];
    //添加新的输入对象
    if ([self.captureSession canAddInput:toChangeDeviceInput]) {
        [self.captureSession addInput:toChangeDeviceInput];
        self.captureDeviceInput=toChangeDeviceInput;
    }
    //提交会话配置
    [self.captureSession commitConfiguration];

 

设置闪光灯,闪光灯设置有3种属性,

typedef NS_ENUM(NSInteger, AVCaptureFlashMode) {

AVCaptureFlashModeOff  = 0,

AVCaptureFlashModeOn   = 1,

AVCaptureFlashModeAuto = 2

} NS_AVAILABLE(10_7, 4_0) __TVOS_PROHIBITED;

 看意思就可以明白是那些了,关闭,开启,自动

                [captureDevice setFlashMode:AVCaptureFlashModeOn];
                [captureDevice setFlashMode:AVCaptureFlashModeOff];

 

如此,功能的需求也就完成了一半以上了。

接下来给图片添加上了滤镜,我实现了一个工具类,给图片添加滤镜

        _name = [name substringFromIndex:name.length - 6];
        CIImage *ciImage = [[CIImage alloc] initWithImage:image];
        CIFilter *filter = [CIFilter filterWithName:name
                                      keysAndValues:kCIInputImageKey, ciImage, nil];
        [filter setDefaults];
        CIContext *context = [CIContext contextWithOptions:nil];
        CIImage *outputImage = [filter outputImage];
        CGImageRef cgImage = [context createCGImage:outputImage
                                           fromRect:[outputImage extent]];
        _result = [[UIImage imageWithCGImage:cgImage] copy];
        CGImageRelease(cgImage);

 

这是效果图

技术分享

 

这样我的需求功能就完成了,以上只是一些功能主题的事项的代码还有一些细节方面的代码并没有放上来。

另外,我从项目中把主题的功能代码复制出来写了一个demo如果有兴趣可以去看看。

https://github.com/LuShui/LSCusCarema

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

以上是关于AVFoundation的自定义相机的主要内容,如果未能解决你的问题,请参考以下文章

质量:自定义 AVFoundation 相机应用程序 VS。 iOS 标准相机应用

swift使用AVFoundation实现自定义相机

swift使用AVFoundation实现自定义相机

用AVFoundation自定义相机拍照

swift avfoundation AVCapturePhotoCaptureDelegate 捕获方法

是否可以在 Objective c 中使用 AVFoundation 框架绘制网格线