【问题标题】:AVCaptureDevice Camera ZoomAVCaptureDevice 相机变焦
【发布时间】:2012-04-19 02:42:54
【问题描述】:

我运行了一个简单的 AVCaptureSession,以在我的应用中获取相机源并拍照。如何使用相机的UIGestureRecognizer 实现“捏缩放”功能?

【问题讨论】:

  • 你在哪里添加捏合手势?

标签: iphone objective-c ios avcapturesession avcapture


【解决方案1】:

接受的答案实际上已经过时,我不确定它是否真的会拍摄放大图像的照片。有一种方法可以像 bcattle 回答说的那样放大。他的答案的问题在于,它不负责用户可以放大然后从该缩放位置重新启动的事实。他的解决方案会产生一些不太优雅的跳跃。

最简单、最优雅的方法是使用捏合手势的速度。

-(void) handlePinchToZoomRecognizer:(UIPinchGestureRecognizer*)pinchRecognizer {
    const CGFloat pinchVelocityDividerFactor = 5.0f;

    if (pinchRecognizer.state == UIGestureRecognizerStateChanged) {
        NSError *error = nil;
        if ([videoDevice lockForConfiguration:&error]) {
            CGFloat desiredZoomFactor = device.videoZoomFactor + atan2f(pinchRecognizer.velocity, pinchVelocityDividerFactor);
            // Check if desiredZoomFactor fits required range from 1.0 to activeFormat.videoMaxZoomFactor
            device.videoZoomFactor = MAX(1.0, MIN(desiredZoomFactor, device.activeFormat.videoMaxZoomFactor));
            [videoDevice unlockForConfiguration];
        } else {
            NSLog(@"error: %@", error);
        }
    }
}

我发现将 arctan 函数添加到速度会稍微缓解放大缩小效果。它并不完美,但效果足以满足需求。当它几乎达到 1 时,可能还有另一个功能可以缓解缩小。

注意:此外,捏合手势的范围从 0 到无限,其中 0 到 1 表示捏合(缩小),1 到无限表示捏合(放大)。要获得良好的放大缩小效果,您需要有一个数学方程式。速度实际上是从-infinite到infinite,以0为起点。

编辑:修复了范围异常时的崩溃问题。感谢@garafajon

【讨论】:

  • 谢谢。使用它不会获得范围异常: CGFloat desiredZoom = videoDevice.videoZoomFactor + atan(pinchRecognizer.velocity / pinchZoomScaleFactor); videoDevice.videoZoomFactor = MAX(1.0, MIN(desiredZoom,videoDevice.activeFormat.videoMaxZoomFactor));
  • 注意速度可以返回nan。在继续计算之前,您可能需要检查一下: if (isnan(pinchRecognizer.velocity)) { return; }
  • videoZoomFactor和zoom一样吗?
  • 我应该在哪里添加捏合手势? @garafajon
  • 请查看我的答案,以获得更简单的方法来使用捏识别器处理相机缩放级别stackoverflow.com/a/58704702/1705508
【解决方案2】:

许多人尝试通过将图层上的变换属性设置为CGAffineTransformMakeScale(gesture.scale.x, gesture.scale.y); 来做到这一点 请参阅 here 了解捏合缩放的完整实现。

【讨论】:

  • 感谢您的回复。但是仅仅改变视频预览视图的比例实际上如何改变相机硬件的缩放?
  • 它没有。这就是为什么即使是苹果的相机也不能真正“变焦”。只是一些 CGAffines 和一些花哨的裁剪。
  • 对,相机只是使用“数码变焦”。但是,除了缩放视频预览视图以实际使写入磁盘的图像“缩放”之外,我还需要做什么?
  • 请参阅here。只知道没有 iOS 设备具有硬件缩放功能,因此这需要比我链接到的答案更多的数学知识。
  • 非常感谢。我注意到链接问题的答案说,当缩放因内存而最大化时,应用程序崩溃了。这是我在 ARC 下需要担心的事情吗?
【解决方案3】:

