from typing import List, Optional, Tuple import cv2 import numpy as np def resize_keep_aspect(image: np.ndarray, target_width: int) -> np.ndarray: if target_width <= 0: return image h, w = image.shape[:2] if w == target_width: return image scale = target_width / float(w) new_size = (target_width, int(round(h * scale))) return cv2.resize(image, new_size, interpolation=cv2.INTER_AREA) def enhance_barcode_stripes(gray: np.ndarray, gaussian_ksize: int, sobel_ksize: int) -> np.ndarray: g = gray if gaussian_ksize and gaussian_ksize > 1: g = cv2.GaussianBlur(g, (gaussian_ksize, gaussian_ksize), 0) # 使用水平 Sobel 捕捉垂直边缘(条纹) grad_x = cv2.Sobel(g, cv2.CV_32F, 1, 0, ksize=sobel_ksize) grad_x = cv2.convertScaleAbs(grad_x) return grad_x def binarize_image(img: np.ndarray, method: str = "otsu") -> np.ndarray: if method == "adaptive": return cv2.adaptiveThreshold( img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 31, 10 ) # 默认 OTSU _, th = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) return th def morph_close(img: np.ndarray, kernel_size: int) -> np.ndarray: if kernel_size <= 1: return img kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_size, kernel_size)) closed = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) return closed def find_barcode_roi(binary: np.ndarray, original_shape: Tuple[int, int], min_area_ratio: float, min_wh_ratio: float) -> Optional[Tuple[np.ndarray, Tuple[int, int, int, int]]]: h, w = original_shape contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) image_area = h * w candidates: List[Tuple[float, Tuple[int, int, int, int]]] = [] for cnt in contours: x, y, cw, ch = cv2.boundingRect(cnt) area = cw * ch if area / image_area < min_area_ratio: continue wh_ratio = cw / float(ch + 1e-6) if wh_ratio < min_wh_ratio: continue candidates.append((area, (x, y, cw, ch))) if not candidates: return None candidates.sort(key=lambda t: t[0], reverse=True) _, bbox = candidates[0] x, y, cw, ch = bbox roi = binary[y : y + ch, x : x + cw] return roi, bbox def warp_barcode_region(gray: np.ndarray, bbox: Tuple[int, int, int, int], target_height: int, crop_bottom_ratio: float = 0.0) -> np.ndarray: x, y, cw, ch = bbox crop = gray[y : y + ch, x : x + cw] # 去除底部数字区域干扰 if 0 < crop_bottom_ratio < 1: hb = int(round(ch * (1.0 - crop_bottom_ratio))) hb = max(10, min(ch, hb)) crop = crop[:hb, :] if target_height <= 0: return crop scale = target_height / float(ch) target_width = int(round(cw * scale)) warped = cv2.resize(crop, (target_width, target_height), interpolation=cv2.INTER_CUBIC) return warped