This commit is contained in:
琉璃月光
2025-10-21 14:11:52 +08:00
parent 349449f2b7
commit df7c0730f5
363 changed files with 5386 additions and 578 deletions

View File

@ -0,0 +1,227 @@
# ===================================================
# final_compare_corner.py
# 同时显示 Canny 物理边缘(红线)和 YOLO 预测左边缘(绿线)
# 基于角点拟合直线,剔除离群点
# ===================================================
import os
import cv2
import numpy as np
from pathlib import Path
from ultralytics import YOLO
# ============================
# 参数
# ============================
TARGET_SIZE = 640
MAX_CORNERS = 200
QUALITY_LEVEL = 0.01
MIN_DISTANCE = 5
DIST_THRESH = 15
ROIS = [(859, 810, 696, 328)] # 全局 ROI可按需修改
OUTPUT_DIR = "./final_output"
# ============================
# Canny 边缘部分(保持原逻辑)
# ============================
def load_global_rois(txt_path):
rois = []
if not os.path.exists(txt_path):
print(f"❌ ROI 文件不存在: {txt_path}")
return rois
with open(txt_path, 'r') as f:
for line in f:
s = line.strip()
if s:
try:
x, y, w, h = map(int, s.split(','))
rois.append((x, y, w, h))
except Exception as e:
print(f"⚠️ 无法解析 ROI 行 '{s}': {e}")
return rois
def fit_line_best(points, distance_thresh=5, max_iter=5):
if len(points) < 2:
return None
points = points.astype(np.float32)
for _ in range(max_iter):
mean = np.mean(points, axis=0)
cov = np.cov(points.T)
eigvals, eigvecs = np.linalg.eig(cov)
idx = np.argmax(eigvals)
direction = eigvecs[:, idx]
vx, vy = direction
x0, y0 = mean
dists = np.abs(vy*(points[:,0]-x0) - vx*(points[:,1]-y0)) / np.hypot(vx, vy)
inliers = points[dists <= distance_thresh]
if len(inliers) == len(points) or len(inliers) < 2:
break
points = inliers
if len(points) < 2:
return None
X = points[:, 0].reshape(-1, 1)
y = points[:, 1]
try:
from sklearn.linear_model import RANSACRegressor
ransac = RANSACRegressor(residual_threshold=distance_thresh)
ransac.fit(X, y)
k = ransac.estimator_.coef_[0]
b = ransac.estimator_.intercept_
vx = 1 / np.sqrt(1 + k**2)
vy = k / np.sqrt(1 + k**2)
x0 = np.mean(points[:,0])
y0 = k*x0 + b
except:
mean = np.mean(points, axis=0)
cov = np.cov(points.T)
eigvals, eigvecs = np.linalg.eig(cov)
idx = np.argmax(eigvals)
direction = eigvecs[:, idx]
vx, vy = direction
x0, y0 = mean
return vx, vy, x0, y0
def extract_canny_overlay(image_path, roi_file, distance_thresh=3):
img = cv2.imread(image_path)
if img is None:
print(f"❌ 无法读取图片: {image_path}")
return None
overlay_img = img.copy()
rois = load_global_rois(roi_file)
if not rois:
print("❌ 没有有效 ROI")
return overlay_img
for idx, (x, y, w, h) in enumerate(rois):
roi = img[y:y+h, x:x+w]
gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 100, 200)
contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
longest_contour = max(contours, key=lambda c: cv2.arcLength(c, closed=False), default=None)
if longest_contour is not None and len(longest_contour) >= 2:
points = longest_contour.reshape(-1, 2)
line = fit_line_best(points, distance_thresh=distance_thresh, max_iter=5)
if line is not None:
vx, vy, x0, y0 = line
cols = w
lefty = int(y0 - vy/vx * x0)
righty = int(y0 + vy/vx * (cols - x0))
pt1 = (x, y + lefty)
pt2 = (x + cols - 1, y + righty)
cv2.line(overlay_img, pt1, pt2, (0, 0, 255), 2) # 红色
print(f"✅ ROI {idx} Canny 边缘拟合完成")
return overlay_img
# ============================
# YOLO 角点 + 拟合直线
# ============================
def select_edge_corners(corners, w, left_ratio=0.2, right_ratio=0.2, y_var_thresh=5):
if corners is None:
return np.zeros((0,2), dtype=np.int32), np.zeros((0,2), dtype=np.int32)
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.astype(np.int32)
pts_sorted = pts[np.argsort(pts[:,1])]
diffs = np.abs(np.diff(pts_sorted[:,1]))
keep_idx = np.where(diffs>y_var_thresh)[0]
if len(keep_idx)==0:
return pts_sorted.astype(np.int32)
selected = [pts_sorted[i] for i in keep_idx]+[pts_sorted[i+1] for i in keep_idx]
selected = np.array(selected)
_, idx = np.unique(selected.reshape(-1,2), axis=0, return_index=True)
selected = selected[np.sort(idx)]
return selected.astype(np.int32)
return filter_by_y_variation(left_candidates), filter_by_y_variation(right_candidates)
def fit_line_with_outlier_removal(pts, dist_thresh=DIST_THRESH):
if pts is None or len(pts)<2:
return None, np.zeros((0,2), dtype=np.int32)
pts = np.array(pts, dtype=np.float64)
x, y = pts[:,0], pts[:,1]
try:
m, b = np.polyfit(y, x, 1)
except:
return None, pts.astype(np.int32)
x_fit = m*y + b
mask = np.abs(x-x_fit)<dist_thresh
if np.sum(mask)<2:
return (m,b), pts.astype(np.int32)
x2, y2 = x[mask], y[mask]
m2, b2 = np.polyfit(y2, x2, 1)
inliers = np.stack([x2,y2],axis=1).astype(np.int32)
return (m2,b2), inliers
def get_yolo_left_edge_lines(image_path, model_path, rois=ROIS, imgsz=TARGET_SIZE):
model = YOLO(model_path)
img = cv2.imread(image_path)
if img is None:
print(f"❌ 无法读取图片: {image_path}")
return []
lines = []
for (x, y, w, h) in rois:
roi_img = img[y:y+h, x:x+w]
resized = cv2.resize(roi_img, (imgsz, imgsz))
results = model(resized, imgsz=imgsz, verbose=False)
result = results[0]
if result.masks is None or len(result.masks.data)==0:
continue
mask = result.masks.data[0].cpu().numpy()
mask_bin = (mask>0.5).astype(np.uint8)
mask_bin = cv2.resize(mask_bin, (w,h), interpolation=cv2.INTER_NEAREST)
# 角点检测
mask_gray = (mask_bin*255).astype(np.uint8)
corners = cv2.goodFeaturesToTrack(mask_gray, maxCorners=MAX_CORNERS,
qualityLevel=QUALITY_LEVEL, minDistance=MIN_DISTANCE)
left_pts, _ = select_edge_corners(corners, w)
line_params, inliers = fit_line_with_outlier_removal(left_pts)
if line_params is None:
continue
m,b = line_params
y1, y2 = 0, h-1
x1 = int(m*y1 + b)
x2 = int(m*y2 + b)
lines.append((x+x1, y+y1, x+x2, y+y2))
return lines
# ============================
# 对比融合
# ============================
def compare_canny_vs_yolo(image_path, canny_roi_file, model_path, output_dir=OUTPUT_DIR):
output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
canny_img = extract_canny_overlay(image_path, canny_roi_file, distance_thresh=6)
if canny_img is None:
return
yolo_lines = get_yolo_left_edge_lines(image_path, model_path)
result_img = canny_img.copy()
for x1,y1,x2,y2 in yolo_lines:
cv2.line(result_img, (x1,y1), (x2,y2), (0,255,0), 2) # 绿色
cv2.circle(result_img, (x1,y1), 4, (255,0,0), -1) # 蓝色起点
output_path = output_dir / f"compare_{Path(image_path).stem}.jpg"
cv2.imwrite(str(output_path), result_img)
print(f"✅ 对比图已保存: {output_path}")
# ============================
# 使用示例
# ============================
if __name__ == "__main__":
IMAGE_PATH = "../test_image/2.jpg"
CANNY_ROI_FILE = "../roi_coordinates/1_rois1.txt"
MODEL_PATH = "best.pt"
compare_canny_vs_yolo(IMAGE_PATH, CANNY_ROI_FILE, MODEL_PATH)

