How to Use Python ZXing and Python ZBar on Windows 10

When creating a barcode reader app with a free barcode SDK, you may come up with ZXing instantly. ZXing is an open-source barcode reading and decoding library implemented in Java. There are some third-party projects, either ports or bindings, available for other programming languages. So if you want to create a Python barcode reader, you can use Python ZXing, which is a Python wrapper for invoking ZXing Java class. If you care about the performance, we can use Python ZBar, a Python wrapper for the ZBar C++ code, as a comparison.

Making Python ZXing Work

Get the source code of ZXing:

git clone https://github.com/zxing/zxing.git

Build the project using Maven:

cd zxing
mvn install

Generate a .jar file with dependencies:

cd javase
mvn package assembly:single -DskipTests
maven jar dependency

On Windows 10, you may fail to decode a barcode image using the command as follows:

java -cp D:\zxing\javase\target\javase-3.4.1-SNAPSHOT-jar-with-dependencies.jar  com.google.zxing.client.j2se.CommandLineRunner D:\dataset\20499525_2.jpg
zxing command error

If you change the backward slash contained in the file path to forward slash, you will get different error information:

java -cp D:\zxing\javase\target\javase-3.4.1-SNAPSHOT-jar-with-dependencies.jar  com.google.zxing.client.j2se.CommandLineRunner D:/dataset/20499525_2.jpg          
zxing command unknown protocol

How to fix the unknown protocol error? The workaround is to use the URI scheme file:///D:/dataset/20499525_2.jpg:

java -cp D:\zxing\javase\target\javase-3.4.1-SNAPSHOT-jar-with-dependencies.jar  com.google.zxing.client.j2se.CommandLineRunner file:///D:/dataset/20499525_2.jpg
zxing command line runner

Search for the keyword “Python” on the homepage of ZXing repository.

zxing homepage

Visit the python-zxing project to get the source code:

git clone https://github.com/oostendo/python-zxing.git

Edit the line in zxing\__init__.py file. Replace the libs with the .jar file built from the ZXing source code:

libs = ["javase/target/javase-3.4.1-SNAPSHOT-jar-with-dependencies.jar"]
python setup.py change

Build and install the Python module:

python setup.py build install

Create an app.py file:

import argparse
import zxing

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("-i", "--image", type=str,
        help="path to input image")
    args = vars(ap.parse_args())
 
    image = args["image"]
    if image == None:
        print('Try command: python app.py -i <image_file> ')
        return

    # ZXing
    reader = zxing.BarCodeReader('D:/zxing') # Set the ZXing project directory
    barcode = reader.decode('file:///' + image.replace('\\', '/'))
    print('ZXing: {}'.format(barcode.data.rstrip()))

# Command: python app.py -i <image_file>
if __name__ == "__main__":
    main()

The barcode result returned from ZXing contains a trailing newline, so we can use rstrip() to remove it.

Run the program:

> python app.py -i D:\dataset\20499525_2.jpg
ZXing: 20499525
barcode image

ZBar: The Alternative to ZXing

Not like ZXing, installing ZBar is much easier:

pip install pyzbar

Add the code snippet of using ZBar to app.py file:

import pyzbar.pyzbar as zbar
from PIL import Image
barcode = zbar.decode(Image.open(image))
print('ZBar: {}'.format(barcode[0].data.decode("utf-8")))

Rerun the Python program and we can see both of them decode barcode correctly:

> python3 app.py -i D:\dataset\20499525_2.jpg                                                
ZXing: 20499525

ZBar: 20499525

We can take a further look at the time cost of decoding barcode:

# ZXing
    reader = zxing.BarCodeReader('D:/zxing') # Set the ZXing project directory
    start = time.time()
    barcode = reader.decode('file:///' + image.replace('\\', '/'))
    elapsed_time = time.time() - start
    print('ZXing: {}. Elapsed time: {}ms'.format(barcode.data.rstrip(), int(elapsed_time * 1000)))

    # ZBar
    start = time.time()
    barcode = zbar.decode(Image.open(image))
    elapsed_time = time.time() - start
    print('ZBar: {}. Elapsed time: {}ms'.format(barcode[0].data.decode("utf-8"), int(elapsed_time * 1000)))

Although they both return the correct results, Python ZBar is much faster than ZXing:

> python3 app.py -i D:\dataset\20499525_2.jpg
ZXing: 20499525. Elapsed time: 2170ms
ZBar: 20499525. Elapsed time: 207ms
python zxing zbar barcode

The reason is Python ZXing uses subprocess module to get barcode results from Java process:

def decode(self, files, try_harder = False, qr_only = False):
    cmd = [self.command]
    cmd += self.args[:] #copy arg values
    if try_harder:
      cmd.append("--try_harder")
    if qr_only:
      cmd.append("--possibleFormats=QR_CODE")

    libraries = [self.location + "/" + l for l in self.libs]

    cmd = [ c if c != "LIBS" else os.pathsep.join(libraries) for c in cmd ]

    # send one file, or multiple files in a list
    SINGLE_FILE = False
    if type(files) != type(list()):
      cmd.append(files)
      SINGLE_FILE = True
    else:
      cmd += files

    (stdout, stderr) = subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True).communicate()

In my next article, I will use Python code to compare the recognition rate among ZXing, ZBar, and Dynamsoft Barcode Reader based on the public dataset https://drive.google.com/uc?id=1uThXXH8HiHAw6KlpdgcimBSbrvi0Mksf&export=download.

Source Code

https://github.com/yushulx/python-zxing-zbar-dbr