This article is about how to use OpenCV and Dynamsoft Barcode Reader SDK to create a Python barcode reader on macOS.
How to Install OpenCV on macOS
Use ‘sw_vers’ to check macOS system version information:
Install Homebrew.
If you have already installed Homebrew, just update it. When running ‘brew update’, you may see following errors.
Error: /usr/local/Library/brew.sh: line 32: /usr/local/Library/ENV/scm/git: No such file or directory.
To fix the error, run:
cd "$(brew --repository)" && git fetch && git reset --hard origin/master
Error: Fetching /usr/local/Library/Taps/flutter/homebrew-flutter failed!
To repair the error, run:
brew untap flutter/flutter
The next step is to install Python and NumPy. Python is pre-installed on macOS. The default version is not compatible with the latest OpenCV. Therefore, you need to install the latest Python using Homebrew.
brew install python python3 numpy echo 'import site; site.addsitedir("/usr/local/lib/python2.7/site-packages")' >> /Users/xiao/Library/Python/2.7/lib/python/site-packages/homebrew.pth
Use command ‘python –version’ to check the current version. If it is not the latest version, you can edit .bash_profile and export the path:
vim ~/.bash_profile export PATH=/usr/local/Cellar/python/2.7.13/bin:$PATH source ~/.bash_profile
Install OpenCV:
brew tap homebrew/science brew install opencv3
Python Barcode Reader for macOS
Get libDynamsoftBarcodeReader.dylib and relevant header files from DBR-Libs.zip.
To link the dynamic library and use barcode reading APIs, we need to write code in C/C++. Include the header files:
#include <Python.h> #include "If_DBR.h" #include "BarcodeFormat.h" #include "BarcodeStructs.h" #include "ErrorCode.h" #include <ndarraytypes.h>
Where is ndarraytypes.h?
Use command ‘find / -name ndarraytypes.h’ to find it:
/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/numpy/core/include/numpy/ndarraytypes.h /usr/local/Cellar/numpy/1.13.0/lib/python2.7/site-packages/numpy/core/include/numpy/ ndarraytypes.h /usr/local/lib/python2.7/site-packages/numpy/core/include/numpy/ndarraytypes.h
Get native image data that decoded by OpenCV Python API:
PyObject *o; if (!PyArg_ParseTuple(args, "O", &o)) return NULL; PyObject *ao = PyObject_GetAttrString(o, "__array_struct__"); PyObject *retval; if ((ao == NULL) || !PyCObject_Check(ao)) { PyErr_SetString(PyExc_TypeError, "object does not have array interface"); return NULL; } PyArrayInterface *pai = (PyArrayInterface*)PyCObject_AsVoidPtr(ao); if (pai->two != 2) { PyErr_SetString(PyExc_TypeError, "object does not have array interface"); Py_DECREF(ao); return NULL; } char *buffer = (char*)pai->data; // The address of image data int width = pai->shape[1]; // image width int height = pai->shape[0]; // image height int size = pai->strides[0] * pai->shape[0]; // image size = stride * height
Define BITMAPINFOHEADER structure. The DWORD defined by Microsoft is unsigned long, but here it is unsigned int. The size of the type should be 4 bytes.
typedef unsigned int DWORD; typedef int LONG; typedef unsigned short WORD; #pragma pack(push) #pragma pack(1) typedef struct tagBITMAPINFOHEADER { DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER; #pragma pack(pop)
Construct a buffer with bitmap header info for barcode detection.
int dib_header_size = sizeof(BITMAPINFOHEADER); char *total = (char *)malloc(size + dib_header_size); // buffer size = image size + header size memset(total, 0, size + dib_header_size); BITMAPINFOHEADER bitmap_info = {dib_header_size, width, height, 0, 24, 0, size, 0, 0, 0, 0}; memcpy(total, &bitmap_info, dib_header_size); // Copy image data to buffer from bottom to top char *data = total + dib_header_size; int stride = pai->strides[0]; int i = 1; for (; i <= height; i++) { memcpy(data, buffer + stride * (height - i), stride); data += stride; } int iRet = DBR_DecodeBuffer((unsigned char *)total, size + dib_header_size, &ro, &pResults);
Get and return barcode results:
int count = pResults->iBarcodeCount; pBarcodeResult* ppBarcodes = pResults->ppBarcodes; pBarcodeResult tmp = NULL; retval = PyList_New(count); // The returned Python object PyObject* result = NULL; i = 0; for (; i < count; i++) { tmp = ppBarcodes[i]; result = PyString_FromString(tmp->pBarcodeData); printf("result: %s\n", tmp->pBarcodeData); PyList_SetItem(retval, i, Py_BuildValue("iN", (int)tmp->llFormat, result)); // Add results to list } // release memory DBR_FreeBarcodeResults(&pResults);
Configure setup.py for building Python extension:
from distutils.core import setup, Extension module_dbr = Extension('dbr', sources = ['dbr.c'], include_dirs=['/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/numpy/core/include/numpy', './include'], library_dirs=['./lib'], libraries=['DynamsoftBarcodeReader']) setup (name = 'DynamsoftBarcodeReader', version = '1.0', description = 'Python barcode extension', ext_modules = [module_dbr])
Build and install dbr.so:
python setup.py build install
Write a simple Python barcode detection app:
import cv2 from dbr import * import sys import os.path initLicense("D426ABF246933C82A16D537FC46C064F") frame = cv2.imread(fileName, cv2.CV_LOAD_IMAGE_COLOR) results = decodeBuffer(frame)
Note: when using imread, you have to set second parameter CV_LOAD_IMAGE_COLOR. The native API only works for a color image.
Source Code
https://github.com/dynamsoft-dbr/mac-opencv
The post Using OpenCV to Build Python Barcode Reader for macOS appeared first on Code Pool.