PHP前端开发

构建 Python 相机 SDK 并使用它进行多条码扫描

百变鹏仔 5天前 #Python
文章标签 用它

现在,轻量级 c 相机 sdk 已针对 windowslinuxmacos 完成,我们可以将其集成到其他高级编程语言中。在本文中,我们将探讨如何基于 c 相机库构建 python 相机 sdk,并使用它与 dynamsoft barcode reader sdk 进行多条码扫描。

python 多条码扫描仪演示视频

github.com/user-attachments/assets/bfb7009b-2cff-42c8-a37c-c58a732f01f5" controls="controls">

搭建 cpython 扩展项目的脚手架

cpython 扩展是一个共享库(例如,windows 上的 dll、linux 上的 .so 或 macos 上的 .dylib)可以在运行时加载到python解释器中并用于扩展其功能。 lite相机cpython扩展项目的结构如下:

python-lite-camera││── include│   ├── camera.h│   ├── camerapreview.h│   ├── stb_image_write.h│── lib│   ├── linux│   │   ├── liblitecam.so│   ├── macos│   │   ├── liblitecam.dylib│   ├── windows│   │   ├── litecam.dll│   │   ├── litecam.lib├── src│   ├── litecam.cpp│   ├── pycamera.h│   ├── pywindow.h│── litecam│   ├── __init__.py│── setup.py│── manifest.in│── readme.md

说明

为 python 扩展编写构建脚本 setup.py

在setup.py中添加以下内容:

from setuptools.command import build_extfrom setuptools import setup, extensionimport sysimport osimport iofrom setuptools.command.install import installimport shutilfrom pathlib import pathlib_dir = ''sources = [    "src/litecam.cpp",]include_dirs = [os.path.join(os.path.dirname(__file__), "include")]libraries = ['litecam']extra_compile_args = []if sys.platform == "linux" or sys.platform == "linux2":    lib_dir = 'lib/linux'    extra_compile_args = ['-std=c++11']    extra_link_args = ["-wl,-rpath=$origin"]elif sys.platform == "darwin":    lib_dir = 'lib/macos'    extra_compile_args = ['-std=c++11']    extra_link_args = ["-wl,-rpath,@loader_path"]elif sys.platform == "win32":    lib_dir = 'lib/windows'    extra_link_args = []else:    raise runtimeerror("unsupported platform")long_description = io.open("readme.md", encoding="utf-8").read()module_litecam = extension(    "litecam",    sources=sources,    include_dirs=include_dirs,    library_dirs=[lib_dir],    libraries=libraries,    extra_compile_args=extra_compile_args,    extra_link_args=extra_link_args,)def copyfiles(src, dst):    if os.path.isdir(src):        filelist = os.listdir(src)        for file in filelist:            libpath = os.path.join(src, file)            shutil.copy2(libpath, dst)    else:        shutil.copy2(src, dst)class custombuildext(build_ext.build_ext):    def run(self):        build_ext.build_ext.run(self)        dst = os.path.join(self.build_lib, "litecam")        copyfiles(lib_dir, dst)        filelist = os.listdir(self.build_lib)        for file in filelist:            filepath = os.path.join(self.build_lib, file)            if not os.path.isdir(file):                copyfiles(filepath, dst)                os.remove(filepath)class custombuildextdev(build_ext.build_ext):    def run(self):        build_ext.build_ext.run(self)        dev_folder = os.path.join(path(__file__).parent, 'litecam')        copyfiles(lib_dir, dev_folder)        filelist = os.listdir(self.build_lib)        for file in filelist:            filepath = os.path.join(self.build_lib, file)            if not os.path.isdir(file):                copyfiles(filepath, dev_folder)class custominstall(install):    def run(self):        install.run(self)setup(name='lite-camera',      version='2.0.1',      description='litecam is a lightweight, cross-platform library for capturing rgb frames from cameras and displaying them. designed with simplicity and ease of integration in mind, litecam supports windows, linux and macos platforms.',      long_description=long_description,      long_description_content_type="text/markdown",      author='yushulx',      url='https://github.com/yushulx/python-lite-camera',      license='mit',      packages=['litecam'],      ext_modules=[module_litecam],      classifiers=[           "development status :: 5 - production/stable",           "environment :: console",           "intended audience :: developers",          "intended audience :: education",          "intended audience :: information technology",          "intended audience :: science/research",          "license :: osi approved :: mit license",          "operating system :: microsoft :: windows",          "operating system :: macos",          "operating system :: posix :: linux",          "programming language :: python",          "programming language :: python :: 3",          "programming language :: python :: 3 :: only",          "programming language :: python :: 3.6",          "programming language :: python :: 3.7",          "programming language :: python :: 3.8",          "programming language :: python :: 3.9",          "programming language :: python :: 3.10",          "programming language :: python :: 3.11",          "programming language :: python :: 3.12",          "programming language :: c++",          "programming language :: python :: implementation :: cpython",          "topic :: scientific/engineering",          "topic :: software development",      ],      cmdclass={          'install': custominstall,          'build_ext': custombuildext,          'develop': custombuildextdev},      )

