How to Run ARM32 and ARM64 Python Barcode Reader in Docker Containers

These are just a few Python barcode SDKs that support ARM32 and ARM64 architectures. Dynamsoft Barcode Reader is the only commercial 1D and 2D barcode SDK that provides barcode recognition capabilities for ARM32 and ARM64 devices. This article demonstrates how to create ARM32 and ARM64 emulated environments on Windows to implement barcode scanning with Dynamsoft Python Barcode SDK.

Getting Started with Dynamsoft Python Barcode SDK

You can install the Dynamsoft Python Barcode SDK using pip:

pip install dbr

The Python package supports Python 3.6 and above.

Supported Platforms

  • Windows (x64)
  • Linux (x64, ARM32, and ARM64)
  • macOS (x64 and ARM64)

License Key

After installing the Python barcode SDK, you need to apply for a valid license to activate it.

from dbr import *
BarcodeReader.init_license(license_key)

Quick Start

from dbr import *

license_key = "Input your own license"
image = r"Please input your own image path"

BarcodeReader.init_license(license_key)

reader = BarcodeReader()

try:
   text_results = reader.decode_file(image)

   if text_results != None:
      for text_result in text_results:
            print("Barcode Format : ")
            print(text_result.barcode_format_string)
            print("Barcode Text : ")
            print(text_result.barcode_text)
            print("Localization Points : ")
            print(text_result.localization_result.localization_points)
            print("Exception : ")
            print(text_result.exception)
            print("-------------")
except BarcodeReaderError as bre:
   print(bre)

Sample Code

https://github.com/Dynamsoft/barcode-reader-python-samples

Online API Documentation

https://www.dynamsoft.com/barcode-reader/docs/server/programming/python/

Visual Studio Code Extension

https://marketplace.visualstudio.com/items?itemName=Dynamsoft.dynamsoft-barcode-reader

Why Do You Need ARM32 and ARM64 Emulated Environments?

If you want to create a Python barcode scanning application that can run on an ARM32 or ARM64 device, like Raspberry Pi and Jetson Nano, an emulated environment is a convenient way to write and test code without the need for a physical ARM device.

How to Create Docker ARM32 and ARM64 Images Containing Dynamsoft Barcode SDK on Windows

QEMU is a popular open-source emulator that can emulate a variety of CPU architectures, including ARM, PowerPC, and MIPS. multiarch/qemu-user-static is a Docker image that allows you to emulate different CPU architectures on your host machine using QEMU.

Here are the steps to create Docker ARM32 and ARM64 images containing Dynamsoft Barcode SDK on Windows:

  1. Install Docker Desktop for Windows.
  2. Install qemu-user-static:
     docker run --rm --privileged multiarch/qemu-user-static:register --reset
    
  3. Create a Dockerfile that specifies the base image as an arm32 or arm64 architecture, and installs the necessary tools and dependencies: CMake, Dynamsoft Barcode Reader, OpenCV and Pillow.

    DockerfileArm32

     FROM arm32v7/python
     RUN apt-get update && apt-get install -y cmake libgl1-mesa-glx
     RUN pip install dbr opencv-python pillow
    

    DockerfileArm64

     FROM arm64v8/python
     RUN apt-get update && apt-get install -y cmake libgl1-mesa-glx
     RUN pip install dbr opencv-python pillow
    

    The opencv-python package is used to load and decode images to NumPy arrays, but it does not provide a pre-built wheel for ARM32. So, it may take a long time to build the package from the source code. The pillow package is an alternative to opencv-python for loading and decoding images.

  4. Build the Docker images for ARM32 and ARM64:
     docker build --platform linux/arm64 -f DockerfileArm64 -t <IMAGE-NAME> .
     docker build --platform linux/arm/v7 -f DockerfileArm32 -t <IMAGE-NAME> .
    

Now, we have successfully created Docker ARM32 and ARM64 images that contain Dynamsoft Python Barcode SDK. Next, we can write a Python barcode reader script and run it in the Docker container.

How to Run a Python Barcode Recognition Script in an ARM32 or ARM64 Docker Container

Most single-board computers, like Raspberry Pi and Jetson Nano, do not feature high-end hardware. Therefore, the decoding speed may not be ideal for large-sized images. The common scenario is to acquire images from a low-resolution (e.g. 640x480) USB camera and scan barcode QR codes in real-time.

To simulate the process, we first write a Python script to capture consecutive frames that contains QR codes from a USB camera and save them to a folder.

import cv2 as cv
from dbr import *

capture = cv.VideoCapture(0)

