WeChat QR Code Scanner in OpenCV

Learn how to use the WeChat QR code scanner in OpenCV using Python and C++. We will also quantitatively and qualitatively compare its performance with the default QR code decoder of OpenCV.
QR Code Scanner Feature Image
Feature image WeChat QR code

WeChat QR Code scanner is a Convolutional Neural Network (CNN) based library to detect and decode QR codes. As the name suggests, it was developed and contributed by the WeChat Computer Vision team.  It was integrated into OpenCV with the release 4.5.2 in the opencv-contrib package. 

In this blog post, we will go through python and c++ code for implementing the WeChat QR code scanner in OpenCV and compare its performance with the built-in QR code scanner of OpenCV.

  1. Structure of a QR code
  2. WeChat QR Code Scanner in OpenCV
  3. Python code
  4. C++ code
  5. OpenCV vs WeChat QRCode

1. Structure of a QR code

At the very outset, let us take a look at the structure to get a basic idea of QR code formatting.

Structure of a QR code

fig: Structure of a QR code. source: Wikipedia

The illustration shows the format parameters of a QR code. Five parameters describe the format of a QR code to the decoder. 

  1. Position
  2. Timing
  3. Error correction level
  4. Mask pattern
  5. Format error correction

The distinct black squares containing smaller squares are the tracker boxes that help determine the position and rotation of the QR code. The timing (repeating black and white pattern between position boxes) provides information about the size of the QR code. There are 40 different types of QR code based on the timing. Version 1 ( 21×21 box), Version 2 (25×25), version 3(29×29) to version 40 (177×177). Error correction level tells about the percentage of data bytes that can be recovered. Based on the level of error correction, there can be 4 different categories. Low(7%), medium(15%), quartile(25%) and high(30%). The higher the level, the lower is the storage capacity. The scanner detects the QR code with the help of these parameters and decodes the data. In this blog post, we are not going to discuss every parameter in detail. If you are interested in knowing how a QR code is encoded and decoded, check out this link for more information. Moreover, there are plenty of good online resources explaining QR code.

2. WeChat QR Code Scanner in OpenCV

The WeChat QR code scanner is part of the opencv-contrib repository. 

Please download the code from the link above. Navigate to the code folder, fire up terminal/powershell and install the required packages using the requirements.txt file. Now, without further ado, let’s dive into the code.

pip install -r requirements.txt
Download Code To easily follow along this tutorial, please download code by clicking on the button below. It's FREE!

3. Python Code

3.1 Import Libraries

import cv2
import sys
import time

3.2 Function to generate a bounding box

The bounding box coordinates are obtained from the detector as a list. Hence, we convert them to tuples for drawing lines.

def displayBbox(im, bbox):
    if bbox is not None:
        bbox = [bbox[0].astype(int)]
        n = len(bbox[0])
        for i in range(n):
            cv2.line(im, tuple(bbox[0][i]), tuple(bbox[0][(i+1) % n]), (0,255,0), 3)

3.3 Instantiate WeChat QR Code detector

WeChat QR code scanner uses two CNN models internally. 

  1. Detector Model: This CNN model is used to locate the QR code. 
  2. Super Resolution Model: The class of techniques used for improving the resolution of an image is called Super Resolution. WeChat QR code scanner uses a super-resolution model to enhance the resolution of the QR code. It is, therefore, able to deliver superior results when the resolution of the QR code is small (e.g., when you are scanning it from far away.)

The two CNN models are in the Caffe Deep Learning framework format which consists of two files- 

  1. A model architecture file (“model.prototxt”) 
  2. A model weights file (“model.caffemodel”) contains the weights and biases learned in the training process.  

We have included these files in the model directory. You can also download these files from this link

The default OpenCV detector uses traditional computer vision methods for detection and decoding. 

OpenCV Documentation

3.4 Main Function

In the main function, we first read the image. Then use the detectAndDecode method to parse the QR code. You can also pass your QR code image in the command line. We calculate the time of execution using the time module and obtain the output in milliseconds.

