iOS自定义相机

本文对之前做过的相机模块做个小结,包括自定义相机拍照界面,照片处理及保存等,感兴趣的朋友可以做个参考

如果你要执行以下操作,你应该使用该系统API来构建自定义相机:

构建自定义的相机用户界面,将拍照或视频录制集成到应用中
为用户提供对照片和视频捕获更直接的控制,例如焦点,曝光等增强选项。
与系统相机 UI 产生不同的结果,例如 RAW 格式的照片,深度图或需要自定义视频元数据
从采集设备 (Capture device) 实时获取视频像素或音频数据。

准备工作

1.添加plist配置相机权限
2.判断有无权限

AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];

如果未申请过权限,则进行权限获取

[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
            dispatch_sync(dispatch_get_main_queue(), ^{
                if (granted) {
                   // 申请权限成功
                } else {
                    // 申请权限失败
                }
            });
        }];
  1. 如果默认横屏,需要根据屏幕方向进行旋转

自定义相机配置信息

需要创建以下属性

//捕获设备,通常是前置摄像头,后置摄像头,麦克风(音频输入)
@property(nonatomic) AVCaptureDevice *device;
//session会话 由它把输入输出结合在一起,并开始启动捕获设备(摄像头)
@property(nonatomic) AVCaptureSession *session;
//AVCaptureDeviceInput 代表输入设备,使用AVCaptureDevice 来初始化
@property(nonatomic) AVCaptureDeviceInput *input;
//照片输出流
@property (nonatomic) AVCapturePhotoOutput *imageOutPut;
//图像预览层,实时显示捕获的图像
@property(nonatomic) AVCaptureVideoPreviewLayer *previewLayer;
  1. 初始化session会话,用来结合输入输出
self.session = [[AVCaptureSession alloc] init];
    if ([self.session canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
        [self.session setSessionPreset:AVCaptureSessionPreset1280x720];  //拿到的图像的大小可以自行设定
    }
  1. 获取视频输入设备(摄像头)
self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
  1. 创建视频输入源 并添加到会话
self.input = [[AVCaptureDeviceInput alloc]initWithDevice:self.device error:nil];
    if ([self.session canAddInput:self.input]) {
        [self.session addInput:self.input];
    }
  1. 创建视频输出源 并添加到会话
self.imageOutPut = [[AVCapturePhotoOutput alloc]init];
    if ([self.session canAddOutput:self.imageOutPut]) {
        [self.session addOutput:self.imageOutPut];
    }
    AVCaptureConnection *imageConnection = [self.imageOutPut connectionWithMediaType:AVMediaTypeVideo];
    // 设置 imageConnection 控制相机拍摄图片的角度方向
    if (imageConnection.supportsVideoOrientation) {
        imageConnection.videoOrientation = AVCaptureVideoOrientationLandscapeRight;
    }
  1. 初始化预览层,session会话负责驱动input输入源进行信息的采集,layer预览层负责把采集到的图像进行渲染显示
self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
    self.previewLayer.frame = CGRectMake(0, 0, width,height);
    self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight; // 图层展示拍摄角度方向
    self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    [self.view.layer addSublayer:self.previewLayer];
  1. 开始采集界面
[self.session startRunning];

拍照属性设置 (可选项)

需要先加锁在进行设置

if ([self.device lockForConfiguration:nil]) { // 修改设备的属性,先加锁
       
        ……设置内容……
        //解锁
        [self.device unlockForConfiguration];
    }
  1. 聚焦
// 添加聚焦手势
- (void)addTap {
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(focusGesture:)];
    [self.view addGestureRecognizer:tap];
}
- (void)focusGesture:(UITapGestureRecognizer*)gesture{
    CGPoint point = [gesture locationInView:gesture.view];
   CGSize size = self.view.bounds.size;
    // focusPoint 函数后面Point取值范围是取景框左上角(0,0)到取景框右下角(1,1)之间,有时按这个来但位置不对,按实际适配
    CGPoint focusPoint = CGPointMake( point.x /size.width , point.y/size.height );
    if ([self.device lockForConfiguration:nil]) {
        [self.session beginConfiguration];
        /*****必须先设定聚焦位置,在设定聚焦方式******/
        //聚焦点的位置
        if ([self.device isFocusPointOfInterestSupported]) {
            [self.device setFocusPointOfInterest:focusPoint];
        }
        // 聚焦模式
        if ([self.device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
            [self.device setFocusMode:AVCaptureFocusModeAutoFocus];
        }else{
            NSLog(@"聚焦模式修改失败");
        }
        //曝光点的位置
        if ([self.device isExposurePointOfInterestSupported]) {
            [self.device setExposurePointOfInterest:focusPoint];
        }
        //曝光模式
        if ([self.device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
            [self.device setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
        } else {
            NSLog(@"曝光模式修改失败");
        }
        [self.device unlockForConfiguration];
        [self.session commitConfiguration];
    }
}
  1. 白平衡
 //自动白平衡
        if ([self.device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeAutoWhiteBalance]) {
            [self.device setWhiteBalanceMode:AVCaptureWhiteBalanceModeAutoWhiteBalance];
        }
  1. 切换摄像头
//获取摄像头的数量
    NSUInteger cameraCount = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] count];
    //摄像头小于等于1的时候直接返回
    if (cameraCount <= 1) return;
    AVCaptureDevice *newCamera = nil;
    AVCaptureDeviceInput *newInput = nil;
    //获取当前相机的方向(前还是后)
    AVCaptureDevicePosition position = [[self.input device] position];
    if (position == AVCaptureDevicePositionFront) {
        //获取后置摄像头
        newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack];
    }else{
        //获取前置摄像头
        newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront];
    }
    [self.previewLayer addAnimation:animation forKey:nil];
    //输入流
    newInput = [AVCaptureDeviceInput deviceInputWithDevice:newCamera error:nil];
    if (newInput != nil) {
        [self.session beginConfiguration];
        //先移除原来的input
        [self.session removeInput:self.input];
        if ([self.session canAddInput:newInput]) {
            [self.session addInput:newInput];
            self.input = newInput;
        } else {
            //如果不能加现在的input,就加原来的input
            [self.session addInput:self.input];
        }
        [self.session commitConfiguration];
    }
  1. 闪光灯配置
