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

如何在Camera2中修改预览及拍摄的宽高比?现有尝试未生效

Camera2 修改宽高比的正确实现方法

要在Camera2里修改拍摄/预览的宽高比,核心是必须使用相机硬件支持的分辨率参数,不能随意自定义尺寸,否则要么被系统强制 fallback 到默认宽高比,要么出现画面变形。以下是具体步骤:

1. 先获取相机支持的对应宽高比的分辨率

相机硬件只支持固定的分辨率组合,你需要先从相机特性中筛选出目标宽高比的可用尺寸:

// 获取CameraCharacteristics
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
// 获取流配置映射
StreamConfigurationMap configMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

// 筛选目标宽高比(比如1:1)的JPEG输出尺寸(用于拍照)
int targetAspectRatioNumerator = 1;
int targetAspectRatioDenominator = 1;
Size[] jpegSizes = configMap.getOutputSizes(ImageFormat.JPEG);
Size selectedJpegSize = null;
for (Size size : jpegSizes) {
    int width = size.getWidth();
    int height = size.getHeight();
    // 计算宽高比的最简形式,避免因尺寸不同导致判断错误
    int gcd = gcd(width, height);
    int ratioW = width / gcd;
    int ratioH = height / gcd;
    if (ratioW == targetAspectRatioNumerator && ratioH == targetAspectRatioDenominator) {
        selectedJpegSize = size;
        break; // 也可选择最合适的分辨率,比如最大的1:1尺寸
    }
}

// 同理筛选预览用的SurfaceTexture支持的尺寸(比如1:1)
Size[] previewSizes = configMap.getOutputSizes(SurfaceTexture.class);
Size selectedPreviewSize = null;
for (Size size : previewSizes) {
    int gcd = gcd(size.getWidth(), size.getHeight());
    int ratioW = size.getWidth() / gcd;
    int ratioH = size.getHeight() / gcd;
    if (ratioW == targetAspectRatioNumerator && ratioH == targetAspectRatioDenominator) {
        selectedPreviewSize = size;
        break;
    }
}

// 辅助计算最大公约数的方法
private int gcd(int a, int b) {
    while (b != 0) {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

2. 正确配置ImageReader(拍照用)

使用上面筛选出的selectedJpegSize来创建ImageReader,而不是自定义500x500:

ImageReader imageReader = ImageReader.newInstance(
    selectedJpegSize.getWidth(),
    selectedJpegSize.getHeight(),
    ImageFormat.JPEG,
    1
);

3. 避免预览画面变形

  • 第一步:用筛选出的selectedPreviewSize设置SurfaceTexture的默认缓冲尺寸:
surfaceTexture.setDefaultBufferSize(
    selectedPreviewSize.getWidth(),
    selectedPreviewSize.getHeight()
);
  • 第二步:确保预览控件(TextureView/SurfaceView)的布局宽高比和selectedPreviewSize的宽高比一致。比如在布局文件中设置android:layout_width="match_parent",android:layout_height="0dp",然后用app:layout_constraintDimensionRatio="1:1"来强制1:1比例;或者在代码中动态计算控件的高度,保持宽高比。

4. 配置CaptureRequest时的注意事项

  • 拍照的CaptureRequest需要将ImageReader的Surface加入输出目标:
CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(imageReader.getSurface());
// 其他参数设置(自动对焦、曝光等)
  • 如果需要对画面进行裁剪(比如相机原生没有目标宽高比的分辨率),可以通过设置CaptureRequest.SCALER_CROP_REGION来实现,但这会损失部分画面内容:
// 计算裁剪区域,以1:1为例
Rect sensorRect = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
int cropWidth = Math.min(sensorRect.width(), sensorRect.height());
int cropLeft = (sensorRect.width() - cropWidth) / 2;
int cropTop = (sensorRect.height() - cropWidth) / 2;
Rect cropRegion = new Rect(cropLeft, cropTop, cropLeft + cropWidth, cropTop + cropWidth);
captureBuilder.set(CaptureRequest.SCALER_CROP_REGION, cropRegion);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 04:15:40