Scanning QR Codes

Recognize QR Codes in images from scratch. We'll do all the bit math to figure out the location markers and then read data from the black/white array.

Extracting bits from the QR code

Series: Scanning QR Codes:

  1. Introduction
  2. Locating the QR code
  3. Verifying the finder patterns
  4. Extracting bits from the QR code

Until now, we've been able to get the finder patterns and get the centers of the three. Now, let's take a look at actually deciphering the contents of the QR code. The actual decoding process is a bit involved and requires Solomon-Reed codes. For the purpose of this tutorial, we'll read the pixels and translate them into bits.

Updates to the QR reader class

We'll be adding a bunch of new functions to our QR code reader class to help us extract just the code out.

class qrReader {
    bool findAlignmentMarker(const Mat& img);
    void getTransformedMarker(const Mat &img, Mat& output);
    void extractBits(const Mat &img);

    bool checkRatio(int* stateCount);
    int computeDimension(Point2f tl, Point2f tr, Point2f bl, float moduleSize);

    vector<float> estimatedModuleSize;
    int dimension = -1;

Computing the dimensions of a code

Any valid QR Code is always a square with 21x21, 25x25, 29x29, etc modules along each dimension. These are called "versions" of a QR code. Version 1 corresponds to 21x21. Version 2 corresponds to 25x25, etc. The dimensions always have a remainder of 1 when divided by four.

Thus, we should be able to compute the dimensions of the code using the finder pattern locations we've computed until now.

int qrReader::computeDimension(Point2f tl, Point2f tr, Point2f bl, float moduleSize) {
    // The dimension is always a square of dimension 21*21, 25*25, 29*29, etc

    Point2f diff_top = tl-tr;
    Point2f diff_left = tl-bl;

    // Calculate the distance between the top-* and *-left points
    float dist_top = sqrt(;
    float dist_left = sqrt(;

Once we have the approximate number of pixels in each dimension, we can convert that into the number of modules:

    int width = (int)round(dist_top/moduleSize);
    int height = (int)round(dist_left/moduleSize);
    dimension = ((width+height)/2) + 7;

We know that the dimension has to have a remainder of one when divided by four. So we enforce this by adding/subtracting an appropriate number.

    switch(dimension % 4) {
        case 0:
            dimension += 1;

        case 1:

        case 2:
            dimension -= 1;

        case 3:
            dimension -= 2;
    return dimension;

Finding the bottom-right corner

Before we can warp the QR code into a nice grid, we need to find the bottom-right corner. Usually, this is done with an alignment marker. Let's start by ensuring we've at least found the other three points.

bool qrReader::findAlignmentMarker(const Mat& img) {
    // Make sure we already found the finder patterns first
    if(possibleCenters.size() != 3) {
        return false;

Now, I cheat a little bit for the purpose of this tutorial. I assume that the first element in possibleCenters is the top-left finder pattern, second is the top-right and third is the bottom-left. This is a valid assumption as long as you're not rotating your code a lot. It shouldn't be hard to find the actual top-left point etc.

We also compute the estimated module size and the dimension of the QR code.

    Point2f ptTopLeft = possibleCenters[0];
    Point2f ptTopRight = possibleCenters[1];
    Point2f ptBottomLeft = possibleCenters[2];
    float moduleSize = (estimatedModuleSize[0] + estimatedModuleSize[1] + estimatedModuleSize[2]) / 3.0f;

    computeDimension(ptTopLeft, ptTopRight, ptBottomLeft, moduleSize);

Finally, we can actually compute the bottom-right corner. QR code version 1 does not include a finder pattern - it requires "guessing" the bottom-right corner. So, let's do that.

    if(dimension == 21 || true) {
        // This is the smallest QR code and does not have have an alignment marker
        Point2f ptBottomRight = ptTopRight - ptTopLeft + ptBottomLeft;
        return true;

    // TODO: Detect the alignment marker using a technique similar to previous posts
    return false;

The above code uses the "guessed" bottom-right coordinate for all QR codes. This is kind-of accurate but probably won't work in real-world images. You need to detect the alignment markers using the state count and cross-check approach we did earlier.

Warping the code and extracting bits

Now that we've added the bottom-right corner to our possibleCenters list, warping the code is a piece of cake.

void qrReader::getTransformedMarker(const Mat &img, Mat& output) {
    vector<Point2f> src;
    src.push_back(Point2f(3.5f, 3.5f));
    src.push_back(Point2f(dimension - 3.5f, 3.5f));
    src.push_back(Point2f(3.5f, dimension - 3.5f));
    src.push_back(Point2f(dimension - 3.5f, dimension - 3.5f));

    Mat transform = getPerspectiveTransform(possibleCenters, src);
    warpPerspective(img, output, transform, Size(dimension, dimension), INTER_NEAREST);
The warped QR code

The warped QR code. The tiny code on the right is the output of the function we just wrote.

With the warped image, we can now extract bits. For simplicity, I printout the zeros and ones on screen.

void qrReader::extractBits(const Mat& marker) {
    const int width = marker.cols;
    const int height = marker.rows;

    for(int y=0;y<height;y++) {
        const uchar* ptr = marker.ptr<uchar>(y);
        for(int x=0;x<width;x++) {
            if(ptr[x] > 128) {
                cout << "1";
            } else {
                cout << "0";
        cout << endl;

Here's the output:


You can see the errors in the above code. Actually finding the alignment marker and using the error-correction code will ensure that you can read the data without corruption. We won't do that in this tutorial series - I'm sure you should be able to find enough material on this online.


We covered quite a bit in this series - going from a raw image, some preprocessing and detecting the finder patterns. Finally, warping the QR code to extract bits out of it. I hope you had fun and learned something through this series. Until next time!

More in the series

This tutorial is part of a series called Scanning QR Codes:

  1. Introduction
  2. Locating the QR code
  3. Verifying the finder patterns
  4. Extracting bits from the QR code

Utkarsh Sinha created AI Shack in 2010 and has since been working on computer vision and related fields. He is currently at Microsoft working on computer vision.