添加状态分类和液面分割

This commit is contained in:
琉璃月光
2025-09-01 14:14:18 +08:00
parent 6e553f6a20
commit ad52ab9125
2379 changed files with 102501 additions and 1465 deletions

BIN
1.rknn Normal file

Binary file not shown.

View File

@ -0,0 +1,74 @@
from ultralytics import YOLO
import cv2
import numpy as np
import os
# 1. 加载模型
model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/runs/train/exp_obb/weights/best.pt')
# 2. 读取图像
img_path = r"/output_masks/3.png"
img = cv2.imread(img_path)
# ✅ 检查图像是否加载成功
if img is None:
print(f"❌ 错误:无法读取图像!请检查路径:{img_path}")
print("💡 提示:可能是文件不存在、路径错误或图像损坏")
exit(1)
# 3. 预测(使用 OBB 模式!)
results = model(
img,
save=False,
imgsz=1280, # 必须和训练时一致
conf=0.25,
mode='obb' # 启用旋转框模式
)
# 4. 获取结果并绘制
result = results[0]
annotated_img = result.plot() # 自动绘制旋转框
# ✅ 5. 保存推理结果图像
output_dir = "./inference_results"
os.makedirs(output_dir, exist_ok=True)
filename = os.path.basename(img_path)
save_path = os.path.join(output_dir, "detected_" + filename)
cv2.imwrite(save_path, annotated_img)
print(f"✅ 推理结果已保存至: {save_path}")
# ✅ 6. 提取旋转框信息并计算夹角(修正版)
boxes = result.obb
if boxes is None or len(boxes) == 0:
print("❌ No objects detected.")
else:
print(f"✅ Detected {len(boxes)} object(s):")
angles = [] # 存储每个框的旋转角度(角度制)
for i, box in enumerate(boxes):
cls = int(box.cls.cpu().numpy()[0])
conf = box.conf.cpu().numpy()[0]
# ✅ 正确方式:使用 .xywhr 获取旋转角度(新版本 API
xywhr = box.xywhr.cpu().numpy()[0] # [x_center, y_center, width, height, rotation]
angle_rad = xywhr[4] # 第5个值是旋转角度弧度
angle_deg = np.degrees(angle_rad) # 转为角度
angles.append(angle_deg)
print(f" Box {i+1}: Class: {cls}, Confidence: {conf:.3f}, Angle: {angle_deg:.2f}°")
# ✅ 计算任意两个框之间的最小夹角差
if len(angles) >= 2:
print("\n🔍 计算旋转框之间的夹角差:")
for i in range(len(angles)):
for j in range(i + 1, len(angles)):
diff = abs(angles[i] - angles[j])
min_angle_diff = min(diff, 180 - diff) # 取最小夹角0~180°内
print(f" Box {i+1} 与 Box {j+1} 的最小夹角差: {min_angle_diff:.2f}°")
else:
print("⚠️ 检测到少于两个目标,无法计算夹角。")
# ✅ 7. 显示图像
cv2.imshow("YOLO OBB Prediction", annotated_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1012 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 851 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 851 KiB

View File

@ -2,10 +2,10 @@ from ultralytics import YOLO
import cv2 import cv2
# 1. 加载模型 # 1. 加载模型
model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/runs/train/exp_obb2/weights/best.pt') model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/runs/train/exp_obb/weights/best.pt')
# 2. 读取图像 # 2. 读取图像
img_path = r"/home/hx/桌面/image/images/test/1.jpg" img_path = r"/home/hx/yolo/test_image/2.png"
img = cv2.imread(img_path) img = cv2.imread(img_path)
# ✅ 检查图像是否加载成功 # ✅ 检查图像是否加载成功

140
angle_base_point/angle3.py Normal file
View File

