Android平台修改相机曝光方法及OpenCV+Camera2崩溃问题解决
嘿,我来帮你搞定Android上OpenCV结合相机调整曝光的问题!你之前用Camera2时程序崩溃,大概率是相机生命周期、权限处理或者Surface的逻辑没捋顺,我给你两个可行的方案,一步步来:
方案一:用OpenCV自带的CameraView + Camera1 API快速调整曝光
OpenCV的JavaCameraView底层其实是基于Camera1实现的,咱们可以直接拿到Camera实例来修改曝光参数,不用折腾Camera2,适合快速实现:
- 在你的
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(); } } }
- 注意事项:不同设备支持的曝光模式、补偿范围差异很大,一定要先做
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
相关产品推荐
相关产品推荐

