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

如何修改扫描类实现CameraX+Zxing/MLKit任意方向识别二维码与条形码(含MLKit扫描失效排查)

Fixing Your Barcode/QR Code Scanning Issues

Hey there! Let's break down and solve both of your scanning problems step by step.

1. Modifying ZXing's QRCodeImageAnalyzer for Any-Orientation Barcode Scanning

Your current implementation only targets QR codes because you're using QRCodeMultiReader. To support all common barcode formats (and any orientation), we'll switch to MultiFormatReader and add handling for image rotation (since CameraX delivers images in a fixed orientation that might not match the device's current rotation).

Here's the updated code:

public class QRCodeImageAnalyzer implements ImageAnalysis.Analyzer {
    private final QRCodeFoundListener listener;
    private final MultiFormatReader multiFormatReader;

    public QRCodeImageAnalyzer(QRCodeFoundListener listener) {
        this.listener = listener;
        // Configure the reader to support all common barcode formats
        multiFormatReader = new MultiFormatReader();
        Hashtable<DecodeHintType, Object> hints = new Hashtable<>();
        List<BarcodeFormat> formats = new ArrayList<>();
        formats.add(BarcodeFormat.QR_CODE);
        formats.add(BarcodeFormat.CODE_128);
        formats.add(BarcodeFormat.EAN_13);
        formats.add(BarcodeFormat.EAN_8);
        formats.add(BarcodeFormat.UPC_A);
        formats.add(BarcodeFormat.UPC_E);
        formats.add(BarcodeFormat.CODE_39);
        hints.put(DecodeHintType.POSSIBLE_FORMATS, formats);
        // Enable auto-rotation to detect barcodes in any orientation
        hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
        multiFormatReader.setHints(hints);
    }

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

        if (image.getFormat() == ImageFormat.YUV_420_888 || image.getFormat() == ImageFormat.YUV_422_888 || image.getFormat() == ImageFormat.YUV_444_888) {
            ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer();
            byte[] imageData = new byte[byteBuffer.capacity()];
            byteBuffer.get(imageData);

            int imageWidth = image.getWidth();
            int imageHeight = image.getHeight();

            // Adjust for image rotation from CameraX
            int rotationDegrees = imageProxy.getImageInfo().getRotationDegrees();
            PlanarYUVLuminanceSource source;
            if (rotationDegrees == 90 || rotationDegrees == 270) {
                // Rotate the luminance source to match device orientation
                source = new PlanarYUVLuminanceSource(
                        imageData, imageWidth, imageHeight,
                        0, 0, imageWidth, imageHeight,
                        false
                ).rotateCounterClockwise();
                // Swap width/height after rotation
                imageWidth = source.getWidth();
                imageHeight = source.getHeight();
            } else {
                source = new PlanarYUVLuminanceSource(
                        imageData, imageWidth, imageHeight,
                        0, 0, imageWidth, imageHeight,
                        false
                );
            }

            BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));

            try {
                Result result = multiFormatReader.decode(binaryBitmap);
                listener.onQRCodeFound(result.getText());
            } catch (FormatException | ChecksumException | NotFoundException e) {
                listener.qrCodeNotFound();
            }
        }
        imageProxy.close();
    }

    public interface QRCodeFoundListener {
        void onQRCodeFound(String code);
        void qrCodeNotFound();
    }
}

Key changes made:

  • Replaced QRCodeMultiReader with MultiFormatReader and configured it to support multiple barcode formats
  • Added TRY_HARDER hint to enable more aggressive scanning (helps with rotated or blurry codes)
  • Added logic to rotate the luminance source based on CameraX's image rotation degrees, ensuring barcodes in any orientation are detected
  • Added null check for the Image object to avoid crashes

2. Fixing ML Kit's QrCodeAnalyzer Detection Issues

Your ML Kit implementation has a few critical bugs that are preventing detection. Let's fix them:

import android.annotation.SuppressLint
import android.util.Log
import androidx.camera.core.ExperimentalGetImage
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import com.google.mlkit.vision.barcode.Barcode
import com.google.mlkit.vision.barcode.BarcodeScannerOptions
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.common.InputImage

@OptIn(ExperimentalGetImage::class)
class QrCodeAnalyzer(
    private val onQrCodesDetected: (qrCodes: List<Barcode>?) -> Unit
) : ImageAnalysis.Analyzer {
    private val TAG: String = "Kamran"
    // Create the scanner once as a member variable (avoid recreating it every frame)
    private val scanner = BarcodeScanning.getClient(
        BarcodeScannerOptions.Builder()
            .setBarcodeFormats(
                Barcode.FORMAT_QR_CODE,
                Barcode.FORMAT_EAN_8,
                Barcode.FORMAT_EAN_13
            )
            .build()
    )

    private fun rotationDegreesToFirebaseRotation(rotationDegrees: Int): Int {
        return when (rotationDegrees) {
            0 -> 0
            90 -> 1
            180 -> 2
            270 -> 3
            else -> 0 // Fallback to 0 if rotation is unexpected
        }
    }

    @SuppressLint("UnsafeOptInUsageError")
    override fun analyze(imageProxy: ImageProxy) {
        val mediaImage = imageProxy.image ?: run {
            imageProxy.close()
            return
        }

        // Use the Firebase-compatible rotation value, not raw degrees
        val rotation = rotationDegreesToFirebaseRotation(imageProxy.imageInfo.rotationDegrees)
        val inputImage = InputImage.fromMediaImage(mediaImage, rotation)

        scanner.process(inputImage)
            .addOnSuccessListener { barcodes ->
                Log.d(TAG, "Detected ${barcodes.size} barcodes")
                onQrCodesDetected(barcodes)
            }
            .addOnFailureListener { failure ->
                failure.printStackTrace()
                onQrCodesDetected(null)
            }
            .addOnCompleteListener {
                // Always close the imageProxy after processing, regardless of success/failure
                imageProxy.close()
                Log.d(TAG, "Processing complete, closed imageProxy")
            }
    }
}

Key fixes explained:

  • Reused Barcode Scanner: Created the BarcodeScanning client once as a member variable instead of recreating it every frame (this improves performance and avoids potential initialization issues)
  • Correct Rotation Value: Fixed the rotation parameter passed to InputImage.fromMediaImage()—we now use the Firebase-compatible rotation constant from your rotationDegreesToFirebaseRotation() method, not the raw rotation degrees
  • Improved Image Handling: Added a null check for mediaImage and moved the imageProxy.close() call to addOnCompleteListener (ensures the image is always closed after processing, even if detection fails)
  • Simplified Callbacks: Cleaned up the success listener to directly pass results to your callback, making the code more readable

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 11:17:30