Files
zjsh_yolov11/yemian/yemian_line/seg_bushu/danmu_bushu.py
琉璃月光 8b263167f8 更新
2025-12-11 08:37:09 +08:00

244 lines
8.0 KiB
Python
Raw 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.

import cv2
import numpy as np
from pathlib import Path
from rknnlite.api import RKNNLite
# --------------------
# 配置参数
# --------------------
IMAGE_PATH = "./11.jpg"
MODEL_PATH = "segr.rknn"
OUTPUT_PATH = "./single_result.jpg"
TARGET_SIZE = 640
# 像素到实际尺寸换算比例
def calculate_pixel_to_real_ratio(real_length_mm, pixel_length):
if pixel_length == 0:
raise ValueError("像素长度不能为0")
return real_length_mm / pixel_length
PIXEL_TO_REAL_RATIO = 1.0
real_length_mm = 100 # 实际长度(毫米)
pixel_length = 200 # 对应像素长度
try:
PIXEL_TO_REAL_RATIO = calculate_pixel_to_real_ratio(real_length_mm, pixel_length)
print(f"换算比例已设定: {PIXEL_TO_REAL_RATIO:.4f} mm/像素")
except ValueError as e:
print(e)
# 全局 ROI 定义:(x, y, w, h)
ROIS = [
(859, 810, 696, 328),
]
# --------------------
# RKNN 单例管理
# --------------------
_rknn_instance = None
def init_rknn(model_path):
global _rknn_instance
if _rknn_instance is not None:
return _rknn_instance
_rknn_instance = RKNNLite(verbose=False)
ret = _rknn_instance.load_rknn(model_path)
if ret != 0:
print(f"[ERROR] 加载 RKNN 模型失败: {ret}")
_rknn_instance = None
return None
ret = _rknn_instance.init_runtime(core_mask=RKNNLite.NPU_CORE_1)
if ret != 0:
print(f"[ERROR] 初始化 NPU 失败: {ret}")
_rknn_instance.release()
_rknn_instance = None
return None
print("[✅] RKNN 分割模型加载成功")
return _rknn_instance
def release_rknn():
global _rknn_instance
if _rknn_instance:
_rknn_instance.release()
_rknn_instance = None
print("[INFO] RKNN 模型已释放")
# --------------------
# 工具函数letterbox resize
# --------------------
def letterbox_resize(image, size, bg_color=114):
target_w, target_h = size
h, w = image.shape[:2]
scale = min(target_w / w, target_h / h)
new_w, new_h = int(w * scale), int(h * scale)
resized = cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_LINEAR)
canvas = np.full((target_h, target_w, 3), bg_color, dtype=np.uint8)
dx = (target_w - new_w) // 2
dy = (target_h - new_h) // 2
canvas[dy:dy+new_h, dx:dx+new_w] = resized
return canvas, scale, dx, dy
# --------------------
# 辅助函数
# --------------------
def select_edge_corners(corners, w, left_ratio=0.2, right_ratio=0.2, y_var_thresh=5):
if corners is None:
return [], []
corners = np.int32(corners).reshape(-1, 2)
left_thresh = int(w * left_ratio)
right_thresh = w - int(w * right_ratio)
left_candidates = corners[corners[:, 0] <= left_thresh]
right_candidates = corners[corners[:, 0] >= right_thresh]
def filter_by_y_variation(pts):
if len(pts) < 2:
return pts
pts_sorted = pts[np.argsort(pts[:, 1])]
diffs = np.abs(np.diff(pts_sorted[:, 1]))
keep_idx = np.where(diffs > y_var_thresh)[0]
selected = [pts_sorted[i] for i in keep_idx] + [pts_sorted[i + 1] for i in keep_idx]
return np.array(selected) if len(selected) > 0 else pts_sorted
left_final = filter_by_y_variation(left_candidates)
right_final = filter_by_y_variation(right_candidates)
return left_final, right_final
def fit_line_with_outlier_removal(pts, dist_thresh=10):
if pts is None or len(pts) < 2:
return None, pts
pts = np.array(pts)
x, y = pts[:, 0], pts[:, 1]
m, b = np.polyfit(y, x, 1) # x = m*y + b
x_fit = m * y + b
dists = np.abs(x - x_fit)
mask = dists < dist_thresh
if mask.sum() < 2:
return (m, b), pts
m, b = np.polyfit(y[mask], x[mask], 1)
inliers = np.stack([x[mask], y[mask]], axis=1)
return (m, b), inliers
# --------------------
# RKNN 推理生成 mask增加去除小区域
# --------------------
def get_mask_from_rknn(rknn_model, roi_img, target_size=640, min_area=100):
h_orig, w_orig = roi_img.shape[:2]
preprocessed, scale, dx, dy = letterbox_resize(roi_img, (target_size, target_size))
infer_input = preprocessed[np.newaxis, :, :, ::-1].astype(np.float32)
try:
outputs = rknn_model.inference(inputs=[infer_input])
except Exception as e:
print(f"[ERROR] RKNN 推理异常: {e}")
return None
try:
proto = outputs[12][0] # (32,160,160)
mask_proto = np.mean(proto, axis=0)
mask_proto = 1 / (1 + np.exp(-mask_proto))
mask_lb = cv2.resize(mask_proto, (target_size, target_size), interpolation=cv2.INTER_LINEAR)
scale = min(target_size / w_orig, target_size / h_orig)
new_w, new_h = int(w_orig*scale), int(h_orig*scale)
pad_x = (target_size - new_w)//2
pad_y = (target_size - new_h)//2
mask_cropped = mask_lb[pad_y:pad_y+new_h, pad_x:pad_x+new_w]
mask_resized = cv2.resize(mask_cropped, (w_orig, h_orig), interpolation=cv2.INTER_LINEAR)
mask_bin = (mask_resized > 0.5).astype(np.uint8)
# 去除小区域
num_labels, labels, stats, _ = cv2.connectedComponentsWithStats(mask_bin, connectivity=8)
mask_clean = np.zeros_like(mask_bin)
for i in range(1, num_labels):
area = stats[i, cv2.CC_STAT_AREA]
if area >= min_area:
mask_clean[labels == i] = 1
return mask_clean
except Exception as e:
print(f"[ERROR] 生成 mask 失败: {e}")
return None
# --------------------
# 主函数
# --------------------
def infer_single_image(image_path, model_path, output_path):
orig_img = cv2.imread(str(image_path))
if orig_img is None:
print(f"无法读取图像: {image_path}")
return None
overlay_img = orig_img.copy()
x_diff_pixel = None
rknn = init_rknn(model_path)
if rknn is None:
print("RKNN 初始化失败")
return None
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
for idx, (x, y, w, h) in enumerate(ROIS):
roi_img = orig_img[y:y+h, x:x+w]
mask_bin = get_mask_from_rknn(rknn, roi_img, target_size=TARGET_SIZE, min_area=30000)
if mask_bin is None or mask_bin.sum() == 0:
print("未检测到有效 mask")
continue
color_mask = np.zeros_like(roi_img, dtype=np.uint8)
color_mask[mask_bin == 1] = (0, 255, 0)
overlay_img[y:y+h, x:x+w] = cv2.addWeighted(roi_img, 0.7, color_mask, 0.3, 0)
mask_gray = (mask_bin * 255).astype(np.uint8)
corners = cv2.goodFeaturesToTrack(mask_gray, maxCorners=200, qualityLevel=0.01, minDistance=5)
left_pts, right_pts = select_edge_corners(corners, w)
left_line, _ = fit_line_with_outlier_removal(left_pts)
right_line, _ = fit_line_with_outlier_removal(right_pts)
if left_line and right_line:
y_ref = h * 0.6
m1, b1 = left_line
m2, b2 = right_line
x1 = m1 * y_ref + b1
x2 = m2 * y_ref + b2
x_diff_pixel = abs(x2 - x1)
cv2.line(overlay_img[y:y+h, x:x+w], (0, int(y_ref)), (w, int(y_ref)), (0, 255, 255), 2)
cv2.putText(overlay_img[y:y+h, x:x+w],
f"x_diff={x_diff_pixel:.1f}px",
(10, 30), cv2.FONT_HERSHEY_SIMPLEX,
1, (0, 255, 255), 2)
for (m, b), color in [(left_line, (0, 0, 255)), (right_line, (255, 0, 0))]:
y1, y2 = 0, h
x1_line, x2_line = int(m * y1 + b), int(m * y2 + b)
cv2.line(overlay_img[y:y+h, x:x+w], (x1_line, y1), (x2_line, y2), color, 3)
cv2.imwrite(output_path, overlay_img)
print(f"结果已保存至: {output_path}")
if x_diff_pixel is not None:
x_diff_real = x_diff_pixel * PIXEL_TO_REAL_RATIO
print(f"x差值像素 = {x_diff_pixel:.2f} px")
print(f"x差值实际 = {x_diff_real:.2f} mm")
else:
print("未能计算 x 差值")
release_rknn()
return x_diff_pixel
# =====================
# 运行入口
# =====================
if __name__ == "__main__":
infer_single_image(IMAGE_PATH, MODEL_PATH, OUTPUT_PATH)