How to Decode QR Code from WebP Images in C++ and Python
Tags: webp, qrcode, cpp, python
Published: 2021-09-30 02:44:25

Dynamsoft Barcode Reader currently does not support WebP images. If you download a QR code image encoded with WebP format, how can you recognize the QR code with Dynamsoft Barcode Reader? In this article, we combine libwebp and Dynamsoft Barcode Reader to fulfill decoding QR code from WebP images.

## Installation
- [Dynamsoft Barcode Reader C++ SDK](https://www.dynamsoft.com/barcode-reader/downloads/)
- [libwebp](https://developers.google.com/speed/webp/download)

## SDK License Key
Get a valid license key from the [Dynamsoft Customer Portal](https://www.dynamsoft.com/customer/license/trialLicense?product=dbr) and save it to a license.txt file.

## CMake Project Configuration for libwebp
To learn WebP relevant API and build the library for cross-platform barcode reading applications, we download its [source code](https://chromium.googlesource.com/webm/libwebp/).

Since the WebP source code is configured with CMake, we just need to copy the source code folder to our project root directory and add the folder as the subdirectory of the current project in `CMakeLists.txt`.

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

When building the whole project, it will intermediately build the `imageioutil` library which is used to decode WebP format to RGB format.

Thus, one more change is to link the `imageioutil` library to our executable file in `CMakeLists.txt`:

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

The build configuration is done. We can now start coding.

## Decoding QR Code from WebP Images 
The examples included in the libwebp package are the best crash course for learning WebP API.

According to the source code of `examples/webpinfo.c`, we can get the file name from the command line arguments as follows:

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

Why in this way? 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`.

```cpp

#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
```

`WebPData` is a structure that contains the WebP image data. As we get the file name from the command line arguments, we can call `ImgIoUtilReadFile()` to read the WebP image data:

```cpp
#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 decoded from WebP. The required parameters include the pointer to the RGBA data, the width, height, and stride of the image. How can we get these parameters?

There are some decoding functions defined in `src/webp/decode.h`:

```cpp
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);
...
```

And the corresponding implementations are in `src/dec/webp_dec.c`:

```cpp
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);
}
```

All of the existing decoding functions only return the pointer to the decoded data. We have noticed that they all set `NULL` to the last parameter when calling the `static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* const data,
                       size_t data_size, int* const width, int* const height,
                       WebPDecBuffer* const keep_info)` function.

To keep all the information we want, we create a new function:

```cpp
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);
}

```

Afterward, calling the function can obtain the data passed to `DBR_DecodeBuffer`:

```cpp
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, "");
```

Finally, do not forget to free the memory:

```cpp
WebPFreeDecBuffer(&output);
WebPDataClear(&webp_data);
```

Build and run the program in terminal:

```bash
mkdir build
cd build
cmake ..
cmake --build .
BarcodeReader <webp file> license.txt
```

![Decode QR Code from WebP Images in C++](https://www.dynamsoft.com/codepool/img/2021/09/webp-qr-code-decode-cpp.jpg)

## A Mush Easier Way with Python
It takes a bit of time to get the program running in C++. Nevertheless, it is easy to write the same program in Python. 

Let's install `dbr` and `opencv-python`:

```bash
pip install dbr opencv-python
```

OpenCV supports WebP decoding, so it saves us from the hassle of decoding the WebP image:

```python
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/QR-recognition-from-webp](https://github.com/yushulx/QR-recognition-from-webp)