如何在iOS扫码组件中实现自动对焦和曝光?

在 iOS 扫码组件中实现自动对焦和曝光,核心依赖AVFoundation框架的AVCaptureDevice类,通过配置设备的对焦模式、曝光模式,结合用户交互(如点击对焦)或环境检测(如弱光自动补光)来实现智能调控。以下是分步实现方案,包含基础自动对焦 / 曝光、手动触发优化及特殊场景适配:

一、基础配置:开启自动对焦与曝光

首先需初始化AVCaptureSession并获取摄像头设备(通常为后置摄像头),然后配置对焦和曝光的默认模式:
swift
 
 
 
 
 
import AVFoundation
import UIKit

class ScanViewController: UIViewController {
    private let captureSession = AVCaptureSession()
    private var captureDevice: AVCaptureDevice?
    private let previewLayer = AVCaptureVideoPreviewLayer()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupCaptureSession()
    }
    
    // 初始化采集会话
    private func setupCaptureSession() {
        // 1. 获取后置摄像头设备
        guard let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else {
            print("获取摄像头设备失败")
            return
        }
        captureDevice = device
        
        // 2. 配置输入(摄像头)
        guard let input = try? AVCaptureDeviceInput(device: device) else {
            print("配置摄像头输入失败")
            return
        }
        if captureSession.canAddInput(input) {
            captureSession.addInput(input)
        }
        
        // 3. 配置输出(元数据,用于识别条码)
        let metadataOutput = AVCaptureMetadataOutput()
        if captureSession.canAddOutput(metadataOutput) {
            captureSession.addOutput(metadataOutput)
            metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
            metadataOutput.metadataObjectTypes = [.qr, .code128] // 指定识别码制
        }
        
        // 4. 配置预览层
        previewLayer.session = captureSession
        previewLayer.frame = view.bounds
        previewLayer.videoGravity = .resizeAspectFill
        view.layer.insertSublayer(previewLayer, at: 0)
        
        // 5. 开启自动对焦和曝光
        setupAutoFocusAndExposure()
        
        // 启动采集会话
        captureSession.startRunning()
    }
    
    // 配置自动对焦与曝光
    private func setupAutoFocusAndExposure() {
        guard let device = captureDevice else { return }
        do {
            try device.lockForConfiguration() // 锁定配置(必须)
            
            // 自动对焦:持续自动对焦(适合扫码时随条码移动调整)
            if device.isFocusModeSupported(.continuousAutoFocus) {
                device.focusMode = .continuousAutoFocus
                device.focusPointOfInterest = CGPoint(x: 0.5, y: 0.5) // 对焦中心区域
            }
            
            // 自动曝光:持续自动曝光(根据环境光线调整)
            if device.isExposureModeSupported(.continuousAutoExposure) {
                device.exposureMode = .continuousAutoExposure
                device.exposurePointOfInterest = CGPoint(x: 0.5, y: 0.5) // 曝光中心区域
            }
            
            device.unlockForConfiguration() // 解锁配置
        } catch {
            print("配置对焦/曝光失败:\(error)")
        }
    }
}

// 实现元数据代理(扫码识别逻辑)
extension ScanViewController: AVCaptureMetadataOutputObjectsDelegate {
    func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
        // 处理识别结果...
    }
}
 

二、进阶优化:点击屏幕手动对焦 + 曝光

扫码时用户可能需要对准特定区域(如远距离条码),添加点击预览层触发对焦 / 曝光
swift
 
 
 
 
 
// 在viewDidLoad中添加点击手势
override func viewDidLoad() {
    super.viewDidLoad()
    setupCaptureSession()
    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
    view.addGestureRecognizer(tapGesture)
}

// 处理点击对焦
@objc private func handleTap(_ gesture: UITapGestureRecognizer) {
    let point = gesture.location(in: view)
    // 将点击坐标转换为摄像头的兴趣区域坐标(摄像头坐标系:0~1,原点在左上角)
    let cameraPoint = previewLayer.captureDevicePointConverted(fromLayerPoint: point)
    focus(with: cameraPoint)
}

