Back to Blog
Python

Decode QR Codes and Barcodes with Python pyzbar

python pyzbar decode qr codes and barcodes: Learn to decode QR codes and barcodes in Python using pyzbar: installation, image loading, handling multiple codes, preproc...

pyzbarQR code decodingbarcode decodingZBar wrapperimage processing
Python code with a QR code and barcode being decoded into text, representing pyzbar's decode function.

When you need to decode QR codes or barcodes in Python, pyzbar is one of the most direct libraries to use. It wraps the ZBar C library and exposes a simple decode function that accepts PIL images or numpy arrays. The phrase python pyzbar decode qr codes and barcodes describes exactly that workflow: you feed an image in, get a list of decoded objects, and read the payload. This article covers the setup, the API, common pitfalls, and how to get reliable results in real-world applications.

What pyzbar Does and How It Fits Into a Python Project

pyzbar is a Python wrapper around the ZBar barcode reader. It supports QR codes, EAN-13, UPC-A, Code 128, and many other 1D and 2D symbologies. The library does not decode images on its own; it relies on ZBar's native code, which means you must install the system library separately. Once installed, you can decode an image in three lines:

from pyzbar.pyzbar import decode from PIL import Image result = decode(Image.open("sample.png")) print(result)

This minimal example works for both QR codes and linear barcodes. The decode function returns a list of Decoded objects, one for each barcode found. If nothing is found, the list is empty.

Because pyzbar is a wrapper, it does not reimplement decoding logic. That means its behavior is tied to ZBar's capabilities and limitations. For example, ZBar expects an image with reasonable contrast and sharp edges; blurry or low-resolution images may fail to decode even though a human can still read them.

Installing pyzbar and Its System Dependencies

Before you can import pyzbar, you need both the Python package and the ZBar native library. The Python package installs via pip:

pip install pyzbar

The system dependency varies by operating system. On Debian-based Linux distributions, install libzbar0:

sudo apt-get install libzbar0

On macOS, Homebrew provides zbar:

brew install zbar

On Windows, the situation is more involved. You need the ZBar DLL. One common approach is to use the pyzbar wheel that bundles the DLL, or you can download the ZBar library and add its bin directory to your PATH. If you see an ImportError mentioning libzbar, it means the native library is missing. The error message usually points to the exact file that could not be loaded.

For image handling, you also need either Pillow or numpy, because pyzbar accepts PIL.Image objects or numpy arrays. Install one of them:

pip install Pillow numpy

If you plan to work with OpenCV, you will pass numpy arrays directly, so install opencv-python as well.

Decoding QR Codes and Barcodes From an Image

PyZbar's decode function is the single entry point. It accepts a PIL.Image or a numpy array. The following example shows both approaches.

Using PIL

from pyzbar.pyzbar import decode from PIL import Image image = Image.open("barcode.png") results = decode(image) for item in results: print(item.data.decode("utf-8"))

The data field is a bytes object, so you need to decode it to a string if you expect text. For QR codes, the payload is usually UTF-8 text, but it can be binary. For barcodes like EAN-13, the payload is a numeric string.

Using OpenCV (numpy array)

import cv2 from pyzbar.pyzbar import decode image = cv2.imread("qr.png") results = decode(image) for item in results: print(item.data.decode("utf-8"))

OpenCV loads images in BGR order. pyzbar does not care about color channels because it internally converts to grayscale, but the image must be a contiguous numpy array with shape (height, width, 3) or (height, width). If you pass a grayscale image, it works directly.

What Happens When No Barcode Is Found

If the image contains no decodable barcode, decode returns an empty list. This is not an error; it is the expected behavior. You should check the length of the returned list before accessing elements.

results = decode(image) if not results: print("No barcode found") else: for item in results: print(item.data)

Understanding the Decoded Object and Its Fields

Each item in the list returned by decode is a Decoded object with four attributes:

FieldTypeDescription
databytesRaw payload of the barcode or QR code.
typestrSymbology name, e.g., 'QRCODE', 'EAN13', 'CODE128'.
rectRectangleBounding box of the barcode in the image.
polygonlistFour corner points of the detected barcode.

The rect object has left, top, width, and height attributes. The polygon is a list of four Point objects, each with x and y coordinates. These are useful for drawing annotations or cropping the region.

Here is how you can access them:

for item in results: print("Type:", item.type) print("Data:", item.data.decode("utf-8")) print("Rect:", item.rect.left, item.rect.top, item.rect.width, item.rect.height) print("Polygon:", [(p.x, p.y) for p in item.polygon])

The type field is especially important when you need to filter for specific barcode formats. For example, if you only care about QR codes, you can ignore everything else.

Decoding Multiple Codes and Filtering by Type

A single image can contain several barcodes. pyzbar detects all of them and returns them in one list. You can iterate over the list and process each result independently.

results = decode(image) qr_codes = [r for r in results if r.type == 'QRCODE'] other_codes = [r for r in results if r.type != 'QRCODE']

This filtering is straightforward because the type field is a string. The exact values follow ZBar's naming convention: 'QRCODE', 'EAN13', 'EAN8', 'UPCA', 'UPCE', 'CODE128', 'CODE39', 'I25', and others.

When you need to associate a decoded value with its location, use the rect or polygon fields. This is common in applications that overlay results on an image or trigger actions based on where the barcode appears.

Improving Decode Accuracy With Image Preprocessing

ZBar works best on clean, high-contrast images. Real-world photos often contain noise, perspective distortion, or uneven lighting. Preprocessing can dramatically improve the chance of a successful decode.

Convert to Grayscale and Increase Contrast

If you are using a color image, converting it to grayscale removes color noise and reduces data size. You can then apply contrast enhancement, such as histogram equalization, to make the black-and-white pattern more distinct.

import cv2 from pyzbar.pyzbar import decode image = cv2.imread("photo.jpg") gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # Increase contrast contrast = cv2.convertScaleAbs(gray, alpha=1.5, beta=0) results = decode(contrast)

Resize Very Large Images

Decoding a 4000×3000 photo is slower and may actually reduce accuracy because ZBar's internal algorithms assume a certain resolution. Downscaling to a width of around 800–1000 pixels often speeds up decoding without losing the barcode. Use OpenCV's resize function:

resized = cv2.resize(gray, (800, int(gray.shape[0] * 800 / gray.shape[1]))) results = decode(resized)

Remove Perspective Distortion

If the barcode is photographed at an angle, ZBar may fail. You can use OpenCV's perspective correction to straighten the region before decoding. This is more complex and requires detecting the barcode's corners, which is a separate problem. For simple use cases, try preprocessing steps first.

Use Morphological Operations

For damaged or noisy barcodes, morphological operations like cv2.morphologyEx with a closing kernel can fill small gaps in the lines. This is not a silver bullet, but it can help with printed barcodes that have scratches.

Performance and Resource Considerations

Decoding is CPU-bound. The time it takes depends on image size, number of barcodes, and the complexity of the symbology. For a typical 640×480 image, decoding usually takes a few milliseconds to tens of milliseconds. For large images, it can take hundreds of milliseconds.

To keep performance predictable in production, consider these practices:

  • Resize images before decoding when the barcode occupies a small portion of the frame.
  • Convert to grayscale before passing to pyzbar, especially if you are using OpenCV.
  • If you are processing a video stream, decode only every Nth frame or use a dedicated thread to avoid blocking the main loop.
  • Reuse image buffers where possible to reduce memory allocation overhead.

pyzbar itself does not provide a way to limit the search region. If you know the barcode appears in a specific area, crop the image before calling decode. This reduces the amount of data ZBar has to scan and can improve both speed and accuracy.

Handling Edge Cases and Common Failures

Missing Native Library

The most common failure is an ImportError when importing pyzbar. The error message will say something like ImportError: libzbar.so: cannot open shared object file. This means the system dependency is not installed. Revisit the installation section for your platform.

Empty Results on Valid Barcodes

If you have a barcode that a phone can read but pyzbar cannot, the image is likely too blurry, too small, or has poor contrast. Try preprocessing as described earlier. Also check that the barcode is not inverted (white bars on a black background). ZBar expects dark bars on a light background. You can invert the image with cv2.bitwise_not and try again.

Non-UTF-8 Data

QR codes can store binary data. Calling .decode("utf-8") on the data field will raise a UnicodeDecodeError if the payload is not valid UTF-8. Handle this by catching the exception or by using errors="replace":

text = item.data.decode("utf-8", errors="replace")

Multiple Decodes of the Same Barcode

In a video stream, the same barcode will appear in many frames. If you are tracking unique codes, you need to deduplicate based on the decoded data and possibly the location. The rect field can help you decide whether the same physical barcode is being seen again.

Choosing Between pyzbar and Other Decoding Libraries

pyzbar is not the only option. OpenCV includes a QRCodeDetector that can detect and decode QR codes, but it does not handle linear barcodes. ZBar's native library is also available through other wrappers, but pyzbar is the most widely used and well-maintained.

The main tradeoff is dependency complexity: pyzbar requires the ZBar system library, which can be awkward to install on Windows. If you only need QR codes and want to avoid system dependencies, OpenCV's built-in detector might be simpler. However, if you need both QR codes and traditional barcodes, pyzbar is the more complete solution.

For a production system, consider wrapping pyzbar in a service that isolates the dependency and provides a clean API. This way, if you later switch to a different decoding library, the rest of your codebase does not change.

When you do switch, remember that the Decoded object is pyzbar-specific. Other libraries return different structures, so you will need to adapt your code. The main advantage of pyzbar is its consistent API and the fact that it returns a polygon for each detected barcode, which is useful for augmented reality or document scanning applications.

python pyzbar decode qr codes and barcodes: Practical Usage | RYUSLOG DEV