How to Make DotCode Webcam Scanner in C++ on Windows 10

Previously, I wrote an article showing how to build a DotCode reader in Java. I also updated my C# barcode demo to make it support DotCode decoding recently. In this article, I will share a new webcam scanner sample implemented in C++.

Reading DotCode with Dynamsoft Barcode SDK

Download Dynamsoft Barcode Reader v7.4.

OpenCV is always my first choice for getting the webcam video stream. My version is OpenCV 3.4.7. If you want to use the latest OpenCV library, you have to update the relevant linking option in CMakeLists.txt file. Here’s mine:

target_link_libraries (BarcodeReader "DBRx64" "opencv_core347d.lib" "opencv_highgui347d.lib" "opencv_videoio347d.lib" "opencv_imgcodecs347d.lib" "opencv_imgproc347d.lib")

What you need to know about the APIs of Dynamsoft Barcode SDK

Dynamsoft provides 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().

Using video decoding APIs is much simpler because it has implemented threading and a frame sieving algorithm. The remaining work for you is to handle the returned data in callback functions.

Read DotCode from video stream

Instantiate barcode reader and configure parameters:

// Get license from https://www.dynamsoft.com/CustomerPortal/Portal/Triallicense.aspx
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);

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 and 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);

We cannot show the image instantly, for the imshow() function must be called in the main thread. Make the Mat data globally accessible and render it in the video capture loop:

    for (;;)
    {
        int key = waitKey(10);
        if ((key &amp; 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

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
dotcode c++

After getting the barcode results, press any key to quit the app.

Source Code

https://github.com/yushulx/dotcode-webcam-scanner