if __name__ == '__main__':
    # Load image.
    if len(sys.argv)>1:
        img = cv2.imread(sys.argv[1])
    else:
        img = cv2.imread('sample-qrcode.jpg')

    t1 = time.time()
    # Detect and decode.
    res, points = detector.detectAndDecode(img)
    t2 = time.time()
    # Detected outputs.
    if len(res) > 0:
        print('Time Taken : ', round(1000*(t2 - t1),1), ' ms')
        print('Output : ', res[0])
        print('Bounding Box : ', points)
        displayBbox(img, points)
    else:
        print('QRCode not detected')

    cv2.imshow("Image", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

4. C++ Code

The CMakeLists.txt file has been provided within the downloaded code.

4.1 Include Libraries

#include <chrono>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/wechat_qrcode.hpp>

using namespace std;
using namespace cv;
using namespace std::chrono;

4.2 Utility function to draw a bounding box

The detect and decode method in c++ returns a vector containing the bounding box coordinates. Which is converted to Mat so as to access the elements as shown below.

void display(Mat &im, Mat &bbox)
{
	int n = bbox.rows;
	for (int i = 0; i < n; i++)
	{
	      line(im, Point2i(bbox.at<float>(i,0), bbox.at<float>(i,1)), 
		     Point2i( bbox.at<float>((i+1) % n,0), 
                 bbox.at<float>((i+1) % n,1)), Scalar(0,255,0), 3);
	}
	imshow("Image", im);
}

4.3 Main Function

Similar to python, we read the image from command line input or use the default image. The output data and bounding box coordinates are stored in string res and vector points respectively. Unlike python, here we need to take care of the data type.

int main(int argc, char* argv[])
{
	// Instantiate WeChat QR Code detector.
	Ptr<wechat_qrcode::WeChatQRCode> detector;
	detector = makePtr<wechat_qrcode::WeChatQRCode>(
                            "../model/detect.prototxt", 
	                      "../model/detect.caffemodel",
	                      "../model/sr.prototxt", 
	                      "../model/sr.caffemodel");

	// Read image from command line input or the default.
	Mat img;
	if(argc>1)
	{
	     img = imread(argv[1]);
	}
      else
	img = imread("sample-qr-code.jpg");
      // Declare vector 'points' to store bounding box coordinates.
	vector<Mat> points;
	// Start time.
	auto start = high_resolution_clock::now();
	// Detect and decode.
	auto res = detector->detectAndDecode(img, points);
	// End time.
	auto stop = high_resolution_clock::now();
	// Time taken in milliseconds.
	auto duration = duration_cast<milliseconds>(stop - start);
      // If detected.
	if (res.size() > 0)
	{
		// Print detected data.
		for (const auto& value : res) 
		{
			cout << value << endl;
		}
		// Iterate through the vector and convert to Mat. Required,
            // as we need to access the elements while drawing the box.
		Mat1f matBbox;
		for(int i=0; i<points[0].size().height; i++)
		{
			matBbox.push_back( points[0].row(i));
		}
		cout << "Time taken : "  << duration.count() <<
            " milliseconds" << endl;
		cout << matBbox << endl;
		// Display bounding box. 
		display(img, matBbox);
	}
	else
	cout << "QR Code not detected." << endl;
	imshow("Image", img);
	waitKey(0);
	return 0;
}

4.4 Output

The decoded data is shown in the terminal along with the execution time and a bounding box is drawn around the detected QR Code.

http://LearnOpenCV.com
Time taken : 18 milliseconds
[242.27815, 62.109261;
 402.75635, 62.109261;
 402.75635, 225.11674;
 242.27815, 225.11674]
WeChat QR decoder bounding box output

5. OpenCV vs WeChat QRCode

We took 4 QR code samples in increasing order of complexity as shown below and performed analysis on the following grounds. We are going to plot the processing time using bar graphs and create a table for the rest of the qualitative variations.

  1. Processing time.
  2. Occluded image (less than 15% of the QR code)
  3. Low light situation
  4. Far away images ( 1.5-2.0 meters, resolution : 640x480p)
  5. Blurred images ( Gaussian Blur, various kernel sizes)
  6. Rotated images
QR code sample

fig: QR code samples

Sample 1

  • Encoded data: [https://learnopencv.com]
  • Type: Version 2 (25×25)
  • Error level: Quartile (25%)
  • Time taken by OpenCV: 17.02 ms
  • Time taken by WeChat: 24.72 ms
QR code parsing time plot for sample 1

fig: QR code parsing time plot for sample 1

Table 1: Observation on sample 1

Table 1: Observations on sample 1

Occluded QR code

 fig: Occluded QR code

Low light condition

fig: Low light condition 

far away detections

fig: Far away

detections on rotated qr code

fig: Rotated

Detection test on Gaussian blurred images

fig: Gaussian Blurred (kernel size 3, 5, 7, and 9 respectively)

Sample 2

  • Encoded Data: [Why is 11 not pronounced as ‘onety one’?]
  • Type: Version 3 (29×29)
  • Error level: Quartile (25%)
  • Time taken by OpenCV: 18.82 ms
  • Time taken by WeChat: 23.77 ms
QR code parsing time plot for sample 2

fig: QR code parsing time plot for sample 2

The observations on sample 2 are similar to sample 1 except for the blurred images.

Detection test on Gaussian blurred images

fig: Gaussian Blurred (kernel size 3, 5, 7 and 9 respectively)

Sample 3

  • Encoded Data: [Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat]
  • Type: Version 9 (53×53)
  • Error level: Quartile (25%)
  • Time taken by OpenCV: 426.8 ms
  • Time taken by WeChat: 26.0 ms
QR code parsing time plot for sample 3

fig: QR code parsing time plot for sample 3

For the last two samples, OpenCV was performing marginally better than WeChat but when we increased the complexity of the QR code, WeChat outperformed OpenCV.

Table 2: Observations on sample 3

Occluded QR code

fig: Occluded QR code

Low light condition

fig: Low light condition

far away detections

fig: Far away

detections on rotated QR code

fig: Rotated

Detection test on Gaussian blurred images

fig: Gaussian Blurred (kernel size 3, 5, 7 and 9 respectively)

Sample 4

  • Encoded Data: [1. 70% of the earth’s surface is covered in water. However, only 3% of the earth’s water is freshwater and two-thirds of the freshwater is frozen in ice sheets and glaciers. The other third is found in lakes, rivers, and underground. \n2. Back in a jiffy? You’d better be fast! A “jiffy” is an actual length of time, equal to about 1/100th of a second]
  • Type: Version 12 (65×65)
  • Error level: Quartile (25%)
  • Time taken by OpenCV: Failed to parse.
  • Time taken by WeChat: 30.5 ms
QR code parsing time plot for sample 4

fig: QR code parsing time plot for sample 4

Here, OpenCV failed to decode the QR code after processing for about 730 ms. Whereas WeChat completed processing in just 30.5 ms. The observations, in this case, are similar to sample 3, except for the OpenCV QR code decoder.

Detection test on Gaussian blurred images

fig: Gaussian Blurred (kernel size 3, 5, 7 and 9 respectively)

Conclusion

From the experiment above, we observed that OpenCV performs fairly well for QR code with lesser information. However, it performs poorly on complex QR code and mostly fails above a certain complexity. From the video, we can see that OpenCV has a better corner detection algorithm but that does not necessarily imply better decoding. WeChat is more robust to variations like rotation or when it’s far away. All in all, if you are building an application that requires parsing of complex QR code and time is a concern, then WeChat is the choice. Otherwise, the OpenCV QR code decoder works fine.

So that’s all about the experiment that we performed on OpenCV and WeChat QR code decoder. I hope you enjoyed reading the post and learned a few things. If you are interested in learning about a library even better than both OpenCV and WeChat, check out our previous post on zBar.

References

  1. OpenCV QR Code Detector
  2. WeChat QR Code Detector OpenCV


Read Next

VideoRAG: Redefining Long-Context Video Comprehension

VideoRAG: Redefining Long-Context Video Comprehension

Discover VideoRAG, a framework that fuses graph-based reasoning and multi-modal retrieval to enhance LLMs' ability to understand multi-hour videos efficiently.

AI Agent in Action: Automating Desktop Tasks with VLMs

AI Agent in Action: Automating Desktop Tasks with VLMs

Learn how to build AI agent from scratch using Moondream3 and Gemini. It is a generic task based agent free from…

The Ultimate Guide To VLM Evaluation Metrics, Datasets, And Benchmarks

The Ultimate Guide To VLM Evaluation Metrics, Datasets, And Benchmarks

Get a comprehensive overview of VLM Evaluation Metrics, Benchmarks and various datasets for tasks like VQA, OCR and Image Captioning.

Subscribe to our Newsletter

Subscribe to our email newsletter to get the latest posts delivered right to your email.

Subscribe to receive the download link, receive updates, and be notified of bug fixes

Which email should I send you the download link?

🎃 Halloween Sale: Exclusive Offer – 30% Off on All Courses.
D
H
M
S
Expired
 

Get Started with OpenCV

Subscribe To Receive

We hate SPAM and promise to keep your email address safe.​