龙芯MIPS64上实现C++,Python一维码,二维码识别

语言: CN / TW / HK

Dynamsoft最近提供了支持MIPS64的一维码,二维码的C++ SDK。我在统信UOS上用C++和Python上做了测试,可以正常使用。

SDK下载

Mips64el.zip

统信UOS环境配置

使用sudo apt update的时候可能会碰到错误:无法安全地用该源进行更新,所以默认禁用该源。解决方法是在/etc/apt/sources.list中更换源 deb http://mirrors.aliyun.com/debian stable main contrib non-free

C++识别一维码,二维码

创建一个CMake工程,包含CMakeLists.txtBarcodeReaderDemo.cpp, DynamsoftBarcodeReader.h, DynamsoftCommon.h以及libDynamsoftBarcodeReader.so

CMakeLists.txt文件很简单。配置一下头文件,库文件路径以及源文件:

cmake_minimum_required(VERSION 3.0.0)

project(BarcodeReaderDemo)

INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../include")
LINK_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../lib")

add_executable(BarcodeReaderDemo BarcodeReaderDemo.cpp)

target_link_libraries (BarcodeReaderDemo "DynamsoftBarcodeReader")

C++的实现很简单:读取图片文件,调用文件解码接口:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <fstream>
#include <streambuf>
#include <iostream>
#include <sstream>

#if defined(_WIN64) || defined(_WIN32)
#include <windows.h>
#include <conio.h>
#define snprintf sprintf_s 
#else
#include <sys/time.h>
#endif

#include "DynamsoftBarcodeReader.h"
#include "DynamsoftCommon.h"
using namespace dynamsoft::dbr;

void decode(CBarcodeReader& reader, const char *file)
{
	unsigned long ullTimeBegin = 0;
	unsigned long ullTimeEnd = 0;
	ullTimeBegin = GetTiming();
	int ret = reader.DecodeFile(file, "");
	ullTimeEnd = GetTiming();
	OutputResult(reader, ret, (((float)(ullTimeEnd - ullTimeBegin)) / 1000));
}

int main(int argc, const char* argv[])
{
	if (argc != 3)
	{
		printf("Usage: BarcodeReader <license_file> <image_file>\r\n");
		return 0;
	}

	// License file
	std::ifstream licenseFile(argv[1]);
	std::stringstream strStream;
    strStream << licenseFile.rdbuf(); 
    std::string licenseStr = strStream.str(); 

	// Image file
	std::string sImageFile = argv[2];

	BarcodeFormatSet iBarcodeFormatId = { 0,0 };
	char pszBuffer[512] = { 0 };
	char pszImageFile[512] = { 0 };
	int iIndex = 0;
	int iRet = -1;
	char * pszTemp = NULL;
	char * pszTemp1 = NULL;
	

	size_t iLen = 0;
	FILE* fp = NULL;
	bool bExit = false;
	char szErrorMsg[256];
	PublicRuntimeSettings runtimeSettings;

	printf("*************************************************\r\n");
	printf("Welcome to Dynamsoft Barcode Reader Demo\r\n");
	printf("*************************************************\r\n");
	printf("Hints: Please input 'Q' or 'q' to quit the application.\r\n");

	CBarcodeReader reader;
	reader.InitLicense(licenseStr.c_str());

	//Best coverage settings
	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);
	//Best speed settings
	//reader.InitRuntimeSettingsWithString("{\"ImageParameter\":{\"Name\":\"BestSpeed\",\"DeblurLevel\":3,\"ExpectedBarcodesCount\":512,\"LocalizationModes\":[{\"Mode\":\"LM_SCAN_DIRECTLY\"}],\"TextFilterModes\":[{\"MinImageDimension\":262144,\"Mode\":\"TFM_GENERAL_CONTOUR\"}]}}",CM_OVERWRITE,szErrorMsg,256);
	//Balance settings
	//reader.InitRuntimeSettingsWithString("{\"ImageParameter\":{\"Name\":\"Balance\",\"DeblurLevel\":5,\"ExpectedBarcodesCount\":512,\"LocalizationModes\":[{\"Mode\":\"LM_CONNECTED_BLOCKS\"},{\"Mode\":\"LM_STATISTICS\"}]}}",CM_OVERWRITE,szErrorMsg,256);

	reader.GetRuntimeSettings(&runtimeSettings);
	runtimeSettings.barcodeFormatIds = BF_ALL;
	iRet = reader.UpdateRuntimeSettings(&runtimeSettings, szErrorMsg, 256);

	decode(reader, sImageFile.c_str());

	if (iRet != DBR_OK)
	{
		printf("Error code: %d. Error message: %s\n", iRet, szErrorMsg);
		return -1;
	}

	while (1)
	{
		bExit = GetImagePath(pszImageFile);
		if (bExit)
			break;

		bExit = SetBarcodeFormat(&iBarcodeFormatId);
		if (bExit)
			break;

		reader.GetRuntimeSettings(&runtimeSettings);
		runtimeSettings.barcodeFormatIds = iBarcodeFormatId.barcodeFormatIds;
		runtimeSettings.barcodeFormatIds_2 = iBarcodeFormatId.barcodeFormatIds_2;
		iRet = reader.UpdateRuntimeSettings(&runtimeSettings, szErrorMsg, 256);

		decode(reader, pszImageFile);
	}

	return 0;
}

