DEV Community

Xiao Ling
Xiao Ling

Posted on • Originally published at dynamsoft.com

Building a Python Camera SDK and Using It for Multi-Barcode Scanning

Now that the lightweight C++ camera SDK is complete for Windows, Linux and macOS, we can integrate it into other high-level programming languages. In this article, we will explore how to build a Python camera SDK based on the C++ camera library and use it for multi-barcode scanning with the Dynamsoft Barcode Reader SDK.

Python Multi-Barcode Scanner Demo Video

Scaffolding a CPython Extension Project

A CPython extension is a shared library (e.g., a DLL on Windows, a .so on Linux, or a .dylib on macOS) that can be loaded into the Python interpreter at runtime and used to extend its functionality. The structure of the lite camera CPython extension project is as follows:


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
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • include: The header files of the C++ camera library.
  • lib: The shared libraries of the C++ camera library.
  • src: The source code of the Python camera SDK.
  • litecam: The entry point of the Python extension.
  • setup.py: The build script.
  • MANIFEST.in: The manifest file for including non-Python files.
  • README.md: The documentation.

Writing Build Script setup.py for Python Extension

Add the following content to setup.py:

from setuptools.command import build_ext
from setuptools import setup, Extension
import sys
import os
import io
from setuptools.command.install import install
import shutil
from pathlib import Path

lib_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},
      )
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • lite-camera: The name of the Python package.
  • ext_modules: A list of CPython extensions. It specifies the source files, include directories, library directories, libraries, and compile/link flags for different platforms.
  • develop: A custom command for development. It copies the shared libraries to the litecam folder for easy testing.
  • build_ext: Customizes the build process for packaging the shared libraries into the wheel package.

Implementing Python Camera SDK API in C++

The pycamera.h file defines the PyCamera Python class for capturing frames from the camera, while the pywindow.h file defines the PyWindow Python class for displaying the frames in a window. The litecam.cpp file serves as the entry point for the Python extension, where some global methods are defined, and the PyCamera and PyWindow classes are registered.

pycamera.h

Includes

#include <Python.h>
#include <structmember.h>
#include "Camera.h"
Enter fullscreen mode Exit fullscreen mode
  • Python.h: Included to use the Python C API.
  • structmember.h: Provides macros and helpers for managing object members.
  • Camera.h: Defines the Camera class, which the PyCamera extension wraps.

PyCamera Struct Definition

typedef struct
{
    PyObject_HEAD Camera *handler;
} PyCamera;
Enter fullscreen mode Exit fullscreen mode

The PyCamera struct represents the Python object that wraps around the C++ Camera object. The handler is a pointer to an instance of the Camera class.

Methods

  • PyCamera_dealloc

    
    static int PyCamera_clear(PyCamera *self)
    {
        if (self->handler)
        {
            delete self->handler;
        }
        return 0;
    }
    
    static void PyCamera_dealloc(PyCamera *self)
    {
        PyCamera_clear(self);
        Py_TYPE(self)->tp_free((PyObject *)self);
    }
    
    

    Deallocates the Camera object and releases the associated memory.

  • PyCamera_new

    static PyObject *PyCamera_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
    {
        PyCamera *self;
    
        self = (PyCamera *)type->tp_alloc(type, 0);
        if (self != NULL)
        {
            self->handler = new Camera();
        }
    
        return (PyObject *)self;
    }
    
    

    Creates a new instance of PyCamera. It allocates memory for the Python object, creates a new Camera object, and assigns it to self->handler.

  • open

    static PyObject *open(PyObject *obj, PyObject *args)
    {
        PyCamera *self = (PyCamera *)obj;
    
        int index = 0;
        if (!PyArg_ParseTuple(args, "i", &index))
        {
            return NULL;
        }
    
        bool ret = self->handler->Open(index);
        return Py_BuildValue("i", ret);
    }
    

    Opens the camera with the specified index.

  • listMediaTypes

    static PyObject *listMediaTypes(PyObject *obj, PyObject *args)
    {
        PyCamera *self = (PyCamera *)obj;
    
        std::vector<MediaTypeInfo> mediaTypes = self->handler->ListSupportedMediaTypes();
    
        PyObject *pyList = PyList_New(0);
    
        for (size_t i = 0; i < mediaTypes.size(); i++)
        {
            MediaTypeInfo &mediaType = mediaTypes[i];
    
    #ifdef _WIN32
            PyObject *subtypeName = PyUnicode_FromWideChar(mediaType.subtypeName, wcslen(mediaType.subtypeName));
            PyObject *pyMediaType = Py_BuildValue("{s:i,s:i,s:O}",
                                                  "width", mediaType.width,
                                                  "height", mediaType.height,
                                                  "subtypeName", subtypeName);
            if (subtypeName != NULL)
            {
                Py_DECREF(subtypeName);
            }
    
    #else
            PyObject *pyMediaType = Py_BuildValue("{s:i,s:i,s:s}",
                                                  "width", mediaType.width,
                                                  "height", mediaType.height,
                                                  "subtypeName", mediaType.subtypeName);
    #endif
    
            PyList_Append(pyList, pyMediaType);
        }
    
        return pyList;
    }
    
    

    Returns a list of supported media types. For Windows, it converts the wide character string to a Python Unicode object.

  • release

    static PyObject *release(PyObject *obj, PyObject *args)
    {
        PyCamera *self = (PyCamera *)obj;
    
        self->handler->Release();
        Py_RETURN_NONE;
    }
    

    Releases the camera.

  • setResolution

    static PyObject *setResolution(PyObject *obj, PyObject *args)
    {
        PyCamera *self = (PyCamera *)obj;
        int width = 0, height = 0;
        if (!PyArg_ParseTuple(args, "ii", &width, &height))
        {
            return NULL;
        }
        int ret = self->handler->SetResolution(width, height);
        return Py_BuildValue("i", ret);
    }
    

    Sets the resolution of the camera.

  • captureFrame

    static PyObject *captureFrame(PyObject *obj, PyObject *args)
    {
        PyCamera *self = (PyCamera *)obj;
    
        FrameData frame = self->handler->CaptureFrame();
        if (frame.rgbData)
        {
            PyObject *rgbData = PyByteArray_FromStringAndSize((const char *)frame.rgbData, frame.size);
            PyObject *pyFrame = Py_BuildValue("iiiO", frame.width, frame.height, frame.size, rgbData);
            ReleaseFrame(frame);
    
            return pyFrame;
        }
        else
        {
            Py_RETURN_NONE;
        }
    }
    

    Captures a frame from the camera and returns the RGB data as a Python byte array.

  • getWidth and getHeight

    static PyObject *getWidth(PyObject *obj, PyObject *args)
    {
        PyCamera *self = (PyCamera *)obj;
    
        int width = self->handler->frameWidth;
        return Py_BuildValue("i", width);
    }
    
    static PyObject *getHeight(PyObject *obj, PyObject *args)
    {
        PyCamera *self = (PyCamera *)obj;
    
        int height = self->handler->frameHeight;
        return Py_BuildValue("i", height);
    }
    

    Returns the width and height of the captured frame.

