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