说明

用 c 实现 python camera sdk api

pycamera.h 文件定义了用于从相机捕获帧的 pycamera python 类,而 pywindow.h 文件定义了用于在窗口中显示帧的 pywindow python 类。 litecam.cpp 文件作为 python 扩展的入口点,其中定义了一些全局方法,并注册了 pycamera 和 pywindow 类。

pycamera.h

包括

#include <python.h>#include <structmember.h>#include "camera.h"

pycamera 结构体定义

typedef struct{    pyobject_head camera *handler;} pycamera;

pycamera 结构体表示包装 c camera 对象的 python 对象。该处理程序是一个指向 camera 类实例的指针。

立即学习“Python免费学习笔记(深入)”;

方法

实例方法

static pymethoddef instance_methods[] = {    {"open", open, meth_varargs, null},    {"listmediatypes", listmediatypes, meth_varargs, null},    {"release", release, meth_varargs, null},    {"setresolution", setresolution, meth_varargs, null},    {"captureframe", captureframe, meth_varargs, null},    {"getwidth", getwidth, meth_varargs, null},    {"getheight", getheight, meth_varargs, null},    {null, null, 0, null}};

定义 pycamera python 对象上可用的方法。这些方法与上面定义的相应 c 函数相关联。

pycamera类型

static pytypeobject pycameratype = {    pyvarobject_head_init(null, 0) "litecam.pycamera", /* tp_name */    sizeof(pycamera),                                  /* tp_basicsize */    0,                                                 /* tp_itemsize */    (destructor)pycamera_dealloc,                      /* tp_dealloc */    0,                                                 /* tp_print */    0,                                                 /* tp_getattr */    0,                                                 /* tp_setattr */    0,                                                 /* tp_reserved */    0,                                                 /* tp_repr */    0,                                                 /* tp_as_number */    0,                                                 /* tp_as_sequence */    0,                                                 /* tp_as_mapping */    0,                                                 /* tp_hash  */    0,                                                 /* tp_call */    0,                                                 /* tp_str */    pyobject_genericgetattr,                           /* tp_getattro */    pyobject_genericsetattr,                           /* tp_setattro */    0,                                                 /* tp_as_buffer */    py_tpflags_default | py_tpflags_basetype,          /*tp_flags*/    "pycamera",                                        /* tp_doc */    0,                                                 /* tp_traverse */    0,                                                 /* tp_clear */    0,                                                 /* tp_richcompare */    0,                                                 /* tp_weaklistoffset */    0,                                                 /* tp_iter */    0,                                                 /* tp_iternext */    instance_methods,                                  /* tp_methods */    0,                                                 /* tp_members */    0,                                                 /* tp_getset */    0,                                                 /* tp_base */    0,                                                 /* tp_dict */    0,                                                 /* tp_descr_get */    0,                                                 /* tp_descr_set */    0,                                                 /* tp_dictoffset */    0,                                                 /* tp_init */    0,                                                 /* tp_alloc */    pycamera_new,                                      /* tp_new */};

定义 pycamera 类型,包括其方法、内存分配、释放和其他行为。

pywindow.h

包括

#include <python.h>#include <structmember.h>#include "camerapreview.h"

