Using OpenCV to Build Python Barcode Reader for macOS

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/ 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

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 dataint width = pai->shape[1];       // image widthint height = pai->shape[0];      // image heightint 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 sizememset(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 topchar *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 objectPyObject* 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 memoryDBR_FreeBarcodeResults(&pResults);

Configure for building Python extension:

from distutils.core import setup, Extensionmodule_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

python build install

Write a simple Python barcode detection app:

import cv2from dbr import *import sysimport os.pathinitLicense("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

Originally published at on July 17, 2017.

Manager of Dynamsoft Open Source Projects | Tech Lover