Рубрики
Без рубрики

Python Ctypes для погрузки и вызова общих библиотек

Как оригинальный писатель Dynamsoft Barcode Reader для Python (PIP Установка DBR), я предпочитаю использовать … Помечено Python, Ctypes, Cmake.

Как оригинальный писатель Dynamsoft Carcode Reader для Python ( PIP Установка DBR ), я предпочитаю использовать CPYthon и Dynamsoft C/C ++ Carcode SDK SDK, чтобы принести API для обнаружения штрих-кода для Python. Тем не менее, CTYPES Также стоит изучить. Это позволяет разработчикам вызывать функции C совместных библиотек в чистом виде Python. Статья проходит через шаги о том, как вызвать API на Dynamsoft C/C ++ Barcode API на Python Ctypes.

Попытка позвонить в функции C SDK Carcode SDK Dynamsoft в чистом Python

Предположим, вы скачали C/C ++ Пакеты , который содержит * .dll и * .so Файлы для Windows и Linux, с веб-сайта Dynamsoft. Мы копируем файлы * .dll и * .so в ту же папку нашего проекта Python.

В файле Python мы импортируем библиотеку Ctypes, а затем загрузите общую библиотеку DynamSoft:

import os
import platform
from ctypes import *

dbr = None
if 'Windows' in system:         
    os.environ['path'] += ';' + os.path.join(os.path.abspath('.'), r'')
    dbr = windll.LoadLibrary('DynamsoftBarcodeReaderx64.dll')
else:
    dbr = CDLL(os.path.join(os.path.abspath('.'), ''))

Dynamsoftbarcodereaderx64.dll . зависит от vcom110.dll Отказ Если vcom110.dll Пропущено, вы получите следующую ошибку:

Traceback (most recent call last):
  File ".\failure.py", line 80, in 
    dbr = windll.LoadLibrary('DynamsoftBarcodeReaderx64.dll')
  File "C:\Python37\lib\ctypes\__init__.py", line 442, in LoadLibrary
    return self._dlltype(name)
  File "C:\Python37\lib\ctypes\__init__.py", line 364, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: [WinError 126] The specified module could not be found

Для Linux, путь libdynamsoftbarcodereader.so достаточно.

Шаги для декодирования изображения штрих-кода с CTYPES

Чтобы понять функции вызова, вы можете обратиться к Dynamsoftbarcodereader.h файл.

Шаг 1: Инициализируйте считыватель DynamSoft Barcode

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

Шаг 2: Установите лицензионный ключ

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-KEY'.encode('utf-8')))

