Scythe Studio website employs cookies to improve your user experience (Read more)

How to scan barcodes in Qt Qml application?

Many applications require to have a barcodes scanner among other features. This feature exists for a long time as well as ready-made implementations for it. Anyway, time flies and old solutions become inefficient or not compatible at all. As a result of it, we decided to implement a barcodes wrapper for Qt & Qml projects that is up to date and follow modern code standards. 

In this post, you will learn how does it work and how to scan barcodes in your Qml application using our custom wrapper.

Features and idea behind SCodes

ZXing library is a popular library for scanning and generating barcodes & QR codes that was originally written in Java. Features provided by this library were needed in other technologies – too. That’s why this library has many ports to other programming languages. There is also a Qt C++ port, but it was initially released around 2011, so we felt that there is a need for something new.

That’s how the idea for SCodes was born. We took the most recent C++ port of ZXing library, which was coded in modern C++17 to have a base adjusted to today’s projects. This library allows you to decode both 1D and 2D barcodes – SCodes wraps this functionality in order to use it in Qt projects. The main focus was to use it with a camera in mobile Qml applications, but it is supported and runnable on desktops as well. The library allows to generate barcodes as well, but this topic will be covered in one of our next blog posts.

 

qt qml qr code scanner

 

For now SCodes allows you to scan only two types of barcodes – Code 128 and QR Code. However, allowing users to specify the barcodes types they want to scan is already on our to-do list. You can find wrapper on our Github along with the easy, step by step guide on how to include it in your project.

 

Development insights

Let’s take a quick look at how the wrapper was implemented. Base C++ ZXing library doesn’t have anything connected to Qml and camera at all. To scan barcodes, the library’s mechanisms require to get an image that is nothing else than just an array of bytes. So how to get an image from Qml Camera type? One of the solutions is to write custom QAbstractVideoFilter that allows straightforward image processing with the cooperation of Qml VideoOutput type. It can be done in this way:

 

class SBarcodeFilter : public QAbstractVideoFilter
{
    Q_OBJECT
public:
    explicit SBarcodeFilter(QObject *parent = nullptr);

    QVideoFilterRunnable * createFilterRunnable() override {
        return new SBarcodeFilterRunnable();
    }
}

You can see the exact code on SCodes’s GitHub and the code snippets are just examples. You see here that we extended QAbstractVideoFilter by overriding the createFilterRunnable method. This method is used to return an instance of the corresponding subclass of QVideoFilterRunnable. It’s necessary to process image decoding in a dedicated thread, so let’s take a look now at how it can look like.

class SBarcodeFilterRunnable: public QVideoFilterRunnable
{

public:
    SBarcodeFilterRunnable() { }

    QVideoFrame run(QVideoFrame *input,
                    const QVideoSurfaceFormat &surfaceFormat,
                    QVideoFilterRunnable::RunFlags flags) override
    {
        Q_UNUSED(surfaceFormat);
        Q_UNUSED(flags);

        const QImage croppedCapturedImage = SBarcodeDecoder::videoFrameToImage(*input, _filter->captureRect().toRect());
        // you have an image now. Have fun with it.

        return *input;
    }
};

In the subclass of QVideoFilterRunnable we need to override run(…) method in order to asynchronously process the input video frame. To get an instance of QImage object form QVideoFrame we implemented a custom static method that requires QVideoFrame and QRect as parameters. The second one could be unnecessary if you use video filters for other purposes, but here there is a possibility to scan the selected part of the screen and for this rectangle, a parameter is used.

QImage SBarcodeDecoder::videoFrameToImage(const QVideoFrame &videoFrame, const QRect &captureRect)
{
    if (videoFrame.handleType() == QAbstractVideoBuffer::NoHandle) {
        const QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(videoFrame.pixelFormat());
        QImage image(videoFrame.bits(),
                     videoFrame.width(),
                     videoFrame.height(),
                     videoFrame.bytesPerLine(),
                     imageFormat);

        if (image.isNull()) {
            return QImage();
        }

        if ( image.format() != QImage::Format_ARGB32) {
            image = image.convertToFormat(QImage::Format_ARGB32);
        }

        return image.copy(captureRect);
    }

    if (videoFrame.handleType() == QAbstractVideoBuffer::GLTextureHandle) {
        QImage image(videoFrame.width(), videoFrame.height(), QImage::Format_ARGB32);
        GLuint textureId = static_cast<GLuint>(videoFrame.handle().toInt());
        QOpenGLContext* ctx = QOpenGLContext::currentContext();
        QOpenGLFunctions* f = ctx->functions();
        GLuint fbo;
        f->glGenFramebuffers( 1, &fbo);
        GLint prevFbo;
        f->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFbo);
        f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0);
        f->glReadPixels(0, 0,  videoFrame.width(),  videoFrame.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.bits());
        f->glBindFramebuffer( GL_FRAMEBUFFER, static_cast<GLuint>( prevFbo ) );
        return image.rgbSwapped().copy(captureRect);
    }

    return QImage();
}

Here we adjusted the video according to the video frame’s handle type. Once we have our image, we can further process it.

 

But how to actually use our custom video filter it in Qml? First, you need to expose your class to Qml. To do that you need to run this single line for example in your main.cpp file:

qmlRegisterType<SBarcodeFilter>("com.scythestudio.scodes", 1, 0, "SBarcodeFilter");

Qt 5.15 comes with a new approach in exposing C++ classes to Qml. All you need to do is to put this macro into your class declaration:

QML_ELEMENT

Then go to your .pro/.pri file and add these two lines:

QML_IMPORT_NAME = com.scythestudio.scodes
QML_IMPORT_MAJOR_VERSION = 1

Change the import name to any string you like and use the import statement at the beginning of your Qml file.

Once you have class exposed to Qml you need to create Camera, VideoOutput, and SBarcodeFilter objects in your .qml file.

import QtQuick 2.12
import QtMultimedia 5.12
import com.scythestudio.scodes1.0
 

ApplicationWindow {
  id: root
  visible: true

  Camera {
    id: camera
    // ...
  }

  VideoOutput {
    id: videoOutput

    source: camera
    anchors.fill: parent
    autoOrientation: true
    fillMode: VideoOutput.PreserveAspectCrop
    // add barcodeFilter to videoOutput's filters to enable catching barcodes

    filters: [
      barcodeFilter
    ]
  }

  SBarcodeFilter {
    id: barcodeFilter
    // ...
  }
}

Set the source of video output to be a camera object and put your filter among video output’s filters and let the magic happen. The video will be processed in the background asynchronously.

Summary and development plans

Visit our Github to get a SCodes wrapper if you didn’t do that yet. Currently, wrapper allows us to scan barcodes in Code 128 and QR Code formats, but we still plan some improvements like support for other barcodes formats or the equivalent of SCodes.pri with CMake to allow easy usage. Stay tuned!

Latest posts