How to Run ARM32 and ARM64 Python Barcode Reader in Docker Containers
Tags: docker, python, programming, raspberrypi
Published: 2023-03-07 02:50:23

These are just a few Python barcode SDKs that support **ARM32** and **ARM64** architectures. [Dynamsoft Barcode Reader](https://www.dynamsoft.com/barcode-reader/sdk-desktop-server/) 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:

```bash
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](https://www.dynamsoft.com/customer/license/trialLicense/?product=dbr) to activate it.

```python
from dbr import *
BarcodeReader.init_license(license_key)
```

### Quick Start

```python
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](https://github.com/Dynamsoft/barcode-reader-python-samples)

### Online API Documentation
[https://www.dynamsoft.com/barcode-reader/docs/server/programming/python/](https://www.dynamsoft.com/barcode-reader/docs/server/programming/python/)

### Visual Studio Code Extension
[https://marketplace.visualstudio.com/items?itemName=yushulx.dbr-code-generator](https://marketplace.visualstudio.com/items?itemName=yushulx.dbr-code-generator)

## 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](https://www.qemu.org/download/) is a popular open-source emulator that can emulate a variety of CPU architectures, including ARM, PowerPC, and MIPS. [multiarch/qemu-user-static](https://hub.docker.com/r/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](https://docs.docker.com/desktop/install/windows-install/).
2. Install [qemu-user-static](https://hub.docker.com/r/multiarch/qemu-user-static):
    ```bash
    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**

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

    **DockerfileArm64**

    ```dockerfile
    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:
    ```bash
    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. 

```python
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.

```python
#!/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.

```bash
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](https://www.dynamsoft.com/codepool/img/2023/03/docker-windows-cpu-resource.png)

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

![docker container cpu speed](https://www.dynamsoft.com/codepool/img/2023/03/docker-container-cpu-speed.png)

Although it is impossible to change the CPU speed in Docker, we can use [--cpuset-cpus](https://docs.docker.com/config/containers/resource_constraints/#cpu) 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.

```bash
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:

```bash
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](https://github.com/lukechilds/dockerpi) to emulate different Raspberry Pi models. 

```bash
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](https://github.com/yushulx/docker-arm64-arm32-python-barcode-detection)