掌握使用 C++ 扫描包裹:条形码和 OCR 文本提取

在本文中,我们将继续介绍包裹扫描技术系列,深入研究 C++ 世界。在之前的 JavaScript 教程的基础上,我们将探索如何使用 C++ 实现条形码扫描和 OCR 文本提取。本指南将介绍如何设置开发环境、集成必要的库,并提供分步教程,以使用 C++ 为WindowsLinux平台创建强大的包裹扫描应用程序。

掌握使用 C++ 扫描包裹:条形码和 OCR 文本提取-LMLPHP

先决条件

Dynamsoft条形码阅读器Dynamsoft 标签识别器是 Dynamsoft Capture Vision Framework 的组成部分。条形码阅读器专为条形码扫描而设计,而标签识别器则专注于 OCR 文本识别。这些工具共享几个通用组件和头文件。要有效地同时使用这两个工具,只需将两个包合并到一个目录中即可。以下是您的设置的最终目录结构。

<span style="color:#212529"><span style="background-color:#eaeaea"><code>|- SDK
    |- include
        |- DynamsoftBarcodeReader.h
        |- DynamsoftCaptureVisionRouter.h
        |- DynamsoftCodeParser.h
        |- DynamsoftCore.h
        |- DynamsoftDocumentNormalizer.h
        |- DynamsoftImageProcessing.h
        |- DynamsoftLabelRecognizer.h
        |- DynamsoftLicense.h
        |- DynamsoftUtility.h
    |- platforms
        |- linux
            |- libDynamicImage.so
            |- libDynamicPdf.so
            |- libDynamicPdfCore.so
            |- libDynamsoftBarcodeReader.so
            |- libDynamsoftCaptureVisionRouter.so
            |- libDynamsoftCore.so
            |- libDynamsoftImageProcessing.so
            |- libDynamsoftLabelRecognizer.so
            |- libDynamsoftLicense.so
            |- libDynamsoftNeuralNetwork.so
            |- libDynamsoftUtility.so
        |- win
            |- bin
                |- DynamicImagex64.dll
                |- DynamicPdfCorex64.dll
                |- DynamicPdfx64.dll
                |- DynamsoftBarcodeReaderx64.dll
                |- DynamsoftCaptureVisionRouterx64.dll
                |- DynamsoftCorex64.dll
                |- DynamsoftImageProcessingx64.dll
                |- DynamsoftLabelRecognizerx64.dll
                |- DynamsoftLicensex64.dll
                |- DynamsoftNeuralNetworkx64.dll
                |- DynamsoftUtilityx64.dll
                |- vcomp140.dll
            |- lib
                |- DynamsoftBarcodeReaderx64.lib
                |- DynamsoftCaptureVisionRouterx64.lib
                |- DynamsoftCorex64.lib
                |- DynamsoftImageProcessingx64.lib
                |- DynamsoftLabelRecognizerx64.lib
                |- DynamsoftLicensex64.lib
                |- DynamsoftNeuralNetworkx64.lib
                |- DynamsoftUtilityx64.lib
    |- CharacterModel
    |- DBR-PresetTemplates.json
    |- DLR-PresetTemplates.json
</code></span></span>

配置 CMakeLists.txt 以构建 C++ 应用程序

目标项目依赖头文件、共享库、JSON格式的模板以及神经网络模型。为了简化构建过程,我们可以使用CMake来管理依赖项并生成不同平台的项目文件。

  1. 指定头文件和共享库的目录:

     if(WINDOWS)
         if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
             link_directories("${PROJECT_SOURCE_DIR}/../sdk/platforms/win/bin/") 
         else()
             link_directories("${PROJECT_SOURCE_DIR}/../sdk/platforms/win/lib/") 
         endif()
     elseif(LINUX)
         if (CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64)
             MESSAGE( STATUS "Link directory: ${PROJECT_SOURCE_DIR}/../sdk/platforms/linux/" )
             link_directories("${PROJECT_SOURCE_DIR}/../sdk/platforms/linux/")
         endif()
     endif()
     include_directories("${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/../sdk/include/")
  2. 链接所需的库:

     add_executable(${PROJECT_NAME} main.cxx)
     if(WINDOWS)
         if(CMAKE_CL_64)
             target_link_libraries (${PROJECT_NAME} "DynamsoftCorex64" "DynamsoftLicensex64" "DynamsoftCaptureVisionRouterx64" "DynamsoftUtilityx64" ${OpenCV_LIBS})
         endif()
     else()
         target_link_libraries (${PROJECT_NAME} "DynamsoftCore" "DynamsoftLicense" "DynamsoftCaptureVisionRouter" "DynamsoftUtility" pthread ${OpenCV_LIBS})
     endif()
  3. 将所需的共享库、JSON 文件和神经网络模型复制到输出目录:

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

使用 C++ 实现条形码扫描和 OCR 文本提取

为了快速开始使用 API,我们可以参考 Dynamsoft 提供的条形码示例代码VideoDecoding.cpp。示例代码演示了如何使用 OpenCV 从视频帧解码条形码。基于此示例代码,我们可以使用 Dynamsoft Label Recognizer 添加 OCR 文本提取功能。让我们逐步了解如何在 C++ 中实现条形码扫描和 OCR 文本提取。

步骤 1:包含 SDK 头文件

要将Dynamsoft Barcode ReaderDynamsoft Label Recognizer一起使用,我们需要包含两个头文件:DynamsoftCaptureVisionRouter.hDynamsoftUtility.h,它们包含所有必要的类和函数。OpenCV 头文件可能因您使用的版本而异。以下代码片段显示了示例代码中使用的头文件和命名空间:

