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

CameraX前置摄像头ImageProxy旋转处理:固定输出竖屏Bitmap

解决CameraX ImageAnalysis固定竖屏Bitmap问题

核心思路

放弃不可靠的OrientationEventListener,直接利用CameraX提供的图像旋转信息,结合相机朝向(前置/后置)计算正确的旋转矩阵,确保Bitmap始终为竖屏。Activity已锁定竖屏,无需监听系统方向变化。

步骤1:修正ImageAnalysis初始化配置

不要固定setTargetRotation为Surface.ROTATION_0,而是绑定Activity窗口的实际旋转角度(竖屏模式下该值固定),让CameraX自动对齐图像与窗口方向:

// 获取当前窗口旋转角度(竖屏模式下固定为Surface.ROTATION_0)
int displayRotation = getWindowManager().getDefaultDisplay().getRotation();

imageAnalysis = new ImageAnalysis.Builder()
        .setTargetRotation(displayRotation)
        .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
        .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_YUV_420_888)
        .setTargetResolution(new Size(720, 480)) 
        .build();
imageAnalysis.setAnalyzer(getExecutor(), this);

步骤2:记录相机朝向

在绑定CameraX时记录当前使用的是前置还是后置摄像头,用于后续旋转计算:

// 全局变量存储相机朝向,默认后置
private int lensFacing = CameraSelector.LENS_FACING_BACK;

// 绑定相机时设置
CameraSelector cameraSelector = new CameraSelector.Builder()
        .requireLensFacing(lensFacing)
        .build();
// 后续绑定CameraProvider的代码...

步骤3:在analyze方法中计算旋转矩阵并转换Bitmap

利用imageProxy.getImageInfo().getRotationDegrees()获取图像与窗口方向的偏移角度,结合相机朝向处理旋转和镜像:

@Override
public void analyze(@NonNull ImageProxy imageProxy) {
    Image image = imageProxy.getImage();
    if (image == null) {
        imageProxy.close();
        return;
    }

    // 1. 将YUV_420_888格式图像转为Bitmap
    Bitmap rawBitmap = convertYuvToBitmap(image);

    // 2. 计算旋转矩阵
    int imageRotation = imageProxy.getImageInfo().getRotationDegrees();
    Matrix transformMatrix = new Matrix();

    if (lensFacing == CameraSelector.LENS_FACING_FRONT) {
        // 前置摄像头:旋转角度取反,同时添加水平镜像(前置图像默认是镜像的)
        int targetRotation = (360 - imageRotation) % 360;
        transformMatrix.postRotate(targetRotation);
        transformMatrix.postScale(-1, 1); // 水平翻转
    } else {
        // 后置摄像头:直接旋转对应角度即可
        transformMatrix.postRotate(imageRotation);
    }

    // 3. 应用矩阵得到竖屏Bitmap
    Bitmap portraitBitmap = Bitmap.createBitmap(
            rawBitmap, 0, 0, rawBitmap.getWidth(), rawBitmap.getHeight(),
            transformMatrix, true
    );

    // ------------------------------
    // 这里添加你的Bitmap后续处理逻辑
    // ------------------------------

    // 4. 资源回收,避免内存泄漏
    imageProxy.close();
    if (rawBitmap != portraitBitmap) {
        rawBitmap.recycle();
    }
}

步骤4:实现YUV转Bitmap工具方法

将CameraX返回的YUV_420_888格式图像转换为Bitmap:

private Bitmap convertYuvToBitmap(Image image) {
    Image.Plane[] planes = image.getPlanes();
    ByteBuffer yBuffer = planes[0].getBuffer();
    ByteBuffer uBuffer = planes[1].getBuffer();
    ByteBuffer vBuffer = planes[2].getBuffer();

    int ySize = yBuffer.remaining();
    int uSize = uBuffer.remaining();
    int vSize = vBuffer.remaining();

    // 将YUV_420_888转为NV21格式(YuvImage支持的格式)
    byte[] nv21Bytes = new byte[ySize + uSize + vSize];
    yBuffer.get(nv21Bytes, 0, ySize);
    vBuffer.get(nv21Bytes, ySize, vSize);
    uBuffer.get(nv21Bytes, ySize + vSize, uSize);

    YuvImage yuvImage = new YuvImage(
            nv21Bytes, ImageFormat.NV21,
            image.getWidth(), image.getHeight(), null
    );
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    yuvImage.compressToJpeg(new Rect(0, 0, image.getWidth(), image.getHeight()), 100, outputStream);
    byte[] jpegBytes = outputStream.toByteArray();

    return BitmapFactory.decodeByteArray(jpegBytes, 0, jpegBytes.length);
}

关键说明

  1. 为什么OrientationEventListener失效?
    Activity锁定竖屏后,系统不会触发方向变化事件,且物理方向与显示方向分离,该方法无法准确反映图像需要的旋转角度。

  2. 前置摄像头rotationDegrees固定270是正常的?
    前置摄像头的sensor方向与后置相反,当Activity锁定竖屏时,图像需要顺时针旋转270度才能对齐窗口方向,因此该值固定是符合预期的,只需通过矩阵处理旋转和镜像即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 21:05:21