instance_methods

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

Enter fullscreen mode Exit fullscreen mode

Defines the methods that are available on the PyCamera Python object. These methods are associated with the corresponding C functions defined above.

PyCameraType

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 */
};

Enter fullscreen mode Exit fullscreen mode

Defines the PyCamera type, including its methods, memory allocation, deallocation, and other behaviors.

pywindow.h

Includes

#include <Python.h>
#include <structmember.h>
#include "CameraPreview.h"
Enter fullscreen mode Exit fullscreen mode
  • Python.h: Required to use the Python C API.
  • structmember.h: Provides macros for managing object members.
  • CameraPreview.h: Defines the CameraWindow class, which is used to display a camera preview and interact with it.

PyWindow Struct Definition


typedef struct
{
    PyObject_HEAD CameraWindow *handler;
} PyWindow;

Enter fullscreen mode Exit fullscreen mode

Defines a PyWindow struct that wraps a C++ CameraWindow object. The handler member points to an instance of CameraWindow.

Methods for PyWindow Object

  • PyWindow_dealloc

    static int PyWindow_clear(PyWindow *self)
    {
        if (self->handler)
        {
            delete self->handler;
        }
        return 0;
    }
    
    static void PyWindow_dealloc(PyWindow *self)
    {
        PyWindow_clear(self);
        Py_TYPE(self)->tp_free((PyObject *)self);
    }
    
    

    Deallocates the CameraWindow object and releases the memory.

  • PyWindow_new

    static PyObject *PyWindow_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
    {
        PyWindow *self;
    
        int width = 0, height = 0;
        char *title = NULL;
    
        if (!PyArg_ParseTuple(args, "iis", &width, &height, &title))
        {
            return NULL;
        }
    
        self = (PyWindow *)type->tp_alloc(type, 0);
        if (self != NULL)
        {
            self->handler = new CameraWindow(width, height, title);
    
            if (!self->handler->Create())
            {
                std::cerr << "Failed to create window." << std::endl;
                return NULL;
            }
    
            self->handler->Show();
        }
    
        return (PyObject *)self;
    }
    
    

    Creates a new instance of PyWindow. It allocates memory for the Python object, creates a new CameraWindow object, and assigns it to self->handler.

  • waitKey

    static PyObject *waitKey(PyObject *obj, PyObject *args)
    {
        PyWindow *self = (PyWindow *)obj;
    
        const char *key = NULL;
    
        if (!PyArg_ParseTuple(args, "s", &key) || strlen(key) != 1)
        {
            PyErr_SetString(PyExc_ValueError, "Expected a single character string");
            return NULL;
        }
    
        bool ret = self->handler->WaitKey(key[0]);
        return Py_BuildValue("i", ret);
    }
    
    

    Waits for a key press event and returns False if the key matches the specified character. The False means the application should exit.

  • showFrame

    static PyObject *showFrame(PyObject *obj, PyObject *args)
    {
        PyWindow *self = (PyWindow *)obj;
    
        int width = 0, height = 0;
        PyObject *byteArray = NULL;
    
        if (!PyArg_ParseTuple(args, "iiO", &width, &height, &byteArray))
        {
            return NULL;
        }
    
        unsigned char *data = (unsigned char *)PyByteArray_AsString(byteArray);
    
        self->handler->ShowFrame(data, width, height);
    
        Py_RETURN_NONE;
    }
    
    

    Displays a frame in the window.

  • drawContour

    static PyObject *drawContour(PyObject *obj, PyObject *args)
    {
        PyWindow *self = (PyWindow *)obj;
    
        PyObject *pyPoints = NULL;
    
        if (!PyArg_ParseTuple(args, "O", &pyPoints))
        {
            return NULL;
        }
    
        std::vector<std::pair<int, int>> points;
    
        for (Py_ssize_t i = 0; i < PyList_Size(pyPoints); i++)
        {
            PyObject *item = PyList_GetItem(pyPoints, i);
            int x = PyLong_AsLong(PyTuple_GetItem(item, 0));
            int y = PyLong_AsLong(PyTuple_GetItem(item, 1));
            points.push_back(std::make_pair(x, y));
        }
    
        self->handler->DrawContour(points);
    
        Py_RETURN_NONE;
    }
    
    

    Draws a contour (a series of points) on the frame.

  • drawText

    static PyObject *drawText(PyObject *obj, PyObject *args)
    {
        PyWindow *self = (PyWindow *)obj;
    
        const char *text = NULL;
        int x = 0, y = 0, fontSize = 0;
        PyObject *pyColor = NULL;
    
        if (!PyArg_ParseTuple(args, "siiiO", &text, &x, &y, &fontSize, &pyColor))
        {
            return NULL;
        }
    
        CameraWindow::Color color;
        color.r = (unsigned char)PyLong_AsLong(PyTuple_GetItem(pyColor, 0));
        color.g = (unsigned char)PyLong_AsLong(PyTuple_GetItem(pyColor, 1));
        color.b = (unsigned char)PyLong_AsLong(PyTuple_GetItem(pyColor, 2));
    
        self->handler->DrawText(text, x, y, fontSize, color);
    
        Py_RETURN_NONE;
    }
    
    

    Draws text on the frame.

