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()