Home >Backend Development >Python Tutorial >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.
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
Explanation:
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}, )
Explanation:
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.
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
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}, )
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.
PyCamera_dealloc
#include <Python.h> #include <structmember.h> #include "Camera.h"
Deallocates the Camera object and releases the associated memory.
PyCamera_new
typedef struct { PyObject_HEAD Camera *handler; } PyCamera;
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 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); }
Opens the camera with the specified index.
listMediaTypes
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; }
Returns a list of supported media types. For Windows, it converts the wide character string to a Python Unicode object.
release
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); }
Releases the camera.
setResolution
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; }
Sets the resolution of the camera.
captureFrame
static PyObject *release(PyObject *obj, PyObject *args) { PyCamera *self = (PyCamera *)obj; self->handler->Release(); Py_RETURN_NONE; }
Captures a frame from the camera and returns the RGB data as a Python byte array.
getWidth and getHeight
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); }
Returns the width and height of the captured frame.
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; } }
Defines the methods that are available on the PyCamera Python object. These methods are associated with the corresponding C functions defined above.
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); }
Defines the PyCamera type, including its methods, memory allocation, deallocation, and other behaviors.
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}};
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 */ };
Defines a PyWindow struct that wraps a C CameraWindow object. The handler member points to an instance of CameraWindow.
PyWindow_dealloc
#include <Python.h> #include <structmember.h> #include "CameraPreview.h"
Deallocates the CameraWindow object and releases the memory.
PyWindow_new
typedef struct { PyObject_HEAD CameraWindow *handler; } PyWindow;
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
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
Waits for a key press event and returns False if the key matches the specified character. The False means the application should exit.
showFrame
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}, )
Displays a frame in the window.
drawContour
#include <Python.h> #include <structmember.h> #include "Camera.h"
Draws a contour (a series of points) on the frame.
drawText
typedef struct { PyObject_HEAD Camera *handler; } PyCamera;
Draws text on the frame.
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); }
Defines the methods that are available on the PyWindow Python object.
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; }
Defines the PyWindow type, including its methods, memory allocation, deallocation, and other behavior.
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); }
Explanation:
Development Mode
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; }
Wheel Package
static PyObject *release(PyObject *obj, PyObject *args) { PyCamera *self = (PyCamera *)obj; self->handler->Release(); Py_RETURN_NONE; }
Source Distribution
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); }
Install the Python camera SDK and Dynamsoft Barcode Reader SDK:
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; } }
Obtain a 30-day free trial license for Dynamsoft Barcode Reader.
Create a Python script for multi-barcode scanning:
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); }
Replace LICENSE-KEY with your Dynamsoft Barcode Reader license key.
Run the script:
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}};
https://github.com/yushulx/python-lite-camera
The above is the detailed content of Building a Python Camera SDK and Using It for Multi-Barcode Scanning. For more information, please follow other related articles on the PHP Chinese website!