Set an initial focal distance on iOS

Ryuu Tora picture Ryuu Tora · Apr 3, 2013 · Viewed 9.1k times · Source

I'm working on an iOS-app where one of the features is scanning QR-codes. For this I'm using the excellent library, ZBar. The scanning works fine and is generally really quick. However when you use smaller QR-codes it takes a bit longer to scan, mostly due to the fact that the autofocus needs some time to adjust. I was experimenting and noticed that the focus could be locked using the following code:

AVCaptureDevice *cameraDevice = readerView.device;
if ([cameraDevice lockForConfiguration:nil]) {
     [cameraDevice setFocusMode:AVCaptureFocusModeLocked];
     [cameraDevice unlockForConfiguration];
}

When this code is used after a successful scan, the coming scans are really quick. That made me wonder, could I somehow lock the focus before even scanning one code? The app will only scan rather small QR-codes so there will never be a need for focusing on something far away. Sure, I could implement something like tap to focus, but preferably I would like to avoid that extra step. Is there a way to achieve this? Or are there maybe another way of speeding things up when dealing with smaller QR-codes?

// Alexander

Answer

Ryuu Tora picture Ryuu Tora · Sep 25, 2013

In iOS7 this is now possible!

Apple has added the property autoFocusRangeRestriction to the AVCaptureDevice class. This property is of the enum AVCaptureAutoFocusRangeRestriction which has three different values:

  1. AVCaptureAutoFocusRangeRestrictionNone - Default, no restrictions
  2. AVCaptureAutoFocusRangeRestrictionNear - The subject that matters is close to the camera
  3. AVCaptureAutoFocusRangeRestrictionFar - The subject that matters is far from the camera

To check if the method is available we should first check if the property autoFocusRangeRestrictionSupported is true. And since it's only supported in iOS7 an onwards we should also use respondsToSelector so we don't get an exception on earlier iOS-versions.
So the resulting code should look something like this:

AVCaptureDevice *cameraDevice = zbarReaderView.device;
if ([cameraDevice respondsToSelector:@selector(isAutoFocusRangeRestrictionSupported)] && cameraDevice.autoFocusRangeRestrictionSupported) {
    // If we are on an iOS version that supports AutoFocusRangeRestriction and the device supports it
    // Set the focus range to "near"
    if ([cameraDevice lockForConfiguration:nil]) {
        cameraDevice.autoFocusRangeRestriction = AVCaptureAutoFocusRangeRestrictionNear;
        [cameraDevice unlockForConfiguration];
    }
}

This seems to somewhat speed up the scanning of small QR-codes according to my initial tests :)

Update - iOS8

With iOS8, Apple has given us lots of new camera API's to play with. One of this new methods is this one:

- (void)setFocusModeLockedWithLensPosition:(float)lensPosition completionHandler:(void (^)(CMTime syncTime))handler

This method locks focus by moving the lens to a position between 0.0 and 1.0. I played around with the method, locking the lens at close values. However, in general it caused more problems then it solved. You had to keep the QR-codes/barcodes at a very specific distance, which could cause issues when you had codes of different sizes.
But. I think I have found a pretty good alternative to locking focus altogether. When the user press the scan button, I lock the lens to a close distance, and when it's finished I switch the camera back to auto focus. This gives us the benefits of keeping auto focus on, but forces the camera to begin at a close distance where a QR-code/barcode is likely to be found. This in combination with:

cameraDevice.autoFocusRangeRestriction = AVCaptureAutoFocusRangeRestrictionNear;

And:

cameraDevice.focusPointOfInterest = CGPointMake(0.5,0.5);

Results in a pretty snappy scanner. I also built a custom scanner with the API's introduced in iOS7, instead of using ZBar. Mostly because the ZBar-libs are quite outdated and as when iPhone 5 introduced ARMv7s I now had to recompile it again for ARM64.

// Alexander