iOS 實現拍照的焦距調節

我們在自定義相機時,若要實現鏡頭變焦,也就是推近或者拉遠焦距,有兩種方法可以實現:可以通過修改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信息

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容