Android Camera2点击对焦功能失效问题排查与方案咨询
Camera2定点对焦问题解决及可靠实现
问题分析
你的代码存在两个核心问题:
- 坐标转换错误:Camera2传感器坐标系与预览/屏幕坐标系存在旋转、比例差异,直接用
activeArea.width()作为X坐标会导致点偏移(传感器默认是横向,屏幕可能是纵向,前置摄像头还需要镜像处理)。 - 对焦流程冗余:多次调用
stopRepeating()和重复设置请求会导致大量预览帧丢失,且AF触发逻辑混乱,没有正确处理对焦完成后的状态。
正确实现步骤
1. 屏幕点转传感器坐标系
必须将屏幕点击的点转换为Camera2传感器的Active Array坐标系,核心逻辑:
fun convertScreenPointToSensor( screenPoint: Point, previewViewSize: Size, sensorActiveArray: Rect, sensorOrientation: Int, isFrontCamera: Boolean ): Point { // 计算预览视图与传感器的比例 val scaleX = sensorActiveArray.width().toFloat() / previewViewSize.width() val scaleY = sensorActiveArray.height().toFloat() / previewViewSize.height() // 处理屏幕与传感器的旋转差异 val rotatedPoint = when (sensorOrientation) { 90 -> Point(previewViewSize.height() - screenPoint.y, screenPoint.x) 180 -> Point(previewViewSize.width() - screenPoint.x, previewViewSize.height() - screenPoint.y) 270 -> Point(screenPoint.y, previewViewSize.width() - screenPoint.x) else -> screenPoint } // 前置摄像头镜像翻转X轴 val finalX = if (isFrontCamera) { previewViewSize.width() - rotatedPoint.x } else { rotatedPoint.x } // 缩放到传感器坐标系 return Point( (finalX * scaleX).toInt() + sensorActiveArray.left, (rotatedPoint.y * scaleY).toInt() + sensorActiveArray.top ) }
2. 简化对焦流程(协程版)
不需要频繁停止/重启重复请求,基于现有重复请求修改参数,用单次捕获触发对焦,减少帧丢失:
private const val FOCUS_RESET_TIMEOUT = 3000L private var focusJob: Job? = null suspend fun focus(screenPoint: Point, previewViewSize: Size, characteristics: CameraCharacteristics) { // 取消之前的对焦超时任务,避免冲突 focusJob?.cancel() // 坐标转换 val sensorActiveArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE)!! val sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!! val isFrontCamera = characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT val sensorPoint = convertScreenPointToSensor(screenPoint, previewViewSize, sensorActiveArray, sensorOrientation, isFrontCamera) // 创建测光区域 val meteringSize = 200 // 根据需求调整,不要超过传感器尺寸 val meteringRectangle = MeteringRectangle( sensorPoint.x - meteringSize / 2, sensorPoint.y - meteringSize / 2, meteringSize, meteringSize, MeteringRectangle.METERING_WEIGHT_MAX - 1 ) // 基于当前重复请求构建对焦请求,保留原有参数(如曝光、白平衡) val baseRequest = createBaseCaptureRequest(device, outputs) baseRequest.apply { set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO) set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO) set(CaptureRequest.CONTROL_AF_REGIONS, arrayOf(meteringRectangle)) set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START) } // 提交单次捕获请求触发对焦,等待完成 session.capture(baseRequest.build(), false) // 更新重复请求,保持对焦区域(可选,若需要持续对焦该区域) val ongoingRequest = createBaseCaptureRequest(device, outputs).apply { set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO) set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO) set(CaptureRequest.CONTROL_AF_REGIONS, arrayOf(meteringRectangle)) set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE) } session.setRepeatingRequest(ongoingRequest.build(), null, null) // 超时后恢复自动对焦 focusJob = coroutineScope { launch { delay(FOCUS_RESET_TIMEOUT) val resetRequest = createBaseCaptureRequest(device, outputs).apply { set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO) set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) remove(CaptureRequest.CONTROL_AF_REGIONS) } session.setRepeatingRequest(resetRequest.build(), null, null) } } }
关键优化点
- 减少帧丢失:基于现有重复请求修改参数,避免全量重建;仅在必要时更新重复请求,而非多次停止/重启。
- 正确坐标转换:覆盖旋转、镜像、比例缩放三种场景,确保对焦区域准确。
- 状态管理:取消旧的对焦任务,避免冲突;对焦完成后设置IDLE状态,防止重复触发。
可靠实现核心逻辑
Camera2标准定点对焦的核心是:
- 坐标转换必须严格匹配传感器坐标系
- 用单次捕获请求触发
AF_TRIGGER_START - 对焦完成后更新重复请求保持状态或恢复自动对焦
- 避免不必要的
stopRepeating()调用,减少帧中断
内容的提问来源于stack exchange,提问作者mrousavy
相关产品推荐
相关产品推荐

