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

Camera2设置中间区域AE/AF/AWB后预览模糊,求实现方案

Camera2实现中间区域对焦锁定+AE/AWB基于中间区域计算的正确流程

你遇到的预览模糊问题核心在于AF模式选择错误以及区域坐标转换逻辑不准确,以下是完整的实现步骤和修正后的代码:

一、核心问题分析

  1. AF模式问题:你使用CONTROL_AF_MODE_AUTO是单次自动对焦模式,不会锁定对焦状态,且未主动触发对焦动作,导致相机可能未完成对焦就进入预览,或后续重新对焦偏离中间区域。
  2. 区域坐标错误:MeteringRectangle的坐标基于传感器原始方向(通常为横向,与设备屏幕方向无关),直接交换left和top的逻辑无法正确匹配屏幕预览的中间区域,需根据传感器方向和预览方向做完整坐标映射。

二、正确实现流程

1. 确认设备支持性(必须步骤,规避兼容性问题)

在使用区域测光/对焦前,先检查设备是否支持对应功能:

// 检查AF区域支持
val afRegionsSupported = characteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF) ?: 0 > 0
// 检查AE区域支持
val aeRegionsSupported = characteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AE) ?: 0 > 0
// 检查AWB区域支持
val awbRegionsSupported = characteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB) ?: 0 > 0

2. 正确计算中间区域的MeteringRectangle

修正坐标转换逻辑,需考虑预览旋转方向(设备屏幕方向与传感器方向的差值),以下是正确的区域计算函数:

fun getCenterMeteringRegion(characteristics: CameraCharacteristics, previewWidth: Int, previewHeight: Int): MeteringRectangle {
    val sensorRect = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE)!!
    val sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!
    // 计算预览与传感器区域的比例(适配预览裁剪/缩放)
    val ratioX = sensorRect.width().toFloat() / previewWidth.toFloat()
    val ratioY = sensorRect.height().toFloat() / previewHeight.toFloat()

    // 预览画面的中间区域(尺寸可按需调整)
    val previewRegionWidth = 200
    val previewRegionHeight = 200
    val previewLeft = (previewWidth - previewRegionWidth) / 2
    val previewTop = (previewHeight - previewRegionHeight) / 2

    // 将预览坐标转换为传感器坐标,并处理旋转
    val (sensorLeft, sensorTop, sensorRegionWidth, sensorRegionHeight) = when (sensorOrientation) {
        90 -> {
            // 传感器顺时针转90度(竖屏预览)
            val rotatedLeft = previewTop * ratioY
            val rotatedTop = (previewWidth - (previewLeft + previewRegionWidth)) * ratioX
            val rotatedWidth = previewRegionHeight * ratioY
            val rotatedHeight = previewRegionWidth * ratioX
            Quad(rotatedLeft.toInt(), rotatedTop.toInt(), rotatedWidth.toInt(), rotatedHeight.toInt())
        }
        270 -> {
            // 传感器顺时针转270度
            val rotatedLeft = (previewHeight - (previewTop + previewRegionHeight)) * ratioY
            val rotatedTop = previewLeft * ratioX
            val rotatedWidth = previewRegionHeight * ratioY
            val rotatedHeight = previewRegionWidth * ratioX
            Quad(rotatedLeft.toInt(), rotatedTop.toInt(), rotatedWidth.toInt(), rotatedHeight.toInt())
        }
        180 -> {
            // 传感器顺时针转180度
            val rotatedLeft = (previewWidth - (previewLeft + previewRegionWidth)) * ratioX
            val rotatedTop = (previewHeight - (previewTop + previewRegionHeight)) * ratioY
            Quad(rotatedLeft.toInt(), rotatedTop.toInt(), (previewRegionWidth * ratioX).toInt(), (previewRegionHeight * ratioY).toInt())
        }
        else -> { // 0度(传感器横向)
            Quad((previewLeft * ratioX).toInt(), (previewTop * ratioY).toInt(), (previewRegionWidth * ratioX).toInt(), (previewRegionHeight * ratioY).toInt())
        }
    }

    return MeteringRectangle(
        sensorLeft,
        sensorTop,
        sensorRegionWidth,
        sensorRegionHeight,
        MeteringRectangle.METERING_WEIGHT_MAX // 最大权重确保区域优先级最高
    )
}

// 辅助数据类,简化坐标返回
data class Quad(val left: Int, val top: Int, val width: Int, val height: Int)

3. 配置CaptureRequest并实现对焦锁定

需先触发对焦,完成后锁定AF状态,同时设置AE/AWB区域:

private fun setupPreviewAndLockFocus() {
    val previewSurface = Surface(viewFinder.surfaceTexture)
    val previewWidth = viewFinder.width
    val previewHeight = viewFinder.height
    val meteringRegion = getCenterMeteringRegion(characteristics, previewWidth, previewHeight)

    // 1. 初始化预览请求模板
    val previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply {
        addTarget(previewSurface)
        // 设置AE/AWB区域
        set(CaptureRequest.CONTROL_AE_REGIONS, arrayOf(meteringRegion))
        set(CaptureRequest.CONTROL_AWB_REGIONS, arrayOf(meteringRegion))
        set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_AUTO)
        set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON)
        set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH)
    }

    // 2. 触发单次对焦请求
    val afRequestBuilder = previewRequestBuilder.clone().apply {
        set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO)
        set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START)
        set(CaptureRequest.CONTROL_AF_REGIONS, arrayOf(meteringRegion))
    }

    // 发送对焦请求,在回调中处理锁定逻辑
    cameraCaptureSession.capture(afRequestBuilder.build(), object : CameraCaptureSession.CaptureCallback() {
        override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) {
            super.onCaptureCompleted(session, request, result)
            // 检查对焦是否完成
            val afState = result.get(CaptureResult.CONTROL_AF_STATE)
            if (afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
                // 3. 创建锁定对焦的重复预览请求
                val lockedPreviewRequest = previewRequestBuilder.clone().apply {
                    set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_LOCKED)
                    set(CaptureRequest.CONTROL_AF_TRIGGER, null) // 清除触发信号
                }
                // 设置重复预览,此时对焦已锁定在中间区域
                cameraCaptureSession.setRepeatingRequest(lockedPreviewRequest.build(), captureCallBack, cameraHandler)
            } else {
                // 对焦失败,降级使用普通预览
                cameraCaptureSession.setRepeatingRequest(previewRequestBuilder.build(), captureCallBack, cameraHandler)
            }
        }
    }, cameraHandler)
}

4. 关键注意事项

  • 权重设置:MeteringRectangle使用METERING_WEIGHT_MAX可确保该区域测光/对焦优先级最高,避免其他区域干扰。
  • AF触发与锁定:必须先发送带CONTROL_AF_TRIGGER_START的请求触发单次对焦,完成后切换到CONTROL_AF_MODE_LOCKED锁定状态,否则无法固定对焦。
  • 预览尺寸匹配:计算区域时必须使用当前预览Surface的实际尺寸,避免预览缩放导致区域偏移。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 10:18:11