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.

QR code detection with OpenCV DNN and YOLOv3

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

  1. 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
    
  2. 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)
    
  3. 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)
    
  4. 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)
    
  5. 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))
        
    
  6. 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()
    

    Python QR Code detection

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.

Python QR code scan

Source Code

https://github.com/yushulx/python-barcode-qrcode-sdk/tree/main/examples/official/9.x/yolo_qr