Получить действительный Лицензионный ключ с веб-сайта Dynamsoft, а затем обновить C_CHAR_P («Лицензия-ключ'.encode ('utf-8')) .

При преобразовании Python Строка к Чар * , вам нужно кодировать его на UTF-8 первый. В противном случае вы получите TypeError: BYTES или целочисленный адрес, ожидаемый вместо экземпляра CAR Отказ

Шаг 3: Позвоните в API декодирования

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

Шаг 4: Получите результат штрих-кода

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(ret)

resultsCount = pResults.contents.resultsCount
print(resultsCount)
results = pResults.contents.results
print(results)

if bool(results):
    for i in range(resultsCount):
        result = results[i] 
        if bool(result):
            print(result)
            print('Format: %s' % result.contents.barcodeFormatString.decode('utf-8'))                   
            print('Text: %s' % result.contents.barcodeText.decode('utf-8'))

Это самая жесткая часть всего процесса. Результатом является TextresUltarray Структура, которая содержит список Textresult структуры. Каждый Textresult Структура содержит формат штрих-кода, текст штрих-кода и другую информацию. Чтобы получить доступ к результатам декодирования штрих-кода, нам нужно определить все соответствующие сооружения C в Python:

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

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

class LocalizationResult(Structure):
    _fields_ = [("terminatePhase", c_uint), 
    ("barcodeFormat", c_uint),
    ("barcodeFormatString", c_char_p),
    ("barcodeFormat_2", c_uint),
    ("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_uint),
    ("accompanyingTextBytes", POINTER(c_byte)),
    ("accompanyingTextBytesLength", c_int),
    ("confidence", c_int),
    ("reserved", c_char * 52),
    ]

class TextResult(Structure):
    _fields_ = [("barcodeFormat", c_uint), 
    ("barcodeFormatString", c_char_p), 
    ("barcodeFormat_2", c_uint), 
    ("barcodeFormatString_2", c_char_p), 
    ("barcodeText", c_char_p), 
    ("barcodeBytes", POINTER(c_byte)),
    ("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)))]

Шаг 5: Бесплатная память о результате штрих-кода

DBR_FreeTextResults = dbr.DBR_FreeTextResults
DBR_FreeTextResults.argtypes = [POINTER(POINTER(TextResultArray))]
if bool(pResults):
    DBR_FreeTextResults(byref(pResults)) 

Шаг 6: Уничтожьте Reader DynamSoft Barcode Reader

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

Тестирование программы

До сих пор мы успешно реализовали читатель Python Barcode с CTYPES. Тем не менее, работает скрипт ничего. Количество результата отличное, чем ноль, но Bool (результаты) Возвращает false, что означает, что результаты декодирования штрих-кода пусты. Это невозможно, по-видимому,. Но, к сожалению, я еще не разобрался, где есть ошибка. Вероятно, это вызвано копией памяти вложенных структур C. Чтобы избежать нарушения сегментации, лучше написать немного C-кода для упрощения Структур C, определенных в Python.

Написать немного C-кода, чтобы упростить вызовы ctypes

Мы создаем проект библиотеки CMAKE с именем мост Для реализации двух методов dbr_get_results. и dbr_free_results. вместо Dbr_getalltextresults. и Dbr_freetextresults Отказ C-код написан в bridge.c Отказ

Cmakelists.txt Файл выглядит следующим образом:

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/Windows")
else()
    LINK_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/lib/Linux")
endif()
add_library(${PROJECT_NAME} SHARED bridge.c)

if(CMAKE_HOST_WIN32)
    target_link_libraries (${PROJECT_NAME} "DBRx64")
else()
    target_link_libraries (${PROJECT_NAME} "DynamsoftBarcodeReader")
endif()

Для экспорта функций для Windows и Linux мы определяем макрос в bridge.h :

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

Кроме того, мы определяем структуры и функции C, которые будут использоваться Python Ctypes:

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 мы добавляем реализацию dbr_get_results. и dbr_free_results. :

#include 
#include 
#include 
#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;
        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)
                free(resultList->format);
            if (resultList->text)
                free(resultList->text);

            free(resultList);
        }
    }

    if (pResultInfo)
        free(pResultInfo);
}

Как только сборка библиотеки моста сделана, мы вернемся к сценарию Python. Новый файл библиотеки bridge.dll/bridge.so готов к нагрузке:

dbr = None
bridge = None
if 'Windows' in system:                   
    os.environ['path'] += ';' + os.path.join(os.path.abspath('.'), r'bridge\lib\Windows')
    dbr = windll.LoadLibrary('DynamsoftBarcodeReaderx64.dll')
    bridge = windll.LoadLibrary(os.path.join(os.path.abspath('.'), r'bridge\build\Debug\bridge.dll'))
else:
    dbr = CDLL(os.path.join(os.path.abspath('.'), 'bridge/lib/Linux/libDynamsoftBarcodeReader.so'))
    bridge = CDLL(os.path.join(os.path.abspath('.'), 'bridge/build/libbridge.so'))

Последовательность загрузки библиотеки жизненно важно для Linux: libdynamsoftbarcodereader.so Во-первых, тогда libbridge.so. . Если библиотека загружена в неправильном порядке, код Python не будет работать на Linux.

Структуры C теперь намного проще и очиститель, чем структуры, определенные на предыдущем шаге:

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

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

Поэтому кода Python для получения и уничтожения результатов декодирования штрих-кода меняется на:

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

Наконец, мы можем успешно запустить приложение декодирования штрих-кода Python.

Исходный код

https://github.com/yushulx/python-ctypes-barcode-shared-library

Оригинал: “https://dev.to/yushulx/python-ctypes-for-loading-and-calling-shared-libraries-49n7”