@ -0,0 +1,140 @@
import cv2
import numpy as np
from ultralytics import YOLO
import time
import os
# ====================== 用户配置 ======================
MODEL_PATH = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/point/exp_pose2/weights/best.pt"
IMAGE_PATH = "/home/hx/yolo/output_masks/3.png"
SAVE_RESULT = True
SAVE_PATH = "/home/hx/yolo/test_image/result_with_angle2.jpg"
SHOW_IMAGE = True
# 关键点颜色:红、绿、蓝(对应 kpt 1, 2, 3
KPT_COLORS = [
(0, 0, 255), # 红色 - kpt 1
(0, 255, 0), # 绿色 - kpt 2
(255, 0, 0), # 蓝色 - kpt 3
]
# ====================== 计算两向量夹角0~180°=======================
def calculate_angle_between_vectors(v1, v2):
"""
计算两个向量之间的夹角(单位:度),范围 0 ~ 180°
"""
v1 = np.array(v1)
v2 = np.array(v2)
if np.linalg.norm(v1) == 0 or np.linalg.norm(v2) == 0:
return 0.0
v1_u = v1 / np.linalg.norm(v1)
v2_u = v2 / np.linalg.norm(v2)
dot = np.clip(np.dot(v1_u, v2_u), -1.0, 1.0)
angle_rad = np.arccos(dot)
angle_deg = np.degrees(angle_rad)
return angle_deg
# ====================== 主程序 =======================
if __name__ == "__main__":
print("正在加载模型...")
try:
model = YOLO(MODEL_PATH)
print(f"✅ 模型加载完成: {MODEL_PATH}")
except Exception as e:
print(f"❌ 模型加载失败: {e}")
exit(1)
# 读取图像
img = cv2.imread(IMAGE_PATH)
if img is None:
raise FileNotFoundError(f"❌ 无法加载图像: {IMAGE_PATH}")
h, w = img.shape[:2]
print(f"✅ 图像加载成功: {IMAGE_PATH} ({w}x{h})")
# --- 推理 ---
results = model(img, conf=0.25, imgsz=1280)
result = results[0]
# 存储每个实例的关键点和置信度
instances = []
if result.keypoints is not None and result.boxes is not None:
kpts_list = result.keypoints.xy.cpu().numpy() # (N, K, 2)
confs = result.boxes.conf.cpu().numpy() # (N,)
bboxes = result.boxes.xyxy.cpu().numpy() # (N, 4)
# 组合:置信度 + 关键点 + 边框
for i in range(len(kpts_list)):
if len(kpts_list[i]) >= 2: # 至少有两个关键点
kpt1 = (int(kpts_list[i][0][0]), int(kpts_list[i][0][1]))
kpt2 = (int(kpts_list[i][1][0]), int(kpts_list[i][1][1]))
instances.append({
'conf': confs[i],
'kpt1': kpt1,
'kpt2': kpt2,
'bbox': bboxes[i]
})
# 按置信度降序排序
instances.sort(key=lambda x: x['conf'], reverse=True)
# 只保留置信度最高的两个实例
top_two = instances[:2]
if len(top_two) == 2:
inst1, inst2 = top_two
# 绘制第一个实例的关键点和连线
cv2.circle(img, inst1['kpt1'], 10, KPT_COLORS[0], -1)
cv2.circle(img, inst1['kpt2'], 10, KPT_COLORS[1], -1)
cv2.line(img, inst1['kpt1'], inst1['kpt2'], (255, 255, 0), 3) # 黄色线
cv2.putText(img, "1", (inst1['kpt1'][0]+10, inst1['kpt1'][1]-10),
cv2.FONT_HERSHEY_SIMPLEX, 1.0, KPT_COLORS[0], 2)
cv2.putText(img, "2", (inst1['kpt2'][0]+10, inst1['kpt2'][1]-10),
cv2.FONT_HERSHEY_SIMPLEX, 1.0, KPT_COLORS[1], 2)
# 绘制第二个实例的关键点和连线
cv2.circle(img, inst2['kpt1'], 10, KPT_COLORS[0], -1)
cv2.circle(img, inst2['kpt2'], 10, KPT_COLORS[1], -1)
cv2.line(img, inst2['kpt1'], inst2['kpt2'], (0, 255, 255), 3) # 青色线
cv2.putText(img, "1", (inst2['kpt1'][0]+10, inst2['kpt1'][1]-10),
cv2.FONT_HERSHEY_SIMPLEX, 1.0, KPT_COLORS[0], 2)
cv2.putText(img, "2", (inst2['kpt2'][0]+10, inst2['kpt2'][1]-10),
cv2.FONT_HERSHEY_SIMPLEX, 1.0, KPT_COLORS[1], 2)
# 构造两个向量kpt2 - kpt1
vec1 = np.array([inst1['kpt2'][0] - inst1['kpt1'][0],
inst1['kpt2'][1] - inst1['kpt1'][1]])
vec2 = np.array([inst2['kpt2'][0] - inst2['kpt1'][0],
inst2['kpt2'][1] - inst2['kpt1'][1]])
# 计算夹角
angle = calculate_angle_between_vectors(vec1, vec2)
print(f"✅ 置信度最高的两个实例其关键点1-2连线夹角: {angle:.1f}°")
# 在图像上显示角度
cv2.putText(img, f"Angle: {angle:.1f}°", (50, 50),
cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 255, 255), 3, lineType=cv2.LINE_AA)
else:
print(f"⚠️ 检测到 {len(top_two)} 个有效实例需要至少2个")
else:
print("⚠️ 未检测到关键点或边界框")
# --- 保存结果 ---
os.makedirs(os.path.dirname(SAVE_PATH), exist_ok=True)
if SAVE_RESULT:
cv2.imwrite(SAVE_PATH, img)
print(f"✅ 结果图像已保存至: {SAVE_PATH}")
# --- 显示图像 ---
if SHOW_IMAGE:
cv2.imshow("Keypoint Angle Between Two Best Instances", img)
print("👉 按任意键关闭窗口...")
cv2.waitKey(0)
cv2.destroyAllWindows()