BIN
yemian/yemian_line/best.pt Normal file

Binary file not shown.

View File

@ -0,0 +1,149 @@
import os
import cv2
import numpy as np
from pathlib import Path
from sklearn.linear_model import RANSACRegressor
# ---------------------------
# 读取 ROI 列表 txt
# ---------------------------
def load_rois_from_txt(txt_path):
rois = []
if not os.path.exists(txt_path):
print(f"❌ ROI 文件不存在: {txt_path}")
return rois
with open(txt_path, 'r') as f:
for line in f:
s = line.strip()
if s:
try:
x, y, w, h = map(int, s.split(','))
rois.append((x, y, w, h))
except Exception as e:
print(f"⚠️ 无法解析 ROI 行 '{s}': {e}")
return rois
# ---------------------------
# PCA + RANSAC + 迭代去离群点拟合直线
# ---------------------------
def fit_line_best(points, distance_thresh=5, max_iter=5):
if len(points) < 2:
return None
points = points.astype(np.float32)
for _ in range(max_iter):
mean = np.mean(points, axis=0)
cov = np.cov(points.T)
eigvals, eigvecs = np.linalg.eig(cov)
idx = np.argmax(eigvals)
direction = eigvecs[:, idx]
vx, vy = direction
x0, y0 = mean
dists = np.abs(vy*(points[:,0]-x0) - vx*(points[:,1]-y0)) / np.hypot(vx, vy)
inliers = points[dists <= distance_thresh]
if len(inliers) == len(points) or len(inliers) < 2:
break
points = inliers
if len(points) < 2:
return None
# RANSAC 拟合 y = kx + b
X = points[:, 0].reshape(-1, 1)
y = points[:, 1]
try:
ransac = RANSACRegressor(residual_threshold=distance_thresh)
ransac.fit(X, y)
k = ransac.estimator_.coef_[0]
b = ransac.estimator_.intercept_
vx = 1 / np.sqrt(1 + k**2)
vy = k / np.sqrt(1 + k**2)
x0 = np.mean(points[:,0])
y0 = k*x0 + b
except:
mean = np.mean(points, axis=0)
cov = np.cov(points.T)
eigvals, eigvecs = np.linalg.eig(cov)
idx = np.argmax(eigvals)
direction = eigvecs[:, idx]
vx, vy = direction
x0, y0 = mean
return vx, vy, x0, y0
# ---------------------------
# 封装函数:读取 ROI txt -> 拟合直线 -> 可视化
# ---------------------------
def fit_lines_from_image_txt(image_path, roi_txt_path, distance_thresh=5, draw_overlay=True):
"""
输入:
image_path: 原图路径
roi_txt_path: ROI txt 文件路径,每行 x,y,w,h
distance_thresh: 直线拟合残差阈值
draw_overlay: 是否在原图上叠加拟合直线
输出:
lines: 每个 ROI 的拟合直线 [(vx, vy, x0, y0), ...]
overlay_img: 可视化原图叠加拟合直线
"""
img = cv2.imread(image_path)
if img is None:
print(f"❌ 无法读取图片: {image_path}")
return [], None
rois = load_rois_from_txt(roi_txt_path)
if not rois:
print("❌ 没有有效 ROI")
return [], None
overlay_img = img.copy() if draw_overlay else None
lines = []
for idx, (x, y, w, h) in enumerate(rois):
roi = img[y:y+h, x:x+w]
gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 100, 200)
contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
longest_contour = max(contours, key=lambda c: cv2.arcLength(c, closed=False), default=None)
if longest_contour is not None and len(longest_contour) >= 2:
points = longest_contour.reshape(-1, 2)
line = fit_line_best(points, distance_thresh=distance_thresh, max_iter=5)
if line:
vx, vy, x0, y0 = line
lines.append(line)
if draw_overlay:
cols = gray.shape[1]
lefty = int(y0 - vy/vx * x0)
righty = int(y0 + vy/vx * (cols - x0))
# 绘制在原图
cv2.line(overlay_img, (x, y + lefty), (x + cols - 1, y + righty), (0, 0, 255), 2)
cv2.drawContours(overlay_img, [longest_contour + np.array([x, y])], -1, (0, 255, 0), 1)
else:
lines.append(None)
else:
lines.append(None)
return lines, overlay_img
# ---------------------------
# 使用示例
# ---------------------------
if __name__ == "__main__":
image_path = "../test_image/1.jpg"
roi_txt_path = "../roi_coordinates/1_rois1.txt"
lines, overlay = fit_lines_from_image_txt(image_path, roi_txt_path, distance_thresh=5)
for idx, line in enumerate(lines):
print(f"ROI {idx} 拟合直线: {line}")
if overlay is not None:
cv2.imwrite("overlay_result.jpg", overlay)
print("✅ 原图叠加拟合直线已保存: overlay_result.jpg")