#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/videoio.hpp"
#include "opencv2/core/utility.hpp"
#include "opencv2/imgcodecs.hpp"
#include <iostream>
#include <vector>
#include <chrono>
#include <iostream>
#include <string>

#include "DynamsoftCaptureVisionRouter.h"
#include "DynamsoftUtility.h"

using namespace std;
using namespace dynamsoft::license;
using namespace dynamsoft::cvr;
using namespace dynamsoft::dlr;
using namespace dynamsoft::dbr;
using namespace dynamsoft::utility;
using namespace dynamsoft::basic_structures;
using namespace cv;

第 2 步:设置许可证密钥

申请试用许可证密钥时,您可以选择单个产品密钥或多个产品的组合密钥。在此示例中,我们需要设置一个组合许可证密钥来激活两个 SDK 模块:

int iRet = CLicenseManager::InitLicense("LICENSE-KEY");

步骤 3:创建用于缓冲视频帧的类

CImageSourceAdapter用于获取和缓冲图像帧。我们可以创建一个继承自的自定义类CImageSourceAdapter

class MyVideoFetcher : public CImageSourceAdapter
{
public:
    MyVideoFetcher(){};
    ~MyVideoFetcher(){};
    bool HasNextImageToFetch() const override
    {
        return true;
    }
    void MyAddImageToBuffer(const CImageData *img, bool bClone = true)
    {
        AddImageToBuffer(img, bClone);
    }
};

MyVideoFetcher *fetcher = new MyVideoFetcher();

步骤4:图像处理和结果处理

该类CCaptureVisionRouter是图像处理的核心类。它提供了一个内置线程池,用于异步处理对象获取的图像CImageSourceAdapter

CCaptureVisionRouter *cvr = new CCaptureVisionRouter;
cvr->SetInput(fetcher);

为了处理条形码和 OCR 文本结果,我们需要注册一个从对象继承的自定义CCapturedResultReceiverCCaptureVisionRouter

class MyCapturedResultReceiver : public CCapturedResultReceiver
{
    virtual void OnRecognizedTextLinesReceived(CRecognizedTextLinesResult *pResult) override
    {
        
    }

    virtual void OnDecodedBarcodesReceived(CDecodedBarcodesResult *pResult) override
    {
    }
};

CCapturedResultReceiver *capturedReceiver = new MyCapturedResultReceiver;
cvr->AddResultReceiver(capturedReceiver);

步骤 5:流式传输视频帧并馈送到图像处理器

OpenCV 提供了一种从摄像头捕获视频帧的简单方法。我们可以使用该类VideoCapture打开摄像头并连续获取帧。同时,我们调用对象的StartCapturing()StopCapturing()方法CCaptureVisionRouter来打开和关闭处理任务。

string settings = R"(
{
    "CaptureVisionTemplates": [
        {
            "Name": "ReadBarcode&AccompanyText",
            "ImageROIProcessingNameArray": [
                "roi-read-barcodes-only", "roi-read-text"
            ]
        }
    ],
    "TargetROIDefOptions": [
        {
            "Name": "roi-read-barcodes-only",
            "TaskSettingNameArray": ["task-read-barcodes"]
        },
        {

图像处理任务支持预设模板和自定义设置。如果我们只需要扫描一维和二维条码,可以使用模板CPresetTemplate::PT_READ_BARCODES。如果我们想在提取条码的同时提取 OCR 文本标签,则需要高度自定义的模板。

步骤6:处理结果并显示

由于结果是从工作线程返回的,为了将它们显示在主线程中的视频帧上,我们创建一个向量来存储它们并使用互斥来保护共享资源。

struct BarcodeResult
{
	std::string type;
	std::string value;
	std::vector<cv::Point> localizationPoints;
	int frameId;
	string line;
	std::vector<cv::Point> textLinePoints;
};

std::vector<BarcodeResult> barcodeResults;
std::mutex barcodeResultsMutex;

OnRecognizedTextLinesReceived以下是和回调函数的实现OnDecodedBarcodesReceived

virtual void OnRecognizedTextLinesReceived(CRecognizedTextLinesResult *pResult) override
{
    std::lock_guard<std::mutex> lock(barcodeResultsMutex);
    barcodeResults.clear();

    const CFileImageTag *tag = dynamic_cast<const CFileImageTag *>(pResult->GetOriginalImageTag());

    if (pResult->GetErrorCode() != EC_OK)
    {
        cout << "Error: " << pResult->GetErrorString() << endl;
    }
    else
    {
        int lCount = pResult->GetItemsCount();
        for (int li = 0; li < lCount; ++li)
        {

以下代码片段展示了如何在视频帧上绘制条形码和OCR文本结果:

{
    std::lock_guard<std::mutex> lock(barcodeResultsMutex);
    for (const auto &result : barcodeResults)
    {
        // Draw the bounding box
        if (result.localizationPoints.size() == 4)
        {
            for (size_t i = 0; i < result.localizationPoints.size(); ++i)
            {
                cv::line(frame, result.localizationPoints[i],
                            result.localizationPoints[(i + 1) % result.localizationPoints.size()],
                            cv::Scalar(0, 255, 0), 2);
            }
        }

        // Draw the barcode type and value

构建并运行应用程序

视窗

mkdir build
cd build
cmake -DCMAKE_GENERATOR_PLATFORM=x64 ..
cmake --build . --config Release
Release\main.exe

Linux

mkdir build
cd build
cmake ..
cmake --build . --config Release
./main

源代码

cmake-cpp-barcode-qrcode/examples/10.x/opencv_camera at main · yushulx/cmake-cpp-barcode-qrcode · GitHub

06-02 20:52