if ([[self.imageOutPut supportedFlashModes] containsObject:@(AVCaptureFlashModeOn)]) {
         self.imageOutPut.photoSettingsForSceneMonitoring.flashMode = AVCaptureFlashModeOn;             
 }
  1. 手电筒配置
//获取当前相机的方向(前还是后) 前置摄像头不允许开启手电筒
    AVCaptureDevicePosition position = [[self.input device] position];
    if (position == AVCaptureDevicePositionFront) {
        NSLog(@"前置摄像时无法开启手电筒");
        return;
    }
    AVCaptureDevice *device = self.device;
    if ([device hasTorch]) { // 判断是否有闪光灯
        // 请求独占访问硬件设备
        [device lockForConfiguration:nil];
        if (sender.selected == NO) {
            sender.selected = YES;
            [device setTorchMode:AVCaptureTorchModeOn]; // 手电筒开
        } else {
            sender.selected = NO;
            [device setTorchMode:AVCaptureTorchModeOff]; // 手电筒关
        }
        // 请求解除独占访问硬件设备
        [device unlockForConfiguration];
    }

拍照获取图片

  1. 拍照操作
AVCaptureConnection * videoConnection = [self.imageOutPut connectionWithMediaType:AVMediaTypeVideo];
    if (videoConnection ==  nil) {
        return;
    }
    AVCapturePhotoSettings *set = [AVCapturePhotoSettings photoSettings];
    [self.imageOutPut capturePhotoWithSettings:set delegate:self];
  1. 通过代理方法captureOutput:didFinishProcessingPhoto:error:获取到图片
-(void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(NSError *)error {
    if (!error) {
        NSData *imageData = [photo fileDataRepresentation];
        UIImage *image = [UIImage imageWithData:imageData];
        //处理图片
        [self handleOriginalImage:image];
    }
}

获取到图片需要进行处理,前置摄像头需处理左右成像问题
例:

CGAffineTransform transform = CGAffineTransformIdentity;
            transform = CGAffineTransformTranslate(transform, aImage.size.height, aImage.size.width);
            transform = CGAffineTransformRotate(transform, M_PI);
            transform = CGAffineTransformTranslate(transform, aImage.size.height,0);
            transform = CGAffineTransformScale(transform, -1, 1);
            CGContextRef ctx =CGBitmapContextCreate(NULL, aImage.size.height, aImage.size.width,CGImageGetBitsPerComponent(aImage.CGImage),0,CGImageGetColorSpace(aImage.CGImage),CGImageGetBitmapInfo(aImage.CGImage));
            CGContextConcatCTM(ctx, transform);
            CGContextDrawImage(ctx,CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
            CGImageRef cgimg =CGBitmapContextCreateImage(ctx);
            img = [UIImage imageWithCGImage:cgimg];
            CGContextRelease(ctx);
            CGImageRelease(cgimg);
  1. 保存到相册
//存储
    [[PHPhotoLibrary sharedPhotoLibrary]performChanges:^{
        [PHAssetChangeRequest creationRequestForAssetFromImage:fixImage];
    } completionHandler:^(BOOL success, NSError * _Nullable error) {
        if (error) {
            NSLog(@"%@",@"保存失败");
        } else {
            NSLog(@"%@",@"保存成功");
        }
    }];

其他

可以给图片加水印,定位信息,拍摄时间信息等 给图像添加信息需要使用文件存储,UIImage无法存储资料

github项目地址: https://github.com/percival888/CustomCameraDemo

参考:
https://cloud.tencent.com/developer/article/1532831
https://blog.csdn.net/u010029439/article/details/113201454

你可能感兴趣的:(iOS自定义相机)