Reading Barcode/QR code using mobile Vision API in Android

    Reading barcode, QR code is a popular topic in mobile application development. The fact that barcodes and QR codes have become ubiquitous in recent years, it requires programmers to create applications for reading them by any smartphone with a decent camera.
    As you followed my blog, I also had 2 posts about this topic:
    Both of 2 posts above is guided about using ZXing library to create a barcode/QR code scanner. But now, the latest release of the Google Play services SDK includes the mobile vision API which, among other things, makes it very easy for Android developers to create apps capable of detecting and reading barcode, QR code in real time.
    In this tutorial, I am going to help you get started with it.

Project configurations

    After starting a new Android Studio project, adding Google Play Services dependency to dependencies scope of your app-level build.gradle:
compile 'com.google.android.gms:play-services:9.6.1'
    Add this meta-data to <application> tag in your AndroidManifest.xml:
<meta-data
            android:name="com.google.android.gms.vision.DEPENDENCIES"
            android:value="barcode" />

Reading barcode/QR code from a photo

    Let’s now write some code that can read a QR code from a photo stored in your app’s assets folder. I’m going to name the photo qr_code.png:
    Firstly, you must decode your photo to a Bitmap by using BitmapFactory, this Bitmap is needed to Vison API as input:
Bitmap myQRCode = BitmapFactory.decodeStream(getAssets().open("qr_code.png"));
    To detect QR codes(and other types of barcodes), you should use an instance of the BarcodeDetector class. The following code shows you how to create one using BarcodeDetector.Builder:
BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(PhotoActivity.this)
                            .setBarcodeFormats(Barcode.QR_CODE) // set QR code as the format type
                            .build();
    Create a Frame using the Bitmap you created:
Frame frame = new Frame.Builder().setBitmap(myQRCode).build();
    Call the detect() method of the BarcodeDetector to generate a SparseArray containing all the QR codes the BarcodeDetector detected in your photo:
SparseArray barcodes = barcodeDetector.detect(frame);
    Okey, after detecting some Barcode objects, you can get their values by call displayValue field:
                    // Check if at least one barcode was detected
                    if (barcodes.size() != 0) {
                        // Display the QR code's message
                        textView.setText("QR CODE Data: " + barcodes.valueAt(0).displayValue);
                        //Display QR code image to ImageView
                        imageView.setImageBitmap(myQRCode);
                    } else {
                        textView.setText("No QR Code found!");
                        textView.setTextColor(Color.RED);
                    }
    And this is full code for this activity:
PhotoActivity.java
package info.devexchanges.barcodescannermobilevisionapi;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.SparseArray;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import com.google.android.gms.vision.Frame;
import com.google.android.gms.vision.barcode.Barcode;
import com.google.android.gms.vision.barcode.BarcodeDetector;

