【问题标题】:iOS tap to focusiOS 点击对焦
【发布时间】:2015-01-22 10:11:35
【问题描述】:

我使用此代码在 iOS 自定义相机应用程序中实现 Tap-to-Focus,但它不起作用。这是代码

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    let touchPer = touches.anyObject() as UITouch
    let screenSize = UIScreen.mainScreen().bounds.size
    var focus_x = touchPer.locationInView(self.view).x / screenSize.width
    var focus_y = touchPer.locationInView(self.view).y / screenSize.height

    if let device = captureDevice {
        if(device.lockForConfiguration(nil)) {
            device.focusMode = AVCaptureFocusMode.ContinuousAutoFocus

            device.focusPointOfInterest = CGPointMake(focus_x, focus_y)
            device.exposureMode = AVCaptureExposureMode.ContinuousAutoExposure
            device.unlockForConfiguration()
        }
    }
}

【问题讨论】:

  • 它不起作用怎么办?会产生什么错误?预期的行为是什么?实际行为是什么?
  • 我触摸一点。并且重点应该在这一点上。但没有效果。它没有集中在那个点上。相反,它始终处于无限远焦点。那就是问题所在。 @大卫
  • 你有没有通过它来确保两个 if 都被采纳?
  • 是的。我在每个 if 后面放了一个 println()。他们正在经历 ifs。
  • 为什么我的问题被否决了?

标签: ios swift avcapturesession


【解决方案1】:

videoView: UIView 显示视频,cameraDevice: AVCaptureDevice,以下似乎对我有用:

override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
    var touchPoint = touches.first as! UITouch
    var screenSize = videoView.bounds.size
    var focusPoint = CGPoint(x: touchPoint.locationInView(videoView).y / screenSize.height, y: 1.0 - touchPoint.locationInView(videoView.x / screenSize.width)

    if let device = cameraDevice {
        if(device.lockForConfiguration(nil)) {
            if device.focusPointOfInterestSupported {
                device.focusPointOfInterest = focusPoint
                device.focusMode = AVCaptureFocusMode.AutoFocus
            }
            if device.exposurePointOfInterestSupported {
                device.exposurePointOfInterest = focusPoint
                device.exposureMode = AVCaptureExposureMode.AutoExpose
            }
            device.unlockForConfiguration()
        }
    }
} 

请注意,我必须交换 xy 坐标,并将 x 坐标从 1 重新映射到 0,而不是从 0 到 1 — 不知道为什么会这样,但似乎有必要让它正常工作(尽管测试它也有点棘手)

编辑:Apple's documentation 解释了坐标变换的原因。

此外,设备可能支持兴趣焦点。您使用 focusPointOfInterestSupported 测试支持。如果支持,您可以使用 focusPointOfInterest 设置焦点。您传递一个 CGPoint,其中 {0,0} 代表图片区域的左上角,{1,1} 代表横向模式下的右下角,主页按钮在右侧——即使设备处于纵向模式也适用.

在我的示例中,我一直使用.ContinuousAutoFocus.ContinuousAutoExposure,但文档表明.AutoFocus 是正确的选择。奇怪的是文档没有提到.AutoExpose,但我在我的代码中使用它并且它工作正常。

我还修改了我的示例代码以包含 .focusPointOfInterestSupported.exposurePointOfInterestSupported 测试 — 文档还提到使用给定焦点/曝光模式的 isFocusModeSupported:isExposureModeSupported: 方法来测试它是否适用于给定设备,但我假设如果设备支持兴趣点模式,那么它也支持自动模式。在我的应用中似乎一切正常。

【讨论】:

  • device.focusMode = AVCptureFocusMode.ContinuousAutoExposure 是错误的。应该是“ContinuousAutoFocus”
  • 更简单的是,如果您使用的是 AVCaptureVideoPreviewLayer 类,则可以使用 captureDevicePointOfInterestForPoint
  • 请参阅我的答案以获得该答案的 Swift 3 版本。
【解决方案2】:

Swift 3.0 解决方案

使用 Swift 3 将 Cody 的答案转换为可行的解决方案。

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    let touchPoint = touches.first! as UITouch
    let screenSize = cameraView.bounds.size
    let focusPoint = CGPoint(x: touchPoint.location(in: cameraView).y / screenSize.height, y: 1.0 - touchPoint.location(in: cameraView).x / screenSize.width)

    if let device = captureDevice {
        do {
            try device.lockForConfiguration()
            if device.isFocusPointOfInterestSupported {
                device.focusPointOfInterest = focusPoint
                device.focusMode = AVCaptureFocusMode.autoFocus
            }
            if device.isExposurePointOfInterestSupported {
                device.exposurePointOfInterest = focusPoint
                device.exposureMode = AVCaptureExposureMode.autoExpose
            }
            device.unlockForConfiguration()

        } catch {
            // Handle errors here
        }
    }
}

