What is Dotcode?
Dotcode is a type of two-dimensional barcode used in various applications, such as retail supply chain management, product monitoring, mobile payment processing, event ticketing, and user identification. They provide efficient data collection with high levels of accuracy, leading to better productivity and enhanced user experiences. In this article, we will share a webcam scanner sample implemented in C++ to read DotCodes.
Reading DotCode with Dynamsoft Barcode SDK
We will be using the Dynamsoft Barcode Reader SDK for this project. You can download it here.
OpenCV is an excellent choice for getting the webcam video stream. We'll be using this version- OpenCV 3.4.7.
If you want to use the latest OpenCV library, you need to update the relevant linking option in the CMakeLists.txt file.
Here's an example:
target_link_libraries (BarcodeReader "DBRx64" "opencv_core347d.lib" "opencv_highgui347d.lib" "opencv_videoio347d.lib" "opencv_imgcodecs347d.lib" "opencv_imgproc347d.lib")
An Introduction to the APIs of Dynamsoft Barcode SDK
Dynamsoft offers flexible APIs for adapting barcode algorithms to different scenarios. In the case of decoding barcodes from webcam video frames, you have two options:
- Invoke decodeBuffer() in a worker thread and make a frame-sieving strategy by yourself.
- Invoke a set of video decoding APIs, including StartFrameDecoding(), StopFrameDecoding(), and AppendFrame().
With the addition of threading and a frame-sieving method, video decoding APIs are significantly easier to use. All that's left is for you to process the returned information using callback functions.
Reading DotCode from a video stream
Let's instantiate the barcode scanner and configure the parameters:
// Get license from https://www.dynamsoft.com/customer/license/trialLicense/
CBarcodeReader reader = reader.InitLicense("LICENSE-LEY");
PublicRuntimeSettings runtimeSettings;
char szErrorMsg[256];
reader.InitRuntimeSettingsWithString("{\"ImageParameter\":{\"Name\":\"BestCoverage\",\"DeblurLevel\":9,\"ExpectedBarcodesCount\":512,\"ScaleDownThreshold\":100000,\"LocalizationModes\":[{\"Mode\":\"LM_CONNECTED_BLOCKS\"},{\"Mode\":\"LM_SCAN_DIRECTLY\"},{\"Mode\":\"LM_STATISTICS\"},{\"Mode\":\"LM_LINES\"},{\"Mode\":\"LM_STATISTICS_MARKS\"}],\"GrayscaleTransformationModes\":[{\"Mode\":\"GTM_ORIGINAL\"},{\"Mode\":\"GTM_INVERTED\"}]}}", CM_OVERWRITE, szErrorMsg, 256);
reader.GetRuntimeSettings( & runtimeSettings);
runtimeSettings.barcodeFormatIds = BF_ALL;
runtimeSettings.barcodeFormatIds_2 = BF2_POSTALCODE | BF2_DOTCODE;
runtimeSettings.intermediateResultTypes = IRT_ORIGINAL_IMAGE;
reader.UpdateRuntimeSettings( & runtimeSettings, szErrorMsg, 256);
reader.SetTextResultCallback(textResultCallback, NULL);
reader.SetIntermediateResultCallback(intermediateResultCallback, NULL);
reader.SetErrorCallback(errorcb, NULL);
Now, initialize and start the video decoding thread:
reader.StartFrameDecoding(10, 10, width, height, frame.step.p[0], IPF_RGB_888, "");
Append frames in the video capture loop:
for (;;) {
int key = waitKey(10);
if ((key & 0xff) == 27 /*ESC*/ ) break;
capture >> frame; // read the next frame from camera
if (frame.empty()) {
cerr << "ERROR: Can't grab camera frame." << endl;
break;
}
reader.AppendFrame(frame.data);
imshow("Dynamsoft Barcode Reader", frame);
}
Get the results in the textResultCallback() function:
void textResultCallback(int frameId, TextResultArray * pResults, void * pUser) {
char * pszTemp = NULL;
char * pszTemp1 = NULL;
char * pszTemp2 = NULL;
pszTemp = (char * ) malloc(4096);
for (int iIndex = 0; iIndex < pResults -> resultsCount; iIndex++) {
snprintf(pszTemp, 4096, "Barcode %d:\r\n", iIndex + 1);
printf(pszTemp);
snprintf(pszTemp, 4096, " Type: %s\r\n", pResults -> results[iIndex] -> barcodeFormatString_2);
printf(pszTemp);
snprintf(pszTemp, 4096, " Value: %s\r\n", pResults -> results[iIndex] -> barcodeText);
printf(pszTemp);
pszTemp1 = (char * ) malloc(pResults -> results[iIndex] -> barcodeBytesLength * 3 + 1);
pszTemp2 = (char * ) malloc(pResults -> results[iIndex] -> barcodeBytesLength * 3 + 100);
ToHexString(pResults -> results[iIndex] -> barcodeBytes, pResults -> results[iIndex] -> barcodeBytesLength, pszTemp1);
snprintf(pszTemp2, pResults -> results[iIndex] -> barcodeBytesLength * 3 + 100, " Hex Data: %s\r\n", pszTemp1);
printf(pszTemp2);
free(pszTemp1);
free(pszTemp2);
}
free(pszTemp);
}
You can assign the frame id to a global variable to get the corresponding image in the intermediateResultCallback() function:
void intermediateResultCallback(int frameId, IntermediateResultArray * pResults, void * pUser) {
if (id == frameId) {}
}
Get the image pointer:
ImageData* tempImageData = (ImageData*)(pResults->results[0]->results[0]);
Convert bytes to Mat type:
Mat resultImage = Mat(tempImageData->height, tempImageData->width, CV_8UC3, tempImageData->bytes);
Draw the barcode positions by points:
TextResult * barcode = results -> results[i];
int x1 = barcode -> localizationResult -> x1;
int y1 = barcode -> localizationResult -> y1;
int x2 = barcode -> localizationResult -> x2;
int y2 = barcode -> localizationResult -> y2;
int x3 = barcode -> localizationResult -> x3;
int y3 = barcode -> localizationResult -> y3;
int x4 = barcode -> localizationResult -> x4;
int y4 = barcode -> localizationResult -> y4;
line(resultImage, Point(x1, y1), Point(x2, y2), color, thickness);
line(resultImage, Point(x2, y2), Point(x3, y3), color, thickness);
line(resultImage, Point(x3, y3), Point(x4, y4), color, thickness);
line(resultImage, Point(x4, y4), Point(x1, y1), color, thickness);
Since the imshow() function must be called on the main thread, we can't display the picture immediately. Get the Mat data globally accessible and render it in the video capture loop:
for (;;) {
int key = waitKey(10);
if ((key & 0xff) == 27 /*ESC*/ ) break;
if (!isVideoRunning) {
if (isResultReady) {
imshow("Dynamsoft Barcode Reader", resultImage);
break;
}
continue;
}
capture >> frame; // read the next frame from camera
if (frame.empty()) {
cerr << "ERROR: Can't grab camera frame." << endl;
break;
}
reader.AppendFrame(frame.data);
imshow("Dynamsoft Barcode Reader", frame);
}
Finally, stop the barcode reading thread and then exit the program:
reader.StopFrameDecoding();
Build and run the app
To run the app, we need to execute the following commands in cmd.exe to build and run the program:
mkdir build
cd build
cmake -G"Visual Studio 15 2017 Win64" ..
cmake --build .
.\debug\BarcodeReader.exe
Once you've received the barcode results, you can press any key to quit the app.
Source Code