美文网首页
iOS 实现拍照的焦距调节

iOS 实现拍照的焦距调节

作者: 苏沫离 | 来源:发表于2018-09-19 14:58 被阅读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信息

    相关文章

      网友评论

          本文标题:iOS 实现拍照的焦距调节

          本文链接:https://www.haomeiwen.com/subject/ocjinftx.html