Raspberry Pi Barcode Scanner in Python
Previously, I wrote an article Raspberry Pi Barcode Scanner with Webcam and Python illustrating how to build a simple barcode scanner using Dynamsoft Barcode Reader SDK and OpenCV from scratch. The method decodeFile() was used for detecting barcodes from an image file. To use the API, you have to write image buffer that obtained by OpenCV API to a file beforehand. Because the I/O operation takes too much time, this API is not suitable for real-time barcode detection from webcam video stream. Considering this scenario, I have added a new Python API decodeBuffer(). In this article, I will illustrate how to create and use the new API.
Testing Environment
- Device: Raspberry Pi 3
- Operating System: RASPBIAN JESSIE WITH PIXEL
Prerequisites
- Dynamsoft Barcode Reader for Raspberry Pi
- Python 2.7.0
- OpenCV 3.0.0
- Raspberry Pi 2 or 3
- USB webcam
Building and Installation
How to Build OpenCV on Raspberry Pi
- Download and extract the source code.
-
Install dependencies:
sudo apt-get install cmake sudo apt-get install libjpeg-dev libtiff5-dev libjasper-dev libpng12-dev sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev sudo apt-get install libxvidcore-dev libx264-dev sudo apt-get install python-dev
-
Setting up the build:
cd ~/opencv-3.0.0/ mkdir build cd build cmake -D CMAKE_BUILD_TYPE=RELEASE \ -D CMAKE_INSTALL_PREFIX=/usr/local \ -D INSTALL_C_EXAMPLES=ON \ -D INSTALL_PYTHON_EXAMPLES=ON \ -D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib-3.0.0/modules \ -D BUILD_EXAMPLES=ON ..
-
Compile and install OpenCV:
make -j4 sudo make install
The shared library will be located at /usr/local/lib/python2.7/dist-packages/cv2.so.
How to build the Python extension with Dynamsoft Barcode Reader SDK
- Download and extract the SDK package.
-
Create a symbolic link for libDynamsoftBarcodeReader.so:
sudo ln –s <Your dbr path>/lib/libDynamsoftBarcodeReader.so /usr/lib/libDynamsoftBarcodeReader.so
-
Open setup.py and modify the paths of include and lib files:
include_dirs=["/usr/lib/python2.7/dist-packages/numpy/core/include/numpy", "<Your dbr path>/include"], library_dirs=['<Your dbr path>/lib'],
-
Build the extension:
sudo python setup.py build install
How to implement the decodeBuffer method?
Because the source code is transplanted from Windows edition, we have to define following types and structure:
typedef unsigned long DWORD;
typedef long LONG;
typedef unsigned short WORD;
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;
Convert the numpy data from Python to C in decodeBuffer(). Besides that, we have to construct a buffer for barcode reading:
#include <ndarraytypes.h>
static PyObject *
decodeBuffer(PyObject *self, PyObject *args)
{
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;
}
// Construct data with header info and image data
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
char *total = (char *)malloc(size + 40); // buffer size = image size + header size
memset(total, 0, size + 40);
BITMAPINFOHEADER bitmap_info = {40, width, height, 0, 24, 0, size, 0, 0, 0, 0};
memcpy(total, &bitmap_info, 40);
// Copy image data to buffer from bottom to top
char *data = total + 40;
int stride = pai->strides[0];
int i = 1;
for (; i <= height; i++) {
memcpy(data, buffer + stride * (height - i), stride);
data += stride;
}
// Dynamsoft Barcode Reader initialization
__int64 llFormat = (OneD | QR_CODE | PDF417 | DATAMATRIX);
int iMaxCount = 0x7FFFFFFF;
ReaderOptions ro = {0};
pBarcodeResultArray pResults = NULL;
ro.llBarcodeFormat = llFormat;
ro.iMaxBarcodesNumPerPage = iMaxCount;
printf("width: %d, height: %d, size:%d\n", width, height, size);
int iRet = DBR_DecodeBuffer((unsigned char *)total, size + 40, &ro, &pResults);
printf("DBR_DecodeBuffer ret: %d\n", iRet);
free(total); // Do not forget to release the constructed buffer
// Get 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);
Py_DECREF(ao);
return retval;
}
Raspberry Pi Barcode Scanner
How to set video frame rate, frame width, and frame height?
You can refer to the Property identifier:
- CV_CAP_PROP_FRAME_WIDTH: Width of the frames in the video stream.
- CV_CAP_PROP_FRAME_HEIGHT: Height of the frames in the video stream.
- CV_CAP_PROP_FPS: Frame rate.
If failed to use the property identifier, set the value directly as follows:
vc = cv2.VideoCapture(0)
vc.set(5, 30) #set FPS
vc.set(3, 320) #set width
vc.set(4, 240) #set height
How to use the API decodeBuffer()?
while True:
cv2.imshow(windowName, frame)
rval, frame = vc.read();
results = decodeBuffer(frame)
if (len(results) > 0):
print "Total count: " + str(len(results))
for result in results:
print "Type: " + types[result[0]]
print "Value: " + result[1] + "\n"
# 'ESC' for quit
key = cv2.waitKey(20)
if key == 27:
break
How to run the Raspberry Pi Barcode Scanner?
- Connect a USB webcam to Raspberry Pi 2 or 3.
-
Run app.py:
python app.py
Raspberry Pi Camera (Not USB Camera)
If you are using a Pi camera, please follow the tutorial - Accessing the Raspberry Pi Camera with OpenCV and Python - to convert the image data before using barcode detection API.
Install picamera:
pip install "picamera[array]"
Try following code:
from picamera.array import PiRGBArray
from picamera import PiCamera
import time
import cv2
# initialize the camera and grab a reference to the raw camera capture
camera = PiCamera()
rawCapture = PiRGBArray(camera)
# allow the camera to warmup
time.sleep(0.1)
# grab an image from the camera
camera.capture(rawCapture, format="bgr")
image = rawCapture.array
# Barcode detection
results = decodeBuffer(image)