How to Decode Barcode and QR Code from WebP Images in C++ and Python

WebP images enhance web performance, but decoding barcodes embedded in them can be a challenge since Dynamsoft Barcode Reader doesn’t support WebP directly. If you have a QR code in WebP format, how can you recognize it with Dynamsoft Barcode Reader? This article shows how to use libwebp with Dynamsoft Barcode Reader to decode QR codes from WebP images.

Prerequisites

License Key

Get a 30-day free trial license key and save it in a file named license.txt.

CMake Project Configuration for libwebp

To learn the WebP API and build the library for cross-platform barcode reading applications, download the source code.

Since the WebP source code is configured with CMake, copy the source code folder to your project root directory and add it as a subdirectory in CMakeLists.txt.

add_subdirectory("${PROJECT_SOURCE_DIR}/libwebp-1.2.1")

When building the project, it will build the imageioutil library, which is used to decode WebP format to RGB format. Link the imageioutil library to your executable in CMakeLists.txt:

target_link_libraries (${PROJECT_NAME} "DynamsoftBarcodeReader" pthread "imageioutil")

The full CMakeLists.txt file is as follows:

cmake_minimum_required (VERSION 3.8)
project (main)
MESSAGE( STATUS "PROJECT_NAME: " ${PROJECT_NAME} )

option(ARM32_BUILD "Build for ARM32" OFF)

# Check ../../../../platforms
if (CMAKE_HOST_WIN32)
    set(WINDOWS 1)
elseif(CMAKE_HOST_APPLE)
    set(MACOS 1)
elseif(CMAKE_HOST_UNIX)
    set(LINUX 1)
endif()

# Check compiler architecture
if(CMAKE_CL_64)
    MESSAGE( STATUS ">>>>>>>> 64-bit")
else()
    MESSAGE( STATUS ">>>>>>>> 32-bit")
endif()

# Check compilers
MESSAGE( STATUS ">>>>>>>> ${CMAKE_CXX_COMPILER_ID}")
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
    MESSAGE( STATUS "Using Clang" )
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
    MESSAGE( STATUS "Using GNU" )
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
    MESSAGE( STATUS "Using Intel" )
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
    MESSAGE( STATUS "Using MSVC" )
endif()

# Set RPATH
if(CMAKE_HOST_UNIX)
    if(CMAKE_HOST_APPLE)
        SET(CMAKE_CXX_FLAGS "-std=c++11 -O3 -Wl,-rpath,@loader_path")
        SET(CMAKE_INSTALL_RPATH "@loader_path")
    else()
        SET(CMAKE_CXX_FLAGS "-std=c++11 -O3 -Wl,-rpath=$ORIGIN")
        SET(CMAKE_INSTALL_RPATH "$ORIGIN")
    endif()
    SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
endif()

add_subdirectory("${PROJECT_SOURCE_DIR}/libwebp-1.2.1")

# Add search path for include and lib files
MESSAGE( STATUS "CPU architecture ${CMAKE_SYSTEM_PROCESSOR}" )
if(WINDOWS)
    if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
        link_directories("${PROJECT_SOURCE_DIR}/../../../../platforms/win/bin/") 
    else()
        link_directories("${PROJECT_SOURCE_DIR}/../../../../platforms/win/lib/") 
    endif()
elseif(LINUX)
    if (CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64)
        MESSAGE( STATUS "Link directory: ${PROJECT_SOURCE_DIR}/../../../../platforms/linux/" )
        link_directories("${PROJECT_SOURCE_DIR}/../../../../platforms/linux/")
    elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL armv7l OR ARM32_BUILD)
        MESSAGE( STATUS "Link directory: ${PROJECT_SOURCE_DIR}/../../../../platforms/arm32/" )
        link_directories("${PROJECT_SOURCE_DIR}/../../../../platforms/arm32/") 
    elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64) 
        MESSAGE( STATUS "Link directory: ${PROJECT_SOURCE_DIR}/../../../../platforms/aarch64/" )
        link_directories("${PROJECT_SOURCE_DIR}/../../../../platforms/aarch64/") 
    endif()
