-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
338 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# ZXing vs. ZBar vs. Dynamsoft Barcode Reader in Python | ||
This repository provides a comparison of barcode recognition performance between three popular barcode scanning libraries: **ZXing**, **ZBar**, and [Dynamsoft Barcode Reader](https://pypi.org/project/dbr/). | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
axxel
|
||
|
||
## Dataset Download | ||
Download the full dataset from the following link: https://drive.google.com/uc?id=1uThXXH8HiHAw6KlpdgcimBSbrvi0Mksf&export=download | ||
|
||
## Installation | ||
|
||
To get started, install the required dependencies: | ||
|
||
```bash | ||
pip install -r requirements.txt | ||
``` | ||
|
||
|
||
## Usage | ||
1. Obtain a [Dynamsoft Barcode Reader trial license](ttps://www.dynamsoft.com/customer/license/trialLicense) and update the code with the license key in `app.py`. | ||
|
||
```python | ||
BarcodeReader.init_license('LICENSE-KEY') | ||
``` | ||
|
||
2. Run the Python script: | ||
|
||
```bash | ||
python app.py | ||
|
||
Usage: | ||
python app.py -i <image_file> | ||
python app.py -d <folder_directory> | ||
``` | ||
|
||
## Benchmark Results | ||
Below is a visual comparison of the barcode recognition rates among ZXing, ZBar, and Dynamsoft Barcode Reader based on the dataset. | ||
|
||
![barcode sdk benchmark](https://www.dynamsoft.com/codepool/img/2020/02/benchmark-barcode-sdk.png) | ||
|
||
## Blog | ||
[How to Use Python ZXing and Python ZBar on Windows 10](https://www.dynamsoft.com/codepool/python-zxing-zbar-barcode.html) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
import argparse | ||
import pyzbar.pyzbar as zbar | ||
from PIL import Image | ||
import zxingcpp | ||
from dbr import * | ||
import time | ||
import os | ||
import data | ||
import cv2 | ||
|
||
|
||
def zxing_decode(filename): | ||
start = time.time() | ||
img = cv2.imread(filename) | ||
zxing_results = zxingcpp.read_barcodes(img) | ||
elapsed_time = time.time() - start | ||
if zxing_results != None: | ||
for result in zxing_results: | ||
print('ZXing: {}. Elapsed time: {}ms'.format( | ||
result.text, int(elapsed_time * 1000))) | ||
return zxing_results | ||
else: | ||
print('ZXing failed to decode {}'.format(filename)) | ||
|
||
return None | ||
|
||
|
||
def zbar_decode(zbar_reader, filename): | ||
start = time.time() | ||
zbar_results = zbar.decode(Image.open(filename)) | ||
elapsed_time = time.time() - start | ||
if len(zbar_results) > 0: | ||
for zbar_result in zbar_results: | ||
print('ZBar: {}. Elapsed time: {}ms'.format( | ||
zbar_result.data.decode("utf-8"), int(elapsed_time * 1000))) | ||
|
||
return zbar_results | ||
else: | ||
print('ZBar failed to decode {}'.format(filename)) | ||
|
||
return None | ||
|
||
|
||
def dbr_decode(dbr_reader, filename): | ||
try: | ||
start = time.time() | ||
dbr_results = dbr_reader.decode_file(filename) | ||
elapsed_time = time.time() - start | ||
|
||
if dbr_results != None: | ||
for text_result in dbr_results: | ||
# print(textResult["BarcodeFormatString"]) | ||
print('Dynamsoft Barcode Reader: {}. Elapsed time: {}ms'.format( | ||
text_result.barcode_text, int(elapsed_time * 1000))) | ||
|
||
return dbr_results | ||
else: | ||
print("DBR failed to decode {}".format(filename)) | ||
except Exception as err: | ||
print("DBR failed to decode {}".format(filename)) | ||
|
||
return None | ||
|
||
|
||
def dataset(directory=None, zbar_reader=None, dbr_reader=None): | ||
if directory != None: | ||
print(directory) | ||
files = os.listdir(directory) | ||
files = [f for f in files if f.endswith('.jpg') or f.endswith('.png')] | ||
total_count = len(files) | ||
if total_count == 0: | ||
print('No image files') | ||
return | ||
|
||
# Create a .xlsx file | ||
datafile = 'benchmark.xlsx' | ||
wb = data.get_workbook(datafile) | ||
index = 2 | ||
|
||
print('Total count of barcode image files: {}'.format(total_count)) | ||
zbar_count = 0 | ||
dbr_count = 0 | ||
zxing_count = 0 | ||
|
||
for filename in files: | ||
file_path = os.path.join(directory, filename) | ||
expected_result = filename.split('_')[0] | ||
|
||
r1 = '' | ||
r2 = '' | ||
r3 = '' | ||
|
||
# ZBar | ||
if zbar_reader != None: | ||
zbar_results = zbar_decode(zbar_reader, file_path) | ||
if zbar_results != None: | ||
for zbar_result in zbar_results: | ||
zbar_text = zbar_result.data.decode("utf-8") | ||
r1 = zbar_text | ||
if r1 == expected_result: | ||
zbar_count += 1 | ||
break | ||
else: | ||
print('Fail to decode {}'.format(filename)) | ||
|
||
# DBR | ||
if dbr_reader != None: | ||
textResults = dbr_decode(dbr_reader, file_path) | ||
if textResults != None: | ||
for textResult in textResults: | ||
r2 = textResult.barcode_text | ||
if r2 == expected_result: | ||
dbr_count += 1 | ||
break | ||
else: | ||
print("DBR failed to decode {}".format(filename)) | ||
|
||
# ZXing | ||
print('ZXing decoding {}'.format(filename)) | ||
zxing_results = zxing_decode(file_path) | ||
if zxing_results != None: | ||
for result in zxing_results: | ||
r3 = result.text | ||
if r3 == expected_result: | ||
zxing_count += 1 | ||
else: | ||
print('ZXing failed to decode {}'.format(filename)) | ||
|
||
# Add results to .xlsx file | ||
data.update_row(wb, index, filename, expected_result, r1, r2, r3) | ||
index += 1 | ||
|
||
# Test | ||
# if index == 9: | ||
# break | ||
|
||
r1 = 0 | ||
r2 = 0 | ||
r3 = 0 | ||
if zbar_reader != None: | ||
zbar_rate = zbar_count * 100 / total_count | ||
r1 = '{0:.2f}%'.format(zbar_rate) | ||
print('ZBar recognition rate: {0:.2f}%'.format(zbar_rate)) | ||
|
||
if dbr_reader != None: | ||
dbr_rate = dbr_count * 100 / total_count | ||
r2 = '{0:.2f}%'.format(dbr_rate) | ||
print('DBR recognition rate: {0:.2f}%'.format(dbr_rate)) | ||
|
||
zxing_rate = zxing_count * 100 / total_count | ||
r3 = '{0:.2f}%'.format(zxing_rate) | ||
print('ZXing recognition rate: {0:.2f}%'.format(zxing_rate)) | ||
|
||
data.set_recognition_rate(wb, index, r1, r2, r3) | ||
# Save data to .xlsx file | ||
data.save_workbook(wb, datafile) | ||
|
||
|
||
def main(): | ||
ap = argparse.ArgumentParser() | ||
ap.add_argument("-i", "--image", type=str, | ||
help="path to input image") | ||
ap.add_argument("-d", "--directory", type=str, | ||
help="directory of image folder") | ||
args = vars(ap.parse_args()) | ||
|
||
image = args["image"] | ||
directory = args["directory"] | ||
if image == None and directory == None: | ||
print(''' | ||
Usage: | ||
python app.py -i <image_file> | ||
python app.py -d <folder_directory> | ||
''') | ||
return | ||
|
||
# Initialize barcode reader | ||
BarcodeReader.init_license( | ||
'DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==') | ||
dbr_reader = BarcodeReader() | ||
|
||
if image != None: | ||
# ZXing | ||
zxing_decode(image) | ||
|
||
# ZBar | ||
zbar_decode(zbar, image) | ||
|
||
# Dynamsoft Barcode Reader | ||
dbr_decode(dbr_reader, image) | ||
|
||
if directory != None: | ||
dataset(directory, | ||
zbar_reader=zbar, dbr_reader=dbr_reader) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
from openpyxl import utils | ||
from openpyxl import Workbook | ||
from openpyxl import load_workbook | ||
from openpyxl.styles import Color, PatternFill | ||
|
||
import os | ||
|
||
# Define cell color | ||
red = PatternFill(start_color='FFFF0000', | ||
end_color='FFFF0000', | ||
fill_type='solid') | ||
|
||
green = PatternFill(start_color='FF00FF00', | ||
end_color='FF00FF00', | ||
fill_type='solid') | ||
|
||
yellow = PatternFill(start_color='00FFFF00', | ||
end_color='00FFFF00', | ||
fill_type='solid') | ||
|
||
passed = 'Passed' | ||
|
||
def get_workbook(wb_name): | ||
if os.path.isfile(wb_name): | ||
wb = load_workbook(wb_name) | ||
else: | ||
wb = Workbook() | ||
ws = wb.active | ||
ws.title = 'Recognition Rate' | ||
ws['A1'] = 'File Name' | ||
# Set column width | ||
ws.column_dimensions[utils.get_column_letter(1)].width = 25 | ||
ws['B1'] = 'Expected Results' | ||
ws.column_dimensions[utils.get_column_letter(2)].width = 20 | ||
ws['C1'] = 'ZBar' | ||
ws.column_dimensions[utils.get_column_letter(3)].width = 20 | ||
ws['D1'] = 'DBR' | ||
ws.column_dimensions[utils.get_column_letter(4)].width = 20 | ||
ws['E1'] = 'ZXing' | ||
ws.column_dimensions[utils.get_column_letter(5)].width = 20 | ||
return wb | ||
|
||
def save_workbook(wb, wb_name): | ||
if wb != None: | ||
wb.save(wb_name) | ||
|
||
def append_row(wb, filename=None, expected_results=None, zbar_results=None, dbr_results=None, ZXing_results=None): | ||
ws = wb.active | ||
ws.append([filename, expected_results, zbar_results, dbr_results, ZXing_results]) | ||
|
||
def update_row(wb, row_index, filename=None, expected_results=None, zbar_results=None, dbr_results=None, ZXing_results=None): | ||
ws = wb.active | ||
row = ws[row_index] | ||
row[0].value = filename | ||
row[1].value = expected_results | ||
if zbar_results != None: | ||
row[2].value = zbar_results | ||
if zbar_results == expected_results: | ||
row[2].fill = green | ||
else: | ||
row[2].fill = red | ||
|
||
if dbr_results != None: | ||
row[3].value = dbr_results | ||
if dbr_results == expected_results: | ||
row[3].fill = green | ||
else: | ||
row[3].fill = red | ||
|
||
if ZXing_results != None: | ||
row[4].value = ZXing_results | ||
if ZXing_results == expected_results: | ||
row[4].fill = green | ||
else: | ||
row[4].fill = red | ||
|
||
def set_recognition_rate(wb, row_index, r1=None, r2=None, r3=None): | ||
ws = wb.active | ||
row = ws[row_index] | ||
row[2].value = r1 | ||
row[3].value = r2 | ||
row[4].value = r3 | ||
|
||
|
||
# Test | ||
# name = 'data.xlsx' | ||
# wb = get_workbook(name) | ||
# ws = wb.active | ||
# index = 2 | ||
# update_row(wb, index, r'D:\python-zxing-zbar-dbr\dataset\20499525_2.jpg', '20499525', '20499525', '20499525', '20499521') | ||
# index += 1 | ||
# set_recognition_rate(wb, index, '59.46%', '75.68%', '13.51%') | ||
# save_workbook(wb, name) | ||
|
||
|
||
|
||
|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
dbr | ||
pyzbar | ||
zxing-cpp | ||
openpyxl |
As the maintainer of the
zxing-cpp
project which you are apparently referring to withZXing
here, I'd like to point out that this name is technically misleading. While thezxing-cpp
project started out as a C++ port of the javaZXing
project, it has digressed a lot over the years. Some parts, like the EAN13 decoder that is benchmaked here are a complete rewrite. So to be more accurate, I'd like to suggest to either use the termZXing-C++
orzxing-cpp
here. Thanks.