// 手动对焦+曝光
private func focus(with point: CGPoint) {
    guard let device = captureDevice else { return }
    do {
        try device.lockForConfiguration()
        
        // 1. 手动对焦到点击位置
        if device.isFocusModeSupported(.autoFocus) {
            device.focusPointOfInterest = point
            device.focusMode = .autoFocus // 单次自动对焦(对焦后恢复持续自动对焦)
            // 对焦完成后恢复持续对焦(可选)
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                if device.isFocusModeSupported(.continuousAutoFocus) {
                    device.focusMode = .continuousAutoFocus
                }
            }
        }
        
        // 2. 手动曝光到点击位置
        if device.isExposureModeSupported(.autoExpose) {
            device.exposurePointOfInterest = point
            device.exposureMode = .autoExpose // 单次自动曝光
            // 曝光完成后恢复持续曝光(可选)
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                if device.isExposureModeSupported(.continuousAutoExposure) {
                    device.exposureMode = .continuousAutoExposure
                }
            }
        }
        
        device.unlockForConfiguration()
    } catch {
        print("手动对焦/曝光失败:\(error)")
    }
}
 

三、弱光场景:自定义曝光参数(ISO / 快门)

若环境光线过暗,可手动调整曝光参数(如提高 ISO、延长快门时间),实现弱光补光
swift
 
 
 
 
 
// 弱光下增强曝光
private func enhanceLowLightExposure() {
    guard let device = captureDevice else { return }
    do {
        try device.lockForConfiguration()
        
        // 检查是否支持自定义曝光
        if device.isExposureModeSupported(.custom) {
            // 设置曝光时长(单位:秒,需在device.minExposureDuration和maxExposureDuration之间)
            let exposureDuration = CMTimeMakeWithSeconds(1/30, preferredTimescale: 1000000000) // 30fps
            // 设置ISO(需在device.minISO和maxISO之间)
            let iso = min(400, device.maxISO) // 提高ISO至400(避免噪点过多)
            
            device.setExposureModeCustom(duration: exposureDuration, iso: iso, completionHandler: nil)
        }
        
        device.unlockForConfiguration()
    } catch {
        print("自定义曝光失败:\(error)")
    }
}
 

四、闪光灯联动:弱光自动开启闪光灯

结合环境光检测,在弱光时自动打开闪光灯辅助照明:
swift
 
 
 
 
 
// 自动控制闪光灯
private func autoToggleTorch() {
    guard let device = captureDevice, device.hasTorch else { return }
    do {
        try device.lockForConfiguration()
        
        // 模拟环境光检测(实际可通过亮度传感器获取)
        let isLowLight = true // 弱光标记
        device.torchMode = isLowLight ? .on : .off
        // 设置闪光灯亮度(0~1)
        if device.isTorchModeSupported(.on) {
            try device.setTorchModeOn(level: 0.8)
        }
        
        device.unlockForConfiguration()
    } catch {
        print("闪光灯控制失败:\(error)")
    }
}
 

五、关键注意事项

  1. 配置锁定机制:所有对AVCaptureDevice的配置修改必须在lockForConfiguration()unlockForConfiguration()之间进行,否则会崩溃。
  2. 设备能力检查:不同设备支持的对焦 / 曝光模式不同(如部分旧机型不支持自定义曝光),需通过isFocusModeSupported()isExposureModeSupported()判断。
  3. 坐标系转换:点击屏幕的坐标是UIKit坐标系(原点在左上角),需通过previewLayer.captureDevicePointConverted(fromLayerPoint:)转换为摄像头的兴趣区域坐标(0~1 范围)。
  4. 性能平衡:持续自动对焦 / 曝光会增加 CPU 消耗,若需极致性能,可在识别到条码后暂停自动调整。

总结

通过AVFoundationAVCaptureDevice配置,可实现基础自动对焦 / 曝光、点击手动调控及弱光场景优化,结合闪光灯联动能进一步提升扫码成功率。实际开发中需根据业务场景(如支付扫码、商品盘点)调整策略,例如支付场景优先保证识别速度,商品盘点场景支持连续对焦适配批量扫码。
posted @ 2025-11-29 15:49  老大程序员  阅读(0)  评论(0)    收藏  举报