A week ago, I made an experiment to test the QR code detection performance with YOLOv4 on Android. The inference time of running the YOLOv4 model on CPU is more than 100ms, which does not reach my expectation. Since Android Neural Networks API (NNAPI) provides hardware acceleration for TensorFlow Lite models, I decided to train a TensorFlow Lite model to speed up the inference time.
Steps to Train QR Code Detection Model with TensorFlow Lite Model Maker
-
Install TensorFlow Lite Model Maker:
pip install tflite-model-maker
-
Get the GitHub repository that contains the image set:
git clone https://github.com/yushulx/barcode-qrcode-images.git
-
Partition the image set into training and validation sets:
cd tensorflow-lite python partition_dataset.py -x -i ../images -r 0.1 -o ./
-
According to the TensorFlow tutorial, create the following Python script to train the model:
from tflite_model_maker.config import QuantizationConfig from tflite_model_maker.config import ExportFormat from tflite_model_maker import model_spec from tflite_model_maker import object_detector import tensorflow as tf assert tf.__version__.startswith('2') tf.get_logger().setLevel('ERROR') from absl import logging logging.set_verbosity(logging.ERROR) spec = model_spec.get('efficientdet_lite0') train_data = object_detector.DataLoader.from_pascal_voc(images_dir="train", annotations_dir="train", label_map={1: "QR_CODE"} ) validation_data = object_detector.DataLoader.from_pascal_voc(images_dir="test", annotations_dir="test", label_map={1: "QR_CODE"} ) model = object_detector.create(train_data, model_spec=spec, batch_size=8, train_whole_model=True, validation_data=validation_data) model.export(export_dir='.')
EfficientDet-D0 has comparable accuracy as YOLOv3.
Once the model is saved, we can download the TensorFlow Lite example and replace the model file with the one we just created.
To make the model work:
- Drag the
model.tflite
file to assets folder. -
Change
TF_OD_API_MODEL_FILE
tomodel.tflite
inDetectorActivity.java
:
private static final String TF_OD_API_MODEL_FILE = "model.tflite";
-
Clear
labelmap.txt
and add the following line:
QR_CODE
So far, the QR code detection model is ready to be used. We can test it on the Android device.
QR Code Scanner with TensorFlow Lite
Now, let's integrate TensorFlow Lite API into our Android QR Code Scanner.
- Copy
model.tflite
andlabelmap.txt
to assets folder. -
Add the TensorFlow Lite library to
build.gradle
:
// Import the Task Vision Library dependency (NNAPI is included) implementation 'org.tensorflow:tensorflow-lite-task-vision:0.3.0'
-
Copy
Detector.java
andTFLiteObjectDetectionAPIModel.java
from the TensorFlow Lite example project to our project. Use NNAPI delegate for TensorFlow Lite:
private TFLiteObjectDetectionAPIModel(Context context, String modelFilename) throws IOException { modelBuffer = FileUtil.loadMappedFile(context, modelFilename); optionsBuilder = ObjectDetectorOptions.builder(); optionsBuilder.setBaseOptions(BaseOptions.builder().useNnapi().build()); objectDetector = ObjectDetector.createFromBufferAndOptions(modelBuffer, optionsBuilder.build()); }
-
Initialize the model detector in
CameraXActivity.java
:
// Configuration values for the prepackaged QR Code model. private static final int TF_OD_API_INPUT_SIZE = 416; private static final boolean TF_OD_API_IS_QUANTIZED = true; private static final String TF_OD_API_MODEL_FILE = "model.tflite"; private static final String TF_OD_API_LABELS_FILE = "labelmap.txt"; // Minimum detection confidence to track a detection. private static final float MINIMUM_CONFIDENCE_TF_OD_API = 0.5f; private Detector detector; int cropSize = TF_OD_API_INPUT_SIZE; private void initTFDetector() { try { detector = TFLiteObjectDetectionAPIModel.create( this, TF_OD_API_MODEL_FILE, TF_OD_API_LABELS_FILE, TF_OD_API_INPUT_SIZE, TF_OD_API_IS_QUANTIZED); cropSize = TF_OD_API_INPUT_SIZE; } catch (final IOException e) { e.printStackTrace(); Toast toast = Toast.makeText( getApplicationContext(), "Detector could not be initialized", Toast.LENGTH_SHORT); toast.show(); finish(); } }
-
In the camera frame callback function, convert
ImageProxy
toBitmap
:
analysisUseCase.setAnalyzer(cameraExecutor, imageProxy -> { Bitmap bitmap = ImageUtils.getBitmap(imageProxy); imageProxy.close(); });
Note: the bitmap has been rotated, so we don't need to rotate the coordinates of the QR code bounding box for rendering overlay.
To understand the implementation of
getBitmap()
, please refer to BitmapUtils.java. -
Detect QR code and draw bounding boxes:
final List<Detector.Recognition> tfResults = detector.recognizeImage(bitmap); if (tfResults.size() > 0) { for (final Detector.Recognition tfResult : tfResults) { final RectF location = tfResult.getLocation(); if (location != null && tfResult.getConfidence() >= MINIMUM_CONFIDENCE_TF_OD_API) { overlay.add(new BarcodeGraphic(overlay, location, null, isPortrait)); } } }
-
Decode QR code with Dynamsoft Barcode Reader API:
TextResult[] results = null; try { PublicRuntimeSettings settings = reader.getRuntimeSettings(); settings.barcodeFormatIds = EnumBarcodeFormat.BF_QR_CODE; settings.expectedBarcodesCount = tfResults.size(); reader.updateRuntimeSettings(settings); } catch (BarcodeReaderException e) { e.printStackTrace(); }
-
Finally, test the QR code detection model on the Android device.
We can see running TensorFlow Lite model to scan QR code is faster than running YOLOv4, but still slower than purely running Dynamsoft Barcode Reader.
Source Code
https://github.com/yushulx/android-camera2-preview-qr-code-scanner