【讨论】:

    【解决方案3】:
     device.focusPointOfInterest = focusPoint
     device.focusMode = AVCaptureFocusMode.AutoFocus
     device.exposurePointOfInterest = focusPoint
     device.exposureMode = AVCaptureExposureMode.ContinuousAutoExposure
    

    我不知道为什么会这样,但确实如此。

    【讨论】:

      【解决方案4】:

      你应该在focusPointOfInterest 上阅读Apple docs,它说了三件重要的事情:

      1. 为此属性设置值不会启动聚焦操作。要将相机聚焦在兴趣点上,首先设置此属性的值,然后将 focusMode 属性设置为 autoFocus 或 ContinuousAutoFocus。

      2. 此属性的 CGPoint 值使用坐标系,其中 {0,0} 是图片区域的左上角,{1,1} 是右下角。 无论实际设备方向如何,此坐标系始终相对于具有右侧主页按钮的横向设备方向。您可以使用 AVCaptureVideoPreviewLayer 方法在此坐标系和视图坐标之间进行转换。

      3. 在更改此属性的值之前,您必须调用 lockForConfiguration() 以获取对设备配置属性的独占访问权限。否则,设置此属性的值会引发异常。完成设备配置后,调用 unlockForConfiguration() 以释放锁定并允许其他设备配置设置。

      这是一个实现所有这些的实现:

      // In your camera preview view    
      @objc private func cameraViewTapped(with gestureRecognizer: UITapGestureRecognizer) {
          let location = gestureRecognizer.location(in: self)
          addFocusIndicatorView(at: location) // If you want to indicate it in the UI
      
          // This is the point you want to pass to your capture device
          let captureDeviceLocation = previewLayer.captureDevicePointConverted(fromLayerPoint: location)
      
          // Somehow pass the point to where your AVCaptureDevice is
          viewDelegate?.cameraPreviewView(self, didTapToFocusAt: captureDeviceLocation) 
      }
      
      
      // In your camera controller
      func focus(at point: CGPoint) {
          guard let device = videoDevice else {
              return
          }
      
          guard device.isFocusPointOfInterestSupported, device.isExposurePointOfInterestSupported else {
              return
          }
      
          do {
              try device.lockForConfiguration()
      
              device.focusPointOfInterest = point
              device.exposurePointOfInterest = point
      
              device.focusMode = .continuousAutoFocus
              device.exposureMode = .continuousAutoExposure
      
              device.unlockForConfiguration()
          } catch {
              print(error)
          }
      }
      

      【讨论】:

      • 这与之前的其他答案有何不同?
      • @AshleyMills 接受的答案没有提到AVCaptureVideoPreviewLayer.captureDevicePointConverted(fromLayerPoint:) - 甚至以笨拙的手动方式这样做。这是 Swift 4,公认的答案在 Swift 2 中。此外,这个答案有来源,并遵循 Swift 风格约定/最佳实践。我不明白你为什么认为这会引起反对。
      【解决方案5】:

      设置兴趣点的更好方法:

      • 首先计算兴趣点:

         let devicePoint: CGPoint = (self.previewView.layer as!  AVCaptureVideoPreviewLayer).captureDevicePointOfInterestForPoint(gestureRecognizer.locationInView(gestureRecognizer.view))
        
      • 然后设置关注点:

        let device: AVCaptureDevice! = self.videoDeviceInput!.device
        
            do {
                try device.lockForConfiguration()
        
                if device.focusPointOfInterestSupported && device.isFocusModeSupported(focusMode){
        
                    device.focusPointOfInterest = devicePoint
                    device.focusMode = focusMode
                }
        
                device.unlockForConfiguration()
        
            }catch{
                print(error)
            }
        

      【讨论】:

        【解决方案6】:

        你必须以正确的顺序调用方法:

        if(device.lockForConfiguration(nil)) {
        
            device.focusPointOfInterest = CGPointMake(focus_x, focus_y)
            device.focusMode = AVCaptureFocusMode.ContinuousAutoFocus
        
            device.exposureMode = AVCaptureExposureMode.ContinuousAutoExposure
            device.unlockForConfiguration()
        }
        

        在设置焦点模式之前设置兴趣点,否则焦点将放在上一个兴趣点上。

        同样适用于exposurePointOfInterest

        【讨论】:

          【解决方案7】:

          Swift 5.0 版本

          // The back camera as default device
          var captureDevice: AVCaptureDevice? {
              return AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
          }
          
          // The camera view.
          var cameraView: UIView!
          
          // The layer that contains the camera output
          var previewLayer: AVCaptureVideoPreviewLayer
          
          // The focus square view - the yellow one ;)
          var squareFocusView: UIView
          
          // User taps on screen to select focus
          @IBAction func tapToFocus(_ sender: UITapGestureRecognizer) {
              // make sure we capture one tap only
              if (sender.state == .ended) {
                  
                  guard let captureDevice = captureDevice else {
                      return
                  }
                  
                  let tappedFocusPoint = sender.location(in: cameraView)
                  
                  // we need to move the focus point to be the center of the tap instead of (0.0, 0.0)
                  let centerX = tappedFocusPoint.x - (squareFocusView.frame.size.width / 2.0)
                  let centerY = tappedFocusPoint.y - (squareFocusView.frame.size.height / 2.0)
          
                  let focusPoint = CGPoint(x: centerX, y: centerY)
                  
                  // we need to remap the point because of different coordination systems.
                  let convertedFocusPoint = previewLayer.captureDevicePointConverted(fromLayerPoint: focusPoint)
                  
                  do {
                      // changing focusMode and exposureMode requires the device config to be locked.
                      try captureDevice.lockForConfiguration()
                      
                      if (captureDevice.isFocusModeSupported(.autoFocus) && captureDevice.isFocusPointOfInterestSupported) {
                          captureDevice.focusPointOfInterest = convertedFocusPoint
                          captureDevice.focusMode = .autoFocus
                      }
                      
                      if (captureDevice.isExposureModeSupported(.autoExpose) && captureDevice.isExposurePointOfInterestSupported) {
                          captureDevice.exposurePointOfInterest = convertedFocusPoint
                          captureDevice.exposureMode = .autoExpose
                      }
                      
                      // unlocks device config
                      captureDevice.unlockForConfiguration()
                      
                  } catch {
                      // handle error here
                  }
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-04-27
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多