You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用ML Kit与CameraX调用前置摄像头读取二维码时识别困难且返回错误值的问题求助

ML Kit与CameraX调用前置摄像头读取二维码时识别困难且返回错误值的问题求助

您好,我之前也碰到过一模一样的前置摄像头扫码异常问题,核心原因其实很好理解:前置摄像头捕获的图像是水平镜像的,而ML Kit的条码扫描器默认是针对正常方向(非镜像)的图像做识别优化的,这种镜像差异就导致了识别困难,甚至会解码出错误内容。下面我来帮您拆解问题并给出可直接复用的修改方案:

问题根源拆解

后置摄像头拍出来的图像和我们肉眼看到的场景方向一致,ML Kit可以直接正常处理;但前置摄像头为了贴合用户自拍的使用习惯,会自动把图像做水平镜像翻转,这种翻转后的图像不符合ML Kit条码扫描的默认预期,自然就会出现识别失败、解码错误的情况。

具体解决步骤

我们需要在把图像传给ML Kit之前,对前置摄像头的图像做水平镜像还原,再交给扫描器处理。结合您的现有代码,具体修改如下:

1. 切换到前置摄像头

先确认您的CameraSelector已指定为前置(您当前代码里写的是DEFAULT_BACK_CAMERA,测试前置时一定要修改):

CameraSelector cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA;

2. 标记当前摄像头类型

在绑定相机生命周期时,先获取相机信息判断是否为前置,后续用来针对性处理镜像:

// 绑定相机前先获取摄像头信息
Camera camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis);
// 把这个变量声明为Activity的成员变量,方便后续分析器中访问
boolean isFrontCamera = camera.getCameraInfo().getLensFacing() == CameraSelector.LENS_FACING_FRONT;

3. 处理前置摄像头的镜像图像

ImageAnalysis的分析器中,对前置摄像头的图像做水平翻转后,再创建InputImage传给ML Kit:

imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(this), imageProxy -> {
    Image mediaImage = imageProxy.getImage();
    if (mediaImage != null && !isScanned) {
        InputImage image;
        if (isFrontCamera) {
            // 将MediaImage转成Bitmap并做水平镜像还原
            Bitmap originalBitmap = toBitmap(mediaImage);
            Matrix flipMatrix = new Matrix();
            flipMatrix.postScale(-1, 1); // 水平镜像翻转
            Bitmap flippedBitmap = Bitmap.createBitmap(originalBitmap, 0, 0, originalBitmap.getWidth(), originalBitmap.getHeight(), flipMatrix, true);
            // 用翻转后的Bitmap创建InputImage,并传入正确的旋转角度
            image = InputImage.fromBitmap(flippedBitmap, imageProxy.getImageInfo().getRotationDegrees());
            // 及时释放Bitmap避免内存泄漏
            originalBitmap.recycle();
            flippedBitmap.recycle();
        } else {
            // 后置摄像头直接按原有逻辑处理
            image = InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees());
        }

        scanner.process(image)
                .addOnSuccessListener(barcodes -> {
                    if (!barcodes.isEmpty()) {
                        isScanned = true;
                        String rawValue = barcodes.get(0).getRawValue();
                        Log.d("MLKit", "Scanned: " + rawValue);
                        Toast.makeText(this, "Scanned: " + rawValue, Toast.LENGTH_SHORT).show();
                        scanner.close();
                        startActivity(new Intent(this, DirectionActivity.class));
                        finish();
                    }
                })
                .addOnFailureListener(e -> Log.e("MLKit", "Scan failed: " + e.getMessage()))
                .addOnCompleteListener(task -> imageProxy.close());
    } else {
        imageProxy.close();
    }
});

4. 补充MediaImage转Bitmap工具方法

需要在您的Activity中添加一个将系统Image对象转为Bitmap的工具方法:

private Bitmap toBitmap(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();

    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[] imageBytes = outputStream.toByteArray();
    return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
}

额外优化小建议

  • 记得在AndroidManifest.xml中声明前置摄像头的使用权限:<uses-feature android:name="android.hardware.camera.front" android:required="false" />
  • 图像转Bitmap会有一定性能开销,如果您的设备系统版本较高,可以考虑使用更高效的硬件加速图像转换方式
  • 识别成功后,除了关闭scanner,还可以调用imageAnalysis.clearAnalyzer()停止分析,避免不必要的资源消耗

这样修改后,前置摄像头的镜像图像会被还原成正常方向,ML Kit就能像处理后置摄像头图像一样,精准识别二维码了。

内容来源于stack exchange

火山引擎 最新活动