Skip to content

Commit 79e5de2

Browse files
committed
Add a ctypes example
1 parent 1eb990e commit 79e5de2

File tree

8 files changed

+459
-0
lines changed

8 files changed

+459
-0
lines changed

examples/ctypes/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/analytics-quickstart
2+
/node_modules
3+
build

examples/ctypes/README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Python Ctypes for Dynamsoft Barcode Shared Library
2+
This repository demonstrates how to load and utilize Dynamsoft Barcode Reader shared libraries using Python's Ctypes module.
3+
4+
## Why Use Ctypes?
5+
While Dynamsoft Barcode Reader for Python is available on [PyPI](https://pypi.org/project/dbr/) and can be installed with:
6+
7+
```bash
8+
pip install dbr
9+
```
10+
11+
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.
12+
13+
## How to Use
14+
1. Build the `bridge` CMake project:
15+
16+
**On Windows**:
17+
18+
```bash
19+
cd bridge && mkdir build && cd build
20+
cmake -DCMAKE_GENERATOR_PLATFORM=x64 ..
21+
cmake --build .
22+
```
23+
24+
**On Linux:**
25+
26+
```bash
27+
cd bridge && mkdir build && cd build
28+
cmake ..
29+
cmake --build .
30+
```
31+
32+
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.
33+
34+
```python
35+
license_key = b"LICENSE-KEY"
36+
```
37+
38+
3. Execute the `success.py` script.
39+
40+
```bash
41+
python success.py
42+
```
43+
44+
## Blog
45+
[Python Ctypes for Loading and Calling Shared Libraries](https://www.dynamsoft.com/codepool/python-ctypes-load-call-shared-library.html)

examples/ctypes/bridge/CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
cmake_minimum_required(VERSION 3.0.0)
2+
3+
project(bridge VERSION 0.1.0)
4+
5+
INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/../../../include")
6+
if (CMAKE_HOST_WIN32)
7+
LINK_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../../../lib/Win")
8+
else()
9+
LINK_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../../../lib/linux")
10+
endif()
11+
add_library(${PROJECT_NAME} SHARED bridge.cpp)
12+
13+
if(CMAKE_HOST_WIN32)
14+
target_link_libraries (${PROJECT_NAME} "DBRx64")
15+
else()
16+
target_link_libraries (${PROJECT_NAME} "DynamsoftBarcodeReader")
17+
endif()

examples/ctypes/bridge/bridge.cpp

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#include <stdio.h>
2+
#include <string.h>
3+
#include <stdlib.h>
4+
#include "bridge.h"
5+
#include <thread>
6+
7+
using namespace std;
8+
9+
callback_t callback = NULL;
10+
thread t;
11+
12+
ResultList *dbr_get_results(void *barcodeReader)
13+
{
14+
TextResultArray *pResults;
15+
int ret = DBR_GetAllTextResults(barcodeReader, &pResults);
16+
int count = pResults->resultsCount;
17+
TextResult **results = pResults->results;
18+
19+
ResultInfo **pResultInfo = (ResultInfo **)malloc(sizeof(ResultInfo *) * count);
20+
ResultList *resultList = (ResultList *)malloc(sizeof(ResultList));
21+
resultList->size = count;
22+
resultList->pResultInfo = pResultInfo;
23+
24+
for (int i = 0; i < count; i++)
25+
{
26+
TextResult *pResult = results[i];
27+
ResultInfo *pInfo = (ResultInfo *)malloc(sizeof(ResultInfo));
28+
pInfo->format = NULL;
29+
pInfo->text = NULL;
30+
pResultInfo[i] = pInfo;
31+
// printf("Barcode format: %s, text: %s\n", pResult->barcodeFormatString, pResult->barcodeText);
32+
pInfo->format = (char *)calloc(strlen(pResult->barcodeFormatString) + 1, sizeof(char));
33+
strncpy(pInfo->format, pResult->barcodeFormatString, strlen(pResult->barcodeFormatString));
34+
pInfo->text = (char *)calloc(strlen(pResult->barcodeText) + 1, sizeof(char));
35+
strncpy(pInfo->text, pResult->barcodeText, strlen(pResult->barcodeText));
36+
}
37+
38+
DBR_FreeTextResults(&pResults);
39+
40+
return resultList;
41+
}
42+
43+
void dbr_free_results(ResultList *resultList)
44+
{
45+
int count = resultList->size;
46+
ResultInfo **pResultInfo = resultList->pResultInfo;
47+
48+
for (int i = 0; i < count; i++)
49+
{
50+
ResultInfo *resultList = pResultInfo[i];
51+
if (resultList)
52+
{
53+
if (resultList->format != NULL)
54+
free(resultList->format);
55+
if (resultList->text != NULL)
56+
free(resultList->text);
57+
58+
free(resultList);
59+
}
60+
}
61+
62+
if (pResultInfo != NULL)
63+
free(pResultInfo);
64+
}
65+
66+
void thread_func(void *barcodeReader, const char *fileName)
67+
{
68+
DBR_DecodeFile(barcodeReader, fileName, "");
69+
70+
TextResultArray *pResults;
71+
int ret = DBR_GetAllTextResults(barcodeReader, &pResults);
72+
int count = pResults->resultsCount;
73+
TextResult **results = pResults->results;
74+
75+
ResultInfo **pResultInfo = (ResultInfo **)malloc(sizeof(ResultInfo *) * count);
76+
ResultList *resultList = (ResultList *)malloc(sizeof(ResultList));
77+
resultList->size = count;
78+
resultList->pResultInfo = pResultInfo;
79+
80+
for (int i = 0; i < count; i++)
81+
{
82+
TextResult *pResult = results[i];
83+
ResultInfo *pInfo = (ResultInfo *)malloc(sizeof(ResultInfo));
84+
pInfo->format = NULL;
85+
pInfo->text = NULL;
86+
pResultInfo[i] = pInfo;
87+
// printf("Barcode format: %s, text: %s\n", pResult->barcodeFormatString, pResult->barcodeText);
88+
pInfo->format = (char *)calloc(strlen(pResult->barcodeFormatString) + 1, sizeof(char));
89+
strncpy(pInfo->format, pResult->barcodeFormatString, strlen(pResult->barcodeFormatString));
90+
pInfo->text = (char *)calloc(strlen(pResult->barcodeText) + 1, sizeof(char));
91+
strncpy(pInfo->text, pResult->barcodeText, strlen(pResult->barcodeText));
92+
}
93+
94+
DBR_FreeTextResults(&pResults);
95+
96+
if (callback)
97+
{
98+
int res = callback(resultList);
99+
}
100+
}
101+
102+
void thread_decode(void *barcodeReader, const char *fileName)
103+
{
104+
t = thread(thread_func, barcodeReader, fileName);
105+
t.join();
106+
}
107+
108+
int registerCallback(callback_t foo)
109+
{
110+
callback = foo;
111+
return 0;
112+
}

examples/ctypes/bridge/bridge.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# include "DynamsoftBarcodeReader.h"
2+
3+
#if !defined(_WIN32) && !defined(_WIN64)
4+
#define EXPORT_API
5+
#else
6+
#define EXPORT_API __declspec(dllexport)
7+
#endif
8+
9+
typedef struct {
10+
char* format;
11+
char* text;
12+
} ResultInfo;
13+
14+
typedef struct {
15+
int size;
16+
ResultInfo** pResultInfo;
17+
} ResultList;
18+
19+
typedef int (*callback_t)(ResultList*);
20+
21+
#ifdef __cplusplus
22+
extern "C" {
23+
#endif
24+
25+
EXPORT_API ResultList* dbr_get_results(void* barcodeReader);
26+
EXPORT_API void dbr_free_results(ResultList* resultList);
27+
EXPORT_API void thread_decode(void* barcodeReader, const char *fileName);
28+
EXPORT_API int registerCallback(callback_t foo);
29+
30+
#ifdef __cplusplus
31+
}
32+
#endif

examples/ctypes/failure.py

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import os
2+
import platform
3+
from ctypes import *
4+
5+
system = platform.system()
6+
7+
8+
class SamplingImageData(Structure):
9+
_fields_ = [
10+
("bytes", POINTER(c_ubyte)),
11+
("width", c_int),
12+
("height", c_int)
13+
]
14+
15+
16+
class LocalizationResult(Structure):
17+
_fields_ = [
18+
("terminatePhase", c_int),
19+
("barcodeFormat", c_int),
20+
("barcodeFormatString", c_char_p),
21+
("barcodeFormat_2", c_int),
22+
("barcodeFormatString_2", c_char_p),
23+
("x1", c_int),
24+
("y1", c_int),
25+
("x2", c_int),
26+
("y2", c_int),
27+
("x3", c_int),
28+
("y3", c_int),
29+
("x4", c_int),
30+
("y4", c_int),
31+
("angle", c_int),
32+
("moduleSize", c_int),
33+
("pageNumber", c_int),
34+
("regionName", c_char_p),
35+
("documentName", c_char_p),
36+
("resultCoordinateType", c_int),
37+
("accompanyingTextBytes", c_char_p),
38+
("accompanyingTextBytesLength", c_int),
39+
("confidence", c_int),
40+
("transformationMatrix", c_double * 9),
41+
("reserved", c_char * 52)
42+
]
43+
44+
45+
class ExtendedResult(Structure):
46+
_fields_ = [
47+
("resultType", c_int),
48+
("barcodeFormat", c_int),
49+
("barcodeFormatString", c_char_p),
50+
("barcodeFormat_2", c_int),
51+
("barcodeFormatString_2", c_char_p),
52+
("confidence", c_int),
53+
("bytes", POINTER(c_ubyte)),
54+
("bytesLength", c_int),
55+
("accompanyingTextBytes", POINTER(c_ubyte)),
56+
("accompanyingTextBytesLength", c_int),
57+
("deformation", c_int),
58+
("detailedResult", c_void_p),
59+
("samplingImage", SamplingImageData),
60+
("clarity", c_int),
61+
("reserved", c_char * 40)
62+
]
63+
64+
65+
class TextResult(Structure):
66+
_fields_ = [
67+
("barcodeFormat", c_int),
68+
("barcodeFormatString", c_char_p),
69+
("barcodeFormat_2", c_int),
70+
("barcodeFormatString_2", c_char_p),
71+
("barcodeText", c_char_p),
72+
("barcodeBytes", POINTER(c_ubyte)),
73+
("barcodeBytesLength", c_int),
74+
("localizationResult", POINTER(LocalizationResult)),
75+
("detailedResult", c_void_p),
76+
("resultsCount", c_int),
77+
("results", POINTER(POINTER(ExtendedResult))),
78+
("exception", c_char_p),
79+
("isDPM", c_int),
80+
("isMirrored", c_int),
81+
("reserved", c_char * 44)
82+
]
83+
84+
85+
class TextResultArray(Structure):
86+
_fields_ = [
87+
("resultsCount", c_int),
88+
("results", POINTER(POINTER(TextResult)))
89+
]
90+
91+
92+
dbr = None
93+
if 'Windows' in system:
94+
dll_path = license_dll_path = os.path.join(os.path.abspath(
95+
'.'), r'..\..\lib\win\DynamsoftBarcodeReaderx64.dll')
96+
97+
# os.environ['path'] += ';' + dll_path
98+
# print(os.environ['path'])
99+
dbr = windll.LoadLibrary(dll_path)
100+
else:
101+
dbr = CDLL(os.path.join(os.path.abspath('.'),
102+
'../../lib/linux/libDynamsoftBarcodeReader.so'))
103+
104+
# DBR_InitLicense
105+
DBR_InitLicense = dbr.DBR_InitLicense
106+
DBR_InitLicense.argtypes = [c_char_p, c_char_p, c_int]
107+
DBR_InitLicense.restype = c_int
108+
109+
license_key = b"DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ=="
110+
error_msg_buffer = create_string_buffer(256)
111+
error_msg_buffer_len = len(error_msg_buffer)
112+
# https://www.dynamsoft.com/customer/license/trialLicense?product=dbr
113+
ret = DBR_InitLicense(license_key, error_msg_buffer, error_msg_buffer_len)
114+
print('initLicense: {}'.format(ret))
115+
116+
# DBR_CreateInstance
117+
DBR_CreateInstance = dbr.DBR_CreateInstance
118+
DBR_CreateInstance.restype = c_void_p
119+
instance = dbr.DBR_CreateInstance()
120+
121+
# DBR_DecodeFile
122+
DBR_DecodeFile = dbr.DBR_DecodeFile
123+
DBR_DecodeFile.argtypes = [c_void_p, c_char_p, c_char_p]
124+
DBR_DecodeFile.restype = c_int
125+
ret = DBR_DecodeFile(instance, c_char_p(
126+
'test.png'.encode('utf-8')), c_char_p(''.encode('utf-8')))
127+
print('DBR_DecodeFile: {}'.format(ret))
128+
129+
####################################################################################
130+
# Failed to get barcode detection results
131+
# DBR_GetAllTextResults
132+
pResults = POINTER(TextResultArray)()
133+
DBR_GetAllTextResults = dbr.DBR_GetAllTextResults
134+
DBR_GetAllTextResults.argtypes = [c_void_p, POINTER(POINTER(TextResultArray))]
135+
DBR_GetAllTextResults.restype = c_int
136+
137+
138+
ret = DBR_GetAllTextResults(instance, byref(pResults))
139+
print('DBR_GetAllTextResults: {}'.format(ret))
140+
141+
if ret != 0 or pResults.contents.resultsCount == 0:
142+
print("No barcode found.")
143+
else:
144+
print(f"Total barcode(s) found: {pResults.contents.resultsCount}")
145+
for i in range(pResults.contents.resultsCount):
146+
result = pResults.contents.results[i]
147+
print(result)
148+
print(f"Barcode {i+1}:")
149+
# crash
150+
# print(result.contents)
151+
# print(f" Type: {result.contents.barcodeFormatString.decode('utf-8')}")
152+
# print(f" Text: {result.contents.barcodeText.decode('utf-8')}")
153+
154+
# DBR_FreeTextResults
155+
DBR_FreeTextResults = dbr.DBR_FreeTextResults
156+
DBR_FreeTextResults.argtypes = [POINTER(POINTER(TextResultArray))]
157+
DBR_FreeTextResults.restype = None
158+
159+
if bool(pResults):
160+
DBR_FreeTextResults(byref(pResults))
161+
162+
####################################################################################
163+
164+
# DBR_DestroyInstance
165+
DBR_DestroyInstance = dbr.DBR_DestroyInstance
166+
DBR_DestroyInstance.argtypes = [c_void_p]
167+
DBR_DestroyInstance(instance)

0 commit comments

Comments
 (0)