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

Android平台修改相机曝光方法及OpenCV+Camera2崩溃问题解决

嘿,我来帮你搞定Android上OpenCV结合相机调整曝光的问题!你之前用Camera2时程序崩溃,大概率是相机生命周期、权限处理或者Surface的逻辑没捋顺,我给你两个可行的方案,一步步来:

方案一:用OpenCV自带的CameraView + Camera1 API快速调整曝光

OpenCV的JavaCameraView底层其实是基于Camera1实现的,咱们可以直接拿到Camera实例来修改曝光参数,不用折腾Camera2,适合快速实现:

  1. 在你的CameraBridgeViewBase.CvCameraViewListener2回调里,获取Camera对象并修改曝光参数:
@Override
public void onCameraViewStarted(int width, int height) {
    // 强制转换为JavaCameraView,获取底层Camera实例
    Camera camera = ((JavaCameraView) mOpenCvCameraView).getCamera();
    if (camera != null) {
        try {
            Camera.Parameters params = camera.getParameters();
            
            // 先检查设备是否支持目标曝光模式,避免崩溃
            if (params.isExposureModeSupported(Camera.Parameters.EXPOSURE_MODE_AUTO)) {
                // 设置自动曝光(也可以选EXPOSURE_MODE_MANUAL等模式,看设备支持)
                params.setExposureMode(Camera.Parameters.EXPOSURE_MODE_AUTO);
            }
            
            // 如果设备支持曝光补偿,可以调整补偿值(范围由minExposureCompensation到maxExposureCompensation)
            if (params.getMinExposureCompensation() != params.getMaxExposureCompensation()) {
                // 比如设置+2档曝光补偿
                params.setExposureCompensation(2);
            }
            
            // 应用参数
            camera.setParameters(params);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  1. 注意事项:不同设备支持的曝光模式、补偿范围差异很大,一定要先做isExposureModeSupported这类判断,不要直接硬编码设置参数。
方案二:正确整合Camera2 + OpenCV(解决崩溃问题)

如果你需要Camera2的高级功能,那得把权限、相机状态、CaptureSession这些核心环节处理好,我给你梳理关键代码和避坑点:

1. 先搞定权限

确保在AndroidManifest.xml里添加相机权限,并且在Android 6.0+版本中动态申请Manifest.permission.CAMERA——没有权限的话Camera2会直接崩溃。

2. 初始化CameraManager并打开相机

private CameraManager mCameraManager;
private String mBackCameraId;
private CameraDevice mCameraDevice;

// 在Activity的onCreate里初始化CameraManager
private void initCameraManager() {
    mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    try {
        // 遍历相机列表,获取后置摄像头ID
        for (String cameraId : mCameraManager.getCameraIdList()) {
            CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(cameraId);
            Integer lensFacing = characteristics.get(CameraCharacteristics.LENS_FACING);
            if (lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
                mBackCameraId = cameraId;
                break;
            }
        }
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

// 打开相机(需确保已获取权限)
private void openCamera() {
    try {
        mCameraManager.openCamera(mBackCameraId, new CameraDevice.StateCallback() {
            @Override
            public void onOpened(@NonNull CameraDevice camera) {
                mCameraDevice = camera;
                // 相机打开成功后,创建CaptureSession并设置曝光参数
                createCaptureSession();
            }

            @Override
            public void onDisconnected(@NonNull CameraDevice camera) {
                camera.close();
                mCameraDevice = null;
            }

            @Override
            public void onError(@NonNull CameraDevice camera, int error) {
                camera.close();
                mCameraDevice = null;
                // 处理相机打开失败的情况
            }
        }, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

3. 创建CaptureSession并设置曝光参数

private void createCaptureSession() {
    try {
        // 用TextureView的Surface作为预览目标(也可以用OpenCV的SurfaceTexture)
        SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
        surfaceTexture.setDefaultBufferSize(1920, 1080); // 设置预览分辨率
        Surface previewSurface = new Surface(surfaceTexture);

        // 创建预览请求Builder
        CaptureRequest.Builder previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        previewRequestBuilder.addTarget(previewSurface);

        // 检查设备是否支持手动曝光控制
        CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(mBackCameraId);
        Boolean isManualAESupported = characteristics.get(CameraCharacteristics.CONTROL_AE_MODE_SUPPORTED) != null 
                && characteristics.get(CameraCharacteristics.CONTROL_AE_MODE_SUPPORTED).contains(CameraMetadata.CONTROL_AE_MODE_OFF);
        
        if (isManualAESupported) {
            // 关闭自动曝光,手动设置曝光时间和ISO
            previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF);
            previewRequestBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, 10000000L); // 10ms,单位是纳秒
            previewRequestBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, 400); // ISO值
        }

        // 创建CaptureSession
        mCameraDevice.createCaptureSession(Collections.singletonList(previewSurface), new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(@NonNull CameraCaptureSession session) {
                try {
                    // 启动重复预览请求
                    session.setRepeatingRequest(previewRequestBuilder.build(), null, null);
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                // 处理Session配置失败的情况
            }
        }, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

4. 将Camera2预览帧传给OpenCV处理

通过TextureView的SurfaceTextureListener获取帧数据,转换成OpenCV的Mat进行处理:

mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        // 纹理可用时打开相机
        openCamera();
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
        // 调整预览尺寸
    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        // 页面销毁时关闭相机
        if (mCameraDevice != null) {
            mCameraDevice.close();
            mCameraDevice = null;
        }
        return true;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
        // 获取当前帧的Bitmap,转换成Mat
        Bitmap frameBitmap = mTextureView.getBitmap();
        Mat frameMat = new Mat(frameBitmap.getHeight(), frameBitmap.getWidth(), CvType.CV_8UC4);
        Utils.bitmapToMat(frameBitmap, frameMat);

        // 在这里做OpenCV图像处理
        // ...

        // 处理完后可以将Mat转回Bitmap显示
        Utils.matToBitmap(frameMat, frameBitmap);
        mTextureView.setBitmap(frameBitmap);
        
        // 记得释放Mat,避免内存泄漏
        frameMat.release();
    }
});

关键避坑点

  • 相机生命周期:一定要在onPause中关闭相机,onResume中重新初始化,避免内存泄漏和崩溃。
  • 参数兼容性:不同设备支持的曝光时间、ISO范围差异很大,建议通过CameraCharacteristics获取设备支持的参数范围后再设置。
  • 线程处理:Camera2的回调都是在非主线程执行的,如果需要更新UI,要切换到主线程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:29:02