Files
zjsh_yolov11/angle_base_seg/angle_main.py
2025-09-01 14:14:18 +08:00

160 lines
5.5 KiB
Python
Raw Permalink 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.

from ultralytics import YOLO
import cv2
import numpy as np
import os
# ------------------ 模型与路径配置 ------------------
MODEL_PATH = '../ultralytics_yolo11-main/runs/train/seg_j/exp/weights/best.pt'
OUTPUT_DIR = '../test_image'
os.makedirs(OUTPUT_DIR, exist_ok=True)
# 加载模型(全局一次)
model = YOLO(MODEL_PATH)
model.to('cuda') # 如无 GPU可改为 'cpu'
def detect_jaw_angle(image_path, mode='show'):
"""
检测图像中两个夹具的开合角度
参数:
image_path (str): 输入图像路径
mode (str): 'show' -> 保存可视化结果;'silent' -> 只返回角度
返回:
float: 夹具开合角度 (0 ~ 90°)
"""
img = cv2.imread(image_path)
if img is None:
raise FileNotFoundError(f"无法读取图像: {image_path}")
h, w = img.shape[:2]
filename = os.path.basename(image_path)
name_only = os.path.splitext(filename)[0]
# 创建掩码并检测
composite_mask = np.zeros((h, w), dtype=np.uint8)
results = model(image_path, imgsz=640, conf=0.5)
jaws = []
for r in results:
if r.masks is not None:
masks = r.masks.data.cpu().numpy()
boxes = r.boxes.xyxy.cpu().numpy()
for i, mask in enumerate(masks):
x1, y1, x2, y2 = map(int, boxes[i])
x1, y1 = max(0, x1), max(0, y1)
x2, y2 = min(w, x2), min(h, y2)
obj_mask = np.zeros((h, w), dtype=np.uint8)
mask_resized = cv2.resize(mask, (w, h))
obj_mask[y1:y2, x1:x2] = (mask_resized[y1:y2, x1:x2] * 255).astype(np.uint8)
contours, _ = cv2.findContours(obj_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
if len(contours) == 0:
continue
largest_contour = max(contours, key=cv2.contourArea)
area = cv2.contourArea(largest_contour)
if area < 100:
continue
rect = cv2.minAreaRect(largest_contour)
jaws.append({
'contour': largest_contour,
'rect': rect,
'area': area
})
composite_mask = np.maximum(composite_mask, obj_mask)
# 必须检测到至少两个夹具
if len(jaws) < 2:
print(f"检测到的夹具少于2个{len(jaws)}个)")
if mode == 'show':
cv2.imwrite(os.path.join(OUTPUT_DIR, f'mask_{name_only}.png'), composite_mask)
return None
# 取面积最大的两个
jaws.sort(key=lambda x: x['area'], reverse=True)
jaw1, jaw2 = jaws[0], jaws[1]
# ------------------ 计算角度 ------------------
def get_long_edge_vector(rect):
center, (w, h), angle = rect
rad = np.radians(angle + (0 if w >= h else 90))
return np.array([np.cos(rad), np.sin(rad)])
def get_center(contour):
M = cv2.moments(contour)
return np.array([M['m10']/M['m00'], M['m01']/M['m00']]) if M['m00'] != 0 else np.array([0,0])
dir1 = get_long_edge_vector(jaw1['rect'])
dir2 = get_long_edge_vector(jaw2['rect'])
# 校正方向:指向夹具中心
center1 = get_center(jaw1['contour'])
center2 = get_center(jaw2['contour'])
fixture_center = (center1 + center2) / 2
to_center1 = fixture_center - center1
to_center2 = fixture_center - center2
if np.linalg.norm(to_center1) > 1e-6 and np.dot(dir1, to_center1) < 0:
dir1 = -dir1
if np.linalg.norm(to_center2) > 1e-6 and np.dot(dir2, to_center2) < 0:
dir2 = -dir2
# 计算最小内角
cos_angle = np.clip(np.dot(dir1, dir2), -1.0, 1.0)
angle = np.degrees(np.arccos(cos_angle))
opening_angle = min(angle, 180 - angle)
# ------------------ 可视化(仅 mode='show'------------------
if mode == 'show':
vis_img = np.stack([composite_mask]*3, axis=-1)
vis_img[composite_mask > 0] = [255, 255, 255]
# 绘制矩形框
box1 = np.int32(cv2.boxPoints(jaw1['rect']))
box2 = np.int32(cv2.boxPoints(jaw2['rect']))
cv2.drawContours(vis_img, [box1], 0, (0, 0, 255), 2)
cv2.drawContours(vis_img, [box2], 0, (255, 0, 0), 2)
# 绘制方向箭头
scale = 60
c1 = tuple(np.int32(center1))
c2 = tuple(np.int32(center2))
end1 = tuple(np.int32(center1 + scale * dir1))
end2 = tuple(np.int32(center2 + scale * dir2))
cv2.arrowedLine(vis_img, c1, end1, (0, 255, 0), 2, tipLength=0.3)
cv2.arrowedLine(vis_img, c2, end2, (0, 255, 0), 2, tipLength=0.3)
# 标注角度
cv2.putText(vis_img, f"Angle: {opening_angle:.2f}°", (20, 50),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
# 保存
save_path = os.path.join(OUTPUT_DIR, f'mask_with_angle_{name_only}.png')
cv2.imwrite(save_path, vis_img)
print(f"✅ 可视化结果已保存: {save_path}")
return round(opening_angle, 2)
# ------------------ 主函数 ------------------
if __name__ == '__main__':
# ✅ 设置输入图像路
image_path = r"/home/hx/yolo/output_masks/2.jpg" # ← 修改为你自己的图片路径
# ✅ 模式选择:
# mode='show': 保存可视化图像
# mode='silent': 只返回角度
mode = 'show' # 或 'silent'
print(f"🔍 正在处理图像: {image_path}")
angle = detect_jaw_angle(image_path, mode=mode)
if angle is not None:
print(f"🎉 检测到的夹具开合角度: {angle}°")
else:
print("❌ 未能检测到足够的夹具")