iOS 实现拍照的焦距调节

2018-09-19  本文已影响0人  苏沫离

我们在自定义相机时,若要实现镜头变焦,也就是推近或者拉远焦距,有两种方法可以实现:可以通过修改AVCaptureDevice的缩放系数videoZoomFactor来实现镜头变焦,也可以通过修改AVCaptureConnection的缩放系数videoScaleAndCropFactor来实现镜头变焦。

1、修改AVCaptureDevice的缩放系数videoZoomFactor

通过修改AVCaptureDevice的缩放系数videoZoomFactor来实现镜头变焦的核心代码如下:

//最小缩放值
- (CGFloat)minZoomFactor
{
    CGFloat minZoomFactor = 1.0;
    if (@available(iOS 11.0, *)) {
        minZoomFactor = self.device.minAvailableVideoZoomFactor;
    }
    return minZoomFactor;
}

//最大缩放值
- (CGFloat)maxZoomFactor
{
    CGFloat maxZoomFactor = self.device.activeFormat.videoMaxZoomFactor;
    if (@available(iOS 11.0, *)) {
        maxZoomFactor = self.device.maxAvailableVideoZoomFactor;
    }
    
    if (maxZoomFactor > 6.0) {
        maxZoomFactor = 6.0;
    }
    return maxZoomFactor;
}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]]){
        self.slider.minimumValue = self.minZoomFactor;
        self.slider.maximumValue = self.maxZoomFactor;
        self.currentZoomFactor = self.device.videoZoomFactor;
    }
    return YES;
}

//缩放手势
- (void)zoomChangePinchGestureRecognizerClick:(UIPinchGestureRecognizer *)pinchGestureRecognizer
{
    if (pinchGestureRecognizer.state == UIGestureRecognizerStateBegan ||
        pinchGestureRecognizer.state == UIGestureRecognizerStateChanged)
    {
        CGFloat currentZoomFactor = self.currentZoomFactor * pinchGestureRecognizer.scale;
        self.slider.hidden = NO;
        
        if (currentZoomFactor < self.maxZoomFactor &&
            currentZoomFactor > self.minZoomFactor){
            
            NSError *error = nil;
            if ([self.device lockForConfiguration:&error] ) {
                self.device.videoZoomFactor = currentZoomFactor;
                self.slider.value = self.device.videoZoomFactor;
                [self.device unlockForConfiguration];
            }
            else {
                NSLog( @"Could not lock device for configuration: %@", error );
            }
        }
    }
    else
    {
        self.slider.hidden = YES;
    }
}

- (void)sliderValueChangeClick:(UISlider *)sender
{
    self.slider.hidden = NO;
    
    if (sender.value < self.maxZoomFactor &&
        sender.value > self.minZoomFactor){
        
        NSError *error = nil;
        if ([self.device lockForConfiguration:&error] ) {
            self.device.videoZoomFactor = sender.value;
            [self.device unlockForConfiguration];
        }
        else {
            NSLog( @"Could not lock device for configuration: %@", error );
        }
    }
}

以上这么多代码,核心代码只有一处,通过该句代码设置了新的缩放比例:

self.device.videoZoomFactor = currentZoomFactor;

2、修改AVCaptureConnection的缩放系数videoScaleAndCropFactor

通过修改AVCaptureConnection的缩放系数videoScaleAndCropFactor来实现镜头变焦的核心代码如下:

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    if ( [gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]] ) {
        beginGestureScale = effectiveScale;
    }
    return YES;
}

// scale image depending on users pinch gesture
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)recognizer
{
    BOOL allTouchesAreOnThePreviewLayer = YES;
    NSUInteger numTouches = [recognizer numberOfTouches], i;
    for ( i = 0; i < numTouches; ++i ) {
        CGPoint location = [recognizer locationOfTouch:i inView:previewView];
        CGPoint convertedLocation = [previewLayer convertPoint:location fromLayer:previewLayer.superlayer];
        if ( ! [previewLayer containsPoint:convertedLocation] ) {
            allTouchesAreOnThePreviewLayer = NO;
            break;
        }
    }
    
    if ( allTouchesAreOnThePreviewLayer ) {
        effectiveScale = beginGestureScale * recognizer.scale;
        if (effectiveScale < 1.0)
            effectiveScale = 1.0;
        CGFloat maxScaleAndCropFactor = [[stillImageOutput connectionWithMediaType:AVMediaTypeVideo] videoMaxScaleAndCropFactor];
        if (effectiveScale > maxScaleAndCropFactor)
            effectiveScale = maxScaleAndCropFactor;
        [CATransaction begin];
        [CATransaction setAnimationDuration:.025];
        [previewLayer setAffineTransform:CGAffineTransformMakeScale(effectiveScale, effectiveScale)];
        [CATransaction commit];
    }
}

以上代码的核心是针对预览图层 AVCaptureVideoPreviewLayer做了放大或者缩小变换,但是这并没有改变拍摄出来的照片是变焦前的图片的本质!我们还要接着往下做工作:

- (void)recordButtonClick:(UIButton *)sender
{
    //imgaeOutput 此时没有捕获图像
    if (self.imgaeOutput.capturingStillImage == NO)
    {
        //获取指定连接
        AVCaptureConnection *stillImageConnection = [self.imgaeOutput connectionWithMediaType:AVMediaTypeVideo];
        
        //设置视频方向
        UIDeviceOrientation curDeviceOrientation = [[UIDevice currentDevice] orientation];
        AVCaptureVideoOrientation avcaptureOrientation = (AVCaptureVideoOrientation)curDeviceOrientation;
        if (curDeviceOrientation == UIDeviceOrientationLandscapeLeft){
            avcaptureOrientation = AVCaptureVideoOrientationLandscapeRight;
        }else if(curDeviceOrientation == UIDeviceOrientationLandscapeRight){
            avcaptureOrientation = AVCaptureVideoOrientationLandscapeLeft;
        }
        [stillImageConnection setVideoOrientation:avcaptureOrientation];
        
        //设置缩放比例
        [stillImageConnection setVideoScaleAndCropFactor:effectiveScale];
        
        [self.imgaeOutput setOutputSettings:@{AVVideoCodecKey:AVVideoCodecJPEG}];
        [self.imgaeOutput captureStillImageAsynchronouslyFromConnection:stillImageConnection completionHandler:^(CMSampleBufferRef  _Nullable imageDataSampleBuffer, NSError * _Nullable error) {
            if (error){
                
            }else{
                NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
                UIImage *image = [UIImage imageWithData:jpegData];
                dispatch_async(dispatch_get_main_queue(), ^{
                    [self.navigationController pushViewController:[[AVImageViewController alloc]initWithImage:image] animated:YES];
                });
                
                //写入相册
                CFDictionaryRef attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, imageDataSampleBuffer, kCMAttachmentMode_ShouldPropagate);
                ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
                [library writeImageDataToSavedPhotosAlbum:jpegData metadata:(__bridge id)attachments completionBlock:^(NSURL *assetURL, NSError *error) {
                    if (error) {
                        
                    }
                }];
                
                if (attachments)
                    CFRelease(attachments);
            }
        }];
    }
}

在拍照时,获取当前缩放比例并设置AVCaptureConnection的缩放比例videoScaleAndCropFactor

//获取指定连接
AVCaptureConnection *stillImageConnection = [self.imgaeOutput connectionWithMediaType:AVMediaTypeVideo];

//设置缩放比例
[stillImageConnection setVideoScaleAndCropFactor:effectiveScale];

点击查看更多AVCaptureDevice信息
点击查看更多AVCaptureConnection信息

上一篇 下一篇

猜你喜欢

热点阅读