如何在ARCore应用中获取与屏幕比例一致的未增强相机图像?
解决ARCore获取与屏幕比例一致的YUV图像问题
方法1:配置ARCore使用16:9的相机分辨率
acquireCameraImage返回的是相机原生传感器帧,默认多为4:3比例。你可以通过指定相机配置,让ARCore直接输出16:9的YUV帧:
- 先获取当前Session支持的所有相机配置:
List<CameraConfig> supportedConfigs = session.getSupportedCameraConfigs(CameraConfigFilter.create(session)); - 从列表中筛选16:9比例的配置,优先选分辨率最接近屏幕的选项:
CameraConfig targetConfig = null; final float targetAspectRatio = 16f/9f; DisplayMetrics metrics = getResources().getDisplayMetrics(); for (CameraConfig config : supportedConfigs) { Size size = config.getImageSize(); float aspectRatio = (float) size.getWidth() / size.getHeight(); // 匹配16:9比例(允许微小误差) if (Math.abs(aspectRatio - targetAspectRatio) < 0.01f) { // 优先选择分辨率更高的配置 if (targetConfig == null || size.getWidth() * size.getHeight() > targetConfig.getImageSize().getWidth() * targetConfig.getImageSize().getHeight()) { targetConfig = config; } } } - 找到符合条件的配置后,设置给Session:
if (targetConfig != null) { session.setCameraConfiguration(targetConfig); }
之后调用acquireCameraImage就能拿到16:9比例的YUV帧,再缩放至屏幕分辨率即可用于非实时处理。
方法2:裁剪4:3的YUV图像为16:9比例
如果设备不支持16:9的相机配置,可对获取到的4:3 YUV帧进行裁剪:
- 计算裁剪区域:以640x480的4:3图像为例,16:9的目标高度为
640 * 9 / 16 = 360,上下各裁剪(480-360)/2 = 60像素; - 针对YUV_420_888的三个平面分别裁剪:Y平面是全分辨率,直接裁剪对应区域;U、V平面是半分辨率,裁剪区域的坐标和尺寸需同步除以2;
- 将裁剪后的YUV数据转换为Bitmap或保存为图像文件,再缩放至屏幕分辨率。
示例裁剪代码:
CameraImage image = frame.acquireCameraImage(); try { Image.Plane yPlane = image.getPlanes()[0]; Image.Plane uPlane = image.getPlanes()[1]; Image.Plane vPlane = image.getPlanes()[2]; int originalWidth = image.getWidth(); int originalHeight = image.getHeight(); int targetHeight = originalWidth * 9 / 16; int cropTop = (originalHeight - targetHeight) / 2; // 处理Y平面 ByteBuffer yBuffer = yPlane.getBuffer(); yBuffer.position(cropTop * yPlane.getRowStride()); byte[] yData = new byte[originalWidth * targetHeight]; for (int y = 0; y < targetHeight; y++) { yBuffer.get(yData, y * originalWidth, originalWidth); yBuffer.position(yBuffer.position() + (yPlane.getRowStride() - originalWidth)); } // 处理U平面(半分辨率) int uCropTop = cropTop / 2; int uTargetHeight = targetHeight / 2; ByteBuffer uBuffer = uPlane.getBuffer(); uBuffer.position(uCropTop * uPlane.getRowStride()); byte[] uData = new byte[(originalWidth / 2) * uTargetHeight]; for (int y = 0; y < uTargetHeight; y++) { uBuffer.get(uData, y * (originalWidth / 2), originalWidth / 2); uBuffer.position(uBuffer.position() + (uPlane.getRowStride() - (originalWidth / 2))); } // 处理V平面(同U平面逻辑) ByteBuffer vBuffer = vPlane.getBuffer(); vBuffer.position(uCropTop * vPlane.getRowStride()); byte[] vData = new byte[(originalWidth / 2) * uTargetHeight]; for (int y = 0; y < uTargetHeight; y++) { vBuffer.get(vData, y * (originalWidth / 2), originalWidth / 2); vBuffer.position(vBuffer.position() + (vPlane.getRowStride() - (originalWidth / 2))); } // 后续将yData、uData、vData转换为所需格式 } finally { image.close(); }
注意事项
- 优先用方法1,直接获取对应比例的帧无需额外裁剪,效率更高;
- 非实时场景下,裁剪和缩放的性能开销可忽略;
- 转换YUV到RGB时,需注意色彩空间匹配,避免偏色。
内容的提问来源于stack exchange,提问作者Csaba Toth
相关产品推荐
相关产品推荐

