在条形码 MLKit 条形码扫描仪上绘制边界框

问题描述 投票:0回答:1

我正在使用 google MLkit 条码扫描仪和cameraX api 开发一个简单的条码扫描仪。但边界框没有正确绘制在条形码上。我已经尝试了很多东西,例如变换坐标等,但没有用:

附上图片。

bounding boxes on barcode

代码:

条形码扫描仪活动

public class BarcodeScannerActivity extends AppCompatActivity {

    private static final int REQUEST_CAMERA_PERMISSION = 1001;
    private ExecutorService cameraExecutor;
    private static final String TAG = "BarcodeScannerActivity";

    private int currentOrientation = 0; // 0: portrait, 1: landscape

    private BarcodeOverlay barcodeOverlay;
    private PreviewView previewView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_barcode_scanner);
        previewView = findViewById(R.id.previewView);
        barcodeOverlay = findViewById(R.id.barcodeOverlay);
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
                == PackageManager.PERMISSION_GRANTED) {
            startCamera();
        } else {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
        }

        cameraExecutor = Executors.newSingleThreadExecutor();

        // Set up orientation listener
        OrientationEventListener orientationEventListener = new OrientationEventListener(this) {
            @Override
            public void onOrientationChanged(int orientation) {
                // Update orientation state based on the device's orientation
                if ((orientation >= 0 && orientation < 45) || (orientation >= 315)) {
                    currentOrientation = 0; // Portrait
                } else if (orientation >= 135 && orientation < 225) {
                    currentOrientation = 1; // Landscape
                }
            }
        };
        orientationEventListener.enable();

    }

    private void startCamera() {
        ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);

        cameraProviderFuture.addListener(() -> {
            try {
                ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
                bindCameraUseCases(cameraProvider);
            } catch (Exception e) {
                Log.e(TAG, "Camera provider failed.", e);
            }
        }, ContextCompat.getMainExecutor(this));
    }

    private void bindCameraUseCases(@NonNull ProcessCameraProvider cameraProvider) {
//        PreviewView previewView = findViewById(R.id.previewView);

        // Camera selector for the back camera
        CameraSelector cameraSelector = new CameraSelector.Builder()
                .requireLensFacing(CameraSelector.LENS_FACING_BACK)
                .build();

        // Preview use case
        Preview preview = new Preview.Builder().build();
        preview.setSurfaceProvider(previewView.getSurfaceProvider());

        // Image analysis use case
        ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                .build();

        BarcodeScannerOptions options = new BarcodeScannerOptions.Builder()
                .setBarcodeFormats(Barcode.FORMAT_ALL_FORMATS)
                .build();

        BarcodeScanner scanner = BarcodeScanning.getClient(options);

        imageAnalysis.setAnalyzer(cameraExecutor, image -> processImageProxy(scanner, image));

        // Bind to lifecycle
        cameraProvider.bindToLifecycle(
                (LifecycleOwner) this,
                cameraSelector,
                preview,
                imageAnalysis
        );
    }

    private void processImageProxy(BarcodeScanner scanner, ImageProxy imageProxy) {
        @SuppressWarnings("UnsafeOptInUsageError")
        InputImage image = InputImage.fromMediaImage(imageProxy.getImage(), imageProxy.getImageInfo().getRotationDegrees());

        scanner.process(image)
                .addOnSuccessListener(barcodes -> {
                    if (!barcodes.isEmpty()) {
                        // Map all barcode bounding boxes to the view coordinates
                        List<Rect> mappedBoundingBoxes = new ArrayList<>();
                        for (com.google.mlkit.vision.barcode.common.Barcode barcode : barcodes) {
                            Rect boundingBox = barcode.getBoundingBox();
                            if (boundingBox != null) {
                                Rect rect = mapBoundingBoxToView(boundingBox, imageProxy);
                                mappedBoundingBoxes.add(rect);
                            }
                        }

                        // Update the overlay with all mapped bounding boxes
                        barcodeOverlay.updateBarcodes(mappedBoundingBoxes);
                    }
                })
                .addOnFailureListener(e -> Log.e(TAG, "Barcode detection failed.", e))
                .addOnCompleteListener(task -> imageProxy.close());
    }


    public Rect mapBoundingBoxToView(Rect boundingBox, ImageProxy imageProxy) {

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


        int viewWidth = previewView.getWidth();
        int viewHeight = previewView.getHeight();


        float scaleX = (float) viewWidth / imageHeight;
        float scaleY = (float) viewHeight / imageWidth;


        float offsetX = (viewWidth - imageHeight * scaleX) / 2;
        float offsetY = (viewHeight - imageWidth * scaleY) / 2;

        int left = (int) (boundingBox.left * scaleX + offsetX);
        int top = (int) (boundingBox.top * scaleY + offsetY);
        int right = (int) (boundingBox.right * scaleX + offsetX);
        int bottom = (int) (boundingBox.bottom * scaleY + offsetY);

        return new Rect(left, top, right, bottom);
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        cameraExecutor.shutdown();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CAMERA_PERMISSION) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                startCamera();
            } else {
                Toast.makeText(this, "Camera permission denied", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

BarcodeOverlay 类

public class BarcodeOverlay extends View {

    private final Paint paint = new Paint();
    private List<Rect> barcodeRects = new ArrayList<>();

    public BarcodeOverlay(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5f);
    }

    public void updateBarcodes(List<Rect> newBarcodeRects) {
        barcodeRects = newBarcodeRects;
        invalidate(); // Trigger a redraw
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (Rect rect : barcodeRects) {
            canvas.drawRect(rect, paint);
        }
    }
}
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.camera.view.PreviewView
        android:id="@+id/previewView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center" />

    <com.invenits.barcodescannerinvenits.BarcodeOverlay
        android:id="@+id/barcodeOverlay"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

请帮忙!

我想扫描条形码并绘制边界框,但我的框没有正确绘制在条形码上。

java android barcode barcode-scanner google-mlkit
1个回答
0
投票

相机馈送和叠加之间的坐标映射可能很棘手。虽然您可以调整

mapBoundingBoxToView
逻辑,但如果这是用于生产应用程序,您可能需要考虑使用商业解决方案。例如,Scanbot SDK 提供开箱即用的边界框覆盖,这可以节省您调试这些坐标映射问题的时间。您可以在 https://docs.scanbot.io/barcode-scanner-sdk/android/barcode-scanner/ready-to-use-ui/#ar-overlay 查看他们的文档(完整披露:我与扫描机器人)。

© www.soinside.com 2019 - 2024. All rights reserved.