MLKit Android 倒置数据矩阵扫描时间过长

MLKit Android inverted datamatrix scan takes too long time

提问人:Anatoliy Voronin 提问时间:8/24/2023 更新时间:10/2/2023 访问量:80

问:

首先 - 对不起我的英语:)

我在项目中使用 MLKit 扫描条形码(数据矩阵)。但是我在黑色背景上有白色数据矩阵,就像这张 Datamatrix 图像一样

在我的代码中,我在扫描之前裁剪图像。在此之后,我反转了这张裁剪图像的颜色,并将这张图片放入扫描仪。

我遇到的问题是扫描仪无法定义具有良好图像的代码。它正在经历一些分析,并从第 6-10 次分析中阅读它。

例如。这是数据矩阵扫描的屏幕截图。中间有方形图像。imageView被裁剪和反转的图像进入扫描仪。但它并没有给出快速的结果。我需要等待 3-5 秒才能从那里获取代码。而它的扫描扫描仪可在 3 秒内进行 4-1-1 次分析。

我有这个 MLKit 扫描的 iOS 应用程序替代品。它执行相同的步骤(裁剪和反转),但 iOS 应用程序正在扫描此步骤 1 秒,可能更快。

我尝试了一切方法来加快扫描速度,但没有任何帮助。请帮忙

我把我的类放在 git 中。所以我在这里放了短代码。

这是我的带有初始化的 ScanFragment.java

    private CamcorderProfile camProfile;
    private ListenableFuture cameraProviderFuture;
    private ExecutorService cameraExecutor;
    private PreviewView previewView;
    private ImageAnalyser analyser;
    private androidx.camera.core.Camera camera;

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_scan_ui, container, false);
        previewView = view.findViewById(R.id.previewview);

        requireActivity().getWindow().setFlags(1024, 1024);
        cameraExecutor = Executors.newSingleThreadExecutor();
        cameraProviderFuture = ProcessCameraProvider.getInstance(requireActivity());
        try {
            processCameraProvider = (ProcessCameraProvider) cameraProviderFuture.get();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        cameraProviderFuture.addListener(() -> {
                if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) != (PackageManager.PERMISSION_GRANTED)) {
                    requestPermissionLauncher.launch(Manifest.permission.CAMERA);
                } else {
                    bindpreview();
                }
        }, ContextCompat.getMainExecutor(requireContext()));

        return view;
    }

    private void setTabDatamatrix() {
        camProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_1080P);
        BarcodeScannerOptions barcodeScanOptions = new BarcodeScannerOptions.Builder()
                .setBarcodeFormats(
                        Barcode.FORMAT_DATA_MATRIX
                ).build();
        if (analyser == null)
            analyser = new ImageAnalyser(getParentFragmentManager(), barcodeScanOptions, this, this);
        else
            analyser.setScanOptions(barcodeScanOptions, ScanType.DATAMATRIX);
        scanType = ScanType.DATAMATRIX;

        hideProgressBar();
        bottomBar.performShow();
        showCursor();
        setTabBackground(ivScanTabDatamatrix);
        tvScanHintMoveCamera.setVisibility(View.VISIBLE);
        tvScanHintMoveCamera.setText(Utils.getString(R.string.scanner_tab_mark_hint, requireContext()));
        tooltip.show(ivScanTabDatamatrix, Utils.getString(R.string.scanner_tab_mark, requireContext()));

        showHint(coordinatorLayout, ScanHintView.ScanHintType.MARK);
        bindpreview();
    }

    private void bindpreview() {
        Preview preview = new Preview.Builder().build();
        CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build();
        preview.setSurfaceProvider(previewView.getSurfaceProvider());
        ImageCapture imageCapture = new ImageCapture.Builder().build();
        ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
                .setTargetResolution(new Size(camProfile.videoFrameHeight, camProfile.videoFrameWidth))  //480 320
                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                .build();
        imageAnalysis.setAnalyzer(cameraExecutor, analyser);
        processCameraProvider.unbindAll();
        camera = processCameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture, imageAnalysis);
    }