View File

@ -0,0 +1,161 @@
import os
import cv2
import numpy as np
from pathlib import Path
from ultralytics import YOLO
TARGET_SIZE = 640 # 模型输入尺寸
# --------------------
# 全局 ROI 定义
# --------------------
ROIS = [
(859, 810, 696, 328), # (x, y, w, h)
]
# --------------------
# 根据角点分布,选取左右边缘角点
# --------------------
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)
x_min, x_max = 0, w
left_thresh = x_min + int(w * left_ratio)
right_thresh = x_max - int(w * right_ratio)
# 左右候选角点
left_candidates = corners[corners[:, 0] <= left_thresh]
right_candidates = corners[corners[:, 0] >= right_thresh]
# --------------------
# 进一步按 y 变化筛选
# --------------------
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=15):
"""
pts: (N,2) array
dist_thresh: 点到拟合直线的最大允许距离
返回 (m, b) 直线参数, 以及拟合用到的点
"""
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
x2, y2 = x[mask], y[mask]
if len(x2) < 2:
return (m, b), pts # 保底返回
# 二次拟合
m, b = np.polyfit(y2, x2, 1)
return (m, b), np.stack([x2, y2], axis=1)
# --------------------
# 推理 ROI 并可视化 mask + 边缘角点 + 拟合直线
# --------------------
def infer_mask_with_selected_corners(image_path, model_path, output_dir="./output"):
model = YOLO(model_path)
image_path = Path(image_path)
output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
orig_img = cv2.imread(str(image_path))
overlay_img = orig_img.copy()
for idx, (x, y, w, h) in enumerate(ROIS):
roi_img = orig_img[y:y+h, x:x+w]
resized_img = cv2.resize(roi_img, (TARGET_SIZE, TARGET_SIZE))
# 模型推理
results = model(source=resized_img, imgsz=TARGET_SIZE, verbose=False)
result = results[0]
# 可视化 mask
if result.masks is not None and len(result.masks.data) > 0:
mask = result.masks.data[0].cpu().numpy()
mask_bin = (mask > 0.5).astype(np.uint8)
mask_bin = cv2.resize(mask_bin, (w, h), interpolation=cv2.INTER_NEAREST)
# 绿色 mask 覆盖
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, left_inliers = fit_line_with_outlier_removal(left_pts)
right_line, right_inliers = fit_line_with_outlier_removal(right_pts)
# 可视化角点
for cx, cy in left_inliers:
cv2.circle(overlay_img[y:y+h, x:x+w], (int(cx), int(cy)), 5, (0, 0, 255), -1)
for cx, cy in right_inliers:
cv2.circle(overlay_img[y:y+h, x:x+w], (int(cx), int(cy)), 5, (255, 0, 0), -1)
# 可视化拟合直线
if left_line is not None:
m, b = left_line
y1, y2 = 0, h
x1, x2 = int(m * y1 + b), int(m * y2 + b)
cv2.line(overlay_img[y:y+h, x:x+w], (x1, y1), (x2, y2), (0, 0, 255), 3)
if right_line is not None:
m, b = right_line
y1, y2 = 0, h
x1, x2 = int(m * y1 + b), int(m * y2 + b)
cv2.line(overlay_img[y:y+h, x:x+w], (x1, y1), (x2, y2), (255, 0, 0), 3)
# 保存结果
save_path = output_dir / f"mask_edge_corners_{image_path.name}"
cv2.imwrite(str(save_path), overlay_img)
print(f"✅ 保存结果: {save_path}")
return overlay_img
# ===================== 使用示例 =====================
if __name__ == "__main__":
IMAGE_PATH = "../test_image/1.jpg"
MODEL_PATH = "best.pt"
infer_mask_with_selected_corners(IMAGE_PATH, MODEL_PATH)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 851 KiB

