diff --git a/ailai_obb/angle.py b/ailai_obb/angle.py deleted file mode 100644 index 1ff66c4..0000000 --- a/ailai_obb/angle.py +++ /dev/null @@ -1,105 +0,0 @@ -from ultralytics import YOLO -import cv2 -import os -import numpy as np - -def get_best_obb_angle(image_path, weight_path, return_degree=False): - """ - 输入: - image_path: 图像路径 - weight_path: YOLO权重路径 - return_degree: 是否返回角度单位为度,默认 False(返回弧度) - 输出: - 置信度最高目标的旋转角 - 如果未检测到目标返回 None - """ - # 读取图像 - img = cv2.imread(image_path) - if img is None: - print(f"❌ 无法读取图像:{image_path}") - return None - - # 加载模型并预测 - model = YOLO(weight_path) - results = model(img, save=False, imgsz=640, conf=0.15, mode='obb') - result = results[0] - - boxes = result.obb - if not boxes: - print("⚠️ 未检测到目标。") - return None - - # 取置信度最高框的旋转角 - best_box = max(boxes, key=lambda x: x.conf.cpu().numpy()[0]) - r = best_box.xywhr.cpu().numpy()[0][4] # 弧度 - - if return_degree: - return np.degrees(r) - else: - return r - - -def save_obb_visual(image_path, weight_path, save_path): - """ - 输入: - image_path: 图像路径 - weight_path: YOLO权重路径 - save_path: 保存带角度标注图像路径 - 功能: - 检测 OBB 并标注置信度最高框旋转角度,保存图片 - """ - img = cv2.imread(image_path) - if img is None: - print(f"❌ 无法读取图像:{image_path}") - return - - model = YOLO(weight_path) - results = model(img, save=False, imgsz=640, conf=0.15, mode='obb') - result = results[0] - - boxes = result.obb - if not boxes: - print("⚠️ 未检测到目标。") - return - - best_box = max(boxes, key=lambda x: x.conf.cpu().numpy()[0]) - cx, cy, w, h, r = best_box.xywhr.cpu().numpy()[0] - angle_deg = np.degrees(r) - - # 绘制 OBB - annotated_img = img.copy() - rect = ((cx, cy), (w, h), angle_deg) - box_pts = cv2.boxPoints(rect).astype(int) - cv2.polylines(annotated_img, [box_pts], isClosed=True, color=(0, 255, 0), thickness=2) - - # 标注角度 - text = f"{angle_deg:.1f}°" - font_scale = max(0.5, min(w, h)/100) - thickness = 2 - text_size, _ = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, font_scale, thickness) - text_x = int(cx - text_size[0]/2) - text_y = int(cy + text_size[1]/2) - cv2.putText(annotated_img, text, (text_x, text_y), - cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 255), thickness) - - # 保存 - os.makedirs(os.path.dirname(save_path), exist_ok=True) - cv2.imwrite(save_path, annotated_img) - print(f"✅ 检测结果已保存至: {save_path}") - - -# =============================== -# 示例调用 -# =============================== -if __name__ == "__main__": - weight = r"/home/hx/yolo/ultralytics_yolo11-main/runs/train/exp_obb3/weights/best.pt" - image = r"/home/hx/yolo/output_masks/2.jpg" - save_path = "./inference_results/best_detected_2.jpg" - - angle_rad = get_best_obb_angle(image, weight) - print(f"旋转角(弧度):{angle_rad:.4f}") - - angle_deg = get_best_obb_angle(image, weight, return_degree=True) - print(f"旋转角(度):{angle_deg:.2f}°") - - save_obb_visual(image, weight, save_path) diff --git a/ailai_obb/bag_bushu.py b/ailai_obb/bag_bushu.py deleted file mode 100644 index a57228d..0000000 --- a/ailai_obb/bag_bushu.py +++ /dev/null @@ -1,171 +0,0 @@ -import cv2 -import numpy as np -import math -from shapely.geometry import Polygon -from rknnlite.api import RKNNLite -import os - -CLASSES = ['clamp'] -nmsThresh = 0.4 -objectThresh = 0.5 - -# ------------------- 工具函数 ------------------- -def letterbox_resize(image, size, bg_color=114): - target_width, target_height = size - image_height, image_width, _ = image.shape - scale = min(target_width / image_width, target_height / image_height) - new_width, new_height = int(image_width * scale), int(image_height * scale) - image = cv2.resize(image, (new_width, new_height), interpolation=cv2.INTER_AREA) - canvas = np.ones((target_height, target_width, 3), dtype=np.uint8) * bg_color - offset_x, offset_y = (target_width - new_width) // 2, (target_height - new_height) // 2 - canvas[offset_y:offset_y + new_height, offset_x:offset_x + new_width] = image - return canvas, scale, offset_x, offset_y - -class DetectBox: - def __init__(self, classId, score, xmin, ymin, xmax, ymax, angle): - self.classId = classId - self.score = score - self.xmin = xmin - self.ymin = ymin - self.xmax = xmax - self.ymax = ymax - self.angle = angle - -def rotate_rectangle(x1, y1, x2, y2, a): - cx, cy = (x1 + x2) / 2, (y1 + y2) / 2 - x1_new = int((x1 - cx) * math.cos(a) - (y1 - cy) * math.sin(a) + cx) - y1_new = int((x1 - cx) * math.sin(a) + (y1 - cy) * math.cos(a) + cy) - x2_new = int((x2 - cx) * math.cos(a) - (y2 - cy) * math.sin(a) + cx) - y2_new = int((x2 - cx) * math.sin(a) + (y2 - cy) * math.cos(a) + cy) - x3_new = int((x1 - cx) * math.cos(a) - (y2 - cy) * math.sin(a) + cx) - y3_new = int((x1 - cx) * math.sin(a) + (y2 - cy) * math.cos(a) + cy) - x4_new = int((x2 - cx) * math.cos(a) - (y1 - cy) * math.sin(a) + cx) - y4_new = int((x2 - cx) * math.sin(a) + (y1 - cy) * math.cos(a) + cy) - return [(x1_new, y1_new), (x3_new, y3_new), (x2_new, y2_new), (x4_new, y4_new)] - -def intersection(g, p): - g = Polygon(np.array(g).reshape(-1,2)) - p = Polygon(np.array(p).reshape(-1,2)) - if not g.is_valid or not p.is_valid: - return 0 - inter = g.intersection(p).area - union = g.area + p.area - inter - return 0 if union == 0 else inter / union - -def NMS(detectResult): - predBoxs = [] - sort_detectboxs = sorted(detectResult, key=lambda x: x.score, reverse=True) - for i in range(len(sort_detectboxs)): - if sort_detectboxs[i].classId == -1: - continue - p1 = rotate_rectangle(sort_detectboxs[i].xmin, sort_detectboxs[i].ymin, - sort_detectboxs[i].xmax, sort_detectboxs[i].ymax, - sort_detectboxs[i].angle) - predBoxs.append(sort_detectboxs[i]) - for j in range(i + 1, len(sort_detectboxs)): - if sort_detectboxs[j].classId == sort_detectboxs[i].classId: - p2 = rotate_rectangle(sort_detectboxs[j].xmin, sort_detectboxs[j].ymin, - sort_detectboxs[j].xmax, sort_detectboxs[j].ymax, - sort_detectboxs[j].angle) - if intersection(p1, p2) > nmsThresh: - sort_detectboxs[j].classId = -1 - return predBoxs - -def sigmoid(x): - return np.where(x >= 0, 1 / (1 + np.exp(-x)), np.exp(x) / (1 + np.exp(x))) - -def softmax(x, axis=-1): - exp_x = np.exp(x - np.max(x, axis=axis, keepdims=True)) - return exp_x / np.sum(exp_x, axis=axis, keepdims=True) - -def process(out, model_w, model_h, stride, angle_feature, index, scale_w=1, scale_h=1): - class_num = len(CLASSES) - angle_feature = angle_feature.reshape(-1) - xywh = out[:, :64, :] - conf = sigmoid(out[:, 64:, :]).reshape(-1) - boxes = [] - for ik in range(model_h * model_w * class_num): - if conf[ik] > objectThresh: - w = ik % model_w - h = (ik % (model_w * model_h)) // model_w - c = ik // (model_w * model_h) - # 解析xywh - xywh_ = xywh[0, :, (h * model_w) + w].reshape(1, 4, 16, 1) - data = np.arange(16).reshape(1, 1, 16, 1) - xywh_ = softmax(xywh_, 2) - xywh_ = np.sum(xywh_ * data, axis=2).reshape(-1) - xywh_add = xywh_[:2] + xywh_[2:] - xywh_sub = (xywh_[2:] - xywh_[:2]) / 2 - # 安全取角度 - angle_idx = min(index + (h * model_w) + w, len(angle_feature) - 1) - angle = (angle_feature[angle_idx] - 0.25) * math.pi - cos_a, sin_a = math.cos(angle), math.sin(angle) - xy = xywh_sub[0] * cos_a - xywh_sub[1] * sin_a, xywh_sub[0] * sin_a + xywh_sub[1] * cos_a - xywh1 = np.array([xy[0] + w + 0.5, xy[1] + h + 0.5, xywh_add[0], xywh_add[1]]) - xywh1 *= stride - xmin = (xywh1[0] - xywh1[2]/2) * scale_w - ymin = (xywh1[1] - xywh1[3]/2) * scale_h - xmax = (xywh1[0] + xywh1[2]/2) * scale_w - ymax = (xywh1[1] + xywh1[3]/2) * scale_h - boxes.append(DetectBox(c, conf[ik], xmin, ymin, xmax, ymax, angle)) - return boxes - -# ------------------- 主函数 ------------------- -def detect_boxes_angle_rknn(model_path, image_path, save_path=None): - img = cv2.imread(image_path) - if img is None: - print(f"❌ 无法读取图像: {image_path}") - return None, None - - img_resized, scale, offset_x, offset_y = letterbox_resize(img, (640, 640)) - infer_img = cv2.cvtColor(img_resized, cv2.COLOR_BGR2RGB) - infer_img = np.expand_dims(infer_img, 0) - - rknn_lite = RKNNLite(verbose=False) - rknn_lite.load_rknn(model_path) - rknn_lite.init_runtime(core_mask=RKNNLite.NPU_CORE_0) - - results = rknn_lite.inference([infer_img]) - detect_boxes = [] - for x in results[:-1]: - index, stride = 0, 0 - if x.shape[2] == 20: - stride, index = 32, 20*4*20*4 + 20*2*20*2 - elif x.shape[2] == 40: - stride, index = 16, 20*4*20*4 - elif x.shape[2] == 80: - stride, index = 8, 0 - feature = x.reshape(1, 65, -1) - detect_boxes += process(feature, x.shape[3], x.shape[2], stride, results[-1], index) - - detect_boxes = NMS(detect_boxes) - - # 输出每个检测框角度 - for i, box in enumerate(detect_boxes): - print(f"框 {i+1}: angle = {box.angle:.4f} rad ({np.degrees(box.angle):.2f}°)") - if save_path: - xmin = int((box.xmin - offset_x)/scale) - ymin = int((box.ymin - offset_y)/scale) - xmax = int((box.xmax - offset_x)/scale) - ymax = int((box.ymax - offset_y)/scale) - points = rotate_rectangle(xmin, ymin, xmax, ymax, box.angle) - cv2.polylines(img, [np.array(points, np.int32)], True, (0, 255, 0), 1) - cv2.putText(img, f"{np.degrees(box.angle):.1f}°", (xmin, ymin-5), - cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,255), 1) - - if save_path: - os.makedirs(os.path.dirname(save_path), exist_ok=True) - cv2.imwrite(save_path, img) - print(f"✅ 带角度的检测结果已保存到 {save_path}") - - rknn_lite.release() - return detect_boxes, img - -# ------------------- 使用示例 ------------------- -if __name__ == "__main__": - model_path = "obb.rknn" - image_path = "2.jpg" - save_path = "./inference_results/boxes_with_angle.jpg" - os.makedirs(os.path.dirname(save_path), exist_ok=True) - detect_boxes_angle_rknn(model_path, image_path, save_path) - diff --git a/angle_base_obb/angle_caculate.py b/angle_base_obb/angle_caculate.py index dc7d9aa..d197218 100644 --- a/angle_base_obb/angle_caculate.py +++ b/angle_base_obb/angle_caculate.py @@ -25,6 +25,7 @@ def predict_obb_best_angle(model_path, image_path, save_path=None): # 3. 推理 OBB results = model(img, save=False, imgsz=640, conf=0.5, mode='obb') result = results[0] + print(result) # 4. 可视化 annotated_img = result.plot() @@ -62,8 +63,8 @@ def predict_obb_best_angle(model_path, image_path, save_path=None): # ------------------- 测试 ------------------- if __name__ == "__main__": - weight_path = r'best.pt' - image_path = r"./test_image/3.jpg" + weight_path = r'obb.pt' + image_path = r"./test_image/7.jpg" save_path = "./inference_results/detected_3.jpg" #angle_deg, annotated_img = predict_obb_best_angle(weight_path, image_path, save_path) diff --git a/angle_base_obb/angle_caculate_file.py b/angle_base_obb/angle_caculate_file.py index 6ba93ec..3815168 100644 --- a/angle_base_obb/angle_caculate_file.py +++ b/angle_base_obb/angle_caculate_file.py @@ -91,8 +91,8 @@ def process_obb_images(model_path, image_dir, output_dir="./inference_results", # ------------------- 测试调用 ------------------- if __name__ == "__main__": - MODEL_PATH = r'best.pt' - IMAGE_SOURCE_DIR = r"./test_image" + MODEL_PATH = r'obb.pt' + IMAGE_SOURCE_DIR = r"/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/obb4/val" OUTPUT_DIR = "./inference_results" results = process_obb_images(MODEL_PATH, IMAGE_SOURCE_DIR, OUTPUT_DIR) diff --git a/angle_base_obb/best.pt b/angle_base_obb/best.pt deleted file mode 100644 index 7b5b32d..0000000 Binary files a/angle_base_obb/best.pt and /dev/null differ diff --git a/angle_base_obb/label_view.py b/angle_base_obb/label_view.py new file mode 100644 index 0000000..5a7f738 --- /dev/null +++ b/angle_base_obb/label_view.py @@ -0,0 +1,120 @@ +import cv2 +import numpy as np +import matplotlib.pyplot as plt +import os + +# ========================= +# 强制使用非 GUI 后端(关键!) +# ========================= +import matplotlib + +matplotlib.use('Agg') # 必须在 import pyplot 之前设置 + + +def visualize_obb(image_path, label_path, output_dir="output_visualizations"): + """ + 可视化图片及其 OBB 标签,并保存结果图像到指定目录。 + + :param image_path: 图片路径 + :param label_path: 标签路径 + :param output_dir: 输出目录(自动创建) + """ + # 读取图像 + image = cv2.imread(image_path) + if image is None: + print(f"❌ 无法读取图像: {image_path}") + return + + h, w = image.shape[:2] + print(f"✅ 正在处理图像: {os.path.basename(image_path)} | 尺寸: {w} x {h}") + + # 创建用于绘图的副本(BGR → 绘图用) + img_draw = image.copy() + + # 读取标签 + try: + with open(label_path, 'r') as f: + lines = f.readlines() + except Exception as e: + print(f"❌ 无法读取标签文件 {label_path}: {e}") + return + + for line in lines: + parts = line.strip().split() + if len(parts) < 9: + print(f"⚠️ 跳过无效标签行: {line}") + continue + + # 解析:class_id x1 y1 x2 y2 x3 y3 x4 y4 + try: + points = np.array([float(x) for x in parts[1:9]]).reshape(4, 2) + except: + print(f"⚠️ 坐标解析失败: {line}") + continue + + # 归一化坐标 → 像素坐标 + points[:, 0] *= w # x + points[:, 1] *= h # y + points = np.int32(points) + + # 绘制四边形(绿色) + cv2.polylines(img_draw, [points], isClosed=True, color=(0, 255, 0), thickness=3) + + # 绘制顶点(红色圆圈) + for (x, y) in points: + cv2.circle(img_draw, (x, y), 6, (0, 0, 255), -1) # 红色实心圆 + + # 转为 RGB 用于 matplotlib 保存 + img_rgb = cv2.cvtColor(img_draw, cv2.COLOR_BGR2RGB) + + # 创建输出目录 + os.makedirs(output_dir, exist_ok=True) + + # 生成输出路径 + filename = os.path.splitext(os.path.basename(image_path))[0] + "_vis.png" + output_path = os.path.join(output_dir, filename) + + # 使用 matplotlib 保存图像(不显示) + plt.figure(figsize=(16, 9), dpi=100) + plt.imshow(img_rgb) + plt.title(f"OBB Visualization - {os.path.basename(image_path)}", fontsize=14) + plt.axis('off') + plt.tight_layout() + plt.savefig(output_path, bbox_inches='tight', pad_inches=0) + plt.close() # 释放内存 + + print(f"✅ 可视化结果已保存: {output_path}") + + +def process_directory(directory): + """ + 遍历目录,处理所有图片和对应的 .txt 标签文件 + """ + print(f"🔍 开始处理目录: {directory}") + count = 0 + + for filename in os.listdir(directory): + if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.tiff')): + image_path = os.path.join(directory, filename) + label_path = os.path.splitext(image_path)[0] + ".txt" + + if os.path.exists(label_path): + visualize_obb(image_path, label_path) + count += 1 + else: + print(f"🟡 跳过 (无标签): {filename}") + + print(f"🎉 处理完成!共处理 {count} 张图像。") + + +# ========================= +# 主程序入口 +# ========================= +if __name__ == "__main__": + # 设置你的数据目录 + directory = '/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/obb4/labels' + + if not os.path.exists(directory): + raise FileNotFoundError(f"目录不存在: {directory}") + + process_directory(directory) \ No newline at end of file diff --git a/angle_base_obb/obb.pt b/angle_base_obb/obb.pt new file mode 100644 index 0000000..c247a65 Binary files /dev/null and b/angle_base_obb/obb.pt differ diff --git a/angle_base_obb/test_image/4.jpg b/angle_base_obb/test_image/4.jpg new file mode 100755 index 0000000..d788c6d Binary files /dev/null and b/angle_base_obb/test_image/4.jpg differ diff --git a/angle_base_obb/test_image/5.jpg b/angle_base_obb/test_image/5.jpg new file mode 100755 index 0000000..2b221ba Binary files /dev/null and b/angle_base_obb/test_image/5.jpg differ diff --git a/angle_base_obb/test_image/6.jpg b/angle_base_obb/test_image/6.jpg new file mode 100755 index 0000000..89faa9b Binary files /dev/null and b/angle_base_obb/test_image/6.jpg differ diff --git a/angle_base_obb/tongji.py b/angle_base_obb/tongji.py new file mode 100644 index 0000000..66f3447 --- /dev/null +++ b/angle_base_obb/tongji.py @@ -0,0 +1,101 @@ +import cv2 +import os +import numpy as np +from ultralytics import YOLO + +IMG_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.bmp', '.tif', '.tiff', '.webp'} + + +def process_obb_images_for_angle_distribution(model_path, image_dir, conf_thresh=0.15, imgsz=640): + """ + 批量处理图像的 OBB 推理,计算每张图像检测目标的主方向和夹角,并统计夹角分布情况。 + + 输入: + model_path: YOLO 权重路径 + image_dir: 图像文件夹路径 + conf_thresh: 置信度阈值 + imgsz: 输入图像大小 + 输出: + angle_distribution: {'<6': count, '6-20': count, '>20': count} + """ + results_dict = {} + angle_distribution = {'<6': 0, '6-20': 0, '>20': 0} + + print("加载 YOLO 模型...") + model = YOLO(model_path) + print("✅ 模型加载完成") + + # 获取图像文件 + image_files = [f for f in os.listdir(image_dir) if os.path.splitext(f.lower())[1] in IMG_EXTENSIONS] + if not image_files: + print(f"❌ 未找到图像文件:{image_dir}") + return angle_distribution + + print(f"发现 {len(image_files)} 张图像待处理") + + for img_filename in image_files: + img_path = os.path.join(image_dir, img_filename) + print(f"\n正在处理:{img_filename}") + + img = cv2.imread(img_path) + if img is None: + print(f"❌ 跳过:无法读取图像 {img_path}") + continue + + # 推理 OBB + results = model(img, save=False, imgsz=imgsz, conf=conf_thresh, mode='obb') + result = results[0] + + # 提取旋转角 + boxes = result.obb + angles_deg = [] + if boxes is None or len(boxes) == 0: + print("❌ 该图像中未检测到任何目标") + else: + for i, box in enumerate(boxes): + cx, cy, w, h, r_rad = box.xywhr.cpu().numpy()[0] + direction = r_rad if w >= h else r_rad + np.pi / 2 + direction = direction % np.pi + angle_deg = np.degrees(direction) + angles_deg.append(angle_deg) + + # 两两夹角 + pairwise_angles_deg = [] + if len(angles_deg) >= 2: + for i in range(len(angles_deg)): + for j in range(i + 1, len(angles_deg)): + diff_rad = abs(np.radians(angles_deg[i]) - np.radians(angles_deg[j])) + min_diff_rad = min(diff_rad, np.pi - diff_rad) + angle_deg_diff = np.degrees(min_diff_rad) + pairwise_angles_deg.append(angle_deg_diff) + + # 更新角度分布统计 + if angle_deg_diff < 6: + angle_distribution['<6'] += 1 + elif 6 <= angle_deg_diff <= 20: + angle_distribution['6-20'] += 1 + else: + angle_distribution['>20'] += 1 + + print(f" Box {i + 1} 与 Box {j + 1} 夹角: {angle_deg_diff:.2f}°") + + # 保存每张图像结果 + results_dict[img_filename] = { + "angles_deg": angles_deg, + "pairwise_angles_deg": pairwise_angles_deg + } + + print("\n所有图像处理完成!") + return angle_distribution + + +# ------------------- 测试调用 ------------------- +if __name__ == "__main__": + MODEL_PATH = r'best1.pt' + IMAGE_SOURCE_DIR = r"/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/obb3/train" + + distribution = process_obb_images_for_angle_distribution(MODEL_PATH, IMAGE_SOURCE_DIR) + print("\n夹角分布统计:") + print(f"小于6度的夹角数量: {distribution['<6']}") + print(f"在6至20度之间的夹角数量: {distribution['6-20']}") + print(f"大于20度的夹角数量: {distribution['>20']}") \ No newline at end of file diff --git a/angle_base_obb/error_E.py b/angle_base_obb/view_E.py similarity index 66% rename from angle_base_obb/error_E.py rename to angle_base_obb/view_E.py index 8ea8ddb..dde0066 100644 --- a/angle_base_obb/error_E.py +++ b/angle_base_obb/view_E.py @@ -2,15 +2,18 @@ import os import cv2 import numpy as np from ultralytics import YOLO +import matplotlib.pyplot as plt # ================== 配置参数 ================== -MODEL_PATH = r"/home/hx/yolo/ultralytics_yolo11-main/runs/train/exp_obb4/weights/best.pt" -IMAGE_SOURCE_DIR = r"/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/obb2/test" +MODEL_PATH = r"obb.pt" +IMAGE_SOURCE_DIR = r"/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/obb4/train" LABEL_SOURCE_DIR = IMAGE_SOURCE_DIR # 假设标签和图像在同一目录 OUTPUT_DIR = "./inference_results" - +VISUAL_DIR = os.path.join(OUTPUT_DIR, "visual_errors_gt5deg") # 保存误差 >5° 的可视化图 IMG_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.bmp', '.tif', '.tiff', '.webp'} + os.makedirs(OUTPUT_DIR, exist_ok=True) +os.makedirs(VISUAL_DIR, exist_ok=True) # 加载模型 print("🔄 加载 YOLO OBB 模型...") @@ -27,32 +30,38 @@ if not image_files: print(f"❌ 错误:未找到图像文件") exit(1) print(f"📁 发现 {len(image_files)} 张图像待处理") + all_angle_errors = [] # 存储每张图的夹角误差(度) # ================== 工具函数 ================== -def parse_obb_label_file(label_path): - """解析 OBB 标签文件,返回 [{'cls': int, 'points': (4,2)}]""" +def parse_obb_label_file(label_path, img_shape): + """ + 解析 OBB 标签文件,并将归一化坐标转换为像素坐标 + img_shape: (height, width) 用于去归一化 + """ boxes = [] + h, w = img_shape[:2] if not os.path.exists(label_path): + print(f"⚠️ 标签文件不存在: {label_path}") return boxes with open(label_path, 'r') as f: for line in f: parts = line.strip().split() if len(parts) != 9: + print(f"⚠️ 标签行格式错误 (期望9列): {parts}") continue cls_id = int(parts[0]) coords = list(map(float, parts[1:])) points = np.array(coords).reshape(4, 2) + points[:, 0] *= w # x * width + points[:, 1] *= h # y * height boxes.append({'cls': cls_id, 'points': points}) return boxes def compute_main_direction(points): - """ - 根据四个顶点计算旋转框的主方向(长边方向), - 返回 [0, π) 范围内的弧度值。 - """ + """根据四个顶点计算旋转框的主方向(长边方向),返回 [0, π) 范围内的弧度值""" edges = [] for i in range(4): p1 = points[i] @@ -65,11 +74,8 @@ def compute_main_direction(points): if not edges: return 0.0 - # 找最长边 longest_edge = max(edges, key=lambda x: x[0])[1] angle_rad = np.arctan2(longest_edge[1], longest_edge[0]) - - # 归一化到 [0, π) angle_rad = angle_rad % np.pi return angle_rad @@ -78,9 +84,29 @@ def compute_min_angle_between_two_dirs(dir1_rad, dir2_rad): """计算两个方向之间的最小夹角(0 ~ 90°),返回角度制""" diff = abs(dir1_rad - dir2_rad) min_diff_rad = min(diff, np.pi - diff) - return np.degrees(min_diff_rad) # 返回 0~90° + return np.degrees(min_diff_rad) +def draw_boxes_on_image(image, pred_boxes=None, true_boxes=None): + """在图像上绘制预测框(绿色)和真实框(红色)""" + img_vis = image.copy() + + # 绘制真实框(红色) + if true_boxes is not None: + for box in true_boxes: + pts = np.int32(box['points']).reshape((-1, 1, 2)) + cv2.polylines(img_vis, [pts], isClosed=True, color=(0, 0, 255), thickness=2) + + # 绘制预测框(绿色) + if pred_boxes is not None: + for box in pred_boxes: + xyxyxyxy = box.xyxyxyxy.cpu().numpy()[0] + pts = xyxyxyxy.reshape(4, 2).astype(int) + pts = pts.reshape((-1, 1, 2)) + cv2.polylines(img_vis, [pts], isClosed=True, color=(0, 255, 0), thickness=2) + + return img_vis + # ================== 主循环 ================== for img_filename in image_files: img_path = os.path.join(IMAGE_SOURCE_DIR, img_filename) @@ -102,7 +128,7 @@ for img_filename in image_files: # === 提取预测框主方向 === pred_dirs = [] if pred_boxes is not None and len(pred_boxes) >= 2: - for box in pred_boxes[:2]: # 只取前两个 + for box in pred_boxes[:2]: xywhr = box.xywhr.cpu().numpy()[0] cx, cy, w, h, r_rad = xywhr main_dir = r_rad if w >= h else r_rad + np.pi / 2 @@ -113,13 +139,13 @@ for img_filename in image_files: continue # === 提取真实框主方向 === - true_boxes = parse_obb_label_file(label_path) + true_boxes = parse_obb_label_file(label_path, img.shape) if len(true_boxes) < 2: print("❌ 标签框不足两个") continue true_dirs = [] - for tb in true_boxes[:2]: # 取前两个 + for tb in true_boxes[:2]: d = compute_main_direction(tb['points']) true_dirs.append(d) true_angle = compute_min_angle_between_two_dirs(true_dirs[0], true_dirs[1]) @@ -132,6 +158,17 @@ for img_filename in image_files: print(f" 🔹 真实夹角: {true_angle:.2f}°") print(f" 🔺 夹角误差: {error_deg:.2f}°") + # === 可视化误差 >5° 的情况 === + if error_deg > 3: + print(f" 🎯 误差 >5°,生成可视化图像...") + img_with_boxes = draw_boxes_on_image(img, pred_boxes=pred_boxes, true_boxes=true_boxes) + # 添加文字 + cv2.putText(img_with_boxes, f"Error: {error_deg:.2f}°", (20, 50), + cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA) + vis_output_path = os.path.join(VISUAL_DIR, f"error_{error_deg:.2f}deg_{img_filename}") + cv2.imwrite(vis_output_path, img_with_boxes) + print(f" ✅ 已保存可视化图像: {vis_output_path}") + # ================== 输出统计 ================== print("\n" + "=" * 60) print("📊 夹角误差统计(基于两框间最小夹角)") diff --git a/camera/siyi_caiji.py b/camera/siyi_caiji.py new file mode 100644 index 0000000..778ab6e --- /dev/null +++ b/camera/siyi_caiji.py @@ -0,0 +1,137 @@ +import cv2 +import time +import os +import numpy as np +from PIL import Image +from skimage.metrics import structural_similarity as ssim +import shutil + +# ================== 配置参数 ================== +rtsp_url = "rtsp://192.168.144.25:8554/main.264" # RTSP 流地址 +capture_interval = 1.0 # 每隔多少秒采集一次(单位:秒) +SSIM_THRESHOLD = 0.9 # SSIM 相似度阈值,>0.9 认为太像 +output_dir = os.path.join("userdata", "image") # 图片保存路径 + +# 灰色判断参数 +GRAY_LOWER = 70 +GRAY_UPPER = 230 +GRAY_RATIO_THRESHOLD = 0.7 + +# 创建输出目录 +if not os.path.exists(output_dir): + os.makedirs(output_dir) + print(f"已创建目录: {output_dir}") + +def is_large_gray(image, gray_lower=GRAY_LOWER, gray_upper=GRAY_UPPER, ratio_thresh=GRAY_RATIO_THRESHOLD): + """ + 判断图片是否大面积为灰色(R/G/B 都在 [gray_lower, gray_upper] 区间) + """ + img_array = np.array(image) + if len(img_array.shape) != 3 or img_array.shape[2] != 3: + return True # 非三通道图视为无效/灰色 + + h, w, _ = img_array.shape + total = h * w + + gray_mask = ( + (img_array[:, :, 0] >= gray_lower) & (img_array[:, :, 0] <= gray_upper) & + (img_array[:, :, 1] >= gray_lower) & (img_array[:, :, 1] <= gray_upper) & + (img_array[:, :, 2] >= gray_lower) & (img_array[:, :, 2] <= gray_upper) + ) + gray_pixels = np.sum(gray_mask) + gray_ratio = gray_pixels / total + + return gray_ratio > ratio_thresh + +max_retry_seconds = 10 # 最大重试时间为10秒 +retry_interval_seconds = 1 # 每隔1秒尝试重新连接一次 + +last_gray = None # 用于 SSIM 去重 + +while True: # 外层循环用于处理重新连接逻辑 + cap = cv2.VideoCapture(rtsp_url) + start_time = time.time() # 记录开始尝试连接的时间 + + while not cap.isOpened(): + if time.time() - start_time >= max_retry_seconds: + print(f"已尝试重新连接 {max_retry_seconds} 秒,但仍无法获取视频流。") + exit() + + print("无法打开摄像头,正在尝试重新连接...") + time.sleep(retry_interval_seconds) + cap = cv2.VideoCapture(rtsp_url) + + print("✅ 开始读取视频流...") + + last_capture_time = time.time() + frame_count = 0 + + try: + while True: + ret, frame = cap.read() + if not ret: + print("读取帧失败,可能是流中断或摄像头断开") + cap.release() + break + + current_time = time.time() + if current_time - last_capture_time < capture_interval: + continue + + frame_count += 1 + last_capture_time = current_time + + print(f"处理帧 {frame_count}") + + rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + pil_image = Image.fromarray(rgb_frame) + + if is_large_gray(pil_image): + print(f"跳过:大面积灰色图像 (frame_{frame_count})") + continue + + gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + if last_gray is not None: + similarity = ssim(gray, last_gray) + if similarity > SSIM_THRESHOLD: + print(f"跳过:与上一帧太相似 (SSIM={similarity:.3f})") + continue + + last_gray = gray.copy() + + timestamp = time.strftime("%Y%m%d_%H%M%S") + ms = int((time.time() % 1) * 1000) + filename = f"frame_{timestamp}_{ms:03d}.png" + filepath = os.path.join(output_dir, filename) + + total, used, free = shutil.disk_usage(output_dir) + if free < 1024 * 1024 * 20: # 小于 20MB 就停止 + print(f"❌ 磁盘空间严重不足(仅剩 {free / (1024**3):.2f} GB),停止运行。") + raise SystemExit(1) + + try: + pil_image.save(filepath, format='PNG') + print(f"已保存: {filepath}") + except (OSError, IOError) as e: + error_msg = str(e) + if "No space left on device" in error_msg or "disk full" in error_msg.lower() or "quota" in error_msg.lower(): + print(f"磁盘空间不足,无法保存 {filepath}!错误: {e}") + print("停止程序以防止无限错误。") + raise SystemExit(1) + else: + print(f"保存失败 {filename}: {e}(非磁盘空间问题,继续运行)") + + cv2.imshow('Camera Stream (Live)', frame) + if cv2.waitKey(1) & 0xFF == ord('q'): + raise KeyboardInterrupt + + except KeyboardInterrupt: + print("\n用户中断") + break + + finally: + cap.release() + cv2.destroyAllWindows() + print(f"视频流已关闭,共处理 {frame_count} 帧。") + +print("程序结束") \ No newline at end of file diff --git a/camera/siyi_rtsp.py b/camera/siyi_rtsp.py new file mode 100644 index 0000000..168440f --- /dev/null +++ b/camera/siyi_rtsp.py @@ -0,0 +1,32 @@ +import cv2 + +def read_rtsp(rtsp_url): + # 创建 VideoCapture 对象 + cap = cv2.VideoCapture(rtsp_url) + # 判断是否成功连接到 RTSP 流 + if not cap.isOpened(): + print("无法连接到 RTSP 流") + return + + try: + while True: + # 读取一帧 + ret, frame = cap.read() + if not ret: + print("无法读取帧") + break + + # 显示帧 + cv2.imshow('frame', frame) + + # 按下 'q' 键退出 + if cv2.waitKey(1) & 0xFF == ord('q'): + break + finally: + # 释放资源 + cap.release() + cv2.destroyAllWindows() + +if __name__ == '__main__': + rtsp_url = "rtsp://192.168.144.25:8554/main.264" # 替换为实际的 RTSP 地址 + read_rtsp(rtsp_url) \ No newline at end of file diff --git a/camera/userdata/image/frame_20251016_102204_124.png b/camera/userdata/image/frame_20251016_102204_124.png new file mode 100644 index 0000000..14cb641 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102204_124.png differ diff --git a/camera/userdata/image/frame_20251016_102205_189.png b/camera/userdata/image/frame_20251016_102205_189.png new file mode 100644 index 0000000..a12c36a Binary files /dev/null and b/camera/userdata/image/frame_20251016_102205_189.png differ diff --git a/camera/userdata/image/frame_20251016_102207_222.png b/camera/userdata/image/frame_20251016_102207_222.png new file mode 100644 index 0000000..f195112 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102207_222.png differ diff --git a/camera/userdata/image/frame_20251016_102208_221.png b/camera/userdata/image/frame_20251016_102208_221.png new file mode 100644 index 0000000..bc4a588 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102208_221.png differ diff --git a/camera/userdata/image/frame_20251016_102210_302.png b/camera/userdata/image/frame_20251016_102210_302.png new file mode 100644 index 0000000..286e01d Binary files /dev/null and b/camera/userdata/image/frame_20251016_102210_302.png differ diff --git a/camera/userdata/image/frame_20251016_102211_347.png b/camera/userdata/image/frame_20251016_102211_347.png new file mode 100644 index 0000000..e56208a Binary files /dev/null and b/camera/userdata/image/frame_20251016_102211_347.png differ diff --git a/camera/userdata/image/frame_20251016_102212_386.png b/camera/userdata/image/frame_20251016_102212_386.png new file mode 100644 index 0000000..c4a5aa8 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102212_386.png differ diff --git a/camera/userdata/image/frame_20251016_102243_374.png b/camera/userdata/image/frame_20251016_102243_374.png new file mode 100644 index 0000000..d1619ad Binary files /dev/null and b/camera/userdata/image/frame_20251016_102243_374.png differ diff --git a/camera/userdata/image/frame_20251016_102331_476.png b/camera/userdata/image/frame_20251016_102331_476.png new file mode 100644 index 0000000..11bd158 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102331_476.png differ diff --git a/camera/userdata/image/frame_20251016_102332_545.png b/camera/userdata/image/frame_20251016_102332_545.png new file mode 100644 index 0000000..5e2e0fd Binary files /dev/null and b/camera/userdata/image/frame_20251016_102332_545.png differ diff --git a/camera/userdata/image/frame_20251016_102333_543.png b/camera/userdata/image/frame_20251016_102333_543.png new file mode 100644 index 0000000..e131365 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102333_543.png differ diff --git a/camera/userdata/image/frame_20251016_102334_581.png b/camera/userdata/image/frame_20251016_102334_581.png new file mode 100644 index 0000000..208e493 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102334_581.png differ diff --git a/camera/userdata/image/frame_20251016_102335_583.png b/camera/userdata/image/frame_20251016_102335_583.png new file mode 100644 index 0000000..7101092 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102335_583.png differ diff --git a/camera/userdata/image/frame_20251016_102336_582.png b/camera/userdata/image/frame_20251016_102336_582.png new file mode 100644 index 0000000..ecbd493 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102336_582.png differ diff --git a/camera/userdata/image/frame_20251016_102337_658.png b/camera/userdata/image/frame_20251016_102337_658.png new file mode 100644 index 0000000..602a522 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102337_658.png differ diff --git a/camera/userdata/image/frame_20251016_102338_695.png b/camera/userdata/image/frame_20251016_102338_695.png new file mode 100644 index 0000000..737e022 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102338_695.png differ diff --git a/camera/userdata/image/frame_20251016_102339_703.png b/camera/userdata/image/frame_20251016_102339_703.png new file mode 100644 index 0000000..9a57dea Binary files /dev/null and b/camera/userdata/image/frame_20251016_102339_703.png differ diff --git a/camera/userdata/image/frame_20251016_102340_700.png b/camera/userdata/image/frame_20251016_102340_700.png new file mode 100644 index 0000000..6c4b7ea Binary files /dev/null and b/camera/userdata/image/frame_20251016_102340_700.png differ diff --git a/camera/userdata/image/frame_20251016_102341_742.png b/camera/userdata/image/frame_20251016_102341_742.png new file mode 100644 index 0000000..ef96fff Binary files /dev/null and b/camera/userdata/image/frame_20251016_102341_742.png differ diff --git a/camera/userdata/image/frame_20251016_102342_740.png b/camera/userdata/image/frame_20251016_102342_740.png new file mode 100644 index 0000000..6cde715 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102342_740.png differ diff --git a/camera/userdata/image/frame_20251016_102343_738.png b/camera/userdata/image/frame_20251016_102343_738.png new file mode 100644 index 0000000..c1dc633 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102343_738.png differ diff --git a/camera/userdata/image/frame_20251016_102344_785.png b/camera/userdata/image/frame_20251016_102344_785.png new file mode 100644 index 0000000..3aa2925 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102344_785.png differ diff --git a/camera/userdata/image/frame_20251016_102345_786.png b/camera/userdata/image/frame_20251016_102345_786.png new file mode 100644 index 0000000..a08e93b Binary files /dev/null and b/camera/userdata/image/frame_20251016_102345_786.png differ diff --git a/camera/userdata/image/frame_20251016_102346_824.png b/camera/userdata/image/frame_20251016_102346_824.png new file mode 100644 index 0000000..859e040 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102346_824.png differ diff --git a/camera/userdata/image/frame_20251016_102347_821.png b/camera/userdata/image/frame_20251016_102347_821.png new file mode 100644 index 0000000..d2e7076 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102347_821.png differ diff --git a/camera/userdata/image/frame_20251016_102348_857.png b/camera/userdata/image/frame_20251016_102348_857.png new file mode 100644 index 0000000..7e0e26a Binary files /dev/null and b/camera/userdata/image/frame_20251016_102348_857.png differ diff --git a/camera/userdata/image/frame_20251016_102349_902.png b/camera/userdata/image/frame_20251016_102349_902.png new file mode 100644 index 0000000..fde3e02 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102349_902.png differ diff --git a/camera/userdata/image/frame_20251016_102351_983.png b/camera/userdata/image/frame_20251016_102351_983.png new file mode 100644 index 0000000..bb80226 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102351_983.png differ diff --git a/camera/userdata/image/frame_20251016_102353_024.png b/camera/userdata/image/frame_20251016_102353_024.png new file mode 100644 index 0000000..14f16a1 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102353_024.png differ diff --git a/camera/userdata/image/frame_20251016_102354_059.png b/camera/userdata/image/frame_20251016_102354_059.png new file mode 100644 index 0000000..1837073 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102354_059.png differ diff --git a/camera/userdata/image/frame_20251016_102355_101.png b/camera/userdata/image/frame_20251016_102355_101.png new file mode 100644 index 0000000..c5254ca Binary files /dev/null and b/camera/userdata/image/frame_20251016_102355_101.png differ diff --git a/camera/userdata/image/frame_20251016_102356_138.png b/camera/userdata/image/frame_20251016_102356_138.png new file mode 100644 index 0000000..faae6c4 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102356_138.png differ diff --git a/camera/userdata/image/frame_20251016_102357_184.png b/camera/userdata/image/frame_20251016_102357_184.png new file mode 100644 index 0000000..645ed6b Binary files /dev/null and b/camera/userdata/image/frame_20251016_102357_184.png differ diff --git a/camera/userdata/image/frame_20251016_102358_224.png b/camera/userdata/image/frame_20251016_102358_224.png new file mode 100644 index 0000000..8a70048 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102358_224.png differ diff --git a/camera/userdata/image/frame_20251016_102359_259.png b/camera/userdata/image/frame_20251016_102359_259.png new file mode 100644 index 0000000..3dccf3e Binary files /dev/null and b/camera/userdata/image/frame_20251016_102359_259.png differ diff --git a/camera/userdata/image/frame_20251016_102400_300.png b/camera/userdata/image/frame_20251016_102400_300.png new file mode 100644 index 0000000..c4bdb64 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102400_300.png differ diff --git a/camera/userdata/image/frame_20251016_102401_345.png b/camera/userdata/image/frame_20251016_102401_345.png new file mode 100644 index 0000000..bd754ad Binary files /dev/null and b/camera/userdata/image/frame_20251016_102401_345.png differ diff --git a/camera/userdata/image/frame_20251016_102402_388.png b/camera/userdata/image/frame_20251016_102402_388.png new file mode 100644 index 0000000..a56cb16 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102402_388.png differ diff --git a/camera/userdata/image/frame_20251016_102403_419.png b/camera/userdata/image/frame_20251016_102403_419.png new file mode 100644 index 0000000..9f0a4d1 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102403_419.png differ diff --git a/camera/userdata/image/frame_20251016_102404_460.png b/camera/userdata/image/frame_20251016_102404_460.png new file mode 100644 index 0000000..67dcfa6 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102404_460.png differ diff --git a/camera/userdata/image/frame_20251016_102405_498.png b/camera/userdata/image/frame_20251016_102405_498.png new file mode 100644 index 0000000..036d310 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102405_498.png differ diff --git a/camera/userdata/image/frame_20251016_102406_543.png b/camera/userdata/image/frame_20251016_102406_543.png new file mode 100644 index 0000000..8022a34 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102406_543.png differ diff --git a/camera/userdata/image/frame_20251016_102407_577.png b/camera/userdata/image/frame_20251016_102407_577.png new file mode 100644 index 0000000..184ecd4 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102407_577.png differ diff --git a/camera/userdata/image/frame_20251016_102408_585.png b/camera/userdata/image/frame_20251016_102408_585.png new file mode 100644 index 0000000..7c8c3ff Binary files /dev/null and b/camera/userdata/image/frame_20251016_102408_585.png differ diff --git a/camera/userdata/image/frame_20251016_102409_658.png b/camera/userdata/image/frame_20251016_102409_658.png new file mode 100644 index 0000000..34fa7ff Binary files /dev/null and b/camera/userdata/image/frame_20251016_102409_658.png differ diff --git a/camera/userdata/image/frame_20251016_102410_697.png b/camera/userdata/image/frame_20251016_102410_697.png new file mode 100644 index 0000000..f267012 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102410_697.png differ diff --git a/camera/userdata/image/frame_20251016_102411_743.png b/camera/userdata/image/frame_20251016_102411_743.png new file mode 100644 index 0000000..03fb9a3 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102411_743.png differ diff --git a/camera/userdata/image/frame_20251016_102412_777.png b/camera/userdata/image/frame_20251016_102412_777.png new file mode 100644 index 0000000..00b025c Binary files /dev/null and b/camera/userdata/image/frame_20251016_102412_777.png differ diff --git a/camera/userdata/image/frame_20251016_102413_814.png b/camera/userdata/image/frame_20251016_102413_814.png new file mode 100644 index 0000000..4db761b Binary files /dev/null and b/camera/userdata/image/frame_20251016_102413_814.png differ diff --git a/camera/userdata/image/frame_20251016_102414_916.png b/camera/userdata/image/frame_20251016_102414_916.png new file mode 100644 index 0000000..e5e3b06 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102414_916.png differ diff --git a/camera/userdata/image/frame_20251016_102445_808.png b/camera/userdata/image/frame_20251016_102445_808.png new file mode 100644 index 0000000..9683860 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102445_808.png differ diff --git a/camera/userdata/image/frame_20251016_102518_932.png b/camera/userdata/image/frame_20251016_102518_932.png new file mode 100644 index 0000000..23fa883 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102518_932.png differ diff --git a/camera/userdata/image/frame_20251016_102520_009.png b/camera/userdata/image/frame_20251016_102520_009.png new file mode 100644 index 0000000..eb79f77 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102520_009.png differ diff --git a/camera/userdata/image/frame_20251016_102521_044.png b/camera/userdata/image/frame_20251016_102521_044.png new file mode 100644 index 0000000..528d1ee Binary files /dev/null and b/camera/userdata/image/frame_20251016_102521_044.png differ diff --git a/camera/userdata/image/frame_20251016_102522_086.png b/camera/userdata/image/frame_20251016_102522_086.png new file mode 100644 index 0000000..6a5fdc1 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102522_086.png differ diff --git a/camera/userdata/image/frame_20251016_102523_081.png b/camera/userdata/image/frame_20251016_102523_081.png new file mode 100644 index 0000000..4c8ca30 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102523_081.png differ diff --git a/camera/userdata/image/frame_20251016_102524_162.png b/camera/userdata/image/frame_20251016_102524_162.png new file mode 100644 index 0000000..6e64753 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102524_162.png differ diff --git a/camera/userdata/image/frame_20251016_102525_161.png b/camera/userdata/image/frame_20251016_102525_161.png new file mode 100644 index 0000000..a9c3eed Binary files /dev/null and b/camera/userdata/image/frame_20251016_102525_161.png differ diff --git a/camera/userdata/image/frame_20251016_102526_160.png b/camera/userdata/image/frame_20251016_102526_160.png new file mode 100644 index 0000000..f0d9416 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102526_160.png differ diff --git a/camera/userdata/image/frame_20251016_102527_200.png b/camera/userdata/image/frame_20251016_102527_200.png new file mode 100644 index 0000000..5786160 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102527_200.png differ diff --git a/camera/userdata/image/frame_20251016_102528_200.png b/camera/userdata/image/frame_20251016_102528_200.png new file mode 100644 index 0000000..340dbcb Binary files /dev/null and b/camera/userdata/image/frame_20251016_102528_200.png differ diff --git a/camera/userdata/image/frame_20251016_102529_205.png b/camera/userdata/image/frame_20251016_102529_205.png new file mode 100644 index 0000000..c66efbc Binary files /dev/null and b/camera/userdata/image/frame_20251016_102529_205.png differ diff --git a/camera/userdata/image/frame_20251016_102530_240.png b/camera/userdata/image/frame_20251016_102530_240.png new file mode 100644 index 0000000..651f183 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102530_240.png differ diff --git a/camera/userdata/image/frame_20251016_102531_238.png b/camera/userdata/image/frame_20251016_102531_238.png new file mode 100644 index 0000000..dc549c3 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102531_238.png differ diff --git a/camera/userdata/image/frame_20251016_102532_277.png b/camera/userdata/image/frame_20251016_102532_277.png new file mode 100644 index 0000000..9f88603 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102532_277.png differ diff --git a/camera/userdata/image/frame_20251016_102533_278.png b/camera/userdata/image/frame_20251016_102533_278.png new file mode 100644 index 0000000..3522a9e Binary files /dev/null and b/camera/userdata/image/frame_20251016_102533_278.png differ diff --git a/camera/userdata/image/frame_20251016_102535_317.png b/camera/userdata/image/frame_20251016_102535_317.png new file mode 100644 index 0000000..5e869fb Binary files /dev/null and b/camera/userdata/image/frame_20251016_102535_317.png differ diff --git a/camera/userdata/image/frame_20251016_102536_363.png b/camera/userdata/image/frame_20251016_102536_363.png new file mode 100644 index 0000000..5acc6ab Binary files /dev/null and b/camera/userdata/image/frame_20251016_102536_363.png differ diff --git a/camera/userdata/image/frame_20251016_102537_365.png b/camera/userdata/image/frame_20251016_102537_365.png new file mode 100644 index 0000000..874eadc Binary files /dev/null and b/camera/userdata/image/frame_20251016_102537_365.png differ diff --git a/camera/userdata/image/frame_20251016_102538_399.png b/camera/userdata/image/frame_20251016_102538_399.png new file mode 100644 index 0000000..2ed9a9a Binary files /dev/null and b/camera/userdata/image/frame_20251016_102538_399.png differ diff --git a/camera/userdata/image/frame_20251016_102714_874.png b/camera/userdata/image/frame_20251016_102714_874.png new file mode 100644 index 0000000..c6c6397 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102714_874.png differ diff --git a/camera/userdata/image/frame_20251016_102726_211.png b/camera/userdata/image/frame_20251016_102726_211.png new file mode 100644 index 0000000..a06d6bd Binary files /dev/null and b/camera/userdata/image/frame_20251016_102726_211.png differ diff --git a/camera/userdata/image/frame_20251016_102727_213.png b/camera/userdata/image/frame_20251016_102727_213.png new file mode 100644 index 0000000..fc8712d Binary files /dev/null and b/camera/userdata/image/frame_20251016_102727_213.png differ diff --git a/camera/userdata/image/frame_20251016_102728_214.png b/camera/userdata/image/frame_20251016_102728_214.png new file mode 100644 index 0000000..970d5b0 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102728_214.png differ diff --git a/camera/userdata/image/frame_20251016_102731_294.png b/camera/userdata/image/frame_20251016_102731_294.png new file mode 100644 index 0000000..9e7ec32 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102731_294.png differ diff --git a/camera/userdata/image/frame_20251016_102732_293.png b/camera/userdata/image/frame_20251016_102732_293.png new file mode 100644 index 0000000..d8f0fad Binary files /dev/null and b/camera/userdata/image/frame_20251016_102732_293.png differ diff --git a/camera/userdata/image/frame_20251016_102733_334.png b/camera/userdata/image/frame_20251016_102733_334.png new file mode 100644 index 0000000..79ae52c Binary files /dev/null and b/camera/userdata/image/frame_20251016_102733_334.png differ diff --git a/camera/userdata/image/frame_20251016_102734_370.png b/camera/userdata/image/frame_20251016_102734_370.png new file mode 100644 index 0000000..cfe6d42 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102734_370.png differ diff --git a/camera/userdata/image/frame_20251016_102735_381.png b/camera/userdata/image/frame_20251016_102735_381.png new file mode 100644 index 0000000..d6d24a3 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102735_381.png differ diff --git a/camera/userdata/image/frame_20251016_102736_414.png b/camera/userdata/image/frame_20251016_102736_414.png new file mode 100644 index 0000000..e8d6a65 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102736_414.png differ diff --git a/camera/userdata/image/frame_20251016_102737_452.png b/camera/userdata/image/frame_20251016_102737_452.png new file mode 100644 index 0000000..30dcb92 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102737_452.png differ diff --git a/camera/userdata/image/frame_20251016_102739_496.png b/camera/userdata/image/frame_20251016_102739_496.png new file mode 100644 index 0000000..a4b9955 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102739_496.png differ diff --git a/camera/userdata/image/frame_20251016_102740_533.png b/camera/userdata/image/frame_20251016_102740_533.png new file mode 100644 index 0000000..5a37c66 Binary files /dev/null and b/camera/userdata/image/frame_20251016_102740_533.png differ diff --git a/camera/userdata/image/frame_20251016_102741_535.png b/camera/userdata/image/frame_20251016_102741_535.png new file mode 100644 index 0000000..95e403f Binary files /dev/null and b/camera/userdata/image/frame_20251016_102741_535.png differ diff --git a/camera/userdata/image/frame_20251016_103115_945.png b/camera/userdata/image/frame_20251016_103115_945.png new file mode 100644 index 0000000..9fe2f63 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103115_945.png differ diff --git a/camera/userdata/image/frame_20251016_103119_046.png b/camera/userdata/image/frame_20251016_103119_046.png new file mode 100644 index 0000000..340c0e5 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103119_046.png differ diff --git a/camera/userdata/image/frame_20251016_103121_126.png b/camera/userdata/image/frame_20251016_103121_126.png new file mode 100644 index 0000000..b6cef79 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103121_126.png differ diff --git a/camera/userdata/image/frame_20251016_103135_366.png b/camera/userdata/image/frame_20251016_103135_366.png new file mode 100644 index 0000000..de9939b Binary files /dev/null and b/camera/userdata/image/frame_20251016_103135_366.png differ diff --git a/camera/userdata/image/frame_20251016_103137_407.png b/camera/userdata/image/frame_20251016_103137_407.png new file mode 100644 index 0000000..7b8bfe7 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103137_407.png differ diff --git a/camera/userdata/image/frame_20251016_103138_448.png b/camera/userdata/image/frame_20251016_103138_448.png new file mode 100644 index 0000000..aeedc84 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103138_448.png differ diff --git a/camera/userdata/image/frame_20251016_103139_482.png b/camera/userdata/image/frame_20251016_103139_482.png new file mode 100644 index 0000000..97475b1 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103139_482.png differ diff --git a/camera/userdata/image/frame_20251016_103140_488.png b/camera/userdata/image/frame_20251016_103140_488.png new file mode 100644 index 0000000..8248ee0 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103140_488.png differ diff --git a/camera/userdata/image/frame_20251016_103141_491.png b/camera/userdata/image/frame_20251016_103141_491.png new file mode 100644 index 0000000..3e25028 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103141_491.png differ diff --git a/camera/userdata/image/frame_20251016_103142_527.png b/camera/userdata/image/frame_20251016_103142_527.png new file mode 100644 index 0000000..3e846ca Binary files /dev/null and b/camera/userdata/image/frame_20251016_103142_527.png differ diff --git a/camera/userdata/image/frame_20251016_103143_566.png b/camera/userdata/image/frame_20251016_103143_566.png new file mode 100644 index 0000000..ae64cc0 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103143_566.png differ diff --git a/camera/userdata/image/frame_20251016_103144_605.png b/camera/userdata/image/frame_20251016_103144_605.png new file mode 100644 index 0000000..7cdc9b2 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103144_605.png differ diff --git a/camera/userdata/image/frame_20251016_103145_653.png b/camera/userdata/image/frame_20251016_103145_653.png new file mode 100644 index 0000000..f1a82f2 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103145_653.png differ diff --git a/camera/userdata/image/frame_20251016_103146_687.png b/camera/userdata/image/frame_20251016_103146_687.png new file mode 100644 index 0000000..b15ec76 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103146_687.png differ diff --git a/camera/userdata/image/frame_20251016_103147_684.png b/camera/userdata/image/frame_20251016_103147_684.png new file mode 100644 index 0000000..db71c66 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103147_684.png differ diff --git a/camera/userdata/image/frame_20251016_103148_689.png b/camera/userdata/image/frame_20251016_103148_689.png new file mode 100644 index 0000000..a356fa0 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103148_689.png differ diff --git a/camera/userdata/image/frame_20251016_103149_729.png b/camera/userdata/image/frame_20251016_103149_729.png new file mode 100644 index 0000000..e7633df Binary files /dev/null and b/camera/userdata/image/frame_20251016_103149_729.png differ diff --git a/camera/userdata/image/frame_20251016_103150_769.png b/camera/userdata/image/frame_20251016_103150_769.png new file mode 100644 index 0000000..51a11ae Binary files /dev/null and b/camera/userdata/image/frame_20251016_103150_769.png differ diff --git a/camera/userdata/image/frame_20251016_103151_809.png b/camera/userdata/image/frame_20251016_103151_809.png new file mode 100644 index 0000000..28b7e94 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103151_809.png differ diff --git a/camera/userdata/image/frame_20251016_103152_854.png b/camera/userdata/image/frame_20251016_103152_854.png new file mode 100644 index 0000000..409b3e5 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103152_854.png differ diff --git a/camera/userdata/image/frame_20251016_103153_891.png b/camera/userdata/image/frame_20251016_103153_891.png new file mode 100644 index 0000000..9a546e2 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103153_891.png differ diff --git a/camera/userdata/image/frame_20251016_103154_927.png b/camera/userdata/image/frame_20251016_103154_927.png new file mode 100644 index 0000000..3828733 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103154_927.png differ diff --git a/camera/userdata/image/frame_20251016_103155_978.png b/camera/userdata/image/frame_20251016_103155_978.png new file mode 100644 index 0000000..862bfd2 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103155_978.png differ diff --git a/camera/userdata/image/frame_20251016_103157_010.png b/camera/userdata/image/frame_20251016_103157_010.png new file mode 100644 index 0000000..9ba2f32 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103157_010.png differ diff --git a/camera/userdata/image/frame_20251016_103158_049.png b/camera/userdata/image/frame_20251016_103158_049.png new file mode 100644 index 0000000..a1dd381 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103158_049.png differ diff --git a/camera/userdata/image/frame_20251016_103159_048.png b/camera/userdata/image/frame_20251016_103159_048.png new file mode 100644 index 0000000..6e0bfc0 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103159_048.png differ diff --git a/camera/userdata/image/frame_20251016_103201_130.png b/camera/userdata/image/frame_20251016_103201_130.png new file mode 100644 index 0000000..0079f22 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103201_130.png differ diff --git a/camera/userdata/image/frame_20251016_103203_166.png b/camera/userdata/image/frame_20251016_103203_166.png new file mode 100644 index 0000000..a972a78 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103203_166.png differ diff --git a/camera/userdata/image/frame_20251016_103206_254.png b/camera/userdata/image/frame_20251016_103206_254.png new file mode 100644 index 0000000..9c52053 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103206_254.png differ diff --git a/camera/userdata/image/frame_20251016_103209_292.png b/camera/userdata/image/frame_20251016_103209_292.png new file mode 100644 index 0000000..d27c17f Binary files /dev/null and b/camera/userdata/image/frame_20251016_103209_292.png differ diff --git a/camera/userdata/image/frame_20251016_103211_324.png b/camera/userdata/image/frame_20251016_103211_324.png new file mode 100644 index 0000000..f409c4d Binary files /dev/null and b/camera/userdata/image/frame_20251016_103211_324.png differ diff --git a/camera/userdata/image/frame_20251016_103214_410.png b/camera/userdata/image/frame_20251016_103214_410.png new file mode 100644 index 0000000..1242bb8 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103214_410.png differ diff --git a/camera/userdata/image/frame_20251016_103217_450.png b/camera/userdata/image/frame_20251016_103217_450.png new file mode 100644 index 0000000..5858572 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103217_450.png differ diff --git a/camera/userdata/image/frame_20251016_103220_524.png b/camera/userdata/image/frame_20251016_103220_524.png new file mode 100644 index 0000000..2fee56a Binary files /dev/null and b/camera/userdata/image/frame_20251016_103220_524.png differ diff --git a/camera/userdata/image/frame_20251016_103223_606.png b/camera/userdata/image/frame_20251016_103223_606.png new file mode 100644 index 0000000..beba7ac Binary files /dev/null and b/camera/userdata/image/frame_20251016_103223_606.png differ diff --git a/camera/userdata/image/frame_20251016_103227_767.png b/camera/userdata/image/frame_20251016_103227_767.png new file mode 100644 index 0000000..6881b37 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103227_767.png differ diff --git a/camera/userdata/image/frame_20251016_103231_929.png b/camera/userdata/image/frame_20251016_103231_929.png new file mode 100644 index 0000000..e9da601 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103231_929.png differ diff --git a/camera/userdata/image/frame_20251016_103237_006.png b/camera/userdata/image/frame_20251016_103237_006.png new file mode 100644 index 0000000..2c76c86 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103237_006.png differ diff --git a/camera/userdata/image/frame_20251016_103244_166.png b/camera/userdata/image/frame_20251016_103244_166.png new file mode 100644 index 0000000..06b2a17 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103244_166.png differ diff --git a/camera/userdata/image/frame_20251016_103248_254.png b/camera/userdata/image/frame_20251016_103248_254.png new file mode 100644 index 0000000..35a0948 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103248_254.png differ diff --git a/camera/userdata/image/frame_20251016_103249_252.png b/camera/userdata/image/frame_20251016_103249_252.png new file mode 100644 index 0000000..9faeb9d Binary files /dev/null and b/camera/userdata/image/frame_20251016_103249_252.png differ diff --git a/camera/userdata/image/frame_20251016_103255_372.png b/camera/userdata/image/frame_20251016_103255_372.png new file mode 100644 index 0000000..ef857de Binary files /dev/null and b/camera/userdata/image/frame_20251016_103255_372.png differ diff --git a/camera/userdata/image/frame_20251016_103259_519.png b/camera/userdata/image/frame_20251016_103259_519.png new file mode 100644 index 0000000..d045a0c Binary files /dev/null and b/camera/userdata/image/frame_20251016_103259_519.png differ diff --git a/camera/userdata/image/frame_20251016_103307_611.png b/camera/userdata/image/frame_20251016_103307_611.png new file mode 100644 index 0000000..2ee05ea Binary files /dev/null and b/camera/userdata/image/frame_20251016_103307_611.png differ diff --git a/camera/userdata/image/frame_20251016_103312_555.png b/camera/userdata/image/frame_20251016_103312_555.png new file mode 100644 index 0000000..5791860 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103312_555.png differ diff --git a/camera/userdata/image/frame_20251016_103321_851.png b/camera/userdata/image/frame_20251016_103321_851.png new file mode 100644 index 0000000..ef17679 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103321_851.png differ diff --git a/camera/userdata/image/frame_20251016_103722_125.png b/camera/userdata/image/frame_20251016_103722_125.png new file mode 100644 index 0000000..75befac Binary files /dev/null and b/camera/userdata/image/frame_20251016_103722_125.png differ diff --git a/camera/userdata/image/frame_20251016_103802_691.png b/camera/userdata/image/frame_20251016_103802_691.png new file mode 100644 index 0000000..6e3809a Binary files /dev/null and b/camera/userdata/image/frame_20251016_103802_691.png differ diff --git a/camera/userdata/image/frame_20251016_103803_758.png b/camera/userdata/image/frame_20251016_103803_758.png new file mode 100644 index 0000000..1ecdafc Binary files /dev/null and b/camera/userdata/image/frame_20251016_103803_758.png differ diff --git a/camera/userdata/image/frame_20251016_103804_788.png b/camera/userdata/image/frame_20251016_103804_788.png new file mode 100644 index 0000000..26a2ec8 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103804_788.png differ diff --git a/camera/userdata/image/frame_20251016_103805_787.png b/camera/userdata/image/frame_20251016_103805_787.png new file mode 100644 index 0000000..14a8056 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103805_787.png differ diff --git a/camera/userdata/image/frame_20251016_103806_793.png b/camera/userdata/image/frame_20251016_103806_793.png new file mode 100644 index 0000000..76a2daa Binary files /dev/null and b/camera/userdata/image/frame_20251016_103806_793.png differ diff --git a/camera/userdata/image/frame_20251016_103807_792.png b/camera/userdata/image/frame_20251016_103807_792.png new file mode 100644 index 0000000..d3d6fec Binary files /dev/null and b/camera/userdata/image/frame_20251016_103807_792.png differ diff --git a/camera/userdata/image/frame_20251016_103808_875.png b/camera/userdata/image/frame_20251016_103808_875.png new file mode 100644 index 0000000..fe16851 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103808_875.png differ diff --git a/camera/userdata/image/frame_20251016_103809_912.png b/camera/userdata/image/frame_20251016_103809_912.png new file mode 100644 index 0000000..f02eae2 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103809_912.png differ diff --git a/camera/userdata/image/frame_20251016_103810_955.png b/camera/userdata/image/frame_20251016_103810_955.png new file mode 100644 index 0000000..e6ddffd Binary files /dev/null and b/camera/userdata/image/frame_20251016_103810_955.png differ diff --git a/camera/userdata/image/frame_20251016_103811_991.png b/camera/userdata/image/frame_20251016_103811_991.png new file mode 100644 index 0000000..ed9eaf2 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103811_991.png differ diff --git a/camera/userdata/image/frame_20251016_103812_988.png b/camera/userdata/image/frame_20251016_103812_988.png new file mode 100644 index 0000000..2e577f2 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103812_988.png differ diff --git a/camera/userdata/image/frame_20251016_103814_030.png b/camera/userdata/image/frame_20251016_103814_030.png new file mode 100644 index 0000000..a2ac7f9 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103814_030.png differ diff --git a/camera/userdata/image/frame_20251016_103815_032.png b/camera/userdata/image/frame_20251016_103815_032.png new file mode 100644 index 0000000..2716fe8 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103815_032.png differ diff --git a/camera/userdata/image/frame_20251016_103818_110.png b/camera/userdata/image/frame_20251016_103818_110.png new file mode 100644 index 0000000..b0355f5 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103818_110.png differ diff --git a/camera/userdata/image/frame_20251016_103819_115.png b/camera/userdata/image/frame_20251016_103819_115.png new file mode 100644 index 0000000..25faf72 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103819_115.png differ diff --git a/camera/userdata/image/frame_20251016_103820_148.png b/camera/userdata/image/frame_20251016_103820_148.png new file mode 100644 index 0000000..a9f9a41 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103820_148.png differ diff --git a/camera/userdata/image/frame_20251016_103821_157.png b/camera/userdata/image/frame_20251016_103821_157.png new file mode 100644 index 0000000..1a29d2c Binary files /dev/null and b/camera/userdata/image/frame_20251016_103821_157.png differ diff --git a/camera/userdata/image/frame_20251016_103822_150.png b/camera/userdata/image/frame_20251016_103822_150.png new file mode 100644 index 0000000..40ecbf1 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103822_150.png differ diff --git a/camera/userdata/image/frame_20251016_103823_154.png b/camera/userdata/image/frame_20251016_103823_154.png new file mode 100644 index 0000000..befeca1 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103823_154.png differ diff --git a/camera/userdata/image/frame_20251016_103824_195.png b/camera/userdata/image/frame_20251016_103824_195.png new file mode 100644 index 0000000..7298651 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103824_195.png differ diff --git a/camera/userdata/image/frame_20251016_103825_237.png b/camera/userdata/image/frame_20251016_103825_237.png new file mode 100644 index 0000000..d5b0789 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103825_237.png differ diff --git a/camera/userdata/image/frame_20251016_103826_246.png b/camera/userdata/image/frame_20251016_103826_246.png new file mode 100644 index 0000000..1e9634e Binary files /dev/null and b/camera/userdata/image/frame_20251016_103826_246.png differ diff --git a/camera/userdata/image/frame_20251016_103827_270.png b/camera/userdata/image/frame_20251016_103827_270.png new file mode 100644 index 0000000..cfd3b79 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103827_270.png differ diff --git a/camera/userdata/image/frame_20251016_103828_309.png b/camera/userdata/image/frame_20251016_103828_309.png new file mode 100644 index 0000000..5013b82 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103828_309.png differ diff --git a/camera/userdata/image/frame_20251016_103829_353.png b/camera/userdata/image/frame_20251016_103829_353.png new file mode 100644 index 0000000..796e7bc Binary files /dev/null and b/camera/userdata/image/frame_20251016_103829_353.png differ diff --git a/camera/userdata/image/frame_20251016_103830_359.png b/camera/userdata/image/frame_20251016_103830_359.png new file mode 100644 index 0000000..a31bde7 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103830_359.png differ diff --git a/camera/userdata/image/frame_20251016_103831_394.png b/camera/userdata/image/frame_20251016_103831_394.png new file mode 100644 index 0000000..cae5826 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103831_394.png differ diff --git a/camera/userdata/image/frame_20251016_103832_435.png b/camera/userdata/image/frame_20251016_103832_435.png new file mode 100644 index 0000000..fb73f89 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103832_435.png differ diff --git a/camera/userdata/image/frame_20251016_103833_480.png b/camera/userdata/image/frame_20251016_103833_480.png new file mode 100644 index 0000000..a23e3cc Binary files /dev/null and b/camera/userdata/image/frame_20251016_103833_480.png differ diff --git a/camera/userdata/image/frame_20251016_103834_474.png b/camera/userdata/image/frame_20251016_103834_474.png new file mode 100644 index 0000000..6ecc48a Binary files /dev/null and b/camera/userdata/image/frame_20251016_103834_474.png differ diff --git a/camera/userdata/image/frame_20251016_103835_513.png b/camera/userdata/image/frame_20251016_103835_513.png new file mode 100644 index 0000000..bcde0de Binary files /dev/null and b/camera/userdata/image/frame_20251016_103835_513.png differ diff --git a/camera/userdata/image/frame_20251016_103836_553.png b/camera/userdata/image/frame_20251016_103836_553.png new file mode 100644 index 0000000..4df5ed2 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103836_553.png differ diff --git a/camera/userdata/image/frame_20251016_103837_594.png b/camera/userdata/image/frame_20251016_103837_594.png new file mode 100644 index 0000000..0f8903e Binary files /dev/null and b/camera/userdata/image/frame_20251016_103837_594.png differ diff --git a/camera/userdata/image/frame_20251016_103838_593.png b/camera/userdata/image/frame_20251016_103838_593.png new file mode 100644 index 0000000..4e1b6a8 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103838_593.png differ diff --git a/camera/userdata/image/frame_20251016_103839_634.png b/camera/userdata/image/frame_20251016_103839_634.png new file mode 100644 index 0000000..7e5981c Binary files /dev/null and b/camera/userdata/image/frame_20251016_103839_634.png differ diff --git a/camera/userdata/image/frame_20251016_103840_634.png b/camera/userdata/image/frame_20251016_103840_634.png new file mode 100644 index 0000000..8791284 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103840_634.png differ diff --git a/camera/userdata/image/frame_20251016_103841_632.png b/camera/userdata/image/frame_20251016_103841_632.png new file mode 100644 index 0000000..0caf9ee Binary files /dev/null and b/camera/userdata/image/frame_20251016_103841_632.png differ diff --git a/camera/userdata/image/frame_20251016_103842_677.png b/camera/userdata/image/frame_20251016_103842_677.png new file mode 100644 index 0000000..9db772b Binary files /dev/null and b/camera/userdata/image/frame_20251016_103842_677.png differ diff --git a/camera/userdata/image/frame_20251016_103843_711.png b/camera/userdata/image/frame_20251016_103843_711.png new file mode 100644 index 0000000..1ffcae0 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103843_711.png differ diff --git a/camera/userdata/image/frame_20251016_103844_754.png b/camera/userdata/image/frame_20251016_103844_754.png new file mode 100644 index 0000000..654bc4e Binary files /dev/null and b/camera/userdata/image/frame_20251016_103844_754.png differ diff --git a/camera/userdata/image/frame_20251016_103845_753.png b/camera/userdata/image/frame_20251016_103845_753.png new file mode 100644 index 0000000..5df555c Binary files /dev/null and b/camera/userdata/image/frame_20251016_103845_753.png differ diff --git a/camera/userdata/image/frame_20251016_103846_794.png b/camera/userdata/image/frame_20251016_103846_794.png new file mode 100644 index 0000000..b21a401 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103846_794.png differ diff --git a/camera/userdata/image/frame_20251016_103847_790.png b/camera/userdata/image/frame_20251016_103847_790.png new file mode 100644 index 0000000..71b952b Binary files /dev/null and b/camera/userdata/image/frame_20251016_103847_790.png differ diff --git a/camera/userdata/image/frame_20251016_103848_797.png b/camera/userdata/image/frame_20251016_103848_797.png new file mode 100644 index 0000000..e44e07f Binary files /dev/null and b/camera/userdata/image/frame_20251016_103848_797.png differ diff --git a/camera/userdata/image/frame_20251016_103849_874.png b/camera/userdata/image/frame_20251016_103849_874.png new file mode 100644 index 0000000..ca895fa Binary files /dev/null and b/camera/userdata/image/frame_20251016_103849_874.png differ diff --git a/camera/userdata/image/frame_20251016_103850_909.png b/camera/userdata/image/frame_20251016_103850_909.png new file mode 100644 index 0000000..fcf14f0 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103850_909.png differ diff --git a/camera/userdata/image/frame_20251016_103851_909.png b/camera/userdata/image/frame_20251016_103851_909.png new file mode 100644 index 0000000..d116b17 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103851_909.png differ diff --git a/camera/userdata/image/frame_20251016_103852_952.png b/camera/userdata/image/frame_20251016_103852_952.png new file mode 100644 index 0000000..7c66ee1 Binary files /dev/null and b/camera/userdata/image/frame_20251016_103852_952.png differ diff --git a/camera/userdata/image/frame_20251016_103853_956.png b/camera/userdata/image/frame_20251016_103853_956.png new file mode 100644 index 0000000..7cc764a Binary files /dev/null and b/camera/userdata/image/frame_20251016_103853_956.png differ diff --git a/guanghuaban/1.jpg b/guanghuaban/1.jpg new file mode 100644 index 0000000..c608b44 Binary files /dev/null and b/guanghuaban/1.jpg differ diff --git a/guanghuaban/2.jpg b/guanghuaban/2.jpg new file mode 100644 index 0000000..30dcf3a Binary files /dev/null and b/guanghuaban/2.jpg differ diff --git a/guanghuaban/2.png b/guanghuaban/2.png new file mode 100644 index 0000000..1f7ff54 Binary files /dev/null and b/guanghuaban/2.png differ diff --git a/guanghuaban/FFT.py b/guanghuaban/FFT.py new file mode 100644 index 0000000..e2683be --- /dev/null +++ b/guanghuaban/FFT.py @@ -0,0 +1,76 @@ +import matplotlib +matplotlib.use('Agg') # 使用非 GUI 后端 + +import cv2 +import numpy as np +import matplotlib.pyplot as plt + +def analyze_surface_roughness(image_path): + # 读取图像 + img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) + if img is None: + raise FileNotFoundError(f"无法加载图像: {image_path}") + + # 预处理 + clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) + img = clahe.apply(img) + img = cv2.medianBlur(img, 5) + + # FFT + f = np.fft.fft2(img) + fshift = np.fft.fftshift(f) + magnitude_spectrum = 20 * np.log(np.abs(fshift) + 1e-9) + + # 动态低频区域 + rows, cols = img.shape + crow, ccol = rows // 2, cols // 2 + radius = min(rows, cols) // 20 + mask = np.ones((rows, cols), np.uint8) + cv2.circle(mask, (ccol, crow), radius, 0, thickness=-1) + + # 计算能量占比 + total_energy = np.sum(magnitude_spectrum) + high_freq_energy = np.sum(magnitude_spectrum * mask) + ratio = high_freq_energy / total_energy + + # 可视化并保存 + plt.figure(figsize=(12, 6)) + + plt.subplot(1, 3, 1) + plt.imshow(img, cmap='gray') + plt.title('预处理后图像') + plt.axis('off') + + plt.subplot(1, 3, 2) + plt.imshow(magnitude_spectrum, cmap='gray') + plt.title('频谱图(中心为低频)') + plt.axis('off') + + overlay = magnitude_spectrum.copy() + if len(overlay.shape) == 2: + overlay = np.stack([overlay]*3, axis=-1) + cv2.circle(overlay, (ccol, crow), radius, (1, 0, 0), 2) + plt.subplot(1, 3, 3) + plt.imshow(overlay) + plt.title(f'低频区域(红圈内)\n高频能量占比: {ratio:.3f}') + plt.axis('off') + + plt.tight_layout() + plt.savefig("fft_analysis_result.png", dpi=150, bbox_inches='tight') + plt.close() # 释放内存 + + # 输出结果 + print(f"✅ 图像尺寸: {cols}x{rows}") + print(f"📊 高频能量占比: {ratio:.3f}") + if ratio < 0.8: + print("🟢 评价: 表面较光滑") + elif ratio < 0.95: + print("🟡 评价: 表面中等粗糙") + else: + print("🔴 评价: 表面非常粗糙") + + return ratio + +# 运行 +if __name__ == "__main__": + analyze_surface_roughness("1.jpg") \ No newline at end of file diff --git a/guanghuaban/GLCM.py b/guanghuaban/GLCM.py new file mode 100644 index 0000000..ffaa330 --- /dev/null +++ b/guanghuaban/GLCM.py @@ -0,0 +1,56 @@ +from skimage import feature +import cv2 +import numpy as np +import os + +# 图像路径 +img_path = "2.jpg" + +# ✅ 1. 检查文件是否存在 +if not os.path.exists(img_path): + raise FileNotFoundError(f"图像文件不存在,请检查路径: {img_path}") + +# ✅ 2. 以灰度模式读取图像 +img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) +if img is None: + raise ValueError(f"无法读取图像,可能是文件损坏或格式问题: {img_path}") + +print(f"✅ 图像加载成功,原始尺寸: {img.shape}") + +''' +height, width = img.shape +start_row = height // 3 * 2 # 从 2/3 处开始,到底部 +bottom_region = img[start_row:, :] # 所有列,只取底部 1/3 行 + +print(f"✅ 裁剪出底部 1/3 区域,尺寸: {bottom_region.shape}") +''' +# ✅ 3. 提取最下方三分之一的区域 + +bottom_region = img +# ✅ 4. 计算 GLCM(建议降低 levels 或使用压缩) +# 注意:levels=256 对计算资源要求高,可考虑先归一化到 32 或 64 灰度级 +# 这里我们先保持 256,若报内存错误可改用降级方法 + +glcm = feature.graycomatrix( + bottom_region, + distances=[1], + angles=[0, np.pi/4, np.pi/2, 3*np.pi/4], # 多方向 + levels=256, + symmetric=True, + normed=True +) + +# ✅ 5. 提取平均纹理特征(在所有方向上取均值) +properties = ['contrast', 'dissimilarity', 'homogeneity', 'energy', 'correlation'] +features = {} +for prop in properties: + values = feature.graycoprops(glcm, prop)[0] + features[prop] = np.mean(values) # 对多个角度取平均 + +# ✅ 6. 输出结果 +print("\n📊 图像底部 1/3 区域的纹理特征:") +print(f"Contrast: {features['contrast']:.3f}") # 越大越粗糙230 +print(f"Dissimilarity: {features['dissimilarity']:.3f}") # 越大差异越大7.265 +print(f"Homogeneity: {features['homogeneity']:.3f}") # 越小越不均匀(粗糙)0.291 +print(f"Energy: {features['energy']:.3f}") # 越小越复杂(纹理丰富)0.033 +print(f"Correlation: {features['correlation']:.3f}") # 灰度线性相关性0.941 \ No newline at end of file diff --git a/guanghuaban/LBP.py b/guanghuaban/LBP.py new file mode 100644 index 0000000..acfa1a9 --- /dev/null +++ b/guanghuaban/LBP.py @@ -0,0 +1,17 @@ +from skimage import feature +import matplotlib.pyplot as plt +import cv2 +import numpy as np +# 读取图像 +img = cv2.imread("2.jpg", cv2.IMREAD_GRAYSCALE) +# 计算 LBP 图像 +radius = 3 +n_points = 8 * radius +lbp = feature.local_binary_pattern(img, n_points, radius, method='uniform') + +# 统计 LBP 直方图 +hist, _ = np.histogram(lbp, bins=256, range=(0, 256)) + +# 直方图熵(越高表示纹理越复杂) +entropy = -np.sum(hist * np.log(hist + 1e-9)) +print(f"LBP Entropy: {entropy:.3f}") \ No newline at end of file diff --git a/guanghuaban/T.py b/guanghuaban/T.py new file mode 100644 index 0000000..26e50ab --- /dev/null +++ b/guanghuaban/T.py @@ -0,0 +1,101 @@ +import cv2 +import numpy as np + +# 读取图像(以灰度模式加载) +# 作用:将 '1.jpg' 图像读入内存,转换为单通道灰度图,便于后续图像处理 +# 注意:如果路径错误或文件不存在,image 将为 None,导致后续报错 +image = cv2.imread('1.jpg', cv2.IMREAD_GRAYSCALE) + +# 检查图像是否成功加载 +if image is None: + raise FileNotFoundError("无法读取图像,请检查文件路径是否正确:'1.jpg'") + +# 均值滤波,滤波核大小为7x7 +# 作用:对图像进行平滑处理,去除高频噪声(如随机噪点) +# 原理:用每个像素周围7x7邻域的平均值替换该像素值,使图像更“柔和” +blurred = cv2.blur(image, (7, 7)) + +# 使用局部自适应阈值进行图像分割 +# 作用:将图像二值化,突出比局部背景更暗的区域(如划痕、凹坑等缺陷) +# 参数说明: +# - cv2.ADAPTIVE_THRESH_MEAN_C:阈值基于局部邻域的平均值 +# - 11:邻域大小(奇数),决定局部范围 +# - 2:从均值中减去的常数,用于微调灵敏度 +# - cv2.THRESH_BINARY_INV:反转结果,缺陷区域为白色(255),背景为黑色(0) +thresholded = cv2.adaptiveThreshold( + blurred, 255, cv2.ADAPTIVE_THRESH_MEAN_C, + cv2.THRESH_BINARY_INV, 11, 2 +) + +# 查找图像中的轮廓 +# 作用:检测二值图像中所有连通的白色区域(即潜在缺陷) +# 返回值: +# - contours: 所有轮廓的坐标点列表 +# - _ : 轮廓的层级关系(此处不需要) +# 方法选择: +# - cv2.RETR_LIST:提取所有轮廓,不建立层级 +# - cv2.CHAIN_APPROX_SIMPLE:压缩水平/垂直/对角线方向的轮廓点,节省内存 +contours, _ = cv2.findContours(thresholded, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) + +# 创建一个彩色图像用于结果显示 +# 作用:将原始灰度图转换为BGR三通道图像,以便用不同颜色绘制检测结果 +output_image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) + +# 对每个检测到的轮廓进行面积筛选 +# 作用:过滤掉太小(可能是噪声)或太大(可能是背景干扰)的区域 +# 设置面积阈值范围:250 ~ 1000 像素 +min_area, max_area = 250, 1000 +for contour in contours: + area = cv2.contourArea(contour) # 计算轮廓包围的面积 + if min_area < area < max_area: + # 绘制符合条件的轮廓 + # 颜色:绿色 (0, 255, 0),线宽:2 + cv2.drawContours(output_image, [contour], -1, (0, 255, 0), 2) + +# 形态学膨胀操作 +# 作用:扩大二值图像中的白色区域,连接邻近的断裂缺陷(如断开的划痕) +# 结构元素:椭圆形(模拟圆形),大小为7x7 +# iterations=1:执行一次膨胀 +kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7)) +dilated = cv2.dilate(thresholded, kernel, iterations=1) + + +# 骨架化处理(细化为单像素宽的中心线) +# 作用:将膨胀后的缺陷区域“细线化”,便于分析划痕的路径和长度 +# 注意:OpenCV 无内置骨架化函数,这里使用经典的Zhang-Suen细化算法简化版 +def thinning(img): + skel = np.zeros(img.shape, np.uint8) # 存储骨架结果 + size = np.size(img) + element = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3)) # 3x3十字结构元 + done = False + + while not done: + # 重复执行:腐蚀 → 膨胀 → 差分 → 累加到骨架 + eroded = cv2.erode(img, element) # 腐蚀:缩小区域 + temp = cv2.dilate(eroded, element) # 膨胀回来 + temp = cv2.subtract(img, temp) # 得到最外层轮廓(即“剥皮”一层) + skel = cv2.bitwise_or(skel, temp) # 累加到骨架图像 + img = eroded.copy() # 更新图像为腐蚀后的结果 + + # 当图像完全变黑(无非零像素)时停止 + zeros = size - cv2.countNonZero(img) + if zeros == size: + done = True + + return skel + + +# 执行骨架化 +skeleton = thinning(dilated) + +# 显示所有中间结果和最终检测图 +# 作用:可视化每一步的处理效果,便于调试和分析 +cv2.imshow("Original Image", image) # 原始灰度图 +cv2.imshow("Thresholded Image", thresholded) # 自适应阈值分割结果 +cv2.imshow("Dilated Image", dilated) # 膨胀后结果 +cv2.imshow("Skeletonized Image", skeleton) # 骨架化结果(细线) +cv2.imshow("Detected Regions", output_image) # 最终检测结果(绿色轮廓) + +# 等待按键后关闭所有窗口 +cv2.waitKey(0) +cv2.destroyAllWindows() \ No newline at end of file diff --git a/guanghuaban/fft_analysis_result.png b/guanghuaban/fft_analysis_result.png new file mode 100644 index 0000000..b3b6a95 Binary files /dev/null and b/guanghuaban/fft_analysis_result.png differ diff --git a/guanghuaban/guanghua.py b/guanghuaban/guanghua.py new file mode 100644 index 0000000..c2d45db --- /dev/null +++ b/guanghuaban/guanghua.py @@ -0,0 +1,28 @@ +import cv2 +import numpy as np + +# 加载图像 +img = cv2.imread('1.jpg', cv2.IMREAD_GRAYSCALE) +''' +height, width = img.shape +start_row = height // 7 * 4 # 从 2/3 处开始,到底部 +img = img[start_row:, :] # 所有列,只取底部 1/3 行 + +print(f"✅ 裁剪出底部 1/3 区域,尺寸: {img.shape}") + +''' +# ✅ 3. 提取最下方三分之一的区域 + + +# 高斯模糊去除噪声 +blurred = cv2.GaussianBlur(img, (5, 5), 0) + +# 边缘检测 +edges = cv2.Canny(blurred, 50, 150) + +# 显示结果 +cv2.imshow("Edges", edges) +cv2.waitKey(0) +cv2.destroyAllWindows() + +# 接下来可以根据需要进一步处理边缘信息,比如计算裂缝长度、宽度等参数。 \ No newline at end of file diff --git a/image/class5.py b/image/class5.py new file mode 100644 index 0000000..89f7294 --- /dev/null +++ b/image/class5.py @@ -0,0 +1,133 @@ +import os +from pathlib import Path +import cv2 +import numpy as np +from ultralytics import YOLO +import shutil + +# --------------------------- +# 全局配置 +# --------------------------- + +# ✅ 定义唯一的 ROI (x, y, w, h) —— 请根据你的实际坐标修改! +SINGLE_ROI = (859,810,696,328) # 格式: x, y, w, h + +CLASS_NAMES = { + 0: "未堆料", + 1: "小堆料", + 2: "大堆料", + 3: "未浇筑满", + 4: "浇筑满" +} + +TARGET_SIZE = 640 +THRESHOLD = 0.4 # 加权得分阈值 + + +# --------------------------- +# class1/class2 加权判断 +# --------------------------- +def weighted_small_large(pred_probs, threshold=0.4, w1=0.3, w2=0.7): + p1 = float(pred_probs[1]) + p2 = float(pred_probs[2]) + total = p1 + p2 + if total > 0: + score = (w1 * p1 + w2 * p2) / total + else: + score = 0.0 + final_class = "大堆料" if score >= threshold else "小堆料" + return final_class, score, p1, p2 + + +# --------------------------- +# 批量推理主函数(单 ROI + 批量处理) +# --------------------------- +def batch_classify_images(model_path, input_folder, output_root, target_size=640, threshold=0.4): + model = YOLO(model_path) + + output_root = Path(output_root) + output_root.mkdir(parents=True, exist_ok=True) + + # 创建每个类别的输出目录 + class_dirs = {} + for name in CLASS_NAMES.values(): + d = output_root / name + d.mkdir(exist_ok=True) + class_dirs[name] = d + + x, y, w, h = SINGLE_ROI + + # 存储所有裁剪后的图像和对应的原始路径 + crops = [] + img_paths = [] + + input_folder = Path(input_folder) + supported_exts = {'.jpg', '.jpeg', '.png', '.bmp', '.tif'} + + print("🔍 正在裁剪所有图片的 ROI 区域...") + for img_path in input_folder.glob("*.*"): + if img_path.suffix.lower() not in supported_exts: + continue + img = cv2.imread(str(img_path)) + if img is None: + continue + + # 裁剪并 resize 到模型输入大小 + roi = img[y:y+h, x:x+w] + roi_resized = cv2.resize(roi, (target_size, target_size), interpolation=cv2.INTER_AREA) + crops.append(roi_resized) + img_paths.append(img_path) + + if not crops: + print("❌ 没有有效图片可供处理") + return + + total = len(crops) + print(f"✅ 共准备 {total} 张图片,开始批量推理...") + + # 🔥 批量推理(一次 forward pass) + results = model( + source=crops, + verbose=False, + imgsz=target_size, + half=False, # 如果使用 GPU 可开启半精度: half=True + device=0 # 使用 GPU 0,如用 CPU 改为: device=None + ) + + # 后处理结果 + print("📦 正在处理结果并保存...") + for i, result in enumerate(results): + pred_probs = result.probs.data.cpu().numpy().flatten() + class_id = int(pred_probs.argmax()) + confidence = float(pred_probs[class_id]) + class_name = CLASS_NAMES.get(class_id, f"未知类别({class_id})") + + if class_id in [1, 2]: + final_class, score, p1, p2 = weighted_small_large(pred_probs, threshold=threshold) + else: + final_class = class_name + score = confidence + p1 = float(pred_probs[1]) + p2 = float(pred_probs[2]) + + # 构造目标路径 + original_path = img_paths[i] + suffix = f"_roi0_{final_class}_score{score:.2f}_p1{p1:.2f}_p2{p2:.2f}" + dst_path = class_dirs[final_class] / f"{original_path.stem}{suffix}{original_path.suffix}" + + # 复制原图到对应类别文件夹 + shutil.copy2(str(original_path), str(dst_path)) + print(f"{original_path.name} -> {final_class} (score={score:.2f}, p1={p1:.2f}, p2={p2:.2f})") + + print(f"\n🎉 分类完成!共处理 {total} 张图片") + + +# --------------------------- +# 使用示例 +# --------------------------- +if __name__ == "__main__": + MODEL_PATH = r"cls5.pt" + INPUT_FOLDER = r"./test_image" + OUTPUT_ROOT = r"./classified_images" + + batch_classify_images(MODEL_PATH, INPUT_FOLDER, OUTPUT_ROOT, TARGET_SIZE, THRESHOLD) \ No newline at end of file diff --git a/image/class_data.py b/image/class_data.py new file mode 100644 index 0000000..307bf39 --- /dev/null +++ b/image/class_data.py @@ -0,0 +1,75 @@ +import os +import shutil +from pathlib import Path + +from ultralytics import YOLO + + +def classify_images_by_model(model_path, image_folder): + """ + 使用分类模型对图片进行预测,并将每张图片复制到对应类别的子文件夹中。 + + Args: + model_path (str): 分类模型权重路径(.pt 文件) + image_folder (str): 包含待分类图片的文件夹路径 + """ + image_folder = Path(image_folder) + if not image_folder.exists(): + raise FileNotFoundError(f"图片文件夹不存在: {image_folder}") + + # 支持的图片格式 + image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp'} + image_files = [f for f in image_folder.iterdir() if f.suffix.lower() in image_extensions] + if not image_files: + print(f"在 {image_folder} 中未找到图片文件。") + return + + # 加载模型 + print(f"正在加载分类模型: {model_path}") + try: + model = YOLO(model_path) # 支持 YOLOv8 分类模型 + print("模型加载完成。") + except Exception as e: + print(f"加载模型失败: {e}") + return + + classified_count = 0 + for img_path in image_files: + try: + # 推理 + results = model(img_path, verbose=False) + result = results[0] + + # 获取预测类别名称 + # 注意:分类模型 result.probs.top1 可直接获取类别索引 + if hasattr(result.probs, 'top1'): + class_idx = result.probs.top1 + class_name = result.names[class_idx] + else: + print(f"[跳过] {img_path.name}: 未获取到有效分类结果") + continue + + print(f"[{img_path.name}] 预测类别: {class_name}") + + # 创建类别子文件夹 + class_folder = image_folder / class_name + class_folder.mkdir(exist_ok=True) + + # ✅ 复制图片到对应类别文件夹(保留原图) + dest_path = class_folder / img_path.name + shutil.copy2(str(img_path), str(dest_path)) # ← 关键:使用 copy2 + print(f" → 已复制到 {class_name} 文件夹") + classified_count += 1 + + except Exception as e: + print(f"[错误] 处理 {img_path.name} 时出错: {e}") + + print(f"\n✅ 处理完成!共复制 {classified_count} 张图片到对应的类别文件夹。") + + +# ================== 使用示例 ================== +if __name__ == "__main__": + MODEL_PATH = "cls5.pt" # 替换为你的分类模型 .pt 文件路径 + IMAGE_FOLDER = "./test_image" # 替换为你的图片文件夹 + + classify_images_by_model(MODEL_PATH, IMAGE_FOLDER) \ No newline at end of file diff --git a/image/cls.pt b/image/cls.pt new file mode 100644 index 0000000..9e3f2ac Binary files /dev/null and b/image/cls.pt differ diff --git a/image/cls5.pt b/image/cls5.pt new file mode 100644 index 0000000..3fd6d73 Binary files /dev/null and b/image/cls5.pt differ diff --git a/image/roi_coordinates/1_rois.txt b/image/roi_coordinates/1_rois.txt new file mode 100644 index 0000000..bb8f71d --- /dev/null +++ b/image/roi_coordinates/1_rois.txt @@ -0,0 +1 @@ +859,810,696,328 diff --git a/image/roi_coordinates/1_rois1.txt b/image/roi_coordinates/1_rois1.txt new file mode 100644 index 0000000..723dc91 --- /dev/null +++ b/image/roi_coordinates/1_rois1.txt @@ -0,0 +1 @@ +589,789,210,319 diff --git a/image/test_image/1.jpg b/image/test_image/1.jpg new file mode 100644 index 0000000..2882cd5 Binary files /dev/null and b/image/test_image/1.jpg differ diff --git a/image/test_image/2.jpg b/image/test_image/2.jpg new file mode 100644 index 0000000..dcd5369 Binary files /dev/null and b/image/test_image/2.jpg differ diff --git a/yolo11_seg/1.png b/image/test_image/3.jpg similarity index 100% rename from yolo11_seg/1.png rename to image/test_image/3.jpg diff --git a/image/test_image/class0/1.jpg b/image/test_image/class0/1.jpg new file mode 100644 index 0000000..2882cd5 Binary files /dev/null and b/image/test_image/class0/1.jpg differ diff --git a/image/test_image/class0/2.jpg b/image/test_image/class0/2.jpg new file mode 100644 index 0000000..dcd5369 Binary files /dev/null and b/image/test_image/class0/2.jpg differ diff --git a/image/test_image/class4/3.jpg b/image/test_image/class4/3.jpg new file mode 100644 index 0000000..8df7feb Binary files /dev/null and b/image/test_image/class4/3.jpg differ diff --git a/image_chuantong/1.png b/image_chuantong/1.png new file mode 100644 index 0000000..52fcf5c Binary files /dev/null and b/image_chuantong/1.png differ diff --git a/image_chuantong/2.jpg b/image_chuantong/2.jpg new file mode 100755 index 0000000..fc88a51 Binary files /dev/null and b/image_chuantong/2.jpg differ diff --git a/image_chuantong/gama.py b/image_chuantong/gama.py new file mode 100644 index 0000000..90baaf3 --- /dev/null +++ b/image_chuantong/gama.py @@ -0,0 +1,53 @@ +#!/usr/bin/python +# coding:utf-8 + +import cv2 +import numpy as np + +# 读取彩色图 +img = cv2.imread('2.jpg') # 读取原始图像 (BGR格式) + +if img is None: + print("错误:无法读取图像 '1.png',请确认文件存在且路径正确。") + exit() + +# 转为RGB进行处理(便于归一化等操作) +img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + +# 归一化到0~1 +img_normalized = img_rgb / 255.0 + +# 定义两个不同的gamma值 +gamma1 = 1 / 1.5 # 约等于 0.67,提亮暗部 +gamma2 = 1.5 # 压暗亮部 + +# 伽马校正 +img_gamma1 = np.power(img_normalized, gamma1) +img_gamma2 = np.power(img_normalized, gamma2) + +# 转回 0~255 并转换为 uint8 类型 +img_gamma1 = np.uint8(img_gamma1 * 255) +img_gamma2 = np.uint8(img_gamma2 * 255) + +# 转回 BGR 格式以便用 OpenCV 保存(OpenCV 默认使用 BGR) +img_gamma1_bgr = cv2.cvtColor(img_gamma1, cv2.COLOR_RGB2BGR) +img_gamma2_bgr = cv2.cvtColor(img_gamma2, cv2.COLOR_RGB2BGR) + +# 保存图像 +cv2.imwrite('original.png', img) # 原始图像 +cv2.imwrite('gamma_0.67.png', img_gamma1_bgr) # gamma = 1/1.5 ≈ 0.67 +cv2.imwrite('gamma_1.5.png', img_gamma2_bgr) # gamma = 1.5 + +print("已保存三张图像:") +print(" original.png") +print(" gamma_0.67.png") +print(" gamma_1.5.png") + +# 可选:显示图像(如果在有GUI的环境下运行) +show_images = False # 设置为 True 可显示图像 +if show_images: + cv2.imshow('Original', img) + cv2.imshow('Gamma = 1/1.5 (~0.67)', img_gamma1_bgr) + cv2.imshow('Gamma = 1.5', img_gamma2_bgr) + cv2.waitKey(0) + cv2.destroyAllWindows() \ No newline at end of file diff --git a/image_chuantong/gamma_0.67.png b/image_chuantong/gamma_0.67.png new file mode 100644 index 0000000..294353c Binary files /dev/null and b/image_chuantong/gamma_0.67.png differ diff --git a/image_chuantong/gamma_1.5.png b/image_chuantong/gamma_1.5.png new file mode 100644 index 0000000..113666e Binary files /dev/null and b/image_chuantong/gamma_1.5.png differ diff --git a/image_chuantong/original.png b/image_chuantong/original.png new file mode 100644 index 0000000..c80160c Binary files /dev/null and b/image_chuantong/original.png differ diff --git a/output_masks2/labels/frame_20250805_121956_26985.txt b/output_masks2/labels/frame_20250805_121956_26985.txt new file mode 100644 index 0000000..423dbb2 --- /dev/null +++ b/output_masks2/labels/frame_20250805_121956_26985.txt @@ -0,0 +1 @@ +0 0.375000 0.683333 0.375000 0.694444 0.371875 0.700000 0.371875 0.702778 0.370312 0.705556 0.370312 0.708333 0.368750 0.711111 0.368750 0.713889 0.364063 0.722222 0.359375 0.722222 0.359375 0.752778 0.378125 0.752778 0.379688 0.755556 0.417187 0.755556 0.418750 0.758333 0.498437 0.758333 0.500000 0.755556 0.559375 0.755556 0.560937 0.752778 0.590625 0.752778 0.592187 0.750000 0.601562 0.750000 0.601562 0.738889 0.596875 0.738889 0.593750 0.733333 0.593750 0.727778 0.592187 0.725000 0.592187 0.716667 0.590625 0.713889 0.590625 0.705556 0.589063 0.702778 0.589063 0.697222 0.587500 0.694444 0.587500 0.683333 diff --git a/output_masks2/labels/frame_20250805_122246_31770.txt b/output_masks2/labels/frame_20250805_122246_31770.txt new file mode 100644 index 0000000..e88bf38 --- /dev/null +++ b/output_masks2/labels/frame_20250805_122246_31770.txt @@ -0,0 +1 @@ +0 0.376563 0.683333 0.376563 0.697222 0.373437 0.702778 0.373437 0.705556 0.371875 0.708333 0.371875 0.711111 0.370312 0.713889 0.370312 0.716667 0.364063 0.727778 0.359375 0.727778 0.359375 0.755556 0.404687 0.755556 0.406250 0.758333 0.429688 0.758333 0.431250 0.761111 0.431250 0.769444 0.487500 0.769444 0.487500 0.761111 0.489063 0.758333 0.528125 0.758333 0.529687 0.755556 0.581250 0.755556 0.582812 0.752778 0.601562 0.752778 0.601562 0.727778 0.596875 0.727778 0.593750 0.722222 0.593750 0.716667 0.592187 0.713889 0.592187 0.705556 0.590625 0.702778 0.590625 0.694444 0.589063 0.691667 0.589063 0.683333 diff --git a/output_masks2/labels/frame_20250805_122346_33510.txt b/output_masks2/labels/frame_20250805_122346_33510.txt new file mode 100644 index 0000000..05b776c --- /dev/null +++ b/output_masks2/labels/frame_20250805_122346_33510.txt @@ -0,0 +1 @@ +0 0.379688 0.683333 0.379688 0.694444 0.376563 0.700000 0.376563 0.705556 0.373437 0.711111 0.373437 0.713889 0.371875 0.716667 0.371875 0.719444 0.370312 0.722222 0.370312 0.725000 0.364063 0.736111 0.359375 0.736111 0.359375 0.755556 0.404687 0.755556 0.406250 0.758333 0.432812 0.758333 0.434375 0.761111 0.434375 0.769444 0.479687 0.769444 0.479687 0.761111 0.481250 0.758333 0.526563 0.758333 0.528125 0.755556 0.581250 0.755556 0.582812 0.752778 0.592187 0.752778 0.593750 0.750000 0.601562 0.750000 0.601562 0.741667 0.596875 0.741667 0.593750 0.736111 0.593750 0.730556 0.592187 0.727778 0.592187 0.719444 0.590625 0.716667 0.590625 0.708333 0.589063 0.705556 0.589063 0.700000 0.587500 0.697222 0.587500 0.683333 diff --git a/output_masks2/masks/mask_frame_20250805_121956_26985.png b/output_masks2/masks/mask_frame_20250805_121956_26985.png new file mode 100644 index 0000000..725c210 Binary files /dev/null and b/output_masks2/masks/mask_frame_20250805_121956_26985.png differ diff --git a/output_masks2/masks/mask_frame_20250805_122246_31770.png b/output_masks2/masks/mask_frame_20250805_122246_31770.png new file mode 100644 index 0000000..514e4c0 Binary files /dev/null and b/output_masks2/masks/mask_frame_20250805_122246_31770.png differ diff --git a/output_masks2/masks/mask_frame_20250805_122346_33510.png b/output_masks2/masks/mask_frame_20250805_122346_33510.png new file mode 100644 index 0000000..d552e87 Binary files /dev/null and b/output_masks2/masks/mask_frame_20250805_122346_33510.png differ diff --git a/output_masks2/seg_frame_20250805_121956_26985.jpg b/output_masks2/seg_frame_20250805_121956_26985.jpg new file mode 100644 index 0000000..4010fbf Binary files /dev/null and b/output_masks2/seg_frame_20250805_121956_26985.jpg differ diff --git a/output_masks2/seg_frame_20250805_122246_31770.jpg b/output_masks2/seg_frame_20250805_122246_31770.jpg new file mode 100644 index 0000000..9e294a5 Binary files /dev/null and b/output_masks2/seg_frame_20250805_122246_31770.jpg differ diff --git a/output_masks2/seg_frame_20250805_122346_33510.jpg b/output_masks2/seg_frame_20250805_122346_33510.jpg new file mode 100644 index 0000000..9c54e31 Binary files /dev/null and b/output_masks2/seg_frame_20250805_122346_33510.jpg differ diff --git a/rknn_bushu/yolov11_obb/2.jpg b/rknn_bushu/yolov11_obb/2.jpg new file mode 100644 index 0000000..dcd5369 Binary files /dev/null and b/rknn_bushu/yolov11_obb/2.jpg differ diff --git a/ailai_obb/bushu_angle.py b/rknn_bushu/yolov11_obb/bag_bushu.py similarity index 100% rename from ailai_obb/bushu_angle.py rename to rknn_bushu/yolov11_obb/bag_bushu.py index 891ee62..d4e23c7 100644 --- a/ailai_obb/bushu_angle.py +++ b/rknn_bushu/yolov11_obb/bag_bushu.py @@ -1,4 +1,3 @@ - import cv2 import numpy as np import math @@ -195,3 +194,4 @@ if __name__ == "__main__": save_path_top = "./inference_results/top_box.jpg" visualize_top_box(img.copy(), detect_boxes, save_path_top) + diff --git a/rknn_bushu/yolov11_obb/obb.rknn b/rknn_bushu/yolov11_obb/obb.rknn new file mode 100644 index 0000000..de69ac3 Binary files /dev/null and b/rknn_bushu/yolov11_obb/obb.rknn differ diff --git a/rknn_bushu/yolov11_point/11.jpg b/rknn_bushu/yolov11_point/11.jpg new file mode 100644 index 0000000..c4626ed Binary files /dev/null and b/rknn_bushu/yolov11_point/11.jpg differ diff --git a/rknn_bushu/yolov11_point/README.md b/rknn_bushu/yolov11_point/README.md new file mode 100644 index 0000000..8a2627a --- /dev/null +++ b/rknn_bushu/yolov11_point/README.md @@ -0,0 +1,56 @@ +# RKNN 关键点推理与偏移量计算工具 + +该工具通过使用RKNN模型对输入图像进行关键点检测,并根据检测结果计算相对于固定参考点的偏移量(单位:毫米)。此外,还提供了可视化选项来展示计算结果。 + +## 目录结构 + +├── calculate_offset.py # 主程序脚本 +├── point.rknn # RKNN 模型文件 (请确保正确路径) +└── README.md # 说明文档 + +## 配置 + +在 `calculate_offset.py` 文件顶部的配置区中,您可以修改如下参数以适应您的需求: + +- **MODEL_PATH**: RKNN 模型文件路径。 +- **OUTPUT_DIR**: 输出目录路径。 +- **FIXED_REF_POINT**: 固定参考点坐标(像素)。 +- **SCALE_X**, **SCALE_Y**: 缩放因子,用于将像素坐标转换为毫米。 +- **IMG_SIZE**: 输入图像尺寸。 + +## 安装依赖 + +请确保安装了必要的 Python 库。可以通过 pip 安装: + +```bash +pip install opencv-python numpy rknnlite +``` + +## 函数调用 + +您也可以直接调用 calculate_offset_from_image 函数,以便集成到其他项目中: +示例 1: 仅获取偏移量(不画图) + +```bash +from calculate_offset import calculate_offset_from_image +result = calculate_offset_from_image("your_image_path.jpg", visualize=False) +if result['success']: + print(f"Offset: DeltaX={result['dx_mm']:+.2f} mm, DeltaY={result['dy_mm']:+.2f} mm") +else: + print("Error:", result['message']) +``` +示例 2: 获取偏移量并保存可视化图 + +```bash +from calculate_offset import calculate_offset_from_image +result = calculate_offset_from_image("your_image_path.jpg", visualize=True) +``` + +该函数返回一个包含下列字段的字典: + + success: 成功标志(True/False) + dx_mm: 水平偏移(毫米) + dy_mm: 垂直偏移(毫米) + cx: 中心点 x 坐标(像素) + cy: 中心点 y 坐标(像素) + message: 错误信息或成功提示 diff --git a/rknn_bushu/yolov11_point/calculate_diff.py b/rknn_bushu/yolov11_point/calculate_diff.py new file mode 100644 index 0000000..98ff4bd --- /dev/null +++ b/rknn_bushu/yolov11_point/calculate_diff.py @@ -0,0 +1,230 @@ +import cv2 +import numpy as np +import os +from rknnlite.api import RKNNLite + +# ====================== 配置区 ====================== +MODEL_PATH = "point.rknn" +OUTPUT_DIR = "./output_rknn" +os.makedirs(OUTPUT_DIR, exist_ok=True) + +# 固定参考点(像素坐标) +FIXED_REF_POINT = (535, 605) + +# mm/px 缩放因子(根据标定数据填写) +width_mm = 70.0 +width_px = 42 +SCALE_X = width_mm / float(width_px) +height_mm = 890.0 +height_px = 507 +SCALE_Y = height_mm / float(height_px) +print(f"Scale factors: SCALE_X={SCALE_X:.3f} mm/px, SCALE_Y={SCALE_Y:.3f} mm/px") + +# 输入尺寸 +IMG_SIZE = (640, 640) + + +def letterbox_resize(image, size, bg_color=114): + """保持比例缩放并填充到指定大小""" + target_w, target_h = size + h, w = image.shape[:2] + scale = min(target_w / w, target_h / h) + new_w, new_h = int(w * scale), int(h * scale) + resized = cv2.resize(image, (new_w, new_h)) + canvas = np.full((target_h, target_w, 3), bg_color, dtype=np.uint8) + dx, dy = (target_w - new_w) // 2, (target_h - new_h) // 2 + canvas[dy:dy + new_h, dx:dx + new_w] = resized + return canvas, scale, dx, dy + + +def safe_sigmoid(x): + x = np.clip(x, -50, 50) + return 1.0 / (1.0 + np.exp(-x)) + + +def parse_pose_outputs(outputs, dx=0, dy=0, scale=1.0): + """ + 解析 RKNN YOLO-Pose 关键点输出 + outputs[3]: shape (1, 4, 3, 8400) -> [kpt_id, (x,y,conf), anchor] + """ + kpt_output = np.array(outputs[3])[0] # (4, 3, 8400) + confs = kpt_output[:, 2, :] # 取每个关键点的 visible_conf + mean_conf_per_anchor = np.mean(confs, axis=0) # 每个 anchor 的平均可见性 + best_anchor_idx = np.argmax(mean_conf_per_anchor) + kpt_data = kpt_output[:, :, best_anchor_idx] # (4, 3): x, y, vis_conf + + keypoints = [] + for i in range(4): + x_img = kpt_data[i, 0] + y_img = kpt_data[i, 1] + vis_conf_raw = kpt_data[i, 2] + vis_prob = safe_sigmoid(vis_conf_raw) + + # 映射回原图坐标 + x_orig = (x_img - dx) / scale + y_orig = (y_img - dy) / scale + keypoints.append([x_orig, y_orig, vis_prob]) + + return np.array(keypoints) + + +def compute_offset(keypoints, fixed_point, scale_x, scale_y): + """ + 计算中心点相对于固定参考点的偏移量(mm) + 中心点 = P0 和 P1 的中点 + 返回: (center_x, center_y, dx_mm, dy_mm) + """ + if len(keypoints) < 2: + return None + + p1, p2 = keypoints[0], keypoints[1] + cx = (p1[0] + p2[0]) / 2.0 + cy = (p1[1] + p2[1]) / 2.0 + + dx_px = cx - fixed_point[0] + dy_px = cy - fixed_point[1] + dx_mm = dx_px * scale_x + dy_mm = dy_px * scale_y + + return cx, cy, dx_mm, dy_mm + + +def visualize_result(image, keypoints, fixed_point, offset_info, save_path): + """ + 可视化关键点、参考点、中心点、偏移箭头和文字 + """ + vis = image.copy() + colors = [(0, 0, 255), (255, 0, 0), (0, 255, 0), (255, 255, 0)] + cx, cy, dx_mm, dy_mm = offset_info + fx, fy = map(int, fixed_point) + + # 绘制关键点 + for i, (x, y, conf) in enumerate(keypoints): + if conf > 0.5: + cv2.circle(vis, (int(x), int(y)), 8, colors[i], -1) + cv2.putText(vis, f"P{i}", (int(x) + 10, int(y) - 10), + cv2.FONT_HERSHEY_SIMPLEX, 0.8, colors[i], 2) + + # 绘制中心点 + cv2.circle(vis, (int(cx), int(cy)), 12, (0, 255, 0), 3) + cv2.putText(vis, "Center", (int(cx) + 20, int(cy)), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 3) + + # 绘制参考点 + cv2.circle(vis, (fx, fy), 15, (255, 255, 0), 3) + cv2.putText(vis, "Ref", (fx + 20, fy), + cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 0), 3) + + # 绘制偏移箭头和文字 + cv2.arrowedLine(vis, (fx, fy), (int(cx), int(cy)), (0, 255, 255), 3, tipLength=0.05) + cv2.putText(vis, f"DeltaX={dx_mm:+.1f}mm", (fx + 40, fy - 40), + cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 255), 3) + cv2.putText(vis, f"DeltaY={dy_mm:+.1f}mm", (fx + 40, fy + 40), + cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 255), 3) + + cv2.imwrite(save_path, vis) + + +def calculate_offset_from_image(image_path, visualize=False): + """ + 主函数:输入图片路径,输出偏移量 (dx_mm, dy_mm) + + 参数: + image_path (str): 输入图像路径 + visualize (bool): 是否保存可视化结果 + + 返回: + dict: { + 'success': bool, + 'dx_mm': float or None, + 'dy_mm': float or None, + 'cx': float or None, # 中心点 x + 'cy': float or None, # 中心点 y + 'message': str + } + """ + # 读取图像 + orig = cv2.imread(image_path) + if orig is None: + return { + 'success': False, + 'dx_mm': None, 'dy_mm': None, 'cx': None, 'cy': None, + 'message': f'Failed to load image: {image_path}' + } + + h0, w0 = orig.shape[:2] + + # 预处理 + img_resized, scale, dx, dy = letterbox_resize(orig, IMG_SIZE) + infer_img = np.expand_dims(img_resized[..., ::-1], 0).astype(np.uint8) + + # 加载模型并推理 + rknn = RKNNLite(verbose=False) + ret = rknn.load_rknn(MODEL_PATH) + if ret != 0: + return { + 'success': False, + 'dx_mm': None, 'dy_mm': None, 'cx': None, 'cy': None, + 'message': 'Failed to load RKNN model' + } + + try: + rknn.init_runtime(core_mask=RKNNLite.NPU_CORE_0) + outputs = rknn.inference([infer_img]) + except Exception as e: + rknn.release() + return { + 'success': False, + 'dx_mm': None, 'dy_mm': None, 'cx': None, 'cy': None, + 'message': f'Inference error: {str(e)}' + } + finally: + rknn.release() + + # 解析关键点 + try: + keypoints = parse_pose_outputs(outputs, dx=dx, dy=dy, scale=scale) + except Exception as e: + return { + 'success': False, + 'dx_mm': None, 'dy_mm': None, 'cx': None, 'cy': None, + 'message': f'Parse keypoint error: {str(e)}' + } + + # 计算偏移 + offset_info = compute_offset(keypoints, FIXED_REF_POINT, SCALE_X, SCALE_Y) + if offset_info is None: + return { + 'success': False, + 'dx_mm': None, 'dy_mm': None, 'cx': None, 'cy': None, + 'message': 'Not enough keypoints to compute offset' + } + + cx, cy, dx_mm, dy_mm = offset_info + + # 可视化(可选) + if visualize: + vis_save_path = os.path.join(OUTPUT_DIR, f"result_{os.path.basename(image_path)}") + visualize_result(orig, keypoints, FIXED_REF_POINT, offset_info, vis_save_path) + + return { + 'success': True, + 'dx_mm': dx_mm, + 'dy_mm': dy_mm, + 'cx': cx, + 'cy': cy, + 'message': 'Success' + } + + +# ====================== 使用示例 ====================== +if __name__ == "__main__": + image_path = "11.jpg" + + result = calculate_offset_from_image(image_path, visualize=True) + + if result['success']: + print(f"Center point: ({result['cx']:.1f}, {result['cy']:.1f})") + print(f"Offset: DeltaX={result['dx_mm']:+.2f} mm, DeltaY={result['dy_mm']:+.2f} mm") + else: + print("Error:", result['message']) \ No newline at end of file diff --git a/rknn_bushu/yolov11_point/point.rknn b/rknn_bushu/yolov11_point/point.rknn new file mode 100644 index 0000000..1ba05e9 Binary files /dev/null and b/rknn_bushu/yolov11_point/point.rknn differ diff --git a/rknn_bushu/yolov11_seg/2.jpg b/rknn_bushu/yolov11_seg/2.jpg new file mode 100644 index 0000000..dcd5369 Binary files /dev/null and b/rknn_bushu/yolov11_seg/2.jpg differ diff --git a/rknn_bushu/yolov11_seg/seg.rknn b/rknn_bushu/yolov11_seg/seg.rknn new file mode 100644 index 0000000..7df9e1c Binary files /dev/null and b/rknn_bushu/yolov11_seg/seg.rknn differ diff --git a/rknn_bushu/yolov11_seg/seg_angle.py b/rknn_bushu/yolov11_seg/seg_angle.py new file mode 100644 index 0000000..0463945 --- /dev/null +++ b/rknn_bushu/yolov11_seg/seg_angle.py @@ -0,0 +1,75 @@ +import os +import cv2 +import numpy as np +from rknnlite.api import RKNNLite + +objectThresh = 0.5 # 置信度阈值,可调整 + +def letterbox_resize(image, size, bg_color=114): + target_width, target_height = size + h, w, _ = image.shape + scale = min(target_width / w, target_height / h) + new_w, new_h = int(w * scale), int(h * scale) + resized = cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_AREA) + canvas = np.ones((target_height, target_width, 3), dtype=np.uint8) * bg_color + offset_x, offset_y = (target_width - new_w) // 2, (target_height - new_h) // 2 + canvas[offset_y:offset_y+new_h, offset_x:offset_x+new_w] = resized + return canvas, scale, offset_x, offset_y + +def sigmoid(x): + return 1 / (1 + np.exp(-x)) + +def detect_top2_single_masks(model_path, image_path): + img = cv2.imread(image_path) + if img is None: + print(f"❌ 无法读取图像 {image_path}") + return [] + + img_resized, scale, offset_x, offset_y = letterbox_resize(img, (640, 640)) + infer_img = img_resized[..., ::-1] # BGR->RGB + infer_img = np.expand_dims(infer_img, 0) + + rknn = RKNNLite(verbose=True) + rknn.load_rknn(model_path) + rknn.init_runtime(core_mask=RKNNLite.NPU_CORE_0) + outputs = rknn.inference([infer_img]) + rknn.release() + + mask_coeffs_list = [outputs[3], outputs[7], outputs[11]] # mask coefficients + conf_list = [outputs[1], outputs[5], outputs[9]] # object confidence + proto = outputs[12][0] # proto [32,160,160] + + # 遍历所有候选,生成(置信度, 尺度索引, 目标索引) + candidates = [] + for scale_idx, conf_map in enumerate(conf_list): + conf_map_flat = conf_map.flatten() + for idx, conf in enumerate(conf_map_flat): + if conf > objectThresh: + candidates.append((conf, scale_idx, idx)) + + if not candidates: + print(f"⚠️ 未检测到目标,置信度低于 {objectThresh}") + return [] + + # 按置信度排序,取前两个 + candidates.sort(key=lambda x: x[0], reverse=True) + top2 = candidates[:2] + + masks = [] + for i, (conf, scale_idx, idx) in enumerate(top2): + coeff = mask_coeffs_list[scale_idx].reshape(mask_coeffs_list[scale_idx].shape[1], -1) + mask_flat = np.matmul(coeff[:, idx], proto.reshape(proto.shape[0], -1)) + mask = sigmoid(mask_flat).reshape(proto.shape[1], proto.shape[2]) + mask_resized = cv2.resize(mask, (img.shape[1], img.shape[0])) + mask_bin = (mask_resized > 0.5).astype(np.uint8) * 255 + masks.append(mask_bin) + cv2.imwrite(f"mask_single_{i+1}.png", mask_bin) + print(f"✅ mask {i+1} 已保存: mask_single_{i+1}.png") + + return masks + +if __name__ == "__main__": + model_path = "seg.rknn" + image_path = "2.jpg" + detect_top2_single_masks(model_path, image_path) + diff --git a/ultralytics_yolo11-main/data.yaml b/ultralytics_yolo11-main/data.yaml index 80a371e..006ee88 100644 --- a/ultralytics_yolo11-main/data.yaml +++ b/ultralytics_yolo11-main/data.yaml @@ -1,4 +1,4 @@ -path: /media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/dataset2 # 数据集所在路径 +path: /media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/yemian_seg_camera01 # 数据集所在路径 train: train # 数据集路径下的train.txt val: val # 数据集路径下的val.txt test: test # 数据集路径下的test.txt diff --git a/ultralytics_yolo11-main/data_ailai.yaml b/ultralytics_yolo11-main/data_ailai.yaml new file mode 100644 index 0000000..4c14415 --- /dev/null +++ b/ultralytics_yolo11-main/data_ailai.yaml @@ -0,0 +1,59 @@ +# Ultralytics YOLO 🚀, AGPL-3.0 license +# DOTA8 dataset 8 images from split DOTAv1 dataset by Ultralytics +# Documentation: https://docs.ultralytics.com/datasets/obb/dota8/ +# Example usage: yolo train model=yolov8n-obb.pt data=dota8.yaml +# parent +# ├── ultralytics +# └── datasets +# └── dota8 ← downloads here (1MB) + +# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..] +path: /media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/point2 +train: train # 数据集路径下的train.txt +val: val # 数据集路径下的val.txt +test: test # 数据集路径下的test.txt + +nc: 1 # number of classes +kpt_shape: [4, 3] # number of keypoints, number of dims (2 for x,y or 3 for x,y,visible) +scales: # model compound scaling constants, i.e. 'model=yolo11n-pose.yaml' will call yolo11.yaml with scale 'n' + # [depth, width, max_channels] + n: [0.50, 0.25, 1024] # summary: 344 layers, 2908507 parameters, 2908491 gradients, 7.7 GFLOPs + s: [0.50, 0.50, 1024] # summary: 344 layers, 9948811 parameters, 9948795 gradients, 23.5 GFLOPs + m: [0.50, 1.00, 512] # summary: 434 layers, 20973273 parameters, 20973257 gradients, 72.3 GFLOPs + l: [1.00, 1.00, 512] # summary: 656 layers, 26230745 parameters, 26230729 gradients, 91.4 GFLOPs + x: [1.00, 1.50, 512] # summary: 656 layers, 58889881 parameters, 58889865 gradients, 204.3 GFLOPs + +# YOLO11n backbone +backbone: + # [from, repeats, module, args] + - [-1, 1, Conv, [64, 3, 2]] # 0-P1/2 + - [-1, 1, Conv, [128, 3, 2]] # 1-P2/4 + - [-1, 2, C3k2, [256, False, 0.25]] + - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8 + - [-1, 2, C3k2, [512, False, 0.25]] + - [-1, 1, Conv, [512, 3, 2]] # 5-P4/16 + - [-1, 2, C3k2, [512, True]] + - [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32 + - [-1, 2, C3k2, [1024, True]] + - [-1, 1, SPPF, [1024, 5]] # 9 + - [-1, 2, C2PSA, [1024]] # 10 + +# YOLO11n head +head: + - [-1, 1, nn.Upsample, [None, 2, "nearest"]] + - [[-1, 6], 1, Concat, [1]] # cat backbone P4 + - [-1, 2, C3k2, [512, False]] # 13 + + - [-1, 1, nn.Upsample, [None, 2, "nearest"]] + - [[-1, 4], 1, Concat, [1]] # cat backbone P3 + - [-1, 2, C3k2, [256, False]] # 16 (P3/8-small) + + - [-1, 1, Conv, [256, 3, 2]] + - [[-1, 13], 1, Concat, [1]] # cat head P4 + - [-1, 2, C3k2, [512, False]] # 19 (P4/16-medium) + + - [-1, 1, Conv, [512, 3, 2]] + - [[-1, 10], 1, Concat, [1]] # cat head P5 + - [-1, 2, C3k2, [1024, True]] # 22 (P5/32-large) + + - [[16, 19, 22], 1, Pose, [nc, kpt_shape]] # Detect(P3, P4, P5) \ No newline at end of file diff --git a/ultralytics_yolo11-main/obb_data1.yaml b/ultralytics_yolo11-main/obb_data1.yaml new file mode 100644 index 0000000..f6be844 --- /dev/null +++ b/ultralytics_yolo11-main/obb_data1.yaml @@ -0,0 +1,18 @@ +# Ultralytics YOLO 🚀, AGPL-3.0 license +# DOTA8 dataset 8 images from split DOTAv1 dataset by Ultralytics +# Documentation: https://docs.ultralytics.com/datasets/obb/dota8/ +# Example usage: yolo train model=yolov8n-obb.pt data=dota8.yaml +# parent +# ├── ultralytics +# └── datasets +# └── dota8 ← downloads here (1MB) + +# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..] +path: /media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/obb4 +train: train # 数据集路径下的train.txt +val: val # 数据集路径下的val.txt +test: test # 数据集路径下的test.txt + +nc: 1 +names: ['clamp'] + diff --git a/ultralytics_yolo11-main/resize_seg_data.yaml b/ultralytics_yolo11-main/resize_seg_data.yaml index 51f541d..2995a65 100644 --- a/ultralytics_yolo11-main/resize_seg_data.yaml +++ b/ultralytics_yolo11-main/resize_seg_data.yaml @@ -1,4 +1,4 @@ -path: /media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/resize_seg2 # 数据集所在路径 +path: /media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/resize_camera01 # 数据集所在路径 train: train # 数据集路径下的train.txt val: val # 数据集路径下的val.txt test: test # 数据集路径下的test.txt diff --git a/ultralytics_yolo11-main/train_ailai_main.py b/ultralytics_yolo11-main/train_ailai_main.py new file mode 100644 index 0000000..237d320 --- /dev/null +++ b/ultralytics_yolo11-main/train_ailai_main.py @@ -0,0 +1,19 @@ +from ultralytics import YOLO + +if __name__ == '__main__': + #model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11-obb.yaml') + model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/runs/train/exp_ailai3/weights/last.pt') + results = model.train( + data='data_ailai.yaml', + epochs=300, + imgsz=640, + batch=4, + workers=10, + device='0', + project='runs/train', + name='exp_ailai', + exist_ok=False, + optimizer='AdamW', + lr0=0.0001, + patience=0, + ) diff --git a/ultralytics_yolo11-main/train_obb_main.py b/ultralytics_yolo11-main/train_obb_main.py index 94352e2..a917374 100644 --- a/ultralytics_yolo11-main/train_obb_main.py +++ b/ultralytics_yolo11-main/train_obb_main.py @@ -16,3 +16,4 @@ if __name__ == '__main__': optimizer='AdamW', lr0=0.001, ) +# \ No newline at end of file diff --git a/ultralytics_yolo11-main/train_obb_zengqiang.py b/ultralytics_yolo11-main/train_obb_zengqiang.py new file mode 100644 index 0000000..fd730ce --- /dev/null +++ b/ultralytics_yolo11-main/train_obb_zengqiang.py @@ -0,0 +1,40 @@ +from ultralytics import YOLO + +if __name__ == '__main__': + # ✅ 推荐:加载官方预训练模型 或 自己的 best.pt + # model = YOLO('yolo11m-obb.pt') # 官方预训练(如果有) + model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/obb.pt') # 使用 best 而非 last + + results = model.train( + data='obb_data1.yaml', + epochs=300, # 减少 epochs,配合早停 + patience=0, # 50 轮无提升则停止 + imgsz=640, + batch=4, + workers=10, + device='0', + project='runs/train', + name='exp_obb5', # 建议递增实验编号 + exist_ok=False, + + # 优化器 + optimizer='AdamW', + lr0=0.0005, # 更稳定的学习率 + weight_decay=0.01, + momentum=0.937, + + # 数据增强(OBB 关键) + degrees=5.0, # 随机旋转 ±10° + translate=0.1, # 平移 + scale=0.5, # 缩放比例 + shear=1.0, # 剪切 + flipud=0.0, # 不推荐上下翻转(角度易错) + fliplr=0.5, # ✅ 水平翻转,OBB 支持良好 + hsv_h=0.015, # 色调扰动 + hsv_s=0.7, # 饱和度 + hsv_v=0.4, # 明度 + + # 其他 + close_mosaic=50, # 最后10轮关闭 Mosaic 增强,提升稳定性 + val=True, # 每轮验证 + ) \ No newline at end of file diff --git a/ultralytics_yolo11-main/train_resize_cls.py b/ultralytics_yolo11-main/train_resize_cls.py index 8461e9b..b303572 100644 --- a/ultralytics_yolo11-main/train_resize_cls.py +++ b/ultralytics_yolo11-main/train_resize_cls.py @@ -4,7 +4,7 @@ if __name__ == '__main__': model = YOLO(r'/home/hx/yolo/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11-cls-resize.yaml') results = model.train( data='/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/classdata3', - epochs=1000, + epochs=100, imgsz=640, batch=4, workers=10, @@ -13,6 +13,6 @@ if __name__ == '__main__': name='exp_cls', exist_ok=False, optimizer='AdamW', - lr0=0.001, + lr0=0.0003, patience=0, ) diff --git a/ultralytics_yolo11-main/train_seg_main.py b/ultralytics_yolo11-main/train_seg_main.py index 53146f2..74f25ff 100644 --- a/ultralytics_yolo11-main/train_seg_main.py +++ b/ultralytics_yolo11-main/train_seg_main.py @@ -11,7 +11,7 @@ if __name__ == '__main__': batch=4, # 每批图像数量 workers=10, # 数据加载线程数 device='0', # 使用 GPU 0 - project='runs/train/seg_j', # 保存项目目录 + project='runs/train/seg_01', # 保存项目目录 name='exp', # 实验名称 exist_ok=False, # 不覆盖已有实验 optimizer='AdamW', # 可选优化器 diff --git a/ultralytics_yolo11-main/train_seg_resize_main.py b/ultralytics_yolo11-main/train_seg_resize_main.py index 13fb8e1..05c46a6 100644 --- a/ultralytics_yolo11-main/train_seg_resize_main.py +++ b/ultralytics_yolo11-main/train_seg_resize_main.py @@ -7,8 +7,8 @@ if __name__ == '__main__': # 开始训练 results = model.train( data='/home/hx/yolo/ultralytics_yolo11-main/resize_seg_data.yaml', # 数据配置文件 - epochs=1000, # 训练轮数 - imgsz=640, + epochs=100, # 训练轮数 + imgsz=1280, batch=4, # 每批图像数量 workers=10, # 数据加载线程数 device='0', # 使用 GPU 0 @@ -16,6 +16,6 @@ if __name__ == '__main__': name='exp', # 实验名称 exist_ok=False, # 不覆盖已有实验 optimizer='AdamW', # 可选优化器 - lr0=0.001, # 初始学习率 - patience=500, # 早停轮数 + lr0=0.0005, # 初始学习率 + patience=0, # 早停轮数 ) \ No newline at end of file diff --git a/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11-ailai.yaml b/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11-ailai.yaml new file mode 100644 index 0000000..2de92c7 --- /dev/null +++ b/ultralytics_yolo11-main/ultralytics/cfg/models/11/yolo11-ailai.yaml @@ -0,0 +1,48 @@ +# Ultralytics YOLO 🚀, AGPL-3.0 license +# YOLO11-pose keypoints/pose estimation model. For Usage examples see https://docs.ultralytics.com/tasks/pose + +# Parameters +nc: 1 # number of classes +kpt_shape: [4, 3] # number of keypoints, number of dims (2 for x,y or 3 for x,y,visible) +scales: # model compound scaling constants, i.e. 'model=yolo11n-pose.yaml' will call yolo11.yaml with scale 'n' + # [depth, width, max_channels] + n: [0.50, 0.25, 1024] # summary: 344 layers, 2908507 parameters, 2908491 gradients, 7.7 GFLOPs + s: [0.50, 0.50, 1024] # summary: 344 layers, 9948811 parameters, 9948795 gradients, 23.5 GFLOPs + m: [0.50, 1.00, 512] # summary: 434 layers, 20973273 parameters, 20973257 gradients, 72.3 GFLOPs + l: [1.00, 1.00, 512] # summary: 656 layers, 26230745 parameters, 26230729 gradients, 91.4 GFLOPs + x: [1.00, 1.50, 512] # summary: 656 layers, 58889881 parameters, 58889865 gradients, 204.3 GFLOPs + +# YOLO11n backbone +backbone: + # [from, repeats, module, args] + - [-1, 1, Conv, [64, 3, 2]] # 0-P1/2 + - [-1, 1, Conv, [128, 3, 2]] # 1-P2/4 + - [-1, 2, C3k2, [256, False, 0.25]] + - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8 + - [-1, 2, C3k2, [512, False, 0.25]] + - [-1, 1, Conv, [512, 3, 2]] # 5-P4/16 + - [-1, 2, C3k2, [512, True]] + - [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32 + - [-1, 2, C3k2, [1024, True]] + - [-1, 1, SPPF, [1024, 5]] # 9 + - [-1, 2, C2PSA, [1024]] # 10 + +# YOLO11n head +head: + - [-1, 1, nn.Upsample, [None, 2, "nearest"]] + - [[-1, 6], 1, Concat, [1]] # cat backbone P4 + - [-1, 2, C3k2, [512, False]] # 13 + + - [-1, 1, nn.Upsample, [None, 2, "nearest"]] + - [[-1, 4], 1, Concat, [1]] # cat backbone P3 + - [-1, 2, C3k2, [256, False]] # 16 (P3/8-small) + + - [-1, 1, Conv, [256, 3, 2]] + - [[-1, 13], 1, Concat, [1]] # cat head P4 + - [-1, 2, C3k2, [512, False]] # 19 (P4/16-medium) + + - [-1, 1, Conv, [512, 3, 2]] + - [[-1, 10], 1, Concat, [1]] # cat head P5 + - [-1, 2, C3k2, [1024, True]] # 22 (P5/32-large) + + - [[16, 19, 22], 1, Pose, [nc, kpt_shape]] # Detect(P3, P4, P5) diff --git a/yemian/__pycache__/test_edge.cpython-39-pytest-8.4.1.pyc b/yemian/__pycache__/test_edge.cpython-39-pytest-8.4.1.pyc new file mode 100644 index 0000000..a0e3d71 Binary files /dev/null and b/yemian/__pycache__/test_edge.cpython-39-pytest-8.4.1.pyc differ diff --git a/yemian/__pycache__/test_two_line.cpython-39-pytest-8.4.1.pyc b/yemian/__pycache__/test_two_line.cpython-39-pytest-8.4.1.pyc new file mode 100644 index 0000000..41e41b3 Binary files /dev/null and b/yemian/__pycache__/test_two_line.cpython-39-pytest-8.4.1.pyc differ diff --git a/yemian/__pycache__/val_labels.cpython-39.pyc b/yemian/__pycache__/val_labels.cpython-39.pyc new file mode 100644 index 0000000..3a57421 Binary files /dev/null and b/yemian/__pycache__/val_labels.cpython-39.pyc differ diff --git a/yemian/best.pt b/yemian/best.pt new file mode 100644 index 0000000..9a7e8a1 Binary files /dev/null and b/yemian/best.pt differ diff --git a/yemian/txt_1.py b/yemian/lianghua_txt.py similarity index 100% rename from yemian/txt_1.py rename to yemian/lianghua_txt.py diff --git a/yemian/resize/best.pt b/yemian/resize/best.pt new file mode 100644 index 0000000..7cd4889 Binary files /dev/null and b/yemian/resize/best.pt differ diff --git a/yemian/resize/resize1/test/frame_20250805_121717_22320.jpg b/yemian/resize/resize1/test/frame_20250805_121717_22320.jpg new file mode 100644 index 0000000..dfaede6 Binary files /dev/null and b/yemian/resize/resize1/test/frame_20250805_121717_22320.jpg differ diff --git a/yemian/resize/resize1/test/frame_20250805_121717_22320.txt b/yemian/resize/resize1/test/frame_20250805_121717_22320.txt new file mode 100644 index 0000000..79b0162 --- /dev/null +++ b/yemian/resize/resize1/test/frame_20250805_121717_22320.txt @@ -0,0 +1 @@ +0 0.317016 0.412804 0.274836 0.624093 0.466866 0.652477 0.748806 0.636707 0.729938 0.409651 0.482407 0.422264 0.436896 0.425418 \ No newline at end of file diff --git a/yemian/resize/resize1/train/frame_20250805_121723_22500.jpg b/yemian/resize/resize1/train/frame_20250805_121723_22500.jpg new file mode 100644 index 0000000..f00a953 Binary files /dev/null and b/yemian/resize/resize1/train/frame_20250805_121723_22500.jpg differ diff --git a/yemian/resize/resize1/train/frame_20250805_121723_22500.txt b/yemian/resize/resize1/train/frame_20250805_121723_22500.txt new file mode 100644 index 0000000..b3b6eb0 --- /dev/null +++ b/yemian/resize/resize1/train/frame_20250805_121723_22500.txt @@ -0,0 +1 @@ +0 0.314796 0.422264 0.268177 0.620940 0.462427 0.652477 0.756576 0.627247 0.732158 0.409651 0.480187 0.425418 0.432457 0.431725 \ No newline at end of file diff --git a/yemian/resize/resize1/val/frame_20250805_121800_23580.jpg b/yemian/resize/resize1/val/frame_20250805_121800_23580.jpg new file mode 100644 index 0000000..31f7b2c Binary files /dev/null and b/yemian/resize/resize1/val/frame_20250805_121800_23580.jpg differ diff --git a/yemian/resize/resize1/val/frame_20250805_121800_23580.txt b/yemian/resize/resize1/val/frame_20250805_121800_23580.txt new file mode 100644 index 0000000..3315bf7 --- /dev/null +++ b/yemian/resize/resize1/val/frame_20250805_121800_23580.txt @@ -0,0 +1 @@ +0 0.315906 0.409651 0.274836 0.617786 0.466866 0.643014 0.751026 0.630400 0.733267 0.406497 0.490177 0.422264 0.434676 0.419111 \ No newline at end of file diff --git a/yemian/resize/resize_dataset_image.py b/yemian/resize/resize_dataset_image.py new file mode 100644 index 0000000..19ae2f0 --- /dev/null +++ b/yemian/resize/resize_dataset_image.py @@ -0,0 +1,52 @@ +import os +import cv2 + +# ---------------------------- +# 配置 +# ---------------------------- +SOURCE_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/resize_seg2" # 原始图片目录 +TARGET_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/resize_seg3" # 输出目录 +TARGET_SIZE = 640 # resize 尺寸 +SUBSETS = ["train", "val", "test"] + +# ---------------------------- +# 全局 ROI (x, y, w, h) +# ---------------------------- +GLOBAL_ROI = [562, 798, 1287, 453] + +# ---------------------------- +# 主处理函数 +# ---------------------------- +def process_images(): + x, y, w, h = GLOBAL_ROI + for subset in SUBSETS: + src_dir = os.path.join(SOURCE_DIR, subset) + tgt_dir = os.path.join(TARGET_DIR, subset) + os.makedirs(tgt_dir, exist_ok=True) + + for file in os.listdir(src_dir): + if not (file.endswith(".jpg") or file.endswith(".png")): + continue + + img_path = os.path.join(src_dir, file) + img = cv2.imread(img_path) + if img is None: + print(f"❌ 无法读取图片: {img_path}") + continue + + h_img, w_img = img.shape[:2] + x1, y1 = max(0, x), max(0, y) + x2, y2 = min(w_img, x + w), min(h_img, y + h) + + cropped = img[y1:y2, x1:x2] + if cropped.size == 0: + print(f"❌ 裁剪结果为空: {file}") + continue + + resized = cv2.resize(cropped, (TARGET_SIZE, TARGET_SIZE)) + tgt_path = os.path.join(tgt_dir, file) + cv2.imwrite(tgt_path, resized) + print(f"✅ 图片处理完成: {subset}/{file}") + +if __name__ == "__main__": + process_images() diff --git a/yemian/resize/resize_dataset_label.py b/yemian/resize/resize_dataset_label.py new file mode 100644 index 0000000..836978c --- /dev/null +++ b/yemian/resize/resize_dataset_label.py @@ -0,0 +1,93 @@ +import os + +# ---------------------------- +# 配置 +# ---------------------------- +SOURCE_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/resize_seg2" # 原始图片目录 +TARGET_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/resize_seg3" # 输出目录 +SUBSETS = ["train", "val", "test"] + +# 全局 ROI (x, y, w, h) +GLOBAL_ROI = [562, 798, 1287, 453] + +# ---------------------------- +# 分割标签处理函数 +# ---------------------------- +def adjust_seg_labels(label_path, crop_coords, orig_size): + x1, y1, x2, y2 = crop_coords + crop_w = x2 - x1 + crop_h = y2 - y1 + new_labels = [] + + if not os.path.exists(label_path): + print(f"⚠️ 标签源文件不存在: {label_path}") + return new_labels + + with open(label_path, 'r') as f: + lines = f.readlines() + + orig_w, orig_h = orig_size + + for line in lines: + parts = line.strip().split() + if len(parts) < 3 or len(parts) % 2 == 0: + continue # 至少类别 + 一个点(x,y) + + cls = parts[0] + coords = list(map(float, parts[1:])) + + new_coords = [] + for i in range(0, len(coords), 2): + x = coords[i] * orig_w + y = coords[i+1] * orig_h + x -= x1 + y -= y1 + x_new = x / crop_w + y_new = y / crop_h + new_coords.extend([x_new, y_new]) + + new_line = cls + " " + " ".join([f"{c:.6f}" for c in new_coords]) + new_labels.append(new_line) + + return new_labels + +# ---------------------------- +# 主处理函数 +# ---------------------------- +def process_seg_labels(): + x, y, w, h = GLOBAL_ROI + fixed_roi = (x, y, x + w, y + h) + + for subset in SUBSETS: + src_dir = os.path.join(SOURCE_DIR, subset) + tgt_dir = os.path.join(TARGET_DIR, subset) + os.makedirs(tgt_dir, exist_ok=True) + + for file in os.listdir(src_dir): + if not (file.endswith(".txt")): + continue + + label_path = os.path.join(src_dir, file) + # 读取原图尺寸 + img_name = os.path.splitext(file)[0] + ".jpg" + img_path = os.path.join(src_dir, img_name) + if not os.path.exists(img_path): + print(f"❌ 无法读取图片以获取尺寸: {img_path}") + continue + + import cv2 + img = cv2.imread(img_path) + if img is None: + print(f"❌ 无法读取图片: {img_path}") + continue + h_img, w_img = img.shape[:2] + + new_labels = adjust_seg_labels(label_path, fixed_roi, (w_img, h_img)) + tgt_label_path = os.path.join(tgt_dir, file) + with open(tgt_label_path, 'w') as f: + f.write("\n".join(new_labels)) + + print(f"✅ 标签处理完成: {subset}/{file}, 条数 {len(new_labels)}") + +if __name__ == "__main__": + process_seg_labels() diff --git a/yemian/resize/roi_coordinates/1_rois2.txt b/yemian/resize/roi_coordinates/1_rois2.txt new file mode 100644 index 0000000..f8f68b7 --- /dev/null +++ b/yemian/resize/roi_coordinates/1_rois2.txt @@ -0,0 +1 @@ +562, 798, 1287, 453 diff --git a/yemian/resize/rtest.py b/yemian/resize/rtest.py index afca979..d3331ae 100644 --- a/yemian/resize/rtest.py +++ b/yemian/resize/rtest.py @@ -6,12 +6,12 @@ import numpy as np from ultralytics import YOLO from pathlib import Path -# ====================== 配置参数 ====================== -MODEL_PATH = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/seg_r/exp2/weights/best.pt" +# ====================== 配置参数 ======================3 +MODEL_PATH = "best.pt" #SOURCE_IMG_DIR = "/home/hx/yolo/output_masks" # 原始输入图像目录 -SOURCE_IMG_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/f6" # 原始输入图像目录 +SOURCE_IMG_DIR = "/home/hx/yolo/yemian/test_image" # 原始输入图像目录 OUTPUT_DIR = "/home/hx/yolo/output_masks2" # 推理输出根目录 -ROI_COORDS_FILE = "./roi_coordinates/1_rois.txt" # 必须与训练时相同 +ROI_COORDS_FILE = "./roi_coordinates/1_rois2.txt" # 必须与训练时相同 CONF_THRESHOLD = 0.25 IOU_THRESHOLD = 0.45 DEVICE = "cuda:0" if torch.cuda.is_available() else "cpu" diff --git a/yemian/resize/test/test/frame_20250805_121717_22320.jpg b/yemian/resize/test/test/frame_20250805_121717_22320.jpg new file mode 100755 index 0000000..f70f296 Binary files /dev/null and b/yemian/resize/test/test/frame_20250805_121717_22320.jpg differ diff --git a/yemian/resize/test/test/frame_20250805_121717_22320.txt b/yemian/resize/test/test/frame_20250805_121717_22320.txt new file mode 100755 index 0000000..2d31c4b --- /dev/null +++ b/yemian/resize/test/test/frame_20250805_121717_22320.txt @@ -0,0 +1 @@ +0 0.378906 0.684028 0.357701 0.750496 0.454241 0.759425 0.595982 0.754464 0.586496 0.683036 0.462054 0.687004 0.439174 0.687996 diff --git a/yemian/resize/test/train/frame_20250805_121723_22500.jpg b/yemian/resize/test/train/frame_20250805_121723_22500.jpg new file mode 100755 index 0000000..7848e56 Binary files /dev/null and b/yemian/resize/test/train/frame_20250805_121723_22500.jpg differ diff --git a/yemian/resize/test/train/frame_20250805_121723_22500.txt b/yemian/resize/test/train/frame_20250805_121723_22500.txt new file mode 100755 index 0000000..7a053b2 --- /dev/null +++ b/yemian/resize/test/train/frame_20250805_121723_22500.txt @@ -0,0 +1 @@ +0 0.377790 0.687004 0.354353 0.749504 0.452009 0.759425 0.599888 0.751488 0.587612 0.683036 0.460938 0.687996 0.436942 0.689980 diff --git a/yemian/resize/test/val/frame_20250805_121800_23580.jpg b/yemian/resize/test/val/frame_20250805_121800_23580.jpg new file mode 100755 index 0000000..3ec4e91 Binary files /dev/null and b/yemian/resize/test/val/frame_20250805_121800_23580.jpg differ diff --git a/yemian/resize/test/val/frame_20250805_121800_23580.txt b/yemian/resize/test/val/frame_20250805_121800_23580.txt new file mode 100755 index 0000000..e02e37b --- /dev/null +++ b/yemian/resize/test/val/frame_20250805_121800_23580.txt @@ -0,0 +1 @@ +0 0.378348 0.683036 0.357701 0.748512 0.454241 0.756448 0.597098 0.752480 0.588170 0.682044 0.465960 0.687004 0.438058 0.686012 diff --git a/yemian/resize_labels.py b/yemian/resize_labels.py new file mode 100644 index 0000000..575ece1 --- /dev/null +++ b/yemian/resize_labels.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +import os, cv2, numpy as np + +# ========== 配置 ========== +roi_label_dir = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/label" # 640x640 ROI 标签 +original_image_dir = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/class1" +roi_coords_file = "/home/hx/yolo/zhuangtai_class_cls/roi_coordinates/1_rois.txt" # 每行: x,y,w,h +output_label_dir = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/classdata3_orig_norm_labels" +debug_vis = True +debug_vis_dir = output_label_dir + "_vis" +# ========================== + +os.makedirs(output_label_dir, exist_ok=True) +if debug_vis: + os.makedirs(debug_vis_dir, exist_ok=True) + +# ----------- 加载 ROI ----------- +with open(roi_coords_file, 'r') as f: + roi_x, roi_y, roi_w, roi_h = map(int, f.readline().strip().split(',')) + +# ----------- 处理标签 ----------- +for label_file in os.listdir(roi_label_dir): + if not label_file.lower().endswith('.txt'): + continue + base_name = os.path.splitext(label_file)[0] + + # 对应原图 + orig_img_path = None + for ext in ('.jpg','.jpeg','.png','.bmp'): + tmp_path = os.path.join(original_image_dir, base_name + ext) + if os.path.exists(tmp_path): + orig_img_path = tmp_path + break + if orig_img_path is None: + print(f"[SKIP] 未找到原图: {base_name}") + continue + + img = cv2.imread(orig_img_path) + if img is None: + print(f"[SKIP] 无法读取原图: {orig_img_path}") + continue + h_img, w_img = img.shape[:2] + + # 读取 ROI 标签 + in_path = os.path.join(roi_label_dir, label_file) + lines = [ln.strip() for ln in open(in_path, 'r') if ln.strip()] + out_lines = [] + + for ln in lines: + parts = ln.split() + cls = parts[0] + coords = list(map(float, parts[1:])) + mapped = [] + for i in range(0, len(coords), 2): + x_n, y_n = coords[i], coords[i+1] # ROI 0~1 + x_abs = roi_x + x_n * roi_w + y_abs = roi_y + y_n * roi_h + x_abs = min(max(x_abs, 0.0), w_img-1) + y_abs = min(max(y_abs, 0.0), h_img-1) + mapped.append((x_abs / w_img, y_abs / h_img)) + out_line = cls + " " + " ".join(f"{x:.6f} {y:.6f}" for x, y in mapped) + out_lines.append(out_line) + + out_path = os.path.join(output_label_dir, label_file) + with open(out_path, 'w') as fw: + fw.write("\n".join(out_lines)) + print(f"[OK] {out_path}") + + # 可视化 + if debug_vis and out_lines: + vis = img.copy() + cv2.rectangle(vis, (roi_x, roi_y), (roi_x+roi_w-1, roi_y+roi_h-1), (0,165,255), 2) + for ol in out_lines: + pts = np.array([[int(float(parts[i])*w_img), int(float(parts[i+1])*h_img)] + for parts in [ol.split()] for i in range(1, len(parts), 2)], np.int32) + if len(pts) >= 3: + cv2.polylines(vis, [pts], True, (0,0,255), 2) + vis_name = os.path.join(debug_vis_dir, os.path.basename(orig_img_path)) + cv2.imwrite(vis_name, vis) + +print("🎉 ROI 标签已还原到原图归一化完成!") diff --git a/yemian/roi_coordinates/1_rois.txt b/yemian/roi_coordinates/1_rois.txt new file mode 100644 index 0000000..bb8f71d --- /dev/null +++ b/yemian/roi_coordinates/1_rois.txt @@ -0,0 +1 @@ +859,810,696,328 diff --git a/yemian/roi_coordinates/1_rois1.txt b/yemian/roi_coordinates/1_rois1.txt new file mode 100644 index 0000000..723dc91 --- /dev/null +++ b/yemian/roi_coordinates/1_rois1.txt @@ -0,0 +1 @@ +589,789,210,319 diff --git a/yemian/segr.pt b/yemian/segr.pt new file mode 100644 index 0000000..95de616 Binary files /dev/null and b/yemian/segr.pt differ diff --git a/yemian/test.py b/yemian/test.py index a738401..4cae394 100644 --- a/yemian/test.py +++ b/yemian/test.py @@ -1,14 +1,13 @@ -import os +#!/usr/bin/env python3 import cv2 -import torch -import argparse import numpy as np -from ultralytics import YOLO from pathlib import Path +from ultralytics import YOLO +import torch -# ====================== 配置参数 ====================== -MODEL_PATH = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/seg/exp7/weights/best.pt" -SOURCE_IMG_DIR = "/home/hx/yolo/output_masks" +# ====================== 配置 ====================== +MODEL_PATH = "best.pt" +SOURCE_IMG_DIR = "/home/hx/yolo/yemian/test_image" OUTPUT_DIR = "/home/hx/yolo/output_masks2" CONF_THRESHOLD = 0.25 IOU_THRESHOLD = 0.45 @@ -17,207 +16,121 @@ SAVE_TXT = True SAVE_MASKS = True VIEW_IMG = False LINE_WIDTH = 2 +IMG_SIZE = 640 # YOLO 输入尺寸 -def plot_result_with_opacity(result, line_width=2, mask_opacity=0.5): - """ - 手动绘制 YOLO 分割结果,支持掩码透明度叠加,并修复掩码尺寸不匹配问题 - """ - img = result.orig_img.copy() # HWC, BGR +# ====================== Letterbox 缩放函数 ====================== +def letterbox_image(img, new_size=IMG_SIZE): h, w = img.shape[:2] + scale = min(new_size / w, new_size / h) + new_w, new_h = int(w*scale), int(h*scale) + resized = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_LINEAR) + canvas = np.full((new_size, new_size, 3), 114, dtype=np.uint8) + pad_w, pad_h = new_size - new_w, new_size - new_h + pad_top, pad_left = pad_h // 2, pad_w // 2 + canvas[pad_top:pad_top+new_h, pad_left:pad_left+new_w] = resized + return canvas, scale, pad_left, pad_top, new_w, new_h - # 获取原始图像尺寸 - orig_shape = img.shape[:2] # (height, width) +# ====================== 绘制 mask & 边框 ====================== +def plot_mask_on_image(result, orig_shape, scale, pad_left, pad_top, new_w, new_h, alpha=0.5): + H_ori, W_ori = orig_shape[:2] + img = np.zeros((H_ori, W_ori, 3), dtype=np.uint8) if result.masks is not None and len(result.boxes) > 0: - # 将掩码从 GPU 移到 CPU 并转为 numpy - masks = result.masks.data.cpu().numpy() # (N, H_mask, W_mask) - - # resize 掩码到原始图像尺寸 - resized_masks = [] - for mask in masks: - mask_resized = cv2.resize(mask, (w, h), interpolation=cv2.INTER_NEAREST) - mask_resized = (mask_resized > 0.5).astype(np.uint8) # 二值化 - resized_masks.append(mask_resized) - resized_masks = np.array(resized_masks) - - # 随机颜色 (BGR) - num_masks = len(result.boxes) - colors = np.random.randint(0, 255, size=(num_masks, 3), dtype=np.uint8) - - # 创建叠加层 + masks = result.masks.data.cpu().numpy() # (N, IMG_SIZE, IMG_SIZE) overlay = img.copy() - for i in range(num_masks): - color = colors[i].tolist() - mask_resized = resized_masks[i] - overlay[mask_resized == 1] = color + num_masks = len(masks) + colors = np.random.randint(0,255,(num_masks,3),dtype=np.uint8) - # 透明叠加 - cv2.addWeighted(overlay, mask_opacity, img, 1 - mask_opacity, 0, img) + for i, mask in enumerate(masks): + # 去掉 padding + mask_crop = mask[pad_top:pad_top+new_h, pad_left:pad_left+new_w] + # resize 回原图 + mask_orig = cv2.resize(mask_crop, (W_ori, H_ori), interpolation=cv2.INTER_NEAREST) + overlay[mask_orig>0.5] = colors[i].tolist() - # 绘制边界框和标签(保持不变) - if result.boxes is not None: - boxes = result.boxes.xyxy.cpu().numpy() - classes = result.boxes.cls.cpu().numpy().astype(int) - confidences = result.boxes.conf.cpu().numpy() - - colors = np.random.randint(0, 255, size=(len(classes), 3), dtype=np.uint8) - font = cv2.FONT_HERSHEY_SIMPLEX - font_scale = 0.6 - thickness = 1 - - for i in range(len(boxes)): - box = boxes[i].astype(int) - cls_id = classes[i] - conf = confidences[i] - color = colors[i].tolist() - - # 绘制矩形框 - cv2.rectangle(img, (box[0], box[1]), (box[2], box[3]), color, line_width) - - # 标签文本 - label = f"{cls_id} {conf:.2f}" - - # 获取文本大小 - (text_w, text_h), baseline = cv2.getTextSize(label, font, font_scale, thickness) - text_h += baseline - - # 绘制标签背景 - cv2.rectangle(img, (box[0], box[1] - text_h - 6), (box[0] + text_w, box[1]), color, -1) - # 绘制文本 - cv2.putText(img, label, (box[0], box[1] - 4), font, font_scale, - (255, 255, 255), thickness, cv2.LINE_AA) + cv2.addWeighted(overlay, alpha, img, 1-alpha, 0, img) return img +# ====================== 主推理 ====================== +def run_segmentation(): + print(f"🚀 加载模型: {MODEL_PATH}") + model = YOLO(MODEL_PATH) + model.to(DEVICE) -def run_segmentation_inference( - model_path, - source, - output_dir, - conf_threshold=0.25, - iou_threshold=0.45, - device="cuda:0", - save_txt=True, - save_masks=True, - view_img=False, - line_width=2, -): - print(f"🚀 加载模型: {model_path}") - print(f"💻 使用设备: {device}") - - # 加载模型 - model = YOLO(model_path) - - # 创建输出目录 - output_dir = Path(output_dir) + source = Path(SOURCE_IMG_DIR) + output_dir = Path(OUTPUT_DIR) output_dir.mkdir(parents=True, exist_ok=True) - txt_dir = output_dir / "labels" mask_dir = output_dir / "masks" - if save_txt: - txt_dir.mkdir(exist_ok=True) - if save_masks: - mask_dir.mkdir(exist_ok=True) - - # 获取图像文件列表 - source = Path(source) - if source.is_file(): - img_files = [source] - else: - img_files = list(source.glob("*.jpg")) + \ - list(source.glob("*.jpeg")) + \ - list(source.glob("*.png")) + \ - list(source.glob("*.bmp")) + if SAVE_TXT: txt_dir.mkdir(exist_ok=True) + if SAVE_MASKS: mask_dir.mkdir(exist_ok=True) + img_files = list(source.glob("*.jpg")) + list(source.glob("*.png")) if not img_files: - print(f"❌ 在 {source} 中未找到图像文件") + print(f"❌ 未找到图片") return + print(f"🖼️ 待推理图片数量: {len(img_files)}") - print(f"🖼️ 共 {len(img_files)} 张图片待推理...") - - # 推理循环 for img_path in img_files: print(f"🔍 推理: {img_path.name}") + orig_img = cv2.imread(str(img_path)) + if orig_img is None: + print(" ❌ 读取失败") + continue + H_ori, W_ori = orig_img.shape[:2] - # 执行推理 - results = model( - source=str(img_path), - conf=conf_threshold, - iou=iou_threshold, - imgsz=640, - device=device, - verbose=True - ) + # Letterbox 缩放 + img_input, scale, pad_left, pad_top, new_w, new_h = letterbox_image(orig_img, IMG_SIZE) + # YOLO 推理 + results = model(img_input, conf=CONF_THRESHOLD, iou=IOU_THRESHOLD, imgsz=IMG_SIZE, device=DEVICE) result = results[0] - orig_img = result.orig_img # 原始图像 - # ✅ 使用自定义绘制函数(支持透明度) - plotted = plot_result_with_opacity(result, line_width=line_width, mask_opacity=0.5) + # 可视化 mask + plotted = plot_mask_on_image(result, orig_img.shape, scale, pad_left, pad_top, new_w, new_h, alpha=0.5) - # 保存可视化图像 + # 保存结果 save_path = output_dir / f"seg_{img_path.name}" cv2.imwrite(str(save_path), plotted) print(f"✅ 保存结果: {save_path}") - # 保存 YOLO 格式标签(多边形) - if save_txt and result.masks is not None: - txt_path = txt_dir / (img_path.stem + ".txt") - with open(txt_path, 'w') as f: + # 保存标签 + if SAVE_TXT and result.masks is not None: + txt_path = txt_dir / f"{img_path.stem}.txt" + with open(txt_path,"w") as f: for i in range(len(result.boxes)): cls_id = int(result.boxes.cls[i]) - seg = result.masks.xy[i] # 多边形点 (N, 2) - seg = seg.flatten() - seg = seg / [orig_img.shape[1], orig_img.shape[0]] # 归一化 - seg = seg.tolist() - line = f"{cls_id} {' '.join(f'{x:.6f}' for x in seg)}\n" + seg = result.masks.xy[i].copy() + # 去掉 padding + scale 回原图 + seg[:,0] = (seg[:,0] - pad_left) * (W_ori / new_w) + seg[:,1] = (seg[:,1] - pad_top) * (H_ori / new_h) + seg_norm = seg / [W_ori, H_ori] + seg_flat = seg_norm.flatten().tolist() + line = f"{cls_id} " + " ".join(f"{x:.6f}" for x in seg_flat) + "\n" f.write(line) print(f"📝 保存标签: {txt_path}") - # 保存合并的掩码图 - if save_masks and result.masks is not None: - mask = result.masks.data.cpu().numpy() - combined_mask = (mask.sum(axis=0) > 0).astype(np.uint8) * 255 # 合并所有掩码 + # 保存 mask + if SAVE_MASKS and result.masks is not None: + masks = result.masks.data.cpu().numpy() + combined_mask = np.zeros((H_ori, W_ori), dtype=np.uint8) + for mask in masks: + mask_crop = mask[pad_top:pad_top+new_h, pad_left:pad_left+new_w] + mask_orig = cv2.resize(mask_crop, (W_ori, H_ori), interpolation=cv2.INTER_NEAREST) + combined_mask = np.maximum(combined_mask, (mask_orig>0.5).astype(np.uint8)*255) mask_save_path = mask_dir / f"mask_{img_path.stem}.png" cv2.imwrite(str(mask_save_path), combined_mask) print(f"🎨 保存掩码: {mask_save_path}") - # 实时显示(可选) - if view_img: - cv2.imshow("Segmentation Result", plotted) - if cv2.waitKey(0) == 27: # ESC 退出 + # 显示 + if VIEW_IMG: + cv2.imshow("Segmentation", plotted) + if cv2.waitKey(0)==27: cv2.destroyAllWindows() break - if view_img: - cv2.destroyAllWindows() - print(f"\n🎉 推理完成!结果保存在: {output_dir}") + print(f"🎉 推理完成!结果保存到: {output_dir}") - -# ====================== 主程序 ====================== -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--model", default=MODEL_PATH, help="模型权重路径") - parser.add_argument("--source", default=SOURCE_IMG_DIR, help="图片路径或文件夹") - parser.add_argument("--output", default=OUTPUT_DIR, help="输出目录") - parser.add_argument("--conf", type=float, default=CONF_THRESHOLD, help="置信度阈值") - parser.add_argument("--iou", type=float, default=IOU_THRESHOLD, help="IoU 阈值") - parser.add_argument("--device", default=DEVICE, help="设备: cuda:0, cpu") - parser.add_argument("--view-img", action="store_true", help="显示图像") - parser.add_argument("--save-txt", action="store_true", help="保存标签") - parser.add_argument("--save-masks", action="store_true", help="保存掩码") - - opt = parser.parse_args() - - run_segmentation_inference( - model_path=opt.model, - source=opt.source, - output_dir=opt.output, - conf_threshold=opt.conf, - iou_threshold=opt.iou, - device=opt.device, - save_txt=opt.save_txt, - save_masks=opt.save_masks, - view_img=opt.view_img, - line_width=LINE_WIDTH, - ) \ No newline at end of file +if __name__=="__main__": + run_segmentation() diff --git a/yemian/test_image/frame_20250805_121956_26985.jpg b/yemian/test_image/frame_20250805_121956_26985.jpg new file mode 100755 index 0000000..50af506 Binary files /dev/null and b/yemian/test_image/frame_20250805_121956_26985.jpg differ diff --git a/yemian/test_image/frame_20250805_122246_31770.jpg b/yemian/test_image/frame_20250805_122246_31770.jpg new file mode 100755 index 0000000..f5d5ccd Binary files /dev/null and b/yemian/test_image/frame_20250805_122246_31770.jpg differ diff --git a/yemian/test_image/frame_20250805_122346_33510.jpg b/yemian/test_image/frame_20250805_122346_33510.jpg new file mode 100755 index 0000000..086c133 Binary files /dev/null and b/yemian/test_image/frame_20250805_122346_33510.jpg differ diff --git a/yemian/val_labels.py b/yemian/val_labels.py index 84b4e44..86a257d 100644 --- a/yemian/val_labels.py +++ b/yemian/val_labels.py @@ -76,8 +76,8 @@ def draw_mask_on_image(img, labels, alpha=0.5): def main(): # ====================== 配置区 ====================== - IMG_PATH = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/resize/frame_20250805_121717_22320.jpg" # 修改为你的某张测试图 - TXT_PATH = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/resize/frame_20250805_121717_22320.txt" # 对应的 .txt 文件 + IMG_PATH = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/resize_seg3/train/frame_20250805_180639_775485.jpg" # 修改为你的某张测试图 + TXT_PATH = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/resize_seg3/train/frame_20250805_180639_775485.txt" # 对应的 .txt 文件 OUTPUT_PATH = "/home/hx/yolo/output_masks2/debug_overlay.png" # ==================================================== diff --git a/yemian/val_labels_film.py b/yemian/val_labels_film.py new file mode 100644 index 0000000..a3acea1 --- /dev/null +++ b/yemian/val_labels_film.py @@ -0,0 +1,57 @@ +import cv2 +import numpy as np +from pathlib import Path +from val_labels import load_yolo_polygon_labels,draw_mask_on_image + +# 之前的load_yolo_polygon_labels和draw_mask_on_image函数保持不变 + +def visualize_labels_in_folder(src_img_dir, src_label_dir, dst_dir, alpha=0.5): + """ + 遍历源文件夹中的所有图片和对应标签,并在图片上绘制标签后保存至目标文件夹。 + :param src_img_dir: 源图像文件夹路径 + :param src_label_dir: 源标签文件夹路径 + :param dst_dir: 目标文件夹路径,用于保存可视化结果 + :param alpha: 叠加mask的透明度 + """ + # 确保目标文件夹存在 + Path(dst_dir).mkdir(parents=True, exist_ok=True) + + # 获取所有的图片文件 + img_paths = list(Path(src_img_dir).glob('*.jpg')) + list(Path(src_img_dir).glob('*.png')) + + for img_path in img_paths: + txt_path = Path(src_label_dir) / f"{img_path.stem}.txt" + if not txt_path.exists(): + print(f"⚠️ 跳过 {img_path.name},未找到对应的标签文件") + continue + + # 读取图像 + img = cv2.imread(str(img_path)) + if img is None: + print(f"❌ 无法读取图像: {img_path}") + continue + + h, w = img.shape[:2] + print(f"🖼️ 正在处理图像: {img_path.name}, 大小: {w}x{h}") + + # 加载标签 + labels = load_yolo_polygon_labels(txt_path, img.shape) + if len(labels) == 0: + print(f"🟡 未找到有效标签: {txt_path}") + continue + + # 绘制叠加图 + result_img = draw_mask_on_image(img, labels, alpha) + + # 保存结果 + output_path = Path(dst_dir) / img_path.name + cv2.imwrite(str(output_path), result_img) + print(f"✅ 已保存可视化结果: {output_path}") + + +if __name__ == "__main__": + SRC_IMG_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/resize1" # 修改为你的源图像文件夹路径 + SRC_LABEL_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/resize1" # 修改为你的源标签文件夹路径 + DST_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/resize_seg5" # 修改为目标文件夹路径 + + visualize_labels_in_folder(SRC_IMG_DIR, SRC_LABEL_DIR, DST_DIR) \ No newline at end of file diff --git a/yemian/yemian_line/2line—distance.py b/yemian/yemian_line/2line—distance.py new file mode 100644 index 0000000..086fcca --- /dev/null +++ b/yemian/yemian_line/2line—distance.py @@ -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)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) diff --git a/yemian/yemian_line/__pycache__/2-distance_film.cpython-39-pytest-8.4.1.pyc b/yemian/yemian_line/__pycache__/2-distance_film.cpython-39-pytest-8.4.1.pyc new file mode 100644 index 0000000..5aba704 Binary files /dev/null and b/yemian/yemian_line/__pycache__/2-distance_film.cpython-39-pytest-8.4.1.pyc differ diff --git a/yemian/yemian_line/best.pt b/yemian/yemian_line/best.pt new file mode 100644 index 0000000..99f825c Binary files /dev/null and b/yemian/yemian_line/best.pt differ diff --git a/yemian/yemian_line/gaiban_edge.py b/yemian/yemian_line/gaiban_edge.py new file mode 100644 index 0000000..49a0cea --- /dev/null +++ b/yemian/yemian_line/gaiban_edge.py @@ -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") diff --git a/yemian/yemian_line/main—tuili_line.py b/yemian/yemian_line/main—tuili_line.py new file mode 100644 index 0000000..ef52536 --- /dev/null +++ b/yemian/yemian_line/main—tuili_line.py @@ -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) diff --git a/yemian/yemian_line/output/mask_edge_corners_1.jpg b/yemian/yemian_line/output/mask_edge_corners_1.jpg new file mode 100644 index 0000000..5f55400 Binary files /dev/null and b/yemian/yemian_line/output/mask_edge_corners_1.jpg differ diff --git a/yemian/yemian_line/output/mask_edge_corners_3.jpg b/yemian/yemian_line/output/mask_edge_corners_3.jpg new file mode 100644 index 0000000..2c9a12c Binary files /dev/null and b/yemian/yemian_line/output/mask_edge_corners_3.jpg differ diff --git a/yemian/yemian_line/tuili_c_f.py b/yemian/yemian_line/tuili_c_f.py new file mode 100644 index 0000000..d253895 --- /dev/null +++ b/yemian/yemian_line/tuili_c_f.py @@ -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() diff --git a/yemian/yemian_line/tuili_corner.py b/yemian/yemian_line/tuili_corner.py new file mode 100644 index 0000000..f147787 --- /dev/null +++ b/yemian/yemian_line/tuili_corner.py @@ -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) diff --git a/yolo11_obb/divid_dataset.py b/yolo11_obb/divid_dataset.py new file mode 100644 index 0000000..4040960 --- /dev/null +++ b/yolo11_obb/divid_dataset.py @@ -0,0 +1,83 @@ +import os +import shutil +import random + +def split_train_val_test(train_dir, val_dir, test_dir, val_ratio=0.1, test_ratio=0.1): + """ + 将 train 目录中的数据按比例划分为 train/val/test 三部分。 + val 和 test 各自获得不同的一部分数据。 + """ + # 检查目录 + if not os.path.exists(train_dir): + raise FileNotFoundError(f"训练目录不存在: {train_dir}") + + os.makedirs(val_dir, exist_ok=True) + os.makedirs(test_dir, exist_ok=True) + + # 支持的图片格式 + img_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp'} + + # 获取所有图片文件 + all_files = os.listdir(train_dir) + image_files = [f for f in all_files if os.path.splitext(f.lower())[1] in img_extensions] + + # 配对图片和标签 + pairs = [] + for img in image_files: + name_no_ext = os.path.splitext(img)[0] + txt = name_no_ext + '.txt' + if txt in all_files: + pairs.append((img, txt)) + else: + print(f"⚠️ 忽略 {img}:缺少对应标签文件") + + if len(pairs) == 0: + print("❌ 未找到有效数据对") + return + + total = len(pairs) + num_test = int(total * test_ratio) + num_val = int(total * val_ratio) + + print(f"✅ 共找到 {total} 组有效数据") + print(f"✅ 将移动:val={num_val}, test={num_test}") + + # 打乱并抽样 + random.shuffle(pairs) + test_sample = pairs[:num_test] + val_sample = pairs[num_test:num_test + num_val] + # 剩下的留在 train + + # 移动到 test + for img, txt in test_sample: + try: + shutil.move(os.path.join(train_dir, img), os.path.join(test_dir, img)) + shutil.move(os.path.join(train_dir, txt), os.path.join(test_dir, txt)) + except Exception as e: + print(f"❌ 移动到 test 失败: {img}, {e}") + + # 移动到 val + for img, txt in val_sample: + try: + shutil.move(os.path.join(train_dir, img), os.path.join(val_dir, img)) + shutil.move(os.path.join(train_dir, txt), os.path.join(val_dir, txt)) + except Exception as e: + print(f"❌ 移动到 val 失败: {img}, {e}") + + print(f"\n✅ 划分完成!") + print(f" train 保留: {total - num_val - num_test}") + print(f" val : {num_val}") + print(f" test : {num_test}") + print(f" 所有 val 和 test 数据已从 train 移出。") + + +# ======================== +# 使用示例 +# ======================== +if __name__ == "__main__": + train_dir = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/obb3/train" + val_dir = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/obb3/val" + test_dir = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/obb3/test" + + split_train_val_test(train_dir, val_dir, test_dir, val_ratio=0.1, test_ratio=0.1) +# ======================== diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144144937.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144144937.jpg new file mode 100644 index 0000000..9141761 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144144937.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144222222.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144222222.jpg new file mode 100644 index 0000000..d8c323f Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144222222.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144303840.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144303840.jpg new file mode 100644 index 0000000..2b40fc3 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144303840.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144341882.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144341882.jpg new file mode 100644 index 0000000..4d981d8 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144341882.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144421259.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144421259.jpg new file mode 100644 index 0000000..3f41d3d Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144421259.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144500393.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144500393.jpg new file mode 100644 index 0000000..34d51dc Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144500393.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144538968.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144538968.jpg new file mode 100644 index 0000000..ac86440 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144538968.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144619387.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144619387.jpg new file mode 100644 index 0000000..686be48 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144619387.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144814974.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144814974.jpg new file mode 100644 index 0000000..fded362 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144814974.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144929890.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144929890.jpg new file mode 100644 index 0000000..0841f84 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144929890.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145006232.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145006232.jpg new file mode 100644 index 0000000..a5b4f5b Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145006232.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145045330.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145045330.jpg new file mode 100644 index 0000000..f74e1a4 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145045330.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145128164.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145128164.jpg new file mode 100644 index 0000000..13a9fb0 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145128164.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145244471.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145244471.jpg new file mode 100644 index 0000000..a231114 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145244471.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145321687.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145321687.jpg new file mode 100644 index 0000000..322f64e Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145321687.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150210497.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150210497.jpg new file mode 100644 index 0000000..e160439 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150210497.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150246105.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150246105.jpg new file mode 100644 index 0000000..4632b44 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150246105.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150325231.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150325231.jpg new file mode 100644 index 0000000..75dd03b Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150325231.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150406560.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150406560.jpg new file mode 100644 index 0000000..3222f2f Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150406560.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150444617.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150444617.jpg new file mode 100644 index 0000000..4db12d2 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150444617.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_2025101415052359.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_2025101415052359.jpg new file mode 100644 index 0000000..fd8a6cc Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_2025101415052359.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150601333.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150601333.jpg new file mode 100644 index 0000000..bdb9a8b Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150601333.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150642365.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150642365.jpg new file mode 100644 index 0000000..9812532 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150642365.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150720686.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150720686.jpg new file mode 100644 index 0000000..46490dd Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150720686.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150918102.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150918102.jpg new file mode 100644 index 0000000..4d0d8d9 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150918102.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_2025101415103290.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_2025101415103290.jpg new file mode 100644 index 0000000..fa923d5 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_2025101415103290.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_2025101415111060.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_2025101415111060.jpg new file mode 100644 index 0000000..26ada89 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_2025101415111060.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151150768.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151150768.jpg new file mode 100644 index 0000000..7be2d3a Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151150768.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151232368.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151232368.jpg new file mode 100644 index 0000000..da51dd3 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151232368.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151311222.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151311222.jpg new file mode 100644 index 0000000..0b0bfe2 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151311222.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151353357.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151353357.jpg new file mode 100644 index 0000000..20a047d Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151353357.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_202510141514352.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_202510141514352.jpg new file mode 100644 index 0000000..e69faee Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_202510141514352.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_2025101415152183.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_2025101415152183.jpg new file mode 100644 index 0000000..b44e534 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_2025101415152183.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151922555.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151922555.jpg new file mode 100644 index 0000000..f69e644 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151922555.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153132629.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153132629.jpg new file mode 100644 index 0000000..ccf2994 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153132629.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153215762.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153215762.jpg new file mode 100644 index 0000000..4dbcba7 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153215762.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153257985.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153257985.jpg new file mode 100644 index 0000000..8f5c9d5 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153257985.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153338300.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153338300.jpg new file mode 100644 index 0000000..a66d909 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153338300.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153420611.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153420611.jpg new file mode 100644 index 0000000..4a04002 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153420611.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153506692.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153506692.jpg new file mode 100644 index 0000000..1d5ca4f Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153506692.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153632704.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153632704.jpg new file mode 100644 index 0000000..ee75f44 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153632704.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153800800.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153800800.jpg new file mode 100644 index 0000000..7f7dfd2 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153800800.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153839977.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153839977.jpg new file mode 100644 index 0000000..cd1f939 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153839977.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153918385.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153918385.jpg new file mode 100644 index 0000000..f582908 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153918385.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153957167.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153957167.jpg new file mode 100644 index 0000000..b43fa95 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153957167.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014154037360.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014154037360.jpg new file mode 100644 index 0000000..3f30529 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014154037360.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014154121106.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014154121106.jpg new file mode 100644 index 0000000..ca82513 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014154121106.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014154203390.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014154203390.jpg new file mode 100644 index 0000000..87b6456 Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014154203390.jpg differ diff --git a/yolo11_obb/image_lianghua/192.168.0.234_01_20251014154328204.jpg b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014154328204.jpg new file mode 100644 index 0000000..35263bc Binary files /dev/null and b/yolo11_obb/image_lianghua/192.168.0.234_01_20251014154328204.jpg differ diff --git a/yolo11_obb/image_lianghua/image_list.txt b/yolo11_obb/image_lianghua/image_list.txt new file mode 100644 index 0000000..0d4b23a --- /dev/null +++ b/yolo11_obb/image_lianghua/image_list.txt @@ -0,0 +1,49 @@ +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150642365.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153132629.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144341882.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014154328204.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153839977.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145006232.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150210497.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144500393.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153257985.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150246105.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153420611.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153918385.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144421259.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153506692.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144222222.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150720686.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145128164.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144538968.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151150768.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145321687.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144303840.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_202510141514352.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_2025101415103290.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_2025101415152183.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150444617.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145045330.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144814974.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153338300.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150918102.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151311222.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153215762.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_2025101415111060.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151922555.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144144937.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014154037360.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151232368.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144619387.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014154121106.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150601333.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150325231.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014151353357.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014154203390.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014145244471.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153957167.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014150406560.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_2025101415052359.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153632704.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014144929890.jpg +/home/hx/yolo/yolo11_obb/image_lianghua/192.168.0.234_01_20251014153800800.jpg diff --git a/yolo11_obb/move_photo_label.py b/yolo11_obb/move_photo_label.py new file mode 100644 index 0000000..dfab3fd --- /dev/null +++ b/yolo11_obb/move_photo_label.py @@ -0,0 +1,53 @@ +import os +import shutil + + +def move_images_to_labels(image_dir, label_dir): + """ + 将标签对应的图片从图片文件夹移动到标签文件夹。 + + :param image_dir: 存放图片的文件夹路径 + :param label_dir: 存放标签的文件夹路径,同时也是目标文件夹 + """ + # 支持的图片扩展名集合 + IMG_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.bmp', '.tif', '.tiff', '.webp'} + + # 遍历标签文件夹中的所有文件 + for label_filename in os.listdir(label_dir): + if not os.path.isfile(os.path.join(label_dir, label_filename)): + continue # 跳过非文件项(如目录) + + # 获取文件的基础名称(不带扩展名) + base_name, _ = os.path.splitext(label_filename) + + # 在图片文件夹中查找对应的所有可能扩展名的图片文件 + found_image = False + for ext in IMG_EXTENSIONS: + image_filename = base_name + ext + image_path = os.path.join(image_dir, image_filename) + + if os.path.exists(image_path): + # 构造目标路径 + target_path = os.path.join(label_dir, image_filename) + + # 移动图片文件到标签文件夹 + print(f"移动 {image_path} 到 {target_path}") + shutil.move(image_path, target_path) + + found_image = True + break + + if not found_image: + print(f"未找到与标签 {label_filename} 对应的图片") + + +if __name__ == "__main__": + IMAGE_SOURCE_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/obb4/images" # 替换为你的图片文件夹路径 + LABEL_TARGET_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/obb4/labels" # 替换为你的标签文件夹路径 + + if not os.path.isdir(IMAGE_SOURCE_DIR): + print(f"图片文件夹不存在: {IMAGE_SOURCE_DIR}") + elif not os.path.isdir(LABEL_TARGET_DIR): + print(f"标签文件夹不存在: {LABEL_TARGET_DIR}") + else: + move_images_to_labels(IMAGE_SOURCE_DIR, LABEL_TARGET_DIR) \ No newline at end of file diff --git a/yolo11_obb/trans_cvattoobb.py b/yolo11_obb/trans_cvattoobb.py index ee8059e..47a61c3 100644 --- a/yolo11_obb/trans_cvattoobb.py +++ b/yolo11_obb/trans_cvattoobb.py @@ -116,8 +116,7 @@ if __name__ == "__main__": CVAT_XML_PATH = "annotations.xml" # 你的 annotations.xml 文件 OUTPUT_DIR = "yolo_obb_dataset1" # 输出目录 CLASS_MAPPING = { - "clamp": 0, # 请根据你的实际类别修改 - # "other_class": 1, + "clamp": 0, } # 执行转换 diff --git a/yolo11_obb/txt_1.py b/yolo11_obb/txt_1.py index 29bb06b..38cde29 100644 --- a/yolo11_obb/txt_1.py +++ b/yolo11_obb/txt_1.py @@ -2,10 +2,10 @@ import os # ================== 配置参数 ================== # 图片所在的文件夹路径 -image_folder = '/home/hx/yolo/yolo11_obb/yolo_obb_dataset' # 修改为你的图片文件夹路径 +image_folder = '/home/hx/yolo/yolo11_obb/image_lianghua' # 修改为你的图片文件夹路径 # 输出的txt文件路径 -output_txt = '/home/hx/yolo/yolo11_obb/yolo_obb_dataset/image_list.txt' # 修改为你想保存的路径 +output_txt = '/home/hx/yolo/yolo11_obb/image_lianghua/image_list.txt' # 修改为你想保存的路径 # 支持的图片格式 image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.webp', '.tiff', '.gif'} diff --git a/yolo11_point/annotations.xml b/yolo11_point/annotations.xml new file mode 100644 index 0000000..edc4e21 --- /dev/null +++ b/yolo11_point/annotations.xml @@ -0,0 +1,1896 @@ + + + 1.1 + + + 198 + 10.19ailai + 311 + annotation + 0 + + 2025-10-20 02:57:36.437741+00:00 + 2025-10-20 08:14:17.105231+00:00 + default + 0 + 310 + + + + 141 + 0 + 310 + http://www.xj-robot.com:9000/api/jobs/141 + + + + huangxin + 2193534909@qq.com + + + + + + + + + + 2025-10-20 08:47:48.041459+00:00 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/yolo11_point/trans_cvattopoint.py b/yolo11_point/trans_cvattopoint.py new file mode 100644 index 0000000..c1db9a1 --- /dev/null +++ b/yolo11_point/trans_cvattopoint.py @@ -0,0 +1,83 @@ +# convert_cvat_to_yolo_keypoints.py +import xml.etree.ElementTree as ET +import os + +# =================== 配置 =================== +xml_file = 'annotations.xml' # 你的 CVAT XML 文件路径 +images_dir = 'images' # 图像文件夹(用于读取宽高) +output_dir = 'labels_keypoints' # 输出 YOLO 标签目录 +os.makedirs(output_dir, exist_ok=True) + +# 类别映射(根据你的 XML 中的 label name) +class_mapping = { + 'clamp1': 0, + 'clamp0': 1, + 'kongliao': 2, + 'duiliao': 3 +} +# ============================================ + +def parse_points(points_str): + """解析 CVAT 的 points 字符串,返回 [(x, y), ...]""" + return [(float(p.split(',')[0]), float(p.split(',')[1])) for p in points_str.split(';')] + +def normalize_bbox_and_kpts(image_w, image_h, bbox, keypoints): + """归一化 bbox 和关键点""" + cx, cy, w, h = bbox + cx_n, cy_n, w_n, h_n = cx/image_w, cy/image_h, w/image_w, h/image_h + kpts_n = [] + for x, y in keypoints: + kpts_n.append(x / image_w) + kpts_n.append(y / image_h) + kpts_n.append(2) # v=2: visible + return (cx_n, cy_n, w_n, h_n), kpts_n + +def points_to_bbox(points): + """从点集生成最小外接矩形 (x_center, y_center, width, height)""" + xs = [p[0] for p in points] + ys = [p[1] for p in points] + x_min, x_max = min(xs), max(xs) + y_min, y_max = min(ys), max(ys) + cx = (x_min + x_max) / 2 + cy = (y_min + y_max) / 2 + w = x_max - x_min + h = y_max - y_min + return cx, cy, w, h + +# 解析 XML +tree = ET.parse(xml_file) +root = tree.getroot() + +for image_elem in root.findall('image'): + image_name = image_elem.get('name') + image_w = int(image_elem.get('width')) + image_h = int(image_elem.get('height')) + label_file = os.path.splitext(image_name)[0] + '.txt' + label_path = os.path.join(output_dir, label_file) + + with open(label_path, 'w') as f_out: + # 查找 points 标注 + points_elem = image_elem.find("points[@label='clamp1']") + if points_elem is None: + print(f"⚠️ 警告:图像 {image_name} 缺少 clamp1 关键点标注") + continue + + points_str = points_elem.get('points') + keypoints = parse_points(points_str) + + if len(keypoints) != 4: + print(f"⚠️ 警告:图像 {image_name} 的关键点数量不是 4,实际为 {len(keypoints)}") + continue + + # 生成包围框 + bbox = points_to_bbox(keypoints) + + # 归一化 + (cx_n, cy_n, w_n, h_n), kpts_n = normalize_bbox_and_kpts(image_w, image_h, bbox, keypoints) + + # 写入 YOLO 格式 + line = [str(class_mapping['clamp1']), str(cx_n), str(cy_n), str(w_n), str(h_n)] + [str(k) for k in kpts_n] + f_out.write(' '.join(line) + '\n') + +print("✅ 所有标注已成功转换为 YOLO 关键点格式!") +print(f"📌 输出目录: {output_dir}") \ No newline at end of file diff --git a/yolo11_seg/del_json.py b/yolo11_seg/del_json.py index 2c7ab11..d71eb37 100644 --- a/yolo11_seg/del_json.py +++ b/yolo11_seg/del_json.py @@ -47,7 +47,7 @@ def delete_json_files(folder_path, recursive=True, confirm=True): print(f"\n🎉 删除完成!共删除 {deleted_count} 个 .json 文件。") # ================== 用户配置区 ================== -FOLDER_PATH = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/class4" # ✅ 修改为你要删除 JSON 文件的文件夹路径 +FOLDER_PATH = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/camera_02/f4/folder_end" # ✅ 修改为你要删除 JSON 文件的文件夹路径 RECURSIVE = True # True: 删除所有子文件夹中的 .json;False: 只删除当前文件夹 CONFIRM_BEFORE_DELETE = True # 是否每次删除前确认(推荐开启) diff --git a/yolo11_seg/json_trans_yolo-seg.py b/yolo11_seg/json_trans_yolo-seg.py index 58e4103..43756cb 100644 --- a/yolo11_seg/json_trans_yolo-seg.py +++ b/yolo11_seg/json_trans_yolo-seg.py @@ -90,13 +90,13 @@ def labelme_to_yolo_segmentation_batch(json_dir, output_dir, target_label="夹 # ================== 用户配置区 ================== -JSON_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/class4" # LabelMe JSON 文件夹路径 +JSON_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/class3" # LabelMe JSON 文件夹路径 OUTPUT_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/label" # 输出 YOLO 标注文件夹 -TARGET_LABEL = "yeimian" # 只转换这个标签 +TARGET_LABEL = "液面" # 只转换这个标签 #TARGET_LABEL = "夹具1" # 只转换这个标签i CLASS_ID = 0 # YOLO 中该类的 ID -IMG_SHAPE = (1440, 2560) # 图像实际尺寸 (高度, 宽度) - +#IMG_SHAPE = (1440, 2560) # 图像实际尺寸 (高度, 宽度) +IMG_SHAPE = (640, 640) # 图像实际尺寸 (高度, 宽度) # ================== 执行转换 ================== if __name__ == "__main__": print(f"🚀 开始转换 LabelMe → YOLO (仅 '{TARGET_LABEL}')") diff --git a/zhuangtai_class_cls/best.pt b/zhuangtai_class_cls/best.pt new file mode 100644 index 0000000..3fd6d73 Binary files /dev/null and b/zhuangtai_class_cls/best.pt differ diff --git a/zhuangtai_class_cls/chose.py b/zhuangtai_class_cls/chose_ROI.py similarity index 98% rename from zhuangtai_class_cls/chose.py rename to zhuangtai_class_cls/chose_ROI.py index 66b9e27..b9dc817 100644 --- a/zhuangtai_class_cls/chose.py +++ b/zhuangtai_class_cls/chose_ROI.py @@ -75,7 +75,7 @@ def select_roi(image_path): if key == ord('s'): # 保存坐标 base_name = os.path.splitext(os.path.basename(image_path))[0] - save_path = os.path.join(save_dir, f"{base_name}_rois.txt") # 修改了扩展名为 .txt + save_path = os.path.join(save_dir, f"{base_name}_rois1.txt") # 修改了扩展名为 .txt save_rois_to_txt(roi_list, save_path) # 使用新的保存函数 elif key == ord('n'): diff --git a/zhuangtai_class_cls/resize_dataset.py b/zhuangtai_class_cls/resize_dataset.py deleted file mode 100644 index 20039f5..0000000 --- a/zhuangtai_class_cls/resize_dataset.py +++ /dev/null @@ -1,84 +0,0 @@ -import cv2 -import os - -# 配置路径 -original_images_parent_dir = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/classdata3/val" # 含 class0, class1 等 -roi_coords_file = "./roi_coordinates/1_rois.txt" # 你手动标注的唯一一个 ROI 文件 -output_parent_dir = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/classdata3/classdata3_cropped" -target_size = 640 - -os.makedirs(output_parent_dir, exist_ok=True) - -def load_global_rois(txt_path): - """加载全局 ROI 坐标""" - 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: - line = line.strip() - if line: - try: - x, y, w, h = map(int, line.split(',')) - rois.append((x, y, w, h)) - print(f"📌 加载 ROI: (x={x}, y={y}, w={w}, h={h})") - except Exception as e: - print(f"⚠️ 无法解析 ROI 行: {line}, 错误: {e}") - return rois - -# 加载全局 ROI -rois = load_global_rois(roi_coords_file) -if len(rois) == 0: - print("❌ 没有加载到任何有效的 ROI 坐标,程序退出。") - exit(1) - -# 遍历所有子目录和图像 -for class_name in os.listdir(original_images_parent_dir): - class_dir = os.path.join(original_images_parent_dir, class_name) - if not os.path.isdir(class_dir): - continue - - # 创建输出类别目录 - output_class_dir = os.path.join(output_parent_dir, class_name) - os.makedirs(output_class_dir, exist_ok=True) - - print(f"🔄 处理类别: {class_name}") - - for img_file in os.listdir(class_dir): - if not img_file.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp')): - continue - - img_path = os.path.join(class_dir, img_file) - base_name, ext = os.path.splitext(img_file) - - # 读取图像 - img = cv2.imread(img_path) - if img is None: - print(f"❌ 无法读取图像: {img_path}") - continue - - # 对每一个 ROI 进行裁剪 - for i, (x, y, w, h) in enumerate(rois): - # 检查坐标是否越界 - h_img, w_img = img.shape[:2] - if x < 0 or y < 0 or x + w > w_img or y + h > h_img: - print(f"⚠️ ROI 越界,跳过: {x},{y},{w},{h} in {img_path}") - continue - - roi_img = img[y:y+h, x:x+w] - if roi_img.size == 0: - print(f"⚠️ 空 ROI 区域: {x},{y},{w},{h} in {img_path}") - continue - - # resize - roi_resized = cv2.resize(roi_img, (target_size, target_size), interpolation=cv2.INTER_AREA) - - # 保存文件名:原名 + _roi0, _roi1... - suffix = f"_roi{i}" if len(rois) > 1 else "" - save_filename = f"{base_name}{suffix}{ext}" - save_path = os.path.join(output_class_dir, save_filename) - cv2.imwrite(save_path, roi_resized) - print(f"✅ 保存: {save_path}") - -print("🎉 所有图像已根据全局 ROI 裁剪并保存完成!") \ No newline at end of file diff --git a/zhuangtai_class_cls/resize_dataset_image.py b/zhuangtai_class_cls/resize_dataset_image.py new file mode 100644 index 0000000..c9ea6ff --- /dev/null +++ b/zhuangtai_class_cls/resize_dataset_image.py @@ -0,0 +1,58 @@ +import os +import cv2 + +# ---------------------------- +# 配置 +# ---------------------------- +SOURCE_ROOT_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/classdata2" # 原始图片根目录 +TARGET_ROOT_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/classdata4" # 输出根目录 +CLASSES = ["class0", "class1", "class2", "class3", "class4"] # 类别列表 +TARGET_SIZE = 640 # resize 尺寸 +SUBSETS = ["train", "val", "test"] + +# ---------------------------- +# 全局 ROI (x, y, w, h) +# ---------------------------- +GLOBAL_ROI = [859,810,696,328] + +# ---------------------------- +# 主处理函数 +# ---------------------------- +def process_images(): + x, y, w, h = GLOBAL_ROI + for subset in SUBSETS: + for class_dir in CLASSES: + src_dir = os.path.join(SOURCE_ROOT_DIR, subset, class_dir) + tgt_dir = os.path.join(TARGET_ROOT_DIR, subset, class_dir) + os.makedirs(tgt_dir, exist_ok=True) + + if not os.path.exists(src_dir): + print(f"警告: 源目录 {src_dir} 不存在,跳过") + continue + + for file in os.listdir(src_dir): + if not (file.endswith(".jpg") or file.endswith(".png")): + continue + + img_path = os.path.join(src_dir, file) + img = cv2.imread(img_path) + if img is None: + print(f"❌ 无法读取图片: {img_path}") + continue + + h_img, w_img = img.shape[:2] + x1, y1 = max(0, x), max(0, y) + x2, y2 = min(w_img, x + w), min(h_img, y + h) + + cropped = img[y1:y2, x1:x2] + if cropped.size == 0: + print(f"❌ 裁剪结果为空: {file}") + continue + + resized = cv2.resize(cropped, (TARGET_SIZE, TARGET_SIZE)) + tgt_path = os.path.join(tgt_dir, file) + cv2.imwrite(tgt_path, resized) + print(f"✅ 图片处理完成: {subset}/{class_dir}/{file}") + +if __name__ == "__main__": + process_images() \ No newline at end of file diff --git a/zhuangtai_class_cls/resize_main.py b/zhuangtai_class_cls/resize_main.py index b233c14..ce1f85c 100644 --- a/zhuangtai_class_cls/resize_main.py +++ b/zhuangtai_class_cls/resize_main.py @@ -97,9 +97,9 @@ def classify_and_save_images(model_path, input_folder, output_root, roi_file, ta # 主程序 # --------------------------- if __name__ == "__main__": - model_path = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/cls_resize/exp_cls2/weights/best.pt" + model_path = r"best.pt" input_folder = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/f6" - output_root = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/class111" + output_root = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/class44" roi_file = "./roi_coordinates/1_rois.txt" # 训练时使用的 ROI 文件 target_size = 640 diff --git a/zhuangtai_class_cls/remain_tuili.py b/zhuangtai_class_cls/resize_tuili_file.py similarity index 99% rename from zhuangtai_class_cls/remain_tuili.py rename to zhuangtai_class_cls/resize_tuili_file.py index d23840e..5b5efaf 100644 --- a/zhuangtai_class_cls/remain_tuili.py +++ b/zhuangtai_class_cls/resize_tuili_file.py @@ -134,8 +134,8 @@ def batch_classify_images(model_path, input_folder, output_root, roi_file, targe # --------------------------- if __name__ == "__main__": model_path = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/cls_resize/exp_cls2/weights/best.pt" - input_folder = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/f6" - output_root = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/classified" + input_folder = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1000" + output_root = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1000/classified" roi_file = "./roi_coordinates/1_rois.txt" target_size = 640 threshold = 0.4 # 可调节的比例系数 diff --git a/zhuangtai_class_cls/resize_tuili_image_main.py b/zhuangtai_class_cls/resize_tuili_image_main.py new file mode 100644 index 0000000..60f1eb1 --- /dev/null +++ b/zhuangtai_class_cls/resize_tuili_image_main.py @@ -0,0 +1,168 @@ +import os +from pathlib import Path +import cv2 +import numpy as np +from ultralytics import YOLO + +# --------------------------- +# 类别映射 +# --------------------------- +CLASS_NAMES = { + 0: "未堆料", + 1: "小堆料", + 2: "大堆料", + 3: "未浇筑满", + 4: "浇筑满" +} + +# --------------------------- +# 加载 ROI 列表 +# --------------------------- +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 + +# --------------------------- +# 裁剪并 resize ROI +# --------------------------- +def crop_and_resize(img, rois, target_size=640): + crops = [] + h_img, w_img = img.shape[:2] + for i, (x, y, w, h) in enumerate(rois): + if x < 0 or y < 0 or x + w > w_img or y + h > h_img: + continue + roi = img[y:y+h, x:x+w] + roi_resized = cv2.resize(roi, (target_size, target_size), interpolation=cv2.INTER_AREA) + crops.append((roi_resized, i)) + return crops + +# --------------------------- +# class1/class2 加权判断 +# --------------------------- +def weighted_small_large(pred_probs, threshold=0.4, w1=0.3, w2=0.7): + p1 = float(pred_probs[1]) + p2 = float(pred_probs[2]) + total = p1 + p2 + if total > 0: + score = (w1 * p1 + w2 * p2) / total + else: + score = 0.0 + final_class = "大堆料" if score >= threshold else "小堆料" + return final_class, score, p1, p2 + +# --------------------------- +# 单张图片推理函数 +# --------------------------- +def classify_image_weighted(image, model, threshold=0.4): + results = model(image) + pred_probs = results[0].probs.data.cpu().numpy().flatten() + class_id = int(pred_probs.argmax()) + confidence = float(pred_probs[class_id]) + class_name = CLASS_NAMES.get(class_id, f"未知类别({class_id})") + + # class1/class2 使用加权得分 + if class_id in [1, 2]: + final_class, score, p1, p2 = weighted_small_large(pred_probs, threshold=threshold) + else: + final_class = class_name + score = confidence + p1 = float(pred_probs[1]) + p2 = float(pred_probs[2]) + + return final_class, score, p1, p2 + +# --------------------------- +# 批量推理主函数 +# --------------------------- +def batch_classify_images(model_path, input_folder, output_root, roi_file, target_size=640, threshold=0.5): + # 加载模型 + model = YOLO(model_path) + + # 确保输出根目录存在 + output_root = Path(output_root) + output_root.mkdir(parents=True, exist_ok=True) + + # 为所有类别创建目录 + class_dirs = {} + for name in CLASS_NAMES.values(): + d = output_root / name + d.mkdir(exist_ok=True) + class_dirs[name] = d + + rois = load_global_rois(roi_file) + if not rois: + print("❌ 没有有效 ROI,退出") + return + + # 遍历图片 + for img_path in Path(input_folder).glob("*.*"): + if img_path.suffix.lower() not in ['.jpg', '.jpeg', '.png', '.bmp', '.tif']: + continue + try: + img = cv2.imread(str(img_path)) + if img is None: + continue + + crops = crop_and_resize(img, rois, target_size) + + for roi_resized, roi_idx in crops: + final_class, score, p1, p2 = classify_image_weighted(roi_resized, model, threshold=threshold) + + # 文件名中保存 ROI、类别、加权分数、class1/class2 置信度 + suffix = f"_roi{roi_idx}_{final_class}_score{score:.2f}_p1{p1:.2f}_p2{p2:.2f}" + dst_path = class_dirs[final_class] / f"{img_path.stem}{suffix}{img_path.suffix}" + cv2.imwrite(dst_path, roi_resized) + print(f"{img_path.name}{suffix} -> {final_class} (score={score:.2f}, p1={p1:.2f}, p2={p2:.2f})") + + except Exception as e: + print(f"处理失败 {img_path.name}: {e}") + + +# --------------------------- +# 单张图片使用示例(保留 ROI,不保存文件) +# --------------------------- +if __name__ == "__main__": + model_path = r"best.pt" + image_path = r"./test_image/1.jpg" # 单张图片路径 + roi_file = r"./roi_coordinates/1_rois.txt" + target_size = 640 + threshold = 0.4 #加权得分阈值可以根据大小堆料分类结果进行调整 + + # 加载模型 + model = YOLO(model_path) + + # 读取 ROI + rois = load_global_rois(roi_file) + if not rois: + print("❌ 没有有效 ROI,退出") + exit(1) + + # 读取图片 + img = cv2.imread(image_path) + if img is None: + print(f"❌ 无法读取图片: {image_path}") + exit(1) + + # 注意:必须裁剪 ROI 并推理,因为训练的时候输入的图像是经过resize的 + crops = crop_and_resize(img, rois, target_size) + for roi_resized, roi_idx in crops: + #final_class, score, p1, p2 = classify_image_weighted(roi_resized, model, threshold=threshold) + final_class,_,_,_ = classify_image_weighted(roi_resized, model, threshold=threshold) + # 只输出信息,不保存文件 + #print(f"ROI {roi_idx} -> 类别: {final_class}, 加权分数: {score:.2f}, " + #f"class1 置信度: {p1:.2f}, class2 置信度: {p2:.2f}") + print(f"类别: {final_class}") + + diff --git a/zhuangtai_class_cls/roi_coordinates/1_rois1.txt b/zhuangtai_class_cls/roi_coordinates/1_rois1.txt new file mode 100644 index 0000000..86bce37 --- /dev/null +++ b/zhuangtai_class_cls/roi_coordinates/1_rois1.txt @@ -0,0 +1 @@ +626,725,1247,509 diff --git a/zhuangtai_class_cls/main.py b/zhuangtai_class_cls/test_cls_file.py similarity index 100% rename from zhuangtai_class_cls/main.py rename to zhuangtai_class_cls/test_cls_file.py diff --git a/zhuangtai_class_cls/test_image/1.jpg b/zhuangtai_class_cls/test_image/1.jpg new file mode 100644 index 0000000..2882cd5 Binary files /dev/null and b/zhuangtai_class_cls/test_image/1.jpg differ diff --git a/zhuangtai_class_cls/test_image/2.jpg b/zhuangtai_class_cls/test_image/2.jpg new file mode 100644 index 0000000..dcd5369 Binary files /dev/null and b/zhuangtai_class_cls/test_image/2.jpg differ diff --git a/zhuangtai_class_cls/test_image/3.jpg b/zhuangtai_class_cls/test_image/3.jpg new file mode 100644 index 0000000..8df7feb Binary files /dev/null and b/zhuangtai_class_cls/test_image/3.jpg differ diff --git a/zhuangtai_class_cls/tuili_f_yuantusave.py b/zhuangtai_class_cls/tuili_f_yuantusave.py new file mode 100644 index 0000000..104d38c --- /dev/null +++ b/zhuangtai_class_cls/tuili_f_yuantusave.py @@ -0,0 +1,198 @@ +import os +from pathlib import Path +import cv2 +import numpy as np +from ultralytics import YOLO + +# --------------------------- +# 类别映射 +# --------------------------- +CLASS_NAMES = { + 0: "未堆料", + 1: "小堆料", + 2: "大堆料", + 3: "未浇筑满", + 4: "浇筑满" +} + +# --------------------------- +# 加载 ROI 列表 +# --------------------------- +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 + + +# --------------------------- +# 裁剪并 resize ROI +# --------------------------- +def crop_and_resize(img, rois, target_size=640): + crops = [] + h_img, w_img = img.shape[:2] + for i, (x, y, w, h) in enumerate(rois): + # 边界检查 + if x < 0 or y < 0 or x + w > w_img or y + h > h_img: + print(f"⚠️ ROI 超出图像边界,跳过: ({x}, {y}, {w}, {h})") + continue + roi = img[y:y+h, x:x+w] + roi_resized = cv2.resize(roi, (target_size, target_size), interpolation=cv2.INTER_AREA) + crops.append((roi_resized, i)) + return crops + + +# --------------------------- +# class1/class2 加权判断 +# --------------------------- +def weighted_small_large(pred_probs, threshold=0.4, w1=0.3, w2=0.7): + p1 = float(pred_probs[1]) + p2 = float(pred_probs[2]) + total = p1 + p2 + if total > 0: + score = (w1 * p1 + w2 * p2) / total + else: + score = 0.0 + final_class = "大堆料" if score >= threshold else "小堆料" + return final_class, score, p1, p2 + + +# --------------------------- +# 单张图片推理函数 +# --------------------------- +def classify_image_weighted(image, model, threshold=0.5): + results = model(image) + pred_probs = results[0].probs.data.cpu().numpy().flatten() + class_id = int(pred_probs.argmax()) + confidence = float(pred_probs[class_id]) + class_name = CLASS_NAMES.get(class_id, f"未知类别({class_id})") + + # 对于 小堆料/大堆料 使用加权逻辑 + if class_id in [1, 2]: + final_class, score, p1, p2 = weighted_small_large(pred_probs, threshold=threshold) + else: + final_class = class_name + score = confidence + p1 = float(pred_probs[1]) + p2 = float(pred_probs[2]) + + return final_class, score, p1, p2 + + +# --------------------------- +# 批量推理主函数(保存原图,不改名) +# --------------------------- +def batch_classify_images(model_path, input_folder, output_root, roi_file, target_size=640, threshold=0.5): + # 加载模型 + print("🚀 加载模型...") + model = YOLO(model_path) + + # 确保输出根目录存在 + output_root = Path(output_root) + output_root.mkdir(parents=True, exist_ok=True) + print(f"📁 输出目录: {output_root}") + + # 为所有类别创建子目录 + class_dirs = {} + for name in CLASS_NAMES.values(): + d = output_root / name + d.mkdir(exist_ok=True) + class_dirs[name] = d + + # 加载 ROI 区域 + rois = load_global_rois(roi_file) + if not rois: + print("❌ 没有有效 ROI,退出程序") + return + + print(f"🎯 已加载 {len(rois)} 个 ROI 区域") + + # 定义缺陷严重性等级(数字越小越严重) + severity_rank = { + "未堆料": 0, + "大堆料": 1, + "小堆料": 2, + "未浇筑满": 3, + "浇筑满": 4 + } + + # 遍历输入文件夹中的所有图片 + input_folder = Path(input_folder) + image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tif', '.tiff'} + + processed_count = 0 + for img_path in input_folder.glob("*.*"): + if img_path.suffix.lower() not in image_extensions: + continue + + try: + print(f"\n📄 处理: {img_path.name}") + img = cv2.imread(str(img_path)) + if img is None: + print(f"❌ 无法读取图像: {img_path.name}") + continue + + # 裁剪并缩放 ROI + crops = crop_and_resize(img, rois, target_size) + + if not crops: + print(f"⚠️ 无有效 ROI 裁剪区域: {img_path.name}") + continue + + detected_classes = [] + + # 遍历每个 ROI 进行分类 + for roi_img, roi_idx in crops: + final_class, score, p1, p2 = classify_image_weighted(roi_img, model, threshold=threshold) + detected_classes.append(final_class) + print(f" 🔍 ROI{roi_idx}: {final_class} (score={score:.2f})") + + # 选择最严重的类别 + most_severe_class = min(detected_classes, key=lambda x: severity_rank.get(x, 99)) + + # 构造目标路径:保持原文件名 + dst_path = class_dirs[most_severe_class] / img_path.name + + # 保存原图(不修改内容,不重命名) + cv2.imwrite(str(dst_path), img) + print(f"✅ 保存 -> [{most_severe_class}] {dst_path}") + + processed_count += 1 + + except Exception as e: + print(f"❌ 处理失败 {img_path.name}: {e}") + + print(f"\n🎉 批量处理完成!共处理 {processed_count} 张图像。") + + +# --------------------------- +# 使用示例(请根据实际情况修改路径) +# --------------------------- +if __name__ == "__main__": + # 🔧 用户配置区 + model_path = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/cls_resize/exp_cls2/weights/best.pt" + input_folder = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1000" + output_root = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/1000/classified" + roi_file = "./roi_coordinates/1_rois.txt" + target_size = 640 + threshold = 0.4 # 小堆料 vs 大堆料的加权阈值 + + # 🚀 开始执行 + batch_classify_images( + model_path=model_path, + input_folder=input_folder, + output_root=output_root, + roi_file=roi_file, + target_size=target_size, + threshold=threshold + ) \ No newline at end of file