Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pause barcode scanning in android ML-kit when using ByteBuffer from SurfaceView

Context

I'm using the Android firebase-ml-vision to scan barcodes using SurfaceView with continuous ByteBuffer of pictures frames. I used the ML kit quickstart project as a starting point and it works great.

The objective of my project is to recognise the product associated with the barcode and add it to the list of scanned items.

Problem

Once the camera focuses, the barcode processor would detect the same barcode multiple times, so you would scan 20 rather than 1 barcode in a second.

This is javadoc from CamereSource.FrameProcessingRunnable.run

 * As long as the processing thread is active, this executes detection on frames continuously.
 * The next pending frame is either immediately available or hasn't been received yet. Once it
 * is available, we transfer the frame info to local variables and run detection on that frame.
 * It immediately loops back for the next frame without pausing.

I have tried to add a "paused" check into the FrameProcessingRunnable, but I was still getting the same barcode recognised at least twice, as the next frame/frames were already being fed in for detection:

private class FrameProcessingRunnable implements Runnable {
private volatile boolean paused = false;
.
.
.
public void pause() {
  synchronized (lock) {
    this.paused = true;
    lock.notifyAll();
  }
}

public void resume() {
  synchronized (lock) {
    this.paused = false;
    lock.notifyAll();
  }
}

public void run() {
.
.
.
synchronized (processorLock) {
        if (!paused) {
          Log.d(TAG, "Process an image");
          frameProcessor.process(...

Solution using stop & start

As I couldn't get it to pause, I opted for stop & start when a barcode is detected from the buffer:

private CameraSourcePreview preview;
public void pauseImageProcessing() {
  preview.stop();
  try {
      preview.start(cameraSource, graphicOverlay);
  } catch (IOException e) {
  }
}

This works, but there is about 1 second delay before it starts up again, camera starts focusing and the next barcode can be detected. No doubt this approach also consumes unnecessary resources. You may say this is fine, but in this video you'll see the difference between the camera scanner with Stop&Start and a Bluetooth scanner:

Better approach

I'm looking for a solution that would simply discard any frames immediately after the one with successful detection and start again, but so far I have failed. I'm using 20 frames per second.

VissionProcessorBase does have code for throttling

// Whether we should ignore process(). This is usually caused by feeding input data faster than
// the model can handle.
private final AtomicBoolean shouldThrottle = new AtomicBoolean(false);

But is doesn't go far enough for my need :(

like image 558
Petr Kysela Avatar asked Jul 04 '18 17:07

Petr Kysela


People also ask

Can barcodes with ML kit on Android?

You can use ML Kit to recognize and decode barcodes. There are two ways to integrate barcode scanning: by bundling the model as part of your app, or by using an unbundled model that depends on Google Play Services. If you select the unbundled model, your app will be smaller.

How do you scan a barcode on Android?

On your compatible Android phone or tablet, open the built-in camera app. Point the camera at the QR code. Tap the banner that appears on your Android phone or tablet. Follow the instructions on the screen to finish signing in.


1 Answers

Once the camera focuses, the barcode processor would detect the same barcode multiple times, so you would scan 20 rather than 1 barcode in a second.

VisionProcessorBase.java

private void detectInVisionImage(
        FirebaseVisionImage image,
        final FrameMetadata metadata) {

    detectInImage(image)
            .addOnSuccessListener(
                    new OnSuccessListener<T>() {
                        @Override
                        public void onSuccess(final T results) {
                            new Handler().postDelayed(new Runnable() {
                                @Override
                                public void run() {
                                    shouldThrottle.set(false);
                                }
                            },1000);


                            VisionProcessorBase.this.onSuccess(results, metadata);

                        }
                    })
            .addOnFailureListener(
                    new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            new Handler().postDelayed(new Runnable() {
                                @Override
                                public void run() {
                                    shouldThrottle.set(false);
                                }
                            },1000);
                            VisionProcessorBase.this.onFailure(e);
                        }
                    });
    // Begin throttling until this frame of input has been processed, either in onSuccess or
    // onFailure.
    shouldThrottle.set(true);



}
like image 71
Subratsss Avatar answered Oct 05 '22 13:10

Subratsss