Google ML Kit vs. Dynamsoft's SDK: Scan High Density QR Codes on Android Devices
Tags: qrcode, android
Published: 2021-10-25 06:15:55

There are many QR code tools and SDKs available on the market. For Android development, Google ML Kit is no doubt a good choice, which is free and supports general QR code detection. However, it cannot satisfy the requirement of high density QR code detection. As a commercial barcode SDK, Dynamsoft Barcode Reader can cover special barcode types. In this article, we will simplify the sample of ML Kit and integrate it with Dynamsoft Barcode SDK to compare the performance of recognizing high density QR Code between ML Kit and Dynamsoft Barcode Reader.

![high density QR code from Google](https://www.dynamsoft.com/codepool/img/2021/10/high-density-qr-code.jpg)

## Barcode SDK Download
- ML Kit:
 
    In `AndroidManifest.xml`, add:
        
    ```xml
    <meta-data
        android:name="com.google.mlkit.vision.DEPENDENCIES"
        android:value="barcode"/>
    ```

    In `build.gradle`, add:
    
    ```gradle
    dependencies {
        implementation 'com.google.mlkit:barcode-scanning:17.0.0'
        // Or comment the dependency above and uncomment the dependency below to
        // use unbundled model that depends on Google Play Services
        // implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:16.2.1'
    }
    ```

    You can find the model file in generated apk file `apk/assets/mlkit_barcode_models/barcode_ssd_mobilenet_v1_dmp25_quant.tflite`.

- Dynamsoft Barcode Reader

    [Dynamsoft Barcode Reader 30-day Trial](https://www.dynamsoft.com/barcode-reader/downloads/)
    
    Extract the SDK package and copy the `DynamsoftBarcodeReaderAndroid.aar` file to the libs folder of the project. In `build.gradle`, add:

    
    ```gradle
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
    }
    ```

    Alternatively, you can configure the Maven repository in `build.gradle` file as follows:
    
    ```gradle
    allprojects {
        repositories {
            google()
            jcenter()
            maven {
                url "https://download2.dynamsoft.com/maven/dbr/aar"
            }
        }
    }

    dependencies {
        implementation 'com.dynamsoft:dynamsoftbarcodereader:8.8.0@aar'
    }
    ```

## Reading QR Code from Still Images

### QR Code Scanning with Google ML Kit

Download the [sample code](https://github.com/googlesamples/mlkit/tree/master/android/vision-quickstart) of ML Kit vision.

It is an excellent sample app that features object detection, face detection, text recognition, barcode scanning, image labeling, custom image labeling, pose detection, and selfie segmentation.

To focus on still image detection, we only keep the `StillImageActivity` and set it as the `LAUNCHER` in `AndroidManifest.xml`:

```xml
<activity
    android:name=".java.StillImageActivity"
    android:exported="true"
    android:theme="@style/AppTheme">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>
```

### QR Code Scanning with Dynamsoft's SDK

We create a `dynamsoftbarcodescanner` folder and two class files: `DynamsoftBarcodeGraphic.java` and `DynamsoftBarcodeProcessor.java`. 

The `DynamsoftBarcodeGraphic` class is used to draw the detection results:

```java
@Override
public void draw(Canvas canvas) {
    if (result == null) {
        throw new IllegalStateException("Attempting to draw a null barcode.");
    }

    // Draws the bounding box around the BarcodeBlock.
    Point[] points = result.localizationResult.resultPoints;
    int minx = points[0].x;
    int miny = points[0].y;
    int maxx = points[0].x;
    int maxy = points[0].y;
    for (int i = 1; i < 4; i++) {
        if (points[i].x < minx) {
            minx = points[i].x;
        }
        else if (points[i].x > maxx) {
            maxx = points[i].x;
        }

        if (points[i].y < miny) {
            miny = points[i].y;
        }
        else if (points[i].y > maxy) {
            maxy = points[i].y;
        }
    }
    RectF rect = new RectF(minx, miny, maxx, maxy);
    // If the image is flipped, the left will be translated to right, and the right to left.
    float x0 = translateX(rect.left);
    float x1 = translateX(rect.right);
    rect.left = min(x0, x1);
    rect.right = max(x0, x1);
    rect.top = translateY(rect.top);
    rect.bottom = translateY(rect.bottom);
    canvas.drawRect(rect, rectPaint);

    // Draws other object info.
    float lineHeight = TEXT_SIZE + (2 * STROKE_WIDTH);
    float textWidth = barcodePaint.measureText(result.barcodeText);
    canvas.drawRect(
            rect.left - STROKE_WIDTH,
            rect.top - lineHeight,
            rect.left + textWidth + (2 * STROKE_WIDTH),
            rect.top,
            labelPaint);
    // Renders the barcode at the bottom of the box.
    canvas.drawText(result.barcodeText, rect.left, rect.top - STROKE_WIDTH, barcodePaint);
}
```


The `DynamsoftBarcodeProcessor` class is used to decode QR code. As we load an image file, the `processBitmap(Bitmap bitmap, final GraphicOverlay graphicOverlay)` function will be triggered in `DynamsoftBarcodeProcessor.java`. Thereafter, we call `decodeBufferedImage(bitmap, "")` to recognize QR code:

```java
@Override
public void processBitmap(Bitmap bitmap, final GraphicOverlay graphicOverlay) {
    try {
        long frameStartMs = SystemClock.elapsedRealtime();
        TextResult[] results = barcodeScanner.decodeBufferedImage(bitmap, "");
        long frameEndMs = SystemClock.elapsedRealtime();

        if (results != null) {
            for (int i = 0; i < results.length; ++i) {
                TextResult barcode = results[i];
                graphicOverlay.add(new DynamsoftBarcodeGraphic(graphicOverlay, barcode));
            }
            graphicOverlay.add(
                    new InferenceInfoGraphic(
                            graphicOverlay,
                            frameEndMs - frameStartMs,
                            frameEndMs - frameStartMs,
                            null));
        }

    } catch (IOException e) {
        e.printStackTrace();
    } catch (BarcodeReaderException e) {
        e.printStackTrace();
    }
}
``` 

### Build and run

Build and run the project in Android Studio:

![high density QR code detection](https://www.dynamsoft.com/codepool/img/2021/10/high-density-qr-code-detection.jpg)


## High Density QR Code Detection Performance
Let's take some tests to see how ML Kit and Dynamsoft Barcode Reader perform for high density QR code images.

To make the test transparent and convincing, we use the [public image dataset](https://boofcv.org/notwiki/regression/fiducial/qrcodes_v3.zip) provided by boofcv.

The test images are located in `qrcodes_v3/qrcodes/detection/high_version`.

We pick six images from the dataset and run the test on them.

It is surprising that ML Kit barcode scanning API is incompetent to these images. ML Kit took more time than Dynamsoft Barcode Reader, and recognized nothing. In contrast, Dynamsoft Barcode Reader is able to recognize all high density QR code images.

![high density QR detection comparison](https://www.dynamsoft.com/codepool/img/2021/10/high-density-qr-detection-performance.jpg)


## Source Code

[https://github.com/yushulx/android-dense-qr-code-detection](https://github.com/yushulx/android-dense-qr-code-detection)