Detecting and Decoding QR Codes in Python with YOLO and Dynamsoft Barcode Reader
In the past two weeks, I trained a custom YOLOv3 model specifically for QR code detection and tested it using Darknet. In this article, I will demonstrate how to use OpenCV’s DNN (Deep Neural Network) module to load the YOLO model for detecting QR codes from static images and real-time camera video streams. Additionally, I will show how to use the Dynamsoft Barcode Reader to decode QR codes from the detected regions provided by YOLO.
This article is Part 1 in a 11-Part Series.
- Part 1 - Detecting and Decoding QR Codes in Python with YOLO and Dynamsoft Barcode Reader
- Part 2 - How to a GUI Barcode Reader with Qt PySide6 on Raspberry Pi
- Part 3 - Advanced GUI Python Barcode and QR Code Reader for Windows, Linux, macOS and Rasberry Pi OS
- Part 4 - Advanced QR Code Recognition: Handling Inverted Colors, Perspective Distortion, and Grayscale Images
- Part 5 - Scanning QR Code from Desktop Screen with Qt and Python Barcode SDK
- Part 6 - Building an Online Barcode and QR Code Scanning App with Python Django
- Part 7 - Real-Time Barcode and QR Code Scanning with Webcam, OpenCV, and Python
- Part 8 - How to Build Flet Chat App with Barcode and Gemini APIs
- Part 9 - Comparing Barcode Scanning in Python: ZXing vs. ZBar vs. Dynamsoft Barcode Reader
- Part 10 - Python Ctypes: Invoking C/C++ Shared Library and Native Threading
- Part 11 - A Guide to Running ARM32 and ARM64 Python Barcode Readers in Docker Containers
Prerequisites
-
OpenCV 4.x
pip install opencv-python
-
Dynamsoft Barcode Reader
pip install dbr
-
Obtain a Dynamsoft Barcode Reader trial license and update your code with the provided license key:
from dbr import * license_key = "LICENSE-KEY" BarcodeReader.init_license(license_key) reader = BarcodeReader()
QR Code Detection with YOLO and OpenCV Python
To quickly get started with OpenCV’s DNN module, you can refer to the sample script object_detection.py from the OpenCV GitHub repository. Below, we’ll build on this sample to implement QR code detection using a YOLO model.
Step-by-Step Implementation of QR Detection
-
Load the Image: Use the imread() function to load an image into a Mat object. To manage large images, define maximum width and height values:
import cv2 as cv import numpy as np frame = cv.imread("416x416.jpg") threshold = 0.6 maxWidth = 1280 maxHeight = 720 imgHeight, imgWidth = frame.shape[:2] hScale = 1 wScale = 1 thickness = 1 if imgHeight > maxHeight: hScale = imgHeight / maxHeight thickness = 6 if imgWidth > maxWidth: wScale = imgWidth / maxWidth thickness = 6
-
Initialize the YOLO Model: Load the YOLO configuration (
*.cfg
), weights (*.weights
), and class names (*.names
) files:classes = open('qrcode.names').read().strip().split('\n') net = cv.dnn.readNetFromDarknet('qrcode-yolov3-tiny.cfg', 'qrcode-yolov3-tiny.weights') net.setPreferableBackend(cv.dnn.DNN_BACKEND_OPENCV)
-
Create a Blob: Convert the image into a blob object suitable for input into the network:
blob = cv.dnn.blobFromImage(frame, 1/255, (416, 416), swapRB=True, crop=False)
-
Perform Inference: Feed the blob into the network and run forward pass to get the detections:
ln = net.getLayerNames() ln = [ln[i - 1] for i in net.getUnconnectedOutLayers().flatten()] net.setInput(blob) outs = net.forward(ln)
-
Post-Processing: Extract class names, confidence scores, and bounding boxes from the network outputs, and draw them using OpenCV:
def postprocess(frame, outs): frameHeight, frameWidth = frame.shape[:2] classIds = [] confidences = [] boxes = [] for out in outs: for detection in out: scores = detection[5:] classId = np.argmax(scores) confidence = scores[classId] if confidence > threshold: x, y, width, height = detection[:4] * np.array( [frameWidth, frameHeight, frameWidth, frameHeight]) left = int(x - width / 2) top = int(y - height / 2) classIds.append(classId) confidences.append(float(confidence)) boxes.append([left, top, int(width), int(height)]) indices = cv.dnn.NMSBoxes(boxes, confidences, threshold, threshold - 0.1) if not isinstance(indices, tuple): for i in indices.flatten(): # Flatten the indices if they are in nested format box = boxes[i] left = box[0] top = box[1] width = box[2] height = box[3] # Crop the detected object from the frame cropped_image = frame[top:top + height, left:left + width] cv.imshow('cropped', cropped_image) cv.imwrite('cropped.jpg', cropped_image) # Draw bounding box around the detected object cv.rectangle(frame, (left, top), (left + width, top + height), (0, 0, 255), thickness) # Draw class name and confidence on the bounding box label = '%s:%.2f' % (classes[classIds[i]], confidences[i]) cv.putText(frame, label, (left, top), cv.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255))
-
Resize and Display the Image: Adjust the image size to fit your display:
if hScale > wScale: frame = cv.resize(frame, (int(imgWidth / hScale), maxHeight)) elif hScale < wScale: frame = cv.resize(frame, (maxWidth, int(imgHeight / wScale))) cv.imshow('QR Detection', frame) cv.waitKey()
Decoding QR Codes with Dynamsoft Barcode Reader
After detecting QR codes, use the bounding boxes to decode the QR content. Setting the region parameters speeds up decoding compared to scanning the full image:
from dbr import *
license_key = "LICENSE-KEY"
BarcodeReader.init_license(license_key)
reader = BarcodeReader()
def decodeframe(frame, left, top, right, bottom):
settings = reader.reset_runtime_settings()
settings = reader.get_runtime_settings()
settings.region_bottom = bottom
settings.region_left = left
settings.region_right = right
settings.region_top = top
settings.barcode_format_ids = EnumBarcodeFormat.BF_QR_CODE
settings.expected_barcodes_count = 1
reader.update_runtime_settings(settings)
try:
text_results = reader.decode_buffer(frame)
if text_results != None:
return text_results[0]
except BarcodeReaderError as bre:
print(bre)
return None
Note: Replace
LICENSE-KEY
with your actual license key.
Source Code
https://github.com/yushulx/python-barcode-qrcode-sdk/tree/main/examples/official/9.x/yolo_qr