public class PhotoActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_photo);

        View btnPhotoScan = findViewById(R.id.photo_scan);
        final ImageView imageView = (ImageView) findViewById(R.id.image);
        final TextView textView = (TextView) findViewById(R.id.qr_code_content);
        btnPhotoScan.setOnClickListener(new View.OnClickListener() {
            @SuppressLint("SetTextI18n")
            @Override
            public void onClick(View view) {
                try {
                    Bitmap myQRCode = BitmapFactory.decodeStream(getAssets().open("qr_code.png"));
                    BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(PhotoActivity.this)
                            .setBarcodeFormats(Barcode.QR_CODE)
                            .build();

                    Frame frame = new Frame.Builder().setBitmap(myQRCode).build();
                    SparseArray barcodes = barcodeDetector.detect(frame);

                    // Check if at least one barcode was detected
                    if (barcodes.size() != 0) {
                        // Display the QR code's message
                        textView.setText("QR CODE Data: " + barcodes.valueAt(0).displayValue);
                        //Display QR code image to ImageView
                        imageView.setImageBitmap(myQRCode);
                    } else {
                        textView.setText("No QR Code found!");
                        textView.setTextColor(Color.RED);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}
    Running this Activity, you can get this output:

Reading a barcode Using the Camera

    The mobile vision API also makes it very easy for you to detect and read barcodes using your device’s camera in real time. Let’s create a new Activity that does just that.
    Firstly, you must request CAMERA permission in your AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />
    Creating a layout file (xml) for this Activity. I will use a SurfaceView to display the preview frames captured by the camera and a TextView to display the content barcode value:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">

    <SurfaceView
        android:id="@+id/surface_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true" />

    <TextView
        android:id="@+id/barcode_value"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="@dimen/activity_horizontal_margin"
        android:text="No Barcode"
        android:textColor="@android:color/white"
        android:textSize="20sp" />

</RelativeLayout>
    In the activity Java code, to "stream" the camera preview scene to the SurfaceView, we'll use an instance of CameraSource, initializing it with a BarcodeDetector object:
barcodeDetector = new BarcodeDetector.Builder(this)
                .setBarcodeFormats(Barcode.ALL_FORMATS)
                .build();

cameraSource = new CameraSource.Builder(this, barcodeDetector)
                .setRequestedPreviewSize(1600, 1024)
                .setAutoFocusEnabled(true) //you should add this feature
                .build();
    As noted in code, you should use setAutoFocusEnabled(true) when creating the CameraSource instance, your "camera preview" will be auto focused, not be blurry!
    Next, add a callback to the SurfaceHolder of the SurfaceView so that you know when you can start drawing the preview frames. The callback should implement the SurfaceHolder.Callback interface. Inside the surfaceCreated() method, call the start() method of the CameraSource to start drawing the preview frames:
cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                try {
                    //noinspection MissingPermission
                    cameraSource.start(cameraView.getHolder());
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                cameraSource.stop();
            }
        });
The remaining work is displaying detected barcode content to TextView. We will use setProcessor() method of BarcodeDetector with the parameter is Detector.Processor:
barcodeDetector.setProcessor(new Detector.Processor() {
            @Override
            public void release() {
            }

            @Override
            public void receiveDetections(Detector.Detections detections) {
                final SparseArray barcodes = detections.getDetectedItems();
                if (barcodes.size() != 0) {
                    barcodeValue.post(new Runnable() {
                        @Override
                        public void run() {
                            //Update barcode value to TextView
                            barcodeValue.setText(barcodes.valueAt(0).displayValue);
                        }
                    });
                }
            }
        });
    Moreover, you should override onDestroy() method of your Activity to release the CameraSource to stop drawing the preview frame. Finally, this is full code:
MainActivity.java
package info.devexchanges.barcodescannermobilevisionapi;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.SparseArray;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.TextView;

import com.google.android.gms.vision.CameraSource;
import com.google.android.gms.vision.Detector;
import com.google.android.gms.vision.barcode.Barcode;
import com.google.android.gms.vision.barcode.BarcodeDetector;

import java.io.IOException;

public class MainActivity extends AppCompatActivity {
    private BarcodeDetector barcodeDetector;
    private CameraSource cameraSource;
    private SurfaceView cameraView;
    private TextView barcodeValue;

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.activity_main);

        cameraView = (SurfaceView) findViewById(R.id.surface_view);
        barcodeValue = (TextView) findViewById(R.id.barcode_value);

        barcodeDetector = new BarcodeDetector.Builder(this)
                .setBarcodeFormats(Barcode.ALL_FORMATS)
                .build();

        cameraSource = new CameraSource.Builder(this, barcodeDetector)
                .setRequestedPreviewSize(1600, 1024)
                .setAutoFocusEnabled(true) //you should add this feature
                .build();

        cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                try {
                    //noinspection MissingPermission
                    cameraSource.start(cameraView.getHolder());
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                cameraSource.stop();
            }
        });

        barcodeDetector.setProcessor(new Detector.Processor() {
            @Override
            public void release() {
            }

            @Override
            public void receiveDetections(Detector.Detections detections) {
                final SparseArray barcodes = detections.getDetectedItems();
                if (barcodes.size() != 0) {
                    barcodeValue.post(new Runnable() {
                        @Override
                        public void run() {
                            //Update barcode value to TextView
                            barcodeValue.setText(barcodes.valueAt(0).displayValue);
                        }
                    });
                }
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        cameraSource.release();
        barcodeDetector.release();
    }
}
    Running this activity and scanning a barcode, you may get result like this:

Conclusions

    In this tutorial, you learned how to use the mobile vision API to read barcode and QR codes from static images as well as from live camera streams. To learn more about the mobile vision API, I recommend visiting the API’s documentation.
    These are posts about Mobile Vision API on my blog:

Share


Previous post
« Prev Post
Next post
Next Post »