26
How to Decode QR Code from WebP Images in C++ and Python
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.
Get a valid license key from the Dynamsoft Customer Portal and save it to a license.txt file.
To learn WebP relevant API and build the library for cross-platform barcode reading applications, we download its source code.
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
.
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
:
target_link_libraries (${PROJECT_NAME} "DynamsoftBarcodeReader" pthread "imageioutil")
The build configuration is done. We can now start coding.
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:
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
.
#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:
#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
:
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
:
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,
function.
size_t data_size, int* const width, int* const height,
WebPDecBuffer* const keep_info)
To keep all the information we want, we create a new function:
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
:
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:
WebPFreeDecBuffer(&output);
WebPDataClear(&webp_data);
Build and run the program in terminal:
mkdir build
cd build
cmake ..
cmake --build .
BarcodeReader <webp file> license.txt
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
:
pip install dbr opencv-python
OpenCV supports WebP decoding, so it saves us from the hassle of decoding the WebP image:
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()
26