Initial commit: 包装审核 POC、Docker 与前后端
Made-with: Cursor
This commit is contained in:
103
backend/app/barcode_detector.py
Normal file
103
backend/app/barcode_detector.py
Normal file
@@ -0,0 +1,103 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user