window_instance_methods

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

Enter fullscreen mode Exit fullscreen mode

Defines the methods that are available on the PyWindow Python object.

PyWindowType

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 */
};

Enter fullscreen mode Exit fullscreen mode

Defines the PyWindow type, including its methods, memory allocation, deallocation, and other behavior.

litecam.cpp


#include <Python.h>

#include <stdio.h>

#include "pycamera.h"
#include "pywindow.h"

#define INITERROR return NULL

static 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\n");
        Py_DECREF(module);
        return NULL;
    }

    if (PyModule_AddObject(module, "PyCamera", (PyObject *)&PyCameraType) < 0)
    {
        printf("Failed to add PyCamera to the module\n");
        Py_DECREF(&PyCameraType);
        Py_DECREF(module);
        INITERROR;
    }

    if (PyType_Ready(&PyWindowType) < 0)
    {
        printf("Failed to initialize PyWindowType\n");
        Py_DECREF(module);
        return NULL;
    }

    if (PyModule_AddObject(module, "PyWindow", (PyObject *)&PyWindowType) < 0)
    {
        printf("Failed to add PyWindow to the module\n");
        Py_DECREF(&PyWindowType);
        Py_DECREF(module);
        INITERROR;
    }

    return module;
}

Enter fullscreen mode Exit fullscreen mode

Explanation:

  • getDeviceList: Returns a list of available cameras.
  • saveJpeg: Saves a frame as a JPEG image.
  • PyInit_litecam: Initializes the litecam module and registers the PyCamera and PyWindow types.

Building the Python Camera SDK

  • Development Mode

    python setup.py develop
    
  • Wheel Package

    python setup.py bdist_wheel
    
  • Source Distribution

    python setup.py sdist
    

Steps to Implement a Python Multi-Barcode Scanner

  1. Install the Python camera SDK and Dynamsoft Barcode Reader SDK:

    pip install lite-camera dynamsoft-capture-vision-bundle
    
  2. Obtain a 30-day free trial license for Dynamsoft Barcode Reader.

  3. Create a Python script for multi-barcode scanning:

    import litecam
    from dynamsoft_capture_vision_bundle import *
    import queue
    
    class FrameFetcher(ImageSourceAdapter):
        def has_next_image_to_fetch(self) -> 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()
    
    

    Replace LICENSE-KEY with your Dynamsoft Barcode Reader license key.

  4. Run the script:

    
    python main.py
    

    Python multi-Barcode Scanner

Source Code

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

Top comments (0)