Android Camera2/CameraX相机输出被裁剪,如何显示完整视野?
相机预览与保存画面被裁剪问题解决方案
问题描述
开发的应用需展示相机预览并保存图片,但预览及保存的图片均仅显示相机可拍摄范围的一部分,画面明显被裁剪。目标环境为使用USB摄像头的嵌入式系统,在配置为webcam0的模拟器(连接笔记本USB摄像头)及生产环境中均出现该问题。尝试过Camera2与CameraX框架,问题一致。按照官方CameraX入门示例制作的应用也存在此问题:相机对准目标时仅显示一半画面,旋转模拟器后视野有所扩大但仍不完整,即使设置viewBinding.viewFinder.setScaleType(PreviewView.ScaleType.FIT_CENTER)也无效果。应用基于Java开发,现寻求配置图像捕获以显示相机完整视野的方案。
解决方案
1. 强制配置UseCase分辨率为摄像头支持的最大分辨率
CameraX默认会选择与预览View匹配的最优分辨率,但USB摄像头可能存在固定输出分辨率,导致画面被裁剪。需为Preview和ImageCapture设置摄像头支持的最大分辨率,同时保持两者分辨率一致,避免适配裁剪。
Kotlin代码修改(对应示例中的startCamera方法):
private fun startCamera() { val cameraProviderFuture = ProcessCameraProvider.getInstance(this) cameraProviderFuture.addListener({ val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get() val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA val cameraInfo = cameraProvider.getCameraInfo(cameraSelector) // 获取摄像头支持的所有分辨率,筛选出最大的那个 val supportedResolutions = cameraInfo.getSupportedResolutions(UseCaseGroup.UseCaseType.PREVIEW) val maxResolution = supportedResolutions.maxByOrNull { it.width * it.height } // 配置Preview UseCase val preview = Preview.Builder() .apply { maxResolution?.let { setTargetResolution(it) } // 设置目标旋转,匹配屏幕方向 setTargetRotation(viewBinding.viewFinder.display.rotation) } .build() .also { it.setSurfaceProvider(viewBinding.viewFinder.surfaceProvider) } // 配置ImageCapture UseCase,与Preview使用相同分辨率 imageCapture = ImageCapture.Builder() .apply { maxResolution?.let { setTargetResolution(it) } // 选择最小延迟模式,优先保留完整视野而非画质 setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) setTargetRotation(viewBinding.viewFinder.display.rotation) } .build() try { cameraProvider.unbindAll() cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture) } catch(exc: Exception) { Log.e(TAG, "Use case binding failed", exc) } }, ContextCompat.getMainExecutor(this)) }
Java对应实现:
private void startCamera() { ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this); cameraProviderFuture.addListener(() -> { try { ProcessCameraProvider cameraProvider = cameraProviderFuture.get(); CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA; CameraInfo cameraInfo = cameraProvider.getCameraInfo(cameraSelector); // 获取并筛选最大分辨率 List<Size> supportedResolutions = cameraInfo.getSupportedResolutions(UseCaseGroup.UseCaseType.PREVIEW); Size maxResolution = null; for (Size size : supportedResolutions) { if (maxResolution == null || size.getWidth() * size.getHeight() > maxResolution.getWidth() * maxResolution.getHeight()) { maxResolution = size; } } // 配置Preview Preview preview = new Preview.Builder() .apply(builder -> { if (maxResolution != null) { builder.setTargetResolution(maxResolution); } builder.setTargetRotation(viewBinding.viewFinder.getDisplay().getRotation()); }) .build(); preview.setSurfaceProvider(viewBinding.viewFinder.getSurfaceProvider()); // 配置ImageCapture ImageCapture imageCapture = new ImageCapture.Builder() .apply(builder -> { if (maxResolution != null) { builder.setTargetResolution(maxResolution); } builder.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY); builder.setTargetRotation(viewBinding.viewFinder.getDisplay().getRotation()); }) .build(); // 绑定生命周期与UseCases cameraProvider.unbindAll(); cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture); } catch (ExecutionException | InterruptedException e) { Log.e(TAG, "Use case binding failed", e); } }, ContextCompat.getMainExecutor(this)); }
2. 正确设置PreviewView的ScaleType与布局
确保PreviewView的布局占满容器,并在相机启动前设置ScaleType,避免Surface初始化后设置无效。
布局文件调整:
<androidx.camera.view.PreviewView android:id="@+id/viewFinder" android:layout_width="match_parent" android:layout_height="match_parent" />
Kotlin中提前设置ScaleType:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewBinding = ActivityMainBinding.inflate(layoutInflater) setContentView(viewBinding.root) // 先设置ScaleType再启动相机 viewBinding.viewFinder.scaleType = PreviewView.ScaleType.FIT_CENTER // 权限请求与相机启动逻辑... }
Java中提前设置ScaleType:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); viewBinding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(viewBinding.getRoot()); // 提前设置ScaleType viewBinding.viewFinder.setScaleType(PreviewView.ScaleType.FIT_CENTER); // 权限请求与相机启动逻辑... }
3. 验证摄像头支持的分辨率
通过日志打印所有支持的分辨率,确认是否存在合适的选项,也可根据需求选择与屏幕比例匹配的分辨率:
Kotlin日志打印:
val supportedResolutions = cameraInfo.getSupportedResolutions(UseCaseGroup.UseCaseType.PREVIEW) supportedResolutions.forEach { Log.d(TAG, "Supported resolution: ${it.width}x${it.height}") }
Java日志打印:
List<Size> supportedResolutions = cameraInfo.getSupportedResolutions(UseCaseGroup.UseCaseType.PREVIEW); for (Size size : supportedResolutions) { Log.d(TAG, "Supported resolution: " + size.getWidth() + "x" + size.getHeight()); }
内容的提问来源于stack exchange,提问作者steve
相关产品推荐
相关产品推荐

