Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: autoFocus/Expose and continuousAutoFocus/Exposure at the same time?

Right now in my camera app I let the user touch anywhere to set the focus and exposure, but how can I get the best of both worlds just like Apple's Camera app?

For instance, the user may want to touch to focus on something that is in the foreground, but if the scene changes enough, it should go back to continuousAutoFocus. Same if the user points the camera towards a light, it should change exposure to make it appear correctly, and then when the camera goes back to the scene it should fix the exposure once again so it isn't too dark. However, they still have the option to make it a little lighter or darker depending on what they touch through the camera's view.

Right now I set my defaults to the center of the screen when the view appears:

func setDefaultFocusAndExposure() {

    let focusPoint = CGPoint(x:0.5,y:0.5)

    if let device = AVCaptureDevice.default(for:AVMediaType.video) {
        do {
            try device.lockForConfiguration()
            if device.isFocusPointOfInterestSupported {
                print(focusPoint)
                device.focusPointOfInterest = focusPoint
                device.focusMode = AVCaptureDevice.FocusMode.autoFocus
            }
            if device.isExposurePointOfInterestSupported {
                device.exposurePointOfInterest = focusPoint
                device.exposureMode = AVCaptureDevice.ExposureMode.autoExpose
            }
            device.unlockForConfiguration()

        } catch {
            // Handle errors here
            print("There was an error focusing the device's camera")
        }
    }

}

Also I let the user set the focus and exposure depending where they touch:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    let bounds = UIScreen.main.bounds

    let touchPoint = touches.first! as UITouch
    let screenSize = bounds.size
    let focusPoint = CGPoint(x: touchPoint.location(in: view).y / screenSize.height, y: 1.0 - touchPoint.location(in: view).x / screenSize.width)

    if let device = AVCaptureDevice.default(for:AVMediaType.video) {
        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 {
            // Handle errors here
            print("There was an error focusing the device's camera")
        }
    }
}
like image 491
Chewie The Chorkie Avatar asked Mar 28 '18 18:03

Chewie The Chorkie


1 Answers

Figured it out. First I call a method on myViewDidAppear that sets my default focus and exposure mode:

@objc func setDefaultFocusAndExposure() {

    if let device = AVCaptureDevice.default(for:AVMediaType.video) {
        do {
            try device.lockForConfiguration()
                device.isSubjectAreaChangeMonitoringEnabled = true
                device.focusMode = AVCaptureDevice.FocusMode.continuousAutoFocus
                device.exposureMode = AVCaptureDevice.ExposureMode.continuousAutoExposure
            device.unlockForConfiguration()

        } catch {
            // Handle errors here
            print("There was an error focusing the device's camera")
        }
    }
}

The trick here was not to confuse autoFocus with continuousAutoFocus. This way by default it constantly observes the scene and sets the focus and exposure. Also isSubjectAreaChangeMonitorEnabled is a very important bit. This lets you register a notification to call a function when the scene changes focus or exposure-wise so that you can switch the focus mode. More on that soon.

In a touch on the screen can set the focus and exposure based on a point:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    let bounds = UIScreen.main.bounds

    let touchPoint = touches.first! as UITouch
    let screenSize = bounds.size
    let focusPoint = CGPoint(x: touchPoint.location(in: view).y / screenSize.height, y: 1.0 - touchPoint.location(in: view).x / screenSize.width)

    if let device = AVCaptureDevice.default(for:AVMediaType.video) {
        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 {
            // Handle errors here
            print("There was an error focusing the device's camera")
        }
    }
}

Register the notification in viewDidLoad for setting back the focus and exposure mode to continuous. Here I'm just calling the function that sets the default settings of continuous:

    NotificationCenter.default.addObserver(self,
                                           selector: #selector(self.setDefaultFocusAndExposure),
                                           name: NSNotification.Name.AVCaptureDeviceSubjectAreaDidChange,
                                           object: nil)

Don't forget to remove observers in NotificationCenter:

deinit {
    NotificationCenter.default.removeObserver(self)
}
like image 83
Chewie The Chorkie Avatar answered Nov 06 '22 19:11

Chewie The Chorkie