从Dynamsoft官网获取一个SDK序列号保存到license.txt中。编译运行程序:

mkdir build
cd build
cmake ..
cmake --build .
./BarcodeReaderDemo ../../license.txt ../../test.png

Python(加载C++动态链接库)识别一维码,二维码

考虑到越来越多的人喜欢用Python,这里分享下Python Ctypes动态加载的方法。

Python Ctypes可以让开发者用纯Python代码来调用C++的接口。但是要访问C的结构体数据比较麻烦,需要在Python层再对应定义一遍。如果结构体过于复杂,就容易出现问题。这里因为返回数据的结构体过于复杂。我又写了一点桥接用的C代码,便于简化Python层的定义。

首先创建一个桥接的CMake工程,用于编译出一个bridge.so动态链接库。

CMakeLists.txt:

cmake_minimum_required(VERSION 3.0.0)

project(bridge)

INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/../../include")
LINK_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../../lib")

add_library(${PROJECT_NAME} SHARED bridge.c)

target_link_libraries (${PROJECT_NAME} "DynamsoftBarcodeReader")

bridge.h:

# include "DynamsoftBarcodeReader.h"

#if !defined(_WIN32) && !defined(_WIN64)
#define EXPORT_API
#else
#define EXPORT_API __declspec(dllexport)
#endif

typedef struct {
    char* format;
    char* text;
} ResultInfo;

typedef struct {
    int size;
    ResultInfo** pResultInfo;
} ResultList;

EXPORT_API ResultList* dbr_get_results(void* barcodeReader);
EXPORT_API void dbr_free_results(ResultList* resultList);

bridge.c:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "bridge.h"

ResultList* dbr_get_results(void* barcodeReader)
{
    TextResultArray *pResults;
    int ret = DBR_GetAllTextResults(barcodeReader, &pResults);
    int count = pResults->resultsCount;
    TextResult **results = pResults->results;

    ResultInfo** pResultInfo = (ResultInfo**)malloc(sizeof(ResultInfo*) * count);
    ResultList* resultList = (ResultList*)malloc(sizeof(ResultList));
    resultList->size = count;
    resultList->pResultInfo = pResultInfo;

    for (int i = 0; i < count; i++)
    {
        TextResult* pResult = results[i];
        ResultInfo* pInfo = (ResultInfo*)malloc(sizeof(ResultInfo));
        pInfo->format = NULL;
        pInfo->text = NULL;
        pResultInfo[i] = pInfo;
        // printf("Barcode format: %s, text: %s\n", pResult->barcodeFormatString, pResult->barcodeText);
        pInfo->format = (char *)calloc(strlen(pResult->barcodeFormatString) + 1, sizeof(char));
        strncpy(pInfo->format, pResult->barcodeFormatString, strlen(pResult->barcodeFormatString));
        pInfo->text = (char *)calloc(strlen(pResult->barcodeText) + 1, sizeof(char));
        strncpy(pInfo->text, pResult->barcodeText, strlen(pResult->barcodeText));
    }

    DBR_FreeTextResults(&pResults);

    return resultList;
}