View File

@ -46,7 +46,7 @@ def draw_keypoints_and_angle(image, keypoints, angle):
# ====================== 用户配置 ====================== # ====================== 用户配置 ======================
MODEL_PATH = '/home/hx/yolo/ultralytics_yolo11-main/runs/train/exp_pose/weights/best.pt' # 替换为你的模型路径 MODEL_PATH = '/home/hx/yolo/ultralytics_yolo11-main/runs/train/exp_pose/weights/best.pt' # 替换为你的模型路径
IMAGE_PATH = '/home/hx/yolo/test_image/1.png' # 替换为你的测试图像路径 IMAGE_PATH = '/home/hx/yolo/output_masks/1.jpg' # 替换为你的测试图像路径
CONF_THRESHOLD = 0.25 CONF_THRESHOLD = 0.25
SAVE_RESULT = True SAVE_RESULT = True
SAVE_PATH = '/home/hx/yolo/test_image/result_with_angle.jpg' SAVE_PATH = '/home/hx/yolo/test_image/result_with_angle.jpg'

View File

@ -5,7 +5,7 @@ import os
# ====================== 用户配置 ====================== # ====================== 用户配置 ======================
MODEL_PATH = '/home/hx/yolo/ultralytics_yolo11-main/runs/train/exp_pose/weights/best.pt' MODEL_PATH = '/home/hx/yolo/ultralytics_yolo11-main/runs/train/exp_pose/weights/best.pt'
IMAGE_PATH = '/home/hx/yolo/test_image/2.png' IMAGE_PATH = '/output_masks/3.png'
OUTPUT_DIR = '/home/hx/yolo/output_images' # 保存结果的文件夹 OUTPUT_DIR = '/home/hx/yolo/output_images' # 保存结果的文件夹
os.makedirs(OUTPUT_DIR, exist_ok=True) os.makedirs(OUTPUT_DIR, exist_ok=True)

View File

