"""Detect and decode barcodes / QR codes from an image file using zxing-cpp.""" from __future__ import annotations import logging from dataclasses import dataclass, field from pathlib import Path logger = logging.getLogger(__name__) # Human-readable names for zxing BarcodeFormat values _FORMAT_NAMES: dict[str, str] = { "QRCode": "二维码 (QR Code)", "DataMatrix": "DataMatrix", "Aztec": "Aztec", "PDF417": "PDF417", "MicroQRCode": "微型二维码", "RMQRCode": "R型QR码", "EAN8": "EAN-8", "EAN13": "EAN-13", "UPCE": "UPC-E", "UPCA": "UPC-A", "Code39": "Code 39", "Code93": "Code 93", "Code128": "Code 128", "ITF": "ITF(交叉二五码)", "Codabar": "Codabar", "DataBar": "DataBar", "DataBarExpanded": "DataBar Expanded", "MaxiCode": "MaxiCode", "DXFilmEdge": "DXFilmEdge", "LinearCodes": "一维码", "MatrixCodes": "矩阵码", } @dataclass class BarcodeResult: format: str # zxing format string, e.g. "EAN13" format_label: str # Chinese-friendly label text: str # decoded text / number # bounding box in image pixels (top-left origin) x0: int y0: int x1: int y1: int valid: bool # zxing isValid def detect_barcodes(image_path: Path) -> list[BarcodeResult]: """Scan *image_path* for all barcodes and QR codes. Returns a list of :class:`BarcodeResult`, one entry per detected code. Returns an empty list when nothing is found or on error. """ try: import zxingcpp except ImportError: logger.warning("zxing-cpp not installed; barcode detection skipped") return [] try: from PIL import Image img = Image.open(image_path).convert("RGB") except Exception as exc: logger.warning("barcode_detector: cannot open image %s: %s", image_path, exc) return [] try: results = zxingcpp.read_barcodes(img) except Exception as exc: logger.warning("barcode_detector: zxing scan failed: %s", exc) return [] output: list[BarcodeResult] = [] for r in results: fmt_str = str(r.format).replace("BarcodeFormat.", "") label = _FORMAT_NAMES.get(fmt_str, fmt_str) # zxing-cpp position: r.position is a quadrilateral with four points try: pts = r.position xs = [pts.top_left.x, pts.top_right.x, pts.bottom_right.x, pts.bottom_left.x] ys = [pts.top_left.y, pts.top_right.y, pts.bottom_right.y, pts.bottom_left.y] x0, y0, x1, y1 = int(min(xs)), int(min(ys)), int(max(xs)), int(max(ys)) except Exception: x0 = y0 = x1 = y1 = 0 output.append(BarcodeResult( format=fmt_str, format_label=label, text=r.text, x0=x0, y0=y0, x1=x1, y1=y1, valid=r.valid, )) logger.info( "barcode_detector: found %s text=%r bbox=(%d,%d,%d,%d)", fmt_str, r.text, x0, y0, x1, y1, ) if not output: logger.info("barcode_detector: no barcode/QR found in %s", image_path.name) return output