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
, который говорит три важные вещи:
Установка значения для этого свойства не инициирует операцию фокусировки. Чтобы сфокусировать камеру на интересующей точке, сначала задайте значение этого свойства, а затем установите для свойства focusMode значение autoFocus или ContinentalAutoFocus.
Значение CGPoint этого свойства использует систему координат, где {0,0} - верхний левый угол области изображения, а {1,1} - нижний правый. Эта система координат всегда зависит от ориентации устройства в альбомной ориентации с кнопкой "Домой" справа, независимо от фактической ориентации устройства. Вы можете выполнить преобразование между этой системой координат и просмотром координат, используя методы AVCaptureVideoPreviewLayer.
Перед изменением значения этого свойства необходимо вызвать 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
}
}