Files
zjsh_yolov11/angle_base_seg/angle_main.py

160 lines
5.5 KiB
Python
Raw Normal View History

2025-08-13 12:53:33 +08:00
from ultralytics import YOLO
import cv2
import numpy as np
import os
# ------------------ 模型与路径配置 ------------------
2025-08-14 18:24:45 +08:00
MODEL_PATH = '../ultralytics_yolo11-main/runs/train/exp4/weights/best.pt'
OUTPUT_DIR = '../test_image'
2025-08-13 12:53:33 +08:00
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=1280, 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__':
# ✅ 设置输入图像路径
2025-08-14 18:24:45 +08:00
image_path = '/test_image/1.png' # ← 修改为你自己的图片路径
2025-08-13 12:53:33 +08:00
# ✅ 模式选择:
# mode='show': 保存可视化图像
# mode='silent': 只返回角度
mode = 'silent' # 或 'silent'
print(f"🔍 正在处理图像: {image_path}")
angle = detect_jaw_angle(image_path, mode=mode)
if angle is not None:
print(f"🎉 检测到的夹具开合角度: {angle}°")
else:
print("❌ 未能检测到足够的夹具")