iOS нажмите, чтобы сосредоточиться

Я использовал этот код для создания приложения Tap-to-Focus в приложении пользовательской камеры iOS, но он не работает. Вот код

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

9 ответов

Решение
 device.focusPointOfInterest = focusPoint
 device.focusMode = AVCaptureFocusMode.AutoFocus
 device.exposurePointOfInterest = focusPoint
 device.exposureMode = AVCaptureExposureMode.ContinuousAutoExposure

Я не знаю, почему это работает, но это сработало.

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

Обратите внимание, что я должен был поменять x а также y координаты и переназначить x Координата от 1 до 0 вместо 0 до 1 - не уверен, почему так должно быть, но кажется, что необходимо заставить его работать правильно (хотя это тоже немного сложно проверить).

Изменить: документация Apple объясняет причину преобразования координат.

Кроме того, устройство может поддерживать интересную точку фокусировки. Вы проверяете поддержку с помощью focusPointOfInterestSupported. Если это поддерживается, вы устанавливаете фокус с помощью focusPointOfInterest. Вы передаете CGPoint, где {0,0} представляет верхний левый угол области изображения, а {1,1} представляет нижний правый в ландшафтном режиме с кнопкой "Домой" справа - это применимо, даже если устройство находится в портретном режиме,

В моем примере я использовал .ContinuousAutoFocus а также .ContinuousAutoExposure, но в документации указано .AutoFocus это правильный выбор. Как ни странно, в документации не упоминается .AutoExpose, но я использую его в своем коде, и он работает нормально.

Я также изменил мой пример кода, чтобы включить .focusPointOfInterestSupported а также .exposurePointOfInterestSupported тесты - в документации также упоминается использование isFocusModeSupported: а также isExposureModeSupported: методы для данного режима фокусировки / экспозиции, чтобы проверить, доступно ли оно на данном устройстве, перед его настройкой, но я предполагаю, что если устройство поддерживает режимы точек интереса, то оно также поддерживает автоматические режимы. Кажется, все отлично работает в моем приложении.

Решение Swift 3.0

Превратил ответ Коди в рабочее решение с помощью 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
        }
    }
}

Вы должны прочитать документы Apple на focusPointOfInterest, который говорит три важные вещи:

  1. Установка значения для этого свойства не инициирует операцию фокусировки. Чтобы сфокусировать камеру на интересующей точке, сначала задайте значение этого свойства, а затем установите для свойства focusMode значение autoFocus или ContinentalAutoFocus.

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

Лучший способ установить интересную точку фокусировки:

  • Сначала рассчитаем интересующую точку:

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

Версия Свифт 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
        }
    }
}

В дополнение к предоставленным ответам, которые позволяют вам сфокусировать/экспонировать область/объект в определенной точке, если этот объект переместился или изменился вид камеры, в этот момент нам нужно вернуть focusMode на .continousAutoFocus и режим экспозиции. вернитесь к .continousAutoExposure, чтобы позволить системе обрабатывать фокус/экспозицию

Во-первых, нам нужно наблюдать за уведомлением об изменении предметной области:

      cameraDevice.isSubjectAreaChangeMonitoringEnabled = true
NotificationCenter.default.addObserver(self, selector: #selector(setDefaultFocusAndExposure), name: .AVCaptureDeviceSubjectAreaDidChange, object: nil)

И настройте устройство камеры при получении уведомления:

      @objc private func setDefaultFocusAndExposure() {
        guard let cameraDevice else { return }
        try? cameraDevice.lockForConfiguration()
        if cameraDevice.isFocusModeSupported(.continuousAutoFocus) {
            cameraDevice.focusMode = .continuousAutoFocus
        }
        if cameraDevice.isExposureModeSupported(.continuousAutoExposure) {
            cameraDevice.exposureMode = .continuousAutoExposure
        }
        cameraDevice.unlockForConfiguration()
}

Наконец, не забудьте удалить наблюдателя, я делаю это в deinit:

      deinit {
        NotificationCenter.default.removeObserver(self)
}

Вы должны вызывать методы в правильном порядке:

if(device.lockForConfiguration(nil)) {

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

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

Установите точку интереса перед установкой режима фокусировки, иначе фокус будет сделан на предыдущую точку интереса.

То же самое относится к exposurePointOfInterest,

Свифт 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
    }
     
}
Другие вопросы по тегам