How To Scan Barcodes in Qt QML Application?

Qt QML development
2020-09-21
8 minutes
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.

 

3 Tips to Improve Barcode Scanning

 

Good Camera is Crucial for Successful Barcode Scanning

The camera should have a high resolution and a fast frame rate to ensure that the barcode is captured clearly and quickly. A camera with a wide-angle lens is also recommended to allow for more flexibility in positioning the barcode in the field of view.

 

Lighting is An Important factor when Scanning Barcodes

The barcode should be well-lit to ensure that it is clearly visible to the camera. Avoid scanning in dimly lit areas or areas with strong backlight as this can make it difficult to read the barcode. It’s recommended to have a good lighting in the area you scan the barcode.

 

Putting The Barcode in The Scanning Rectangle is An Important Step

The scanning rectangle is the area on the screen where the camera looks for the barcode. This rectangle should be positioned so that the barcode is centered and fills as much of the rectangle as possible. This will help to ensure that the barcode is read correctly and that the scanning process is as efficient as possible. Additionally, consider the size and orientation of the barcode, and adjust the rectangle accordingly to optimize the scanning process.

 

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.

 

Summary

Discover the power of cross-platform development with Qt. Let Scythe Studio help you create fast, reliable, and responsive software on multiple platforms including desktop, mobile, and embedded environments. Enjoy lightning-fast performance with our C++ based core and build-in modules and APIs. Trust our team of Qt experts to guide you through every step of the development process, from initial design to ongoing support and maintenance. Contact us today to learn how we can help you take your Qt QML projects to the next level.

 

FAQs

 

Why are barcodes not scanning properly?

Barcodes may not scan properly for several reasons, which can be attributed to the equipment not being suitable for the barcodes, the scanner not being operated correctly, or the barcode labels not being suitable for the intended application or environment.

 

Can you scan a QR code internally without using a third-party app?

The latest versions of Android and iOS devices support scanning QR codes internally using Google Lens or the Live text option, so using a third-party QR code scanner app is not necessary to scan a QR code from the gallery.

 

How can I determine if a QR code is static or dynamic?

You cannot alter static QR codes once they are created and printed. If the linked information becomes outdated, you must reprint the QR code. Dynamic QR codes, however, can be edited and updated in real time.

 

How can you distinguish between a Code 128 and Code 39 barcode?

One way to tell the difference between a Code 128 and Code 39 barcode is by looking at the number of characters they can encode. Code 39 barcodes can only encode 39 characters, while Code 128 barcodes have a higher density and can encode more characters. Additionally, Code 39 barcodes do not include a check digit, while Code 128 barcodes do.

Scythe-Studio - Chief Executive Officer

Łukasz Kosiński Chief Executive Officer

Need Qt QML development services?

service partner

Let's face it? It is a challenge to get top Qt QML developers on board. Help yourself and start the collaboration with Scythe Studio - real experts in Qt C++ framework.

Discover our capabilities

Latest posts

[ 134 ]