pywindow 结构体定义

typedef struct{    pyobject_head camerawindow *handler;} pywindow;

定义一个包装 c camerawindow 对象的 pywindow 结构体。 handler 成员指向 camerawindow 的一个实例。

pywindow 对象的方法

窗口实例方法

static pymethoddef window_instance_methods[] = {    {"waitkey", waitkey, meth_varargs, null},    {"showframe", showframe, meth_varargs, null},    {"drawcontour", drawcontour, meth_varargs, null},    {"drawtext", drawtext, meth_varargs, null},    {null, null, 0, null}};

定义 pywindow python 对象上可用的方法。

pywindow类型

static pytypeobject pywindowtype = {    pyvarobject_head_init(null, 0) "litecam.pywindow", /* tp_name */    sizeof(pywindow),                                  /* tp_basicsize */    0,                                                 /* tp_itemsize */    (destructor)pywindow_dealloc,                      /* tp_dealloc */    0,                                                 /* tp_print */    0,                                                 /* tp_getattr */    0,                                                 /* tp_setattr */    0,                                                 /* tp_reserved */    0,                                                 /* tp_repr */    0,                                                 /* tp_as_number */    0,                                                 /* tp_as_sequence */    0,                                                 /* tp_as_mapping */    0,                                                 /* tp_hash  */    0,                                                 /* tp_call */    0,                                                 /* tp_str */    pyobject_genericgetattr,                           /* tp_getattro */    pyobject_genericsetattr,                           /* tp_setattro */    0,                                                 /* tp_as_buffer */    py_tpflags_default | py_tpflags_basetype,          /*tp_flags*/    "pywindow",                                        /* tp_doc */    0,                                                 /* tp_traverse */    0,                                                 /* tp_clear */    0,                                                 /* tp_richcompare */    0,                                                 /* tp_weaklistoffset */    0,                                                 /* tp_iter */    0,                                                 /* tp_iternext */    window_instance_methods,                           /* tp_methods */    0,                                                 /* tp_members */    0,                                                 /* tp_getset */    0,                                                 /* tp_base */    0,                                                 /* tp_dict */    0,                                                 /* tp_descr_get */    0,                                                 /* tp_descr_set */    0,                                                 /* tp_dictoffset */    0,                                                 /* tp_init */    0,                                                 /* tp_alloc */    pywindow_new,                                      /* tp_new */};

定义 pywindow 类型,包括其方法、内存分配、释放和其他行为。

litecam.cpp

#include <python.h>#include <stdio.h>#include "pycamera.h"#include "pywindow.h"#define initerror return nullstatic pyobject *getdevicelist(pyobject *obj, pyobject *args){    pyobject *pylist = pylist_new(0);    std::vector<capturedeviceinfo> devices = listcapturedevices();    for (size_t i = 0; i < devices.size(); i++)    {        capturedeviceinfo &device = devices[i];#ifdef _win32        pyobject *pyname = pyunicode_fromwidechar(device.friendlyname, wcslen(device.friendlyname));        if (pyname != null)        {            pylist_append(pylist, pyname);            py_decref(pyname);        }#else        pyobject *pyname = pyunicode_fromstring(device.friendlyname);        if (pyname != null)        {            pylist_append(pylist, pyname);            py_decref(pyname);        }#endif    }    return pylist;}static pyobject *savejpeg(pyobject *obj, pyobject *args){    const char *filename = null;    int width = 0, height = 0;    pyobject *bytearray = null;    if (!pyarg_parsetuple(args, "siio", &filename, &width, &height, &bytearray))    {        pyerr_setstring(pyexc_typeerror, "expected arguments: str, int, int, pybytearray");        return null;    }    unsigned char *data = (unsigned char *)pybytearray_asstring(bytearray);    py_ssize_t size = pybytearray_size(bytearray);    if (size != (py_ssize_t)(width * height * 3))    {        pyerr_setstring(pyexc_valueerror, "invalid byte array size for the given width and height.");        return null;    }    saveframeasjpeg(data, width, height, filename);    py_return_none;}static pymethoddef litecam_methods[] = {    {"getdevicelist", getdevicelist, meth_varargs, "get available cameras"},    {"savejpeg", savejpeg, meth_varargs, "get available cameras"},    {null, null, 0, null}};static struct pymoduledef litecam_module_def = {    pymoduledef_head_init,    "litecam",    "internal "litecam" module",    -1,    litecam_methods};pymodinit_func pyinit_litecam(void){    pyobject *module = pymodule_create(&litecam_module_def);    if (module == null)        initerror;    if (pytype_ready(&pycameratype) < 0)    {        printf("failed to initialize pycameratype");        py_decref(module);        return null;    }    if (pymodule_addobject(module, "pycamera", (pyobject *)&pycameratype) < 0)    {        printf("failed to add pycamera to the module");        py_decref(&pycameratype);        py_decref(module);        initerror;    }    if (pytype_ready(&pywindowtype) < 0)    {        printf("failed to initialize pywindowtype");        py_decref(module);        return null;    }    if (pymodule_addobject(module, "pywindow", (pyobject *)&pywindowtype) < 0)    {        printf("failed to add pywindow to the module");        py_decref(&pywindowtype);        py_decref(module);        initerror;    }    return module;}

