iosswiftavcapturedevice

AVCaptureDevice's exposurePointOfInterest does not work


I'm trying to change the exposure in my camera app according to certain point of the image.

I'm using the following code that is triggered when the user taps on screen. For now I simply try to expose to the center.

@IBAction func didTap()
{
    if captureDevice.isExposurePointOfInterestSupported
    {
        try! captureDevice.lockForConfiguration()

        captureDevice.exposurePointOfInterest = CGPoint(x: 0.5, y: 0.5)
        captureDevice.exposureMode = .continuousAutoExposure

        captureDevice.unlockForConfiguration()
    }
}

But nothing happens.

captureDevice.isExposurePointOfInterestSupported is true. The captureDevice currently is .builtInDualCamera.

This code is in a simple camera test app based on sample code. It shows the live camera image on screen.

Has anyone got exposurePointOfInterest working on iOS 14.4? What could I be missing?


Solution

  • I actually ran into this issue yesterday. Turns out there's a problem with using exactly (0.5, 0.5). When I use (0.51, 0.51) it works every time 🤷

    extension AVCaptureDevice {
        func change(_ block: (AVCaptureDevice) -> ()) {
            try! self.lockForConfiguration()
            block(self)
            self.unlockForConfiguration()
        }
    }
    
    @objc func handleTap() {
        device.change {
            $0.exposurePointOfInterest = CGPoint(x: 0.51, y: 0.51)
            $0.exposureMode = .autoExpose
        }
    }
    

    Update

    It may also be worth noting that, although it's a point specified exposure, the region around that point still has to be large enough to trigger an exposure adjust. Let's call this the trigger region.

    From what I understand from my tests, the point (0.5, 0.5) has a special effect on the trigger region's size. Whenever this point is used as the exposurePointOfInterest, the trigger region is rather large, regardless of whether exposureMode is .continuousAutoExpose or .autoExpose.

    You can get an idea of the size of this region by using the following code, pointing your phone at a bright area (like a lamp), and seeing how close you have to get until a tap adjusts the exposure. You'll find that the exposure does adjust, but you have to get rather close.

    @objc func handleTap() {
        device.change {
            $0.exposurePointOfInterest = CGPoint(x: 0.5, y: 0.5)
            $0.exposureMode = .autoExpose
        }
    }
    

    enter image description here

    Or, you could not use a tap, and just keep the properties exposureMode and exposurePointOfInterest at their default values of .continuousAutoExpose and (0.5, 0.5). Or you could use the native camera app and see when it automatically adjusts the exposure. The results are the same.

    Now, if you were to set the exposurePointOfInterest to a value close to but not equal to the midpoint, say (0.51, 0.51), you'll find that the trigger region becomes much, much smaller.

    You could also use .continuousAutoExpose and call this only once, and you'll find that the automatic exposure adjustments are a lot more sensitive as the trigger region is a lot smaller:

    func viewDidLoad() {
        super.viewDidLoad()
        device.change {
            $0.exposurePointOfInterest = CGPoint(x: 0.51, y: 0.51)
            $0.exposureMode = .continuousAutoExpose
        }
    }
    

    To get an idea of the size of this smaller region, open the native camera app and tap somewhere to focus/expose at that point. You'll see a small bounding box. That's pretty much the size of the trigger region.

    Say you have a tap like so:

    @objc func handleTap() {
        device.change {
            $0.exposurePointOfInterest = CGPoint(x: 0.51, y: 0.51)
            $0.exposureMode = .autoExpose
        }
    }
    

    If nothing happens, the region is not large enough, and you should be able to reproduce the same no-effect in the native camera app when you try to tap to expose at that point.

    Side Note

    Your didTap() method is setting the default values, so it's essentially useless.

    If you want to adjust exposure on a tap, use .autoExpose if the point is always the same. Don't use .continuousAutoExpose cuz that's gonna be adjusting exposure all the time, not just on a tap. It only makes sense to do this if the tap will change the point.