View File

@ -0,0 +1,190 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import cv2
import numpy as np
from pathlib import Path
from ultralytics import YOLO
# --------------------
# 参数设置(固定在脚本中)
# --------------------
INPUT_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/test_l" # 图片文件夹
MODEL_PATH = "best.pt" # YOLO 模型
OUTPUT_DIR = "./output" # 保存结果
TARGET_SIZE = 640 # YOLO 输入尺寸
DIST_THRESH = 15 # 剔除离群点阈值
MAX_CORNERS = 200 # goodFeaturesToTrack 最大角点数
QUALITY_LEVEL = 0.01 # goodFeaturesToTrack qualityLevel
MIN_DISTANCE = 5 # goodFeaturesToTrack minDistance
# 全局 ROI 定义
ROIS = [
(859, 810, 696, 328), # (x, y, w, h)
]
# --------------------
# 左右边缘角点筛选
# --------------------
def select_edge_corners(corners, w, left_ratio=0.2, right_ratio=0.2, y_var_thresh=5):
if corners is None:
return np.zeros((0,2), dtype=np.int32), np.zeros((0,2), dtype=np.int32)
corners = np.int32(corners).reshape(-1, 2)
x_min, x_max = 0, w
left_thresh = x_min + int(w * left_ratio)
right_thresh = x_max - 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.astype(np.int32)
pts_sorted = pts[np.argsort(pts[:,1])]
diffs = np.abs(np.diff(pts_sorted[:,1]))
keep_idx = np.where(diffs > y_var_thresh)[0]
if len(keep_idx) == 0:
return pts_sorted.astype(np.int32)
selected = [pts_sorted[i] for i in keep_idx] + [pts_sorted[i+1] for i in keep_idx]
selected = np.array(selected)
selected = selected[np.argsort(selected[:,1])]
_, idx = np.unique(selected.reshape(-1,2), axis=0, return_index=True)
selected = selected[np.sort(idx)]
return selected.astype(np.int32)
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=DIST_THRESH):
if pts is None or len(pts) < 2:
return None, np.zeros((0,2), dtype=np.int32)
pts = np.array(pts, dtype=np.float64)
x = pts[:,0]
y = pts[:,1]
try:
m, b = np.polyfit(y, x, 1)
except:
return None, np.zeros((0,2), dtype=np.int32)
x_fit = m*y + b
dists = np.abs(x - x_fit)
mask = dists < dist_thresh
if np.sum(mask) < 2:
return (m,b), pts.astype(np.int32)
x2, y2 = x[mask], y[mask]
try:
m2, b2 = np.polyfit(y2, x2, 1)
except:
return (m,b), np.stack([x2,y2],axis=1).astype(np.int32)
inliers = np.stack([x2,y2],axis=1).astype(np.int32)
return (m2,b2), inliers
# --------------------
# 单张图 ROI 处理
# --------------------
def process_roi_on_image(orig_img, roi):
rx, ry, rw, rh = roi
h_img, w_img = orig_img.shape[:2]
rx = max(0, rx); ry = max(0, ry)
rw = min(rw, w_img - rx); rh = min(rh, h_img - ry)
roi_img = orig_img[ry:ry+rh, rx:rx+rw].copy()
if roi_img.size == 0:
return None
resized = cv2.resize(roi_img, (TARGET_SIZE, TARGET_SIZE))
results = MODEL(resized, imgsz=TARGET_SIZE, verbose=False)
result = results[0]
overlay_roi = roi_img.copy()
if result.masks is None or len(result.masks.data)==0:
return overlay_roi
mask = result.masks.data[0].cpu().numpy()
mask_bin = (mask>0.5).astype(np.uint8)
mask_bin = cv2.resize(mask_bin,(rw,rh), interpolation=cv2.INTER_NEAREST)
# mask 半透明覆盖
color_mask = np.zeros_like(overlay_roi, dtype=np.uint8)
color_mask[mask_bin==1] = (0,255,0)
overlay_roi = cv2.addWeighted(overlay_roi,0.7,color_mask,0.3,0)
# 角点检测
mask_gray = (mask_bin*255).astype(np.uint8)
corners = cv2.goodFeaturesToTrack(mask_gray, maxCorners=MAX_CORNERS,
qualityLevel=QUALITY_LEVEL, minDistance=MIN_DISTANCE)
left_pts, right_pts = select_edge_corners(corners, rw)
left_line, left_inliers = fit_line_with_outlier_removal(left_pts)
right_line, right_inliers = fit_line_with_outlier_removal(right_pts)
# 可视化 inliers
for (cx,cy) in left_inliers:
cv2.circle(overlay_roi,(int(cx),int(cy)),4,(0,0,255),-1)
for (cx,cy) in right_inliers:
cv2.circle(overlay_roi,(int(cx),int(cy)),4,(255,0,0),-1)
# 拟合直线
if left_line is not None:
m,b = left_line
y1,y2 = 0, rh-1
x1 = int(m*y1+b); x2 = int(m*y2+b)
cv2.line(overlay_roi,(x1,y1),(x2,y2),(0,0,200),3)
if right_line is not None:
m,b = right_line
y1,y2 = 0, rh-1
x1 = int(m*y1+b); x2 = int(m*y2+b)
cv2.line(overlay_roi,(x1,y1),(x2,y2),(200,0,0),3)
return overlay_roi
# --------------------
# 批量推理文件夹
# --------------------
def infer_folder_images():
input_dir = Path(INPUT_DIR)
output_dir = Path(OUTPUT_DIR)
output_dir.mkdir(parents=True, exist_ok=True)
exts = ('*.jpg','*.jpeg','*.png','*.bmp','*.tif','*.tiff')
files = []
for e in exts:
files.extend(sorted(input_dir.glob(e)))
if len(files)==0:
print("未找到图片文件")
return
print(f"找到 {len(files)} 张图片,开始推理...")
for img_path in files:
print("-> 处理:", img_path.name)
orig_img = cv2.imread(str(img_path))
if orig_img is None:
print(" 无法读取,跳过")
continue
out_img = orig_img.copy()
for roi in ROIS:
overlay_roi = process_roi_on_image(orig_img, roi)
if overlay_roi is not None:
rx,ry,rw,rh = roi
h,w = overlay_roi.shape[:2]
out_img[ry:ry+h, rx:rx+w] = overlay_roi
save_path = output_dir / f"mask_edge_corners_{img_path.name}"
cv2.imwrite(str(save_path), out_img)
print(" 已保存 ->", save_path.name)
print("批量推理完成,结果保存在:", output_dir)
# --------------------
# 主程序
# --------------------
if __name__ == "__main__":
MODEL = YOLO(MODEL_PATH)
infer_folder_images()

