Skip to content

Commit

Permalink
Add a ctypes example
Browse files Browse the repository at this point in the history
  • Loading branch information
yushulx committed Aug 14, 2024
1 parent 1eb990e commit 79e5de2
Show file tree
Hide file tree
Showing 8 changed files with 459 additions and 0 deletions.
3 changes: 3 additions & 0 deletions examples/ctypes/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/analytics-quickstart
/node_modules
build
45 changes: 45 additions & 0 deletions examples/ctypes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Python Ctypes for Dynamsoft Barcode Shared Library
This repository demonstrates how to load and utilize Dynamsoft Barcode Reader shared libraries using Python's Ctypes module.

## Why Use Ctypes?
While Dynamsoft Barcode Reader for Python is available on [PyPI](https://pypi.org/project/dbr/) and can be installed with:

```bash
pip install dbr
```

This project explores an alternative approach to invoking C APIs from shared libraries using Ctypes, offering a way to use C/C++ native threads and callbacks in Python.

## How to Use
1. Build the `bridge` CMake project:

**On Windows**:

```bash
cd bridge && mkdir build && cd build
cmake -DCMAKE_GENERATOR_PLATFORM=x64 ..
cmake --build .
```

**On Linux:**

```bash
cd bridge && mkdir build && cd build
cmake ..
cmake --build .
```

2. Get a valid license key from [Dynamsoft](https://www.dynamsoft.com/customer/license/trialLicense?product=dbr). Then, update the license key in the `success.py` file.

```python
license_key = b"LICENSE-KEY"
```

3. Execute the `success.py` script.

```bash
python success.py
```

## Blog
[Python Ctypes for Loading and Calling Shared Libraries](https://www.dynamsoft.com/codepool/python-ctypes-load-call-shared-library.html)
17 changes: 17 additions & 0 deletions examples/ctypes/bridge/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.0.0)

project(bridge VERSION 0.1.0)

INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/../../../include")
if (CMAKE_HOST_WIN32)
LINK_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../../../lib/Win")
else()
LINK_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../../../lib/linux")
endif()
add_library(${PROJECT_NAME} SHARED bridge.cpp)

if(CMAKE_HOST_WIN32)
target_link_libraries (${PROJECT_NAME} "DBRx64")
else()
target_link_libraries (${PROJECT_NAME} "DynamsoftBarcodeReader")
endif()
112 changes: 112 additions & 0 deletions examples/ctypes/bridge/bridge.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "bridge.h"
#include <thread>

using namespace std;

callback_t callback = NULL;
thread t;

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

void thread_func(void *barcodeReader, const char *fileName)
{
DBR_DecodeFile(barcodeReader, fileName, "");

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

if (callback)
{
int res = callback(resultList);
}
}

void thread_decode(void *barcodeReader, const char *fileName)
{
t = thread(thread_func, barcodeReader, fileName);
t.join();
}

int registerCallback(callback_t foo)
{
callback = foo;
return 0;
}
32 changes: 32 additions & 0 deletions examples/ctypes/bridge/bridge.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# 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;

typedef int (*callback_t)(ResultList*);

#ifdef __cplusplus
extern "C" {
#endif

EXPORT_API ResultList* dbr_get_results(void* barcodeReader);
EXPORT_API void dbr_free_results(ResultList* resultList);
EXPORT_API void thread_decode(void* barcodeReader, const char *fileName);
EXPORT_API int registerCallback(callback_t foo);

#ifdef __cplusplus
}
#endif
167 changes: 167 additions & 0 deletions examples/ctypes/failure.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import os
import platform
from ctypes import *

system = platform.system()


class SamplingImageData(Structure):
_fields_ = [
("bytes", POINTER(c_ubyte)),
("width", c_int),
("height", c_int)
]


class LocalizationResult(Structure):
_fields_ = [
("terminatePhase", c_int),
("barcodeFormat", c_int),
("barcodeFormatString", c_char_p),
("barcodeFormat_2", c_int),
("barcodeFormatString_2", c_char_p),
("x1", c_int),
("y1", c_int),
("x2", c_int),
("y2", c_int),
("x3", c_int),
("y3", c_int),
("x4", c_int),
("y4", c_int),
("angle", c_int),
("moduleSize", c_int),
("pageNumber", c_int),
("regionName", c_char_p),
("documentName", c_char_p),
("resultCoordinateType", c_int),
("accompanyingTextBytes", c_char_p),
("accompanyingTextBytesLength", c_int),
("confidence", c_int),
("transformationMatrix", c_double * 9),
("reserved", c_char * 52)
]


class ExtendedResult(Structure):
_fields_ = [
("resultType", c_int),
("barcodeFormat", c_int),
("barcodeFormatString", c_char_p),
("barcodeFormat_2", c_int),
("barcodeFormatString_2", c_char_p),
("confidence", c_int),
("bytes", POINTER(c_ubyte)),
("bytesLength", c_int),
("accompanyingTextBytes", POINTER(c_ubyte)),
("accompanyingTextBytesLength", c_int),
("deformation", c_int),
("detailedResult", c_void_p),
("samplingImage", SamplingImageData),
("clarity", c_int),
("reserved", c_char * 40)
]


class TextResult(Structure):
_fields_ = [
("barcodeFormat", c_int),
("barcodeFormatString", c_char_p),
("barcodeFormat_2", c_int),
("barcodeFormatString_2", c_char_p),
("barcodeText", c_char_p),
("barcodeBytes", POINTER(c_ubyte)),
("barcodeBytesLength", c_int),
("localizationResult", POINTER(LocalizationResult)),
("detailedResult", c_void_p),
("resultsCount", c_int),
("results", POINTER(POINTER(ExtendedResult))),
("exception", c_char_p),
("isDPM", c_int),
("isMirrored", c_int),
("reserved", c_char * 44)
]


class TextResultArray(Structure):
_fields_ = [
("resultsCount", c_int),
("results", POINTER(POINTER(TextResult)))
]


dbr = None
if 'Windows' in system:
dll_path = license_dll_path = os.path.join(os.path.abspath(
'.'), r'..\..\lib\win\DynamsoftBarcodeReaderx64.dll')

# os.environ['path'] += ';' + dll_path
# print(os.environ['path'])
dbr = windll.LoadLibrary(dll_path)
else:
dbr = CDLL(os.path.join(os.path.abspath('.'),
'../../lib/linux/libDynamsoftBarcodeReader.so'))

# DBR_InitLicense
DBR_InitLicense = dbr.DBR_InitLicense
DBR_InitLicense.argtypes = [c_char_p, c_char_p, c_int]
DBR_InitLicense.restype = c_int

license_key = b"DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ=="
error_msg_buffer = create_string_buffer(256)
error_msg_buffer_len = len(error_msg_buffer)
# https://www.dynamsoft.com/customer/license/trialLicense?product=dbr
ret = DBR_InitLicense(license_key, error_msg_buffer, error_msg_buffer_len)
print('initLicense: {}'.format(ret))

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

# 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('DBR_DecodeFile: {}'.format(ret))

####################################################################################
# Failed to get barcode detection results
# DBR_GetAllTextResults
pResults = POINTER(TextResultArray)()
DBR_GetAllTextResults = dbr.DBR_GetAllTextResults
DBR_GetAllTextResults.argtypes = [c_void_p, POINTER(POINTER(TextResultArray))]
DBR_GetAllTextResults.restype = c_int


ret = DBR_GetAllTextResults(instance, byref(pResults))
print('DBR_GetAllTextResults: {}'.format(ret))

if ret != 0 or pResults.contents.resultsCount == 0:
print("No barcode found.")
else:
print(f"Total barcode(s) found: {pResults.contents.resultsCount}")
for i in range(pResults.contents.resultsCount):
result = pResults.contents.results[i]
print(result)
print(f"Barcode {i+1}:")
# crash
# print(result.contents)
# print(f" Type: {result.contents.barcodeFormatString.decode('utf-8')}")
# print(f" Text: {result.contents.barcodeText.decode('utf-8')}")

# DBR_FreeTextResults
DBR_FreeTextResults = dbr.DBR_FreeTextResults
DBR_FreeTextResults.argtypes = [POINTER(POINTER(TextResultArray))]
DBR_FreeTextResults.restype = None

if bool(pResults):
DBR_FreeTextResults(byref(pResults))

####################################################################################

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

0 comments on commit 79e5de2

Please sign in to comment.