| import cv2 |
| import os |
| import re |
| import time |
| from collections import defaultdict |
|
|
| BARCODE_DIR = "images/barcode" |
| PART_NUMBER_RE = re.compile(r'^0301BAB\d+N$') |
|
|
|
|
| def get_image_files(): |
| files = sorted([f for f in os.listdir(BARCODE_DIR) |
| if f.lower().endswith(('.jpg', '.jpeg', '.png'))]) |
| return files |
|
|
|
|
| def is_chassis(text): |
| text = text.strip() |
| return bool(text) and not PART_NUMBER_RE.match(text) |
|
|
|
|
|
|
|
|
| def decode_pyzbar(img, gray, thresh): |
| from pyzbar.pyzbar import decode |
| all_values = set() |
| for frame in [img, thresh]: |
| for d in decode(frame): |
| text = d.data.decode("utf-8").strip() |
| all_values.add(text) |
| return all_values |
|
|
|
|
| def decode_zxingcpp(img, gray, thresh): |
| import zxingcpp |
| all_values = set() |
| for frame in [gray, thresh]: |
| try: |
| results = zxingcpp.read_barcodes(frame) |
| for r in results: |
| text = r.text.strip() |
| if text: |
| all_values.add(text) |
| except Exception as e: |
| pass |
| return all_values |
|
|
|
|
| def decode_cv2barcode(img, gray, thresh): |
| all_values = set() |
| try: |
| detector = cv2.barcode.BarcodeDetector() |
| for frame in [gray, thresh]: |
| ok, decoded_info, decoded_type, points = detector.detectAndDecode(frame) |
| if ok and decoded_info is not None: |
| for text in decoded_info: |
| if text and text.strip(): |
| all_values.add(text.strip()) |
| except AttributeError: |
| pass |
| except Exception as e: |
| pass |
| return all_values |
|
|
|
|
| def main(): |
| files = get_image_files() |
| print(f"Testing {len(files)} barcode images from {BARCODE_DIR}/\n") |
|
|
| decoders = { |
| "pyzbar": decode_pyzbar, |
| "zxing-cpp": decode_zxingcpp, |
| "cv2.barcode": decode_cv2barcode, |
| } |
|
|
| stats = {name: {"chassis": 0, "part_only": 0, "none": 0, "chassis_list": []} |
| for name in decoders} |
| timings = {name: 0.0 for name in decoders} |
|
|
| detail_rows = [] |
|
|
| for fname in files: |
| path = os.path.join(BARCODE_DIR, fname) |
| img = cv2.imread(path) |
| if img is None: |
| continue |
| gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) |
| _, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) |
|
|
| key = os.path.splitext(fname)[0] |
| row = {"file": fname} |
|
|
| for name, decoder_fn in decoders.items(): |
| t0 = time.perf_counter() |
| all_values = decoder_fn(img, gray, thresh) |
| elapsed = time.perf_counter() - t0 |
| timings[name] += elapsed |
|
|
| chassis_vals = {v for v in all_values if is_chassis(v)} |
| part_vals = {v for v in all_values if PART_NUMBER_RE.match(v)} |
|
|
| if chassis_vals: |
| stats[name]["chassis"] += 1 |
| stats[name]["chassis_list"].append((key, chassis_vals)) |
| row[name] = ", ".join(sorted(chassis_vals)) |
| elif part_vals: |
| stats[name]["part_only"] += 1 |
| row[name] = "(part# only)" |
| else: |
| stats[name]["none"] += 1 |
| row[name] = "—" |
|
|
| detail_rows.append(row) |
|
|
| print("=" * 100) |
| print(f"{'File':<14} {'pyzbar':<20} {'zxing-cpp':<20} {'cv2.barcode':<20}") |
| print("-" * 100) |
| for row in detail_rows: |
| pyz = row.get("pyzbar", "—") |
| zxc = row.get("zxing-cpp", "—") |
| cv2b = row.get("cv2.barcode", "—") |
| print(f"{row['file']:<14} {pyz:<20} {zxc:<20} {cv2b:<20}") |
|
|
| total = len(files) |
| print("\n" + "=" * 100) |
| print("SUMMARY") |
| print("=" * 100) |
| print(f"{'Metric':<30} {'pyzbar':>12} {'zxing-cpp':>12} {'cv2.barcode':>12}") |
| print("-" * 70) |
| print(f"{'Chassis decoded':.<30} {stats['pyzbar']['chassis']:>12} {stats['zxing-cpp']['chassis']:>12} {stats['cv2.barcode']['chassis']:>12}") |
| print(f"{'Part# only (filtered out)':.<30} {stats['pyzbar']['part_only']:>12} {stats['zxing-cpp']['part_only']:>12} {stats['cv2.barcode']['part_only']:>12}") |
| print(f"{'Nothing decoded':.<30} {stats['pyzbar']['none']:>12} {stats['zxing-cpp']['none']:>12} {stats['cv2.barcode']['none']:>12}") |
| print(f"{'Total time (s)':.<30} {timings['pyzbar']:>12.2f} {timings['zxing-cpp']:>12.2f} {timings['cv2.barcode']:>12.2f}") |
| print("-" * 70) |
| print(f"{'CHASSIS DECODE RATE':.<30} {stats['pyzbar']['chassis']/total:>11.0%} {stats['zxing-cpp']['chassis']/total:>11.0%} {stats['cv2.barcode']['chassis']/total:>11.0%}") |
| print("=" * 100) |
|
|
| best_name = max(decoders.keys(), key=lambda n: stats[n]["chassis"]) |
| print(f"\n★ Best decoder: {best_name} ({stats[best_name]['chassis']}/{total} chassis barcodes)") |
|
|
| for name in decoders: |
| if stats[name]["chassis_list"]: |
| print(f"\n {name} decoded chassis numbers:") |
| for key, vals in stats[name]["chassis_list"]: |
| print(f" {key}: {', '.join(sorted(vals))}") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|