You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Camera2点击对焦功能失效问题排查与方案咨询

Camera2定点对焦问题解决及可靠实现

问题分析

你的代码存在两个核心问题:

  1. 坐标转换错误:Camera2传感器坐标系与预览/屏幕坐标系存在旋转、比例差异,直接用activeArea.width()作为X坐标会导致点偏移(传感器默认是横向,屏幕可能是纵向,前置摄像头还需要镜像处理)。
  2. 对焦流程冗余:多次调用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标准定点对焦的核心是:

  1. 坐标转换必须严格匹配传感器坐标系
  2. 用单次捕获请求触发AF_TRIGGER_START
  3. 对焦完成后更新重复请求保持状态或恢复自动对焦
  4. 避免不必要的stopRepeating()调用,减少帧中断

内容的提问来源于stack exchange,提问作者mrousavy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 08:45:06