This commit is contained in:
2025-09-27 22:57:59 +08:00
parent 8a458ff0a4
commit ed26244cdb
12585 changed files with 1914308 additions and 3474 deletions

View File

@@ -0,0 +1,84 @@
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