View File

@ -0,0 +1,130 @@
import os
import cv2
import numpy as np
from pathlib import Path
from ultralytics import YOLO
TARGET_SIZE = 640 # 模型输入尺寸
# --------------------
# 全局 ROI 定义
# --------------------
ROIS = [
(859,810,696,328), # (x, y, w, h)
]
# --------------------
# 剔除相邻 x 差距过大的离散点
# --------------------
def filter_outliers_by_x(pts, x_thresh=30):
if len(pts) < 2:
return pts
pts_sorted = pts[np.argsort(pts[:, 1])] # 按 y 排序
clean_pts = [pts_sorted[0]]
for i in range(1, len(pts_sorted)):
if abs(pts_sorted[i, 0] - pts_sorted[i-1, 0]) < x_thresh:
clean_pts.append(pts_sorted[i])
return np.array(clean_pts, dtype=np.int32)
# --------------------
# 根据角点分布,选取左右边缘角点
# --------------------
def select_edge_corners(corners, w, left_ratio=0.2, right_ratio=0.2, y_var_thresh=5, x_var_thresh=30):
if corners is None:
return [], []
corners = np.int32(corners).reshape(-1, 2)
x_min, x_max = 0, w
left_thresh = x_min + int(w * left_ratio)
right_thresh = x_max - int(w * right_ratio)
# 左右候选角点
left_candidates = corners[corners[:, 0] <= left_thresh]
right_candidates = corners[corners[:, 0] >= right_thresh]
# --------------------
# 进一步按 y 变化筛选
# --------------------
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)
# --------------------
# 再剔除相邻 x 值差距过大的离散点
# --------------------
left_final = filter_outliers_by_x(left_final, x_var_thresh)
right_final = filter_outliers_by_x(right_final, x_var_thresh)
return left_final, right_final
# --------------------
# 推理 ROI 并可视化 mask + 边缘角点
# --------------------
def infer_mask_with_selected_corners(image_path, model_path, output_dir="./output"):
model = YOLO(model_path)
image_path = Path(image_path)
output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
orig_img = cv2.imread(str(image_path))
overlay_img = orig_img.copy()
for idx, (x, y, w, h) in enumerate(ROIS):
roi_img = orig_img[y:y+h, x:x+w]
resized_img = cv2.resize(roi_img, (TARGET_SIZE, TARGET_SIZE))
# 模型推理
results = model(source=resized_img, imgsz=TARGET_SIZE, verbose=False)
result = results[0]
# 可视化 mask
if result.masks is not None and len(result.masks.data) > 0:
mask = result.masks.data[0].cpu().numpy()
mask_bin = (mask > 0.5).astype(np.uint8)
mask_bin = cv2.resize(mask_bin, (w, h), interpolation=cv2.INTER_NEAREST)
# 绿色 mask 覆盖
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)
# 可视化
for cx, cy in left_pts:
cv2.circle(overlay_img[y:y+h, x:x+w], (cx, cy), 6, (0, 0, 255), -1) # 左边红色
for cx, cy in right_pts:
cv2.circle(overlay_img[y:y+h, x:x+w], (cx, cy), 6, (255, 0, 0), -1) # 右边蓝色
# 保存结果
save_path = output_dir / f"mask_edge_corners_{image_path.name}"
cv2.imwrite(str(save_path), overlay_img)
print(f"✅ 保存结果: {save_path}")
return overlay_img
# ===================== 使用示例 =====================
if __name__ == "__main__":
IMAGE_PATH = "../test_image/1.jpg"
MODEL_PATH = "best.pt"
infer_mask_with_selected_corners(IMAGE_PATH, MODEL_PATH)