elseif(MACOS)
    MESSAGE( STATUS "Link directory: ${PROJECT_SOURCE_DIR}/../../../../platforms/macos/" )
    link_directories("${PROJECT_SOURCE_DIR}/../../../../platforms/macos/") 
endif()
include_directories("${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/../../../../include/")

# Add the executable
add_executable(${PROJECT_NAME} main.cxx)
if(WINDOWS)
    if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
        target_link_libraries (${PROJECT_NAME} "DynamsoftBarcodeReaderx64")
    else()
        if(CMAKE_CL_64)
            target_link_libraries (${PROJECT_NAME} "DBRx64" "imageioutil")
        else()
            target_link_libraries (${PROJECT_NAME} "DBRx86" "imageioutil")
        endif()
    endif()
else()
    target_link_libraries (${PROJECT_NAME} "DynamsoftBarcodeReader" pthread "imageioutil")
endif()

if(WINDOWS)
    add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 
    COMMAND ${CMAKE_COMMAND} -E copy_directory
    "${PROJECT_SOURCE_DIR}/../../../../platforms/win/bin/"      
    $<TARGET_FILE_DIR:main>)
endif()

Decoding Barcode and QR Code from WebP Images

The examples in the libwebp package are great for learning the WebP API. According to examples/webpinfo.c, we can get the file name from the command line arguments as follows:

const W_CHAR* in_file = GET_WARGV(argv, 1);

For Windows, wchar_t is used under the hood. To make the code compatible with both Windows and UNIX-like systems, W_CHAR and GET_WARGV are defined in examples/unicode.h.


#if defined(_WIN32)
#include <wchar.h>
#include <windows.h>
#include <shellapi.h>

// Create a wchar_t array containing Unicode parameters.
#define INIT_WARGV(ARGC, ARGV)                                                \
  int wargc;                                                                  \
  const W_CHAR** const wargv =                                                \
      (const W_CHAR**)CommandLineToArgvW(GetCommandLineW(), &wargc);          \
  do {                                                                        \
    if (wargv == NULL || wargc != (ARGC)) {                                   \
      fprintf(stderr, "Error: Unable to get Unicode arguments.\n");           \
      FREE_WARGV_AND_RETURN(-1);                                              \
    }                                                                         \
  } while (0)

#define GET_WARGV(UNUSED, C) wargv[C]
#define W_CHAR wchar_t
#else
#define INIT_WARGV(ARGC, ARGV)
#define GET_WARGV(ARGV, C) (ARGV)[C]
#define W_CHAR char

Once we get the file name from the command line arguments, we can call ImgIoUtilReadFile() to read the WebP image data to the WebPData structure:

#include "webp/mux_types.h"
#include "webp/decode.h"
#include "libwebp-1.2.1/imageio/imageio_util.h"

int ExUtilReadFileToWebPData(const char* const filename,
                             WebPData* const webp_data) {
  const uint8_t* data;
  size_t size;
  if (webp_data == NULL) return 0;
  if (!ImgIoUtilReadFile(filename, &data, &size)) return 0;
  webp_data->bytes = data;
  webp_data->size = size;
  return 1;
}

const W_CHAR* in_file = GET_WARGV(argv, 1);
WebPData webp_data;
int ok = ExUtilReadFileToWebPData((const char*)in_file, &webp_data);

We will use the DBR_DecodeBuffer method of Dynamsoft Barcode Reader to decode the RGBA data from WebP. The required parameters include the pointer to the RGBA data, the width, height, and stride of the image. We can get these parameters using the decoding functions defined in src/webp/decode.h:

WEBP_EXTERN uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size,
                                    int* width, int* height);
WEBP_EXTERN uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size,
                                    int* width, int* height);
WEBP_EXTERN uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
                                    int* width, int* height);
...

The corresponding functions are implemented in src/dec/webp_dec.c:

uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size,
                        int* width, int* height) {
  return Decode(MODE_RGBA, data, data_size, width, height, NULL);
}

uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size,
                        int* width, int* height) {
  return Decode(MODE_ARGB, data, data_size, width, height, NULL);
}

uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
                        int* width, int* height) {
  return Decode(MODE_BGRA, data, data_size, width, height, NULL);
}

In the example code, all existing decoding functions only return the pointer to the decoded data. To retain all the necessary information, we create a new function to obtain the parameters required for DBR_DecodeBuffer:

WEBP_EXTERN void GetRGBAInfo(const uint8_t* data, size_t data_size,
                        int* width, int* height, WebPDecBuffer *output);

void GetRGBAInfo(const uint8_t* data, size_t data_size,
                        int* width, int* height, WebPDecBuffer *output) {

  Decode(MODE_RGBA, data, data_size, width, height, output);
}

Steps to Implement a C++ Program to Decode Barcode and QR Codes from WebP Images

  1. Set the license key of Dynamsoft Barcode Reader.

     char errorMsgBuffer[512];
     int ret = DBR_InitLicense("LICENSE-KEY", errorMsgBuffer, 512);
     printf("DBR_InitLicense: %s\n", errorMsgBuffer);
    
  2. Initialize the barcode reader.

     void *reader = DBR_CreateInstance();
    
  3. Decode the WebP image and read barcodes from it.

     int width, height;
         WebPGetInfo(webp_data.bytes, webp_data.size, &width, &height);
         WebPDecBuffer output;
         GetRGBAInfo(webp_data.bytes, webp_data.size, &width, &height, &output);
     DBR_DecodeBuffer(reader, output.u.RGBA.rgba, width, height, output.u.RGBA.stride, IPF_ARGB_8888, "");
    
     TextResultArray *paryResult = NULL;
     DBR_GetAllTextResults(reader, &paryResult);
    
     if (paryResult->resultsCount == 0)
     {
       printf("No barcode found.\n");
       DBR_FreeTextResults(&paryResult);
       return -1;
     }
    
     printf("Total barcode(s) found: %d. Time cost: %d ms\n\n", paryResult->resultsCount, timecost);
    
     for (int index = 0; index < paryResult->resultsCount; index++)
     {
       printf("Barcode %d:\n", index + 1);
       printf("    Type: %s\n", paryResult->results[index]->barcodeFormatString);
       printf("    Text: %s\n", paryResult->results[index]->barcodeText);
     }
    
     DBR_FreeTextResults(&paryResult);
    
     DBR_DestroyInstance(reader);
    
     WebPFreeDecBuffer(&output);
     WebPDataClear(&webp_data);
    
  4. Build and run the program in the terminal:

     mkdir build
     cd build
     cmake ..
     cmake --build .
     ./main <webp file> license.txt
    

    Decode QR Code from WebP Images in C++

An Easier Way with Python

While it takes some effort to get the C++ program running, it’s much easier to write the same program in Python.

First, install dbr and opencv-python:

pip install dbr opencv-python

OpenCV supports WebP decoding, which simplifies the process:

from dbr import *
import cv2

def main():
    try:
        filename = sys.argv[1]
        license = ""

        if len(sys.argv) > 2:
            with open(sys.argv[2]) as f:
                license = f.read()

        frame = cv2.imread(filename)
        reader = BarcodeReader()
        ret = reader.init_license(license)
        print('License status {0}'.format(ret))
            
        results = reader.decode_buffer(frame)
        index = 0
        for result in results:
            points = result.localization_result.localization_points
            print("Index: " + str(index) + "\n")
            print("     Barcode format: " + result.barcode_format_string + '\n')
            print("     Barcode value: " + result.barcode_text + '\n')
            print("     Points: " + str(points[0]) + ' ' + str(points[1]) + ' ' + str(points[2]) + ' ' + str(points[3]) + '\n')
            print('-----------------------------------\n')
            index += 1

    except:
        print(__doc__)

if __name__ == '__main__':
    main()

Source Code

https://github.com/yushulx/cmake-cpp-barcode-qrcode/tree/main/examples/9.x/webp