2
This commit is contained in:
84
backend/txm/app/image_processing.py
Normal file
84
backend/txm/app/image_processing.py
Normal 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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user