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

Camera2 API横屏拍照画面出现多余区域的解决求助

解决横屏拍照后照片出现多余侧边的问题

你的问题核心在于:TextureView的预览矩阵变换只是改变了画面的显示方式,但拍照时相机仍然捕获的是传感器的完整画面,没有和预览显示的区域做对应适配,所以最终照片会出现多余的侧边区域。下面是具体的解决步骤和代码修改方案:

1. 匹配预览与拍照的尺寸比例

首先要确保拍照选择的JPEG输出尺寸和预览尺寸的比例一致,这样裁剪后不会出现变形或黑边。修改getPicture()里的尺寸选择逻辑:

// 替换原来的width/height选择代码
if (jpegSizes != null && jpegSizes.length > 0) {
    // 横屏预览时,我们把previewSize的height作为显示宽度,width作为显示高度,所以预览比例是 height/width
    float previewRatio = (float) previewSize.getHeight() / previewSize.getWidth();
    Size bestMatchSize = jpegSizes[0];
    float minRatioDifference = Float.MAX_VALUE;

    // 遍历所有支持的JPEG尺寸,找到和预览比例最接近的
    for (Size size : jpegSizes) {
        // 考虑照片旋转后的比例(因为横屏拍照后照片会旋转90/270度)
        float currentRatio = (float) size.getWidth() / size.getHeight();
        float rotatedRatio = (float) size.getHeight() / size.getWidth();
        
        float diff = Math.abs(currentRatio - previewRatio);
        float rotatedDiff = Math.abs(rotatedRatio - previewRatio);
        
        if (Math.min(diff, rotatedDiff) < minRatioDifference) {
            minRatioDifference = Math.min(diff, rotatedDiff);
            bestMatchSize = size;
        }
    }
    width = bestMatchSize.getWidth();
    height = bestMatchSize.getHeight();
}

2. 设置相机裁剪区域,捕获预览显示的部分

通过SCALER_CROP_REGION参数让相机只捕获和预览画面一致的区域,消除多余侧边。在getPicture()里创建capturebuilder后添加以下代码:

// 先获取相机传感器的有效区域
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraDevice.getId());
Rect sensorActiveRect = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
int screenRotation = getWindowManager().getDefaultDisplay().getRotation();
Rect cropRegion = null;

if (screenRotation == Surface.ROTATION_90 || screenRotation == Surface.ROTATION_270) {
    // 计算预览的宽高比(横屏时viewHeight是显示宽度,viewWidth是显示高度)
    float previewAspectRatio = (float) viewHeight / viewWidth;
    float sensorAspectRatio = (float) sensorActiveRect.width() / sensorActiveRect.height();

    int cropWidth, cropHeight;
    if (previewAspectRatio > sensorAspectRatio) {
        // 预览更宽,需要裁剪传感器的高度,保持宽度不变
        cropHeight = (int) (sensorActiveRect.width() / previewAspectRatio);
        cropWidth = sensorActiveRect.width();
    } else {
        // 预览更高,需要裁剪传感器的宽度,保持高度不变
        cropWidth = (int) (sensorActiveRect.height() * previewAspectRatio);
        cropHeight = sensorActiveRect.height();
    }

    // 计算裁剪区域的偏移,让裁剪区域居中
    int offsetX = (sensorActiveRect.width() - cropWidth) / 2;
    int offsetY = (sensorActiveRect.height() - cropHeight) / 2;
    cropRegion = new Rect(offsetX, offsetY, offsetX + cropWidth, offsetY + cropHeight);

    // 根据屏幕旋转方向,转换裁剪区域的坐标(适配传感器方向与预览的对应关系)
    if (screenRotation == Surface.ROTATION_90) {
        cropRegion = new Rect(
            sensorActiveRect.height() - cropRegion.bottom,
            cropRegion.left,
            sensorActiveRect.height() - cropRegion.top,
            cropRegion.right
        );
    } else { // ROTATION_270
        cropRegion = new Rect(
            cropRegion.top,
            sensorActiveRect.width() - cropRegion.right,
            cropRegion.bottom,
            sensorActiveRect.width() - cropRegion.left
        );
    }
} else {
    // 非横屏模式直接用传感器完整区域
    cropRegion = sensorActiveRect;
}

// 将裁剪区域设置到捕获请求
capturebuilder.set(CaptureRequest.SCALER_CROP_REGION, cropRegion);

注意:这里的viewWidth和viewHeight需要作为类的成员变量保存下来,在transformImage()方法调用时赋值(比如在onMeasure或transformImage里把这两个值存到成员变量中)。

3. 确认照片方向设置正确

检查你的ORIENTATION映射表是否正确,确保照片旋转方向和预览一致,示例正确的映射表如下:

private static final SparseIntArray ORIENTATION = new SparseIntArray();
static {
    ORIENTATION.append(Surface.ROTATION_0, 90);
    ORIENTATION.append(Surface.ROTATION_90, 0);
    ORIENTATION.append(Surface.ROTATION_180, 270);
    ORIENTATION.append(Surface.ROTATION_270, 180);
}

原理说明

通过以上修改,我们做到了两点:

  • 让拍照的输出尺寸比例和预览一致,避免拉伸
  • 告诉相机只捕获预览画面显示的那部分区域,这样最终的照片就不会有多余的侧边了

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:53:56