从 iOS 7 开始,您可以直接使用 AVCaptureDevicevideoZoomFactor 属性设置缩放。

UIPinchGestureRecognizerscale 属性与videoZoomFactor 绑定一个缩放常数。这将让您改变对味觉的敏感度:

-(void) handlePinchToZoomRecognizer:(UIPinchGestureRecognizer*)pinchRecognizer {
    const CGFloat pinchZoomScaleFactor = 2.0;

    if (pinchRecognizer.state == UIGestureRecognizerStateChanged) {
        NSError *error = nil;
        if ([videoDevice lockForConfiguration:&error]) {
            videoDevice.videoZoomFactor = 1.0 + pinchRecognizer.scale * pinchZoomScaleFactor;
            [videoDevice unlockForConfiguration];
        } else {
            NSLog(@"error: %@", error);
        }
    }
}

请注意,AVCaptureDevice 以及与AVCaptureSession 相关的所有其他内容都不是线程安全的。因此,您可能不想从主队列中执行此操作。

【讨论】:

    【解决方案4】:

    斯威夫特 4
    将捏合手势识别器添加到最前面的视图并将其连接到此操作 (pinchToZoom)。 captureDevice 应该是当前为捕获会话提供输入的实例。 pinchToZoom正面和背面 捕获设备提供平滑缩放。

      @IBAction func pinchToZoom(_ pinch: UIPinchGestureRecognizer) {
    
        guard let device = captureDevice else { return }
    
        func minMaxZoom(_ factor: CGFloat) -> CGFloat { return min(max(factor, 1.0), device.activeFormat.videoMaxZoomFactor) }
    
        func update(scale factor: CGFloat) {
          do {
            try device.lockForConfiguration()
            defer { device.unlockForConfiguration() }
            device.videoZoomFactor = factor
          } catch {
            debugPrint(error)
          } 
        }
    
        let newScaleFactor = minMaxZoom(pinch.scale * zoomFactor)
    
        switch sender.state {
          case .began: fallthrough
          case .changed: update(scale: newScaleFactor)
          case .ended:
            zoomFactor = minMaxZoom(newScaleFactor)
            update(scale: zoomFactor)
         default: break
       }
     }
    

    在您的相机或 vc 上声明 zoomFactor 会很有用。我通常把它放在有 AVCaptureSession 的同一个单例上。这将作为 captureDevice 的 videoZoomFactor 的默认值。

    var zoomFactor: Float = 1.0
    

    【讨论】:

    • 嗨,你在这里定义的变量我不清楚。作为初学者。你能告诉我代码中的“pinch”变量是什么吗?谢谢!
    • 再看这个,我觉得pinch应该是sender。或者发件人参数可以捏。
    • 我不得不,var zoomFactor: CGFloat = 1.0
    【解决方案5】:

    在 swift 版本中,您可以通过简单地在 videoZoomFactor 上传递缩放数字来放大/缩小。 UIPinchGestureRecognizer 处理程序中的以下代码将解决该问题。

    do {
        try device.lockForConfiguration()
        switch gesture.state {
        case .began:
            self.pivotPinchScale = device.videoZoomFactor
        case .changed:
            var factor = self.pivotPinchScale * gesture.scale
            factor = max(1, min(factor, device.activeFormat.videoMaxZoomFactor))
            device.videoZoomFactor = factor
        default:
            break
        }
        device.unlockForConfiguration()
    } catch {
        // handle exception
    }
    

    在这里,pivotPinchScale 是一个 CGFloat 属性,它在你的控制器中的某处声明。

    您也可以参考以下项目来了解相机如何与 UIPinchGestureRecognizer 配合使用。 https://github.com/DragonCherry/CameraPreviewController

    【讨论】:

      【解决方案6】:

      我从@Gabriel Cartier 的解决方案开始(谢谢)。在我的代码中,我更喜欢使用更平滑的 rampToVideoZoomFactor 和更简单的方法来计算设备的比例因子。

      (IBAction) pinchForZoom:(id) sender forEvent:(UIEvent*) event {
          UIPinchGestureRecognizer* pinchRecognizer = (UIPinchGestureRecognizer *)sender;
      
          static CGFloat zoomFactorBegin = .0;
          if ( UIGestureRecognizerStateBegan == pinchRecognizer.state ) {
              zoomFactorBegin = self.captureDevice.videoZoomFactor;
      
          } else if (UIGestureRecognizerStateChanged == pinchRecognizer.state) {
              NSError *error = nil;
              if ([self.captureDevice lockForConfiguration:&error]) {
      
                  CGFloat desiredZoomFactor = zoomFactorBegin * pinchRecognizer.scale;
                  CGFloat zoomFactor = MAX(1.0, MIN(desiredZoomFactor, self.captureDevice.activeFormat.videoMaxZoomFactor));
                  [self.captureDevice rampToVideoZoomFactor:zoomFactor withRate:3.0];
      
                  [self.captureDevice unlockForConfiguration];
              } else {
                  NSLog(@"error: %@", error);
              }
          }
      }
      

      【讨论】:

        【解决方案7】:

        有一种更简单的方法可以使用捏合识别器来处理相机缩放级别。您唯一需要做的就是将cameraDevice.videoZoomFactor 设置为.began 状态下的识别器,如下所示

        @objc private func viewPinched(recognizer: UIPinchGestureRecognizer) {
            switch recognizer.state {
                case .began:
                    recognizer.scale = cameraDevice.videoZoomFactor
                case .changed:
                    let scale = recognizer.scale
                    do {
                         try cameraDevice.lockForConfiguration()
                         cameraDevice.videoZoomFactor = max(cameraDevice.minAvailableVideoZoomFactor, min(scale, cameraDevice.maxAvailableVideoZoomFactor))
                         cameraDevice.unlockForConfiguration()
                    }
                    catch {
                        print(error)
                    }
                default:
                    break
            }
        }
        

        【讨论】:

          【解决方案8】:

          基于@Gabriel Cartier 的回答:

          - (void) cameraZoomWithPinchVelocity: (CGFloat)velocity {
              CGFloat pinchVelocityDividerFactor = 40.0f;
              if (velocity < 0) {
                  pinchVelocityDividerFactor = 5.; //zoom in
              }
          
              if (_videoInput) {
                  if([[_videoInput device] position] == AVCaptureDevicePositionBack) {
                      NSError *error = nil;
                      if ([[_videoInput device] lockForConfiguration:&error]) {
                          CGFloat desiredZoomFactor = [_videoInput device].videoZoomFactor + atan2f(velocity, pinchVelocityDividerFactor);
                          // Check if desiredZoomFactor fits required range from 1.0 to activeFormat.videoMaxZoomFactor
                          CGFloat maxFactor = MIN(10, [_videoInput device].activeFormat.videoMaxZoomFactor);
                          [_videoInput device].videoZoomFactor = MAX(1.0, MIN(desiredZoomFactor, maxFactor));
                          [[_videoInput device] unlockForConfiguration];
                      } else {
                          NSLog(@"cameraZoomWithPinchVelocity error: %@", error);
                      }
                  }
              }
          }
          

          【讨论】:

            【解决方案9】:

            我正在使用 iOS SDK 8.3 和 AVfoundation 框架,对我来说,使用以下方法适用于:

            nameOfAVCaptureVideoPreviewLayer.affineTransform = CGAffineTransformMakeScale(scaleX, scaleY) 
            

            为了保存相同比例的图片,我使用了以下方法:

            nameOfAVCaptureConnection.videoScaleAndCropFactor = factorNumber; 
            

            下面的代码用于获取比例中的图像

            [stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
                    if(imageDataSampleBuffer != NULL){
            
                        NSData *imageData = [AVCaptureStillImageOutput  jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
                        UIImage *image = [UIImage imageWithData:imageData];
            }
            }];
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2018-04-24
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-03-04
              相关资源
              最近更新 更多