if not capture.isOpened():
    print("Cannot open camera")
    exit()
    
BarcodeReader.init_license("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==")
reader = BarcodeReader()

index = 0

while True:
    frame = capture.read()[1]
    cv.imshow("frame", frame)
    
    if cv.waitKey(1) == ord('q'):
        break
    
    results = reader.decode_buffer(frame)
    if results != None and len(results) > 0:
        cv.imwrite('images/' + str(index) + '.png', frame)
        index += 1
        
    if index == 30:
        break

Run the script on Windows and save the images to a folder named images.

Then, we write another Python script to read the images using PIL and decode barcodes with Dynamsoft Barcode Reader.

#!/usr/bin/env python3
import os
from dbr import *
import dbr
import time
from PIL import Image

def main():
    print('version: ' + dbr.__version__)
    BarcodeReader.init_license("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==")
    
    reader = BarcodeReader()
    reader.init_runtime_settings_with_file('faster.json', conflict_mode=EnumConflictMode.CM_OVERWRITE)
    
    # read file list
    folder = 'images'
    target_dir = os.path.join(os.getcwd(), folder)
    
    if os.path.exists(target_dir):
        filelist = os.listdir('images')
        
        index = 0
        while index  < 5:
            file = filelist[index]
            filapath = os.path.join(target_dir, file)
            
            index += 1
            
            with Image.open(filapath) as im:
                try:
                    start_time = time.time()
                    results = reader.decode_buffer_manually(im.tobytes(), im.width, im.height, im.width * 3, EnumImagePixelFormat.IPF_RGB_888)
                    elapsed_time = time.time() - start_time
                    print(file +  ", elapsed time: " + str(round(elapsed_time * 1000)) + "ms, " + ' results: ' + str(len(results)))
                
                    if results != None:
                        for result in results:
                            print(result.barcode_format_string + ': ' + result.barcode_text)
                    else:
                        print(' results: 0')
                    
                except Exception as err:
                    print(err)

if __name__ == '__main__':
    main()

We can mount the current Windows folder to the Docker container and run the script in the container.

docker run --platform linux/arm64 -it --rm -v ${pwd}:/usr/src/myapp -w /usr/src/myapp <IMAGE-NAME> python pillow_test.py
docker run --platform linux/arm/v7 -it --rm -v ${pwd}:/usr/src/myapp -w /usr/src/myapp <IMAGE-NAME> python pillow_test.py

There is one more question: how does CPU affect the decoding speed? The default hardware resources can be configured in the settings.

docker windows configuration

By default, each container’s access to the host machine’s CPU cycles is unlimited.

docker container cpu speed

Although it is impossible to change the CPU speed in Docker, we can use –cpuset-cpus to limit the number of CPU cores.

For example, we can limit the CPU cores to 2 and run the script in the container to check the performance difference.

docker run --cpuset-cpus="0,1" --platform linux/arm64 -it --rm -v ${pwd}:/usr/src/myapp -w /usr/src/myapp yushulx/dbr-arm64:1.0 python pillow_test.py

The nproc command can be used to verify the core number:

docker run --cpuset-cpus="0,1" --platform linux/arm64 -it --rm -v ${pwd}:/usr/src/myapp -w /usr/src/myapp yushulx/dbr-arm64:1.0 nproc
2

Raspberry Pi Emulator

To make the emulated environment more realistic, we can use dockerpi to emulate different Raspberry Pi models.

docker run -it lukechilds/dockerpi pi2

pi@raspberrypi:~$ lscpu
Architecture:        armv7l
Byte Order:          Little Endian
CPU(s):              4
On-line CPU(s) list: 0-3
Thread(s) per core:  1
Core(s) per socket:  4
Socket(s):           1
Vendor ID:           ARM
Model:               5
Model name:          Cortex-A7
Stepping:            r0p5
CPU max MHz:         700.0000
CPU min MHz:         700.0000
BogoMIPS:            125.00
Flags:               half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm

docker run -it lukechilds/dockerpi pi3

pi@raspberrypi:~$ lscpu
Architecture:        aarch64
Byte Order:          Little Endian
CPU(s):              4
On-line CPU(s) list: 0-3
Thread(s) per core:  1
Core(s) per socket:  4
Socket(s):           1
Vendor ID:           ARM
Model:               4
Model name:          Cortex-A53
Stepping:            r0p4
CPU max MHz:         700.0000
CPU min MHz:         700.0000
BogoMIPS:            125.00
Flags:               fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid

Source Code

https://github.com/yushulx/docker-arm64-arm32-python-barcode-detection