@ -4,7 +4,7 @@ import numpy as np
import os import os
# ------------------ 模型与路径配置 ------------------ # ------------------ 模型与路径配置 ------------------
MODEL_PATH = '../ultralytics_yolo11-main/runs/train/exp4/weights/best.pt' MODEL_PATH = '../ultralytics_yolo11-main/runs/train/seg_j/exp/weights/best.pt'
OUTPUT_DIR = '../test_image' OUTPUT_DIR = '../test_image'
os.makedirs(OUTPUT_DIR, exist_ok=True) os.makedirs(OUTPUT_DIR, exist_ok=True)
@ -34,7 +34,7 @@ def detect_jaw_angle(image_path, mode='show'):
# 创建掩码并检测 # 创建掩码并检测
composite_mask = np.zeros((h, w), dtype=np.uint8) composite_mask = np.zeros((h, w), dtype=np.uint8)
results = model(image_path, imgsz=1280, conf=0.5) results = model(image_path, imgsz=640, conf=0.5)
jaws = [] jaws = []
for r in results: for r in results:
@ -143,13 +143,13 @@ def detect_jaw_angle(image_path, mode='show'):
# ------------------ 主函数 ------------------ # ------------------ 主函数 ------------------
if __name__ == '__main__': if __name__ == '__main__':
# ✅ 设置输入图像路 # ✅ 设置输入图像路
image_path = '/test_image/1.png' # ← 修改为你自己的图片路径 image_path = r"/home/hx/yolo/output_masks/2.jpg" # ← 修改为你自己的图片路径
# ✅ 模式选择: # ✅ 模式选择:
# mode='show': 保存可视化图像 # mode='show': 保存可视化图像
# mode='silent': 只返回角度 # mode='silent': 只返回角度
mode = 'silent' # 或 'silent' mode = 'show' # 或 'silent'
print(f"🔍 正在处理图像: {image_path}") print(f"🔍 正在处理图像: {image_path}")
angle = detect_jaw_angle(image_path, mode=mode) angle = detect_jaw_angle(image_path, mode=mode)

View File

@ -6,7 +6,7 @@ import torch
import torch.nn.functional as F import torch.nn.functional as F
# ------------------ 模型与路径配置 ------------------ # ------------------ 模型与路径配置 ------------------
MODEL_PATH = '../ultralytics_yolo11-main/runs/train/exp4/weights/best.pt' MODEL_PATH = '../ultralytics_yolo11-main/runs/train/seg/exp2/weights/best.pt'
OUTPUT_DIR = '../test_image' OUTPUT_DIR = '../test_image'
os.makedirs(OUTPUT_DIR, exist_ok=True) os.makedirs(OUTPUT_DIR, exist_ok=True)
@ -24,7 +24,7 @@ def detect_jaw_angle_fast(image_path, mode='silent'):
name_only = os.path.splitext(filename)[0] name_only = os.path.splitext(filename)[0]
# 推理(批量可进一步提速) # 推理(批量可进一步提速)
results = model(image_path, imgsz=1280, conf=0.5, device='cuda') results = model(image_path, imgsz=1280, conf=0.15, device='cuda')
r = results[0] r = results[0]
if r.masks is None: if r.masks is None:
@ -100,9 +100,9 @@ def detect_jaw_angle_fast(image_path, mode='silent'):
# ------------------ 测试 ------------------ # ------------------ 测试 ------------------
if __name__ == '__main__': if __name__ == '__main__':
image_path = '/test_image/1.png' image_path = '/home/hx/yolo/output_masks/1.jpg'
print(f"🚀 处理: {image_path}") print(f"🚀 处理: {image_path}")
angle = detect_jaw_angle_fast(image_path, mode='silent') angle = detect_jaw_angle_fast(image_path, mode='show')
if angle is not None: if angle is not None:
print(f"✅ 角度: {angle}°") print(f"✅ 角度: {angle}°")
else: else:

View File