说明

构建 python 相机 sdk

实现 python 多条码扫描器的步骤

  1. 安装python相机sdk和dynamsoft barcode reader sdk:

    pip install lite-camera dynamsoft-capture-vision-bundle
  2. 获取 dynamsoft barcode reader 30 天免费试用许可证。

  3. 创建用于多条码扫描的 python 脚本:

    import litecamfrom dynamsoft_capture_vision_bundle import *import queueclass framefetcher(imagesourceadapter):    def has_next_image_to_fetch(self) -&gt; bool:        return true    def add_frame(self, imagedata):        self.add_image_to_buffer(imagedata)class mycapturedresultreceiver(capturedresultreceiver):    def __init__(self, result_queue):        super().__init__()        self.result_queue = result_queue    def on_captured_result_received(self, captured_result):        self.result_queue.put(captured_result)if __name__ == '__main__':    errorcode, errormsg = licensemanager.init_license(        "license-key")    if errorcode != enumerrorcode.ec_ok and errorcode != enumerrorcode.ec_license_cache_used:        print("license initialization failed: errorcode:",              errorcode, ", errorstring:", errormsg)    else:        camera = litecam.pycamera()        if camera.open(0):            cvr = capturevisionrouter()            fetcher = framefetcher()            cvr.set_input(fetcher)            result_queue = queue.queue()            receiver = mycapturedresultreceiver(result_queue)            cvr.add_result_receiver(receiver)            errorcode, errormsg = cvr.start_capturing(                enumpresettemplate.pt_read_barcodes.value)            if errorcode != enumerrorcode.ec_ok:                print("error:", errormsg)            window = litecam.pywindow(                camera.getwidth(), camera.getheight(), "camera stream")            while window.waitkey('q'):                frame = camera.captureframe()                if frame is not none:                    width = frame[0]                    height = frame[1]                    size = frame[2]                    data = frame[3]                    window.showframe(width, height, data)                    imagedata = imagedata(                        bytes(data), width, height, width * 3, enumimagepixelformat.ipf_rgb_888)                    fetcher.add_frame(imagedata)                    if not result_queue.empty():                        captured_result = result_queue.get_nowait()                        items = captured_result.get_items()                        for item in items:                            if item.get_type() == enumcapturedresultitemtype.crit_barcode:                                text = item.get_text()                                location = item.get_location()                                x1 = location.points[0].x                                y1 = location.points[0].y                                x2 = location.points[1].x                                y2 = location.points[1].y                                x3 = location.points[2].x                                y3 = location.points[2].y                                x4 = location.points[3].x                                y4 = location.points[3].y                                contour_points = [                                    (x1, y1), (x2, y2), (x3, y3), (x4, y4)]                                window.drawcontour(contour_points)                                window.drawtext(text, x1, y1, 24, (255, 0, 0))                                del location            camera.release()            cvr.stop_capturing()

    将 license-key 替换为您的 dynamsoft barcode reader 许可证密钥。

  4. 运行脚本:

    python main.py

源代码

https://github.com/yushulx/python-lite-camera