Skip to content

Barcode detection with OpenCV, ZBar and Python – a step-by-step tutorial

We’ll show you how to use Pyzbar, a Python wrapper for ZBar, with the computer vision library OpenCV to scan barcodes from an image file.

Kevin December 5, 2024 12 mins read
app store

OpenCV is a powerful open-source library for real-time computer vision and machine learning applications, offering algorithms for tasks such as image processing, object detection, and facial recognition.

Combined with barcode scanning libraries like ZBar, OpenCV can detect and read barcodes from still images and camera feeds.

In this tutorial, we’ll use OpenCV and the ZBar Python wrapper Pyzbar to scan barcodes on Linux using a simple Python script.

Prerequisites

  • An up-to-date Linux environment
  • Python 3 installed

We’re using a Raspberry Pi with Ubuntu Desktop 24.04.1 LTS in this tutorial, but you can also modify the script to run it in a Docker container. More on that later in the tutorial.

Step 1: Install the necessary packages

First, install ZBar on your machine:

sudo apt update
sudo apt install -y libzbar0

Next, we’re going to create a virtual environment for Python. This isolates project dependencies, ensures reproducibility, prevents conflicts between different projects, and ensures your global Python environment remains clean.

If you haven’t installed the venv Python module yet, do so by running:

sudo apt install -y python3-venv

To create the virtual environment, run the following command:

python3 -m venv .env --system-site-packages

💡 A quick explanation of the command’s options and arguments:

  • python3 invokes Python version 3, since the venv module isn’t included in earlier Python versions.
  • -m venv tells Python to search for the module named venv in the Python module path and execute its contents in the main module.
  • .env is the name we’re giving to the directory in which Python will create the virtual environment.
  • --system-site-packages gives the virtual environment access to the packages installed globally on your system while still allowing you to install and manage packages specific to your project within the virtual environment.

To activate the virtual environment, run:

source .env/bin/activate

💡 When executed, this command loads the virtual environment’s settings and modifies the current shell’s environment variables. Specifically, it:

  1. Changes the shell prompt to indicate the active virtual environment.
  2. Modifies the PATH so the virtual environment’s Python interpreter and installed packages are used.
  3. Sets environment variables specific to the virtual environment.

Everything from now on happens in the virtual environment. If you exit the terminal or open another one, you need to reactivate the virtual environment with the above command.

Next, install the Python packages for OpenCV and Pyzbar:

pip install opencv-python pyzbar

Now that all necessary packages are installed, let’s write a script that uses them.

Step 2: Write the Python script

In any directory, create a .py file (e.g., barcode-scanner.py) and open it in your text editor of choice. We’ll use this script to read barcodes from a still image. (Scroll down to the end of this section for the full script.)

Let’s start by importing OpenCV for image processing and the decode function from Pyzbar for barcode detection:

import cv2
from pyzbar.pyzbar import decode

Next, specify from which image the barcodes should be read using OpenCV’s imread function. We’re using the following example image in this tutorial, but feel free to specify the path to any image you’d like to scan.

image = cv2.imread('barcodes.png')

Use Pyzbar’s decode function to detect and decode all barcodes in the image, resulting in a list of detected barcodes:

barcodes = decode(image)

Now comes the main part of the script, a loop for processing the detected barcodes:

for barcode in barcodes:
    # Part 1
    barcode_data = barcode.data.decode('utf-8')
    barcode_type = barcode.type

    # Part 2
    (x, y, w, h) = barcode.rect
    cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)

    # Part 3
    text = f"{barcode_data} ({barcode_type})"
    cv2.putText(image, text, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
  • Part 1 extracts the decoded data as a UTF-8-formatted string and the barcode’s type (QR Code, Code 128, EAN-13, etc.).
  • Part 2 extracts the barcode coordinates and draws a green rectangle around it on the resulting image.
  • Part 3 creates a string with the barcode data and type and draws it above the barcode on the same image.

When the loop is finished, the following code displays the resulting image in a window and waits for the user to press any key before it closes the window:

cv2.imshow('Barcode Scanner', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

The complete script looks like this:

import cv2
from pyzbar.pyzbar import decode

# Read the image
image = cv2.imread('barcodes.png') # Change the image file path if necessary.

# Decode barcodes
barcodes = decode(image)

# Process detected barcodes
for barcode in barcodes:
    # Extract barcode data and type
    barcode_data = barcode.data.decode('utf-8')
    barcode_type = barcode.type

    # Draw rectangle around the barcode
    (x, y, w, h) = barcode.rect
    cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)

    # Put barcode data and type on the image
    text = f"{barcode_data} ({barcode_type})"
    cv2.putText(image, text, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

# Display the result
cv2.imshow('Barcode Scanner', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

💡 If you’re running this script in a Docker container or a similar GUI-less environment, the resulting image won’t show. In that case, you can replace the contents of your script with this terminal-only alternative:

import cv2
from pyzbar.pyzbar import decode

# Read the image
image = cv2.imread('barcodes.png') # Change the image file path if necessary.

# Decode barcodes
barcodes = decode(image)

# Process detected barcodes
if not barcodes:
    print("No barcodes detected.")
else:
    for barcode in barcodes:
        # Extract barcode data and type
        barcode_data = barcode.data.decode('utf-8')
        barcode_type = barcode.type

        # Print barcode data and type
        print(f"Barcode Data: {barcode_data}")
        print(f"Barcode Type: {barcode_type}")
        print("-------------------------")

You might also have to install the headless version of OpenCV for the script to work properly:

pip install opencv-python-headless

Step 3: Run the barcode scanning script

From your file manager or terminal, run the script to process the provided image file.

python3 barcode-scanner.py

The script will output something similar to this:

The output image OpenCV provides after scanning the barcodes with Pyzbar

As you can see, the Code 128 and QR Code were correctly recognized and decoded. Same for the lower EAN‑13 barcode. However, the upper one is actually a UPC‑A barcode.

Neither the Data Matrix Code nor the PDF417 barcode were detected. In addition, only the lower part of the DataBar code (actually a GS1 DataBar Stacked Omnidirectional code) was recognized.

This reveals the shortcomings of scanning barcodes with OpenCV and Pyzbar. More on that further down.

Scanning barcodes from a camera feed

Instead of detecting barcodes on a still image, you can use OpenCV and Pyzbar to decode them from a live camera stream.

However, this requires additonal setup, since the official OpenCV Python packages currently do not include support for GStreamer by default. If you want to scan barcodes from your camera feed, you need to compile OpenCV manually with GStreamer support.

See issue #530 in the opencv-python GitHub repository for further details.

This obstacle and the limited number of barcode types supported by Pyzbar make it hard to recommend OpenCV as a professional barcode scanning solution. For companies that heavily rely on barcode scanning in their business processes, we recommend using an enterprise-grade solution instead.

Scanning barcodes from an image file or camera feed with OpenCV and the Scanbot SDK

We developed the Scanbot Barcode Scanner SDK to help enterprises overcome the hurdles presented by free barcode scanning software. Our goal was to have a developer-friendly solution available for a wide range of platforms that consistently delivers high-quality results – even in challenging circumstances.

Our Linux Barcode Scanner SDK reads barcodes from still images and live camera feeds and returns the barcode data as a list, making it ideal for a variety of deployment scenarios, including private clouds, drones, robots, and edge devices running on Linux.

In this tutorial, we’ll use the Python implementation of our Linux SDK example app on a Raspberry Pi. When we’re done, the values of the scanned barcodes will be displayed in the terminal.

To achieve this, we’re going to follow these steps:

  1. Download and extract the example app
  2. Set up the Python virtual environment
  3. Install the Scanbot SDK package
  4. Run the Python barcode scanner script

⚠️ Our Linux SDK is currently in closed beta. To receive the necessary license key, API token, and version number, please send us an email at: beta@scanbot.io

The Linux SDK also works on all Linux distributions with GLIBC 2.27 or higher. Just make sure that your device has either an ARM64 or x86_64 architecture. To test the live detection feature, your device also needs a camera.

Step 1: Download and extract the example app

Our example app includes Python scripts for processing barcodes both from image files and from a live camera feed. Just download or clone our GitHub repository and unzip the files if necessary.

git clone https://github.com/doo/scanbot-sdk-example-linux.git

Step 2: Set up the Python virtual environment

Install the virtual environment and OpenCV modules. If you’ve already set up the virtual environment module for the Pyzbar implementation, you can also install OpenCV in there via pip install opencv-python.

sudo apt install -y python3-venv python3-opencv

Go to one of the lowest-level example app folders (minimal or live recognition) before inputting the following commands to save yourself the trouble of having to cd into it later.

First, create the virtual environment inside that folder:

python3 -m venv .env --system-site-packages

Then activate it:

source .env/bin/activate

Installing the Scanbot SDK package also requires the setuptools utilities and support for the binary distribution format wheels, so we’re going to install those in the virtual environment.

pip install --upgrade pip setuptools wheel

Step 4: Install the Scanbot SDK package

For this step, you need to know three things:

  • Whether your device uses the ARM64 or x86_64 architecture
  • The current Scanbot SDK version number
  • Your API token for the Scanbot SDK

You need the latter two to download the appropriate Scanbot SDK binary. If you haven’t yet received them, please contact us at beta@scanbot.io.

First, export the SCANBOTSDK_VERSION variable with the version number so we can use them in the next step:

export SCANBOTSDK_VERSION=X.X.XXX

Then do the same for the SCANBOTSDK_API_TOKEN variable with the token ID:

export SCANBOTSDK_API_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Now we’re going to install the correct Scanbot SDK package for your device’s architecture. You don’t need to paste the version number or the API token into the command, since we just exported them.

For ARM64 architectures

pip install https://scanbotsdk-deployment.s3.amazonaws.com/ScanbotSDK-Linux/${SCANBOTSDK_API_TOKEN}/scanbotsdk-${SCANBOTSDK_VERSION}-py3-none-linux_aarch64.whl

For x86_64 architectures

pip install https://scanbotsdk-deployment.s3.amazonaws.com/ScanbotSDK-Linux/${SCANBOTSDK_API_TOKEN}/scanbotsdk-${SCANBOTSDK_VERSION}-py3-none-linux_x86_64.whl

To test if the package has been installed correctly, run the following command. If it returns nothing, you’re good to go!

python3 -c "import scanbotsdk"

💡 If you want to try both the minimal and the live recognition approach, repeat steps 3 and 4 for the other folder.

Step 5: Run the Python barcode scanner script

You’re almost ready to run detect_barcodes.py! But first, open it in your text editor or IDE, go to line 5, and replace Put-your-license-key-here with the license key you received from us (while keeping the surrounding double quotes).

The following steps differ depending on whether you want to run the script included in the minimal folder or the one in the live recognition folder.

For minimal (barcode recognition on an image file)

With the virtual environment activated, run the following command inside the minimal folder. If you’d like to use your own file for testing, just replace the image file path.

python3 detect_barcodes.py ../../example.png

Your output should look similar to this:

Initializing Scanbot SDK...
License Status: LicenseStatus.OK
Found 3 barcodes
	Barcode(text='https://qrcodes.pro/lwKmGZ', format=<BarcodeFormat.QR_CODE: 11>, raw_bytes=b'https://qrcodes.pro/lwKmGZ', points=[135, 175, 256, 175, 256, 296, 135, 296])
	Barcode(text='https://qrcodes.pro/eQnvl5', format=<BarcodeFormat.QR_CODE: 11>, raw_bytes=b'https://qrcodes.pro/eQnvl5', points=[325, 175, 446, 174, 446, 296, 325, 296])
	Barcode(text='https://qrcodes.pro/XPAkG3', format=<BarcodeFormat.QR_CODE: 11>, raw_bytes=b'https://qrcodes.pro/XPAkG3', points=[515, 174, 636, 175, 636, 297, 515, 296])

🎉 Congratulations, you successfully scanned barcodes from an image file!

For live recognition (barcode reading with a camera)

You’ll need to run different commands depending on your setup.

Using a USB camera

In the following command, replace 1 with your camera’s device node number, then run it from the live recognition folder.

python3 detect_barcodes.py --input 1
Using a CSI camera on an NVIDIA Jetson Nano
python3 detect_barcodes.py --input jetson_csi

All you have to do then is to hold barcodes to the camera. The SDK will scan them automatically and print out their data. The SDK will continue to scan barcodes until you terminate the script using Ctrl+C.

🎉 Congratulations, you successfully scanned barcodes from a live camera feed!

Optional flags

You can add additional flags to your command to customize the output:

  • --use-display: Display a window that shows the recognized barcodes in a live view. The recognized barcodes are indicated by a green border overlay.
  • --use-tensorrt: Enable TensorRT acceleration (Jetson only).

💡 The frames per second you can achieve with --use-display during live detection depend on the power of your hardware. Hence, running the script on a Jetson Nano will result in smoother playback than on a Raspberry Pi.

Framerate comparison between Raspberry Pi and NVIDIA Jetson Nano

Learn more about the Linux Barcode Scanner SDK

The Linux Barcode Scanner SDK is currently in closed beta, with the SDK itself and a trial license available on request via beta@scanbot.io.

Our customer Cypher Robotics uses the Linux SDK in its automated inventory cycle counting system to scan barcoded items autonomously in a warehouse environment.

Should you have questions about this tutorial or ran into any issues, we’re happy to help! Just shoot us an email via tutorial-support@scanbot.io.