ML Kit条码扫描报错:Invalid image data size 问题求助
这个错误的根源其实是你对Camera2输出的图像数据和Firebase ML Kit要求的输入格式理解有偏差,咱们一步步拆解问题:
错误原因分析
你现在的流程有两个关键错误:
- Camera2图像数据处理错误:你从
ImageReader获取的是YUV_420_888格式的图像,直接取planes[0]的buffer(这只是Y通道的灰度数据,不是完整图像),然后把它写成.jpg文件——这个文件根本不是合法的JPEG,只是原始Y通道的字节流。 - Firebase ML Kit输入格式不匹配:你读回这个文件的字节流,告诉Firebase这是
IMAGE_FORMAT_NV21格式的原始图像数据,但NV21格式需要的字节数是宽×高×3/2,而你传入的字节数只有宽×高(Y通道大小),大小完全不匹配,自然抛出Invalid image data size错误。
快速解决方法:直接用Firebase加载本地图片文件
最简单的修复方式是跳过手动处理字节数组和metadata的步骤,直接让Firebase从你保存的图片Uri加载图像——Firebase会自动处理编码、旋转、尺寸适配等问题,代码会简洁很多:
在第二个Activity中,替换你的字节数组处理代码为:
// 直接从Uri创建FirebaseVisionImage,无需手动处理字节和metadata FirebaseVisionImage image; try { image = FirebaseVisionImage.fromFilePath(getApplicationContext(), uri); } catch (IOException e) { Log.e(TAG, "加载图片失败: " + e.getMessage()); return; } // 剩下的检测代码保持不变 FirebaseVisionBarcodeDetectorOptions options = new FirebaseVisionBarcodeDetectorOptions.Builder() .setBarcodeFormats(FirebaseVisionBarcode.FORMAT_QR_CODE, FirebaseVisionBarcode.FORMAT_DATA_MATRIX) .build(); FirebaseVisionBarcodeDetector detector = FirebaseVision.getInstance().getVisionBarcodeDetector(options); Task<List<FirebaseVisionBarcode>> result = detector.detectInImage(image); // 处理检测结果...
同时,你需要修正第一个Activity中保存图片的代码——之前保存的不是合法JPEG,应该正确将Camera2的YUV图像转成JPEG再保存,或者直接让ImageReader输出JPEG格式:
修正Camera2的ImageReader配置
创建ImageReader时,指定格式为ImageFormat.JPEG,这样获取的Image就是已经编码好的JPEG数据,直接保存即可:
// 创建ImageReader时设置格式为JPEG,尺寸可以根据需求调整(比如1920×1080) ImageReader imageReader = ImageReader.newInstance(1920, 1080, ImageFormat.JPEG, 1); imageReader.setOnImageAvailableListener(onImageAvailableListener, backgroundHandler);
然后修正onImageAvailableListener中的保存逻辑:
private final ImageReader.OnImageAvailableListener onImageAvailableListener = new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader imageReader) { try { storeData.acquire(); Image resultImg = imageReader.acquireNextImage(); if (resultImg == null) { storeData.release(); return; } // JPEG格式的Image直接取planes[0]的buffer就是完整的JPEG字节流 ByteBuffer buffer = resultImg.getPlanes()[0].getBuffer(); byte[] data = new byte[buffer.remaining()]; buffer.get(data); String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); File media = new File(mediaFile.getPath() + File.separator + "IMG_" + timeStamp + ".jpg"); try (FileOutputStream fos = new FileOutputStream(media)) { fos.write(data); uri = Uri.fromFile(media); } catch (IOException e) { Log.e(TAG, "保存图片失败: " + e.getMessage()); } finally { resultImg.close(); } } catch (InterruptedException e) { Log.e(TAG, e.getMessage()); } finally { storeData.release(); } } };
可选方案:处理原始YUV数据(如果需要)
如果你必须使用原始YUV数据传递给Firebase,需要将Camera2的YUV_420_888格式正确转换为NV21格式,再传递字节数组和对应的metadata:
YUV_420_888转NV21的工具方法
private byte[] yuv420ToNv21(Image image) { Image.Plane yPlane = image.getPlanes()[0]; Image.Plane uPlane = image.getPlanes()[1]; Image.Plane vPlane = image.getPlanes()[2]; byte[] yBuffer = new byte[yPlane.getBuffer().remaining()]; byte[] uBuffer = new byte[uPlane.getBuffer().remaining()]; byte[] vBuffer = new byte[vPlane.getBuffer().remaining()]; yPlane.getBuffer().get(yBuffer); uPlane.getBuffer().get(uBuffer); vPlane.getBuffer().get(vBuffer); // NV21格式是Y通道 + VU交织通道 byte[] nv21 = new byte[yBuffer.length + uBuffer.length + vBuffer.length]; System.arraycopy(yBuffer, 0, nv21, 0, yBuffer.length); // 因为YUV_420_888的U和V通道是平面的,需要交织成VU int uvIndex = yBuffer.length; for (int i = 0; i < uBuffer.length; i++) { nv21[uvIndex++] = vBuffer[i]; nv21[uvIndex++] = uBuffer[i]; } return nv21; }
然后在第一个Activity中,传递转换后的NV21字节数组(或者保存为原始文件),并确保metadata中的格式和尺寸对应,这样Firebase就能正确处理了。
关于图片尺寸的补充
Firebase ML Kit对图片尺寸没有严格的上限,但过大的图片会增加检测时间和内存占用。如果你想捕获更小的图片,只需要在创建ImageReader时指定更小的尺寸即可(比如1280×720,甚至640×480),这样获取的图像数据量更小,检测速度也更快。
内容的提问来源于stack exchange,提问作者Michael

