Files
PartsInquiry/backend/txm/app/pyzbar_engine.py
2025-10-08 19:15:20 +08:00

99 lines
3.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from typing import Dict, List, Tuple
import cv2
import numpy as np
import logging
# 尝试延迟引入 pyzbar在未安装 zbar 运行库的环境下,保证服务可启动,
# 后续仅使用自研 EAN-13 解码或返回空结果。
try:
from pyzbar.pyzbar import decode as _pyzbar_decode, ZBarSymbol as _ZBarSymbol
_PYZBAR_AVAILABLE = True
except Exception as _e: # ImportError 或 zbar 动态库缺失
_PYZBAR_AVAILABLE = False
_PYZBAR_IMPORT_ERR = _e
_pyzbar_decode = None
_ZBarSymbol = None
def _prepare_images(gray: np.ndarray, try_invert: bool, rotations: List[int]) -> List[Tuple[np.ndarray, int, bool]]:
# 返回 (图像, 旋转角度, 是否反色)
images: List[Tuple[np.ndarray, int, bool]] = []
for ang in rotations:
if ang % 360 == 0:
rot = gray
elif ang % 360 == 90:
rot = cv2.rotate(gray, cv2.ROTATE_90_CLOCKWISE)
elif ang % 360 == 180:
rot = cv2.rotate(gray, cv2.ROTATE_180)
elif ang % 360 == 270:
rot = cv2.rotate(gray, cv2.ROTATE_90_COUNTERCLOCKWISE)
else:
# 任意角度插值旋转
h, w = gray.shape[:2]
M = cv2.getRotationMatrix2D((w / 2, h / 2), ang, 1.0)
rot = cv2.warpAffine(gray, M, (w, h), flags=cv2.INTER_LINEAR)
images.append((rot, ang, False))
if try_invert:
images.append((cv2.bitwise_not(rot), ang, True))
return images
def _collect_supported_symbols() -> List["_ZBarSymbol"]:
names = [
"EAN13",
"EAN8",
"UPCA",
"UPCE",
"CODE128",
"CODE39",
"QRCODE",
# 兼容不同版本:有的叫 ITF有的叫 I25
"ITF",
"I25",
]
symbols: List["_ZBarSymbol"] = []
if not _PYZBAR_AVAILABLE:
return symbols
for n in names:
if hasattr(_ZBarSymbol, n):
symbols.append(getattr(_ZBarSymbol, n))
# 若列表为空,退回由 zbar 自行决定的默认集合
return symbols
def decode_with_pyzbar(image_bgr: np.ndarray, try_invert: bool, rotations: List[int]) -> List[Dict[str, str]]:
logger = logging.getLogger("pyzbar_engine")
if not _PYZBAR_AVAILABLE or _pyzbar_decode is None:
# 缺少 pyzbar/zbar返回空集合并提示
logger.warning("pyzbar 或 zbar 未就绪,已跳过 pyzbar 解码: %s", str(locals().get('_PYZBAR_IMPORT_ERR', ''))) # type: ignore
return []
# 输入 BGR转灰度
gray = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)
results: List[Dict[str, str]] = []
symbols = _collect_supported_symbols()
logger.debug("调用 pyzbar: symbols=%d, rotations=%s, try_invert=%s", len(symbols) if symbols else 0, rotations, try_invert)
for img, ang, inv in _prepare_images(gray, try_invert=try_invert, rotations=rotations):
# pyzbar 要求 8bit 图像
decoded = _pyzbar_decode(img, symbols=symbols or None)
for obj in decoded:
data = obj.data.decode("utf-8", errors="ignore")
typ = obj.type
results.append({"type": typ, "code": data})
if results:
# 若当前设置已识别出内容,继续下一个旋转场景以收集更多,但不必反复
# 这里不提前返回,以便尽量收集多结果
pass
# 去重(按 type+code
uniq = []
seen = set()
for r in results:
key = (r["type"], r["code"]) if isinstance(r, dict) else (None, None)
if key not in seen:
seen.add(key)
uniq.append(r)
logger.debug("pyzbar 返回结果数: %d", len(uniq))
return uniq