简体   繁体   English

iOS 点击对焦

[英]iOS tap to focus

I used this code to achieve Tap-to-Focus in iOS custom camera App, but it isn't working.我使用此代码在 iOS 自定义相机 App 中实现了 Tap-to-Focus,但它不起作用。 Here's the code这是代码

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()
        }
    }
}

With a videoView: UIView displaying the video, and cameraDevice: AVCaptureDevice , the following seems to work for me: 使用videoView: UIView显示视频和cameraDevice: AVCaptureDevice ,以下似乎对我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()
        }
    }
} 

Note that I had to swap the x and y coordinates, and remap the x coord from 1 to 0 instead of 0 to 1 — not sure why that should be the case but it seems to be necessary to get it to work right (though it's a little tricky to test it too) . 请注意,我必须交换xy坐标,并将x coord从1重新映射到0而不是0到1 - 不知道为什么会出现这种情况但似乎有必要让它正常工作(尽管它是有点棘手的测试它)

Edit: Apple's documentation explains the reason for the coordinate transformation. 编辑: Apple的文档解释了坐标转换的原因。

In addition, a device may support a focus point of interest. 另外,设备可以支持关注焦点。 You test for support using focusPointOfInterestSupported. 您使用focusPointOfInterestSupported测试支持。 If it's supported, you set the focal point using focusPointOfInterest. 如果支持,则使用focusPointOfInterest设置焦点。 You pass a CGPoint where {0,0} represents the top left of the picture area, and {1,1} represents the bottom right in landscape mode with the home button on the right—this applies even if the device is in portrait mode. 您传递一个CGPoint,其中{0,0}表示图片区域的左上角,{1,1}表示横向模式的右下角,右侧的主页按钮 - 即使设备处于纵向模式,这也适用。

In my example I had been using .ContinuousAutoFocus and .ContinuousAutoExposure , but the documentation indicates .AutoFocus is the right choice. 在我的例子我已经使用了.ContinuousAutoFocus.ContinuousAutoExposure ,但文件表明.AutoFocus是正确的选择。 Oddly the documentation makes no mention of .AutoExpose , but I'm using it in my code and it works fine. 奇怪的是文档没有提到.AutoExpose ,但我在我的代码中使用它并且它工作正常。

I also modified my example code to include .focusPointOfInterestSupported and .exposurePointOfInterestSupported tests — the documentation also mentions using the isFocusModeSupported: and isExposureModeSupported: methods for a given focus/exposure mode to test whether it is available on a given device before setting it, but I assume if the device supports the point of interest modes then it also supports the auto modes. 我还修改了我的示例代码以包含.focusPointOfInterestSupported.exposurePointOfInterestSupported测试 - 文档还提到使用isFocusModeSupported:isExposureModeSupported:给定焦点/曝光模式的方法来测试它在设置之前是否在给定设备上可用,但是我假设设备支持兴趣点模式,那么它也支持自动模式。 It all seems to work fine in my app. 这一切似乎在我的应用程序中正常工作。

Swift 3.0 Solution Swift 3.0解决方案

Converted Cody's answer into a working solution with Swift 3. 将Cody的答案转换为Swift 3的工作解决方案。

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
        }
    }
}
 device.focusPointOfInterest = focusPoint
 device.focusMode = AVCaptureFocusMode.AutoFocus
 device.exposurePointOfInterest = focusPoint
 device.exposureMode = AVCaptureExposureMode.ContinuousAutoExposure

I don't why this works, but it did. 我不知道为什么会这样,但确实如此。

The better way to set focus point of interest: 设置焦点的更好方法是:

  • first calculate the point of interest: 首先计算兴趣点:

      let devicePoint: CGPoint = (self.previewView.layer as! AVCaptureVideoPreviewLayer).captureDevicePointOfInterestForPoint(gestureRecognizer.locationInView(gestureRecognizer.view)) 
  • after that set the focus point of interest : 之后设置了焦点:

     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) } 

You should read Apple docs on focusPointOfInterest , which says three important things: 你应该阅读关于focusPointOfInterest Apple 文档 ,它说三件重要的事情:

  1. Setting a value for this property does not initiate a focusing operation. 为此属性设置值不会启动聚焦操作。 To focus the camera on a point of interest, first set this property's value, then set the focusMode property to autoFocus or continuousAutoFocus. 要将相机聚焦在感兴趣的点上,首先设置此属性的值,然后将focusMode属性设置为autoFocus或continuousAutoFocus。

  2. This property's CGPoint value uses a coordinate system where {0,0} is the top left of the picture area and {1,1} is the bottom right. 此属性的CGPoint值使用坐标系,其中{0,0}是图片区域的左上角,{1,1}是右下角。 This coordinate system is always relative to a landscape device orientation with the home button on the right, regardless of the actual device orientation. 无论实际的设备方向如何,此坐标系始终相对于横向设备方向,主菜单按钮位于右侧。 You can convert between this coordinate system and view coordinates using AVCaptureVideoPreviewLayer methods. 您可以使用AVCaptureVideoPreviewLayer方法在此坐标系和视图坐标之间进行转换。

  3. Before changing the value of this property, you must call lockForConfiguration() to acquire exclusive access to the device's configuration properties. 在更改此属性的值之前,必须调用lockForConfiguration()以获取对设备配置属性的独占访问权。 Otherwise, setting the value of this property raises an exception. 否则,设置此属性的值会引发异常。 When you are done configuring the device, call unlockForConfiguration() to release the lock and allow other devices to configure the settings. 完成配置设备后,请调用unlockForConfiguration()以释放锁定并允许其他设备配置设置。

Here is an implementation which does all of that: 这是一个实现所有这些的实现:

// 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)
    }
}

You have to call the methods in the right order: 您必须按正确的顺序调用方法:

if(device.lockForConfiguration(nil)) {

    device.focusPointOfInterest = CGPointMake(focus_x, focus_y)
    device.focusMode = AVCaptureFocusMode.ContinuousAutoFocus

    device.exposureMode = AVCaptureExposureMode.ContinuousAutoExposure
    device.unlockForConfiguration()
}

Set the point of interest before setting the focus mode else the focus will be made on the previous point of interest. 在设置焦点模式之前设置关注点,否则焦点将在之前的关注点上进行。

The same apply for exposurePointOfInterest . 这同样适用于exposurePointOfInterest

Swift 5.0 version斯威夫特 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
        }
    }
}

Swift 4: Swift 4:

   public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    var touchPoint = touches.first as! UITouch
    let cameraView = cameraViewController.view
    var screenSize = cameraView!.bounds.size
    var focusPoint = CGPoint(x: touchPoint.location(in: cameraView).y / screenSize.height, y: 1.0 - touchPoint.location(in: cameraView).x / screenSize.width)
    
    
    
    
    if #available(iOS 10.0, *) {
        let device = AVCaptureDevice.default(.builtInWideAngleCamera,
                                             for: .video, position: .unspecified)
        
        do{
            try device?.lockForConfiguration()
            
            if device!.isFocusPointOfInterestSupported {
                        device!.focusPointOfInterest = focusPoint
                device!.focusMode = AVCaptureDevice.FocusMode.autoFocus
                    }
            if device!.isExposurePointOfInterestSupported {
                        device!.exposurePointOfInterest = focusPoint
                device!.exposureMode = AVCaptureDevice.ExposureMode.autoExpose
                    }
                    device!.unlockForConfiguration()
        }catch{
            
        }
           
  
            
        
    } else {
        // Fallback on earlier versions
    }
     
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM