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.
💡 Here is a benchmark report of ZXing and its alternatives for scanning QR Codes.
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
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
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
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
Search for the keyword “Python” on the homepage of ZXing repository.
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"]
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
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
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.