@ -0,0 +1,109 @@
from ultralytics import YOLO
import cv2
import numpy as np
import os
import torch
import torch.nn.functional as F
# ------------------ 模型与路径配置 ------------------
MODEL_PATH = '../ultralytics_yolo11-main/runs/train/seg/exp3/weights/best.pt'
OUTPUT_DIR = '../test_image'
os.makedirs(OUTPUT_DIR, exist_ok=True)
model = YOLO(MODEL_PATH)
model.to('cuda')
def detect_jaw_angle_fast(image_path, mode='silent'):
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]
# 推理(批量可进一步提速)
results = model(image_path, imgsz=640, conf=0.5, device='cuda')
r = results[0]
if r.masks is None:
return None
# 【优化1】一次性上采样所有 masks
masks_tensor = r.masks.data # [N, h_m, w_m]
boxes = r.boxes.xyxy.cpu().numpy()
masks = F.interpolate(
masks_tensor.unsqueeze(0).float(),
size=(h, w),
mode='bilinear',
align_corners=False
)
masks = (masks[0] > 0.5).cpu().numpy().astype(np.uint8) # [N, h, w]
jaws = []
for i, (mask, box) in enumerate(zip(masks, boxes)):
x1, y1, x2, y2 = map(int, box)
# 【优化4】提前过滤小框
if (x2 - x1) * (y2 - y1) < 100:
continue
# 【优化2】裁剪区域
x1c, y1c = max(0, x1), max(0, y1)
x2c, y2c = min(w, x2), min(h, y2)
mask_crop = mask[y1c:y2c, x1c:x2c]
# 【优化3】使用 findNonZero + convexHull
coords = cv2.findNonZero(mask_crop)
if coords is None or len(coords) < 5:
continue
hull = cv2.convexHull(coords)
area = cv2.contourArea(hull)
if area < 100:
continue
rect = cv2.minAreaRect(hull)
jaws.append({'rect': rect, 'area': area})
if len(jaws) < 2:
return None
jaws = sorted(jaws, key=lambda x: x['area'], reverse=True)[:2]
rect1, rect2 = jaws[0]['rect'], jaws[1]['rect']
def get_angle(rect):
w, h = rect[1]
angle = rect[2]
return angle + 90 if w < h else angle
angle1 = get_angle(rect1) % 180
angle2 = get_angle(rect2) % 180
opening_angle = min(abs(angle1 - angle2), 180 - abs(angle1 - angle2))
# 可视化(可选)
if mode == 'show':
vis = np.zeros((h, w, 3), dtype=np.uint8)
box1 = cv2.boxPoints(rect1)
box2 = cv2.boxPoints(rect2)
cv2.drawContours(vis, [np.int32(box1)], 0, (0, 0, 255), 2)
cv2.drawContours(vis, [np.int32(box2)], 0, (255, 0, 0), 2)
cv2.putText(vis, f"{opening_angle:.1f}°", (20, 50),
cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 0), 2)
cv2.imwrite(os.path.join(OUTPUT_DIR, f"fast_{name_only}.png"), vis)
return round(opening_angle, 2)
# ------------------ 测试 ------------------
if __name__ == '__main__':
image_path = '/home/hx/yolo/output_masks/2.jpg'
print(f"🚀 处理: {image_path}")
angle = detect_jaw_angle_fast(image_path, mode='show')
if angle is not None:
print(f"✅ 角度: {angle}°")
else:
print("❌ 未检测到两个夹具")

206
angle_base_seg/bushu.py Normal file
View File

