import cv2 import numpy as np from PIL import Image import zxingcpp import barcodenumber class Barcode: def __init__(self): self._SYM_ALIAS = { 'EAN13': 'ean13', 'EAN8': 'ean8', 'UPCA': 'upc', 'UPC-A': 'upc', } def validate_barcode(self, data: str, sym: str) -> bool: # Empty strings are always invalid if not data: return False # For unknown symbology, try all known formats first if sym.upper() not in self._SYM_ALIAS: if data.isdigit(): for known_format in ['ean13', 'ean8', 'upc']: try: if barcodenumber.check_code(known_format, data): return True except (ValueError, KeyError): continue # If no known format matches, validate basic structure return False # For known formats, validate normally code = self._SYM_ALIAS.get(sym, sym.lower()) try: return barcodenumber.check_code(code, data) except (ValueError, KeyError): return False def scan_and_validate(self, image, show_image: bool = False): # 1) normalize to OpenCV BGR numpy array if isinstance(image, np.ndarray): cv_img = image.copy() else: # assume PIL cv_img = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR) # 2) for zxing we need a PIL, so make one from cv_img pil_for_scan = Image.fromarray(cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)) barcodes = zxingcpp.read_barcodes(pil_for_scan) results = [] for i, barcode in enumerate(barcodes): pos = barcode.position if pos: pts = [pos.top_left, pos.top_right, pos.bottom_right, pos.bottom_left] xs = [p.x for p in pts] ys = [p.y for p in pts] x, y = int(min(xs)), int(min(ys)) w, h = int(max(xs) - x), int(max(ys) - y) else: x, y, w, h = 0, 0, 100, 50 raw = barcode.text sym = str(barcode.format) ok = self.validate_barcode(raw, sym) # Create barcode result with position data barcode_result = { 'id': f'BARCODE_{i+1:03d}', 'type': sym, 'data': raw, 'valid': ok, 'position': { 'x': x, 'y': y, 'width': w, 'height': h, 'top_left': {'x': x, 'y': y}, 'top_right': {'x': x + w, 'y': y}, 'bottom_right': {'x': x + w, 'y': y + h}, 'bottom_left': {'x': x, 'y': y + h} } } results.append(barcode_result) return results def draw_box(self, img, x, y, w, h, sym, raw, ok): color = (0,255,0) if ok else (0,0,255) cv2.rectangle(img, (x,y), (x+w, y+h), color, 2) cv2.putText(img, f"{sym}:{raw}", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2) return img