Files
zjsh_yolov11/test.py
2025-08-13 12:53:33 +08:00

192 lines
6.8 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.

from ultralytics import YOLO
import cv2
import numpy as np
import os
# ------------------ 配置 ------------------
model_path = 'ultralytics_yolo11-main/runs/train/exp4/weights/best.pt'
img_folder = '/home/hx/yolo/ultralytics_yolo11-main/dataset1/test'
output_mask_dir = 'output_masks1'
os.makedirs(output_mask_dir, exist_ok=True)
SUPPORTED_FORMATS = ('.jpg', '.jpeg', '.png', '.bmp', '.tif', '.tiff')
# ------------------ 加载模型 ------------------
model = YOLO(model_path)
model.to('cuda') # 使用 GPU如有
def get_long_edge_vector(rect):
"""
从 minAreaRect 中提取长边的方向向量(单位向量)
rect: cv2.minAreaRect 返回的 (center, (w, h), angle)
"""
center, (width, height), angle = rect
# OpenCV 的 angle 范围是 [-90, 0)
# 我们要的是长边的方向
if width >= height:
rad = np.radians(angle) # 长边方向
else:
rad = np.radians(angle + 90) # 短边变长边
dx = np.cos(rad)
dy = np.sin(rad)
direction = np.array([dx, dy])
norm = np.linalg.norm(direction)
return direction if norm < 1e-8 else direction / norm
def get_contour_center(contour):
"""计算轮廓质心"""
M = cv2.moments(contour)
if M["m00"] == 0:
return np.array([0, 0])
return np.array([int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"])])
def calculate_jaw_angle(jaw1, jaw2):
"""
计算两个夹具之间的开合角度(取最小内角)
返回: (angle, dir1, dir2)
"""
# 获取长边方向
dir1_orig = get_long_edge_vector(jaw1['rect'])
dir2_orig = get_long_edge_vector(jaw2['rect'])
# 计算夹角
cos_angle = np.clip(np.dot(dir1_orig, dir2_orig), -1.0, 1.0)
angle = np.degrees(np.arccos(cos_angle))
# 取最小内角0 ~ 90°
opening_angle = min(angle, 180 - angle)
# --- 可选:让方向指向夹具中心(箭头朝内)---
center1 = get_contour_center(jaw1['contour'])
center2 = get_contour_center(jaw2['contour'])
fixture_center = (center1 + center2) / 2.0
to_center1 = fixture_center - center1
if np.linalg.norm(to_center1) > 1e-6:
to_center1 = to_center1 / np.linalg.norm(to_center1)
if np.dot(dir1_orig, to_center1) < 0:
dir1_orig = -dir1_orig
to_center2 = fixture_center - center2
if np.linalg.norm(to_center2) > 1e-6:
to_center2 = to_center2 / np.linalg.norm(to_center2)
if np.dot(dir2_orig, to_center2) < 0:
dir2_orig = -dir2_orig
return opening_angle, dir1_orig, dir2_orig
def process_image(img_path, output_dir):
img = cv2.imread(img_path)
if img is None:
print(f"❌ 无法读取图像: {img_path}")
return
h, w = img.shape[:2]
filename = os.path.basename(img_path)
name_only = os.path.splitext(filename)[0]
print(f"\n🔄 正在处理: {filename}")
# 创建单通道掩码
composite_mask = np.zeros((h, w), dtype=np.uint8)
results = model(img_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)
# 创建三通道可视化图
vis_mask = np.stack([composite_mask] * 3, axis=-1)
vis_mask[composite_mask > 0] = [255, 255, 255]
if len(jaws) < 2:
print(f"⚠️ 检测到的夹具少于2个{len(jaws)}个)")
cv2.imwrite(os.path.join(output_dir, f'mask_{name_only}.png'), composite_mask)
return
# 按面积排序,取最大的两个
jaws.sort(key=lambda x: x['area'], reverse=True)
jaw1, jaw2 = jaws[0], jaws[1]
# === 计算夹角和方向 ===
opening_angle, dir1, dir2 = calculate_jaw_angle(jaw1, jaw2)
print(f"✅ 夹具开合角度: {opening_angle:.2f}°")
# === 可视化 ===
center1 = get_contour_center(jaw1['contour'])
center2 = get_contour_center(jaw2['contour'])
fixture_center = ((center1[0] + center2[0]) // 2, (center1[1] + center2[1]) // 2)
# 绘制最小外接矩形
box1 = cv2.boxPoints(jaw1['rect']).astype(int)
box2 = cv2.boxPoints(jaw2['rect']).astype(int)
cv2.drawContours(vis_mask, [box1], 0, (0, 0, 255), 2) # jaw1: 红色
cv2.drawContours(vis_mask, [box2], 0, (255, 0, 0), 2) # jaw2: 蓝色
# 绘制主方向箭头(绿色,长度可调)
scale = 60
end1 = center1 + scale * dir1
end2 = center2 + scale * dir2
cv2.arrowedLine(vis_mask, tuple(center1), tuple(end1.astype(int)), (0, 255, 0), 2, tipLength=0.3)
cv2.arrowedLine(vis_mask, tuple(center2), tuple(end2.astype(int)), (0, 255, 0), 2, tipLength=0.3)
# 标注中心点和角度
cv2.circle(vis_mask, fixture_center, 5, (255, 255, 0), -1)
cv2.putText(vis_mask, "Center", (fixture_center[0] + 10, fixture_center[1]),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 1)
cv2.putText(vis_mask, 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_mask)
print(f"✅ 结果已保存: {save_path}")
# ------------------ 主程序 ------------------
if __name__ == '__main__':
if not os.path.exists(img_folder):
print(f"❌ 图像文件夹不存在: {img_folder}")
exit()
image_files = [f for f in os.listdir(img_folder) if f.lower().endswith(SUPPORTED_FORMATS)]
if len(image_files) == 0:
print(f"⚠️ 未找到支持的图像文件")
exit()
print(f"✅ 发现 {len(image_files)} 张图像,开始处理...")
for file in image_files:
process_image(os.path.join(img_folder, file), output_mask_dir)
print(f"\n🎉 所有图像处理完成!结果保存在: {output_mask_dir}")