@ -0,0 +1,206 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import cv2
import numpy as np
import argparse
import torch
import torch.nn.functional as F
import torchvision
# ---------------- 配置 ----------------
OBJ_THRESH = 0.25
NMS_THRESH = 0.45
MAX_DETECT = 300
IMG_SIZE = (640, 640) # (W,H)
OUTPUT_DIR = "result"
os.makedirs(OUTPUT_DIR, exist_ok=True)
# ---------------- 工具函数 ----------------
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def dfl(position):
x = torch.tensor(position)
n, c, h, w = x.shape
y = x.reshape(n, 4, c // 4, h, w)
y = y.softmax(2)
acc_metrix = torch.arange(c // 4).float().reshape(1, 1, c // 4, 1, 1)
y = (y * acc_metrix).sum(2)
return y.numpy()
def box_process(position):
grid_h, grid_w = position.shape[2:4]
col, row = np.meshgrid(np.arange(0, grid_w), np.arange(0, grid_h))
col, row = col.reshape(1, 1, grid_h, grid_w), row.reshape(1, 1, grid_h, grid_w)
grid = np.concatenate((col, row), axis=1)
stride = np.array([IMG_SIZE[1] // grid_h, IMG_SIZE[0] // grid_w]).reshape(1, 2, 1, 1)
position = dfl(position)
box_xy = grid + 0.5 - position[:, 0:2, :, :]
box_xy2 = grid + 0.5 + position[:, 2:4, :, :]
xyxy = np.concatenate((box_xy * stride, box_xy2 * stride), axis=1)
return xyxy
def _crop_mask(masks, boxes):
n, h, w = masks.shape
x1, y1, x2, y2 = torch.chunk(boxes[:, :, None], 4, 1)
r = torch.arange(w, device=masks.device, dtype=x1.dtype)[None, None, :]
c = torch.arange(h, device=masks.device, dtype=x1.dtype)[None, :, None]
return masks * ((r >= x1) * (r < x2) * (c >= y1) * (c < y2))
def post_process(input_data):
proto = input_data[-1]
boxes, scores, seg_part = [], [], []
default_branch = 3
pair_per_branch = len(input_data) // default_branch
for i in range(default_branch):
boxes.append(box_process(input_data[pair_per_branch * i]))
scores.append(np.ones_like(input_data[pair_per_branch * i + 1][:, :1, :, :], dtype=np.float32))
seg_part.append(input_data[pair_per_branch * i + 3])
def sp_flatten(_in):
ch = _in.shape[1]
_in = _in.transpose(0, 2, 3, 1)
return _in.reshape(-1, ch)
boxes = np.concatenate([sp_flatten(v) for v in boxes])
scores = np.concatenate([sp_flatten(v) for v in scores])
seg_part = np.concatenate([sp_flatten(v) for v in seg_part])
# 阈值过滤
keep = np.where(scores.reshape(-1) >= OBJ_THRESH)
boxes, scores, seg_part = boxes[keep], scores[keep], seg_part[keep]
# NMS
ids = torchvision.ops.nms(torch.tensor(boxes, dtype=torch.float32),
torch.tensor(scores, dtype=torch.float32), NMS_THRESH)
ids = ids.tolist()[:MAX_DETECT]
boxes, scores, seg_part = boxes[ids], scores[ids], seg_part[ids]
# mask decode
ph, pw = proto.shape[-2:]
proto = proto.reshape(seg_part.shape[-1], -1)
seg_img = np.matmul(seg_part, proto)
seg_img = sigmoid(seg_img)
seg_img = seg_img.reshape(-1, ph, pw)
seg_img = F.interpolate(torch.tensor(seg_img)[None], torch.Size([640, 640]), mode='bilinear', align_corners=False)[0]
seg_img_t = _crop_mask(seg_img, torch.tensor(boxes))
seg_img = seg_img_t.numpy() > 0.5
return boxes, scores, seg_img
# ---------------- 角度计算 ----------------
def compute_angle(boxes, seg_img, h, w, filename, mode="show"):
composite_mask = np.zeros((h, w), dtype=np.uint8)
jaws = []
for i, box in enumerate(boxes):
x1, y1, x2, y2 = map(int, box)
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(seg_img[i].astype(np.uint8), (w, h))
obj_mask[y1:y2, x1:x2] = mask_resized[y1:y2, x1:x2] * 255
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)}个)")
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, dir2 = get_long_edge_vector(jaw1['rect']), get_long_edge_vector(jaw2['rect'])
center1, center2 = get_center(jaw1['contour']), get_center(jaw2['contour'])
fixture_center = (center1 + center2) / 2
to_center1, to_center2 = fixture_center - center1, 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)
if mode == "show":
vis_img = np.stack([composite_mask]*3, axis=-1)
vis_img[composite_mask > 0] = [255, 255, 255]
box1, box2 = np.int32(cv2.boxPoints(jaw1['rect'])), 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, c2 = tuple(np.int32(center1)), tuple(np.int32(center2))
end1, end2 = tuple(np.int32(center1 + scale * dir1)), 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'angle_{filename}')
cv2.imwrite(save_path, vis_img)
print(f"✅ 结果已保存: {save_path}")
return round(opening_angle, 2)
# ---------------- 主程序 ----------------
def main():
# 固定路径(写死)
MODEL_PATH = "/userdata/bushu/seg.rknn"
IMG_PATH = "/userdata/bushu/test.jpg"
from py_utils.rknn_executor import RKNN_model_container
model = RKNN_model_container(MODEL_PATH, target='rk3588', device_id=None)
img_src = cv2.imread(IMG_PATH)
if img_src is None:
print("❌ 图片路径错误:", IMG_PATH)
return
h, w = img_src.shape[:2]
img = cv2.resize(img_src, IMG_SIZE)
outputs = model.run([img])
boxes, scores, seg_img = post_process(outputs)
filename = os.path.basename(IMG_PATH)
angle = compute_angle(boxes, seg_img, h, w, filename, mode="show")
if angle is not None:
print(f"🎉 检测到的夹具开合角度: {angle}°")
model.release()
if __name__ == "__main__":
main()

