Hands on QR Code detection

QR codes can store different types of data. Recently, I was involved in a project that a robot had to detect and decode pre-installed QR codes to refine its position. This post explains how I got 10 FPS performance on a Raspberry Pi Zero.

Which library to use?


There are several libraries for barcode and QR code detection. A simple search in Google lists Zbar, Zxing, OpenCV, BoofCV, etc. Zbar was reported to be faster than OpenCV [link] and Zxing [link]. BoofCV was reported to be even faster than Zbar [link] but my initial experiments with Pyboof (the python wrapper of BoofCV) showed that Zbar works at least two times faster than it. So, I decided to go with Zbar.

Conventional install


Most guides on installing Zbar suggest using sudo apt-get install libzbar-dev libzbar0. Assuming that Raspicam [link] and OpenCV have been installed before, g++ main.cpp `pkg-config –cflags –libs opencv` -lraspicam –lraspicam_cv –lzbar will compile the following code which sets the Raspberry Pi’s camera resolution to 640×480 and then feed the grayscale captured frame to the Zbar scan function.

#include <opencv2/opencv.hpp>
#include <raspicam/raspicam_cv.h>
#include <zbar.h>
#include <time.h>
 
using namespace cv;
using namespace std;
using namespace zbar;

typedef struct
{
    string data;
    vector<Point>  location;
} decodedObject;

// Find and decode barcodes and QR codes
void decode(Mat &imGray, vector<decodedObject> &decodedObjects)
{

    // Create zbar scanner
    ImageScanner scanner;

    // Configure scanner
    scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 0);
    scanner.set_config(ZBAR_QRCODE, ZBAR_CFG_ENABLE, 1);

    // Wrap image data in a zbar image
    Image image(imGray.cols, imGray.rows, "Y800", (uchar *)imGray.data,  imGray.cols *imGray.rows);

    // Scan the image for barcodes and QRCodes
    int n = scanner.scan(image);

    for(Image::SymbolIterator symbol = image.symbol_begin(); symbol != image.symbol_end(); ++symbol)
    {
        decodedObject obj;

        obj.data = symbol->get_data();

        // Obtain location
        for(int i = 0; i< symbol->get_location_size(); i++)
        {
            obj.location.push_back(Point(symbol->get_location_x(i),symbol->get_location_y(i)));
        }

        decodedObjects.push_back(obj);
    }
}

// Display barcode and QR code location
void display(Mat &im, vector&decodedObjects, int FPS)
{
    // Loop over all decoded objects
    for(int i = 0; i < decodedObjects.size(); i++) { vector<Point> points = decodedObjects[i].location; vector<Point> hull; // If the points do not form a quad, find convex hull if(points.size() > 4)
            convexHull(points, hull);
        else
            hull = points;

        // Number of points in the convex hull
        int n = hull.size();

        for(int j = 0; j < n; j++)
            line(im, hull[j], hull[ (j+1) % n], Scalar(255,0,0), 3);

        rectangle(im, Rect(8,5,200,40), Scalar::all(255), -1);
        putText(im, decodedObjects[i].data, Point(8,30), CV_FONT_HERSHEY_SIMPLEX, 0.7, Scalar(0,0,139), 2);
    }

    char text[255];
    sprintf(text, "FPS: %d", FPS);
    putText(im, text, Point(im.cols-100, 20), CV_FONT_HERSHEY_SIMPLEX, 0.7, Scalar::all(255), 2);

    // Display results
    imshow("Results", im);
}


int main()
{
    clock_t start, finish;

    raspicam::RaspiCam_Cv Camera;
    Camera.set( CV_CAP_PROP_FORMAT, CV_8UC1 );
    Camera.set( CV_CAP_PROP_FRAME_HEIGHT, 480 );
    Camera.set( CV_CAP_PROP_FRAME_WIDTH, 640 );
    Camera.set( CV_CAP_PROP_FPS, 30 );

    Camera.open();
    for(;;)
    {
        start = clock();
        Mat image;
        Camera.grab();
        Camera.retrieve (image);

        if (image.empty())
            break;

        // Variable for decoded objects
        vector<decodedObject> decodedObjects;

        // Find and decode barcodes and QR codes
        decode(image, decodedObjects);

        finish = clock();
        int FPS = 1000/(1000*(finish-start)/CLOCKS_PER_SEC);

        // Display location
        display(image, decodedObjects, FPS);

        if (waitKey(1)=='q')
            break;
    }

    Camera.release();
    return EXIT_SUCCESS;
}

Raspberry Pi Zero runs this code at 3 FPS.

Accelerated install


3 FPS was too slow for me. So I decided to find a quick way to get more speed from it. What if I could download Zbar’s source code and build it with customized GCC flags? I tried the following steps and it worked :)

  • Download and extract Zbar
wget http://downloads.sourceforge.net/project/zbar/zbar/0.10/zbar-0.10.tar.bz2
tar xvf zbar-0.10.tar.bz2
  • Configure the installation. Zbar can work with Qt, Pygtk and Java which I didn’t need. I also disabled the installation of Zbarcam because it works with V4L1 which is deprecated in current versions of Linux.
cd zbar-0.10/
./configure --without-qt --without-gtk --without-python --without-java --enable-video=no
  • Set customized compiler flags.
export CFLAGS="-I/usr/local/include -march=armv6 -mfloat-abi=hard -mfpu=vfp -O3"
  • Install the Zbar
make
sudo make install
sudo ldconfig

The code could run 3 times faster with this simple trick.

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *

twenty + twenty =