这是 ImageAnalyser .java类。我在这里分析扫描的图像。

    @Override
    public void analyze(@NonNull ImageProxy image) {
        scanbarcode(image);
    }

    private void scanbarcode(ImageProxy image) {
        @SuppressLint("UnsafeOptInUsageError") Bitmap bitmap = BitmapUtils.getBitmap(image);
        assert bitmap != null;
        Bitmap cropped = Bitmap.createBitmap(bitmap, bitmap.getWidth() / 4, (bitmap.getHeight() / 2) - bitmap.getWidth() / 4, bitmap.getWidth() / 2, bitmap.getWidth() / 2);
        Bitmap inverted = inverseBitmapColors(cropped);
        InputImage invertedImage = InputImage.fromBitmap(inverted, image.getImageInfo().getRotationDegrees());
        BarcodeScanner scanner = BarcodeScanning.getClient(barcodeScannerOptions);
        scanner.process(invertedImage)
                .addOnSuccessListener(barcodes -> {
                    if (active) {
                        readerBarcodeData(barcodes);
                    }
                })
                .addOnFailureListener(e -> {
                })
                .addOnCompleteListener(task -> image.close());

    }

    private Bitmap inverseBitmapColors(Bitmap bitmap) {
        Bitmap invBitmap = bitmap.copy(bitmap.getConfig(), true);
        for (int i = 0; i < invBitmap.getWidth(); i++) {
            for (int j = 0; j < invBitmap.getHeight(); j++) {
                invBitmap.setPixel(i, j, invBitmap.getPixel(i, j) ^ 0x00ffffff);
            }
        }
        return invBitmap;
    }

我尝试了许多裁剪、吟唱质量的操作,尝试了许多反转颜色的方法。 我希望它会在 1 秒或更快的时间内像 iOS 应用程序一样扫描。不是 3-5 秒。

Java Android 条码 Google-MLKit 数据矩阵

评论

0赞 Anatoliy Voronin 9/7/2023
解决了。我收到此日志警告。“”“ML Kit 检测到您似乎将相机帧作为位图对象传递给检测器。这是低效的。请使用 camera2 API 的YUV_420_888格式或(旧版)相机 API 的 NV21 格式,并将字节数组直接传递给 ML Kit。然后只需反转此字节数组中的颜色并像这样使用 InputImage inputImage inputImage = InputImage.fromByteArray(.....如果有人需要更多信息:请告诉我。我会用代码更好地解释它。
0赞 Véger Lóránd 9/11/2023
嗨,我最近遇到了同样的问题,您介意分享最终解决方案吗?
1赞 Anatoliy Voronin 10/2/2023
@VégerLóránd发布了我的解决方案

答:

1赞 Anatoliy Voronin 10/2/2023 #1

我是这样做的。InputImage

@SuppressLint("UnsafeOptInUsageError") Image image = imageProxy.getImage();
        assert image != null;
        byte[] imageByteArray = YUV_420_888toNV21(image);
        int size = min(image.getWidth(), image.getHeight()) / 2;
        byte[] cropped = cropNV21(imageByteArray, image.getWidth(), image.getHeight(), (image.getWidth() - size) / 2, (image.getHeight() - size) / 2, size, size);
        assert cropped != null;
        InputImage inputImage = InputImage.fromByteArray(cropped, size, size, imageProxy.getImageInfo().getRotationDegrees(), InputImage.IMAGE_FORMAT_NV21);
        InputImage invertedImage = InputImage.fromByteArray(inverse(cropped), size, size, imageProxy.getImageInfo().getRotationDegrees(), InputImage.IMAGE_FORMAT_NV21);
        

private byte[] YUV_420_888toNV21(Image image) {
        byte[] nv21;
        ByteBuffer yBuffer = image.getPlanes()[0].getBuffer();
        ByteBuffer uBuffer = image.getPlanes()[1].getBuffer();
        ByteBuffer vBuffer = image.getPlanes()[2].getBuffer();

        int ySize = yBuffer.remaining();
        int uSize = uBuffer.remaining();
        int vSize = vBuffer.remaining();

        nv21 = new byte[ySize + uSize + vSize];

        //U and V are swapped
        yBuffer.get(nv21, 0, ySize);
        vBuffer.get(nv21, ySize, vSize);
        uBuffer.get(nv21, ySize + vSize, uSize);

        return nv21;
    }

    private byte[] inverse(byte[] bytes) {
        byte[] inverted = new byte[bytes.length];
        for (int i = 0; i < bytes.length; i++) {
            inverted[i] = (byte) (bytes[i] ^ 0xff);
        }
        return inverted;
    }

    private byte[] cropNV21(byte[] src, int width, int height, int left, int top, int clip_w, int clip_h) {
        if (left > width || top > height) {
            return null;
        }
        // Take the couple
        int x = left * 2 / 2, y = top * 2 / 2;
        int w = clip_w * 2 / 2, h = clip_h * 2 / 2;
        int y_unit = w * h;
        int src_unit = width * height;
        int uv = y_unit >> 1;
        byte[] nData = new byte[y_unit + uv];


        for (int i = y, len_i = y + h; i < len_i; i++) {
            for (int j = x, len_j = x + w; j < len_j; j++) {
                nData[(i - y) * w + j - x] = src[i * width + j];
                nData[y_unit + ((i - y) / 2) * w + j - x] = src[src_unit + i / 2 * width + j];
            }
        }

        return nData;
    }