View File

@ -5,7 +5,7 @@ import os
# ------------------ 配置 ------------------ # ------------------ 配置 ------------------
model_path = '../ultralytics_yolo11-main/runs/train/exp4/weights/best.pt' model_path = '../ultralytics_yolo11-main/runs/train/exp4/weights/best.pt'
img_folder = '/home/hx/yolo/ultralytics_yolo11-main/dataset1/test' # 你的图片文件夹路径 img_folder = '/home/hx/yolo/test_image' # 你的图片文件夹路径
output_mask_dir = 'output_masks1' output_mask_dir = 'output_masks1'
os.makedirs(output_mask_dir, exist_ok=True) os.makedirs(output_mask_dir, exist_ok=True)

View File

@ -4,8 +4,8 @@ import numpy as np
import os import os
# ------------------ 配置 ------------------ # ------------------ 配置 ------------------
model_path = '../ultralytics_yolo11-main/runs/train/exp4/weights/best.pt' model_path = '/home/hx/yolo/ultralytics_yolo11-main/runs/train/seg/exp3/weights/best.pt'
img_folder = '/home/hx/yolo/ultralytics_yolo11-main/dataset1/test' img_folder = '/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/dataset2/test'
output_mask_dir = 'output_masks1' output_mask_dir = 'output_masks1'
os.makedirs(output_mask_dir, exist_ok=True) os.makedirs(output_mask_dir, exist_ok=True)
@ -139,9 +139,9 @@ def process_image(img_path, output_dir):
composite_mask = np.maximum(composite_mask, obj_mask) composite_mask = np.maximum(composite_mask, obj_mask)
# 保存合并掩码 # 保存合并掩码
mask_save_path = os.path.join(output_dir, f'mask_{name_only}.png') #mask_save_path = os.path.join(output_dir, f'mask_{name_only}.png')
cv2.imwrite(mask_save_path, composite_mask) #cv2.imwrite(mask_save_path, composite_mask)
print(f"✅ 掩码已保存: {mask_save_path}") #print(f"✅ 掩码已保存: {mask_save_path}")
if len(rotated_rects) < 2: if len(rotated_rects) < 2:
print(f"⚠️ 检测到的对象少于2个{len(rotated_rects)}个): {filename}") print(f"⚠️ 检测到的对象少于2个{len(rotated_rects)}个): {filename}")

View File

