Moving Heavy Computation from Raspberry Pi to Windows PC

If you want to use Raspberry Pi as an economical way of detecting barcodes, you can take account for Dynamsoft Barcode Reader SDK. As a business software, Dynamsoft Barcode Reader SDK is designed for overcoming a variety of complicated scenarios with sophisticated algorithms and heavy computations. Although the SDK is flexible for customizing algorithm parameters, subject to the low-frequency CPU of Raspberry Pi, the tradeoff between recognition accuracy and detection speed is still a big challenge. In this article, I will use Socket client-server model as a substitute solution. Thanks to Sabjorn’s NumpySocket module.

System Requirements

Python Barcode Reader Module

Install Dynamsoft Barcode Reader and follow the GitHub repository to build and install the Python barcode module.

Why do We Need the Socket Client-Server Model for Raspberry Pi

I am using the Raspberry Pi 3 Model B+:

  • Broadcom BCM2837B0, Cortex-A53 (ARMv8) 64-bit SoC @ 1.4GHz
  • 1GB LPDDR2 SDRAM

Here is the time cost of calling the barcode decoding method on Raspberry Pi. Let’s see the resolution of 640×480 first.

Raspberry Pi barcode native

The average elapsed time is more than 400ms. What about the resolution of 320×240?

Raspberry Pi barcode native 320x240

It seems a little bit better but not stable. The CPU utilization may reach 100% which will freeze the process. That is why we need the Socket client-server model by which we can continuously send video frames from Raspberry Pi to a remote server and execute barcode detection tasks on the server.

How to Implement the C/S Model for Barcode Detection

Install OpenCVscipy, and pillow on Raspberry Pi:

$ sudo apt-get install libopencv-dev python-opencv python-scipy 
$ python -m pip install pillow

Download NumpySocket. The Python module has already implemented the function of transferring the NumPy array. I only added two methods for sending and receiving barcode results in JSON formats:

import json
def sendJSON(self, data):
    out = json.dumps(data)
 
    try:
        self.connection.sendall(out)
    except Exception:
        exit()
 
def receiveJSON(self):
    try:
        chunk = self.socket.recv(1024)    
    except Exception:
        exit()
             
    return json.loads(chunk)

Create a pc.py file:

from numpysocket import NumpySocket
import cv2
import time
import json
import dbr
 
# Get the license of Dynamsoft Barcode Reader from https://www.dynamsoft.com/CustomerPortal/Portal/Triallicense.aspx
dbr.initLicense('LICENSE KEY')
 
npSocket = NumpySocket()
npSocket.startServer(9999)
 
# Receive frames for barcode detection
while(True):
    try:
        frame = npSocket.recieveNumpy()
        # cv2.imshow('PC Reader', frame)
        results = dbr.decodeBuffer(frame, 0x3FF | 0x2000000 | 0x4000000 | 0x8000000 | 0x10000000)
        out = {}
        out['results'] = results
 
        # Send barcode results to Raspberry Pi
        npSocket.sendJSON({'results': results})
    except:
        break
     
    # Press ESC to exit
    key = cv2.waitKey(20)
    if key == 27 or key == ord('q'):
        break
 
npSocket.endServer()
print('Closed')

In the infinite loop, receive the video frames from Raspberry Pi and then call barcode decoding function to get the results.

Create a rpi.py file. In this file, create a queue for storing and sharing video frames between threads:

def read_barcode():
    vc = cv2.VideoCapture(0)
    vc.set(3, 640) #set width
    vc.set(4, 480) #set height
 
    if not vc.isOpened():
        print('Camera is not ready.')
        return
 
    host_ip = '192.168.8.84'
    npSocket = NumpySocket()
    npSocket.startClient(host_ip, 9999)
 
    socket_thread = SocketThread('SocketThread', npSocket)
    socket_thread.start() 
 
    while vc.isOpened():
         
        ret, f = vc.read()
        cv2.imshow("RPi Reader", f)
        frame = imresize(f, .5)
 
        key = cv2.waitKey(20)
        if key == 27 or key == ord('q'):   
            socket_thread.isRunning = False
            socket_thread.join() 
            break
 
        if not socket_thread.is_alive():
            break
         
        try:
            frame_queue.put_nowait(frame)
        except:
            # Clear unused frames
            try:
                while True:
                    frame_queue.get_nowait()
            except:
                pass
 
    frame_queue.close()
    frame_queue.join_thread()
    vc.release()

Create a worker thread for Socket connection:

class SocketThread (threading.Thread):
    def __init__(self, name, npSocket):
        threading.Thread.__init__(self)
        self.name = name
        self.npSocket = npSocket
        self.isRunning = True
 
    def run(self):      
        while self.isRunning:
            frame = frame_queue.get(timeout=100)
 
            try:
                start_time = time.time()
                self.npSocket.sendNumpy(frame)
                obj = self.npSocket.receiveJSON()
                print("--- %.2f ms seconds ---" % ((time.time() - start_time) * 1000))
                data = obj['results']
 
                if (len(data) > 0):
                    for result in data:
                        print("Type: " + result[0])
                        print("Value: " + result[1] + "\n")
                else:
                    print('No barcode detected.')
            except:
                break
             
        self.npSocket.endClient()

Run pc.py in Windows and run rpi.py on Raspberry Pi. I used the resolution of 640×480 for preview and resized the frame size for TCP/IP transfer.

Raspberry Pi barcode detection

The barcode detection works stalely, and the average elapsed time is less than 150ms.

Reference

Source Code

https://github.com/yushulx/python-socket-rpi-barcode