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

CameraX拍摄图片字节数组较Camera1大一倍左右的问题排查

问题

我们正将基于Camera1 API的旧版相机应用迁移至CameraX API,已为两者配置相同的目标分辨率(1600*1200)与JPEG画质(100)。但发现CameraX回调中获取的JPEG图片字节数组长度约为Camera1的2倍(前者约700KB,后者约400KB)。

以下为双方初始化及回调代码:

Camera1初始化及配置

camera = Camera.open(cameraId); //cameraId is the ID of back camera
parameters = camera.getParameters();

//set target resolution to 1600 * 1200
parameters.setPictureSize(1600, 1200);

//set JPEG quality to 100
parameters.setJpegQuality(100);

//set image format to JPEG
parameters.setImageFormat(ImageFormat.JPEG);

CameraX初始化及配置

//select back camera
CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;

//create preview
Preview preview = new Preview.Builder().build();

//create Image Capture use case, set JPEG quality to 100, target resolution to 1600*1200
ImageCapture imageCapture = new ImageCapture.Builder()
        .setJpegQuality(100)
        .setTargetResolution(new Size(1600, 1200))
        .build();

//by default output format will be JPEG, no need to explicitly set it.

//start camera 
provider.bindToLifecycle(
        this, cameraSelector, preview, imageCapture
);

Camera1拍照回调

camera.takePicture(shutterCallback, null, pictureTaken);

private Camera.PictureCallback pictureTaken = new Camera.PictureCallback() {
   @Override
   public void onPictureTaken(final byte[] data, final Camera camera){
         Log.d("Data byte array length", data.length); // for same image this is around 400000 bytes

       ///Logic to upload data to server    
   }
};

CameraX拍照回调

imageCapture.takePicture(ContextCompat.getMainExecutor(this), 
    new ImageCapture.OnImageCapturedCallback() {
             @Override
             public void onCaptureSuccess(@NonNull ImageProxy imageProxy) {
            //convert imageProxy to byte array, image format is JPEG.
            ByteBuffer buffer = imageProxy.getPlanes()[0].getBuffer();
            buffer.rewind();
            byte[] data = new byte[buffer.capacity()];
            buffer.get(data);

            Log.d("Data byte array length", data.length); //for same image this is around 700000 bytes
        }
});

请问CameraX返回的字节数组是否包含额外元数据?或是我们读取ImageProxy的方式有误?是否遗漏了相关配置?


分析与解决方案

1. 读取ImageProxy的方式存在错误

当前代码直接用buffer.capacity()创建字节数组,但CameraX的ByteBuffer因字节对齐要求,可能包含未使用的填充空间,实际有效数据长度应通过buffer.remaining()获取,而非capacity()。同时处理完ImageProxy后必须调用close()释放资源,避免相机泄漏。

修正后的CameraX回调代码:

imageCapture.takePicture(ContextCompat.getMainExecutor(this), 
    new ImageCapture.OnImageCapturedCallback() {
             @Override
             public void onCaptureSuccess(@NonNull ImageProxy imageProxy) {
                Image.Plane plane = imageProxy.getPlanes()[0];
                ByteBuffer buffer = plane.getBuffer();
                // 获取实际有效字节数
                int byteCount = buffer.remaining();
                byte[] data = new byte[byteCount];
                buffer.get(data);

                Log.d("Data byte array length", String.valueOf(data.length));
                // 必须关闭ImageProxy释放资源
                imageProxy.close();
             }
});

2. 明确JPEG压缩配置

setJpegQuality(100)在部分设备上可能存在兼容性问题,可改用setJpegCompressionQuality(参数为0-1的浮点数,1.0对应100%画质)明确配置:

ImageCapture imageCapture = new ImageCapture.Builder()
        .setJpegCompressionQuality(1.0f)
        .setTargetResolution(new Size(1600, 1200))
        .build();

3. 额外元数据的影响有限

CameraX默认会嵌入Exif元数据,但Camera1同样会添加基础元数据,这部分差异不会达到300KB量级。若修正读取方式后仍有明显差异,可导出两张图片到本地,对比Exif信息大小或用图片工具分析像素数据是否一致。

4. 确认实际输出分辨率

CameraX的setTargetResolution是目标值,硬件可能选择最接近的支持分辨率,可在回调中打印实际分辨率排查:

Log.d("Actual resolution", imageProxy.getWidth() + "x" + imageProxy.getHeight());

若实际分辨率高于1600*1200,会导致文件体积变大,此时可配合setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)或setTargetAspectRatio约束比例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 20:45:23