@ -4,8 +4,8 @@ import numpy as np
import os import os
# ------------------ 配置 ------------------ # ------------------ 配置 ------------------
model_path = '../ultralytics_yolo11-main/runs/train/exp4/weights/best.pt' model_path = '/home/hx/yolo/ultralytics_yolo11-main/runs/train/seg/exp3/weights/best.pt'
img_folder = '/home/hx/yolo/ultralytics_yolo11-main/dataset1/test' img_folder = '/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/dataset2/test'
output_mask_dir = 'output_masks1' output_mask_dir = 'output_masks1'
os.makedirs(output_mask_dir, exist_ok=True) os.makedirs(output_mask_dir, exist_ok=True)

View File

@ -80,8 +80,8 @@ def process_images_in_folder(input_folder, output_folder):
# ================ 使用示例 ================ # ================ 使用示例 ================
if __name__ == "__main__": if __name__ == "__main__":
folder = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/f10" folder = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/f15"
output_folder = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/f10" output_folder = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/f15"
if not os.path.exists(folder): if not os.path.exists(folder):
print("❌ 输入文件夹不存在!") print("❌ 输入文件夹不存在!")

View File

@ -106,7 +106,7 @@ def delete_gray_images(folder_path, extensions=None, dry_run=False):
# ================== 用户配置 ================== # ================== 用户配置 ==================
FOLDER_PATH = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/f10" # 修改为你的图片文件夹 FOLDER_PATH = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/f15" # 修改为你的图片文件夹
DRY_RUN = False # 先设为 True 测试,确认无误后再改为 False DRY_RUN = False # 先设为 True 测试,确认无误后再改为 False
# ================== 执行 ================== # ================== 执行 ==================

View File

@ -91,7 +91,7 @@ def delete_similar_consecutive_images(folder_path, threshold=0.95, extensions=No
# ================== 用户配置 ================== # ================== 用户配置 ==================
FOLDER_PATH = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/f10" # 修改为你的图片文件夹路径 FOLDER_PATH = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/f15" # 修改为你的图片文件夹路径
THRESHOLD = 0.90 # SSIM 阈值 THRESHOLD = 0.90 # SSIM 阈值
# ================== 执行 ================== # ================== 执行 ==================

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 MiB

View File

Before

Width:  |  Height:  |  Size: 4.8 MiB

After

Width:  |  Height:  |  Size: 4.8 MiB

BIN
output_masks/1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 KiB

BIN
output_masks/2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 KiB

View File

Before

Width:  |  Height:  |  Size: 3.9 MiB

After

Width:  |  Height:  |  Size: 3.9 MiB

BIN
output_masks/4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 KiB

BIN
seg.onnx Normal file

Binary file not shown.

BIN
seg.rknn Normal file

Binary file not shown.

BIN
seg01.onnx Normal file

Binary file not shown.

BIN
seg1.rknn Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@ -1,7 +1,7 @@
path: /home/hx/yolo/ultralytics_yolo11-main/dataset1 # 数据集所在路径 path: /media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/dataset2 # 数据集所在路径
train: train # 数据集路径下的train.txt train: train # 数据集路径下的train.txt
val: val # 数据集路径下的val.txt val: val # 数据集路径下的val.txt
test: test # 数据集路径下的test.txt test: test # 数据集路径下的test.txt
nc: 1 nc: 1
names: ['夹具1'] names: ['jiaju']

Binary file not shown.

Before

Width:  |  Height:  |  Size: 515 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 518 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 534 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 529 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 497 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 549 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 579 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 571 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 590 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 554 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 578 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 614 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 616 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 618 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 560 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 572 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 589 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 598 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 607 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 613 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 619 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 551 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 564 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 587 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 599 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 610 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 638 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 583 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 606 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 623 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 633 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 640 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 646 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 647 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 591 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 612 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 625 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 628 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 638 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 641 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 573 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 600 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 620 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 635 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 640 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 576 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 616 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 630 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 633 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 640 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 643 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 583 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 607 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 627 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 636 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 642 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 648 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 650 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 594 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 614 KiB

Some files were not shown because too many files have changed in this diff Show More