void dbr_free_results(ResultList* resultList)
{
    int count = resultList->size;
    ResultInfo** pResultInfo = resultList->pResultInfo;

    for (int i = 0; i < count; i++) 
    {   
        ResultInfo* resultList = pResultInfo[i];
        if (resultList) 
        {
            if (resultList->format != NULL)
                free(resultList->format);
            if (resultList->text != NULL)
                free(resultList->text);
            
            free(resultList);
        }
    }

    if (pResultInfo != NULL)
        free(pResultInfo);
}

这里实现了获取结果和释放结果两个接口,并把结果通过自定义的结构体保存下来返回到Python。

接下来在Python的脚本文件里导入两个库:

import os
import sys
from ctypes import *

def read_text(filename):
    f = open(filename, 'r')
    text = f.read().strip()
    f.close()
    return text
try:
    license = read_text(sys.argv[1])
except Exception as e:
    print(e)
    exit()

class ResultInfo(Structure):
    _fields_ = [("format", c_char_p), ("text", c_char_p)]

class ResultList(Structure):
    _fields_ = [("size", c_int), ("pResultInfo", POINTER(POINTER(ResultInfo)))]

dbr = CDLL(os.path.join(os.path.abspath('.'), '..//lib/libDynamsoftBarcodeReader.so'))
bridge = CDLL(os.path.join(os.path.abspath('.'), 'bridge/build/libbridge.so'))

注意,Linux上要小心导入顺序。

接下来通过调用对应动态链接库里的接口就可以实现Python读码程序:

# DBR_CreateInstance
DBR_CreateInstance = dbr.DBR_CreateInstance
DBR_CreateInstance.restype = c_void_p
instance = dbr.DBR_CreateInstance()

# DBR_InitLicense
DBR_InitLicense = dbr.DBR_InitLicense
DBR_InitLicense.argtypes = [c_void_p, c_char_p] 
DBR_InitLicense.restype = c_int
ret = DBR_InitLicense(instance, c_char_p(license.encode('utf-8'))) # http://www.dynamsoft.com/customer/license/trialLicense?product=dbr
print(ret)

# DBR_DecodeFile
DBR_DecodeFile = dbr.DBR_DecodeFile
DBR_DecodeFile.argtypes = [c_void_p, c_char_p, c_char_p]
DBR_DecodeFile.restype = c_int
ret = DBR_DecodeFile(instance, c_char_p('../test.png'.encode('utf-8')), c_char_p(''.encode('utf-8')))
print(ret)

# dbr_get_results  
dbr_get_results = bridge.dbr_get_results
dbr_get_results.argtypes = [c_void_p]
dbr_get_results.restype = c_void_p
address = dbr_get_results(instance)
data = cast(address, POINTER(ResultList))
size = data.contents.size
results = data.contents.pResultInfo

for i in range(size):   
    result = results[i]                
    print('Format: %s' % result.contents.format.decode('utf-8'))                   
    print('Text: %s' % result.contents.text.decode('utf-8'))

# dbr_free_results
dbr_free_results = bridge.dbr_free_results
dbr_free_results.argtypes = [c_void_p]
if bool(address):
    dbr_free_results(address)        

# DBR_DestroyInstance
DBR_DestroyInstance = dbr.DBR_DestroyInstance
DBR_DestroyInstance.argtypes = [c_void_p]
DBR_DestroyInstance(instance)

视频

http://www.bilibili.com/video/av249186065?zw

源码

http://gitee.com/yushulx/mips64-barcode-qr-detection